From a423916059ae3df0600c9009fbbb8eb5f5e21977 Mon Sep 17 00:00:00 2001 From: maryarm Date: Mon, 24 May 2021 20:19:26 +0300 Subject: [PATCH 01/59] close #1-MVP Draft --- Accountant/.gitignore | 1 + Accountant/accountant-app/.gitignore | 80 + Accountant/accountant-app/Dockerfile | 5 + Accountant/accountant-app/pom.xml | 193 ++ .../co/nilin/mixchange/app/AccountantApp.kt | 12 + .../nilin/mixchange/app/config/AppConfig.kt | 235 +++ .../mixchange/app/config/AppDispatchers.kt | 8 + .../app/controller/AccountantController.kt | 46 + .../app/scheduler/FinancialActionsJob.kt | 21 + .../src/main/resources/application-docker.yml | 34 + .../src/main/resources/application.yml | 33 + Accountant/accountant-core/.gitignore | 4 + Accountant/accountant-core/mvnw | 310 ++++ Accountant/accountant-core/mvnw.cmd | 182 ++ Accountant/accountant-core/pom.xml | 105 ++ .../core/api/FinancialActionJobManager.kt | 5 + .../accountant/core/api/OrderManager.kt | 12 + .../accountant/core/api/TradeManager.kt | 8 + .../accountant/core/model/FinancialAction.kt | 41 + .../mixchange/accountant/core/model/Order.kt | 24 + .../accountant/core/model/PairConfig.kt | 9 + .../accountant/core/model/PairFeeConfig.kt | 9 + .../service/FinancialActionJobManagerImpl.kt | 32 + .../core/service/OrderManagerImpl.kt | 171 ++ .../core/service/TradeManagerImpl.kt | 239 +++ .../core/spi/FinancialActionLoader.kt | 10 + .../core/spi/FinancialActionPersister.kt | 9 + .../accountant/core/spi/OrderPersister.kt | 8 + .../accountant/core/spi/PairConfigLoader.kt | 8 + .../core/spi/PairStaticRateLoader.kt | 5 + .../accountant/core/spi/TempEventPersister.kt | 9 + .../core/spi/TempEventRepublisher.kt | 7 + .../accountant/core/spi/WalletProxy.kt | 18 + .../accountant/core/service/MockitoHelper.kt | 12 + .../core/service/OrderManagerImplTest.kt | 168 ++ .../core/service/TradeManagerImplTest.kt | 201 ++ Accountant/accountant-ports/.gitignore | 4 + .../accountant-eventlistener-kafka/.gitignore | 34 + .../accountant-eventlistener-kafka/mvnw | 310 ++++ .../accountant-eventlistener-kafka/mvnw.cmd | 182 ++ .../accountant-eventlistener-kafka/pom.xml | 113 ++ .../kafka/config/AccountantKafkaConfig.kt | 128 ++ .../kafka/consumer/EventKafkaListener.kt | 28 + .../kafka/consumer/OrderKafkaListener.kt | 32 + .../kafka/consumer/TempEventKafkaListener.kt | 30 + .../kafka/consumer/TradeKafkaListener.kt | 28 + .../accountant/kafka/spi/EventListener.kt | 9 + .../kafka/spi/OrderSubmitRequestListener.kt | 8 + .../accountant/kafka/spi/TempEventListener.kt | 9 + .../accountant/kafka/spi/TradeListener.kt | 9 + .../order/kafka/inout/OrderSubmitRequest.kt | 39 + .../accountant-persister-postgres/.gitignore | 34 + .../accountant-persister-postgres/mvnw | 310 ++++ .../accountant-persister-postgres/mvnw.cmd | 182 ++ .../accountant-persister-postgres/pom.xml | 117 ++ .../postgres/config/PostgresConfig.kt | 109 ++ .../postgres/dao/FinancialActionRepository.kt | 33 + .../postgres/dao/OrderRepository.kt | 14 + .../postgres/dao/PairConfigRepository.kt | 9 + .../postgres/dao/PairFeeConfigRepository.kt | 19 + .../postgres/dao/TempEventRepository.kt | 13 + .../impl/FinancialActionLoaderImpl.kt | 62 + .../impl/FinancialActionPersisterImpl.kt | 66 + .../postgres/impl/OrderPersisterImpl.kt | 51 + .../postgres/impl/PairConfigLoaderImpl.kt | 52 + .../postgres/impl/PairStaticRateLoaderImpl.kt | 17 + .../postgres/impl/TempEventPersisterImpl.kt | 38 + .../postgres/model/FinancialActionModel.kt | 33 + .../accountant/postgres/model/OrderModel.kt | 33 + .../postgres/model/PairConfigModel.kt | 15 + .../postgres/model/PairFeeConfigModel.kt | 15 + .../postgres/model/TempEventModel.kt | 14 + .../accountant-tempsubmitter-kafka/.gitignore | 50 + .../accountant-tempsubmitter-kafka/mvnw | 310 ++++ .../accountant-tempsubmitter-kafka/mvnw.cmd | 182 ++ .../accountant-tempsubmitter-kafka/pom.xml | 120 ++ .../kafka/config/SubmitterKafkaConfig.kt | 53 + .../kafka/service/TempEventSubmitter.kt | 29 + .../accountant-wallet-proxy/.gitignore | 4 + .../accountant-wallet-proxy/mvnw | 310 ++++ .../accountant-wallet-proxy/mvnw.cmd | 182 ++ .../accountant-wallet-proxy/pom.xml | 139 ++ .../wallet/config/WebClientConfig.kt | 25 + .../wallet/proxy/WalletProxyImpl.kt | 63 + Accountant/pom.xml | 19 + Deployment/.gitignore | 3 + Deployment/docker-compose.yml | 259 +++ EventLog/.gitignore | 36 + EventLog/eventlog-app/.gitignore | 33 + EventLog/eventlog-app/Dockerfile | 5 + EventLog/eventlog-app/pom.xml | 189 ++ .../mixchange/eventlog/app/EventLogApp.kt | 12 + .../eventlog/app/config/AppConfig.kt | 121 ++ .../src/main/resources/application-docker.yml | 11 + .../src/main/resources/application.yml | 11 + EventLog/eventlog-core/.gitignore | 33 + EventLog/eventlog-core/pom.xml | 100 + .../co/nilin/mixchange/eventlog/spi/Event.kt | 4 + .../mixchange/eventlog/spi/EventPersister.kt | 7 + .../co/nilin/mixchange/eventlog/spi/Order.kt | 4 + .../mixchange/eventlog/spi/OrderPersister.kt | 11 + .../co/nilin/mixchange/eventlog/spi/Trade.kt | 4 + .../mixchange/eventlog/spi/TradePersister.kt | 7 + .../eventlog-eventlistener-kafka/.gitignore | 33 + .../eventlog-eventlistener-kafka/mvnw | 310 ++++ .../eventlog-eventlistener-kafka/mvnw.cmd | 182 ++ .../eventlog-eventlistener-kafka/pom.xml | 106 ++ .../kafka/config/EventlogKafkaConfig.kt | 109 ++ .../kafka/consumer/EventKafkaListener.kt | 28 + .../kafka/consumer/OrderKafkaListener.kt | 34 + .../kafka/consumer/TradeKafkaListener.kt | 29 + .../port/eventlog/kafka/spi/EventListener.kt | 9 + .../kafka/spi/OrderSubmitRequestListener.kt | 8 + .../port/eventlog/kafka/spi/TradeListener.kt | 9 + .../order/kafka/inout/OrderSubmitRequest.kt | 39 + .../eventlog-persister-postgres/.gitignore | 33 + .../eventlog-persister-postgres/mvnw | 310 ++++ .../eventlog-persister-postgres/mvnw.cmd | 182 ++ .../eventlog-persister-postgres/pom.xml | 112 ++ .../postgres/config/PostgresConfig.kt | 82 + .../eventlog/postgres/dao/EventRepository.kt | 9 + .../postgres/dao/OrderEventRepository.kt | 10 + .../eventlog/postgres/dao/OrderRepository.kt | 9 + .../eventlog/postgres/dao/TradeRepository.kt | 9 + .../postgres/impl/EventPersisterImpl.kt | 72 + .../postgres/impl/OrderPersisterImpl.kt | 66 + .../postgres/impl/TradePersisterImpl.kt | 37 + .../eventlog/postgres/model/EventModel.kt | 23 + .../postgres/model/OrderEventsModel.kt | 20 + .../eventlog/postgres/model/OrderModel.kt | 22 + .../eventlog/postgres/model/TradeModel.kt | 43 + EventLog/pom.xml | 18 + LICENSE-THIRD-PARTY | 6 + MatchingEngine/.gitignore | 86 + MatchingEngine/matching-app/.gitignore | 77 + MatchingEngine/matching-app/Dockerfile | 5 + MatchingEngine/matching-app/pom.xml | 188 ++ .../nilin/mixchange/app/MatchingEngineApp.kt | 12 + .../mixchange/app/bl/ExchangeEventHandler.kt | 29 + .../co/nilin/mixchange/app/bl/OrderBooks.kt | 26 + .../nilin/mixchange/app/config/AppConfig.kt | 104 ++ .../mixchange/app/config/AppSchedulers.kt | 8 + .../src/main/resources/application-docker.yml | 13 + .../src/main/resources/application.yml | 13 + MatchingEngine/matching-core/.gitignore | 77 + MatchingEngine/matching-core/mvnw | 310 ++++ MatchingEngine/matching-core/mvnw.cmd | 182 ++ MatchingEngine/matching-core/pom.xml | 93 + .../matching/core/engine/SimpleOrderBook.kt | 372 ++++ .../matching/core/eventh/EventDispatcher.kt | 50 + .../core/eventh/events/CancelOrderEvent.kt | 48 + .../matching/core/eventh/events/CoreEvent.kt | 9 + .../core/eventh/events/CreateOrderEvent.kt | 48 + .../core/eventh/events/OneOrderEvent.kt | 6 + .../core/eventh/events/RejectOrderEvent.kt | 70 + .../core/eventh/events/SubmitOrderEvent.kt | 48 + .../matching/core/eventh/events/TradeEvent.kt | 54 + .../core/eventh/events/UpdatedOrderEvent.kt | 54 + .../matching/core/factory/OrderBookFactory.kt | 20 + .../matching/core/inout/OrderCancelCommand.kt | 5 + .../matching/core/inout/OrderCreateCommand.kt | 16 + .../matching/core/inout/OrderEditCommand.kt | 5 + .../matching/core/inout/RejectReason.kt | 5 + .../matching/core/inout/RequestedOperation.kt | 5 + .../mixchange/matching/core/model/Order.kt | 6 + .../matching/core/model/OrderBook.kt | 16 + .../matching/core/model/OrderMetaData.kt | 26 + .../mixchange/matching/core/model/Pair.kt | 17 + .../matching/core/model/PersistentOrder.kt | 39 + .../core/model/PersistentOrderBook.kt | 16 + .../matching/core/spi/OrderBookPersister.kt | 8 + .../core/engine/SimpleOrderBookUnitTest.kt | 278 +++ .../matching-eventlistener-kafka/.gitignore | 79 + .../matching-eventlistener-kafka/mvnw | 310 ++++ .../matching-eventlistener-kafka/mvnw.cmd | 182 ++ .../matching-eventlistener-kafka/pom.xml | 113 ++ .../order/kafka/config/OrderKafkaConfig.kt | 100 + .../kafka/consumer/OrderKafkaListener.kt | 30 + .../order/kafka/inout/OrderSubmitRequest.kt | 42 + .../order/kafka/inout/OrderSubmitResult.kt | 3 + .../kafka/spi/OrderSubmitRequestListener.kt | 8 + .../matching-snapshots-redis/.gitignore | 76 + .../matching-snapshots-redis/mvnw | 310 ++++ .../matching-snapshots-redis/mvnw.cmd | 182 ++ .../matching-snapshots-redis/pom.xml | 101 ++ .../port/order/redis/config/RedisConfig.kt | 33 + .../order/redis/service/OrderBookPersister.kt | 24 + .../matching-submitter-kafka/.gitignore | 77 + .../matching-submitter-kafka/mvnw | 310 ++++ .../matching-submitter-kafka/mvnw.cmd | 182 ++ .../matching-submitter-kafka/pom.xml | 113 ++ .../order/kafka/config/EventsKafkaConfig.kt | 77 + .../order/kafka/service/EventsSubmitter.kt | 18 + MatchingEngine/pom.xml | 19 + MatchingGateway/.gitignore | 73 + MatchingGateway/gateway-app/.gitignore | 77 + MatchingGateway/gateway-app/Dockerfile | 5 + MatchingGateway/gateway-app/pom.xml | 211 +++ .../nilin/mixchange/app/MatchingGatewayApp.kt | 12 + .../nilin/mixchange/app/config/AppConfig.kt | 47 + .../mixchange/app/config/SecurityConfig.kt | 44 + .../mixchange/app/config/WebClientConfig.kt | 25 + .../controller/ControllerExceptionHandler.kt | 90 + .../app/controller/OrderController.kt | 28 + .../NotAllowedToSubmitOrderException.kt | 6 + .../mixchange/app/inout/CreateOrderRequest.kt | 16 + .../nilin/mixchange/app/inout/PairConfig.kt | 9 + .../mixchange/app/inout/PairFeeConfig.kt | 9 + .../app/proxy/AccountantProxyImpl.kt | 56 + .../mixchange/app/service/OrderService.kt | 62 + .../mixchange/app/spi/AccountantApiProxy.kt | 11 + .../mixchange/app/spi/PairConfigLoader.kt | 8 + .../src/main/resources/application-docker.yml | 24 + .../src/main/resources/application.yml | 23 + .../src/main/resources/public.cert | 3 + .../order-submitter-kafka/.gitignore | 77 + .../gateway-port/order-submitter-kafka/mvnw | 310 ++++ .../order-submitter-kafka/mvnw.cmd | 182 ++ .../order-submitter-kafka/pom.xml | 113 ++ .../order/kafka/config/OrderKafkaConfig.kt | 44 + .../order/kafka/inout/OrderSubmitRequest.kt | 42 + .../order/kafka/inout/OrderSubmitResult.kt | 3 + .../order/kafka/service/OrderSubmitter.kt | 28 + MatchingGateway/pom.xml | 15 + UserManagement/.gitignore | 76 + UserManagement/keycloak-gateway/.gitignore | 73 + UserManagement/keycloak-gateway/Dockerfile | 5 + UserManagement/keycloak-gateway/pom.xml | 221 +++ .../auth/gateway/ApplicationContextHolder.kt | 11 + .../auth/gateway/KeycloakGatewayApp.kt | 24 + .../auth/gateway/config/AppConfig.kt | 28 + .../config/EmbeddedKeycloakApplication.kt | 79 + .../gateway/config/EmbeddedKeycloakConfig.kt | 77 + .../config/EmbeddedKeycloakRequestFilter.kt | 52 + .../auth/gateway/config/KafkaConfig.kt | 47 + .../config/KeycloakServerProperties.kt | 18 + .../RegularJsonConfigProviderFactory.kt | 6 + .../auth/gateway/config/Resteasy3Provider.kt | 24 + .../gateway/config/SimplePlatformProvider.kt | 30 + .../ExtendedEventListenerProvider.kt | 121 ++ .../ExtendedEventListenerProviderFactory.kt | 28 + .../mixchange/auth/gateway/model/AuthEvent.kt | 7 + .../auth/gateway/model/UserCreatedEvent.kt | 18 + .../resources/META-INF/keycloak-server.json | 240 +++ .../org.keycloak.common.util.ResteasyProvider | 1 + ...ycloak.events.EventListenerProviderFactory | 1 + .../org.keycloak.platform.PlatformProvider | 1 + .../src/main/resources/application-docker.yml | 40 + .../src/main/resources/application.yml | 35 + .../src/main/resources/opex-realm.json | 1615 +++++++++++++++++ UserManagement/pom.xml | 14 + Wallet/.gitignore | 47 + Wallet/pom.xml | 18 + Wallet/wallet-app/.gitignore | 35 + Wallet/wallet-app/Dockerfile | 5 + Wallet/wallet-app/mvnw | 310 ++++ Wallet/wallet-app/mvnw.cmd | 182 ++ Wallet/wallet-app/pom.xml | 223 +++ .../nilin/mixchange/wallet/app/WalletApp.kt | 13 + .../mixchange/wallet/app/config/AppConfig.kt | 55 + .../wallet/app/config/AppDispatchers.kt | 8 + .../mixchange/wallet/app/controller/Symbol.kt | 17 + .../app/controller/TransferController.kt | 47 + .../wallet/app/controller/WalletController.kt | 40 + .../wallet/app/listener/WalletListenerImpl.kt | 24 + .../app/service/UserRegistrationService.kt | 34 + .../src/main/resources/application-docker.yml | 30 + .../src/main/resources/application.yml | 32 + Wallet/wallet-core/.gitignore | 35 + Wallet/wallet-core/mvnw | 310 ++++ Wallet/wallet-core/mvnw.cmd | 182 ++ Wallet/wallet-core/pom.xml | 75 + .../core/exc/CurrencyNotMatchedException.kt | 3 + .../core/exc/DepositLimitExceededException.kt | 3 + .../core/exc/NotEnoughBalanceException.kt | 3 + .../exc/WithdrawLimitExceededException.kt | 3 + .../wallet/core/inout/TransferCommand.kt | 6 + .../wallet/core/inout/TransferResult.kt | 6 + .../mixchange/wallet/core/model/Amount.kt | 5 + .../mixchange/wallet/core/model/Currency.kt | 7 + .../wallet/core/model/Transaction.kt | 5 + .../mixchange/wallet/core/model/Wallet.kt | 9 + .../wallet/core/model/WalletOwner.kt | 8 + .../wallet/core/service/TransferService.kt | 74 + .../wallet/core/spi/CurrencyRateService.kt | 9 + .../wallet/core/spi/TransactionManager.kt | 7 + .../wallet/core/spi/WalletListener.kt | 10 + .../wallet/core/spi/WalletManager.kt | 21 + .../wallet/core/spi/WalletOwnerManager.kt | 11 + .../src/main/resources/application.properties | 1 + .../wallet-eventlistener-kafka/.gitignore | 36 + .../wallet-eventlistener-kafka/mvnw | 310 ++++ .../wallet-eventlistener-kafka/mvnw.cmd | 182 ++ .../wallet-eventlistener-kafka/pom.xml | 102 ++ .../mixchange/auth/gateway/model/AuthEvent.kt | 7 + .../auth/gateway/model/UserCreatedEvent.kt | 18 + .../wallet/kafka/config/WalletKafkaConfig.kt | 89 + .../consumer/UserCreatedKafkaListener.kt | 28 + .../kafka/spi/UserCreatedEventListener.kt | 8 + .../wallet-persister-postgres/.gitignore | 36 + .../wallet-persister-postgres/mvnw | 310 ++++ .../wallet-persister-postgres/mvnw.cmd | 182 ++ .../wallet-persister-postgres/pom.xml | 105 ++ .../wallet/postgres/config/PostgresConfig.kt | 109 ++ .../postgres/dao/CurrencyRateRepository.kt | 18 + .../wallet/postgres/dao/CurrencyRepository.kt | 16 + .../postgres/dao/TransactionRepository.kt | 73 + .../postgres/dao/UserLimitsRepository.kt | 26 + .../postgres/dao/WalletConfigRepository.kt | 12 + .../postgres/dao/WalletLimitsRepository.kt | 35 + .../postgres/dao/WalletOwnerRepository.kt | 14 + .../wallet/postgres/dao/WalletRepository.kt | 22 + .../port/wallet/postgres/dto/SavedWallet.kt | 30 + .../wallet/postgres/dto/TransactionStat.kt | 7 + .../postgres/impl/CurrencyRateServiceImpl.kt | 32 + .../postgres/impl/TransactionManagerImpl.kt | 27 + .../wallet/postgres/impl/WalletManagerImpl.kt | 154 ++ .../postgres/impl/WalletOwnerManagerImpl.kt | 149 ++ .../wallet/postgres/model/CurrencyModel.kt | 27 + .../postgres/model/CurrencyRateModel.kt | 14 + .../wallet/postgres/model/TransactionModel.kt | 19 + .../wallet/postgres/model/UserLimitsModel.kt | 19 + .../postgres/model/WalletConfigModel.kt | 9 + .../postgres/model/WalletLimitsModel.kt | 21 + .../port/wallet/postgres/model/WalletModel.kt | 15 + .../wallet/postgres/model/WalletOwnerModel.kt | 30 + 326 files changed, 22350 insertions(+) create mode 100644 Accountant/.gitignore create mode 100644 Accountant/accountant-app/.gitignore create mode 100644 Accountant/accountant-app/Dockerfile create mode 100644 Accountant/accountant-app/pom.xml create mode 100644 Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/AccountantApp.kt create mode 100644 Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt create mode 100644 Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/config/AppDispatchers.kt create mode 100644 Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/controller/AccountantController.kt create mode 100644 Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/scheduler/FinancialActionsJob.kt create mode 100644 Accountant/accountant-app/src/main/resources/application-docker.yml create mode 100644 Accountant/accountant-app/src/main/resources/application.yml create mode 100644 Accountant/accountant-core/.gitignore create mode 100755 Accountant/accountant-core/mvnw create mode 100644 Accountant/accountant-core/mvnw.cmd create mode 100644 Accountant/accountant-core/pom.xml create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/api/FinancialActionJobManager.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/api/OrderManager.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/api/TradeManager.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/FinancialAction.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/Order.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/PairConfig.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/PairFeeConfig.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/FinancialActionJobManagerImpl.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImpl.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/TradeManagerImpl.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/FinancialActionLoader.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/FinancialActionPersister.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/OrderPersister.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/PairConfigLoader.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/PairStaticRateLoader.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/TempEventPersister.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/TempEventRepublisher.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/WalletProxy.kt create mode 100644 Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/MockitoHelper.kt create mode 100644 Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImplTest.kt create mode 100644 Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/TradeManagerImplTest.kt create mode 100644 Accountant/accountant-ports/.gitignore create mode 100644 Accountant/accountant-ports/accountant-eventlistener-kafka/.gitignore create mode 100755 Accountant/accountant-ports/accountant-eventlistener-kafka/mvnw create mode 100644 Accountant/accountant-ports/accountant-eventlistener-kafka/mvnw.cmd create mode 100644 Accountant/accountant-ports/accountant-eventlistener-kafka/pom.xml create mode 100644 Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/config/AccountantKafkaConfig.kt create mode 100644 Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/EventKafkaListener.kt create mode 100644 Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/OrderKafkaListener.kt create mode 100644 Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/TempEventKafkaListener.kt create mode 100644 Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/TradeKafkaListener.kt create mode 100644 Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/EventListener.kt create mode 100644 Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/OrderSubmitRequestListener.kt create mode 100644 Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/TempEventListener.kt create mode 100644 Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/TradeListener.kt create mode 100644 Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/.gitignore create mode 100755 Accountant/accountant-ports/accountant-persister-postgres/mvnw create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/mvnw.cmd create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/pom.xml create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/config/PostgresConfig.kt create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/FinancialActionRepository.kt create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/OrderRepository.kt create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/PairConfigRepository.kt create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/PairFeeConfigRepository.kt create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/TempEventRepository.kt create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/FinancialActionLoaderImpl.kt create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/FinancialActionPersisterImpl.kt create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/OrderPersisterImpl.kt create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/PairConfigLoaderImpl.kt create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/PairStaticRateLoaderImpl.kt create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/TempEventPersisterImpl.kt create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/FinancialActionModel.kt create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/OrderModel.kt create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/PairConfigModel.kt create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/PairFeeConfigModel.kt create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/TempEventModel.kt create mode 100644 Accountant/accountant-ports/accountant-tempsubmitter-kafka/.gitignore create mode 100755 Accountant/accountant-ports/accountant-tempsubmitter-kafka/mvnw create mode 100644 Accountant/accountant-ports/accountant-tempsubmitter-kafka/mvnw.cmd create mode 100644 Accountant/accountant-ports/accountant-tempsubmitter-kafka/pom.xml create mode 100644 Accountant/accountant-ports/accountant-tempsubmitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/config/SubmitterKafkaConfig.kt create mode 100644 Accountant/accountant-ports/accountant-tempsubmitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/TempEventSubmitter.kt create mode 100644 Accountant/accountant-ports/accountant-wallet-proxy/.gitignore create mode 100755 Accountant/accountant-ports/accountant-wallet-proxy/mvnw create mode 100644 Accountant/accountant-ports/accountant-wallet-proxy/mvnw.cmd create mode 100644 Accountant/accountant-ports/accountant-wallet-proxy/pom.xml create mode 100644 Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/mixchange/port/accountant/wallet/config/WebClientConfig.kt create mode 100644 Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/mixchange/port/accountant/wallet/proxy/WalletProxyImpl.kt create mode 100644 Accountant/pom.xml create mode 100644 Deployment/.gitignore create mode 100644 Deployment/docker-compose.yml create mode 100644 EventLog/.gitignore create mode 100644 EventLog/eventlog-app/.gitignore create mode 100644 EventLog/eventlog-app/Dockerfile create mode 100644 EventLog/eventlog-app/pom.xml create mode 100644 EventLog/eventlog-app/src/main/kotlin/co/nilin/mixchange/eventlog/app/EventLogApp.kt create mode 100644 EventLog/eventlog-app/src/main/kotlin/co/nilin/mixchange/eventlog/app/config/AppConfig.kt create mode 100644 EventLog/eventlog-app/src/main/resources/application-docker.yml create mode 100644 EventLog/eventlog-app/src/main/resources/application.yml create mode 100644 EventLog/eventlog-core/.gitignore create mode 100644 EventLog/eventlog-core/pom.xml create mode 100644 EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Event.kt create mode 100644 EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/EventPersister.kt create mode 100644 EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Order.kt create mode 100644 EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/OrderPersister.kt create mode 100644 EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Trade.kt create mode 100644 EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/TradePersister.kt create mode 100644 EventLog/eventlog-ports/eventlog-eventlistener-kafka/.gitignore create mode 100755 EventLog/eventlog-ports/eventlog-eventlistener-kafka/mvnw create mode 100644 EventLog/eventlog-ports/eventlog-eventlistener-kafka/mvnw.cmd create mode 100644 EventLog/eventlog-ports/eventlog-eventlistener-kafka/pom.xml create mode 100644 EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/config/EventlogKafkaConfig.kt create mode 100644 EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/consumer/EventKafkaListener.kt create mode 100644 EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/consumer/OrderKafkaListener.kt create mode 100644 EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/consumer/TradeKafkaListener.kt create mode 100644 EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/spi/EventListener.kt create mode 100644 EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/spi/OrderSubmitRequestListener.kt create mode 100644 EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/spi/TradeListener.kt create mode 100644 EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt create mode 100644 EventLog/eventlog-ports/eventlog-persister-postgres/.gitignore create mode 100755 EventLog/eventlog-ports/eventlog-persister-postgres/mvnw create mode 100644 EventLog/eventlog-ports/eventlog-persister-postgres/mvnw.cmd create mode 100644 EventLog/eventlog-ports/eventlog-persister-postgres/pom.xml create mode 100644 EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/config/PostgresConfig.kt create mode 100644 EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/EventRepository.kt create mode 100644 EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/OrderEventRepository.kt create mode 100644 EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/OrderRepository.kt create mode 100644 EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/TradeRepository.kt create mode 100644 EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/impl/EventPersisterImpl.kt create mode 100644 EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/impl/OrderPersisterImpl.kt create mode 100644 EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/impl/TradePersisterImpl.kt create mode 100644 EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/EventModel.kt create mode 100644 EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/OrderEventsModel.kt create mode 100644 EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/OrderModel.kt create mode 100644 EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/TradeModel.kt create mode 100644 EventLog/pom.xml create mode 100644 LICENSE-THIRD-PARTY create mode 100644 MatchingEngine/.gitignore create mode 100644 MatchingEngine/matching-app/.gitignore create mode 100644 MatchingEngine/matching-app/Dockerfile create mode 100644 MatchingEngine/matching-app/pom.xml create mode 100644 MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/MatchingEngineApp.kt create mode 100644 MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/bl/ExchangeEventHandler.kt create mode 100644 MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/bl/OrderBooks.kt create mode 100644 MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt create mode 100644 MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/config/AppSchedulers.kt create mode 100644 MatchingEngine/matching-app/src/main/resources/application-docker.yml create mode 100644 MatchingEngine/matching-app/src/main/resources/application.yml create mode 100644 MatchingEngine/matching-core/.gitignore create mode 100755 MatchingEngine/matching-core/mvnw create mode 100644 MatchingEngine/matching-core/mvnw.cmd create mode 100644 MatchingEngine/matching-core/pom.xml create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/engine/SimpleOrderBook.kt create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/EventDispatcher.kt create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/CancelOrderEvent.kt create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/CoreEvent.kt create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/CreateOrderEvent.kt create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/OneOrderEvent.kt create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/RejectOrderEvent.kt create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/SubmitOrderEvent.kt create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/TradeEvent.kt create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/UpdatedOrderEvent.kt create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/factory/OrderBookFactory.kt create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderCancelCommand.kt create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderCreateCommand.kt create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderEditCommand.kt create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/RejectReason.kt create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/RequestedOperation.kt create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/Order.kt create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/OrderBook.kt create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/OrderMetaData.kt create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/Pair.kt create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/PersistentOrder.kt create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/PersistentOrderBook.kt create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/spi/OrderBookPersister.kt create mode 100644 MatchingEngine/matching-core/src/test/kotlin/co/nilin/mixchange/matching/core/engine/SimpleOrderBookUnitTest.kt create mode 100644 MatchingEngine/matching-ports/matching-eventlistener-kafka/.gitignore create mode 100755 MatchingEngine/matching-ports/matching-eventlistener-kafka/mvnw create mode 100644 MatchingEngine/matching-ports/matching-eventlistener-kafka/mvnw.cmd create mode 100644 MatchingEngine/matching-ports/matching-eventlistener-kafka/pom.xml create mode 100644 MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/config/OrderKafkaConfig.kt create mode 100644 MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/consumer/OrderKafkaListener.kt create mode 100644 MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt create mode 100644 MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitResult.kt create mode 100644 MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/spi/OrderSubmitRequestListener.kt create mode 100644 MatchingEngine/matching-ports/matching-snapshots-redis/.gitignore create mode 100755 MatchingEngine/matching-ports/matching-snapshots-redis/mvnw create mode 100644 MatchingEngine/matching-ports/matching-snapshots-redis/mvnw.cmd create mode 100644 MatchingEngine/matching-ports/matching-snapshots-redis/pom.xml create mode 100644 MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/mixchange/port/order/redis/config/RedisConfig.kt create mode 100644 MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/mixchange/port/order/redis/service/OrderBookPersister.kt create mode 100644 MatchingEngine/matching-ports/matching-submitter-kafka/.gitignore create mode 100755 MatchingEngine/matching-ports/matching-submitter-kafka/mvnw create mode 100644 MatchingEngine/matching-ports/matching-submitter-kafka/mvnw.cmd create mode 100644 MatchingEngine/matching-ports/matching-submitter-kafka/pom.xml create mode 100644 MatchingEngine/matching-ports/matching-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/config/EventsKafkaConfig.kt create mode 100644 MatchingEngine/matching-ports/matching-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/service/EventsSubmitter.kt create mode 100644 MatchingEngine/pom.xml create mode 100644 MatchingGateway/.gitignore create mode 100644 MatchingGateway/gateway-app/.gitignore create mode 100644 MatchingGateway/gateway-app/Dockerfile create mode 100644 MatchingGateway/gateway-app/pom.xml create mode 100644 MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/MatchingGatewayApp.kt create mode 100644 MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt create mode 100644 MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/SecurityConfig.kt create mode 100644 MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/WebClientConfig.kt create mode 100644 MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/controller/ControllerExceptionHandler.kt create mode 100644 MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/controller/OrderController.kt create mode 100644 MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/exception/NotAllowedToSubmitOrderException.kt create mode 100644 MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/inout/CreateOrderRequest.kt create mode 100644 MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/inout/PairConfig.kt create mode 100644 MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/inout/PairFeeConfig.kt create mode 100644 MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/proxy/AccountantProxyImpl.kt create mode 100644 MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/service/OrderService.kt create mode 100644 MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/spi/AccountantApiProxy.kt create mode 100644 MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/spi/PairConfigLoader.kt create mode 100644 MatchingGateway/gateway-app/src/main/resources/application-docker.yml create mode 100644 MatchingGateway/gateway-app/src/main/resources/application.yml create mode 100644 MatchingGateway/gateway-app/src/main/resources/public.cert create mode 100644 MatchingGateway/gateway-port/order-submitter-kafka/.gitignore create mode 100755 MatchingGateway/gateway-port/order-submitter-kafka/mvnw create mode 100644 MatchingGateway/gateway-port/order-submitter-kafka/mvnw.cmd create mode 100644 MatchingGateway/gateway-port/order-submitter-kafka/pom.xml create mode 100644 MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/config/OrderKafkaConfig.kt create mode 100644 MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt create mode 100644 MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitResult.kt create mode 100644 MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/service/OrderSubmitter.kt create mode 100644 MatchingGateway/pom.xml create mode 100644 UserManagement/.gitignore create mode 100644 UserManagement/keycloak-gateway/.gitignore create mode 100644 UserManagement/keycloak-gateway/Dockerfile create mode 100644 UserManagement/keycloak-gateway/pom.xml create mode 100644 UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/ApplicationContextHolder.kt create mode 100644 UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/KeycloakGatewayApp.kt create mode 100644 UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/AppConfig.kt create mode 100644 UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakApplication.kt create mode 100644 UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakConfig.kt create mode 100644 UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakRequestFilter.kt create mode 100644 UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/KafkaConfig.kt create mode 100644 UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/KeycloakServerProperties.kt create mode 100644 UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/RegularJsonConfigProviderFactory.kt create mode 100644 UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/Resteasy3Provider.kt create mode 100644 UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/SimplePlatformProvider.kt create mode 100644 UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/extension/ExtendedEventListenerProvider.kt create mode 100644 UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/extension/ExtendedEventListenerProviderFactory.kt create mode 100644 UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/AuthEvent.kt create mode 100644 UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/UserCreatedEvent.kt create mode 100644 UserManagement/keycloak-gateway/src/main/resources/META-INF/keycloak-server.json create mode 100644 UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.common.util.ResteasyProvider create mode 100644 UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory create mode 100644 UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.platform.PlatformProvider create mode 100644 UserManagement/keycloak-gateway/src/main/resources/application-docker.yml create mode 100644 UserManagement/keycloak-gateway/src/main/resources/application.yml create mode 100644 UserManagement/keycloak-gateway/src/main/resources/opex-realm.json create mode 100644 UserManagement/pom.xml create mode 100644 Wallet/.gitignore create mode 100644 Wallet/pom.xml create mode 100644 Wallet/wallet-app/.gitignore create mode 100644 Wallet/wallet-app/Dockerfile create mode 100755 Wallet/wallet-app/mvnw create mode 100644 Wallet/wallet-app/mvnw.cmd create mode 100644 Wallet/wallet-app/pom.xml create mode 100644 Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/WalletApp.kt create mode 100644 Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/AppConfig.kt create mode 100644 Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/AppDispatchers.kt create mode 100644 Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/Symbol.kt create mode 100644 Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/TransferController.kt create mode 100644 Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/WalletController.kt create mode 100644 Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/listener/WalletListenerImpl.kt create mode 100644 Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/service/UserRegistrationService.kt create mode 100644 Wallet/wallet-app/src/main/resources/application-docker.yml create mode 100644 Wallet/wallet-app/src/main/resources/application.yml create mode 100644 Wallet/wallet-core/.gitignore create mode 100755 Wallet/wallet-core/mvnw create mode 100644 Wallet/wallet-core/mvnw.cmd create mode 100644 Wallet/wallet-core/pom.xml create mode 100644 Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/CurrencyNotMatchedException.kt create mode 100644 Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/DepositLimitExceededException.kt create mode 100644 Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/NotEnoughBalanceException.kt create mode 100644 Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/WithdrawLimitExceededException.kt create mode 100644 Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/inout/TransferCommand.kt create mode 100644 Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/inout/TransferResult.kt create mode 100644 Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Amount.kt create mode 100644 Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Currency.kt create mode 100644 Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Transaction.kt create mode 100644 Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Wallet.kt create mode 100644 Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/WalletOwner.kt create mode 100644 Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/service/TransferService.kt create mode 100644 Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/CurrencyRateService.kt create mode 100644 Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/TransactionManager.kt create mode 100644 Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletListener.kt create mode 100644 Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletManager.kt create mode 100644 Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletOwnerManager.kt create mode 100644 Wallet/wallet-core/src/main/resources/application.properties create mode 100644 Wallet/wallet-ports/wallet-eventlistener-kafka/.gitignore create mode 100755 Wallet/wallet-ports/wallet-eventlistener-kafka/mvnw create mode 100644 Wallet/wallet-ports/wallet-eventlistener-kafka/mvnw.cmd create mode 100644 Wallet/wallet-ports/wallet-eventlistener-kafka/pom.xml create mode 100644 Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/AuthEvent.kt create mode 100644 Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/UserCreatedEvent.kt create mode 100644 Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/config/WalletKafkaConfig.kt create mode 100644 Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/consumer/UserCreatedKafkaListener.kt create mode 100644 Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/spi/UserCreatedEventListener.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/.gitignore create mode 100755 Wallet/wallet-ports/wallet-persister-postgres/mvnw create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/mvnw.cmd create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/pom.xml create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/config/PostgresConfig.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/CurrencyRateRepository.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/CurrencyRepository.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/TransactionRepository.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/UserLimitsRepository.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletConfigRepository.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletLimitsRepository.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletOwnerRepository.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletRepository.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dto/SavedWallet.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dto/TransactionStat.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/CurrencyRateServiceImpl.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/TransactionManagerImpl.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/WalletManagerImpl.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/WalletOwnerManagerImpl.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/CurrencyModel.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/CurrencyRateModel.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/TransactionModel.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/UserLimitsModel.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletConfigModel.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletLimitsModel.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletModel.kt create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletOwnerModel.kt diff --git a/Accountant/.gitignore b/Accountant/.gitignore new file mode 100644 index 000000000..797b705f6 --- /dev/null +++ b/Accountant/.gitignore @@ -0,0 +1 @@ +*.iml \ No newline at end of file diff --git a/Accountant/accountant-app/.gitignore b/Accountant/accountant-app/.gitignore new file mode 100644 index 000000000..e7c460346 --- /dev/null +++ b/Accountant/accountant-app/.gitignore @@ -0,0 +1,80 @@ +# Created by .ignore support plugin (hsz.mobi) +### Kotlin template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +.idea/artifacts +.idea/compiler.xml +.idea/jarRepositories.xml +.idea/modules.xml +.idea/*.iml +.idea/modules +*.iml +*.ipr +.mvn/ + +# File-based project format +*.iws + +# IntelliJ +out/ + +target/ + +.DS_Store + + + + diff --git a/Accountant/accountant-app/Dockerfile b/Accountant/accountant-app/Dockerfile new file mode 100644 index 000000000..f2cbd4c26 --- /dev/null +++ b/Accountant/accountant-app/Dockerfile @@ -0,0 +1,5 @@ +FROM openjdk:8-jdk-alpine +VOLUME /tmp +ARG JAR_FILE=target/*.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"] \ No newline at end of file diff --git a/Accountant/accountant-app/pom.xml b/Accountant/accountant-app/pom.xml new file mode 100644 index 000000000..a77a86ad9 --- /dev/null +++ b/Accountant/accountant-app/pom.xml @@ -0,0 +1,193 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + accountant-app + 0.0.1-SNAPSHOT + accountant-app + Accountant app Mixchange + + + 1.8 + 1.4.31 + ${version} + + + + + org.springframework.boot + spring-boot-starter + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + com.fasterxml.jackson.module + jackson-module-kotlin + + + org.springframework.boot + spring-boot-starter + + + co.nilin.mixchange + accountant-core + ${accountant.version} + + + co.nilin.mixchange + accountant-eventlistener-kafka + ${accountant.version} + + + co.nilin.mixchange + accountant-temp-submitter-kafka + ${accountant.version} + + + co.nilin.mixchange + accountant-persister-postgres + ${accountant.version} + + + co.nilin.mixchange + accountant-wallet-proxy + ${accountant.version} + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18 + + + ${skip.unit.tests} + + + **/*IntegrationTest.java + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-test-source + generate-test-sources + + add-test-source + + + + src/test/java + + + + + compile + + add-source + + + + src/main/java + + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + + -Xjsr305=strict + + + spring + + 1.8 + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + compile + compile + + compile + + + + testCompile + test-compile + + testCompile + + + + + + opex-accountant + + diff --git a/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/AccountantApp.kt b/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/AccountantApp.kt new file mode 100644 index 000000000..26b20dad6 --- /dev/null +++ b/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/AccountantApp.kt @@ -0,0 +1,12 @@ +package co.nilin.mixchange.app + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.runApplication +import org.springframework.context.annotation.ComponentScan + +@SpringBootApplication +@ComponentScan("co.nilin.mixchange") +class AccountantApp +fun main(args: Array) { + runApplication(*args) +} \ No newline at end of file diff --git a/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt b/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt new file mode 100644 index 000000000..400b2cd24 --- /dev/null +++ b/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt @@ -0,0 +1,235 @@ +package co.nilin.mixchange.app.config + +import co.nilin.mixchange.accountant.core.api.FinancialActionJobManager +import co.nilin.mixchange.accountant.core.api.OrderManager +import co.nilin.mixchange.accountant.core.api.TradeManager +import co.nilin.mixchange.accountant.core.service.FinancialActionJobManagerImpl +import co.nilin.mixchange.accountant.core.service.OrderManagerImpl +import co.nilin.mixchange.accountant.core.service.TradeManagerImpl +import co.nilin.mixchange.accountant.core.spi.* +import co.nilin.mixchange.matching.core.eventh.events.* +import co.nilin.mixchange.port.accountant.kafka.consumer.OrderKafkaListener +import co.nilin.mixchange.port.accountant.kafka.spi.OrderSubmitRequestListener +import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest +import co.nilin.mixchange.port.trade.consumer.EventKafkaListener +import co.nilin.mixchange.port.trade.consumer.TempEventKafkaListener +import co.nilin.mixchange.port.trade.consumer.TradeKafkaListener +import co.nilin.mixchange.port.trade.spi.EventListener +import co.nilin.mixchange.port.trade.spi.TempEventListener +import co.nilin.mixchange.port.trade.spi.TradeListener +import kotlinx.coroutines.runBlocking +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.scheduling.annotation.EnableScheduling +import java.lang.IllegalArgumentException + +@Configuration +@EnableScheduling +class AppConfig { + @Bean + fun getFinancialActionJobManager( + financialActionLoader: FinancialActionLoader, + financialActionPersister: FinancialActionPersister, + walletProxy: WalletProxy + ): FinancialActionJobManager { + return FinancialActionJobManagerImpl(financialActionLoader, financialActionPersister, walletProxy) + } + + @Bean + fun orderManager( + pairConfigLoader: PairConfigLoader, + financialActionPersister: FinancialActionPersister, + financeActionLoader: FinancialActionLoader, + orderPersister: OrderPersister, + tempEventPersister: TempEventPersister, + tempEventRepublisher: TempEventRepublisher + ): OrderManager { + return OrderManagerImpl( + pairConfigLoader, + financialActionPersister, + financeActionLoader, + orderPersister, + tempEventPersister, + tempEventRepublisher + ) + } + + @Bean + fun tradeManager( + pairStaticRateLoader: PairStaticRateLoader, + financeActionPersister: FinancialActionPersister, + financeActionLoader: FinancialActionLoader, + orderPersister: OrderPersister, + tempEventPersister: TempEventPersister, + walletProxy: WalletProxy, + @Value("\${app.coin}") platformCoin: String, + @Value("\${app.address}") platformAddress: String + ): TradeManager { + return TradeManagerImpl( + pairStaticRateLoader, + financeActionPersister, + financeActionLoader, + orderPersister, + tempEventPersister, + walletProxy, + platformCoin, + platformAddress + ) + } + + @Bean + fun orderListener(orderManager: OrderManager): OrderListener { + return OrderListener(orderManager) + } + + @Bean + fun accountantTradeListener(tradeManager: TradeManager): AccountantTradeListener { + return AccountantTradeListener(tradeManager) + } + + @Bean + fun accountantEventListener( + orderManager: OrderManager + ): AccountantEventListener { + return AccountantEventListener(orderManager) + } + + @Bean + fun accountantTempEventListener( + orderManager: OrderManager, + tradeManager: TradeManager + ): AccountantTempEventListener { + return AccountantTempEventListener(orderManager, tradeManager) + } + + @Bean + fun orderKafkaListener(): OrderKafkaListener { + return OrderKafkaListener() + } + + @Autowired + fun configureOrderListener(orderKafkaListener: OrderKafkaListener, orderListener: OrderListener) { + orderKafkaListener.addOrderListener(orderListener) + } + + @Autowired + fun configureTradeListener( + tradeKafkaListener: TradeKafkaListener, + accountantTradeListener: AccountantTradeListener + ) { + tradeKafkaListener.addTradeListener(accountantTradeListener) + } + + @Autowired + fun configureEventListener( + eventKafkaListener: EventKafkaListener, + accountantEventListener: AccountantEventListener + ) { + eventKafkaListener.addEventListener(accountantEventListener) + } + + @Autowired + fun configureTempEventListener( + tempEventKafkaListener: TempEventKafkaListener, + accountantTempEventListener: AccountantTempEventListener + ) { + tempEventKafkaListener.addEventListener(accountantTempEventListener) + } + + class OrderListener(val orderManager: OrderManager) : OrderSubmitRequestListener { + + override fun id(): String { + return "OrderListener" + } + + override fun onOrder(order: OrderSubmitRequest, partition: Int, offset: Long, timestamp: Long) { + runBlocking(AppDispatchers.kafkaExecutor) { + orderManager.handleRequestOrder( + SubmitOrderEvent( + order.ouid, + order.uuid, + order.orderId, + order.pair, + order.price, + order.quantity, + 0, + order.direction, + order.matchConstraint, + order.orderType + ) + ) + } + } + } + + class AccountantTradeListener(val tradeManager: TradeManager) : TradeListener { + + override fun id(): String { + return "TradeListener" + } + + override fun onTrade(tradeEvent: TradeEvent, partition: Int, offset: Long, timestamp: Long) { + runBlocking(AppDispatchers.kafkaExecutor) { + tradeManager.handleTrade(tradeEvent) + } + } + } + + class AccountantEventListener( + val orderManager: OrderManager + ) : EventListener { + + override fun id(): String { + return "EventListener" + } + + override fun onEvent(coreEvent: CoreEvent, partition: Int, offset: Long, timestamp: Long) { + runBlocking(AppDispatchers.kafkaExecutor) { + if (coreEvent is CreateOrderEvent) + orderManager.handleNewOrder(coreEvent) + else if (coreEvent is RejectOrderEvent) + orderManager.handleRejectOrder(coreEvent) + else if (coreEvent is UpdatedOrderEvent) + orderManager.handleUpdateOrder(coreEvent) + else if (coreEvent is CancelOrderEvent) + orderManager.handleCancelOrder(coreEvent) + else { + throw IllegalArgumentException("Event is not accepted ${coreEvent::class.java}") + } + } + println("onEvent") + } + } + + class AccountantTempEventListener( + val orderManager: OrderManager, + val tradeManager: TradeManager + ) : TempEventListener { + + override fun id(): String { + return "TempEventListener" + } + + override fun onEvent(coreEvent: CoreEvent, partition: Int, offset: Long, timestamp: Long) { + println("TempEvent " + coreEvent) + runBlocking(AppDispatchers.kafkaExecutor) { + if (coreEvent is CreateOrderEvent) + orderManager.handleNewOrder(coreEvent) + else if (coreEvent is RejectOrderEvent) + orderManager.handleRejectOrder(coreEvent) + else if (coreEvent is UpdatedOrderEvent) + orderManager.handleUpdateOrder(coreEvent) + else if (coreEvent is CancelOrderEvent) + orderManager.handleCancelOrder(coreEvent) + else if (coreEvent is TradeEvent) + tradeManager.handleTrade(coreEvent) + else { + throw IllegalArgumentException("Event is not accepted ${coreEvent::class.java}") + } + } + println("onEvent") + } + } +} \ No newline at end of file diff --git a/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/config/AppDispatchers.kt b/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/config/AppDispatchers.kt new file mode 100644 index 000000000..a247a4012 --- /dev/null +++ b/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/config/AppDispatchers.kt @@ -0,0 +1,8 @@ +package co.nilin.mixchange.app.config + +import kotlinx.coroutines.asCoroutineDispatcher +import java.util.concurrent.Executors + +object AppDispatchers { + val kafkaExecutor = Executors.newSingleThreadExecutor().asCoroutineDispatcher() +} \ No newline at end of file diff --git a/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/controller/AccountantController.kt b/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/controller/AccountantController.kt new file mode 100644 index 000000000..c690fb3c6 --- /dev/null +++ b/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/controller/AccountantController.kt @@ -0,0 +1,46 @@ +package co.nilin.mixchange.app.controller + +import co.nilin.mixchange.accountant.core.model.PairFeeConfig +import co.nilin.mixchange.accountant.core.spi.FinancialActionLoader +import co.nilin.mixchange.accountant.core.spi.PairConfigLoader +import co.nilin.mixchange.accountant.core.spi.WalletProxy +import co.nilin.mixchange.matching.core.eventh.events.SubmitOrderEvent +import co.nilin.mixchange.matching.core.model.OrderDirection +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController +import java.math.BigDecimal + +@RestController +class AccountantController( + val walletProxy: WalletProxy, + val financialActionLoader: FinancialActionLoader, + val pairConfigLoader: PairConfigLoader +) { + data class BooleanResponse(val result: Boolean) + + @GetMapping("{uuid}/create_order/{amount}_{currency}/allowed") + suspend fun canCreateOrder( + @PathVariable("uuid") uuid: String, + @PathVariable("currency") currency: String, + @PathVariable("amount") amount: BigDecimal + ): BooleanResponse { + return BooleanResponse( + financialActionLoader.countUnprocessed(uuid, currency, SubmitOrderEvent::class.simpleName!!) <= 0 + && walletProxy.canFulfil(currency, "main", uuid, amount) + ) + } + + @GetMapping( + value = ["/config/{pair}/fee/{direction}-{userLevel}", "/config/{pair}/fee/{direction}"] + ) + suspend fun fetchPairFeeConfig( + @PathVariable("pair") pair: String, + @PathVariable("direction") direction: OrderDirection, + @PathVariable("userLevel") level: String? + ): PairFeeConfig { + return pairConfigLoader.load(pair, direction, level ?: "") + } + +} \ No newline at end of file diff --git a/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/scheduler/FinancialActionsJob.kt b/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/scheduler/FinancialActionsJob.kt new file mode 100644 index 000000000..e0d206482 --- /dev/null +++ b/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/scheduler/FinancialActionsJob.kt @@ -0,0 +1,21 @@ +package co.nilin.mixchange.app.scheduler + +import co.nilin.mixchange.accountant.core.api.FinancialActionJobManager +import kotlinx.coroutines.runBlocking +import org.springframework.scheduling.annotation.Scheduled +import org.springframework.stereotype.Service + +@Service +class FinancialActionsJob( + val financialActionJobManager: FinancialActionJobManager +) { + + @Scheduled(fixedDelay = 10000) + fun processFinancialActions() { + runBlocking { + //read unprocessed fa records and call transfer + financialActionJobManager.processFinancialActions(0, 100) + } + } + +} \ No newline at end of file diff --git a/Accountant/accountant-app/src/main/resources/application-docker.yml b/Accountant/accountant-app/src/main/resources/application-docker.yml new file mode 100644 index 000000000..d679f9dd3 --- /dev/null +++ b/Accountant/accountant-app/src/main/resources/application-docker.yml @@ -0,0 +1,34 @@ +server.port: 8089 +spring: + application: + name: opex-accountant + main: + allow-bean-definition-overriding: false + kafka: + bootstrap-servers: ${KAFKA_IP_PORT} + consumer: + group-id: accountant + redis: + hostname: ${REDIS_HOST} + port: 6379 + r2dbc: + url: r2dbc:postgresql://${DB_IP_PORT}/opex_accountant + username: opex + password: hiopex + initialization-mode: always + cloud: + bootstrap: + enabled: true + consul: + host: ${CONSUL_HOST} + port: 8500 + discovery: + #healthCheckPath: ${management.context-path}/health + instance-id: ${spring.application.name}:${server.port} + healthCheckInterval: 20s + prefer-ip-address: true +app: + coin: nln + address: 1 + wallet: + url: lb://opex-wallet/ diff --git a/Accountant/accountant-app/src/main/resources/application.yml b/Accountant/accountant-app/src/main/resources/application.yml new file mode 100644 index 000000000..a0ba514c7 --- /dev/null +++ b/Accountant/accountant-app/src/main/resources/application.yml @@ -0,0 +1,33 @@ +server.port: 8089 +spring: + application: + name: opex-accountant + main: + allow-bean-definition-overriding: false + kafka: + bootstrap-servers: 192.168.178.29:9092 + consumer: + group-id: accountant + redis: + hostname: 127.0.0.1 + port: 6379 + r2dbc: + url: r2dbc:postgresql://localhost/opex_accountant + username: opex + password: hiopex + initialization-mode: always + cloud: + bootstrap: + enabled: true + consul: + port: 8500 + discovery: + #healthCheckPath: ${management.context-path}/health + instance-id: ${spring.application.name}:${server.port} + healthCheckInterval: 20s + prefer-ip-address: true +app: + coin: nln + address: 1 + wallet: + url: lb://opex-wallet/ diff --git a/Accountant/accountant-core/.gitignore b/Accountant/accountant-core/.gitignore new file mode 100644 index 000000000..f3a8317d6 --- /dev/null +++ b/Accountant/accountant-core/.gitignore @@ -0,0 +1,4 @@ +*.iml +target/ +.mvn/ +.idea/ \ No newline at end of file diff --git a/Accountant/accountant-core/mvnw b/Accountant/accountant-core/mvnw new file mode 100755 index 000000000..a16b5431b --- /dev/null +++ b/Accountant/accountant-core/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/Accountant/accountant-core/mvnw.cmd b/Accountant/accountant-core/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/Accountant/accountant-core/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/Accountant/accountant-core/pom.xml b/Accountant/accountant-core/pom.xml new file mode 100644 index 000000000..789723bb8 --- /dev/null +++ b/Accountant/accountant-core/pom.xml @@ -0,0 +1,105 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + accountant-core + 0.0.1-SNAPSHOT + accountant-core + Accountant logic of Mixchange + + + 1.8 + 1.4.31 + ${version} + + + + + org.springframework.boot + spring-boot-starter + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + exchange.core2 + collections + 0.5.1 + + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + + co.nilin.mixchange + matching-core + ${matching.version} + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + org.springframework + spring-tx + provided + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/api/FinancialActionJobManager.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/api/FinancialActionJobManager.kt new file mode 100644 index 000000000..6aa3635fe --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/api/FinancialActionJobManager.kt @@ -0,0 +1,5 @@ +package co.nilin.mixchange.accountant.core.api + +interface FinancialActionJobManager { + suspend fun processFinancialActions(offset: Long, size: Long) +} diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/api/OrderManager.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/api/OrderManager.kt new file mode 100644 index 000000000..7fb754533 --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/api/OrderManager.kt @@ -0,0 +1,12 @@ +package co.nilin.mixchange.accountant.core.api + +import co.nilin.mixchange.accountant.core.model.FinancialAction +import co.nilin.mixchange.matching.core.eventh.events.* + +interface OrderManager { + suspend fun handleRequestOrder(submitOrderEvent: SubmitOrderEvent): List + suspend fun handleNewOrder(createOrderEvent: CreateOrderEvent): List + suspend fun handleUpdateOrder(updatedOrderEvent: UpdatedOrderEvent): List + suspend fun handleRejectOrder(rejectOrderEvent: RejectOrderEvent): List + suspend fun handleCancelOrder(cancelOrderEvent: CancelOrderEvent): List +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/api/TradeManager.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/api/TradeManager.kt new file mode 100644 index 000000000..3853cab1a --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/api/TradeManager.kt @@ -0,0 +1,8 @@ +package co.nilin.mixchange.accountant.core.api + +import co.nilin.mixchange.accountant.core.model.FinancialAction +import co.nilin.mixchange.matching.core.eventh.events.TradeEvent + +interface TradeManager { + suspend fun handleTrade(trade:TradeEvent): List +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/FinancialAction.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/FinancialAction.kt new file mode 100644 index 000000000..94bb96a7d --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/FinancialAction.kt @@ -0,0 +1,41 @@ +package co.nilin.mixchange.accountant.core.model + +import java.math.BigDecimal +import java.time.LocalDateTime + + +class FinancialAction( + val id: Long? = null, + val parent: FinancialAction?, + val eventType: String, + val pointer: String, + val symbol: String, + val amount: BigDecimal, + val sender: String, + val senderWalletType: String, + val receiver: String, + val receiverWalletType: String, + val createDate: LocalDateTime +) { + constructor( parent: FinancialAction?, + eventType: String, + pointer: String, + symbol: String, + amount: BigDecimal, + sender: String, + senderWalletType: String, + receiver: String, + receiverWalletType: String, + createDate: LocalDateTime) : this(null, parent, eventType, pointer, symbol, amount, sender, senderWalletType, receiver, receiverWalletType, createDate) { + + } + + override fun toString(): String { + return "FinancialAction(id=$id, parent=$parent, eventType='$eventType', pointer='$pointer', symbol='$symbol', amount=$amount, sender='$sender', senderWalletType='$senderWalletType', receiver='$receiver', receiverWalletType='$receiverWalletType', createDate=$createDate)" + } + + +} +enum class FinancialActionStatus { + CREATED, PROCESSED, ERROR +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/Order.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/Order.kt new file mode 100644 index 000000000..1a3216988 --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/Order.kt @@ -0,0 +1,24 @@ +package co.nilin.mixchange.accountant.core.model + +import co.nilin.mixchange.matching.core.model.OrderDirection +import java.math.BigDecimal + +data class Order( + val pair: String, + val ouid: String, + var matchingEngineId: Long?, + val makerFee: Double, + val takerFee: Double, + val leftSideFraction: Double, + val rightSideFraction: Double, + val uuid: String, + val userLevel: String, + val direction: OrderDirection, + val price: Long, + val quantity: Long, + val filledQuantity: Long, + val firstTransferAmount: BigDecimal, + var remainedTransferAmount: BigDecimal, + var status: Int, + val id: Long? = null +) \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/PairConfig.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/PairConfig.kt new file mode 100644 index 000000000..1c8a0f2fa --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/PairConfig.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.accountant.core.model + +class PairConfig( + val pair: String, + val leftSideWalletSymbol: String, //can be same as pair left side + val rightSideWalletSymbol: String, //can be same as pair right side + val leftSideFraction: Double, + val rightSideFraction: Double +) \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/PairFeeConfig.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/PairFeeConfig.kt new file mode 100644 index 000000000..29c28508c --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/PairFeeConfig.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.accountant.core.model + +class PairFeeConfig( + val pairConfig: PairConfig, + val direction: String?, + val userLevel: String?, + val makerFee: Double, + val takerFee: Double +) \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/FinancialActionJobManagerImpl.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/FinancialActionJobManagerImpl.kt new file mode 100644 index 000000000..d447bd48d --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/FinancialActionJobManagerImpl.kt @@ -0,0 +1,32 @@ +package co.nilin.mixchange.accountant.core.service + +import co.nilin.mixchange.accountant.core.api.FinancialActionJobManager +import co.nilin.mixchange.accountant.core.model.FinancialActionStatus +import co.nilin.mixchange.accountant.core.spi.FinancialActionPersister +import co.nilin.mixchange.accountant.core.spi.FinancialActionLoader +import co.nilin.mixchange.accountant.core.spi.WalletProxy + +class FinancialActionJobManagerImpl(val financialActionLoader: FinancialActionLoader + , val financialActionPersister: FinancialActionPersister + , val walletProxy: WalletProxy): FinancialActionJobManager { + override suspend fun processFinancialActions(offset: Long, size: Long) { + val factions = financialActionLoader.loadUnprocessed(offset, size) + factions.forEach { faction -> + try { + walletProxy.transfer( + faction.symbol, + faction.senderWalletType, + faction.sender, + faction.receiverWalletType, + faction.receiver, + faction.amount, + faction.eventType + faction.pointer, + null + ) + financialActionPersister.updateStatus(faction, FinancialActionStatus.PROCESSED) + } catch (e: Exception){ + financialActionPersister.updateStatus(faction, FinancialActionStatus.ERROR) + } + } + } +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImpl.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImpl.kt new file mode 100644 index 000000000..3b3842454 --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImpl.kt @@ -0,0 +1,171 @@ +package co.nilin.mixchange.accountant.core.service + +import co.nilin.mixchange.accountant.core.api.OrderManager +import co.nilin.mixchange.accountant.core.model.FinancialAction +import co.nilin.mixchange.accountant.core.model.Order +import co.nilin.mixchange.accountant.core.spi.* +import co.nilin.mixchange.matching.core.eventh.events.* +import co.nilin.mixchange.matching.core.model.OrderDirection +import org.springframework.transaction.annotation.Transactional +import java.math.BigDecimal +import java.time.LocalDateTime + +open class OrderManagerImpl( + val pairConfigLoader: PairConfigLoader, + val financialActionPersister: FinancialActionPersister, + val financeActionLoader: FinancialActionLoader, + val orderPersister: OrderPersister, + val tempEventPersister: TempEventPersister, + val tempEventRepublisher: TempEventRepublisher +) : OrderManager { + @Transactional + override suspend fun handleRequestOrder(submitOrderEvent: SubmitOrderEvent): List { + //pair + dir -> symbol + //user level? + //pair config.makerFee and takerFee + val symbol = if (submitOrderEvent.direction == OrderDirection.ASK) { + submitOrderEvent.pair.leftSideName + } else { + submitOrderEvent.pair.rightSideName + } + val pairFeeConfig = pairConfigLoader.load(submitOrderEvent.pair.toString(), submitOrderEvent.direction, "") + val makerFee = pairFeeConfig.makerFee * 1 //user level formula + val takerFee = pairFeeConfig.takerFee * 1 //user level formula + + //create fa for transfer uuid symbol main wallet to uuid symbol exchange wallet + /* + amount for sell (ask): quantity + amount for buy (bid): quantity * price + */ + val financialAction = + FinancialAction( + null, + SubmitOrderEvent::class.simpleName!!, + submitOrderEvent.ouid, + symbol, + if (submitOrderEvent.direction == OrderDirection.ASK) { + BigDecimal(submitOrderEvent.quantity).multiply(pairFeeConfig.pairConfig.leftSideFraction.toBigDecimal()) + } else { + BigDecimal(submitOrderEvent.quantity).multiply(pairFeeConfig.pairConfig.leftSideFraction.toBigDecimal()) + .multiply(submitOrderEvent.price.toBigDecimal()) + .multiply(pairFeeConfig.pairConfig.rightSideFraction.toBigDecimal()) + }, + submitOrderEvent.uuid, + "main", + submitOrderEvent.uuid, + "exchange", + LocalDateTime.now() + ) + //store order (ouid, uuid, fees, userlevel, pair, direction, price, quantity, filledQ, status, transfered) + orderPersister.save( + Order( + submitOrderEvent.pair.toString(), + submitOrderEvent.ouid, + null, + makerFee, + takerFee, + pairFeeConfig.pairConfig.leftSideFraction, + pairFeeConfig.pairConfig.rightSideFraction, + submitOrderEvent.uuid, + "", + submitOrderEvent.direction, + submitOrderEvent.price, + submitOrderEvent.quantity, + submitOrderEvent.quantity - submitOrderEvent.remainedQuantity, + financialAction.amount, + financialAction.amount, + 0 + ) + ) + val ret = financialActionPersister.persist(listOf(financialAction)) + println("republish " + submitOrderEvent.ouid) + tempEventRepublisher.republish(tempEventPersister.loadTempEvents(submitOrderEvent.ouid)) + println("remove temp " + submitOrderEvent.ouid) + tempEventPersister.removeTempEvents(submitOrderEvent.ouid) + return ret + } + + override suspend fun handleNewOrder(createOrderEvent: CreateOrderEvent): List { + //update order add id to other fields + val order = orderPersister.load(createOrderEvent.ouid) + if ( order != null) { + order.matchingEngineId = createOrderEvent.orderId + orderPersister.save(order) + } else { + tempEventPersister.saveTempEvent(createOrderEvent.ouid, createOrderEvent) + } + return emptyList() + } + + override suspend fun handleUpdateOrder(updatedOrderEvent: UpdatedOrderEvent): List { + TODO("Not yet implemented") + } + + override suspend fun handleRejectOrder(rejectOrderEvent: RejectOrderEvent): List { + //order by ouid + val order = orderPersister.load(rejectOrderEvent.ouid) + if ( order == null ){ + tempEventPersister.saveTempEvent(rejectOrderEvent.ouid, rejectOrderEvent) + return emptyList() + } + val symbol = if (rejectOrderEvent.direction == OrderDirection.ASK) { + rejectOrderEvent.pair.leftSideName + } else { + rejectOrderEvent.pair.rightSideName + } + //check uuid + //lookup for parent fa + val parentFinancialAction = financeActionLoader.findLast(rejectOrderEvent.uuid, rejectOrderEvent.ouid) + //create fa for transfer remaining transfered uuid symbol exchange wallet to uuid main exchange wallet + val financialAction = FinancialAction( + parentFinancialAction, + RejectOrderEvent::class.simpleName!!, + rejectOrderEvent.ouid, + symbol, + order.remainedTransferAmount, + rejectOrderEvent.uuid, + "exchange", + rejectOrderEvent.uuid, + "main", + LocalDateTime.now() + ) + //update order status + order.status = 3 + orderPersister.save(order) + return financialActionPersister.persist(listOf(financialAction)) + } + + override suspend fun handleCancelOrder(cancelOrderEvent: CancelOrderEvent): List { + //order by ouid + val order = orderPersister.load(cancelOrderEvent.ouid) + if ( order == null ){ + tempEventPersister.saveTempEvent(cancelOrderEvent.ouid, cancelOrderEvent) + return emptyList() + } + val symbol = if (cancelOrderEvent.direction == OrderDirection.ASK) { + cancelOrderEvent.pair.leftSideName + } else { + cancelOrderEvent.pair.rightSideName + } + //check uuid + //lookup for parent fa + val parentFinancialAction = financeActionLoader.findLast(cancelOrderEvent.uuid, cancelOrderEvent.ouid) + //create fa for transfer remaining transfered uuid symbol exchange wallet to uuid main exchange wallet + val financialAction = FinancialAction( + parentFinancialAction, + RejectOrderEvent::class.simpleName!!, + cancelOrderEvent.ouid, + symbol, + order.remainedTransferAmount, + cancelOrderEvent.uuid, + "exchange", + cancelOrderEvent.uuid, + "main", + LocalDateTime.now() + ) + //update order status + order.status = 2 + orderPersister.save(order) + return financialActionPersister.persist(listOf(financialAction)) + } +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/TradeManagerImpl.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/TradeManagerImpl.kt new file mode 100644 index 000000000..1c675fc9b --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/TradeManagerImpl.kt @@ -0,0 +1,239 @@ +package co.nilin.mixchange.accountant.core.service + +import co.nilin.mixchange.accountant.core.api.TradeManager +import co.nilin.mixchange.accountant.core.model.FinancialAction +import co.nilin.mixchange.accountant.core.spi.* +import co.nilin.mixchange.matching.core.eventh.events.TradeEvent +import co.nilin.mixchange.matching.core.model.OrderDirection +import org.slf4j.LoggerFactory +import org.springframework.transaction.annotation.Transactional +import java.lang.RuntimeException +import java.math.BigDecimal +import java.time.LocalDateTime + +open class TradeManagerImpl( + val pairStaticRateLoader: PairStaticRateLoader, + val financeActionPersister: FinancialActionPersister, + val financeActionLoader: FinancialActionLoader, + val orderPersister: OrderPersister, + val tempEventPersister: TempEventPersister, + val walletProxy: WalletProxy, + val platformCoin: String, + val platformAddress: String +) : TradeManager { + + private val log = LoggerFactory.getLogger(TradeManagerImpl::class.java) + + @Transactional + override suspend fun handleTrade(trade: TradeEvent): List { + log.info("trade event started {}", trade) + val financialActions = mutableListOf() + //taker order by ouid + val takerOrder = orderPersister.load(trade.takerOuid) + //maker order by ouid + val makerOrder = orderPersister.load(trade.makerOuid) + if ( takerOrder == null || makerOrder == null ){ + if ( takerOrder == null ){ + tempEventPersister.saveTempEvent(trade.takerOuid, trade) + } + if ( makerOrder == null ){ + tempEventPersister.saveTempEvent(trade.makerOuid, trade) + } + return emptyList() + } + //check taker uuid + // + //check maker uuid + // + val leftSidePCRate = pairStaticRateLoader.calculateStaticRate(platformCoin, trade.pair.leftSideName) + val rightSidePCRate = pairStaticRateLoader.calculateStaticRate(platformCoin, trade.pair.rightSideName) + + val takerMatchedAmount = if (takerOrder.direction == OrderDirection.ASK) { + trade.matchedQuantity.toBigDecimal().multiply(takerOrder.leftSideFraction.toBigDecimal()) + } else { + trade.matchedQuantity.toBigDecimal().multiply(takerOrder.leftSideFraction.toBigDecimal()) + .multiply(trade.makerPrice.toBigDecimal()).multiply(takerOrder.rightSideFraction.toBigDecimal()) + } + + val takerPCFeeCoefficient: Double = if (takerOrder.direction == OrderDirection.ASK) { + leftSidePCRate ?: 0.0 + } else { + rightSidePCRate ?: 0.0 + } + + val makerMatchedAmount = if (makerOrder.direction == OrderDirection.ASK) { + trade.matchedQuantity.toBigDecimal().multiply(makerOrder.leftSideFraction.toBigDecimal()) + } else { + trade.matchedQuantity.toBigDecimal().multiply(makerOrder.leftSideFraction.toBigDecimal()) + .multiply(trade.takerPrice.toBigDecimal()).multiply(makerOrder.rightSideFraction.toBigDecimal()) + } + + val makerPCFeeCoefficient: Double = if (makerOrder.direction == OrderDirection.ASK) { + leftSidePCRate ?: 0.0 + } else { + rightSidePCRate ?: 0.0 + } + log.info("trade event configs loaded ") + //lookup for taker parent fa + val takerParentFinancialAction = financeActionLoader.findLast(trade.takerUuid, trade.takerOuid) + log.info("trade event takerParentFinancialAction {} ", takerParentFinancialAction) + //lookup for maker parent fa + val makerParentFinancialAction = financeActionLoader.findLast(trade.makerUuid, trade.makerOuid) + log.info("trade event makerParentFinancialAction {} ", makerParentFinancialAction) + + + //calculate maker fee + val makerFee = makerOrder.makerFee + val makerTotalFeeWithPlatformCoin = takerMatchedAmount + .multiply(makerFee.toBigDecimal()) + .multiply(makerPCFeeCoefficient.toBigDecimal()) + //check if maker uuid can pay the fee with platform coin + //create fa for transfer taker uuid symbol exchange wallet to maker symbol main wallet + /* + amount for sell (ask): match_quantity (if not pay by platform coin then - maker fee) + amount for buy (bid): match_quantity * maker price (if not pay by platform coin then - maker fee) + */ + val takerTransferAction = FinancialAction( + takerParentFinancialAction, + TradeEvent::class.simpleName!!, + trade.takerOuid, + if (takerOrder.direction == OrderDirection.ASK) { + trade.pair.leftSideName + } else { + trade.pair.rightSideName + }, + takerMatchedAmount, + trade.takerUuid, + "exchange", + trade.makerUuid, + "main", + LocalDateTime.now() + ) + log.info("trade event takerTransferAction {}", takerTransferAction) + financialActions.add(takerTransferAction) + if (makerTotalFeeWithPlatformCoin > BigDecimal.ZERO && + walletProxy.canFulfil(platformCoin, "main", trade.makerUuid, makerTotalFeeWithPlatformCoin)) { + val makerFeeAction = FinancialAction( + makerParentFinancialAction, + TradeEvent::class.simpleName!!, + trade.makerOuid, + platformCoin, + makerTotalFeeWithPlatformCoin, + trade.makerUuid, + "main", + platformAddress, + "exchange", + LocalDateTime.now() + ) + log.info("trade event makerFeeAction {}", makerFeeAction) + financialActions.add(makerFeeAction) + } else { + val makerFeeAction = FinancialAction( + makerParentFinancialAction, + TradeEvent::class.simpleName!!, + trade.makerOuid, + if (takerOrder.direction == OrderDirection.ASK) { + trade.pair.leftSideName + } else { + trade.pair.rightSideName + }, + takerMatchedAmount.multiply(makerFee.toBigDecimal()), + trade.makerUuid, + "main", + platformAddress, + "exchange", + LocalDateTime.now() + ) + log.info("trade event makerFeeAction {}", makerFeeAction) + financialActions.add(makerFeeAction) + } + //update taker order status + takerOrder.remainedTransferAmount -= takerMatchedAmount + if (takerOrder.filledQuantity == takerOrder.quantity) { + takerOrder.status = 1 + } + orderPersister.save(takerOrder) + log.info("taker order saved {}", takerOrder) + + //calculate taker fee + val takerFee = takerOrder.takerFee + val takerTotalFeeWithPlatformCoin = takerMatchedAmount + .multiply(takerFee.toBigDecimal()) + .multiply(takerPCFeeCoefficient.toBigDecimal()) + + //create fa for transfer makeruuid symbol exchange wallet to taker symbol main wallet + /* + amount for sell (ask): match_quantity (if not pay by platform coin then - taker fee) + amount for buy (bid): match_quantity * maker price (if not pay by platform coin then - taker fee) + */ + val makerTransferAction = FinancialAction( + makerParentFinancialAction, + TradeEvent::class.simpleName!!, + trade.takerOuid, + if (makerOrder.direction == OrderDirection.ASK) { + trade.pair.leftSideName + } else { + trade.pair.rightSideName + }, + makerMatchedAmount, + trade.makerUuid, + "exchange", + trade.takerUuid, + "main", + LocalDateTime.now() + ) + log.info("trade event makerTransferAction {}", makerTransferAction) + financialActions.add(makerTransferAction) + //check if taker uuid can pay the fee with platform coin + if (takerTotalFeeWithPlatformCoin > BigDecimal.ZERO && + walletProxy.canFulfil(platformCoin, "main", trade.takerUuid, takerTotalFeeWithPlatformCoin)) { + val takerFeeAction = FinancialAction( + takerParentFinancialAction, + TradeEvent::class.simpleName!!, + trade.takerOuid, + if (makerOrder.direction == OrderDirection.ASK) { + trade.pair.leftSideName + } else { + trade.pair.rightSideName + }, + takerTotalFeeWithPlatformCoin, + trade.takerUuid, + "main", + platformAddress, + "", + LocalDateTime.now() + ) + log.info("trade event takerFeeAction {}", takerFeeAction) + financialActions.add(takerFeeAction) + + } else { + val takerFeeAction = FinancialAction( + takerParentFinancialAction, + TradeEvent::class.simpleName!!, + trade.takerOuid, + if (makerOrder.direction == OrderDirection.ASK) { + trade.pair.leftSideName + } else { + trade.pair.rightSideName + }, + makerMatchedAmount.multiply(takerFee.toBigDecimal()), + trade.takerUuid, + "main", + platformAddress, + "exchange", + LocalDateTime.now() + ) + log.info("trade event takerFeeAction {}", takerFeeAction) + + financialActions.add(takerFeeAction) + } + //update maker order status + makerOrder.remainedTransferAmount -= makerMatchedAmount + if (makerOrder.filledQuantity == makerOrder.quantity) { + makerOrder.status = 1 + } + orderPersister.save(makerOrder) + log.info("maker order saved {}", makerOrder) + return financeActionPersister.persist(financialActions) + } +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/FinancialActionLoader.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/FinancialActionLoader.kt new file mode 100644 index 000000000..e91d43180 --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/FinancialActionLoader.kt @@ -0,0 +1,10 @@ +package co.nilin.mixchange.accountant.core.spi + +import co.nilin.mixchange.accountant.core.model.FinancialAction +import kotlinx.coroutines.flow.Flow + +interface FinancialActionLoader { + suspend fun findLast(uuid: String, ouid: String): FinancialAction? + suspend fun loadUnprocessed(offset: Long, size: Long): List + suspend fun countUnprocessed(uuid: String, symbol: String, eventType: String): Long +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/FinancialActionPersister.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/FinancialActionPersister.kt new file mode 100644 index 000000000..e93ef7915 --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/FinancialActionPersister.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.accountant.core.spi + +import co.nilin.mixchange.accountant.core.model.FinancialAction +import co.nilin.mixchange.accountant.core.model.FinancialActionStatus + +interface FinancialActionPersister { + suspend fun persist(financialActions: List): List + suspend fun updateStatus(financialAction: FinancialAction, status: FinancialActionStatus ) +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/OrderPersister.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/OrderPersister.kt new file mode 100644 index 000000000..6acf4e34c --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/OrderPersister.kt @@ -0,0 +1,8 @@ +package co.nilin.mixchange.accountant.core.spi + +import co.nilin.mixchange.accountant.core.model.Order + +interface OrderPersister { + suspend fun load(ouid: String): Order? + suspend fun save(order: Order): Order +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/PairConfigLoader.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/PairConfigLoader.kt new file mode 100644 index 000000000..bcc7a449e --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/PairConfigLoader.kt @@ -0,0 +1,8 @@ +package co.nilin.mixchange.accountant.core.spi + +import co.nilin.mixchange.accountant.core.model.PairFeeConfig +import co.nilin.mixchange.matching.core.model.OrderDirection + +interface PairConfigLoader { + suspend fun load(pair: String, direction: OrderDirection, userLevel: String):PairFeeConfig +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/PairStaticRateLoader.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/PairStaticRateLoader.kt new file mode 100644 index 000000000..87c9b11de --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/PairStaticRateLoader.kt @@ -0,0 +1,5 @@ +package co.nilin.mixchange.accountant.core.spi + +interface PairStaticRateLoader { + suspend fun calculateStaticRate(leftSide:String, rightSide: String): Double? +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/TempEventPersister.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/TempEventPersister.kt new file mode 100644 index 000000000..ac1c2cd48 --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/TempEventPersister.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.accountant.core.spi + +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent + +interface TempEventPersister { + suspend fun saveTempEvent(ouid: String, event: CoreEvent) + suspend fun loadTempEvents(ouid: String): List + suspend fun removeTempEvents(ouid: String) +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/TempEventRepublisher.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/TempEventRepublisher.kt new file mode 100644 index 000000000..1bdf2a6ec --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/TempEventRepublisher.kt @@ -0,0 +1,7 @@ +package co.nilin.mixchange.accountant.core.spi + +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent + +interface TempEventRepublisher { + suspend fun republish(events: List) +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/WalletProxy.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/WalletProxy.kt new file mode 100644 index 000000000..fef9a0ee8 --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/WalletProxy.kt @@ -0,0 +1,18 @@ +package co.nilin.mixchange.accountant.core.spi + +import java.math.BigDecimal + +interface WalletProxy { + suspend fun transfer( + symbol: String, + senderWalletType: String, + senderUuid: String, + receiverWalletType: String, + receiverUuid: String, + amount: BigDecimal, + description: String?, + transferRef: String? + ) + + suspend fun canFulfil(symbol: String, walletType: String, uuid: String, amount: BigDecimal): Boolean +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/MockitoHelper.kt b/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/MockitoHelper.kt new file mode 100644 index 000000000..c2f0ca98c --- /dev/null +++ b/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/MockitoHelper.kt @@ -0,0 +1,12 @@ +package co.nilin.mixchange.accountant.core.service + +import org.mockito.Mockito + +object MockitoHelper { + fun anyObject(): T { + Mockito.any() + return uninitialized() + } + @Suppress("UNCHECKED_CAST") + fun uninitialized(): T = null as T +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImplTest.kt b/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImplTest.kt new file mode 100644 index 000000000..f50f6eaaa --- /dev/null +++ b/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImplTest.kt @@ -0,0 +1,168 @@ +package co.nilin.mixchange.accountant.core.service + +import co.nilin.mixchange.accountant.core.api.OrderManager +import co.nilin.mixchange.accountant.core.model.FinancialAction +import co.nilin.mixchange.accountant.core.model.PairConfig +import co.nilin.mixchange.accountant.core.model.PairFeeConfig +import co.nilin.mixchange.accountant.core.spi.* +import co.nilin.mixchange.matching.core.eventh.events.SubmitOrderEvent +import co.nilin.mixchange.matching.core.model.MatchConstraint +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.OrderType +import co.nilin.mixchange.matching.core.model.Pair +import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.Test + +import org.junit.jupiter.api.Assertions.* +import org.mockito.ArgumentMatchers.any +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.MockitoAnnotations +import org.mockito.stubbing.Answer +import java.time.LocalDateTime + +internal class OrderManagerImplTest() { + + @Mock + lateinit var financialActionPersister: FinancialActionPersister + + @Mock + lateinit var financialActionLoader: FinancialActionLoader + + @Mock + lateinit var orderPersister: OrderPersister + + @Mock + lateinit var tempEventPersister: TempEventPersister + + @Mock + lateinit var tempEventRepublisher: TempEventRepublisher + + @Mock + lateinit var pairConfigLoader: PairConfigLoader + val orderManager: OrderManager + + init { + MockitoAnnotations.openMocks(this) + orderManager = OrderManagerImpl( + pairConfigLoader, financialActionPersister, financialActionLoader, orderPersister, tempEventPersister, tempEventRepublisher + ) + } + + + @Test + fun givenAskOrder_whenHandleRequestOrder_thenFAMatch() { + runBlocking { + //given + val pair = Pair("eth", "btc") + val pairConfig = PairConfig( + pair.toString(), pair.leftSideName, pair.rightSideName, 1.0, 0.001 + ) + val submitOrderEvent = SubmitOrderEvent( + "ouid", "uuid", null, pair, 30, 60, 0, OrderDirection.ASK, MatchConstraint.GTC, OrderType.LIMIT_ORDER + ) + Mockito.`when`(pairConfigLoader.load(pair.toString(), submitOrderEvent.direction, "")) + .thenReturn(PairFeeConfig(pairConfig, submitOrderEvent.direction.toString(), "", 0.1, 0.12)) + Mockito.`when`(financialActionPersister.persist(MockitoHelper.anyObject())) + .then { + return@then it.getArgument>(0) + } + + //when + val financialActions = orderManager.handleRequestOrder(submitOrderEvent) + + //then + assertEquals(1, financialActions.size) + val expectedFinancialAction = FinancialAction( + null, + SubmitOrderEvent::class.simpleName!!, + submitOrderEvent.ouid, + pair.leftSideName, + pairConfig.leftSideFraction.toBigDecimal().multiply(submitOrderEvent.quantity.toBigDecimal()), + submitOrderEvent.uuid, + "main", + submitOrderEvent.uuid, + "exchange", + LocalDateTime.now() + ) + assertEquals(expectedFinancialAction.eventType, financialActions[0].eventType) + assertEquals(expectedFinancialAction.symbol, financialActions[0].symbol) + assertEquals(expectedFinancialAction.amount, financialActions[0].amount) + assertEquals(expectedFinancialAction.sender, financialActions[0].sender) + assertEquals(expectedFinancialAction.senderWalletType, financialActions[0].senderWalletType) + assertEquals(expectedFinancialAction.receiver, financialActions[0].receiver) + assertEquals(expectedFinancialAction.receiverWalletType, financialActions[0].receiverWalletType) + } + } + + @Test + fun givenBidOrder_whenHandleRequestOrder_thenFAMatch() { + runBlocking { + //given + val pair = Pair("eth", "btc") + val pairConfig = PairConfig( + pair.toString(), pair.leftSideName, pair.rightSideName, 1.0, 0.001 + ) + val submitOrderEvent = SubmitOrderEvent( + "ouid", "uuid", null, pair, 35, 14, 0, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER + ) + Mockito.`when`(pairConfigLoader.load(pair.toString(), submitOrderEvent.direction, "")) + .thenReturn( + PairFeeConfig( + pairConfig, submitOrderEvent.direction.toString(), "", 0.08, 0.1 + ) + ) + Mockito.`when`(financialActionPersister.persist(MockitoHelper.anyObject())) + .then { + return@then it.getArgument>(0) + } + + //when + val financialActions = runBlocking { + orderManager.handleRequestOrder(submitOrderEvent) + } + + //then + assertEquals(1, financialActions.size) + val expectedFinancialAction = FinancialAction( + null, + SubmitOrderEvent::class.simpleName!!, + submitOrderEvent.ouid, + pair.rightSideName, + pairConfig.leftSideFraction.toBigDecimal().multiply(submitOrderEvent.quantity.toBigDecimal()) + .multiply(pairConfig.rightSideFraction.toBigDecimal()) + .multiply(submitOrderEvent.price.toBigDecimal()), + submitOrderEvent.uuid, + "main", + submitOrderEvent.uuid, + "exchange", + LocalDateTime.now() + ) + assertEquals(expectedFinancialAction.eventType, financialActions[0].eventType) + assertEquals(expectedFinancialAction.symbol, financialActions[0].symbol) + assertEquals(expectedFinancialAction.amount, financialActions[0].amount) + assertEquals(expectedFinancialAction.sender, financialActions[0].sender) + assertEquals(expectedFinancialAction.senderWalletType, financialActions[0].senderWalletType) + assertEquals(expectedFinancialAction.receiver, financialActions[0].receiver) + assertEquals(expectedFinancialAction.receiverWalletType, financialActions[0].receiverWalletType) + } + } + + @Test + fun handleNewOrder() { + } + + @Test + fun handleUpdateOrder() { + } + + @Test + fun handleRejectOrder() { + } + + @Test + fun handleCancelOrder() { + } +} + + diff --git a/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/TradeManagerImplTest.kt b/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/TradeManagerImplTest.kt new file mode 100644 index 000000000..8f2a00c96 --- /dev/null +++ b/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/TradeManagerImplTest.kt @@ -0,0 +1,201 @@ +package co.nilin.mixchange.accountant.core.service + +import co.nilin.mixchange.accountant.core.api.OrderManager +import co.nilin.mixchange.accountant.core.api.TradeManager +import co.nilin.mixchange.accountant.core.model.FinancialAction +import co.nilin.mixchange.accountant.core.model.Order +import co.nilin.mixchange.accountant.core.model.PairConfig +import co.nilin.mixchange.accountant.core.model.PairFeeConfig +import co.nilin.mixchange.accountant.core.spi.* +import co.nilin.mixchange.matching.core.eventh.events.SubmitOrderEvent +import co.nilin.mixchange.matching.core.eventh.events.TradeEvent +import co.nilin.mixchange.matching.core.model.MatchConstraint +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.OrderType +import co.nilin.mixchange.matching.core.model.Pair +import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.MockitoAnnotations + +internal class TradeManagerImplTest() { + @Mock + lateinit var financialActionPersister: FinancialActionPersister + + @Mock + lateinit var financeActionLoader: FinancialActionLoader + + @Mock + lateinit var orderPersister: OrderPersister + + @Mock + lateinit var pairConfigLoader: PairConfigLoader + + @Mock + lateinit var pairStaticRateLoader: PairStaticRateLoader + + @Mock + lateinit var walletProxy: WalletProxy + + @Mock + lateinit var tempEventPersister: TempEventPersister + + @Mock + lateinit var tempEventRepublisher: TempEventRepublisher + + val orderManager: OrderManager + + val tradeManager: TradeManager + + + init { + MockitoAnnotations.openMocks(this) + orderManager = OrderManagerImpl( + pairConfigLoader, financialActionPersister, financeActionLoader, orderPersister, tempEventPersister, tempEventRepublisher + ) + tradeManager = TradeManagerImpl( + pairStaticRateLoader, financialActionPersister, financeActionLoader, orderPersister, tempEventPersister, walletProxy, "pcoin", "0x0" + ) + } + + @Test + fun givenMatchOrders_whenTradeCreated_thenFAMatched() { + runBlocking { + //given + val pair = Pair("eth", "btc") + val pairConfig = PairConfig( + pair.toString(), pair.leftSideName, pair.rightSideName, 1.0, 0.001 + ) + val makerSubmitOrderEvent = SubmitOrderEvent( + "mouid", "muuid", null, pair, 29, 60, 0, OrderDirection.ASK, MatchConstraint.GTC, OrderType.LIMIT_ORDER + ) + prepareOrder(pair, pairConfig, makerSubmitOrderEvent, 0.1, 0.12) + + val takerSubmitOrderEvent = SubmitOrderEvent( + "touid", "tuuid", null, pair, 30, 14, 0, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER + ) + + prepareOrder(pair, pairConfig, takerSubmitOrderEvent, 0.08, 0.1) + + val tradeEvent = makeTradeEvent(pair, takerSubmitOrderEvent, makerSubmitOrderEvent) + //when + val tradeFinancialActions = tradeManager.handleTrade(tradeEvent) + Assertions.assertEquals(4, tradeFinancialActions.size) + } + } + + @Test + fun givenMatchOrders2_whenTradeCreated_thenFAMatched() { + runBlocking { + //given + val pair = Pair("eth", "btc") + val pairConfig = PairConfig( + pair.toString(), pair.leftSideName, pair.rightSideName, 0.000001, 0.000001 + ) + val makerSubmitOrderEvent = SubmitOrderEvent( + "mouid", + "muuid", + null, + pair, + 33333, + 5000000, + 0, + OrderDirection.ASK, + MatchConstraint.GTC, + OrderType.LIMIT_ORDER + ) + prepareOrder(pair, pairConfig, makerSubmitOrderEvent, 0.8 * 0.001, 1.0 * 0.001) + + val takerSubmitOrderEvent = SubmitOrderEvent( + "touid", + "tuuid", + null, + pair, + 34482, + 1000000, + 0, + OrderDirection.BID, + MatchConstraint.GTC, + OrderType.LIMIT_ORDER + ) + + prepareOrder(pair, pairConfig, takerSubmitOrderEvent, 0.8 * 0.001, 1.0 * 0.001) + + val tradeEvent = makeTradeEvent(pair, takerSubmitOrderEvent, makerSubmitOrderEvent) + //when + val tradeFinancialActions = tradeManager.handleTrade(tradeEvent) + Assertions.assertEquals(4, tradeFinancialActions.size) + } + } + + private fun makeTradeEvent( + pair: Pair, + takerSubmitOrderEvent: SubmitOrderEvent, + makerSubmitOrderEvent: SubmitOrderEvent + ): TradeEvent { + val tradeEvent = TradeEvent( + pair, + takerSubmitOrderEvent.ouid, + takerSubmitOrderEvent.uuid, + takerSubmitOrderEvent.orderId ?: -1, + takerSubmitOrderEvent.direction, + takerSubmitOrderEvent.price, + 0, + makerSubmitOrderEvent.ouid, + makerSubmitOrderEvent.uuid, + makerSubmitOrderEvent.orderId ?: 1, + makerSubmitOrderEvent.direction, + makerSubmitOrderEvent.price, + makerSubmitOrderEvent.quantity - takerSubmitOrderEvent.quantity, + takerSubmitOrderEvent.quantity + ) + return tradeEvent + } + + private fun prepareOrder( + pair: Pair, + pairConfig: PairConfig, + submitOrderEvent: SubmitOrderEvent, + makerFee: Double, + takerFee: Double + ) { + runBlocking { + Mockito.`when`(pairConfigLoader.load(pair.toString(), submitOrderEvent.direction, "")) + .thenReturn(PairFeeConfig(pairConfig, submitOrderEvent.direction.toString(), "", makerFee, takerFee)) + Mockito.`when`(financialActionPersister.persist(MockitoHelper.anyObject())) + .then { + return@then it.getArgument>(0) + } + + val financialActions = orderManager.handleRequestOrder(submitOrderEvent) + + val orderPairFeeConfig = + pairConfigLoader.load(submitOrderEvent.pair.toString(), submitOrderEvent.direction, "") + val orderMakerFee = orderPairFeeConfig.makerFee * 1 //user level formula + val orderTakerFee = orderPairFeeConfig.takerFee * 1 //user level formula + Mockito.`when`(orderPersister.load(submitOrderEvent.ouid)).thenReturn( + Order( + submitOrderEvent.pair.toString(), + submitOrderEvent.ouid, + null, + orderMakerFee, + orderTakerFee, + orderPairFeeConfig.pairConfig.leftSideFraction, + orderPairFeeConfig.pairConfig.rightSideFraction, + submitOrderEvent.uuid, + "", + submitOrderEvent.direction, + submitOrderEvent.price, + submitOrderEvent.quantity, + submitOrderEvent.quantity - submitOrderEvent.remainedQuantity, + financialActions[0].amount, + financialActions[0].amount, + 0 + ) + ) + } + } +} \ No newline at end of file diff --git a/Accountant/accountant-ports/.gitignore b/Accountant/accountant-ports/.gitignore new file mode 100644 index 000000000..f3a8317d6 --- /dev/null +++ b/Accountant/accountant-ports/.gitignore @@ -0,0 +1,4 @@ +*.iml +target/ +.mvn/ +.idea/ \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/.gitignore b/Accountant/accountant-ports/accountant-eventlistener-kafka/.gitignore new file mode 100644 index 000000000..bb9840a17 --- /dev/null +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/.gitignore @@ -0,0 +1,34 @@ +HELP.md +target/ +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +.mvn/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ + + +### VS Code ### +.vscode/ + +.DS_Store diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/mvnw b/Accountant/accountant-ports/accountant-eventlistener-kafka/mvnw new file mode 100755 index 000000000..a16b5431b --- /dev/null +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/mvnw.cmd b/Accountant/accountant-ports/accountant-eventlistener-kafka/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/pom.xml b/Accountant/accountant-ports/accountant-eventlistener-kafka/pom.xml new file mode 100644 index 000000000..2b423b915 --- /dev/null +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + accountant-eventlistener-kafka + 0.0.1-SNAPSHOT + accountant-eventlistener-kafka + Accountant kafka listener of Mixchange + + + 1.8 + 1.4.31 + ${version} + ${version} + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-webflux + + + co.nilin.mixchange + matching-core + ${matching.version} + provided + + + co.nilin.mixchange + accountant-core + ${accountant.version} + provided + + + org.springframework.kafka + spring-kafka + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + org.springframework.kafka + spring-kafka-test + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/config/AccountantKafkaConfig.kt b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/config/AccountantKafkaConfig.kt new file mode 100644 index 000000000..e714b9664 --- /dev/null +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/config/AccountantKafkaConfig.kt @@ -0,0 +1,128 @@ +package co.nilin.mixchange.port.accountant.kafka.config + + +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +import co.nilin.mixchange.port.accountant.kafka.consumer.OrderKafkaListener +import co.nilin.mixchange.port.trade.consumer.EventKafkaListener +import co.nilin.mixchange.port.trade.consumer.TempEventKafkaListener +import co.nilin.mixchange.port.trade.consumer.TradeKafkaListener +import org.apache.kafka.clients.admin.NewTopic +import org.apache.kafka.clients.consumer.ConsumerConfig +import org.apache.kafka.clients.producer.ProducerConfig +import org.apache.kafka.common.serialization.StringDeserializer +import org.apache.kafka.common.serialization.StringSerializer +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.support.GenericApplicationContext +import org.springframework.kafka.core.ConsumerFactory +import org.springframework.kafka.core.DefaultKafkaConsumerFactory +import org.springframework.kafka.core.DefaultKafkaProducerFactory +import org.springframework.kafka.core.KafkaTemplate +import org.springframework.kafka.core.ProducerFactory +import org.springframework.kafka.listener.ConcurrentMessageListenerContainer +import org.springframework.kafka.listener.ContainerProperties +import org.springframework.kafka.support.serializer.JsonDeserializer +import org.springframework.kafka.support.serializer.JsonSerializer +import java.util.* +import java.util.regex.Pattern + + +@Configuration +class AccountantKafkaConfig { + @Value("\${spring.kafka.bootstrap-servers}") + private val bootstrapServers: String? = null + + @Value("\${spring.kafka.consumer.group-id}") + private val groupId: String? = null + + @Bean("accountantConsumerConfig") + fun consumerConfigs(): Map? { + val props: MutableMap = HashMap() + props[ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG] = bootstrapServers + props[ConsumerConfig.GROUP_ID_CONFIG] = groupId + props[ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG] = StringDeserializer::class.java + props[ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG] = JsonDeserializer::class.java + props[JsonDeserializer.TRUSTED_PACKAGES] = "co.nilin.mixchange.*" + return props + } + + @Bean("accountantConsumerFactory") + fun consumerFactory(@Qualifier("accountantConsumerConfig")consumerConfigs: Map): ConsumerFactory { + return DefaultKafkaConsumerFactory(consumerConfigs) + } + + @Bean("accountantProducerConfig") + fun producerConfigs(): Map { + val props: MutableMap = HashMap() + props[ProducerConfig.BOOTSTRAP_SERVERS_CONFIG] = bootstrapServers + props[ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG] = StringSerializer::class.java + props[ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG] = JsonSerializer::class.java + return props + } + + @Bean("accountantProducerFactory") + fun producerFactory(@Qualifier("accountantProducerConfig") producerConfigs: Map): ProducerFactory { + return DefaultKafkaProducerFactory(producerConfigs) + } + + @Bean("accountantKafkaTemplate") + fun kafkaTemplate(@Qualifier("accountantProducerFactory") producerFactory: ProducerFactory): KafkaTemplate { + return KafkaTemplate(producerFactory) + } + + + @Autowired + @ConditionalOnBean(TradeKafkaListener::class) + fun configureTradeListener(tradeListener: TradeKafkaListener + , @Qualifier("accountantConsumerFactory") consumerFactory: ConsumerFactory) { + val containerProps = ContainerProperties(Pattern.compile("trades_.*")) + containerProps.messageListener = tradeListener + val container = ConcurrentMessageListenerContainer(consumerFactory, containerProps) + container.setBeanName("TradeKafkaListenerContainer") + container.start() + } + + @Autowired + @ConditionalOnBean(EventKafkaListener::class) + fun configureEventListener(eventListener: EventKafkaListener + , @Qualifier("accountantConsumerFactory") consumerFactory: ConsumerFactory) { + val containerProps = ContainerProperties(Pattern.compile("events_.*")) + containerProps.messageListener = eventListener + val container = ConcurrentMessageListenerContainer(consumerFactory, containerProps) + container.setBeanName("EventKafkaListenerContainer") + container.start() + } + + @Autowired + @ConditionalOnBean(OrderKafkaListener::class) + fun configureOrderListener(orderListener: OrderKafkaListener + , @Qualifier("accountantConsumerFactory") consumerFactory: ConsumerFactory) { + val containerProps = ContainerProperties(Pattern.compile("orders_.*")) + containerProps.messageListener = orderListener + val container = ConcurrentMessageListenerContainer(consumerFactory, containerProps) + container.setBeanName("OrderKafkaListenerContainer") + container.start() + } + + @Autowired + fun createTempTopics(applicationContext: GenericApplicationContext){ + applicationContext.registerBean("topic_tempevents", NewTopic::class.java, "tempevents", 1 ,1) + } + + @Autowired + @ConditionalOnBean(TempEventKafkaListener::class) + fun configureTempEventListener(eventListener: TempEventKafkaListener + , @Qualifier("accountantConsumerFactory") consumerFactory: ConsumerFactory) { + val containerProps = ContainerProperties(Pattern.compile("tempevents")) + containerProps.messageListener = eventListener + val container = ConcurrentMessageListenerContainer(consumerFactory, containerProps) + container.setBeanName("TempEventKafkaListenerContainer") + container.start() + } + + +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/EventKafkaListener.kt b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/EventKafkaListener.kt new file mode 100644 index 000000000..e0e6359f3 --- /dev/null +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/EventKafkaListener.kt @@ -0,0 +1,28 @@ +package co.nilin.mixchange.port.trade.consumer + + +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +import co.nilin.mixchange.port.trade.spi.EventListener +import org.apache.kafka.clients.consumer.ConsumerRecord +import org.springframework.kafka.listener.MessageListener +import org.springframework.stereotype.Component + +@Component +class EventKafkaListener: MessageListener { + val eventListeners = arrayListOf() + override fun onMessage(data: ConsumerRecord) { + eventListeners.forEach{ + tl -> tl.onEvent(data.value(), data.partition(), data.offset(), data.timestamp()) + } + } + + fun addEventListener(tl: EventListener){ + eventListeners.add(tl) + } + + fun removeEventListener(tl: EventListener){ + eventListeners.removeIf { + item -> item.id() == tl.id() + } + } +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/OrderKafkaListener.kt b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/OrderKafkaListener.kt new file mode 100644 index 000000000..c8a5b920e --- /dev/null +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/OrderKafkaListener.kt @@ -0,0 +1,32 @@ +package co.nilin.mixchange.port.accountant.kafka.consumer + +import co.nilin.mixchange.port.accountant.kafka.spi.OrderSubmitRequestListener +import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest +import kotlinx.coroutines.ExecutorCoroutineDispatcher +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import org.apache.kafka.clients.consumer.ConsumerRecord +import org.springframework.kafka.listener.MessageListener +import org.springframework.stereotype.Component + + +class OrderKafkaListener() : + MessageListener { + val orderListeners = arrayListOf() + override fun onMessage(data: ConsumerRecord) { + orderListeners.forEach { tl -> + tl.onOrder(data.value(), data.partition(), data.offset(), data.timestamp()) + } + + } + + fun addOrderListener(tl: OrderSubmitRequestListener) { + orderListeners.add(tl) + } + + fun removeOrderListener(tl: OrderSubmitRequestListener) { + orderListeners.removeIf { item -> + item.id() == tl.id() + } + } +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/TempEventKafkaListener.kt b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/TempEventKafkaListener.kt new file mode 100644 index 000000000..8dc4e4e60 --- /dev/null +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/TempEventKafkaListener.kt @@ -0,0 +1,30 @@ +package co.nilin.mixchange.port.trade.consumer + + +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +import co.nilin.mixchange.port.trade.spi.EventListener +import co.nilin.mixchange.port.trade.spi.TempEventListener +import org.apache.kafka.clients.consumer.ConsumerRecord +import org.springframework.kafka.listener.MessageListener +import org.springframework.stereotype.Component + +@Component +class TempEventKafkaListener: MessageListener { + val eventListeners = arrayListOf() + override fun onMessage(data: ConsumerRecord) { + println("TempEvent onMessage") + eventListeners.forEach{ + tl -> tl.onEvent(data.value(), data.partition(), data.offset(), data.timestamp()) + } + } + + fun addEventListener(tl: TempEventListener){ + eventListeners.add(tl) + } + + fun removeEventListener(tl: TempEventListener){ + eventListeners.removeIf { + item -> item.id() == tl.id() + } + } +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/TradeKafkaListener.kt b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/TradeKafkaListener.kt new file mode 100644 index 000000000..0c14cf036 --- /dev/null +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/TradeKafkaListener.kt @@ -0,0 +1,28 @@ +package co.nilin.mixchange.port.trade.consumer + + +import co.nilin.mixchange.matching.core.eventh.events.TradeEvent +import co.nilin.mixchange.port.trade.spi.TradeListener +import org.apache.kafka.clients.consumer.ConsumerRecord +import org.springframework.kafka.listener.MessageListener +import org.springframework.stereotype.Component + +@Component +class TradeKafkaListener: MessageListener { + val tradeListeners = arrayListOf() + override fun onMessage(data: ConsumerRecord) { + tradeListeners.forEach{ + tl -> tl.onTrade(data.value(), data.partition(), data.offset(), data.timestamp()) + } + } + + fun addTradeListener(tl: TradeListener){ + tradeListeners.add(tl) + } + + fun removeTradeListener(tl: TradeListener){ + tradeListeners.removeIf { + item -> item.id() == tl.id() + } + } +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/EventListener.kt b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/EventListener.kt new file mode 100644 index 000000000..0f522ca46 --- /dev/null +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/EventListener.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.port.trade.spi + +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent + + +interface EventListener { + fun id(): String + fun onEvent(coreEvent: CoreEvent, partition: Int, offset: Long, timestamp: Long) +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/OrderSubmitRequestListener.kt b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/OrderSubmitRequestListener.kt new file mode 100644 index 000000000..28b88bea0 --- /dev/null +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/OrderSubmitRequestListener.kt @@ -0,0 +1,8 @@ +package co.nilin.mixchange.port.accountant.kafka.spi + +import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest + +interface OrderSubmitRequestListener { + fun id(): String + fun onOrder(order: OrderSubmitRequest, partition: Int, offset: Long, timestamp: Long) +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/TempEventListener.kt b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/TempEventListener.kt new file mode 100644 index 000000000..292b228ac --- /dev/null +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/TempEventListener.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.port.trade.spi + +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent + + +interface TempEventListener { + fun id(): String + fun onEvent(coreEvent: CoreEvent, partition: Int, offset: Long, timestamp: Long) +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/TradeListener.kt b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/TradeListener.kt new file mode 100644 index 000000000..7f37be88b --- /dev/null +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/TradeListener.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.port.trade.spi + +import co.nilin.mixchange.matching.core.eventh.events.TradeEvent + + +interface TradeListener { + fun id(): String + fun onTrade(tradeEvent: TradeEvent, partition: Int, offset: Long, timestamp: Long) +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt new file mode 100644 index 000000000..1613e63df --- /dev/null +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt @@ -0,0 +1,39 @@ +package co.nilin.mixchange.port.order.kafka.inout + +import co.nilin.mixchange.matching.core.model.MatchConstraint +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.OrderType + +class OrderSubmitRequest() { + lateinit var ouid: String + lateinit var uuid: String + var orderId: Long? = null + lateinit var pair: co.nilin.mixchange.matching.core.model.Pair + var price: Long = 0 + var quantity: Long = 0 + var direction: OrderDirection = OrderDirection.BID + var matchConstraint: MatchConstraint = MatchConstraint.GTC + var orderType: OrderType = OrderType.LIMIT_ORDER + + constructor(ouid: String, + uuid: String, + orderId: Long?, + pair: co.nilin.mixchange.matching.core.model.Pair, + price: Long, + quantity: Long, + direction: OrderDirection, + matchConstraint: MatchConstraint, + orderType: OrderType):this(){ + this.ouid = ouid + this.uuid = uuid + this.orderId = orderId + this.pair = pair + this.price = price + this.quantity = quantity + this.direction = direction + this.matchConstraint = matchConstraint + this.orderType = orderType + } + + +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/.gitignore b/Accountant/accountant-ports/accountant-persister-postgres/.gitignore new file mode 100644 index 000000000..de5a9214d --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/.gitignore @@ -0,0 +1,34 @@ +HELP.md +target/ +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +.mvn/ +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +.DS_Store diff --git a/Accountant/accountant-ports/accountant-persister-postgres/mvnw b/Accountant/accountant-ports/accountant-persister-postgres/mvnw new file mode 100755 index 000000000..a16b5431b --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/Accountant/accountant-ports/accountant-persister-postgres/mvnw.cmd b/Accountant/accountant-ports/accountant-persister-postgres/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/Accountant/accountant-ports/accountant-persister-postgres/pom.xml b/Accountant/accountant-ports/accountant-persister-postgres/pom.xml new file mode 100644 index 000000000..e3a6550e3 --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/pom.xml @@ -0,0 +1,117 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + accountant-persister-postgres + 0.0.1-SNAPSHOT + accountant-persister-postgres + Persist items of Mixchange accountant on Postgres + + + 1.8 + 1.4.31 + ${version} + ${version} + + + + + co.nilin.mixchange + matching-core + ${matching.version} + provided + + + co.nilin.mixchange + accountant-core + ${accountant.version} + provided + + + org.springframework.boot + spring-boot-starter-data-r2dbc + + + io.r2dbc + r2dbc-postgresql + runtime + + + org.postgresql + postgresql + runtime + + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + com.google.code.gson + gson + + + io.projectreactor + reactor-test + test + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/config/PostgresConfig.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/config/PostgresConfig.kt new file mode 100644 index 000000000..43c013fee --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/config/PostgresConfig.kt @@ -0,0 +1,109 @@ +package co.nilin.mixchange.port.order.kafka.config + + +import co.nilin.mixchange.matching.core.model.OrderDirection +import org.springframework.context.annotation.Configuration +import org.springframework.data.annotation.Id +import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import org.springframework.r2dbc.core.DatabaseClient +import java.math.BigDecimal +import java.time.LocalDateTime + + +@Configuration +@EnableR2dbcRepositories(basePackages = ["co.nilin.mixchange"]) +class PostgresConfig(db: DatabaseClient) { + + init { + + val sql = """ + drop table IF EXISTS orders; + drop table IF EXISTS fi_actions; + drop table IF EXISTS pair_config; + drop table IF EXISTS pair_fee_config; + drop table IF EXISTS temp_events; + + CREATE TABLE IF NOT EXISTS orders ( + id SERIAL PRIMARY KEY, + ouid VARCHAR(72) NOT NULL UNIQUE, + uuid VARCHAR(72) NOT NULL, + pair VARCHAR(20), + matching_engine_id numeric, + maker_fee decimal, + taker_fee decimal, + left_side_fraction decimal, + right_side_fraction decimal, + user_level VARCHAR(20), + direction VARCHAR(20), + price numeric, + quantity numeric, + filled_quantity numeric, + first_transfer_amount numeric, + remained_transfer_amount numeric, + status integer, + agent VARCHAR(20), + ip VARCHAR(11), + create_date TIMESTAMP + ); + + CREATE TABLE IF NOT EXISTS fi_actions ( + id SERIAL PRIMARY KEY, + parent_id numeric, + event_type VARCHAR(72) NOT NULL, + pointer VARCHAR(72) NOT NULL, + symbol VARCHAR(36), + amount decimal, + sender VARCHAR(36), + sender_wallet_type VARCHAR(36), + receiver VARCHAR(36), + receiver_wallet_type VARCHAR(36), + agent VARCHAR(20), + ip VARCHAR(11), + create_date TIMESTAMP, + status VARCHAR(20), + retry_count numeric, + last_try_date TIMESTAMP + ); + CREATE TABLE IF NOT EXISTS pair_config ( + pair VARCHAR(72) PRIMARY KEY, + left_side_wallet_symbol VARCHAR(36) NOT NULL, + right_side_wallet_symbol VARCHAR(36) NOT NULL, + left_side_fraction decimal, + right_side_fraction decimal, + rate decimal + ); + CREATE TABLE IF NOT EXISTS pair_fee_config ( + id SERIAL PRIMARY KEY, + pair_config_id VARCHAR(72), + direction VARCHAR(36) NOT NULL, + user_level VARCHAR(36) NOT NULL, + maker_fee decimal, + taker_fee decimal + ); + + CREATE TABLE IF NOT EXISTS temp_events ( + id SERIAL PRIMARY KEY, + ouid VARCHAR(72) NOT NULL, + event_type VARCHAR(72) NOT NULL, + event_body TEXT, + event_date TIMESTAMP + ); + insert into pair_config values('btc_usdt', 'btc', 'usdt', 0.000001, 0.01, 55000)ON CONFLICT DO NOTHING; + insert into pair_fee_config values(1, 'btc_usdt', 'ASK', '*', 0.01, 0.01) ON CONFLICT DO NOTHING; + insert into pair_fee_config values(2, 'btc_usdt', 'BID', '*', 0.01, 0.01) ON CONFLICT DO NOTHING; + insert into pair_config values('nln_usdt', 'nln', 'usdt', 1.0, 0.01, 0.01) ON CONFLICT DO NOTHING; + insert into pair_fee_config values(3, 'nln_usdt', 'ASK', '*', 0.01, 0.01) ON CONFLICT DO NOTHING; + insert into pair_fee_config values(4, 'nln_usdt', 'BID', '*', 0.01, 0.01) ON CONFLICT DO NOTHING; + insert into pair_config values('nln_btc', 'nln', 'btc', 1.0, 0.000001, 1/5500000) ON CONFLICT DO NOTHING; + insert into pair_fee_config values(5, 'nln_btc', 'ASK', '*', 0.01, 0.01) ON CONFLICT DO NOTHING; + insert into pair_fee_config values(6, 'nln_btc', 'BID', '*', 0.01, 0.01) ON CONFLICT DO NOTHING; + commit; + """ + val initDb = db.sql { sql } + initDb // initialize the database + .then() + .subscribe() // execute + } +} diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/FinancialActionRepository.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/FinancialActionRepository.kt new file mode 100644 index 000000000..2b5e30f82 --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/FinancialActionRepository.kt @@ -0,0 +1,33 @@ +package co.nilin.mixchange.port.accountant.postgres.dao + +import co.nilin.mixchange.accountant.core.model.FinancialAction +import co.nilin.mixchange.accountant.core.model.FinancialActionStatus +import co.nilin.mixchange.port.accountant.postgres.model.FinancialActionModel +import kotlinx.coroutines.flow.Flow +import org.springframework.data.domain.PageRequest +import org.springframework.data.domain.Pageable +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono +import java.math.BigDecimal + +@Repository +interface FinancialActionRepository : ReactiveCrudRepository { + @Query("select * from fi_actions fi where pointer = :ouid and :uuid in (fi.sender, fi.receiver)") + fun findByOuidAndUuid( + @Param("ouid") ouid: String, @Param("uuid") uuid: String, paging: Pageable + ): Flow + + @Query("select count(1) from fi_actions fi where fi.sender = :uuid and fi.symbol = :symbol and fi.event_type = :eventType and fi.status = :status") + fun findByUuidAndSymbolAndEventTypeAndStatus( + @Param("uuid") uuid: String, + @Param("symbol") symbol: String, + @Param("eventType") eventType: String, + @Param("status") financialActionStatus: FinancialActionStatus + ): Mono + + @Query("select * from fi_actions fi where status = :status") + fun findByStatus(@Param("status") status: String, paging: Pageable): Flow +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/OrderRepository.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/OrderRepository.kt new file mode 100644 index 000000000..4d937bfb9 --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/OrderRepository.kt @@ -0,0 +1,14 @@ +package co.nilin.mixchange.port.accountant.postgres.dao + +import co.nilin.mixchange.port.accountant.postgres.model.OrderModel +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono + +@Repository +interface OrderRepository: ReactiveCrudRepository { + @Query("select * from orders where ouid = :ouid") + fun findByOuid(@Param("ouid") ouid: String): Mono +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/PairConfigRepository.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/PairConfigRepository.kt new file mode 100644 index 000000000..017f734be --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/PairConfigRepository.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.port.accountant.postgres.dao + +import co.nilin.mixchange.port.accountant.postgres.model.PairConfigModel +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository + +@Repository +interface PairConfigRepository: ReactiveCrudRepository { +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/PairFeeConfigRepository.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/PairFeeConfigRepository.kt new file mode 100644 index 000000000..7406230c7 --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/PairFeeConfigRepository.kt @@ -0,0 +1,19 @@ +package co.nilin.mixchange.port.accountant.postgres.dao + +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.port.accountant.postgres.model.PairConfigModel +import co.nilin.mixchange.port.accountant.postgres.model.PairFeeConfigModel +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono + +@Repository +interface PairFeeConfigRepository: ReactiveCrudRepository { + @Query("select * from pair_fee_config where pair_config_id = :pair and direction = :direction and user_level = :userLevel") + fun findByPairAndDirectionAndUserLevel( + @Param("pair") pair: String + ,@Param("direction") direction: OrderDirection + ,@Param("userLevel") userLevel: String): Mono +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/TempEventRepository.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/TempEventRepository.kt new file mode 100644 index 000000000..99ebfc0fe --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/TempEventRepository.kt @@ -0,0 +1,13 @@ +package co.nilin.mixchange.port.accountant.postgres.dao + +import co.nilin.mixchange.port.accountant.postgres.model.TempEventModel +import kotlinx.coroutines.flow.Flow +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono + +@Repository +interface TempEventRepository: ReactiveCrudRepository { + fun findByOuid(ouid:String): Flow + fun deleteByOuid(ouid:String): Mono +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/FinancialActionLoaderImpl.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/FinancialActionLoaderImpl.kt new file mode 100644 index 000000000..b4223ec53 --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/FinancialActionLoaderImpl.kt @@ -0,0 +1,62 @@ +package co.nilin.mixchange.port.accountant.postgres.impl + +import co.nilin.mixchange.accountant.core.model.FinancialAction +import co.nilin.mixchange.accountant.core.model.FinancialActionStatus +import co.nilin.mixchange.accountant.core.spi.FinancialActionLoader +import co.nilin.mixchange.port.accountant.postgres.dao.FinancialActionRepository +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.reactive.awaitFirst +import kotlinx.coroutines.reactive.awaitFirstOrElse +import org.springframework.data.domain.PageRequest +import org.springframework.data.domain.Sort +import org.springframework.stereotype.Component +import java.math.BigDecimal + +@Component +class FinancialActionLoaderImpl(val financialActionRepository: FinancialActionRepository) : FinancialActionLoader { + + override suspend fun loadUnprocessed(offset: Long, size: Long): List { + return financialActionRepository.findByStatus( + FinancialActionStatus.CREATED.name, + PageRequest.of(offset.toInt(), size.toInt(), Sort.by(Sort.Direction.ASC, "createDate")) + ).map { fim -> + loadFinancialAction(fim.id)!! + }.toList() + } + + override suspend fun findLast(uuid: String, ouid: String): FinancialAction? { + return financialActionRepository.findByOuidAndUuid( + ouid, uuid, PageRequest.of(0, 1, Sort.by(Sort.Direction.DESC, "createDate")) + ).map { fim -> + loadFinancialAction(fim.id) + }.firstOrNull() + } + + private suspend fun loadFinancialAction(id: Long?): FinancialAction? { + if (id != null) { + val fim = financialActionRepository.findById(id).awaitFirst() + return FinancialAction( + fim.id, + loadFinancialAction(fim.parentId), + fim.eventType, + fim.pointer, + fim.symbol, + BigDecimal.valueOf(fim.amount), + fim.sender, + fim.senderWalletType, + fim.receiver, + fim.receiverWalletType, + fim.createDate + ) + } + return null + } + + override suspend fun countUnprocessed(uuid: String, symbol: String, eventType: String): Long { + return financialActionRepository.findByUuidAndSymbolAndEventTypeAndStatus(uuid, symbol, eventType, FinancialActionStatus.CREATED) + .awaitFirstOrElse { BigDecimal.ZERO } + .toLong() + } +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/FinancialActionPersisterImpl.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/FinancialActionPersisterImpl.kt new file mode 100644 index 000000000..0cfbc6c2a --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/FinancialActionPersisterImpl.kt @@ -0,0 +1,66 @@ +package co.nilin.mixchange.port.accountant.postgres.impl + +import co.nilin.mixchange.accountant.core.model.FinancialAction +import co.nilin.mixchange.accountant.core.model.FinancialActionStatus +import co.nilin.mixchange.accountant.core.spi.FinancialActionPersister +import co.nilin.mixchange.port.accountant.postgres.dao.FinancialActionRepository +import co.nilin.mixchange.port.accountant.postgres.model.FinancialActionModel +import kotlinx.coroutines.reactive.awaitFirst +import kotlinx.coroutines.reactive.awaitFirstOrElse +import kotlinx.coroutines.reactive.awaitLast +import org.springframework.stereotype.Component +import java.lang.IllegalArgumentException +import java.time.LocalDateTime + +@Component +class FinancialActionPersisterImpl(val financialActionRepository: FinancialActionRepository) : + FinancialActionPersister { + + override suspend fun persist(financialActions: List): List { + financialActionRepository.saveAll(financialActions.map { fa -> + FinancialActionModel( + null, + fa.parent?.id, + fa.eventType, + fa.pointer, + fa.symbol, + fa.amount.toDouble(), + fa.sender, + fa.senderWalletType, + fa.receiver, + fa.receiverWalletType, + "", + "", + fa.createDate + ) + }).awaitLast() + return financialActions + } + + override suspend fun updateStatus(financialAction: FinancialAction, status: FinancialActionStatus) { + val existing = financialActionRepository.findById(financialAction.id!!).awaitFirstOrElse { + throw IllegalArgumentException() + } + financialActionRepository.save( + FinancialActionModel( + existing.id, + existing.parentId, + existing.eventType, + existing.pointer, + existing.symbol, + existing.amount, + existing.sender, + existing.senderWalletType, + existing.receiver, + existing.receiverWalletType, + existing.agent, + existing.ip, + existing.createDate, + status, + 1 + (existing.retryCount ?: 0), + LocalDateTime.now() + ) + ) + .awaitFirst() + } +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/OrderPersisterImpl.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/OrderPersisterImpl.kt new file mode 100644 index 000000000..bf7578fb4 --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/OrderPersisterImpl.kt @@ -0,0 +1,51 @@ +package co.nilin.mixchange.port.accountant.postgres.impl + +import co.nilin.mixchange.accountant.core.model.Order +import co.nilin.mixchange.accountant.core.spi.OrderPersister +import co.nilin.mixchange.port.accountant.postgres.dao.OrderRepository +import co.nilin.mixchange.port.accountant.postgres.model.OrderModel +import kotlinx.coroutines.reactive.awaitFirstOrElse +import kotlinx.coroutines.reactive.awaitFirstOrNull +import org.springframework.stereotype.Component +import java.lang.IllegalArgumentException +import java.time.LocalDateTime + +@Component +class OrderPersisterImpl(val orderRepository: OrderRepository) : OrderPersister { + override suspend fun load(ouid: String): Order? { + val model = orderRepository.findByOuid(ouid) + .awaitFirstOrNull() ?: return null + return Order(model.pair, model.ouid, model.matchingEngineId, model.makerFee, model.takerFee + , model.leftSideFraction, model.rightSideFraction, model.uuid, model.userLevel + , model.direction, model.price, model.quantity, model.filledQuantity + , model.firstTransferAmount, model.remainedTransferAmount, model.status, model.id) + } + + override suspend fun save(order: Order): Order { + orderRepository.save( + OrderModel( + order.id, + order.ouid, + order.uuid, + order.pair, + order.matchingEngineId, + order.makerFee, + order.takerFee, + order.leftSideFraction, + order.rightSideFraction, + order.userLevel, + order.direction, + order.price, + order.quantity, + order.filledQuantity, + order.firstTransferAmount, + order.remainedTransferAmount, + order.status, + "", + "", + LocalDateTime.now() + ) + ).awaitFirstOrNull() + return order + } +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/PairConfigLoaderImpl.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/PairConfigLoaderImpl.kt new file mode 100644 index 000000000..4b2413577 --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/PairConfigLoaderImpl.kt @@ -0,0 +1,52 @@ +package co.nilin.mixchange.port.accountant.postgres.impl + +import co.nilin.mixchange.accountant.core.model.PairConfig +import co.nilin.mixchange.accountant.core.model.PairFeeConfig +import co.nilin.mixchange.accountant.core.spi.PairConfigLoader +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.port.accountant.postgres.dao.PairConfigRepository +import co.nilin.mixchange.port.accountant.postgres.dao.PairFeeConfigRepository +import co.nilin.mixchange.port.accountant.postgres.model.PairFeeConfigModel +import kotlinx.coroutines.reactive.awaitFirstOrElse +import kotlinx.coroutines.reactive.awaitFirstOrNull +import org.springframework.stereotype.Component +import org.springframework.util.StringUtils +import java.lang.IllegalArgumentException + +@Component +class PairConfigLoaderImpl( + val pairConfigRepository: PairConfigRepository, val pairFeeConfigRepository: PairFeeConfigRepository +) : PairConfigLoader { + override suspend fun load(pair: String, direction: OrderDirection, userLevel: String): PairFeeConfig { + val pairConfig = pairConfigRepository + .findById(pair).awaitFirstOrElse { throw IllegalArgumentException("$pair is not available") } + var pairFeeConfig: PairFeeConfigModel? + if ( userLevel.isEmpty()) { + pairFeeConfig = pairFeeConfigRepository + .findByPairAndDirectionAndUserLevel(pair, direction, "*") + .awaitFirstOrElse { throw IllegalArgumentException("$pair fee is not available") } + } else { + pairFeeConfig = pairFeeConfigRepository + .findByPairAndDirectionAndUserLevel(pair, direction, userLevel) + .awaitFirstOrNull() + if ( pairFeeConfig == null ){ + pairFeeConfig = pairFeeConfigRepository + .findByPairAndDirectionAndUserLevel(pair, direction, "*") + .awaitFirstOrElse { throw IllegalArgumentException("$pair fee is not available") } + } + } + + + + return PairFeeConfig( + PairConfig( + pair, + pairConfig.leftSideWalletSymbol, + pairConfig.rightSideWalletSymbol, + pairConfig.leftSideFraction, + pairConfig.rightSideFraction + ), pairFeeConfig!!.direction, pairFeeConfig.userLevel, pairFeeConfig.makerFee, pairFeeConfig.takerFee + ) + + } +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/PairStaticRateLoaderImpl.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/PairStaticRateLoaderImpl.kt new file mode 100644 index 000000000..cfab9e118 --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/PairStaticRateLoaderImpl.kt @@ -0,0 +1,17 @@ +package co.nilin.mixchange.port.accountant.postgres.impl + +import co.nilin.mixchange.accountant.core.spi.PairStaticRateLoader +import co.nilin.mixchange.port.accountant.postgres.dao.PairConfigRepository +import kotlinx.coroutines.reactive.awaitFirstOrElse +import org.springframework.stereotype.Component +import java.lang.IllegalArgumentException + +@Component +class PairStaticRateLoaderImpl(val pairConfigRepository: PairConfigRepository): PairStaticRateLoader { + override suspend fun calculateStaticRate(leftSide: String, rightSide: String): Double? { + val pairConfig = pairConfigRepository + .findById("${leftSide}_$rightSide") + .awaitFirstOrElse { throw IllegalArgumentException("${leftSide}_$rightSide is not available") } + return pairConfig.rate + } +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/TempEventPersisterImpl.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/TempEventPersisterImpl.kt new file mode 100644 index 000000000..603ab1582 --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/TempEventPersisterImpl.kt @@ -0,0 +1,38 @@ +package co.nilin.mixchange.port.accountant.postgres.impl + +import co.nilin.mixchange.accountant.core.spi.TempEventPersister +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +import co.nilin.mixchange.port.accountant.postgres.dao.TempEventRepository +import co.nilin.mixchange.port.accountant.postgres.model.TempEventModel +import com.google.gson.Gson +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.reactive.awaitFirstOrNull +import kotlinx.coroutines.reactive.awaitSingle +import org.springframework.stereotype.Component +import java.time.LocalDateTime + +@Component +class TempEventPersisterImpl(val tempEventRepository: TempEventRepository) : TempEventPersister { + override suspend fun saveTempEvent(ouid: String, event: CoreEvent) { + tempEventRepository.save( + TempEventModel( + null, ouid, event.javaClass.name, + Gson().toJson(event), LocalDateTime.now() + ) + ).awaitSingle() + } + + override suspend fun loadTempEvents(ouid: String): List { + return tempEventRepository + .findByOuid(ouid) + .map { value: TempEventModel -> + Gson().fromJson(value.eventBody, Class.forName(value.eventType)) as CoreEvent + } + .toList() + } + + override suspend fun removeTempEvents(ouid: String) { + tempEventRepository.deleteByOuid(ouid).awaitFirstOrNull() + } +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/FinancialActionModel.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/FinancialActionModel.kt new file mode 100644 index 000000000..77a6bc385 --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/FinancialActionModel.kt @@ -0,0 +1,33 @@ +package co.nilin.mixchange.port.accountant.postgres.model + + +import co.nilin.mixchange.accountant.core.model.FinancialAction +import co.nilin.mixchange.accountant.core.model.FinancialActionStatus +import co.nilin.mixchange.matching.core.model.OrderDirection +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.math.BigDecimal +import java.time.LocalDateTime + +@Table("fi_actions") +class FinancialActionModel( + @Id var id: Long?, + @Column("parent_id") var parentId: Long?, + @Column("event_type") val eventType: String, + val pointer: String, + val symbol: String, + @Column("amount") val amount: Double, + val sender: String, + @Column("sender_wallet_type") val senderWalletType: String, + val receiver: String, + @Column("receiver_wallet_type") val receiverWalletType: String, + val agent: String, + val ip: String, + @Column("create_date") val createDate: LocalDateTime, + val status: FinancialActionStatus = FinancialActionStatus.CREATED, + @Column("retry_count") val retryCount: Int? = null, + @Column("last_try_date") val lastTryDate: LocalDateTime? = null +) + + diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/OrderModel.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/OrderModel.kt new file mode 100644 index 000000000..19543132d --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/OrderModel.kt @@ -0,0 +1,33 @@ +package co.nilin.mixchange.port.accountant.postgres.model + + +import co.nilin.mixchange.matching.core.model.OrderDirection +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.math.BigDecimal +import java.time.LocalDateTime + +@Table("orders") +class OrderModel( + @Id var id: Long?, + val ouid: String, + val uuid: String, + val pair: String, + @Column(value = "matching_engine_id") val matchingEngineId: Long?, + @Column("maker_fee") val makerFee: Double, + @Column("taker_fee") val takerFee: Double, + @Column("left_side_fraction") val leftSideFraction: Double, + @Column("right_side_fraction") val rightSideFraction: Double, + @Column("user_level") val userLevel: String, + @Column("direction") val direction: OrderDirection, + @Column("price") val price: Long, + @Column("quantity") val quantity: Long, + @Column("filled_quantity") val filledQuantity: Long, + @Column("first_transfer_amount") val firstTransferAmount: BigDecimal, + @Column("remained_transfer_amount") val remainedTransferAmount: BigDecimal, + @Column("status") val status: Int, + val agent: String, + val ip: String, + @Column("create_date") val createDate: LocalDateTime +) \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/PairConfigModel.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/PairConfigModel.kt new file mode 100644 index 000000000..c2ca94ebe --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/PairConfigModel.kt @@ -0,0 +1,15 @@ +package co.nilin.mixchange.port.accountant.postgres.model + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table + +@Table("pair_config") +data class PairConfigModel( + @Id val pair: String, + @Column("left_side_wallet_symbol") val leftSideWalletSymbol: String, //can be same as pair left side + @Column("right_side_wallet_symbol") val rightSideWalletSymbol: String, //can be same as pair right side + @Column("left_side_fraction") val leftSideFraction: Double, + @Column("right_side_fraction") val rightSideFraction: Double, + @Column("rate") val rate: Double +) \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/PairFeeConfigModel.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/PairFeeConfigModel.kt new file mode 100644 index 000000000..9704056fc --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/PairFeeConfigModel.kt @@ -0,0 +1,15 @@ +package co.nilin.mixchange.port.accountant.postgres.model + +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table + +@Table("pair_fee_config") +class PairFeeConfigModel( + val id: Long?, + @Column("pair_config_id") val pairConfigId: String, + @Column("direction") val direction: String?, + @Column("user_level") val userLevel: String?, + @Column("maker_fee") val makerFee: Double, + @Column("taker_fee") val takerFee: Double +) { +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/TempEventModel.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/TempEventModel.kt new file mode 100644 index 000000000..40a7f501d --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/TempEventModel.kt @@ -0,0 +1,14 @@ +package co.nilin.mixchange.port.accountant.postgres.model + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.time.LocalDateTime + +@Table("temp_events") +data class TempEventModel(@Id val id: Long? +, val ouid: String +, @Column("event_type") val eventType: String +, @Column("event_body") val eventBody: String +, @Column("event_date") val eventDate: LocalDateTime) { +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-tempsubmitter-kafka/.gitignore b/Accountant/accountant-ports/accountant-tempsubmitter-kafka/.gitignore new file mode 100644 index 000000000..d98fa6485 --- /dev/null +++ b/Accountant/accountant-ports/accountant-tempsubmitter-kafka/.gitignore @@ -0,0 +1,50 @@ +# Created by .ignore support plugin (hsz.mobi) +### Kotlin template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +.idea +*.iml +*.ipr + +# File-based project format +*.iws + +# IntelliJ +target/ + +.DS_Store + + + + diff --git a/Accountant/accountant-ports/accountant-tempsubmitter-kafka/mvnw b/Accountant/accountant-ports/accountant-tempsubmitter-kafka/mvnw new file mode 100755 index 000000000..a16b5431b --- /dev/null +++ b/Accountant/accountant-ports/accountant-tempsubmitter-kafka/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/Accountant/accountant-ports/accountant-tempsubmitter-kafka/mvnw.cmd b/Accountant/accountant-ports/accountant-tempsubmitter-kafka/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/Accountant/accountant-ports/accountant-tempsubmitter-kafka/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/Accountant/accountant-ports/accountant-tempsubmitter-kafka/pom.xml b/Accountant/accountant-ports/accountant-tempsubmitter-kafka/pom.xml new file mode 100644 index 000000000..8f3620258 --- /dev/null +++ b/Accountant/accountant-ports/accountant-tempsubmitter-kafka/pom.xml @@ -0,0 +1,120 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + accountant-temp-submitter-kafka + 0.0.1-SNAPSHOT + accountant-temp-submitter-kafka + Accountant kafka temp event submitter of Mixchange + + + 1.8 + 1.4.31 + ${version} + ${version} + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-webflux + + + co.nilin.mixchange + matching-core + ${matching.version} + provided + + + co.nilin.mixchange + accountant-core + ${accountant.version} + provided + + + org.springframework.kafka + spring-kafka + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + org.springframework.kafka + spring-kafka-test + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + diff --git a/Accountant/accountant-ports/accountant-tempsubmitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/config/SubmitterKafkaConfig.kt b/Accountant/accountant-ports/accountant-tempsubmitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/config/SubmitterKafkaConfig.kt new file mode 100644 index 000000000..d402cf1f2 --- /dev/null +++ b/Accountant/accountant-ports/accountant-tempsubmitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/config/SubmitterKafkaConfig.kt @@ -0,0 +1,53 @@ +package co.nilin.mixchange.port.accountant.kafka.config + + + +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +import org.apache.kafka.clients.producer.ProducerConfig +import org.apache.kafka.common.serialization.StringSerializer +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.support.GenericApplicationContext +import org.springframework.kafka.core.DefaultKafkaProducerFactory +import org.springframework.kafka.core.KafkaTemplate +import org.springframework.kafka.core.ProducerFactory +import org.springframework.kafka.support.serializer.JsonSerializer +import java.util.* + + +@Configuration +class SubmitterKafkaConfig() { + @Value("\${spring.kafka.bootstrap-servers}") + private lateinit var bootstrapServers: String + + @Value("\${spring.kafka.consumer.group-id}") + private val groupId: String? = null + + @Autowired + private val applicationContext: GenericApplicationContext? = null + + + @Bean("tempeventProducerConfigs") + fun producerConfigs(): Map? { + val props: MutableMap = HashMap() + props[ProducerConfig.BOOTSTRAP_SERVERS_CONFIG] = bootstrapServers + props[ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG] = StringSerializer::class.java + props[ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG] = JsonSerializer::class.java + return props + } + + @Bean("tempeventProducerFactory") + fun producerFactory(@Qualifier("tempeventProducerConfigs") producerConfigs: Map): ProducerFactory { + return DefaultKafkaProducerFactory(producerConfigs) + } + + @Bean("tempeventKafkaTemplate") + fun kafkaTemplate(@Qualifier("tempeventProducerFactory") producerFactory: ProducerFactory): KafkaTemplate { + return KafkaTemplate(producerFactory) + } + + +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-tempsubmitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/TempEventSubmitter.kt b/Accountant/accountant-ports/accountant-tempsubmitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/TempEventSubmitter.kt new file mode 100644 index 000000000..647f82f7a --- /dev/null +++ b/Accountant/accountant-ports/accountant-tempsubmitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/TempEventSubmitter.kt @@ -0,0 +1,29 @@ +package co.nilin.mixchange.port.accountant.kafka.service + +import co.nilin.mixchange.accountant.core.spi.TempEventRepublisher +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.kafka.core.KafkaTemplate +import org.springframework.stereotype.Component +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine + +@Component +class TempEventSubmitter(@Qualifier("tempeventKafkaTemplate") val kafkaTemplate: KafkaTemplate): TempEventRepublisher { + override suspend fun republish(events: List): Unit = suspendCoroutine { cont -> + println("TempEventSubmit!") + events.forEach { event -> + val sendFuture = kafkaTemplate.send("tempevents", event) + sendFuture.addCallback({ sendResult -> + }, { exception -> + cont.resumeWithException(exception) + }) + } + cont.resume(Unit) + } + + + + +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-wallet-proxy/.gitignore b/Accountant/accountant-ports/accountant-wallet-proxy/.gitignore new file mode 100644 index 000000000..f3a8317d6 --- /dev/null +++ b/Accountant/accountant-ports/accountant-wallet-proxy/.gitignore @@ -0,0 +1,4 @@ +*.iml +target/ +.mvn/ +.idea/ \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-wallet-proxy/mvnw b/Accountant/accountant-ports/accountant-wallet-proxy/mvnw new file mode 100755 index 000000000..a16b5431b --- /dev/null +++ b/Accountant/accountant-ports/accountant-wallet-proxy/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/Accountant/accountant-ports/accountant-wallet-proxy/mvnw.cmd b/Accountant/accountant-ports/accountant-wallet-proxy/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/Accountant/accountant-ports/accountant-wallet-proxy/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/Accountant/accountant-ports/accountant-wallet-proxy/pom.xml b/Accountant/accountant-ports/accountant-wallet-proxy/pom.xml new file mode 100644 index 000000000..bcae9b7cb --- /dev/null +++ b/Accountant/accountant-ports/accountant-wallet-proxy/pom.xml @@ -0,0 +1,139 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + accountant-wallet-proxy + 0.0.1-SNAPSHOT + accountant-wallet-proxy + Mixchange wallet proxy + + + 1.8 + 1.4.31 + ${version} + ${version} + 2020.0.2 + + + + + co.nilin.mixchange + matching-core + ${matching.version} + provided + + + co.nilin.mixchange + accountant-core + ${accountant.version} + provided + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework.cloud + spring-cloud-starter-consul-all + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.boot + spring-boot-starter-data-r2dbc + + + io.r2dbc + r2dbc-postgresql + runtime + + + org.postgresql + postgresql + runtime + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + io.projectreactor + reactor-test + test + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + diff --git a/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/mixchange/port/accountant/wallet/config/WebClientConfig.kt b/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/mixchange/port/accountant/wallet/config/WebClientConfig.kt new file mode 100644 index 000000000..bf5f49c95 --- /dev/null +++ b/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/mixchange/port/accountant/wallet/config/WebClientConfig.kt @@ -0,0 +1,25 @@ +package co.nilin.mixchange.port.accountant.wallet.config + +import org.springframework.cloud.client.ServiceInstance +import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties +import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer +import org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerExchangeFilterFunction +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.web.reactive.function.client.WebClient + +@Configuration +class WebClientConfig { + + @Bean + fun webClient(loadBalancerFactory: ReactiveLoadBalancer.Factory): WebClient { + return WebClient.builder() + .filter( + ReactorLoadBalancerExchangeFilterFunction( + loadBalancerFactory, LoadBalancerProperties(), emptyList() + ) + ) + .build() + } + +} diff --git a/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/mixchange/port/accountant/wallet/proxy/WalletProxyImpl.kt b/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/mixchange/port/accountant/wallet/proxy/WalletProxyImpl.kt new file mode 100644 index 000000000..bf197ee59 --- /dev/null +++ b/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/mixchange/port/accountant/wallet/proxy/WalletProxyImpl.kt @@ -0,0 +1,63 @@ +package co.nilin.mixchange.port.accountant.wallet.proxy + +import co.nilin.mixchange.accountant.core.spi.WalletProxy +import kotlinx.coroutines.reactive.awaitFirst +import org.springframework.beans.factory.annotation.Value +import org.springframework.core.ParameterizedTypeReference +import org.springframework.stereotype.Component +import org.springframework.web.reactive.function.client.WebClient +import java.math.BigDecimal +import java.net.URI +import java.time.LocalDateTime + +inline fun typeRef(): ParameterizedTypeReference = object : ParameterizedTypeReference() {} +data class TransferResult(val date: LocalDateTime, val sourceBalanceBeforeAction: Amount, val sourceBalanceAfterAction: Amount, val amount: Amount) +data class Amount(val currency: Currency, val amount: BigDecimal) +data class Currency(val name: String, val symbol: String, val precision: Int) + +@Component +class WalletProxyImpl(@Value("\${app.wallet.url}") val walletBaseUrl: String +, val webClient: WebClient +): WalletProxy { + override suspend fun transfer( + symbol: String, + senderWalletType: String, + senderUuid: String, + receiverWalletType: String, + receiverUuid: String, + amount: BigDecimal, + description: String?, + transferRef: String? + ) { + webClient.post() + .uri(URI.create("$walletBaseUrl/transfer/${amount}_${symbol}/from/${senderUuid}_${senderWalletType}/to/${receiverUuid}_${receiverWalletType}")) + .header("Content-Type", "application/json") + .retrieve() + .onStatus({ t -> t.isError }, { p -> + /* + p.bodyToMono(typeRef>()).map { t -> KycSejamException(p.statusCode().value().toString(), t.error?.errorCode.toString() + + "-" + t.error?.customMessage) } + */ + throw RuntimeException() + }) + .bodyToMono(typeRef()) + .log() + .awaitFirst() + + } + + override suspend fun canFulfil(symbol: String, walletType: String, uuid: String, amount: BigDecimal): Boolean { + data class BooleanResponse(val result: Boolean) + return webClient.get() + .uri(URI.create("$walletBaseUrl/$uuid/wallet_type/${walletType}/can_withdraw/${amount}_${symbol}")) + .header("Content-Type", "application/json") + .retrieve() + .onStatus({ t -> t.isError }, { p -> + throw RuntimeException() + }) + .bodyToMono(typeRef()) + .log() + .awaitFirst() + .result + } +} \ No newline at end of file diff --git a/Accountant/pom.xml b/Accountant/pom.xml new file mode 100644 index 000000000..025f2562b --- /dev/null +++ b/Accountant/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + co.nilin.mixchange + accountant-root + 0.0.1-SNAPSHOT + accountant-root + pom + Accountant root of Mixchange + + accountant-core + accountant-app + accountant-ports/accountant-persister-postgres + accountant-ports/accountant-wallet-proxy + accountant-ports/accountant-tempsubmitter-kafka + accountant-ports/accountant-eventlistener-kafka + + diff --git a/Deployment/.gitignore b/Deployment/.gitignore new file mode 100644 index 000000000..eaddd784f --- /dev/null +++ b/Deployment/.gitignore @@ -0,0 +1,3 @@ +runtime/ +*.iml + diff --git a/Deployment/docker-compose.yml b/Deployment/docker-compose.yml new file mode 100644 index 000000000..5990f9373 --- /dev/null +++ b/Deployment/docker-compose.yml @@ -0,0 +1,259 @@ +version: '3.3' +services: + zookeeper: + image: 'docker.io/bitnami/zookeeper:3-debian-10' + ports: + - '2181:2181' + volumes: + - $PWD/runtime/zookeeper_data:/bitnami + environment: + - ALLOW_ANONYMOUS_LOGIN=yes + networks: + - opex + deploy: + restart_policy: + condition: on-failure + kafka: + image: 'docker.io/bitnami/kafka:2-debian-10' + ports: + - '9092:9092' + volumes: + - $PWD/runtime/kafka-data:/bitnami + links: + - zookeeper:zk + environment: + - KAFKA_CFG_ZOOKEEPER_CONNECT=zk:2181 + - ALLOW_PLAINTEXT_LISTENER=yes + - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092 + depends_on: + - zookeeper + networks: + - opex + deploy: + restart_policy: + condition: on-failure + consul: + image: 'consul' + ports: + - '8500:8500' + - '8300:8300' + - '8600:8600' + environment: + - CONSUL_BIND_INTERFACE=eth0 + networks: + - opex + deploy: + restart_policy: + condition: on-failure + + redis: + image: "redis:alpine" + + command: redis-server + + ports: + - "6379:6379" + + volumes: + - $PWD/runtime/redis-data:/var/lib/redis + - $PWD/runtime/redis.conf:/usr/local/etc/redis/redis.conf + + environment: + - REDIS_REPLICATION_MODE=master + networks: + - opex + deploy: + restart_policy: + condition: on-failure + postgres-accountant: + image: "postgres" + ports: + - 5433:5432 + environment: + - POSTGRES_USER=opex + - POSTGRES_PASSWORD=hiopex + - POSTGRES_DB=opex_accountant + volumes: + - $PWD/runtime/accountant-data:/var/lib/postgresql/data/ + networks: + - opex + postgres-eventlog: + image: "postgres" + ports: + - 5434:5432 + environment: + - POSTGRES_USER=opex + - POSTGRES_PASSWORD=hiopex + - POSTGRES_DB=opex_eventlog + volumes: + - $PWD/runtime/eventlog-data:/var/lib/postgresql/data/ + networks: + - opex + postgres-auth: + image: "postgres" + ports: + - 5435:5432 + environment: + - POSTGRES_USER=opex + - POSTGRES_PASSWORD=hiopex + - POSTGRES_DB=opex_auth + volumes: + - $PWD/runtime/auth-data:/var/lib/postgresql/data/ + networks: + - opex + deploy: + restart_policy: + condition: on-failure + postgres-wallet: + image: "postgres" + ports: + - 5436:5432 + environment: + - POSTGRES_USER=opex + - POSTGRES_PASSWORD=hiopex + - POSTGRES_DB=opex_wallet + volumes: + - $PWD/runtime/wallet-data:/var/lib/postgresql/data/ + networks: + - opex + deploy: + restart_policy: + condition: on-failure + accountant: + container_name: accountant + build: + context: ../Accountant/accountant-app + dockerfile: Dockerfile + ports: + - 8089:8089 + environment: + - JAVA_OPTS=-Xmx256m + - SPRING_PROFILES_ACTIVE=docker + - KAFKA_IP_PORT=kafka:9092 + - REDIS_HOST=redis + - CONSUL_HOST=consul + - DB_IP_PORT=postgres-accountant + networks: + - opex + depends_on: + - zookeeper + - kafka + - redis + - consul + - postgres-accountant + eventlog: + container_name: eventlog + build: + context: ../EventLog/eventlog-app + dockerfile: Dockerfile + ports: + - 8090:8090 + environment: + - JAVA_OPTS=-Xmx256m + - SPRING_PROFILES_ACTIVE=docker + - KAFKA_IP_PORT=kafka:9092 + - REDIS_HOST=redis + - CONSUL_HOST=consul + - DB_IP_PORT=postgres-eventlog + networks: + - opex + depends_on: + - zookeeper + - kafka + - redis + - consul + - postgres-eventlog + matching-engine: + container_name: matching-engine + build: + context: ../MatchingEngine/matching-app + dockerfile: Dockerfile + ports: + - 8092:8092 + - 1046:1044 + environment: + - JAVA_OPTS=-Xmx256m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044 + - SPRING_PROFILES_ACTIVE=docker + - KAFKA_IP_PORT=kafka:9092 + - REDIS_HOST=redis + networks: + - opex + depends_on: + - zookeeper + - kafka + - redis + matching-gateway: + container_name: matching-gateway + build: + context: ../MatchingGateway/gateway-app + dockerfile: Dockerfile + ports: + - 8093:8093 + - 1047:1044 + environment: + - JAVA_OPTS=-Xmx256m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044 + - SPRING_PROFILES_ACTIVE=docker + - KAFKA_IP_PORT=kafka:9092 + - REDIS_HOST=redis + - CONSUL_HOST=consul + networks: + - opex + depends_on: + - zookeeper + - kafka + - consul + auth: + container_name: auth + build: + context: ../UserManagement/keycloak-gateway + dockerfile: Dockerfile + ports: + - 8083:8083 + - 1048:1044 + environment: + - JAVA_OPTS=-Xmx256m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044 + - SPRING_PROFILES_ACTIVE=docker + - KAFKA_IP_PORT=kafka:9092 + - REDIS_HOST=redis + - CONSUL_HOST=consul + - DB_IP_PORT=postgres-auth + networks: + - opex + depends_on: + - zookeeper + - kafka + - redis + - consul + - postgres-auth + deploy: + restart_policy: + condition: on-failure + wallet: + container_name: wallet + build: + context: ../Wallet/wallet-app + dockerfile: Dockerfile + ports: + - 8091:8091 + - 1049:1044 + environment: + - JAVA_OPTS=-Xmx256m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044 + - SPRING_PROFILES_ACTIVE=docker + - KAFKA_IP_PORT=kafka:9092 + - REDIS_HOST=redis + - CONSUL_HOST=consul + - DB_IP_PORT=postgres-wallet + networks: + - opex + depends_on: + - zookeeper + - kafka + - redis + - consul + - postgres-wallet + deploy: + restart_policy: + condition: on-failure +networks: + opex: + driver: bridge \ No newline at end of file diff --git a/EventLog/.gitignore b/EventLog/.gitignore new file mode 100644 index 000000000..f4e066ca5 --- /dev/null +++ b/EventLog/.gitignore @@ -0,0 +1,36 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ +!/.mvn/ + +.DS_Store diff --git a/EventLog/eventlog-app/.gitignore b/EventLog/eventlog-app/.gitignore new file mode 100644 index 000000000..549e00a2a --- /dev/null +++ b/EventLog/eventlog-app/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/EventLog/eventlog-app/Dockerfile b/EventLog/eventlog-app/Dockerfile new file mode 100644 index 000000000..f2cbd4c26 --- /dev/null +++ b/EventLog/eventlog-app/Dockerfile @@ -0,0 +1,5 @@ +FROM openjdk:8-jdk-alpine +VOLUME /tmp +ARG JAR_FILE=target/*.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"] \ No newline at end of file diff --git a/EventLog/eventlog-app/pom.xml b/EventLog/eventlog-app/pom.xml new file mode 100644 index 000000000..1e59072b6 --- /dev/null +++ b/EventLog/eventlog-app/pom.xml @@ -0,0 +1,189 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + eventlog-app + 0.0.1-SNAPSHOT + eventlog-app + Event log running app Mixchange + + + 1.8 + 1.4.31 + ${version} + ${version} + + + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + com.fasterxml.jackson.module + jackson-module-kotlin + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-webflux + + + co.nilin.mixchange + matching-core + ${matching.version} + + + co.nilin.mixchange + eventlog-core + ${eventlog.version} + + + co.nilin.mixchange + eventlog-eventlistener-kafka + ${eventlog.version} + + + co.nilin.mixchange + eventlog-persister-postgres + ${eventlog.version} + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18 + + + ${skip.unit.tests} + + + **/*IntegrationTest.java + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-test-source + generate-test-sources + + add-test-source + + + + src/test/java + + + + + compile + + add-source + + + + src/main/java + + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + + -Xjsr305=strict + + + spring + + 1.8 + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + compile + compile + + compile + + + + testCompile + test-compile + + testCompile + + + + + + opex-eventlog + + + diff --git a/EventLog/eventlog-app/src/main/kotlin/co/nilin/mixchange/eventlog/app/EventLogApp.kt b/EventLog/eventlog-app/src/main/kotlin/co/nilin/mixchange/eventlog/app/EventLogApp.kt new file mode 100644 index 000000000..eeb579d8a --- /dev/null +++ b/EventLog/eventlog-app/src/main/kotlin/co/nilin/mixchange/eventlog/app/EventLogApp.kt @@ -0,0 +1,12 @@ +package co.nilin.mixchange.app + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.runApplication +import org.springframework.context.annotation.ComponentScan + +@SpringBootApplication +@ComponentScan("co.nilin.mixchange") +class EventLogApp +fun main(args: Array) { + runApplication(*args) +} \ No newline at end of file diff --git a/EventLog/eventlog-app/src/main/kotlin/co/nilin/mixchange/eventlog/app/config/AppConfig.kt b/EventLog/eventlog-app/src/main/kotlin/co/nilin/mixchange/eventlog/app/config/AppConfig.kt new file mode 100644 index 000000000..4048aa997 --- /dev/null +++ b/EventLog/eventlog-app/src/main/kotlin/co/nilin/mixchange/eventlog/app/config/AppConfig.kt @@ -0,0 +1,121 @@ +package co.nilin.mixchange.eventlog.app.config + +import co.nilin.mixchange.eventlog.spi.EventPersister +import co.nilin.mixchange.eventlog.spi.OrderPersister +import co.nilin.mixchange.eventlog.spi.TradePersister +import co.nilin.mixchange.matching.core.eventh.events.* +import co.nilin.mixchange.port.eventlog.kafka.consumer.OrderKafkaListener +import co.nilin.mixchange.port.eventlog.kafka.spi.OrderSubmitRequestListener +import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest +import co.nilin.mixchange.port.trade.consumer.EventKafkaListener +import co.nilin.mixchange.port.trade.consumer.TradeKafkaListener +import co.nilin.mixchange.port.trade.spi.EventListener +import co.nilin.mixchange.port.trade.spi.TradeListener +import kotlinx.coroutines.asCoroutineDispatcher +import kotlinx.coroutines.runBlocking +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import java.util.concurrent.Executors + +@Configuration +class AppConfig { + + @Bean + fun orderListener(orderPersister: OrderPersister): OrderListener { + return OrderListener(orderPersister) + } + + @Bean + fun eventlogTradeListener(tradePersister: TradePersister): EventlogTradeListener { + return EventlogTradeListener(tradePersister) + } + + @Bean + fun eventlogEventListener( + orderPersister: OrderPersister, eventPersister: EventPersister + ): EventlogEventListener { + return EventlogEventListener(orderPersister, eventPersister) + } + + @Bean + fun orderKafkaListener():OrderKafkaListener { + return OrderKafkaListener(Executors.newFixedThreadPool(10).asCoroutineDispatcher()) + } + + @Autowired + fun configureOrderListener(orderKafkaListener: OrderKafkaListener, orderListener: OrderListener) { + orderKafkaListener.addOrderListener(orderListener) + } + + @Autowired + fun configureTradeListener(tradeKafkaListener: TradeKafkaListener, eventlogTradeListener: EventlogTradeListener) { + tradeKafkaListener.addTradeListener(eventlogTradeListener) + } + + @Autowired + fun configureEventListener(eventKafkaListener: EventKafkaListener, eventlogEventListener: EventlogEventListener) { + eventKafkaListener.addEventListener(eventlogEventListener) + } + + class OrderListener(val orderPersister: OrderPersister) : OrderSubmitRequestListener { + + override fun id(): String { + return "OrderListener" + } + + override suspend fun onOrder(order: OrderSubmitRequest, partition: Int, offset: Long, timestamp: Long) { + orderPersister.submitOrder( + SubmitOrderEvent( + order.ouid, + order.uuid, + order.orderId, + order.pair, + order.price, + order.quantity, + 0, + order.direction, + order.matchConstraint, + order.orderType + ) + ) + } + } + + class EventlogTradeListener(val tradePersister: TradePersister) : TradeListener { + + override fun id(): String { + return "TradeListener" + } + + override fun onTrade(tradeEvent: TradeEvent, partition: Int, offset: Long, timestamp: Long) { + runBlocking { + tradePersister.saveTrade(tradeEvent) + } + } + } + + class EventlogEventListener( + val orderPersister: OrderPersister, val eventPersister: EventPersister + ) : EventListener { + + override fun id(): String { + return "EventListener" + } + + override fun onEvent(coreEvent: CoreEvent, partition: Int, offset: Long, timestamp: Long) { + runBlocking { + if (coreEvent is CreateOrderEvent) + orderPersister.saveOrder(coreEvent) + else if (coreEvent is RejectOrderEvent) + orderPersister.rejectOrder(coreEvent) + else if (coreEvent is UpdatedOrderEvent) + orderPersister.updateOrder(coreEvent) + else if (coreEvent is CancelOrderEvent) + orderPersister.cancelOrder(coreEvent) + eventPersister.saveEvent(coreEvent) + } + println("onEvent") + } + } +} \ No newline at end of file diff --git a/EventLog/eventlog-app/src/main/resources/application-docker.yml b/EventLog/eventlog-app/src/main/resources/application-docker.yml new file mode 100644 index 000000000..5055b1c51 --- /dev/null +++ b/EventLog/eventlog-app/src/main/resources/application-docker.yml @@ -0,0 +1,11 @@ +server.port: 8090 +spring: + kafka: + bootstrap-servers: ${KAFKA_IP_PORT} + consumer: + group-id: eventlog + r2dbc: + url: r2dbc:postgresql://${DB_IP_PORT}/opex_eventlog + username: opex + password: hiopex + initialization-mode: always \ No newline at end of file diff --git a/EventLog/eventlog-app/src/main/resources/application.yml b/EventLog/eventlog-app/src/main/resources/application.yml new file mode 100644 index 000000000..2b7bd821f --- /dev/null +++ b/EventLog/eventlog-app/src/main/resources/application.yml @@ -0,0 +1,11 @@ +server.port: 8090 +spring: + kafka: + bootstrap-servers: localhost:9092 + consumer: + group-id: eventlog + r2dbc: + url: r2dbc:postgresql://localhost/opex_eventlog + username: opex + password: hiopex + initialization-mode: always \ No newline at end of file diff --git a/EventLog/eventlog-core/.gitignore b/EventLog/eventlog-core/.gitignore new file mode 100644 index 000000000..549e00a2a --- /dev/null +++ b/EventLog/eventlog-core/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/EventLog/eventlog-core/pom.xml b/EventLog/eventlog-core/pom.xml new file mode 100644 index 000000000..18abeb652 --- /dev/null +++ b/EventLog/eventlog-core/pom.xml @@ -0,0 +1,100 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + eventlog-core + 0.0.1-SNAPSHOT + eventlog-core + Event log of Mixchange + + + 1.8 + 1.4.31 + ${version} + + + + + co.nilin.mixchange + matching-core + ${matching.version} + provided + + + org.springframework.boot + spring-boot-starter + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + exchange.core2 + collections + 0.5.1 + + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + diff --git a/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Event.kt b/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Event.kt new file mode 100644 index 000000000..a7699c871 --- /dev/null +++ b/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Event.kt @@ -0,0 +1,4 @@ +package co.nilin.mixchange.eventlog.spi + +interface Event { +} \ No newline at end of file diff --git a/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/EventPersister.kt b/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/EventPersister.kt new file mode 100644 index 000000000..1ad5da724 --- /dev/null +++ b/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/EventPersister.kt @@ -0,0 +1,7 @@ +package co.nilin.mixchange.eventlog.spi + +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent + +interface EventPersister { + suspend fun saveEvent(event: CoreEvent): List +} \ No newline at end of file diff --git a/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Order.kt b/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Order.kt new file mode 100644 index 000000000..bc5b75cf8 --- /dev/null +++ b/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Order.kt @@ -0,0 +1,4 @@ +package co.nilin.mixchange.eventlog.spi + +interface Order { +} \ No newline at end of file diff --git a/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/OrderPersister.kt b/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/OrderPersister.kt new file mode 100644 index 000000000..58c4b56dd --- /dev/null +++ b/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/OrderPersister.kt @@ -0,0 +1,11 @@ +package co.nilin.mixchange.eventlog.spi + +import co.nilin.mixchange.matching.core.eventh.events.* + +interface OrderPersister { + suspend fun submitOrder(orderEvent: SubmitOrderEvent) + suspend fun rejectOrder(orderEvent: RejectOrderEvent) + suspend fun saveOrder(orderEvent: CreateOrderEvent) + suspend fun updateOrder(orderEvent: UpdatedOrderEvent) + suspend fun cancelOrder(orderEvent: CancelOrderEvent) +} \ No newline at end of file diff --git a/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Trade.kt b/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Trade.kt new file mode 100644 index 000000000..d2e763e20 --- /dev/null +++ b/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Trade.kt @@ -0,0 +1,4 @@ +package co.nilin.mixchange.eventlog.spi + +interface Trade { +} \ No newline at end of file diff --git a/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/TradePersister.kt b/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/TradePersister.kt new file mode 100644 index 000000000..543252f93 --- /dev/null +++ b/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/TradePersister.kt @@ -0,0 +1,7 @@ +package co.nilin.mixchange.eventlog.spi + +import co.nilin.mixchange.matching.core.eventh.events.TradeEvent + +interface TradePersister { + suspend fun saveTrade(tradeEvent: TradeEvent): Trade +} \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/.gitignore b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/.gitignore new file mode 100644 index 000000000..549e00a2a --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/mvnw b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/mvnw new file mode 100755 index 000000000..a16b5431b --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/mvnw.cmd b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/pom.xml b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/pom.xml new file mode 100644 index 000000000..2bd01e040 --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/pom.xml @@ -0,0 +1,106 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + eventlog-eventlistener-kafka + 0.0.1-SNAPSHOT + eventlog-eventlistener-kafka + Matching engine kafka trade handler of Mixchange + + + 1.8 + 1.4.31 + ${version} + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-webflux + + + co.nilin.mixchange + matching-core + ${matching.version} + provided + + + org.springframework.kafka + spring-kafka + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + org.springframework.kafka + spring-kafka-test + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/config/EventlogKafkaConfig.kt b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/config/EventlogKafkaConfig.kt new file mode 100644 index 000000000..939e38497 --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/config/EventlogKafkaConfig.kt @@ -0,0 +1,109 @@ +package co.nilin.mixchange.port.trade.config + + +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +import co.nilin.mixchange.port.eventlog.kafka.consumer.OrderKafkaListener +import co.nilin.mixchange.port.trade.consumer.EventKafkaListener +import co.nilin.mixchange.port.trade.consumer.TradeKafkaListener +import org.apache.kafka.clients.consumer.ConsumerConfig +import org.apache.kafka.clients.producer.ProducerConfig +import org.apache.kafka.common.serialization.StringDeserializer +import org.apache.kafka.common.serialization.StringSerializer +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.kafka.core.ConsumerFactory +import org.springframework.kafka.core.DefaultKafkaConsumerFactory +import org.springframework.kafka.core.DefaultKafkaProducerFactory +import org.springframework.kafka.core.KafkaTemplate +import org.springframework.kafka.core.ProducerFactory +import org.springframework.kafka.listener.ConcurrentMessageListenerContainer +import org.springframework.kafka.listener.ContainerProperties +import org.springframework.kafka.support.serializer.JsonDeserializer +import org.springframework.kafka.support.serializer.JsonSerializer +import java.util.* +import java.util.regex.Pattern + + +@Configuration +class EventlogKafkaConfig { + @Value("\${spring.kafka.bootstrap-servers}") + private val bootstrapServers: String? = null + + @Value("\${spring.kafka.consumer.group-id}") + private val groupId: String? = null + + @Bean("eventlogConsumerConfig") + fun consumerConfigs(): Map? { + val props: MutableMap = HashMap() + props[ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG] = bootstrapServers + props[ConsumerConfig.GROUP_ID_CONFIG] = groupId + props[ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG] = StringDeserializer::class.java + props[ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG] = JsonDeserializer::class.java + props[JsonDeserializer.TRUSTED_PACKAGES] = "co.nilin.mixchange.*" + return props + } + + @Bean("eventlogConsumerFactory") + fun consumerFactory(@Qualifier("eventlogConsumerConfig")consumerConfigs: Map): ConsumerFactory { + return DefaultKafkaConsumerFactory(consumerConfigs) + } + + @Bean("eventlogProducerConfig") + fun producerConfigs(): Map { + val props: MutableMap = HashMap() + props[ProducerConfig.BOOTSTRAP_SERVERS_CONFIG] = bootstrapServers + props[ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG] = StringSerializer::class.java + props[ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG] = JsonSerializer::class.java + return props + } + + @Bean("eventlogProducerFactory") + fun producerFactory(@Qualifier("eventlogProducerConfig") producerConfigs: Map): ProducerFactory { + return DefaultKafkaProducerFactory(producerConfigs) + } + + @Bean("eventlogKafkaTemplate") + fun kafkaTemplate(@Qualifier("eventlogProducerFactory") producerFactory: ProducerFactory): KafkaTemplate { + return KafkaTemplate(producerFactory) + } + + + @Autowired + @ConditionalOnBean(TradeKafkaListener::class) + fun configureTradeListener(tradeListener: TradeKafkaListener + , @Qualifier("eventlogConsumerFactory") consumerFactory: ConsumerFactory) { + val containerProps = ContainerProperties(Pattern.compile("trades_.*")) + containerProps.messageListener = tradeListener + val container = ConcurrentMessageListenerContainer(consumerFactory, containerProps) + container.setBeanName("TradeKafkaListenerContainer") + container.start() + } + + @Autowired + @ConditionalOnBean(EventKafkaListener::class) + fun configureEventListener(eventListener: EventKafkaListener + , @Qualifier("eventlogConsumerFactory") consumerFactory: ConsumerFactory) { + val containerProps = ContainerProperties(Pattern.compile("events_.*")) + containerProps.messageListener = eventListener + val container = ConcurrentMessageListenerContainer(consumerFactory, containerProps) + container.setBeanName("EventKafkaListenerContainer") + container.start() + } + + @Autowired + @ConditionalOnBean(OrderKafkaListener::class) + fun configureOrderListener(orderListener: OrderKafkaListener + , @Qualifier("eventlogConsumerFactory") consumerFactory: ConsumerFactory) { + val containerProps = ContainerProperties(Pattern.compile("orders_.*")) + containerProps.messageListener = orderListener + val container = ConcurrentMessageListenerContainer(consumerFactory, containerProps) + container.setBeanName("OrderKafkaListenerContainer") + container.start() + } + + +} \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/consumer/EventKafkaListener.kt b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/consumer/EventKafkaListener.kt new file mode 100644 index 000000000..e0e6359f3 --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/consumer/EventKafkaListener.kt @@ -0,0 +1,28 @@ +package co.nilin.mixchange.port.trade.consumer + + +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +import co.nilin.mixchange.port.trade.spi.EventListener +import org.apache.kafka.clients.consumer.ConsumerRecord +import org.springframework.kafka.listener.MessageListener +import org.springframework.stereotype.Component + +@Component +class EventKafkaListener: MessageListener { + val eventListeners = arrayListOf() + override fun onMessage(data: ConsumerRecord) { + eventListeners.forEach{ + tl -> tl.onEvent(data.value(), data.partition(), data.offset(), data.timestamp()) + } + } + + fun addEventListener(tl: EventListener){ + eventListeners.add(tl) + } + + fun removeEventListener(tl: EventListener){ + eventListeners.removeIf { + item -> item.id() == tl.id() + } + } +} \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/consumer/OrderKafkaListener.kt b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/consumer/OrderKafkaListener.kt new file mode 100644 index 000000000..c904188e7 --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/consumer/OrderKafkaListener.kt @@ -0,0 +1,34 @@ +package co.nilin.mixchange.port.eventlog.kafka.consumer + +import co.nilin.mixchange.port.eventlog.kafka.spi.OrderSubmitRequestListener +import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest +import kotlinx.coroutines.ExecutorCoroutineDispatcher +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import org.apache.kafka.clients.consumer.ConsumerRecord +import org.springframework.kafka.listener.MessageListener +import org.springframework.stereotype.Component + + +class OrderKafkaListener(private val executorCoroutineDispatcher: ExecutorCoroutineDispatcher) : MessageListener { + val orderListeners = arrayListOf() + override fun onMessage(data: ConsumerRecord) { + runBlocking { + orderListeners.forEach { tl -> + withContext(executorCoroutineDispatcher) { + tl.onOrder(data.value(), data.partition(), data.offset(), data.timestamp()) + } + } + } + } + + fun addOrderListener(tl: OrderSubmitRequestListener) { + orderListeners.add(tl) + } + + fun removeOrderListener(tl: OrderSubmitRequestListener) { + orderListeners.removeIf { item -> + item.id() == tl.id() + } + } +} \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/consumer/TradeKafkaListener.kt b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/consumer/TradeKafkaListener.kt new file mode 100644 index 000000000..1d21d73b1 --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/consumer/TradeKafkaListener.kt @@ -0,0 +1,29 @@ +package co.nilin.mixchange.port.trade.consumer + + +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +import co.nilin.mixchange.matching.core.eventh.events.TradeEvent +import co.nilin.mixchange.port.trade.spi.TradeListener +import org.apache.kafka.clients.consumer.ConsumerRecord +import org.springframework.kafka.listener.MessageListener +import org.springframework.stereotype.Component + +@Component +class TradeKafkaListener: MessageListener { + val tradeListeners = arrayListOf() + override fun onMessage(data: ConsumerRecord) { + tradeListeners.forEach{ + tl -> tl.onTrade(data.value(), data.partition(), data.offset(), data.timestamp()) + } + } + + fun addTradeListener(tl: TradeListener){ + tradeListeners.add(tl) + } + + fun removeTradeListener(tl: TradeListener){ + tradeListeners.removeIf { + item -> item.id() == tl.id() + } + } +} \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/spi/EventListener.kt b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/spi/EventListener.kt new file mode 100644 index 000000000..0f522ca46 --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/spi/EventListener.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.port.trade.spi + +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent + + +interface EventListener { + fun id(): String + fun onEvent(coreEvent: CoreEvent, partition: Int, offset: Long, timestamp: Long) +} \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/spi/OrderSubmitRequestListener.kt b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/spi/OrderSubmitRequestListener.kt new file mode 100644 index 000000000..7c4fcc473 --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/spi/OrderSubmitRequestListener.kt @@ -0,0 +1,8 @@ +package co.nilin.mixchange.port.eventlog.kafka.spi + +import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest + +interface OrderSubmitRequestListener { + fun id(): String + suspend fun onOrder(order: OrderSubmitRequest, partition: Int, offset: Long, timestamp: Long) +} \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/spi/TradeListener.kt b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/spi/TradeListener.kt new file mode 100644 index 000000000..7f37be88b --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/spi/TradeListener.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.port.trade.spi + +import co.nilin.mixchange.matching.core.eventh.events.TradeEvent + + +interface TradeListener { + fun id(): String + fun onTrade(tradeEvent: TradeEvent, partition: Int, offset: Long, timestamp: Long) +} \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt new file mode 100644 index 000000000..a25a9372a --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt @@ -0,0 +1,39 @@ +package co.nilin.mixchange.port.order.kafka.inout + +import co.nilin.mixchange.matching.core.model.MatchConstraint +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.OrderType + +public class OrderSubmitRequest() { + lateinit var ouid: String + lateinit var uuid: String + var orderId: Long? = null + lateinit var pair: co.nilin.mixchange.matching.core.model.Pair + var price: Long = 0 + var quantity: Long = 0 + var direction: OrderDirection = OrderDirection.BID + var matchConstraint: MatchConstraint = MatchConstraint.GTC + var orderType: OrderType = OrderType.LIMIT_ORDER + + constructor(ouid: String, + uuid: String, + orderId: Long?, + pair: co.nilin.mixchange.matching.core.model.Pair, + price: Long, + quantity: Long, + direction: OrderDirection, + matchConstraint: MatchConstraint, + orderType: OrderType):this(){ + this.ouid = ouid + this.uuid = uuid + this.orderId = orderId + this.pair = pair + this.price = price + this.quantity = quantity + this.direction = direction + this.matchConstraint = matchConstraint + this.orderType = orderType + } + + +} \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/.gitignore b/EventLog/eventlog-ports/eventlog-persister-postgres/.gitignore new file mode 100644 index 000000000..549e00a2a --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/mvnw b/EventLog/eventlog-ports/eventlog-persister-postgres/mvnw new file mode 100755 index 000000000..a16b5431b --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/mvnw.cmd b/EventLog/eventlog-ports/eventlog-persister-postgres/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/pom.xml b/EventLog/eventlog-ports/eventlog-persister-postgres/pom.xml new file mode 100644 index 000000000..bcaa625a5 --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/pom.xml @@ -0,0 +1,112 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + eventlog-persister-postgres + 0.0.1-SNAPSHOT + eventlog-persister-postgres + Persist items of Mixchange on Postgres + + + 1.8 + 1.4.31 + ${version} + ${version} + + + + + co.nilin.mixchange + matching-core + ${matching.version} + provided + + + co.nilin.mixchange + eventlog-core + ${eventlog.version} + provided + + + org.springframework.boot + spring-boot-starter-data-r2dbc + + + io.r2dbc + r2dbc-postgresql + runtime + + + org.postgresql + postgresql + runtime + + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + io.projectreactor + reactor-test + test + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/config/PostgresConfig.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/config/PostgresConfig.kt new file mode 100644 index 000000000..8639692c1 --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/config/PostgresConfig.kt @@ -0,0 +1,82 @@ +package co.nilin.mixchange.port.order.kafka.config + + +import org.springframework.context.annotation.Configuration +import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories +import org.springframework.r2dbc.core.DatabaseClient + + +@Configuration +@EnableR2dbcRepositories(basePackages = ["co.nilin.mixchange"]) +class PostgresConfig(db: DatabaseClient) { + + init { + val initDb = db.sql { + """ CREATE TABLE IF NOT EXISTS opex_orders ( + id SERIAL PRIMARY KEY, + ouid VARCHAR(72) NOT NULL UNIQUE, + symbol VARCHAR(20), + direction VARCHAR(20), + match_constraint VARCHAR(20), + order_type VARCHAR(20), + uuid VARCHAR(72) NOT NULL, + agent VARCHAR(20), + ip VARCHAR(11), + order_date TIMESTAMP, + create_date TIMESTAMP + ); + CREATE TABLE IF NOT EXISTS opex_order_events ( + id SERIAL PRIMARY KEY, + ouid VARCHAR(72) NOT NULL, + matching_orderid bigint, + price bigint, + quantity bigint, + filled_quantity bigint, + uuid VARCHAR(72) NOT NULL, + event VARCHAR(30) NOT NULL, + agent VARCHAR(20), + ip VARCHAR(11), + event_date TIMESTAMP, + create_date TIMESTAMP + ); + CREATE TABLE IF NOT EXISTS opex_events ( + id SERIAL PRIMARY KEY, + correlation_id VARCHAR(72), + ouid VARCHAR(72), + uuid VARCHAR(72), + symbol VARCHAR(20), + event VARCHAR(30) NOT NULL, + event_json TEXT NOT NULL, + agent VARCHAR(20), + ip VARCHAR(11), + event_date TIMESTAMP, + create_date TIMESTAMP + ); + CREATE TABLE IF NOT EXISTS opex_trades ( + id SERIAL PRIMARY KEY, + symbol VARCHAR(20), + taker_ouid VARCHAR(72) NOT NULL, + taker_uuid VARCHAR(72) NOT NULL, + taker_matching_orderid bigint, + taker_direction VARCHAR(20), + taker_price bigint, + taker_remained_quantity bigint, + maker_ouid VARCHAR(72) NOT NULL, + maker_uuid VARCHAR(72) NOT NULL, + maker_matching_orderid bigint, + maker_direction VARCHAR(20), + maker_price bigint, + maker_remained_quantity bigint, + matched_quantity bigint, + trade_date TIMESTAMP, + create_date TIMESTAMP + ); + """ + } + + initDb // initialize the database + .then() + .subscribe() // execute + } + +} diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/EventRepository.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/EventRepository.kt new file mode 100644 index 000000000..a7c681d41 --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/EventRepository.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.port.eventlog.postgres.dao + +import co.nilin.mixchange.port.eventlog.postgres.model.EventModel +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository + +@Repository +interface EventRepository: ReactiveCrudRepository { +} \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/OrderEventRepository.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/OrderEventRepository.kt new file mode 100644 index 000000000..20bc8cb06 --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/OrderEventRepository.kt @@ -0,0 +1,10 @@ +package co.nilin.mixchange.port.eventlog.postgres.dao + +import co.nilin.mixchange.port.eventlog.postgres.model.OrderEventsModel +import co.nilin.mixchange.port.eventlog.postgres.model.OrderModel +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository + +@Repository +interface OrderEventRepository: ReactiveCrudRepository { +} \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/OrderRepository.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/OrderRepository.kt new file mode 100644 index 000000000..a58984068 --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/OrderRepository.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.port.eventlog.postgres.dao + +import co.nilin.mixchange.port.eventlog.postgres.model.OrderModel +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository + +@Repository +interface OrderRepository: ReactiveCrudRepository { +} \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/TradeRepository.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/TradeRepository.kt new file mode 100644 index 000000000..819c270b9 --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/TradeRepository.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.port.eventlog.postgres.dao + +import co.nilin.mixchange.port.eventlog.postgres.model.TradeModel +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository + +@Repository +interface TradeRepository: ReactiveCrudRepository{ +} \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/impl/EventPersisterImpl.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/impl/EventPersisterImpl.kt new file mode 100644 index 000000000..e63c5e2b3 --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/impl/EventPersisterImpl.kt @@ -0,0 +1,72 @@ +package co.nilin.mixchange.port.eventlog.postgres.impl + +import co.nilin.mixchange.eventlog.spi.Event +import co.nilin.mixchange.eventlog.spi.EventPersister +import co.nilin.mixchange.matching.core.eventh.events.* +import co.nilin.mixchange.port.eventlog.postgres.dao.EventRepository +import co.nilin.mixchange.port.eventlog.postgres.model.EventModel +import kotlinx.coroutines.reactive.awaitFirst +import kotlinx.coroutines.reactive.awaitFirstOrNull +import org.springframework.stereotype.Component +import java.time.LocalDateTime +import java.util.* + +@Component +class EventPersisterImpl(val eventRepository: EventRepository) : EventPersister { + override suspend fun saveEvent(event: CoreEvent): List { + if (event is OneOrderEvent) { + return listOf(eventRepository.save( + EventModel( + null, + UUID.randomUUID().toString(), + event.ouid(), + event.uuid(), + event.pair.toString(), + event::class.simpleName!!, + "", + "agent", + "127.0.0.1", + event.eventDate, + LocalDateTime.now() + ) + ).awaitFirst()) + } else if (event is TradeEvent) { + val correlation = UUID.randomUUID().toString() + val tuple = eventRepository.save( + EventModel( + null, + correlation, + event.takerOuid, + event.takerUuid, + event.pair.toString(), + event::class.simpleName!!, + "", + "agent", + "127.0.0.1", + event.eventDate, + LocalDateTime.now() + ) + ).zipWhen { + eventRepository.save( + EventModel( + null, + correlation, + event.makerOuid, + event.makerUuid, + event.pair.toString(), + event::class.simpleName!!, + "", + "agent", + "127.0.0.1", + event.eventDate, + LocalDateTime.now() + ) + ) + }.awaitFirst() + return listOf(tuple.t1, tuple.t2) + } + TODO() + } + + +} \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/impl/OrderPersisterImpl.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/impl/OrderPersisterImpl.kt new file mode 100644 index 000000000..e426d6a94 --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/impl/OrderPersisterImpl.kt @@ -0,0 +1,66 @@ +package co.nilin.mixchange.port.eventlog.postgres.impl + +import co.nilin.mixchange.eventlog.spi.OrderPersister +import co.nilin.mixchange.matching.core.eventh.events.* +import co.nilin.mixchange.port.eventlog.postgres.dao.OrderEventRepository +import co.nilin.mixchange.port.eventlog.postgres.dao.OrderRepository +import co.nilin.mixchange.port.eventlog.postgres.model.OrderEventsModel +import co.nilin.mixchange.port.eventlog.postgres.model.OrderModel +import kotlinx.coroutines.reactive.awaitFirst +import kotlinx.coroutines.reactive.awaitFirstOrNull +import kotlinx.coroutines.reactive.awaitSingle +import org.springframework.stereotype.Component +import java.time.LocalDateTime + +@Component +class OrderPersisterImpl(val orderRepository: OrderRepository +, val orderEventRepository: OrderEventRepository): OrderPersister { + override suspend fun submitOrder(orderEvent: SubmitOrderEvent) { + orderRepository.save(OrderModel(null, orderEvent.ouid + , orderEvent.pair.toString() + , orderEvent.direction.toString() + , orderEvent.matchConstraint.toString() + , orderEvent.orderType.toString() + , orderEvent.uuid, "agent", "127.0.0.1", orderEvent.eventDate, LocalDateTime.now())) + .block() + orderEventRepository.save(OrderEventsModel(null + , orderEvent.ouid, orderEvent.uuid, orderEvent.orderId, orderEvent.price + , orderEvent.quantity, orderEvent.quantity - orderEvent.remainedQuantity + , orderEvent.javaClass.simpleName + , "agent", "127.0.0.1", orderEvent.eventDate, LocalDateTime.now())) + .awaitFirst() + + } + + override suspend fun rejectOrder(orderEvent: RejectOrderEvent) { + orderEventRepository.save(OrderEventsModel(null + , orderEvent.ouid, orderEvent.uuid, orderEvent.orderId, orderEvent.price + , orderEvent.quantity, 0 + , orderEvent.javaClass.simpleName + , "agent", "127.0.0.1", orderEvent.eventDate, LocalDateTime.now())).awaitFirst() + } + + override suspend fun saveOrder(orderEvent: CreateOrderEvent) { + orderEventRepository.save(OrderEventsModel(null + , orderEvent.ouid, orderEvent.uuid, orderEvent.orderId, orderEvent.price + , orderEvent.quantity, orderEvent.quantity - orderEvent.remainedQuantity + , orderEvent.javaClass.simpleName + , "agent", "127.0.0.1", orderEvent.eventDate, LocalDateTime.now())).awaitFirstOrNull() + } + + override suspend fun updateOrder(orderEvent: UpdatedOrderEvent) { + orderEventRepository.save(OrderEventsModel(null + , orderEvent.ouid, orderEvent.uuid, orderEvent.orderId, orderEvent.price + , orderEvent.quantity, orderEvent.quantity - orderEvent.remainedQuantity + , orderEvent.javaClass.simpleName + , "agent", "127.0.0.1", orderEvent.eventDate, LocalDateTime.now())).awaitFirstOrNull() + } + + override suspend fun cancelOrder(orderEvent: CancelOrderEvent) { + orderEventRepository.save(OrderEventsModel(null + , orderEvent.ouid, orderEvent.uuid, orderEvent.orderId, orderEvent.price + , orderEvent.quantity, orderEvent.quantity - orderEvent.remainedQuantity + , orderEvent.javaClass.simpleName + , "agent", "127.0.0.1", orderEvent.eventDate, LocalDateTime.now())).awaitFirst() + } +} \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/impl/TradePersisterImpl.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/impl/TradePersisterImpl.kt new file mode 100644 index 000000000..cb7762b04 --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/impl/TradePersisterImpl.kt @@ -0,0 +1,37 @@ +package co.nilin.mixchange.port.eventlog.postgres.impl + +import co.nilin.mixchange.eventlog.spi.Trade +import co.nilin.mixchange.eventlog.spi.TradePersister +import co.nilin.mixchange.matching.core.eventh.events.TradeEvent +import co.nilin.mixchange.port.eventlog.postgres.dao.TradeRepository +import co.nilin.mixchange.port.eventlog.postgres.model.TradeModel +import kotlinx.coroutines.reactive.awaitFirst +import org.springframework.stereotype.Component +import java.time.LocalDateTime + +@Component +class TradePersisterImpl(val tradeRepository: TradeRepository) : TradePersister { + override suspend fun saveTrade(tradeEvent: TradeEvent): Trade { + return tradeRepository.save( + TradeModel( + null, + tradeEvent.pair.toString(), + tradeEvent.takerOuid, + tradeEvent.takerUuid, + tradeEvent.takerOrderId, + tradeEvent.takerDirection.toString(), + tradeEvent.takerPrice, + tradeEvent.takerRemainedQuantity, + tradeEvent.makerOuid, + tradeEvent.makerUuid, + tradeEvent.makerOrderId, + tradeEvent.makerDirection.toString(), + tradeEvent.makerPrice, + tradeEvent.makerRemainedQuantity, + tradeEvent.matchedQuantity, + tradeEvent.eventDate, + LocalDateTime.now() + ) + ).awaitFirst() + } +} \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/EventModel.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/EventModel.kt new file mode 100644 index 000000000..cc489589d --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/EventModel.kt @@ -0,0 +1,23 @@ +package co.nilin.mixchange.port.eventlog.postgres.model + +import co.nilin.mixchange.eventlog.spi.Event +import co.nilin.mixchange.eventlog.spi.Trade +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.time.LocalDateTime + +@Table("opex_events") +class EventModel( + @Id var id: Long?, + val correlationId: String, + val ouid: String, + val uuid: String, + val symbol: String, + val event: String, + @Column("event_json") val eventJson: String, + val agent: String, + val ip: String, + @Column("event_date") val eventDate: LocalDateTime, + @Column("create_date") val createDate: LocalDateTime +) : Event \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/OrderEventsModel.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/OrderEventsModel.kt new file mode 100644 index 000000000..dda65f531 --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/OrderEventsModel.kt @@ -0,0 +1,20 @@ +package co.nilin.mixchange.port.eventlog.postgres.model + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.time.LocalDateTime +@Table("opex_order_events") +class OrderEventsModel(@Id var id: Long? + , var ouid: String + , val uuid: String + , @Column("matching_orderid") val matchingOrderId: Long? + , val price: Long? + , val quantity: Long? + , @Column("filled_quantity") val filledQuantity: Long? + , val event: String + , val agent: String + , val ip: String + , @Column("event_date") val eventDate: LocalDateTime + , @Column("create_date") val createDate: LocalDateTime +) \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/OrderModel.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/OrderModel.kt new file mode 100644 index 000000000..8700ec03a --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/OrderModel.kt @@ -0,0 +1,22 @@ +package co.nilin.mixchange.port.eventlog.postgres.model + +import co.nilin.mixchange.eventlog.spi.Order +import co.nilin.mixchange.eventlog.spi.Trade +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.time.LocalDateTime + +@Table("opex_orders") +class OrderModel(@Id var id: Long? +, val ouid: String +, val symbol: String +, val direction: String +, @Column("match_constraint") val matchConstraint: String +, @Column("order_type") val orderType: String +, val uuid: String +, val agent: String +, val ip: String +, @Column("order_date") val orderDate: LocalDateTime +, @Column("create_date") val createDate: LocalDateTime +): Order \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/TradeModel.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/TradeModel.kt new file mode 100644 index 000000000..4f953576a --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/TradeModel.kt @@ -0,0 +1,43 @@ +package co.nilin.mixchange.port.eventlog.postgres.model + +import co.nilin.mixchange.eventlog.spi.Trade +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.Pair +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.time.LocalDateTime + +@Table("opex_trades") +class TradeModel(@Id var id: Long?, + val symbol: String, + @Column("taker_ouid") + val takerOuid: String, + @Column("taker_uuid") + val takerUuid: String, + @Column("taker_matching_orderid") + val takerOrderId: Long, + @Column("taker_direction") + val takerDirection: String, + @Column("taker_price") + val takerPrice: Long, + @Column("taker_remained_quantity") + val takerRemainedQuantity: Long, + @Column("maker_ouid") + val makerOuid: String, + @Column("maker_uuid") + val makerUuid: String, + @Column("maker_matching_orderid") + val makerOrderId: Long, + @Column("maker_direction") + val makerDirection: String, + @Column("maker_price") + val makerPrice: Long, + @Column("maker_remained_quantity") + val makerRemainedQuantity: Long, + @Column("matched_quantity") + val matchedQuantity: Long, + @Column("trade_date") + val eventDate: LocalDateTime, + @Column("create_date") + val createDate: LocalDateTime): Trade \ No newline at end of file diff --git a/EventLog/pom.xml b/EventLog/pom.xml new file mode 100644 index 000000000..035449a5c --- /dev/null +++ b/EventLog/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + co.nilin.mixchange + eventlog + 0.0.1-SNAPSHOT + eventlog + pom + Event log root of Mixchange + + + eventlog-core + eventlog-ports/eventlog-persister-postgres + eventlog-ports/eventlog-eventlistener-kafka + eventlog-app + + diff --git a/LICENSE-THIRD-PARTY b/LICENSE-THIRD-PARTY new file mode 100644 index 000000000..593602476 --- /dev/null +++ b/LICENSE-THIRD-PARTY @@ -0,0 +1,6 @@ +The Opex source code itself does not bundle any third party libraries, but it +depends on a number of libraries which carry their own copyright notices and +license terms. These libraries are normally all linked static into the binary +distributions of Opex: +exchange-core/collection - https://github.com/exchange-core/collections/blob/master/LICENSE + diff --git a/MatchingEngine/.gitignore b/MatchingEngine/.gitignore new file mode 100644 index 000000000..d0fc406b4 --- /dev/null +++ b/MatchingEngine/.gitignore @@ -0,0 +1,86 @@ +# Created by .ignore support plugin (hsz.mobi) +### Kotlin template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 +.idea/ +*.iml +*.ipr + +# CMake +cmake-build-*/ + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + + +### macOS template +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + diff --git a/MatchingEngine/matching-app/.gitignore b/MatchingEngine/matching-app/.gitignore new file mode 100644 index 000000000..7f15adddb --- /dev/null +++ b/MatchingEngine/matching-app/.gitignore @@ -0,0 +1,77 @@ +# Created by .ignore support plugin (hsz.mobi) +### Kotlin template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +.idea/artifacts +.idea/compiler.xml +.idea/jarRepositories.xml +.idea/modules.xml +.idea/*.iml +.idea/modules +*.iml +*.ipr + +# File-based project format +*.iws + +# IntelliJ +out/ + +target/ + + + + diff --git a/MatchingEngine/matching-app/Dockerfile b/MatchingEngine/matching-app/Dockerfile new file mode 100644 index 000000000..f2cbd4c26 --- /dev/null +++ b/MatchingEngine/matching-app/Dockerfile @@ -0,0 +1,5 @@ +FROM openjdk:8-jdk-alpine +VOLUME /tmp +ARG JAR_FILE=target/*.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"] \ No newline at end of file diff --git a/MatchingEngine/matching-app/pom.xml b/MatchingEngine/matching-app/pom.xml new file mode 100644 index 000000000..ea65e34bd --- /dev/null +++ b/MatchingEngine/matching-app/pom.xml @@ -0,0 +1,188 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + matching-app + 0.0.1-SNAPSHOT + matching-app + Matching engine running app Mixchange + + + 1.8 + 1.4.31 + ${version} + + + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + com.fasterxml.jackson.module + jackson-module-kotlin + + + org.springframework.boot + spring-boot-starter + + + co.nilin.mixchange + matching-core + ${matching.version} + + + co.nilin.mixchange + matching-submitter-kafka + ${matching.version} + + + co.nilin.mixchange + matching-eventlistener-kafka + ${matching.version} + + + co.nilin.mixchange + matching-snapshots-redis + ${matching.version} + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18 + + + ${skip.unit.tests} + + + **/*IntegrationTest.java + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-test-source + generate-test-sources + + add-test-source + + + + src/test/java + + + + + compile + + add-source + + + + src/main/java + + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + + -Xjsr305=strict + + + spring + + 1.8 + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + compile + compile + + compile + + + + testCompile + test-compile + + testCompile + + + + + + opex-matching-engine + + + diff --git a/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/MatchingEngineApp.kt b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/MatchingEngineApp.kt new file mode 100644 index 000000000..c67a50220 --- /dev/null +++ b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/MatchingEngineApp.kt @@ -0,0 +1,12 @@ +package co.nilin.mixchange.app + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.runApplication +import org.springframework.context.annotation.ComponentScan + +@SpringBootApplication +@ComponentScan("co.nilin.mixchange") +class MatchingEngineApp +fun main(args: Array) { + runApplication(*args) +} \ No newline at end of file diff --git a/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/bl/ExchangeEventHandler.kt b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/bl/ExchangeEventHandler.kt new file mode 100644 index 000000000..6cfa938d1 --- /dev/null +++ b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/bl/ExchangeEventHandler.kt @@ -0,0 +1,29 @@ +package co.nilin.mixchange.app.bl + +import co.nilin.mixchange.matching.core.eventh.EventDispatcher +import co.nilin.mixchange.matching.core.eventh.events.* +import co.nilin.mixchange.port.order.kafka.service.EventsSubmitter +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.springframework.stereotype.Component + +@Component +class ExchangeEventHandler(eventsSubmitter: EventsSubmitter) +{ + fun register(){ + EventDispatcher.register(CreateOrderEvent::class.java, handler) + EventDispatcher.register(CancelOrderEvent::class.java, handler) + EventDispatcher.register(UpdatedOrderEvent::class.java, handler) + EventDispatcher.register(RejectOrderEvent::class.java, handler) + EventDispatcher.register(SubmitOrderEvent::class.java, handler) + EventDispatcher.register(TradeEvent::class.java, handler) + } + + val handler: (CoreEvent) -> Unit = { + CoroutineScope(Dispatchers.Default).launch { + eventsSubmitter.submit(it) + } + } + +} \ No newline at end of file diff --git a/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/bl/OrderBooks.kt b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/bl/OrderBooks.kt new file mode 100644 index 000000000..14265c6d1 --- /dev/null +++ b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/bl/OrderBooks.kt @@ -0,0 +1,26 @@ +package co.nilin.mixchange.app.bl + +import co.nilin.mixchange.matching.core.factory.OrderBookFactory +import co.nilin.mixchange.matching.core.model.OrderBook +import co.nilin.mixchange.matching.core.model.PersistentOrderBook + +object OrderBooks { + private val orderBooks = mutableMapOf() + + fun createOrderBook(pair: String) { + println("Going to add order book:" + pair + ", current order books#" + orderBooks.size) + if ( orderBooks.containsKey(pair)) + throw IllegalArgumentException("${pair} has an order book right now!") + val pairs = pair.split("_") + orderBooks[pair] = OrderBookFactory.createOrderBook(co.nilin.mixchange.matching.core.model.Pair(pairs[0], pairs[1])) + println("order book:" + pair + " added, current order books#" + orderBooks.size) + } + + fun reloadOrderBook(orderBook: PersistentOrderBook){ + orderBooks["${orderBook.pair.leftSideName}_${orderBook.pair.rightSideName}"] = OrderBookFactory.createOrderBook(orderBook) + } + + fun lookupOrderBook(pair: String): OrderBook { + return orderBooks[pair]?:throw IllegalArgumentException("No orderbook for $pair") + } +} \ No newline at end of file diff --git a/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt new file mode 100644 index 000000000..c3e4fbaa6 --- /dev/null +++ b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt @@ -0,0 +1,104 @@ +package co.nilin.mixchange.app.config + +import co.nilin.mixchange.app.bl.ExchangeEventHandler +import co.nilin.mixchange.app.bl.OrderBooks +import co.nilin.mixchange.matching.core.inout.OrderCreateCommand +import co.nilin.mixchange.matching.core.model.PersistentOrderBook +import co.nilin.mixchange.matching.core.spi.OrderBookPersister +import co.nilin.mixchange.port.order.kafka.consumer.OrderKafkaListener +import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest +import co.nilin.mixchange.port.order.kafka.spi.OrderSubmitRequestListener +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import kotlin.coroutines.EmptyCoroutineContext + +@Configuration +class AppConfig { + @Value("\${spring.app.symbols}") + private val symbols: String? = null + + @Bean + @ConditionalOnMissingBean(value = [OrderBookPersister::class]) + fun orderBookPersister(): OrderBookPersister { + return object : OrderBookPersister { + override suspend fun storeLastState(orderBook: PersistentOrderBook) { + } + + override suspend fun loadLastState(symbol: String): PersistentOrderBook? { + return null + } + } + } + + + @Autowired + fun configureOrderBooks(orderBookPersister: OrderBookPersister) { + symbols!!.split(",") + .forEach { symbol -> + CoroutineScope(AppSchedulers.generalExecutor).launch { + val lastOrderBook = orderBookPersister.loadLastState(symbol) + //todo: load db orders from last order in order book and put in order book + //todo: add missing orders to lastOrderBook or create one + if (lastOrderBook != null) { + withContext(coroutineContext) { + OrderBooks.reloadOrderBook(lastOrderBook) + } + } else { + OrderBooks.createOrderBook(symbol) + } + + } + } + } + + + @Bean + fun orderListener(orderBookPersister: OrderBookPersister): OrderListener { + return OrderListener(orderBookPersister) + } + + @Autowired + fun configureOrderListener(orderKafkaListener: OrderKafkaListener, orderListener: OrderListener) { + orderKafkaListener.addOrderListener(orderListener) + } + + @Autowired + fun configureMatchingEngineListener(exchangeEventHandler: ExchangeEventHandler) { + exchangeEventHandler.register() + } + + class OrderListener(val orderBookPersister: OrderBookPersister) : OrderSubmitRequestListener { + + override fun id(): String { + return "OrderListener" + } + + override suspend fun onOrder(order: OrderSubmitRequest, partition: Int, offset: Long, timestamp: Long) { + val orderBook = OrderBooks.lookupOrderBook( + order.pair.leftSideName + "_" + + order.pair.rightSideName + ) + orderBook.handleNewOrderCommand( + OrderCreateCommand( + order.ouid, + order.uuid, + order.pair, + order.price, + order.quantity, + order.direction, + order.matchConstraint, + order.orderType + ) + ) + orderBookPersister.storeLastState(orderBook.persistent()) + } + } + +} \ No newline at end of file diff --git a/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/config/AppSchedulers.kt b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/config/AppSchedulers.kt new file mode 100644 index 000000000..1c8f69e51 --- /dev/null +++ b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/config/AppSchedulers.kt @@ -0,0 +1,8 @@ +package co.nilin.mixchange.app.config + +import kotlinx.coroutines.asCoroutineDispatcher +import java.util.concurrent.Executors + +object AppSchedulers { + val generalExecutor = Executors.newFixedThreadPool(5).asCoroutineDispatcher() +} \ No newline at end of file diff --git a/MatchingEngine/matching-app/src/main/resources/application-docker.yml b/MatchingEngine/matching-app/src/main/resources/application-docker.yml new file mode 100644 index 000000000..4e47d2249 --- /dev/null +++ b/MatchingEngine/matching-app/src/main/resources/application-docker.yml @@ -0,0 +1,13 @@ +server.port: 8092 +spring: + main: + allow-bean-definition-overriding: false + kafka: + bootstrap-servers: ${KAFKA_IP_PORT} + consumer: + group-id: engine + redis: + hostname: ${REDIS_HOST} + port: 6379 + app: + symbols: btc_usdt,eth_usdt,eth_btc \ No newline at end of file diff --git a/MatchingEngine/matching-app/src/main/resources/application.yml b/MatchingEngine/matching-app/src/main/resources/application.yml new file mode 100644 index 000000000..cc1bcacd1 --- /dev/null +++ b/MatchingEngine/matching-app/src/main/resources/application.yml @@ -0,0 +1,13 @@ +server.port: 8092 +spring: + main: + allow-bean-definition-overriding: false + kafka: + bootstrap-servers: localhost:9092 + consumer: + group-id: engine + redis: + hostname: 127.0.0.1 + port: 6379 + app: + symbols: btc_usdt,eth_usdt,eth_btc \ No newline at end of file diff --git a/MatchingEngine/matching-core/.gitignore b/MatchingEngine/matching-core/.gitignore new file mode 100644 index 000000000..7f15adddb --- /dev/null +++ b/MatchingEngine/matching-core/.gitignore @@ -0,0 +1,77 @@ +# Created by .ignore support plugin (hsz.mobi) +### Kotlin template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +.idea/artifacts +.idea/compiler.xml +.idea/jarRepositories.xml +.idea/modules.xml +.idea/*.iml +.idea/modules +*.iml +*.ipr + +# File-based project format +*.iws + +# IntelliJ +out/ + +target/ + + + + diff --git a/MatchingEngine/matching-core/mvnw b/MatchingEngine/matching-core/mvnw new file mode 100755 index 000000000..a16b5431b --- /dev/null +++ b/MatchingEngine/matching-core/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/MatchingEngine/matching-core/mvnw.cmd b/MatchingEngine/matching-core/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/MatchingEngine/matching-core/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/MatchingEngine/matching-core/pom.xml b/MatchingEngine/matching-core/pom.xml new file mode 100644 index 000000000..96a1cbcfb --- /dev/null +++ b/MatchingEngine/matching-core/pom.xml @@ -0,0 +1,93 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + matching-core + 0.0.1-SNAPSHOT + matching-core + Matching engine of Mixchange + + + 1.8 + 1.4.31 + + + + + org.springframework.boot + spring-boot-starter + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + exchange.core2 + collections + 0.5.1 + + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/engine/SimpleOrderBook.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/engine/SimpleOrderBook.kt new file mode 100644 index 000000000..623fa9188 --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/engine/SimpleOrderBook.kt @@ -0,0 +1,372 @@ +package co.nilin.mixchange.matching.core.engine + +import co.nilin.mixchange.matching.core.eventh.EventDispatcher +import co.nilin.mixchange.matching.core.eventh.events.* +import co.nilin.mixchange.matching.core.inout.* +import co.nilin.mixchange.matching.core.model.* +import exchange.core2.collections.art.LongAdaptiveRadixTreeMap +import java.util.* +import java.util.concurrent.atomic.AtomicLong + +class SimpleOrderBook(val pair: Pair, var replayMode: Boolean) : OrderBook { + + val askOrders = LongAdaptiveRadixTreeMap() + val bidOrders = LongAdaptiveRadixTreeMap() + val orders = TreeMap() + + var bestAskOrder: SimpleOrder? = null + var bestBidOrder: SimpleOrder? = null + + val orderCounter = AtomicLong() + + var lastOrder: SimpleOrder? = null + + data class SimpleOrder(var id: Long?, val ouid: String, val uuid: String, val price: Long, val quantity: Long, val matchConstraint: MatchConstraint, val orderType: OrderType, val direction: OrderDirection, var filledQuantity: Long, var worse: SimpleOrder?, var better: SimpleOrder?, var bucket: Bucket?) : Order { + fun remainedQuantity() = quantity - filledQuantity + override fun id(): Long? = id + override fun toString(): String { + return "SimpleOrder(id=$id, price=$price, quantity=$quantity, matchConstraint=$matchConstraint, orderType=$orderType, filledQuantity=$filledQuantity, worse=${worse?.id}, better=${better?.id}, bucket=${bucket?.totalQuantity})" + } + + override fun persistent(): PersistentOrder { + return PersistentOrder(id!!, ouid, uuid, price, quantity, matchConstraint, orderType, direction, filledQuantity) + } + } + + data class Bucket(val price: Long, var totalQuantity: Long, var ordersCount: Long, var lastOrder: SimpleOrder) + + override fun handleNewOrderCommand(orderCommand: OrderCreateCommand): Order? { + val order = when (orderCommand.matchConstraint) { + MatchConstraint.GTC -> { + if (orderCommand.orderType == OrderType.MARKET_ORDER) { + if (!replayMode) { + EventDispatcher.emit(RejectOrderEvent(orderCommand.ouid, orderCommand.uuid, orderCommand.pair, orderCommand.price, orderCommand.quantity, orderCommand.direction, orderCommand.matchConstraint, orderCommand.orderType, RequestedOperation.PLACE_ORDER, RejectReason.ORDER_TYPE_NOT_MATCHED_MATCHC)) + } + return null + } + val order = SimpleOrder(orderCounter.incrementAndGet(), orderCommand.ouid, orderCommand.uuid, orderCommand.price, orderCommand.quantity, orderCommand.matchConstraint, orderCommand.orderType, orderCommand.direction, 0, null, null, null) + if (!replayMode) { + EventDispatcher.emit(CreateOrderEvent(orderCommand.ouid, orderCommand.uuid, + order.id!!, orderCommand.pair, orderCommand.price, orderCommand.quantity, order.remainedQuantity(), orderCommand.direction, orderCommand.matchConstraint, orderCommand.orderType + )) + } + // try to match instantly + val queueOrder = matchInstantly(order) + //if remained quantity > 0 add to queue + if (queueOrder.filledQuantity != queueOrder.quantity) { + putGtcInQueue(queueOrder) + } + queueOrder + } + MatchConstraint.IOC -> { + val order = SimpleOrder(orderCounter.incrementAndGet(), orderCommand.ouid, orderCommand.uuid,orderCommand.price, orderCommand.quantity, orderCommand.matchConstraint, orderCommand.orderType, orderCommand.direction, 0, null, null, null) + if (!replayMode) { + EventDispatcher.emit( + CreateOrderEvent( + orderCommand.ouid, orderCommand.uuid, + order.id!!, orderCommand.pair, orderCommand.price, + orderCommand.quantity, order.remainedQuantity(), + orderCommand.direction, orderCommand.matchConstraint, orderCommand.orderType + ) + ) + } + // try to match instantly + val queueOrder = matchIocInstantly(order) + if (!replayMode) { + if (queueOrder.filledQuantity != queueOrder.quantity) { + EventDispatcher.emit(CancelOrderEvent(orderCommand.ouid, orderCommand.uuid, + queueOrder.id!!, orderCommand.pair, order.price, order.quantity, order.remainedQuantity(), order.direction, order.matchConstraint, order.orderType + )) + } + } + queueOrder + } + else -> { + if (!replayMode) { + EventDispatcher.emit(RejectOrderEvent(orderCommand.ouid, orderCommand.uuid,orderCommand.pair, orderCommand.price, orderCommand.quantity, orderCommand.direction, orderCommand.matchConstraint, orderCommand.orderType, RequestedOperation.PLACE_ORDER, RejectReason.OPERATION_NOT_MATCHED_MATCHC)) + } + null + } + } + lastOrder = order + return order + } + + override fun handleCancelCommand(orderCommand: OrderCancelCommand) { + val order = orders.remove(orderCommand.orderId) + if (order == null /*check for userid*/) { + if (!replayMode) { + EventDispatcher.emit(RejectOrderEvent(orderCommand.ouid, orderCommand.uuid,orderCommand.orderId, orderCommand.pair, RequestedOperation.CANCEL_ORDER, RejectReason.ORDER_NOT_FOUND)) + } + return + } + if (order.direction == OrderDirection.BID) { + handleCancelOrder(order, bidOrders, bestBidOrder) { newBestOrder: SimpleOrder? -> + bestBidOrder = newBestOrder + } + } else { + handleCancelOrder(order, askOrders, bestAskOrder) { newBestOrder: SimpleOrder? -> + bestAskOrder = newBestOrder + } + } + if (!replayMode) { + EventDispatcher.emit(CancelOrderEvent(orderCommand.ouid, orderCommand.uuid, + orderCommand.orderId, orderCommand.pair, + order.price, order.quantity, + order.remainedQuantity(), order.direction, + order.matchConstraint, order.orderType + )) + } + } + + override fun handleEditCommand(orderCommand: OrderEditCommand): Order? { + val order = orders.remove(orderCommand.orderId) + if (order == null /*check for userid*/) { + if (!replayMode) { + EventDispatcher.emit(RejectOrderEvent(orderCommand.ouid, orderCommand.uuid,orderCommand.orderId, orderCommand.pair, RequestedOperation.EDIT_ORDER, RejectReason.ORDER_NOT_FOUND)) + } + return order + } + if (order.direction == OrderDirection.BID) { + handleCancelOrder(order, bidOrders, bestBidOrder) { newBestOrder: SimpleOrder? -> + bestBidOrder = newBestOrder + } + } else { + handleCancelOrder(order, askOrders, bestAskOrder) { newBestOrder: SimpleOrder? -> + bestAskOrder = newBestOrder + } + } + val newOrder = SimpleOrder(order.id, orderCommand.ouid, orderCommand.uuid,orderCommand.price, orderCommand.quantity, order.matchConstraint, order.orderType, order.direction, order.filledQuantity, null, null, null) + + return when (order.matchConstraint) { + MatchConstraint.GTC -> { + if (!replayMode) { + EventDispatcher.emit(UpdatedOrderEvent(orderCommand.ouid, orderCommand.uuid, + order.id!!, orderCommand.pair, order.price, order.quantity, + orderCommand.price, orderCommand.quantity, order.remainedQuantity(), + order.direction, order.matchConstraint, order.orderType + )) + } + // try to match instantly + val queueOrder = matchInstantly(newOrder) + //if remained quantity > 0 add to queue + if (queueOrder.filledQuantity != queueOrder.quantity) { + putGtcInQueue(queueOrder) + } + queueOrder + } + MatchConstraint.IOC -> { + if (!replayMode) { + EventDispatcher.emit(UpdatedOrderEvent(orderCommand.ouid, orderCommand.uuid, + order.id!!, orderCommand.pair, order.price, order.quantity, orderCommand.price, orderCommand.quantity, order.remainedQuantity(), order.direction, order.matchConstraint, order.orderType + )) + } + // try to match instantly + val queueOrder = matchIocInstantly(newOrder) + if (!replayMode) { + if (queueOrder.filledQuantity != queueOrder.quantity) { + EventDispatcher.emit(CancelOrderEvent(orderCommand.ouid, orderCommand.uuid, + queueOrder.id!!, orderCommand.pair, order.price, order.quantity, order.remainedQuantity(), order.direction, order.matchConstraint, order.orderType + )) + } + } + queueOrder + } + else -> { + if (!replayMode) { + EventDispatcher.emit(RejectOrderEvent(orderCommand.ouid, orderCommand.uuid,orderCommand.orderId, orderCommand.pair, orderCommand.price, orderCommand.quantity, order.direction, order.matchConstraint, order.orderType, RequestedOperation.EDIT_ORDER, RejectReason.OPERATION_NOT_MATCHED_MATCHC)) + } + null + } + } + + } + + fun handleCancelOrder(order: SimpleOrder, bucketQueue: LongAdaptiveRadixTreeMap, bestOrder: SimpleOrder?, setBestOrder: (SimpleOrder?) -> Unit) { + val bucket = order.bucket!! + bucket.ordersCount-- + bucket.totalQuantity -= order.remainedQuantity() + if (bucket.lastOrder == order) { + if (bucket.lastOrder.better == null || bucket.lastOrder.better!!.bucket != bucket) { + bucketQueue.remove(bucket.price) + } else + bucket.lastOrder = bucket.lastOrder.better!! + } + order.better?.worse = order.worse + order.worse?.better = order.better + if (order == bestOrder) + setBestOrder(bestOrder.worse) + } + + private fun matchInstantly(order: SimpleOrder): SimpleOrder { + if (order.direction == OrderDirection.BID) { + return matchInstantly(order, bestAskOrder, askOrders, { makerPrice: Long -> + makerPrice <= order.price + }) { newMakerOrder: SimpleOrder? -> + bestAskOrder = newMakerOrder + } + } else { + return matchInstantly(order, bestBidOrder, bidOrders, { makerPrice: Long -> + makerPrice >= order.price + }) { newMakerOrder: SimpleOrder? -> + bestBidOrder = newMakerOrder + } + } + } + + private fun matchIocInstantly(order: SimpleOrder): SimpleOrder { + if (order.direction == OrderDirection.BID) { + return matchInstantly(order, bestAskOrder, askOrders, { makerPrice: Long -> + order.orderType == OrderType.MARKET_ORDER || makerPrice <= order.price + + }) { newMakerOrder: SimpleOrder? -> + bestAskOrder = newMakerOrder + } + } else { + return matchInstantly(order, bestBidOrder, bidOrders, { makerPrice: Long -> + order.orderType == OrderType.MARKET_ORDER || makerPrice >= order.price + }) { newMakerOrder: SimpleOrder? -> + bestBidOrder = newMakerOrder + } + } + } + + private fun putGtcInQueue(order: SimpleOrder): SimpleOrder { + if (order.direction == OrderDirection.BID) { + return putGtcInQueue(order, bidOrders, { price, queue -> + queue.getHigherValue(price) + }) { newMakerOrder: SimpleOrder? -> + bestBidOrder = newMakerOrder + } + } else { + return putGtcInQueue(order, askOrders, { price, queue -> + queue.getLowerValue(price) + }) { newMakerOrder: SimpleOrder? -> + bestAskOrder = newMakerOrder + } + } + } + + private fun matchInstantly(order: SimpleOrder, makerOrder: SimpleOrder?, queue: LongAdaptiveRadixTreeMap, isPriceMatched: (makerPrice: Long) -> Boolean, setNewMarkerOrder: (SimpleOrder?) -> Unit): SimpleOrder { + //the best sell price is higher the requested buy price, so no instant match + if (makerOrder == null || !isPriceMatched(makerOrder.price)) { + return order + } + var currentMaker = makerOrder + var lastOrderOfMakerBucket = makerOrder.bucket!!.lastOrder + do { + val instantMatchQuantity = Math.min(order.remainedQuantity(), currentMaker!!.remainedQuantity()) + order.filledQuantity += instantMatchQuantity + currentMaker.filledQuantity += instantMatchQuantity + currentMaker.bucket!!.totalQuantity -= instantMatchQuantity + if (!replayMode) { + EventDispatcher.emit(TradeEvent(pair, order.ouid, order.uuid, order.id + ?: 0, order.direction, order.price, order.remainedQuantity(), currentMaker.ouid, currentMaker.uuid, currentMaker.id!!, currentMaker.direction, currentMaker.price, currentMaker.remainedQuantity(), instantMatchQuantity)) + } + if (currentMaker.remainedQuantity() == 0L) { + currentMaker.bucket!!.ordersCount-- + } + //create trade with instantMatchQuantity + if (currentMaker.remainedQuantity() > 0) { + break + } + //remove the makerOrder + orders.remove(currentMaker.id!!) + if (currentMaker == lastOrderOfMakerBucket) { + queue.remove(currentMaker.price) + if (currentMaker.worse != null) + lastOrderOfMakerBucket = currentMaker.worse!!.bucket!!.lastOrder + } + + currentMaker = currentMaker.worse + } while (order.remainedQuantity() > 0 + && currentMaker != null + && isPriceMatched(currentMaker.price) + ) + if (currentMaker != null) { + currentMaker.better = null + } + setNewMarkerOrder(currentMaker) + return order + + } + + + fun putGtcInQueue(order: SimpleOrder, + queue: LongAdaptiveRadixTreeMap, + betterBucketSelector: (price: Long, queue: LongAdaptiveRadixTreeMap) -> Bucket?, + setNewMarkerOrder: (SimpleOrder?) -> Unit + ): SimpleOrder { + if (order.id == null) + order.id = orderCounter.incrementAndGet() + orders[order.id!!] = order + var bucket = queue[order.price] + if (bucket != null) { + bucket.ordersCount++ + bucket.totalQuantity += order.remainedQuantity() + order.bucket = bucket + val bucketLastOrder = bucket.lastOrder + val worseOfBucketLastOrder = bucketLastOrder.worse + bucket.lastOrder = order + bucketLastOrder.worse = order + if (worseOfBucketLastOrder != null) { + worseOfBucketLastOrder.better = order + } + order.better = bucketLastOrder + order.worse = worseOfBucketLastOrder + } else { + bucket = Bucket(order.price, order.remainedQuantity(), 1, order) + order.bucket = bucket + queue.put(order.price, bucket) + val betterBucket = betterBucketSelector(order.price, queue) + if (betterBucket != null) { + val aboveBucketLastOrder = betterBucket.lastOrder + val worseOrder = aboveBucketLastOrder.worse + aboveBucketLastOrder.worse = order + if (worseOrder != null) { + worseOrder.better = order + } + order.better = aboveBucketLastOrder + order.worse = worseOrder + } else { + if (bestBidOrder != null) + bestBidOrder!!.better = order + order.worse = bestBidOrder + setNewMarkerOrder(order) + } + } + return order + } + + override fun pair(): Pair { + return pair + } + + override fun startReplayMode() { + replayMode = true + } + + override fun stopReplayMode() { + replayMode = false + } + + override fun lastOrder(): Order? { + return lastOrder + } + + override fun persistent(): PersistentOrderBook { + val persistent = PersistentOrderBook(pair) + persistent.lastOrder = lastOrder?.persistent() + persistent.orders = orders.values + .map { order -> order.persistent() } + return persistent + } + + fun rebuild(persistentOrderBook: PersistentOrderBook) { + persistentOrderBook.orders?.map { order -> + SimpleOrder(order.id, order.ouid, order.uuid, order.price, order.quantity, order.matchConstraint, order.orderType, order.direction, order.filledQuantity, null, null, null) + }?.filter { order -> order.matchConstraint == MatchConstraint.GTC + }?.forEach { order -> putGtcInQueue(order) } + orderCounter.set(persistentOrderBook.lastOrder?.id?:0) + } +} \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/EventDispatcher.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/EventDispatcher.kt new file mode 100644 index 000000000..022cb84df --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/EventDispatcher.kt @@ -0,0 +1,50 @@ +package co.nilin.mixchange.matching.core.eventh + +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.asCoroutineDispatcher +import kotlinx.coroutines.launch +import java.util.* +import java.util.concurrent.Executors +import kotlin.coroutines.suspendCoroutine + +object EventDispatcher { + + private val executorService = Executors.newFixedThreadPool(10).asCoroutineDispatcher() + private val eventsHandler = mutableMapOf, MutableList>>() + + @JvmStatic + inline fun register(noinline lambda: (T) -> Unit) = register(T::class.java, lambda) + + @JvmStatic + fun register(type: Class, lambda: (T) -> Unit) = register(type, EventListener(lambda)) + + @JvmStatic + fun register(type: Class, listener: EventListener) { + eventsHandler.getOrPut(type, { LinkedList() }).add(listener) + } + + + fun emit(event: CoreEvent) = CoroutineScope(executorService).launch { + var type: Class<*>? = event::class.java + while (type != null) { + eventsHandler[type]?.forEach { eventsHandler -> + suspendCoroutine { + kotlin.runCatching { + eventsHandler(event) + } + } + } + type = type.superclass + } + } + + + open class EventListener( + val lambda: (T) -> Unit + ) { + operator fun invoke(event: Any) { + lambda(event as T) + } + } +} \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/CancelOrderEvent.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/CancelOrderEvent.kt new file mode 100644 index 000000000..1782a558c --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/CancelOrderEvent.kt @@ -0,0 +1,48 @@ +package co.nilin.mixchange.matching.core.eventh.events + +import co.nilin.mixchange.matching.core.model.MatchConstraint +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.OrderType + +class CancelOrderEvent() : CoreEvent(), OneOrderEvent{ + var ouid: String = "" + var uuid: String = "" + var orderId: Long = 0 + var price: Long = 0 + var quantity: Long = 0 + var remainedQuantity: Long = 0 + var direction: OrderDirection = OrderDirection.ASK + var matchConstraint: MatchConstraint = MatchConstraint.GTC + var orderType: OrderType = OrderType.LIMIT_ORDER + + constructor(ouid: String, + uuid: String, + orderId: Long, + pair: co.nilin.mixchange.matching.core.model.Pair, + price: Long, + quantity: Long, + remainedQuantity: Long, + direction: OrderDirection, + matchConstraint: MatchConstraint, + orderType: OrderType) + : this() { + this.ouid = ouid + this.uuid = uuid + this.orderId = orderId + this.pair = pair + this.price = price + this.quantity = quantity + this.remainedQuantity = remainedQuantity + this.direction = direction + this.matchConstraint = matchConstraint + this.orderType = orderType + } + + override fun ouid(): String { + return ouid + } + + override fun uuid(): String { + return uuid + } +} \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/CoreEvent.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/CoreEvent.kt new file mode 100644 index 000000000..59c4c8a1d --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/CoreEvent.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.matching.core.eventh.events + +import co.nilin.mixchange.matching.core.model.Pair +import java.time.LocalDateTime + +open class CoreEvent { + lateinit var pair: Pair + var eventDate: LocalDateTime = LocalDateTime.now() +} \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/CreateOrderEvent.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/CreateOrderEvent.kt new file mode 100644 index 000000000..3d0ae7c4e --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/CreateOrderEvent.kt @@ -0,0 +1,48 @@ +package co.nilin.mixchange.matching.core.eventh.events + +import co.nilin.mixchange.matching.core.model.MatchConstraint +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.OrderType + +class CreateOrderEvent() : CoreEvent() , OneOrderEvent{ + var ouid: String = "" + var uuid: String = "" + var orderId: Long = 0 + var price: Long = 0 + var quantity: Long = 0 + var remainedQuantity: Long = 0 + var direction: OrderDirection = OrderDirection.ASK + var matchConstraint: MatchConstraint = MatchConstraint.GTC + var orderType: OrderType = OrderType.LIMIT_ORDER + + constructor(ouid: String, + uuid: String, + orderId: Long, + pair: co.nilin.mixchange.matching.core.model.Pair, + price: Long, + quantity: Long, + remainedQuantity: Long, + direction: OrderDirection, + matchConstraint: MatchConstraint, + orderType: OrderType) + : this() { + this.ouid = ouid + this.uuid = uuid + this.orderId = orderId + this.pair = pair + this.price = price + this.quantity = quantity + this.remainedQuantity = remainedQuantity + this.direction = direction + this.matchConstraint = matchConstraint + this.orderType = orderType + } + + override fun ouid(): String { + return ouid + } + + override fun uuid(): String { + return uuid + } +} \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/OneOrderEvent.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/OneOrderEvent.kt new file mode 100644 index 000000000..9f08bdc93 --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/OneOrderEvent.kt @@ -0,0 +1,6 @@ +package co.nilin.mixchange.matching.core.eventh.events + +interface OneOrderEvent { + fun ouid(): String + fun uuid(): String +} \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/RejectOrderEvent.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/RejectOrderEvent.kt new file mode 100644 index 000000000..29a55f3f0 --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/RejectOrderEvent.kt @@ -0,0 +1,70 @@ +package co.nilin.mixchange.matching.core.eventh.events + +import co.nilin.mixchange.matching.core.inout.RejectReason +import co.nilin.mixchange.matching.core.inout.RequestedOperation +import co.nilin.mixchange.matching.core.model.MatchConstraint +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.OrderType + +class RejectOrderEvent(): CoreEvent(), OneOrderEvent { + var ouid: String = "" + var uuid: String = "" + var orderId: Long? = null + var price: Long? = null + var quantity: Long? = null + var direction: OrderDirection? = null + var matchConstraint: MatchConstraint? = null + var orderType: OrderType? = null + var requestedOperation: RequestedOperation = RequestedOperation.PLACE_ORDER + var reason: RejectReason? = null + + constructor(ouid: String, + uuid: String, + pair: co.nilin.mixchange.matching.core.model.Pair, + price: Long, + quantity: Long, + direction: OrderDirection, + matchConstraint: MatchConstraint, + orderType: OrderType, + requestedOperation: RequestedOperation, + reason: RejectReason?) + : this(ouid, uuid, null, pair, price, quantity, direction, matchConstraint, orderType, requestedOperation, reason) + constructor(ouid: String, + uuid: String, + orderId: Long, + pair: co.nilin.mixchange.matching.core.model.Pair, + requestedOperation: RequestedOperation, + reason: RejectReason?) + : this(ouid, uuid, orderId, pair, null, null, null, null, null, requestedOperation, reason) + constructor(ouid: String, + uuid: String, + orderId: Long?, + pair: co.nilin.mixchange.matching.core.model.Pair, + price: Long?, + quantity: Long?, + direction: OrderDirection?, + matchConstraint: MatchConstraint?, + orderType: OrderType?, + requestedOperation: RequestedOperation, + reason: RejectReason?) + : this(){ + this.ouid = ouid + this.uuid = uuid + this.orderId = orderId + this.pair = pair + this.price = price + this.quantity = quantity + this.direction = direction + this.matchConstraint = matchConstraint + this.orderType = orderType + this.requestedOperation = requestedOperation + this.reason = reason + } + override fun ouid(): String { + return ouid + } + + override fun uuid(): String { + return uuid + } +} \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/SubmitOrderEvent.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/SubmitOrderEvent.kt new file mode 100644 index 000000000..fe96595b3 --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/SubmitOrderEvent.kt @@ -0,0 +1,48 @@ +package co.nilin.mixchange.matching.core.eventh.events + +import co.nilin.mixchange.matching.core.model.MatchConstraint +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.OrderType + +class SubmitOrderEvent() : CoreEvent() , OneOrderEvent{ + var ouid: String = "" + var uuid: String = "" + var orderId: Long? = null + var price: Long = 0 + var quantity: Long = 0 + var remainedQuantity: Long = 0 + var direction: OrderDirection = OrderDirection.ASK + var matchConstraint: MatchConstraint = MatchConstraint.GTC + var orderType: OrderType = OrderType.LIMIT_ORDER + + constructor(ouid: String, + uuid: String, + orderId: Long?, + pair: co.nilin.mixchange.matching.core.model.Pair, + price: Long, + quantity: Long, + remainedQuantity: Long, + direction: OrderDirection, + matchConstraint: MatchConstraint, + orderType: OrderType) + : this() { + this.ouid = ouid + this.uuid = uuid + this.orderId = orderId + this.pair = pair + this.price = price + this.quantity = quantity + this.remainedQuantity = remainedQuantity + this.direction = direction + this.matchConstraint = matchConstraint + this.orderType = orderType + } + + override fun ouid(): String { + return ouid + } + + override fun uuid(): String { + return uuid + } +} \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/TradeEvent.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/TradeEvent.kt new file mode 100644 index 000000000..298e7060b --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/TradeEvent.kt @@ -0,0 +1,54 @@ +package co.nilin.mixchange.matching.core.eventh.events + +import co.nilin.mixchange.matching.core.model.OrderDirection + +class TradeEvent() : CoreEvent() { + var takerOuid: String = "" + var takerUuid: String = "" + var takerOrderId: Long = 0 + var takerDirection: OrderDirection = OrderDirection.ASK + var takerPrice: Long = 0 + var takerRemainedQuantity: Long = 0 + var makerOuid: String = "" + var makerUuid: String = "" + var makerOrderId: Long = 0 + var makerDirection: OrderDirection = OrderDirection.BID + var makerPrice: Long = 0 + var makerRemainedQuantity: Long = 0 + var matchedQuantity: Long = 0 + + + constructor(pair: co.nilin.mixchange.matching.core.model.Pair, + takerOuid: String, + takerUuid: String, + takerOrderId: Long, + takerDirection: OrderDirection, + takerPrice: Long, + takerRemainedQuantity: Long, + makerOuid: String, + makerUuid: String, + makerOrderId: Long, + makerDirection: OrderDirection, + makerPrice: Long, + makerRemainedQuantity: Long, + matchedQuantity: Long + ) + : this() { + this.takerOuid = takerOuid + this.takerUuid = takerUuid + this.pair = pair + this.takerOrderId = takerOrderId + this.takerPrice = takerPrice + this.takerDirection = takerDirection + this.takerRemainedQuantity = takerRemainedQuantity + + this.makerOuid = makerOuid + this.makerUuid = makerUuid + this.makerOrderId = makerOrderId + this.makerPrice = makerPrice + this.makerDirection = makerDirection + this.makerRemainedQuantity = makerRemainedQuantity + + this.matchedQuantity = matchedQuantity + } +} \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/UpdatedOrderEvent.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/UpdatedOrderEvent.kt new file mode 100644 index 000000000..c6e91a01e --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/UpdatedOrderEvent.kt @@ -0,0 +1,54 @@ +package co.nilin.mixchange.matching.core.eventh.events + +import co.nilin.mixchange.matching.core.model.MatchConstraint +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.OrderType + +class UpdatedOrderEvent(): CoreEvent() , OneOrderEvent{ + var ouid: String = "" + var uuid: String = "" + var orderId: Long = 0 + var oldPrice: Long = 0 + var oldQuantity: Long = 0 + var price: Long = 0 + var quantity: Long = 0 + var remainedQuantity: Long = 0 + var direction: OrderDirection = OrderDirection.ASK + var matchConstraint: MatchConstraint = MatchConstraint.GTC + var orderType: OrderType = OrderType.LIMIT_ORDER + + constructor(ouid: String, + uuid: String, + orderId: Long, + pair: co.nilin.mixchange.matching.core.model.Pair, + oldPrice: Long, + oldQuantity: Long, + price: Long, + quantity: Long, + remainedQuantity: Long, + direction: OrderDirection, + matchConstraint: MatchConstraint, + orderType: OrderType) + : this(){ + this.ouid = ouid + this.uuid = uuid + this.orderId = orderId + this.pair = pair + this.oldPrice = oldPrice + this.oldQuantity = oldQuantity + this.price = price + this.quantity = quantity + this.remainedQuantity = remainedQuantity + this.direction = direction + this.matchConstraint = matchConstraint + this.orderType = orderType + } + + override fun ouid(): String { + return ouid + } + + override fun uuid(): String { + return uuid + } +} \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/factory/OrderBookFactory.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/factory/OrderBookFactory.kt new file mode 100644 index 000000000..d29dc06e0 --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/factory/OrderBookFactory.kt @@ -0,0 +1,20 @@ +package co.nilin.mixchange.matching.core.factory + +import co.nilin.mixchange.matching.core.engine.SimpleOrderBook +import co.nilin.mixchange.matching.core.model.OrderBook +import co.nilin.mixchange.matching.core.model.PersistentOrderBook +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +object OrderBookFactory { + fun createOrderBook(pair: co.nilin.mixchange.matching.core.model.Pair): OrderBook { + return SimpleOrderBook(pair, false) + } + + fun createOrderBook(persistentOrderBook: PersistentOrderBook): OrderBook { + val orderBook = SimpleOrderBook(persistentOrderBook.pair, true) + orderBook.rebuild(persistentOrderBook) + orderBook.stopReplayMode() + return orderBook + } +} \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderCancelCommand.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderCancelCommand.kt new file mode 100644 index 000000000..fe000cadd --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderCancelCommand.kt @@ -0,0 +1,5 @@ +package co.nilin.mixchange.matching.core.inout + +import co.nilin.mixchange.matching.core.model.Pair + +class OrderCancelCommand(val ouid: String, val uuid: String, val orderId: Long, val pair: Pair) \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderCreateCommand.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderCreateCommand.kt new file mode 100644 index 000000000..4fa036afa --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderCreateCommand.kt @@ -0,0 +1,16 @@ +package co.nilin.mixchange.matching.core.inout + +import co.nilin.mixchange.matching.core.model.MatchConstraint +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.OrderType +import co.nilin.mixchange.matching.core.model.Pair + + +data class OrderCreateCommand(val ouid: String, + val uuid: String, + val pair: Pair, + val price: Long, + val quantity: Long, + val direction: OrderDirection, + val matchConstraint: MatchConstraint, + val orderType: OrderType) \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderEditCommand.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderEditCommand.kt new file mode 100644 index 000000000..972d0794e --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderEditCommand.kt @@ -0,0 +1,5 @@ +package co.nilin.mixchange.matching.core.inout + +import co.nilin.mixchange.matching.core.model.Pair + +data class OrderEditCommand(val ouid: String, val uuid: String, val orderId: Long, val pair: Pair, val price: Long, val quantity: Long) \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/RejectReason.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/RejectReason.kt new file mode 100644 index 000000000..b31536a59 --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/RejectReason.kt @@ -0,0 +1,5 @@ +package co.nilin.mixchange.matching.core.inout + +enum class RejectReason { + ORDER_TYPE_NOT_MATCHED_MATCHC, ORDER_NOT_FOUND, OPERATION_NOT_MATCHED_MATCHC +} \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/RequestedOperation.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/RequestedOperation.kt new file mode 100644 index 000000000..c76af4201 --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/RequestedOperation.kt @@ -0,0 +1,5 @@ +package co.nilin.mixchange.matching.core.inout + +enum class RequestedOperation { + PLACE_ORDER, CANCEL_ORDER, EDIT_ORDER +} \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/Order.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/Order.kt new file mode 100644 index 000000000..a4d059ba0 --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/Order.kt @@ -0,0 +1,6 @@ +package co.nilin.mixchange.matching.core.model + +interface Order{ + fun id():Long? + fun persistent():PersistentOrder +} diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/OrderBook.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/OrderBook.kt new file mode 100644 index 000000000..647048ce0 --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/OrderBook.kt @@ -0,0 +1,16 @@ +package co.nilin.mixchange.matching.core.model + +import co.nilin.mixchange.matching.core.inout.OrderCancelCommand +import co.nilin.mixchange.matching.core.inout.OrderEditCommand +import co.nilin.mixchange.matching.core.inout.OrderCreateCommand + +interface OrderBook { + fun pair(): Pair + fun startReplayMode() + fun stopReplayMode() + fun lastOrder(): Order? + fun handleNewOrderCommand(orderCommand: OrderCreateCommand): Order? + fun handleCancelCommand(orderCommand: OrderCancelCommand) + fun handleEditCommand(orderCommand: OrderEditCommand): Order? + fun persistent(): PersistentOrderBook +} \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/OrderMetaData.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/OrderMetaData.kt new file mode 100644 index 000000000..37a855c3d --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/OrderMetaData.kt @@ -0,0 +1,26 @@ +package co.nilin.mixchange.matching.core.model + +enum class OrderDirection { + ASK, BID +} + +enum class MatchConstraint { + GTC, + + // Immediate or Cancel - equivalent to strict-risk market order + IOC, // without price cap + + // with price cap + IOC_BUDGET, // with total amount cap + + // with total amount cap + // Fill or Kill - execute immediately completely or not at all + FOK, // without price cap + + // with price cap + FOK_BUDGET +} + +enum class OrderType { + LIMIT_ORDER, MARKET_ORDER +} diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/Pair.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/Pair.kt new file mode 100644 index 000000000..f08edfb29 --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/Pair.kt @@ -0,0 +1,17 @@ +package co.nilin.mixchange.matching.core.model + +class Pair(){ + lateinit var leftSideName: String + lateinit var rightSideName: String + + constructor(leftSideName: String + , rightSideName: String): this(){ + this.leftSideName = leftSideName + this.rightSideName = rightSideName + } + + override fun toString(): String { + return "${leftSideName}_$rightSideName" + } + +} \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/PersistentOrder.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/PersistentOrder.kt new file mode 100644 index 000000000..5c11715d7 --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/PersistentOrder.kt @@ -0,0 +1,39 @@ +package co.nilin.mixchange.matching.core.model + +class PersistentOrder { + var id: Long = 0 + var ouid: String = "" + var uuid: String = "" + var price: Long = 0 + var quantity: Long = 0 + lateinit var matchConstraint: MatchConstraint + lateinit var orderType: OrderType + lateinit var direction: OrderDirection + var filledQuantity: Long = 0 + + constructor() { + + } + + constructor( + id: Long, + ouid: String, + uuid: String, + price: Long, + quantity: Long, + matchConstraint: MatchConstraint, + orderType: OrderType, + direction: OrderDirection, + filledQuantity: Long + ) { + this.id = id + this.ouid = ouid + this.uuid = uuid + this.price = price + this.quantity = quantity + this.matchConstraint = matchConstraint + this.orderType = orderType + this.direction = direction + this.filledQuantity = filledQuantity + } +} \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/PersistentOrderBook.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/PersistentOrderBook.kt new file mode 100644 index 000000000..49aa3b208 --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/PersistentOrderBook.kt @@ -0,0 +1,16 @@ +package co.nilin.mixchange.matching.core.model + +class PersistentOrderBook { + + lateinit var pair: Pair + var lastOrder: PersistentOrder? = null + var orders : List? = emptyList() + + constructor(){ + } + + constructor(pair: Pair){ + this.pair = pair + } + +} \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/spi/OrderBookPersister.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/spi/OrderBookPersister.kt new file mode 100644 index 000000000..882f0db3d --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/spi/OrderBookPersister.kt @@ -0,0 +1,8 @@ +package co.nilin.mixchange.matching.core.spi + +import co.nilin.mixchange.matching.core.model.PersistentOrderBook + +interface OrderBookPersister { + suspend fun storeLastState(orderBook: PersistentOrderBook) + suspend fun loadLastState(symbol: String): PersistentOrderBook? +} \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/test/kotlin/co/nilin/mixchange/matching/core/engine/SimpleOrderBookUnitTest.kt b/MatchingEngine/matching-core/src/test/kotlin/co/nilin/mixchange/matching/core/engine/SimpleOrderBookUnitTest.kt new file mode 100644 index 000000000..101dc6bef --- /dev/null +++ b/MatchingEngine/matching-core/src/test/kotlin/co/nilin/mixchange/matching/core/engine/SimpleOrderBookUnitTest.kt @@ -0,0 +1,278 @@ +package co.nilin.mixchange.matching.core.engine + +import co.nilin.mixchange.matching.core.inout.OrderCancelCommand +import co.nilin.mixchange.matching.core.inout.OrderCreateCommand +import co.nilin.mixchange.matching.core.inout.OrderEditCommand +import co.nilin.mixchange.matching.core.model.MatchConstraint +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.OrderType +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.newSingleThreadContext +import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import java.util.* + +class SimpleOrderBookUnitTest { + val pair = co.nilin.mixchange.matching.core.model.Pair("BTC", "USDT") + val ETH_BTC_PAIR = co.nilin.mixchange.matching.core.model.Pair("ETH", "BTC") + val uuid = UUID.randomUUID().toString() + + @Test + fun givenEmptyOrderBook_whenGtcBidLimitOrderCreated_then1BucketWithSize1() { + //given + val orderBook = SimpleOrderBook(pair, false) + //when + val order = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 1, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + //then + Assertions.assertEquals(orderBook.bidOrders.entriesList().size , 1) + Assertions.assertEquals(orderBook.bestBidOrder, order) + Dispatchers.Default + } + + @Test + fun givenOrderBookWithBidOrders_whenGtcBidLimitOrderWithSamePriceCreated_then() { + //given + val orderBook = SimpleOrderBook(pair, false) + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 1, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + val bestBidOrder = orderBook.bestBidOrder + //when + val order: SimpleOrderBook.SimpleOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 1, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) as SimpleOrderBook.SimpleOrder + //then + Assertions.assertEquals(orderBook.bidOrders.entriesList().size , 1) + Assertions.assertEquals(orderBook.bestBidOrder, bestBidOrder) + Assertions.assertEquals(bestBidOrder!!.worse, order) + Assertions.assertEquals(order.better, bestBidOrder) + Assertions.assertEquals(orderBook.bidOrders.get(order.price).lastOrder, order) + Assertions.assertEquals(orderBook.bidOrders.get(order.price).totalQuantity, 2) + Assertions.assertEquals(orderBook.bidOrders.get(order.price).ordersCount, 2) + } + + @Test + fun givenOrderBookWithBidOrders_whenGtcBidLimitOrderWithLowerPriceCreated_thenBestOrderNotChange() { + //given + val orderBook = SimpleOrderBook(pair, false) + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 2, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + val bestBidOrder = orderBook.bestBidOrder + //when + val order: SimpleOrderBook.SimpleOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 1, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) as SimpleOrderBook.SimpleOrder + //then + Assertions.assertEquals(orderBook.bidOrders.entriesList().size , 2) + Assertions.assertEquals(orderBook.bestBidOrder, bestBidOrder) + Assertions.assertEquals(bestBidOrder!!.worse, order) + Assertions.assertEquals(order.better, bestBidOrder) + Assertions.assertEquals(orderBook.bidOrders.get(order.price).lastOrder, order) + Assertions.assertEquals(orderBook.bidOrders.get(order.price).totalQuantity, 1) + Assertions.assertEquals(orderBook.bidOrders.get(order.price).ordersCount, 1) + } + + @Test + fun givenOrderBookWithBidOrders_whenGtcBidLimitOrderWithHigherPriceCreated_thenBestOrderChanged() { + //given + val orderBook = SimpleOrderBook(pair, false) + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 1, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + val bestBidOrder = orderBook.bestBidOrder + //when + val order: SimpleOrderBook.SimpleOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 2, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) as SimpleOrderBook.SimpleOrder + //then + Assertions.assertEquals(orderBook.bidOrders.entriesList().size , 2) + Assertions.assertEquals(orderBook.bestBidOrder, order) + Assertions.assertEquals(bestBidOrder!!.better, order) + Assertions.assertEquals(order.worse, bestBidOrder) + Assertions.assertEquals(orderBook.bidOrders.get(order.price).lastOrder, order) + Assertions.assertEquals(orderBook.bidOrders.get(order.price).totalQuantity, 1) + Assertions.assertEquals(orderBook.bidOrders.get(order.price).ordersCount, 1) + } + + @Test + fun givenOrderBookWithBidOrders_whenGtcAskLimitOrderWithSamePriceCreated_thenInstantMatch() { + //given + val orderBook = SimpleOrderBook(pair, false) + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 1, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + //when + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 1, 1, OrderDirection.ASK, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) as SimpleOrderBook.SimpleOrder + //then + Assertions.assertEquals(orderBook.bidOrders.entriesList().size , 0) + Assertions.assertEquals(orderBook.askOrders.entriesList().size , 0) + Assertions.assertNull(orderBook.bestBidOrder) + Assertions.assertNull(orderBook.bestAskOrder) + } + + @Test + fun givenOrderBookWithBidOrders_whenGtcAskLimitOrderWithNotMatchPriceCreated_thenAddToQueue() { + //given + val orderBook = SimpleOrderBook(pair, false) + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 2, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 1, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + //when + val order: SimpleOrderBook.SimpleOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 3, 1, OrderDirection.ASK, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) as SimpleOrderBook.SimpleOrder + //then + Assertions.assertEquals(orderBook.bidOrders.entriesList().size , 2) + Assertions.assertEquals(orderBook.askOrders.entriesList().size , 1) + Assertions.assertNotNull(orderBook.bestBidOrder) + Assertions.assertEquals(orderBook.bestAskOrder, order) + } + + @Test + fun givenOrderBookWithBidAndAskOrders_whenGtcAskLimitOrderWithMatchPriceGreaterQuantityCreated_thenAddToQueue() { + //given + val orderBook = SimpleOrderBook(pair, false) + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 2, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 1, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 3, 1, OrderDirection.ASK, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + //when + val order: SimpleOrderBook.SimpleOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 1, 3, OrderDirection.ASK, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) as SimpleOrderBook.SimpleOrder + //then + Assertions.assertEquals(orderBook.bidOrders.entriesList().size , 0) + Assertions.assertEquals(orderBook.askOrders.entriesList().size , 2) + Assertions.assertNull(orderBook.bestBidOrder) + Assertions.assertEquals(orderBook.bestAskOrder, order) + } + + @Test + fun givenOrderBook_whenCancelBestBidOrder_thenBestBidOrderChange(){ + //given + val orderBook = SimpleOrderBook(pair, false) + val firstOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 2, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + val lastOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 1, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + //when + orderBook.handleCancelCommand(OrderCancelCommand(UUID.randomUUID().toString(), uuid, firstOrder!!.id()!!, pair)) + //then + Assertions.assertEquals(orderBook.bestBidOrder, lastOrder) + Assertions.assertEquals(orderBook.bidOrders.entriesList().size, 1) + } + + @Test + fun givenOrderBookWithMoreBids_whenCancelBestBidOrder_thenBestBidOrderChange(){ + //given + val orderBook = SimpleOrderBook(pair, false) + val firstOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 2, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + val secondOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 2, 3, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 1, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + //when + orderBook.handleCancelCommand(OrderCancelCommand(UUID.randomUUID().toString(), uuid, firstOrder!!.id()!!, pair)) + //then + Assertions.assertEquals(orderBook.bestBidOrder, secondOrder) + Assertions.assertEquals(orderBook.bidOrders.entriesList().size, 2) + } + + @Test + fun givenOrderBookWithMoreBids_whenCancelABidOrder_thenBestBidOrderNotChange(){ + //given + val orderBook = SimpleOrderBook(pair, false) + val firstOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 2, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + val secondOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 2, 3, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 1, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + //when + orderBook.handleCancelCommand(OrderCancelCommand(UUID.randomUUID().toString(), uuid, secondOrder!!.id()!!, pair)) + //then + Assertions.assertEquals(orderBook.bestBidOrder, firstOrder) + Assertions.assertEquals(orderBook.bidOrders.entriesList().size, 2) + } + + + @Test + fun givenOrderBookWithMoreBids_whenEditABidOrder_thenBestBidOrderChange(){ + //given + val orderBook = SimpleOrderBook(pair, false) + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 2, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + val secondOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 2, 3, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 1, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + //when + val order = orderBook.handleEditCommand(OrderEditCommand(UUID.randomUUID().toString(), uuid, secondOrder!!.id()!!, pair, 3, 2)) + //then + Assertions.assertEquals(secondOrder.id(), order?.id()) + Assertions.assertEquals(orderBook.bestBidOrder, order) + Assertions.assertEquals(orderBook.bidOrders.entriesList().size, 3) + } + + @Test + fun givenOrderBookWithBidAndAskOrders_whenEditABidOrder_thenRefill() { + //given + val orderBook = SimpleOrderBook(pair, false) + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 2, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + val secondBid = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 1, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 3, 1, OrderDirection.ASK, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + //when + val order: SimpleOrderBook.SimpleOrder = orderBook.handleEditCommand(OrderEditCommand(UUID.randomUUID().toString(), uuid, secondBid!!.id()!!, pair, 3, 3)) as SimpleOrderBook.SimpleOrder + //then + Assertions.assertEquals(2 , orderBook.bidOrders.entriesList().size ) + Assertions.assertEquals(0, orderBook.askOrders.entriesList().size ) + Assertions.assertEquals(orderBook.bestBidOrder, order) + Assertions.assertNull(orderBook.bestAskOrder) + } + + @Test + fun givenEmptyOrderBook_whenGtcBidMarketOrderCreated_thenRejected() { + //given + val orderBook = SimpleOrderBook(pair, false) + //when + + val order = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 1, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.MARKET_ORDER)) + //then + Assertions.assertEquals(orderBook.bidOrders.entriesList().size , 0) + Assertions.assertNull(orderBook.bestBidOrder) + Assertions.assertNull(order) + } + + @Test + fun givenEmptyOrderBook_whenIocBidMarketOrderCreated_thenNoOrderCreated() { + //given + val orderBook = SimpleOrderBook(pair, false) + //when + + val order = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 1, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.MARKET_ORDER)) + //then + Assertions.assertEquals(orderBook.bidOrders.entriesList().size , 0) + Assertions.assertNull(orderBook.bestBidOrder) + Assertions.assertNull(order) + } + + @Test + fun givenOrderBookWithBidAndAskOrders_whenIocAskMarketOrderWithGreaterQuantityCreated_thenPartiallyFilled() { + //given + val orderBook = SimpleOrderBook(pair, false) + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 2, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 1, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 3, 1, OrderDirection.ASK, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + val bestAskOrder = orderBook.bestAskOrder + //when + val order: SimpleOrderBook.SimpleOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 0, 3, OrderDirection.ASK, MatchConstraint.IOC, OrderType.MARKET_ORDER)) as SimpleOrderBook.SimpleOrder + //then + Assertions.assertEquals(2, order.filledQuantity) + Assertions.assertEquals(orderBook.bidOrders.entriesList().size , 0) + Assertions.assertEquals(orderBook.askOrders.entriesList().size , 1) + Assertions.assertNull(orderBook.bestBidOrder) + Assertions.assertEquals(orderBook.bestAskOrder, bestAskOrder) + } + + @Test + fun givenOrderBookWithBidAndAskOrders_whenIocAskLimitOrderWithHigherPriceAndGreaterQuantityCreated_thenNotFilled() { + //given + val orderBook = SimpleOrderBook(pair, false) + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 2, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 1, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 3, 1, OrderDirection.ASK, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + val bestAskOrder = orderBook.bestAskOrder + val bestBidOrder = orderBook.bestBidOrder + //when + val order: SimpleOrderBook.SimpleOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 3, 3, OrderDirection.ASK, MatchConstraint.IOC, OrderType.LIMIT_ORDER)) as SimpleOrderBook.SimpleOrder + //then + Assertions.assertEquals(0, order.filledQuantity) + Assertions.assertEquals(orderBook.bidOrders.entriesList().size , 2) + Assertions.assertEquals(orderBook.askOrders.entriesList().size , 1) + Assertions.assertEquals(bestBidOrder, orderBook.bestBidOrder) + Assertions.assertEquals(bestAskOrder, orderBook.bestAskOrder) + } + + @Test + fun givenOrderBookWithGtcAskOrder_whenGtcBidLimitOrderWithLessPriceCreated_then() { + //given + val orderBook = SimpleOrderBook(ETH_BTC_PAIR, false) + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, ETH_BTC_PAIR, 33333, 1000000, OrderDirection.ASK, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + //when + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, ETH_BTC_PAIR, 34482, 5000000, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) as SimpleOrderBook.SimpleOrder + //then + } + +} \ No newline at end of file diff --git a/MatchingEngine/matching-ports/matching-eventlistener-kafka/.gitignore b/MatchingEngine/matching-ports/matching-eventlistener-kafka/.gitignore new file mode 100644 index 000000000..8851f0e4a --- /dev/null +++ b/MatchingEngine/matching-ports/matching-eventlistener-kafka/.gitignore @@ -0,0 +1,79 @@ +# Created by .ignore support plugin (hsz.mobi) +### Kotlin template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +.idea/artifacts +.idea/compiler.xml +.idea/jarRepositories.xml +.idea/modules.xml +.idea/*.iml +.idea/modules +*.iml +*.ipr + +# File-based project format +*.iws + +# IntelliJ +out/ + +target/ + +.DS_Store + + + + diff --git a/MatchingEngine/matching-ports/matching-eventlistener-kafka/mvnw b/MatchingEngine/matching-ports/matching-eventlistener-kafka/mvnw new file mode 100755 index 000000000..a16b5431b --- /dev/null +++ b/MatchingEngine/matching-ports/matching-eventlistener-kafka/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/MatchingEngine/matching-ports/matching-eventlistener-kafka/mvnw.cmd b/MatchingEngine/matching-ports/matching-eventlistener-kafka/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/MatchingEngine/matching-ports/matching-eventlistener-kafka/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/MatchingEngine/matching-ports/matching-eventlistener-kafka/pom.xml b/MatchingEngine/matching-ports/matching-eventlistener-kafka/pom.xml new file mode 100644 index 000000000..466667dd3 --- /dev/null +++ b/MatchingEngine/matching-ports/matching-eventlistener-kafka/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + matching-eventlistener-kafka + 0.0.1-SNAPSHOT + matching-eventlistener-kafka + Matching engine kafka order submitter of Mixchange + + + 1.8 + 1.4.31 + ${version} + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-webflux + + + co.nilin.mixchange + matching-core + ${matching.version} + provided + + + org.springframework.kafka + spring-kafka + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + org.springframework.kafka + spring-kafka-test + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + diff --git a/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/config/OrderKafkaConfig.kt b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/config/OrderKafkaConfig.kt new file mode 100644 index 000000000..1007c132e --- /dev/null +++ b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/config/OrderKafkaConfig.kt @@ -0,0 +1,100 @@ +package co.nilin.mixchange.port.order.kafka.config + + +import co.nilin.mixchange.port.order.kafka.consumer.OrderKafkaListener +import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest +import org.apache.kafka.clients.admin.NewTopic +import org.apache.kafka.clients.consumer.ConsumerConfig +import org.apache.kafka.clients.producer.ProducerConfig +import org.apache.kafka.common.serialization.StringDeserializer +import org.apache.kafka.common.serialization.StringSerializer +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.ApplicationContext +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.support.GenericApplicationContext +import org.springframework.kafka.config.TopicBuilder +import org.springframework.kafka.core.ConsumerFactory +import org.springframework.kafka.core.DefaultKafkaConsumerFactory +import org.springframework.kafka.core.DefaultKafkaProducerFactory +import org.springframework.kafka.core.KafkaAdmin +import org.springframework.kafka.core.KafkaTemplate +import org.springframework.kafka.core.ProducerFactory +import org.springframework.kafka.listener.ContainerProperties +import org.springframework.kafka.listener.KafkaMessageListenerContainer +import org.springframework.kafka.support.serializer.JsonDeserializer +import org.springframework.kafka.support.serializer.JsonSerializer +import java.util.* + + +@Configuration +class OrderKafkaConfig() { + @Value("\${spring.kafka.bootstrap-servers}") + private lateinit var bootstrapServers: String + + @Value("\${spring.kafka.consumer.group-id}") + private val groupId: String? = null + + @Value("\${spring.app.symbols}") + private val symbols: String? = null + + @Autowired + private val applicationContext: GenericApplicationContext? = null + + @Autowired + fun createTopics(){ + symbols!!.split(",").map { s -> "orders_$s" } + .map { topic -> + applicationContext?.registerBean("topic_${topic}", NewTopic::class.java, topic, 1 ,1) + } + } + + @Bean("orderProducerConfigs") + fun producerConfigs(): Map? { + val props: MutableMap = HashMap() + props[ProducerConfig.BOOTSTRAP_SERVERS_CONFIG] = bootstrapServers + props[ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG] = StringSerializer::class.java + props[ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG] = JsonSerializer::class.java + return props + } + + @Bean("orderProducerFactory") + fun producerFactory(@Qualifier("orderProducerConfigs") producerConfigs: Map): ProducerFactory { + return DefaultKafkaProducerFactory(producerConfigs) + } + + @Bean("orderKafkaTemplate") + fun kafkaTemplate(@Qualifier("orderProducerFactory") producerFactory: ProducerFactory): KafkaTemplate { + return KafkaTemplate(producerFactory) + } + + @Bean("orderConsumerConfigs") + fun consumerConfigs(): Map? { + val props: MutableMap = HashMap() + props[ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG] = bootstrapServers + props[ConsumerConfig.GROUP_ID_CONFIG] = groupId + props[ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG] = StringDeserializer::class.java + props[ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG] = JsonDeserializer::class.java + props[JsonDeserializer.TRUSTED_PACKAGES] = "co.nilin.mixchange.*" + return props + } + + @Bean("orderConsumerFactory") + fun consumerFactory(@Qualifier("orderConsumerConfigs") consumerConfigs: Map): ConsumerFactory { + return DefaultKafkaConsumerFactory(consumerConfigs) + } + + + @Autowired + fun configureListener(orderKafkaListener: OrderKafkaListener, @Qualifier("orderConsumerFactory") consumerFactory: ConsumerFactory, kafkaAdmin: KafkaAdmin) { + val topics = symbols!!.split(",").map { s -> "orders_$s" }.toTypedArray() + val containerProps = ContainerProperties(*topics) + containerProps.messageListener = orderKafkaListener + val container = KafkaMessageListenerContainer(consumerFactory, containerProps) + container.setBeanName("OrderKafkaListenerContainer") + container.start() + } + +} \ No newline at end of file diff --git a/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/consumer/OrderKafkaListener.kt b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/consumer/OrderKafkaListener.kt new file mode 100644 index 000000000..89ce006df --- /dev/null +++ b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/consumer/OrderKafkaListener.kt @@ -0,0 +1,30 @@ +package co.nilin.mixchange.port.order.kafka.consumer + +import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest +import co.nilin.mixchange.port.order.kafka.spi.OrderSubmitRequestListener +import kotlinx.coroutines.runBlocking +import org.apache.kafka.clients.consumer.ConsumerRecord +import org.springframework.kafka.listener.MessageListener +import org.springframework.stereotype.Component + +@Component +class OrderKafkaListener : MessageListener { + val orderListeners = arrayListOf() + override fun onMessage(data: ConsumerRecord) { + orderListeners.forEach { tl -> + runBlocking { + tl.onOrder(data.value(), data.partition(), data.offset(), data.timestamp()) + } + } + } + + fun addOrderListener(tl: OrderSubmitRequestListener) { + orderListeners.add(tl) + } + + fun removeOrderListener(tl: OrderSubmitRequestListener) { + orderListeners.removeIf { item -> + item.id() == tl.id() + } + } +} \ No newline at end of file diff --git a/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt new file mode 100644 index 000000000..8b385472f --- /dev/null +++ b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt @@ -0,0 +1,42 @@ +package co.nilin.mixchange.port.order.kafka.inout + +import co.nilin.mixchange.matching.core.model.MatchConstraint +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.OrderType +import co.nilin.mixchange.matching.core.model.Pair + +class OrderSubmitRequest() { + + lateinit var ouid: String + lateinit var uuid: String + var orderId: Long? = null + lateinit var pair: Pair + var price: Long = 0 + var quantity: Long = 0 + var direction: OrderDirection = OrderDirection.BID + var matchConstraint: MatchConstraint = MatchConstraint.GTC + var orderType: OrderType = OrderType.LIMIT_ORDER + + + constructor(ouid: String, + uuid: String, + orderId: Long?, + pair: Pair, + price: Long, + quantity: Long, + direction: OrderDirection, + matchConstraint: MatchConstraint, + orderType: OrderType):this(){ + this.ouid = ouid + this.uuid = uuid + this.orderId = orderId + this.pair = pair + this.price = price + this.quantity = quantity + this.direction = direction + this.matchConstraint = matchConstraint + this.orderType = orderType + } + + +} \ No newline at end of file diff --git a/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitResult.kt b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitResult.kt new file mode 100644 index 000000000..3e7c4b50c --- /dev/null +++ b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitResult.kt @@ -0,0 +1,3 @@ +package co.nilin.mixchange.port.order.kafka.inout + +class OrderSubmitResult(offset: Long?) \ No newline at end of file diff --git a/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/spi/OrderSubmitRequestListener.kt b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/spi/OrderSubmitRequestListener.kt new file mode 100644 index 000000000..6098ae3b5 --- /dev/null +++ b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/spi/OrderSubmitRequestListener.kt @@ -0,0 +1,8 @@ +package co.nilin.mixchange.port.order.kafka.spi + +import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest + +interface OrderSubmitRequestListener { + fun id(): String + suspend fun onOrder(order: OrderSubmitRequest, partition: Int, offset: Long, timestamp: Long) +} \ No newline at end of file diff --git a/MatchingEngine/matching-ports/matching-snapshots-redis/.gitignore b/MatchingEngine/matching-ports/matching-snapshots-redis/.gitignore new file mode 100644 index 000000000..807d27eef --- /dev/null +++ b/MatchingEngine/matching-ports/matching-snapshots-redis/.gitignore @@ -0,0 +1,76 @@ +# Created by .ignore support plugin (hsz.mobi) +### Kotlin template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +.idea/artifacts +.idea/compiler.xml +.idea/jarRepositories.xml +.idea/modules.xml +.idea/*.iml +.idea/modules +*.iml +*.ipr + +# File-based project format +*.iws + +# IntelliJ +out/ + +target/ + +.DS_Store + diff --git a/MatchingEngine/matching-ports/matching-snapshots-redis/mvnw b/MatchingEngine/matching-ports/matching-snapshots-redis/mvnw new file mode 100755 index 000000000..a16b5431b --- /dev/null +++ b/MatchingEngine/matching-ports/matching-snapshots-redis/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/MatchingEngine/matching-ports/matching-snapshots-redis/mvnw.cmd b/MatchingEngine/matching-ports/matching-snapshots-redis/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/MatchingEngine/matching-ports/matching-snapshots-redis/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/MatchingEngine/matching-ports/matching-snapshots-redis/pom.xml b/MatchingEngine/matching-ports/matching-snapshots-redis/pom.xml new file mode 100644 index 000000000..61bc53f50 --- /dev/null +++ b/MatchingEngine/matching-ports/matching-snapshots-redis/pom.xml @@ -0,0 +1,101 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + matching-snapshots-redis + 0.0.1-SNAPSHOT + matching-snapshots-redis + Persist Matching engine snapshot of Mixchange on Redis + + + 1.8 + 1.4.31 + ${version} + + + + + co.nilin.mixchange + matching-core + ${matching.version} + provided + + + + org.springframework.boot + spring-boot-starter-data-redis-reactive + + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + io.projectreactor + reactor-test + test + + + + com.fasterxml.jackson.core + jackson-databind + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + diff --git a/MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/mixchange/port/order/redis/config/RedisConfig.kt b/MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/mixchange/port/order/redis/config/RedisConfig.kt new file mode 100644 index 000000000..72878bc4d --- /dev/null +++ b/MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/mixchange/port/order/redis/config/RedisConfig.kt @@ -0,0 +1,33 @@ +package co.nilin.mixchange.port.order.kafka.config + + +import co.nilin.mixchange.matching.core.model.OrderBook +import co.nilin.mixchange.matching.core.model.PersistentOrderBook +import com.fasterxml.jackson.annotation.JsonAutoDetect +import com.fasterxml.jackson.annotation.PropertyAccessor +import com.fasterxml.jackson.databind.ObjectMapper +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory +import org.springframework.data.redis.core.ReactiveRedisTemplate +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer +import org.springframework.data.redis.serializer.RedisSerializationContext +import org.springframework.data.redis.serializer.StringRedisSerializer + + +@Configuration +class RedisConfig() { + @Bean("snapshotRedisTemplate") + fun snapshotRedisTemplate(factory: ReactiveRedisConnectionFactory): ReactiveRedisTemplate { + val jackson2JsonRedisSerializer = Jackson2JsonRedisSerializer(PersistentOrderBook::class.java) + val objectMapper = ObjectMapper() + objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY) + jackson2JsonRedisSerializer.setObjectMapper(objectMapper) + val serializationContext = RedisSerializationContext.newSerializationContext(StringRedisSerializer()) + .hashKey(StringRedisSerializer()) + .hashValue(jackson2JsonRedisSerializer) + .build() + + return ReactiveRedisTemplate(factory, serializationContext) + } +} \ No newline at end of file diff --git a/MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/mixchange/port/order/redis/service/OrderBookPersister.kt b/MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/mixchange/port/order/redis/service/OrderBookPersister.kt new file mode 100644 index 000000000..d9aacc2e0 --- /dev/null +++ b/MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/mixchange/port/order/redis/service/OrderBookPersister.kt @@ -0,0 +1,24 @@ +package co.nilin.mixchange.port.order.redis.service + +import co.nilin.mixchange.matching.core.model.PersistentOrderBook +import co.nilin.mixchange.matching.core.spi.OrderBookPersister +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.data.redis.core.ReactiveRedisTemplate +import org.springframework.stereotype.Component + +@Component +class OrderBookRedisPersister(@Qualifier("snapshotRedisTemplate") + val redisTemplate: ReactiveRedisTemplate) : OrderBookPersister { + + override suspend fun storeLastState(orderBook: PersistentOrderBook) { + redisTemplate.opsForHash() + .put("OrderbookSnapshots", orderBook.pair.toString(), orderBook) + .subscribe() + } + + override suspend fun loadLastState(symbol: String): PersistentOrderBook? = + redisTemplate.opsForHash() + .get("OrderbookSnapshots", symbol) + .blockOptional().orElse(null) + +} diff --git a/MatchingEngine/matching-ports/matching-submitter-kafka/.gitignore b/MatchingEngine/matching-ports/matching-submitter-kafka/.gitignore new file mode 100644 index 000000000..36b20dfa2 --- /dev/null +++ b/MatchingEngine/matching-ports/matching-submitter-kafka/.gitignore @@ -0,0 +1,77 @@ +# Created by .ignore support plugin (hsz.mobi) +### Kotlin template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +.idea/artifacts +.idea/compiler.xml +.idea/jarRepositories.xml +.idea/modules.xml +.idea/*.iml +.idea/modules +*.iml +*.ipr + +# File-based project format +*.iws + +# IntelliJ +out/ + +target/ + +.DS_Store + + diff --git a/MatchingEngine/matching-ports/matching-submitter-kafka/mvnw b/MatchingEngine/matching-ports/matching-submitter-kafka/mvnw new file mode 100755 index 000000000..a16b5431b --- /dev/null +++ b/MatchingEngine/matching-ports/matching-submitter-kafka/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/MatchingEngine/matching-ports/matching-submitter-kafka/mvnw.cmd b/MatchingEngine/matching-ports/matching-submitter-kafka/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/MatchingEngine/matching-ports/matching-submitter-kafka/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/MatchingEngine/matching-ports/matching-submitter-kafka/pom.xml b/MatchingEngine/matching-ports/matching-submitter-kafka/pom.xml new file mode 100644 index 000000000..7014d2f7a --- /dev/null +++ b/MatchingEngine/matching-ports/matching-submitter-kafka/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + matching-submitter-kafka + 0.0.1-SNAPSHOT + matching-submitter-kafka + Matching engine kafka order submitter of Mixchange + + + 1.8 + 1.4.31 + ${version} + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-webflux + + + co.nilin.mixchange + matching-core + ${matching.version} + provided + + + org.springframework.kafka + spring-kafka + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + org.springframework.kafka + spring-kafka-test + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + diff --git a/MatchingEngine/matching-ports/matching-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/config/EventsKafkaConfig.kt b/MatchingEngine/matching-ports/matching-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/config/EventsKafkaConfig.kt new file mode 100644 index 000000000..73e84405a --- /dev/null +++ b/MatchingEngine/matching-ports/matching-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/config/EventsKafkaConfig.kt @@ -0,0 +1,77 @@ +package co.nilin.mixchange.port.order.kafka.config + + +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +import org.apache.kafka.clients.admin.AdminClientConfig +import org.apache.kafka.clients.admin.NewTopic +import org.apache.kafka.clients.producer.ProducerConfig +import org.apache.kafka.common.serialization.StringSerializer +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.support.GenericApplicationContext +import org.springframework.kafka.config.TopicBuilder +import org.springframework.kafka.core.DefaultKafkaProducerFactory +import org.springframework.kafka.core.KafkaAdmin +import org.springframework.kafka.core.KafkaTemplate +import org.springframework.kafka.core.ProducerFactory +import org.springframework.kafka.support.serializer.JsonSerializer +import org.springframework.util.StringUtils +import java.util.* + + +@Configuration +class EventsKafkaConfig() { + @Value("\${spring.kafka.bootstrap-servers}") + private lateinit var bootstrapServers: String + + @Value("\${spring.app.symbols}") + private val symbols: String? = null + + @Autowired + private val applicationContext: GenericApplicationContext? = null + + @Bean("eventsProducerConfigs") + fun producerConfigs(): Map? { + val props: MutableMap = HashMap() + props[ProducerConfig.BOOTSTRAP_SERVERS_CONFIG] = bootstrapServers + props[ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG] = StringSerializer::class.java + props[ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG] = JsonSerializer::class.java + return props + } + + @Bean("eventsProducerFactory") + fun producerFactory(@Qualifier("eventsProducerConfigs") producerConfigs: Map): ProducerFactory { + return DefaultKafkaProducerFactory(producerConfigs) + } + + @Bean("eventsKafkaTemplate") + fun kafkaTemplate(@Qualifier("eventsProducerFactory") producerFactory: ProducerFactory): KafkaTemplate { + return KafkaTemplate(producerFactory) + } + + + @Bean + fun admin(): KafkaAdmin? { + val configs: MutableMap = HashMap() + configs[AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG] = bootstrapServers + return KafkaAdmin(configs) + } + + @Autowired + fun createTopics(){ + symbols!!.split(",") + .map { s -> "events_$s" } + .map { topic -> + applicationContext?.registerBean("topic_${topic}", NewTopic::class.java, topic, 10, 1) + } + symbols.split(",") + .map { s -> "trades_$s" } + .map { topic -> + applicationContext?.registerBean("topic_${topic}", NewTopic::class.java, topic, 10, 1) + } + } + +} \ No newline at end of file diff --git a/MatchingEngine/matching-ports/matching-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/service/EventsSubmitter.kt b/MatchingEngine/matching-ports/matching-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/service/EventsSubmitter.kt new file mode 100644 index 000000000..7a5eb28e1 --- /dev/null +++ b/MatchingEngine/matching-ports/matching-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/service/EventsSubmitter.kt @@ -0,0 +1,18 @@ +package co.nilin.mixchange.port.order.kafka.service + +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +import co.nilin.mixchange.matching.core.eventh.events.TradeEvent +import org.springframework.kafka.core.KafkaTemplate +import org.springframework.stereotype.Component +import kotlin.coroutines.suspendCoroutine + +@Component +class EventsSubmitter(val kafkaTemplate: KafkaTemplate) { + suspend fun submit(event: CoreEvent): Unit= suspendCoroutine { cont -> + println("Submit!") + if ( event is TradeEvent) + kafkaTemplate.send("trades_${event.pair.leftSideName}_${event.pair.rightSideName}", event) + kafkaTemplate.send("events_${event.pair.leftSideName}_${event.pair.rightSideName}", event) + } + +} \ No newline at end of file diff --git a/MatchingEngine/pom.xml b/MatchingEngine/pom.xml new file mode 100644 index 000000000..f45a5802c --- /dev/null +++ b/MatchingEngine/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + co.nilin.mixchange + matching-engine + 0.0.1-SNAPSHOT + matching-engine + pom + Matching Engine root of Mixchange + + + matching-core + matching-ports/matching-submitter-kafka + matching-ports/matching-eventlistener-kafka + matching-ports/matching-snapshots-redis + matching-app + + diff --git a/MatchingGateway/.gitignore b/MatchingGateway/.gitignore new file mode 100644 index 000000000..90ffaa853 --- /dev/null +++ b/MatchingGateway/.gitignore @@ -0,0 +1,73 @@ +# Created by .ignore support plugin (hsz.mobi) +### Kotlin template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +.idea/artifacts +.idea/compiler.xml +.idea/jarRepositories.xml +.idea/modules.xml +.idea/*.iml +.idea/modules +*.iml +*.ipr + +# File-based project format +*.iws + +# IntelliJ +out/ + +target/ diff --git a/MatchingGateway/gateway-app/.gitignore b/MatchingGateway/gateway-app/.gitignore new file mode 100644 index 000000000..7f15adddb --- /dev/null +++ b/MatchingGateway/gateway-app/.gitignore @@ -0,0 +1,77 @@ +# Created by .ignore support plugin (hsz.mobi) +### Kotlin template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +.idea/artifacts +.idea/compiler.xml +.idea/jarRepositories.xml +.idea/modules.xml +.idea/*.iml +.idea/modules +*.iml +*.ipr + +# File-based project format +*.iws + +# IntelliJ +out/ + +target/ + + + + diff --git a/MatchingGateway/gateway-app/Dockerfile b/MatchingGateway/gateway-app/Dockerfile new file mode 100644 index 000000000..f2cbd4c26 --- /dev/null +++ b/MatchingGateway/gateway-app/Dockerfile @@ -0,0 +1,5 @@ +FROM openjdk:8-jdk-alpine +VOLUME /tmp +ARG JAR_FILE=target/*.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"] \ No newline at end of file diff --git a/MatchingGateway/gateway-app/pom.xml b/MatchingGateway/gateway-app/pom.xml new file mode 100644 index 000000000..d34882ea5 --- /dev/null +++ b/MatchingGateway/gateway-app/pom.xml @@ -0,0 +1,211 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + matching-gateway-app + 0.0.1-SNAPSHOT + matching-gateway-app + Matching gateway running app Mixchange + + + 1.8 + 1.4.31 + ${version} + 2020.0.2 + + + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + com.fasterxml.jackson.module + jackson-module-kotlin + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-webflux + + + co.nilin.mixchange + matching-core + ${matching.version} + + + co.nilin.mixchange + gateway-order-submitter-kafka + ${matching.version} + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + org.springframework.cloud + spring-cloud-starter-consul-all + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + + org.bouncycastle + bcprov-jdk15on + 1.60 + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18 + + + ${skip.unit.tests} + + + **/*IntegrationTest.java + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-test-source + generate-test-sources + + add-test-source + + + + src/test/java + + + + + compile + + add-source + + + + src/main/java + + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + + -Xjsr305=strict + + + spring + + 1.8 + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + compile + compile + + compile + + + + testCompile + test-compile + + testCompile + + + + + + opex-matching-gateway + + + diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/MatchingGatewayApp.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/MatchingGatewayApp.kt new file mode 100644 index 000000000..81b16fdda --- /dev/null +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/MatchingGatewayApp.kt @@ -0,0 +1,12 @@ +package co.nilin.mixchange.app + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.runApplication +import org.springframework.context.annotation.ComponentScan + +@SpringBootApplication +@ComponentScan("co.nilin.mixchange") +class MatchingGatewayApp +fun main(args: Array) { + runApplication(*args) +} \ No newline at end of file diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt new file mode 100644 index 000000000..c125c5caf --- /dev/null +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt @@ -0,0 +1,47 @@ +package co.nilin.mixchange.app.config + +import co.nilin.mixchange.app.inout.PairConfig +import co.nilin.mixchange.app.inout.PairFeeConfig +import co.nilin.mixchange.app.spi.PairConfigLoader +import co.nilin.mixchange.app.spi.AccountantApiProxy +import co.nilin.mixchange.matching.core.model.OrderDirection +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import java.math.BigDecimal + + +@Configuration +class AppConfig { + @Bean + @ConditionalOnMissingBean(AccountantApiProxy::class) + fun accountantApiProxy(): AccountantApiProxy { + return object : AccountantApiProxy { + override suspend fun canCreateOrder(uuid: String, symbol: String, value: BigDecimal): Boolean { + return true + } + + override suspend fun fetchPairFeeConfig( + pair: String, + direction: OrderDirection, + userLevel: String + ): PairFeeConfig { + return PairFeeConfig( + PairConfig( + pair, pair.split("_")[0], pair.split("_")[1], 1.0, 1.0 + ), direction.name, userLevel, 0.01, 0.01 + ) + } + } + } + + @Bean + @ConditionalOnMissingBean(PairConfigLoader::class) + fun pairConfigLoader(accountantApiProxy: AccountantApiProxy): PairConfigLoader { + return object : PairConfigLoader { + override suspend fun load(pair: String, direction: OrderDirection, userLevel: String): PairFeeConfig { + return accountantApiProxy.fetchPairFeeConfig(pair, direction, userLevel) + } + } + } +} \ No newline at end of file diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/SecurityConfig.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/SecurityConfig.kt new file mode 100644 index 000000000..f458ebfc3 --- /dev/null +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/SecurityConfig.kt @@ -0,0 +1,44 @@ +package co.nilin.mixchange.app.config + +import org.springframework.core.io.ClassPathResource +import org.springframework.core.io.Resource +import org.springframework.context.annotation.Bean +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity +import org.springframework.security.config.web.server.ServerHttpSecurity +import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder +import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder +import org.springframework.security.web.server.SecurityWebFilterChain +import org.springframework.util.Base64Utils +import org.springframework.util.FileCopyUtils +import java.security.KeyFactory +import java.security.interfaces.RSAPublicKey +import java.security.spec.X509EncodedKeySpec + +@EnableWebFluxSecurity +class SecurityConfig { + @Bean + fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain? { + http.csrf().disable() + .authorizeExchange() + .pathMatchers("/hello").permitAll() + .pathMatchers("/actuator/**").permitAll() + .pathMatchers("/**").hasAuthority("SCOPE_trust") + .anyExchange().authenticated() + .and() + .oauth2ResourceServer() + .jwt() + return http.build() + } + + @Bean + @Throws(Exception::class) + fun reactiveJwtDecoder(): ReactiveJwtDecoder? { + val resource: Resource = ClassPathResource("/public.cert") + val publicKey = String(FileCopyUtils.copyToByteArray(resource.inputStream)) + .replace("-----BEGIN PUBLIC KEY-----\n", "") + .replace("\n-----END PUBLIC KEY-----", "") + val spec = X509EncodedKeySpec(Base64Utils.decodeFromString(publicKey)) + val kf = KeyFactory.getInstance("RSA") + return NimbusReactiveJwtDecoder(kf.generatePublic(spec) as RSAPublicKey) + } +} diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/WebClientConfig.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/WebClientConfig.kt new file mode 100644 index 000000000..edbb7e6cf --- /dev/null +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/WebClientConfig.kt @@ -0,0 +1,25 @@ +package co.nilin.mixchange.app.config + +import org.springframework.cloud.client.ServiceInstance +import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties +import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer +import org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerExchangeFilterFunction +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.web.reactive.function.client.WebClient + +@Configuration +class WebClientConfig { + + @Bean + fun webClient(loadBalancerFactory: ReactiveLoadBalancer.Factory): WebClient { + return WebClient.builder() + .filter( + ReactorLoadBalancerExchangeFilterFunction( + loadBalancerFactory, LoadBalancerProperties(), emptyList() + ) + ) + .build() + } + +} diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/controller/ControllerExceptionHandler.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/controller/ControllerExceptionHandler.kt new file mode 100644 index 000000000..014a06fe6 --- /dev/null +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/controller/ControllerExceptionHandler.kt @@ -0,0 +1,90 @@ +package co.nilin.mixchange.app.controller + +import co.nilin.mixchange.app.exception.NotAllowedToSubmitOrderException +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.databind.ObjectMapper +import org.slf4j.LoggerFactory +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.ExceptionHandler +import org.springframework.web.bind.annotation.RestControllerAdvice +import org.springframework.web.reactive.function.client.WebClientResponseException +import java.nio.charset.StandardCharsets +import java.util.* + +@RestControllerAdvice +class ControllerExceptionHandler { + + data class ErrorResponse( val timestamp: Date + , val status: Int + , val error: String + , val message: String) + + val logger = LoggerFactory.getLogger(ControllerExceptionHandler::class.java) + + val objectMapper: ObjectMapper = ObjectMapper() + + @ExceptionHandler(NotAllowedToSubmitOrderException::class) + fun handle(ex: NotAllowedToSubmitOrderException): ResponseEntity { + logger.error("Trace Error {}", ex ) + val ret = ResponseEntity.status(500).body(ErrorResponse(Date() + , -1 + , ex::class.qualifiedName ?: "" + , ex.message ?: "")) + logger.debug("return error response:{}", ret) + return ret + } + + @JsonIgnoreProperties(ignoreUnknown = true) + class WebClientErrorResponse { + constructor() { + + } + + constructor(timestamp: Date?, path: String?, status: Int?, error: String?, message: String?) { + this.timestamp = timestamp + this.path = path + this.status = status + this.error = error + this.message = message + } + + var timestamp: Date? = null + var path: String? = null + var status: Int? = null + var error: String? = null + var message: String? = null + } + + @ExceptionHandler(WebClientResponseException::class) + fun handle(ex: WebClientResponseException): ResponseEntity { + logger.error("Trace Error {}", ex ) + try { + val body = objectMapper.readValue(ex.responseBodyAsByteArray.toString(StandardCharsets.UTF_8), WebClientErrorResponse::class.java) + val ret = ResponseEntity.status(body.status ?: ex.rawStatusCode).body(ErrorResponse(Date() + , body.status ?: ex.rawStatusCode + , body.error ?: ex::class.qualifiedName ?: "" + , body.message ?: "Internal Server Error")) + logger.debug("return error response:{}", ret) + return ret + } catch (je: Exception) { + logger.error("Trace Error {}", je ) + val ret = ResponseEntity.status(ex.statusCode).body(ErrorResponse(Date() + , ex.rawStatusCode + , ex::class.qualifiedName ?: "" + , "Internal Server Error")) + logger.debug("return error response:{}", ret) + return ret + } + } + + @ExceptionHandler(Throwable::class) + fun handle(ex: Throwable): ResponseEntity { + logger.error("Trace Error {}", ex ) + val ret = ResponseEntity.status(500).body(ErrorResponse(Date() + , 500 + , ex::class.qualifiedName ?: "" + , "Internal Server Error")) + logger.debug("return error response:{}", ret) + return ret + } +} diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/controller/OrderController.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/controller/OrderController.kt new file mode 100644 index 000000000..8c5d63349 --- /dev/null +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/controller/OrderController.kt @@ -0,0 +1,28 @@ +package co.nilin.mixchange.app.controller + +import co.nilin.mixchange.app.inout.CreateOrderRequest +import co.nilin.mixchange.app.service.OrderService +import co.nilin.mixchange.matching.core.model.MatchConstraint +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.OrderType +import co.nilin.mixchange.matching.core.model.Pair +import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest +import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitResult +import co.nilin.mixchange.port.order.kafka.service.OrderSubmitter +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RestController +import java.security.Principal +import java.util.* + +@RestController +class OrderController(val orderService: OrderService) { + + @PostMapping("/order") + suspend fun submitNewOrder(principal: Principal, @RequestBody createOrderRequest: CreateOrderRequest): OrderSubmitResult { + createOrderRequest.uuid = principal.name + return orderService.submitNewOrder(createOrderRequest) + } + +} \ No newline at end of file diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/exception/NotAllowedToSubmitOrderException.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/exception/NotAllowedToSubmitOrderException.kt new file mode 100644 index 000000000..175d08583 --- /dev/null +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/exception/NotAllowedToSubmitOrderException.kt @@ -0,0 +1,6 @@ +package co.nilin.mixchange.app.exception + +import java.lang.RuntimeException + +class NotAllowedToSubmitOrderException: RuntimeException() { +} \ No newline at end of file diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/inout/CreateOrderRequest.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/inout/CreateOrderRequest.kt new file mode 100644 index 000000000..fbf104120 --- /dev/null +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/inout/CreateOrderRequest.kt @@ -0,0 +1,16 @@ +package co.nilin.mixchange.app.inout + +import co.nilin.mixchange.matching.core.model.MatchConstraint +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.OrderType +import java.math.BigDecimal + +data class CreateOrderRequest( + var uuid: String?, + val pair: String, + val price: BigDecimal, + val quantity: BigDecimal, + val direction: OrderDirection, + val matchConstraint: MatchConstraint, + val orderType: OrderType +) \ No newline at end of file diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/inout/PairConfig.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/inout/PairConfig.kt new file mode 100644 index 000000000..1924f2062 --- /dev/null +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/inout/PairConfig.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.app.inout + +class PairConfig( + val pair: String, + val leftSideWalletSymbol: String, //can be same as pair left side + val rightSideWalletSymbol: String, //can be same as pair right side + val rightSideFraction: Double, + val leftSideFraction: Double +) \ No newline at end of file diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/inout/PairFeeConfig.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/inout/PairFeeConfig.kt new file mode 100644 index 000000000..cd232a08d --- /dev/null +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/inout/PairFeeConfig.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.app.inout + +class PairFeeConfig( + val pairConfig: PairConfig, + val direction: String?, + val userLevel: String?, + val makerFee: Double, + val takerFee: Double +) \ No newline at end of file diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/proxy/AccountantProxyImpl.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/proxy/AccountantProxyImpl.kt new file mode 100644 index 000000000..a71ac160a --- /dev/null +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/proxy/AccountantProxyImpl.kt @@ -0,0 +1,56 @@ +package co.nilin.mixchange.port.gateway.wallet.proxy + + +import co.nilin.mixchange.app.inout.PairFeeConfig +import co.nilin.mixchange.app.spi.AccountantApiProxy +import co.nilin.mixchange.matching.core.model.OrderDirection +import kotlinx.coroutines.reactive.awaitFirst +import org.springframework.beans.factory.annotation.Value +import org.springframework.core.ParameterizedTypeReference +import org.springframework.stereotype.Component +import org.springframework.web.reactive.function.client.WebClient +import java.math.BigDecimal +import java.net.URI + +inline fun typeRef(): ParameterizedTypeReference = object : ParameterizedTypeReference() {} + +@Component +class AccountantProxyImpl( + @Value("\${app.accountant.url}") val accountantBaseUrl: String, val webClient: WebClient +) : AccountantApiProxy { + override suspend fun canCreateOrder(uuid: String, symbol: String, value: BigDecimal): Boolean { + data class BooleanResponse(val result: Boolean) + return webClient.get() + .uri(URI.create("$accountantBaseUrl/$uuid/create_order/${value}_${symbol}/allowed")) + .header("Content-Type", "application/json") + .retrieve() + .onStatus({ t -> t.isError }, { p -> + throw RuntimeException() + }) + .bodyToMono(typeRef()) + .log() + .awaitFirst() + .result + } + + override suspend fun fetchPairFeeConfig(pair: String, direction: OrderDirection, userLevel: String): PairFeeConfig { + return webClient.get() + .uri( + URI.create( + if (userLevel.isBlank()) { + "$accountantBaseUrl/config/${pair}/fee/${direction}" + } else { + "$accountantBaseUrl/config/${pair}/fee/${direction}-${userLevel}" + } + ) + ) + .header("Content-Type", "application/json") + .retrieve() + .onStatus({ t -> t.isError }, { p -> + throw RuntimeException() + }) + .bodyToMono(typeRef()) + .log() + .awaitFirst() + } +} \ No newline at end of file diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/service/OrderService.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/service/OrderService.kt new file mode 100644 index 000000000..5bd07735a --- /dev/null +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/service/OrderService.kt @@ -0,0 +1,62 @@ +package co.nilin.mixchange.app.service + +import co.nilin.mixchange.app.exception.NotAllowedToSubmitOrderException +import co.nilin.mixchange.app.inout.CreateOrderRequest +import co.nilin.mixchange.app.spi.PairConfigLoader +import co.nilin.mixchange.app.spi.AccountantApiProxy +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.Pair +import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest +import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitResult +import co.nilin.mixchange.port.order.kafka.service.OrderSubmitter +import org.springframework.stereotype.Service +import java.util.* + +@Service +class OrderService(val accountantApiProxy: AccountantApiProxy +, val orderSubmitter: OrderSubmitter +, val pairConfigLoader: PairConfigLoader +) { + suspend fun submitNewOrder(createOrderRequest: CreateOrderRequest): OrderSubmitResult { + val symbolSides = createOrderRequest.pair.split("_") + val symbol = if ( createOrderRequest.direction == OrderDirection.ASK ) + symbolSides[0] + else + symbolSides[1] + if (!accountantApiProxy.canCreateOrder( + createOrderRequest.uuid!!, + symbol, + if ( createOrderRequest.direction == OrderDirection.ASK ) + createOrderRequest.quantity + else + createOrderRequest.quantity.multiply(createOrderRequest.price) + )) { + throw NotAllowedToSubmitOrderException() + } + val pairFeeConfig = pairConfigLoader.load(createOrderRequest.pair + , createOrderRequest.direction, "") + val orderSubmitRequest = OrderSubmitRequest( + UUID.randomUUID().toString() + , createOrderRequest.uuid!!//get from auth2 + , null + , Pair(symbolSides[0], symbolSides[1]) + , createOrderRequest.price.divide( + //(if ( createOrderRequest.direction == OrderDirection.ASK ){ + pairFeeConfig.pairConfig.rightSideFraction + /*} else { + pairFeeConfig.pairConfig.leftSideFraction + })*/.toBigDecimal() + ).longValueExact() + , createOrderRequest.quantity.divide( + ///(if ( createOrderRequest.direction == OrderDirection.ASK ){ + pairFeeConfig.pairConfig.leftSideFraction + /*} else { + pairFeeConfig.pairConfig.rightSideFraction + })*/.toBigDecimal()) + .longValueExact() + , createOrderRequest.direction + , createOrderRequest.matchConstraint + , createOrderRequest.orderType) + return orderSubmitter.submit(orderSubmitRequest) + } +} \ No newline at end of file diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/spi/AccountantApiProxy.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/spi/AccountantApiProxy.kt new file mode 100644 index 000000000..447c5cb74 --- /dev/null +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/spi/AccountantApiProxy.kt @@ -0,0 +1,11 @@ +package co.nilin.mixchange.app.spi + +import co.nilin.mixchange.app.inout.PairFeeConfig +import co.nilin.mixchange.matching.core.model.OrderDirection +import java.math.BigDecimal + + +interface AccountantApiProxy { + suspend fun canCreateOrder(uuid: String, symbol: String, value: BigDecimal): Boolean + suspend fun fetchPairFeeConfig(pair: String, direction: OrderDirection, userLevel: String): PairFeeConfig +} \ No newline at end of file diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/spi/PairConfigLoader.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/spi/PairConfigLoader.kt new file mode 100644 index 000000000..e0e8ebcc2 --- /dev/null +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/spi/PairConfigLoader.kt @@ -0,0 +1,8 @@ +package co.nilin.mixchange.app.spi + +import co.nilin.mixchange.app.inout.PairFeeConfig +import co.nilin.mixchange.matching.core.model.OrderDirection + +interface PairConfigLoader { + suspend fun load(pair: String, direction: OrderDirection, userLevel: String): PairFeeConfig +} \ No newline at end of file diff --git a/MatchingGateway/gateway-app/src/main/resources/application-docker.yml b/MatchingGateway/gateway-app/src/main/resources/application-docker.yml new file mode 100644 index 000000000..b39c85247 --- /dev/null +++ b/MatchingGateway/gateway-app/src/main/resources/application-docker.yml @@ -0,0 +1,24 @@ +server.port: 8093 +spring: + application: + name: opex-gateway + main: + allow-bean-definition-overriding: false + kafka: + bootstrap-servers: ${KAFKA_IP_PORT} + consumer: + group-id: gateway + cloud: + bootstrap: + enabled: true + consul: + host: ${CONSUL_HOST} + port: 8500 + discovery: + #healthCheckPath: ${management.context-path}/health + instance-id: ${spring.application.name}:${server.port} + healthCheckInterval: 20s + prefer-ip-address: true +app: + accountant: + url: lb://opex-accountant diff --git a/MatchingGateway/gateway-app/src/main/resources/application.yml b/MatchingGateway/gateway-app/src/main/resources/application.yml new file mode 100644 index 000000000..efafd9860 --- /dev/null +++ b/MatchingGateway/gateway-app/src/main/resources/application.yml @@ -0,0 +1,23 @@ +server.port: 8093 +spring: + application: + name: opex-gateway + main: + allow-bean-definition-overriding: false + kafka: + bootstrap-servers: localhost:9092 + consumer: + group-id: gateway + cloud: + bootstrap: + enabled: true + consul: + port: 8500 + discovery: + #healthCheckPath: ${management.context-path}/health + instance-id: ${spring.application.name}:${server.port} + healthCheckInterval: 20s + prefer-ip-address: true +app: + accountant: + url: lb://opex-accountant \ No newline at end of file diff --git a/MatchingGateway/gateway-app/src/main/resources/public.cert b/MatchingGateway/gateway-app/src/main/resources/public.cert new file mode 100644 index 000000000..8589de065 --- /dev/null +++ b/MatchingGateway/gateway-app/src/main/resources/public.cert @@ -0,0 +1,3 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsw+lmV2HbpgXllKS+ccyCerlWDir32Y3yFvXF3CbzYRKVg/4FddA8itMCPpPjVOAo26bi/0WImHHYPyGxSH3QQIQaSdS5AFUNarpFc9M/W4Bu7k5h8sKb+vzZA3hMLFyvmQ2JtY8Zuqi1BBB33OAlkOHprJFTrrt1xa4a6KJjKXlDp5o5hOFgvZbsW+k/fBU0CLCbL3PlXQLhqHgEncpL9L9FF/xDbPIGftxjEWcBPFG/SuyygT35xRFMxB7ifR+wJZwVnoC4RhbhgJdL2EiUVT7wq5MTsxr2j4nAAJYatK/7OTecs6y/7bGNAGEoqh4Q3X3D95YFex0scmHgY1hWwIDAQAB +-----END PUBLIC KEY----- \ No newline at end of file diff --git a/MatchingGateway/gateway-port/order-submitter-kafka/.gitignore b/MatchingGateway/gateway-port/order-submitter-kafka/.gitignore new file mode 100644 index 000000000..7f15adddb --- /dev/null +++ b/MatchingGateway/gateway-port/order-submitter-kafka/.gitignore @@ -0,0 +1,77 @@ +# Created by .ignore support plugin (hsz.mobi) +### Kotlin template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +.idea/artifacts +.idea/compiler.xml +.idea/jarRepositories.xml +.idea/modules.xml +.idea/*.iml +.idea/modules +*.iml +*.ipr + +# File-based project format +*.iws + +# IntelliJ +out/ + +target/ + + + + diff --git a/MatchingGateway/gateway-port/order-submitter-kafka/mvnw b/MatchingGateway/gateway-port/order-submitter-kafka/mvnw new file mode 100755 index 000000000..a16b5431b --- /dev/null +++ b/MatchingGateway/gateway-port/order-submitter-kafka/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/MatchingGateway/gateway-port/order-submitter-kafka/mvnw.cmd b/MatchingGateway/gateway-port/order-submitter-kafka/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/MatchingGateway/gateway-port/order-submitter-kafka/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/MatchingGateway/gateway-port/order-submitter-kafka/pom.xml b/MatchingGateway/gateway-port/order-submitter-kafka/pom.xml new file mode 100644 index 000000000..fbca58507 --- /dev/null +++ b/MatchingGateway/gateway-port/order-submitter-kafka/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + gateway-order-submitter-kafka + 0.0.1-SNAPSHOT + gateway-order-submitter-kafka + Matching gateway kafka order submitter of Mixchange + + + 1.8 + 1.4.31 + ${version} + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-webflux + + + co.nilin.mixchange + matching-core + ${matching.version} + provided + + + org.springframework.kafka + spring-kafka + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + org.springframework.kafka + spring-kafka-test + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + diff --git a/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/config/OrderKafkaConfig.kt b/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/config/OrderKafkaConfig.kt new file mode 100644 index 000000000..69c15da0b --- /dev/null +++ b/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/config/OrderKafkaConfig.kt @@ -0,0 +1,44 @@ +package co.nilin.mixchange.port.order.kafka.config + + + +import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest +import org.apache.kafka.clients.producer.ProducerConfig +import org.apache.kafka.common.serialization.StringSerializer +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.kafka.core.DefaultKafkaProducerFactory +import org.springframework.kafka.core.KafkaTemplate +import org.springframework.kafka.core.ProducerFactory +import org.springframework.kafka.support.serializer.JsonSerializer +import java.util.* + + +@Configuration +class OrderKafkaConfig() { + @Value("\${spring.kafka.bootstrap-servers}") + private lateinit var bootstrapServers: String + + @Bean("orderProducerConfigs") + fun producerConfigs(): Map? { + val props: MutableMap = HashMap() + props[ProducerConfig.BOOTSTRAP_SERVERS_CONFIG] = bootstrapServers + props[ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG] = StringSerializer::class.java + props[ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG] = JsonSerializer::class.java + return props + } + + @Bean("orderProducerFactory") + fun producerFactory(@Qualifier("orderProducerConfigs") producerConfigs: Map): ProducerFactory { + return DefaultKafkaProducerFactory(producerConfigs) + } + + @Bean("orderKafkaTemplate") + fun kafkaTemplate(@Qualifier("orderProducerFactory") producerFactory: ProducerFactory): KafkaTemplate { + return KafkaTemplate(producerFactory) + } + + +} \ No newline at end of file diff --git a/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt b/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt new file mode 100644 index 000000000..8b385472f --- /dev/null +++ b/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt @@ -0,0 +1,42 @@ +package co.nilin.mixchange.port.order.kafka.inout + +import co.nilin.mixchange.matching.core.model.MatchConstraint +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.OrderType +import co.nilin.mixchange.matching.core.model.Pair + +class OrderSubmitRequest() { + + lateinit var ouid: String + lateinit var uuid: String + var orderId: Long? = null + lateinit var pair: Pair + var price: Long = 0 + var quantity: Long = 0 + var direction: OrderDirection = OrderDirection.BID + var matchConstraint: MatchConstraint = MatchConstraint.GTC + var orderType: OrderType = OrderType.LIMIT_ORDER + + + constructor(ouid: String, + uuid: String, + orderId: Long?, + pair: Pair, + price: Long, + quantity: Long, + direction: OrderDirection, + matchConstraint: MatchConstraint, + orderType: OrderType):this(){ + this.ouid = ouid + this.uuid = uuid + this.orderId = orderId + this.pair = pair + this.price = price + this.quantity = quantity + this.direction = direction + this.matchConstraint = matchConstraint + this.orderType = orderType + } + + +} \ No newline at end of file diff --git a/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitResult.kt b/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitResult.kt new file mode 100644 index 000000000..3e7c4b50c --- /dev/null +++ b/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitResult.kt @@ -0,0 +1,3 @@ +package co.nilin.mixchange.port.order.kafka.inout + +class OrderSubmitResult(offset: Long?) \ No newline at end of file diff --git a/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/service/OrderSubmitter.kt b/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/service/OrderSubmitter.kt new file mode 100644 index 000000000..be061b215 --- /dev/null +++ b/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/service/OrderSubmitter.kt @@ -0,0 +1,28 @@ +package co.nilin.mixchange.port.order.kafka.service + +import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest +import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitResult +import org.springframework.kafka.core.KafkaTemplate +import org.springframework.stereotype.Component +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine + +@Component +class OrderSubmitter(val kafkaTemplate: KafkaTemplate) { + suspend fun submit(order: OrderSubmitRequest): OrderSubmitResult = suspendCoroutine { cont -> + println("OrderSubmit!") + val sendFuture = kafkaTemplate.send("orders_${order.pair.leftSideName}_${order.pair.rightSideName}", order) + sendFuture.addCallback({ sendResult -> + cont.resume(OrderSubmitResult(sendResult?.recordMetadata?.offset())) + }, { exception -> + cont.resumeWithException(exception) + }) + /*cont.invokeOnCancellation { + sendFuture.cancel(true) + }*/ + } + + + +} \ No newline at end of file diff --git a/MatchingGateway/pom.xml b/MatchingGateway/pom.xml new file mode 100644 index 000000000..bb82c2d18 --- /dev/null +++ b/MatchingGateway/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + co.nilin.mixchange + matching-gateway-root + 0.0.1-SNAPSHOT + matching-gateway-root + pom + Matching Api Gateway root of Mixchange + + gateway-app + gateway-port/order-submitter-kafka + + diff --git a/UserManagement/.gitignore b/UserManagement/.gitignore new file mode 100644 index 000000000..807d27eef --- /dev/null +++ b/UserManagement/.gitignore @@ -0,0 +1,76 @@ +# Created by .ignore support plugin (hsz.mobi) +### Kotlin template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +.idea/artifacts +.idea/compiler.xml +.idea/jarRepositories.xml +.idea/modules.xml +.idea/*.iml +.idea/modules +*.iml +*.ipr + +# File-based project format +*.iws + +# IntelliJ +out/ + +target/ + +.DS_Store + diff --git a/UserManagement/keycloak-gateway/.gitignore b/UserManagement/keycloak-gateway/.gitignore new file mode 100644 index 000000000..90ffaa853 --- /dev/null +++ b/UserManagement/keycloak-gateway/.gitignore @@ -0,0 +1,73 @@ +# Created by .ignore support plugin (hsz.mobi) +### Kotlin template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +.idea/artifacts +.idea/compiler.xml +.idea/jarRepositories.xml +.idea/modules.xml +.idea/*.iml +.idea/modules +*.iml +*.ipr + +# File-based project format +*.iws + +# IntelliJ +out/ + +target/ diff --git a/UserManagement/keycloak-gateway/Dockerfile b/UserManagement/keycloak-gateway/Dockerfile new file mode 100644 index 000000000..f2cbd4c26 --- /dev/null +++ b/UserManagement/keycloak-gateway/Dockerfile @@ -0,0 +1,5 @@ +FROM openjdk:8-jdk-alpine +VOLUME /tmp +ARG JAR_FILE=target/*.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"] \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/pom.xml b/UserManagement/keycloak-gateway/pom.xml new file mode 100644 index 000000000..d2a77def2 --- /dev/null +++ b/UserManagement/keycloak-gateway/pom.xml @@ -0,0 +1,221 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + keycloak-gateway + 0.0.1-SNAPSHOT + keycloak-gateway + Keycloak gateway app Mixchange + + + 13 + 13 + 13 + 1.4.31 + 12.0.4 + 3.13.2.Final + 11.0.10.Final + + + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + com.fasterxml.jackson.module + jackson-module-kotlin + + + org.springframework.boot + spring-boot-configuration-processor + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.jboss.resteasy + resteasy-jackson2-provider + ${keycloak.resteasy.version} + + + org.jboss.resteasy + resteasy-client + ${keycloak.resteasy.version} + + + org.keycloak + keycloak-dependencies-server-all + ${keycloak.version} + pom + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + org.postgresql + postgresql + runtime + + + com.zaxxer + HikariCP + + + org.springframework.kafka + spring-kafka + + + org.springframework.kafka + spring-kafka-test + test + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18 + + + ${skip.unit.tests} + + + **/*IntegrationTest.java + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-test-source + generate-test-sources + + add-test-source + + + + src/test/java + + + + + compile + + add-source + + + + src/main/java + + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + + -Xjsr305=strict + + + spring + + 1.8 + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + compile + compile + + compile + + + + testCompile + test-compile + + testCompile + + + + + + opex-auth + + diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/ApplicationContextHolder.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/ApplicationContextHolder.kt new file mode 100644 index 000000000..f185cb3f3 --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/ApplicationContextHolder.kt @@ -0,0 +1,11 @@ +package co.nilin.mixchange.auth.gateway + +import org.springframework.context.ApplicationContext + +class ApplicationContextHolder { + companion object { + var applicationContext: ApplicationContext? = null + fun getCurrentContext(): ApplicationContext? { return applicationContext } + fun setCurrentContext(applicationContext: ApplicationContext) { this.applicationContext = applicationContext } + } +} \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/KeycloakGatewayApp.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/KeycloakGatewayApp.kt new file mode 100644 index 000000000..8aa639743 --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/KeycloakGatewayApp.kt @@ -0,0 +1,24 @@ +package co.nilin.mixchange.auth.gateway + +import co.nilin.mixchange.auth.gateway.config.KeycloakServerProperties +import co.nilin.mixchange.auth.gateway.config.SimplePlatformProvider +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration +import org.springframework.boot.autoconfigure.web.ServerProperties +import org.springframework.boot.context.event.ApplicationReadyEvent +import org.springframework.boot.context.properties.EnableConfigurationProperties +import org.springframework.boot.runApplication +import org.springframework.context.ApplicationListener +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.ComponentScan + +@SpringBootApplication(exclude = [LiquibaseAutoConfiguration::class]) +@ComponentScan(basePackages = arrayOf("co.nilin.mixchange.auth.gateway")) +@EnableConfigurationProperties +class KeycloakGatewayApp + +fun main(args: Array) { + ApplicationContextHolder.setCurrentContext(runApplication(*args)) +} \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/AppConfig.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/AppConfig.kt new file mode 100644 index 000000000..f87400ec7 --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/AppConfig.kt @@ -0,0 +1,28 @@ +package co.nilin.mixchange.auth.gateway.config + +import co.nilin.mixchange.auth.gateway.KeycloakGatewayApp +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.boot.autoconfigure.web.ServerProperties +import org.springframework.boot.context.event.ApplicationReadyEvent +import org.springframework.context.ApplicationListener +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +class AppConfig { + private val LOG: Logger = LoggerFactory.getLogger(KeycloakGatewayApp::class.java) + + @Bean + fun onApplicationReadyEventListener( + serverProperties:ServerProperties, + keycloakServerProperties: KeycloakServerProperties + ):ApplicationListener + { + return ApplicationListener { evt -> + val port = serverProperties.port + val keycloakContextPath = keycloakServerProperties.contextPath + LOG.info("Embedded Keycloak started: http://localhost:{}{} to use keycloak", port, keycloakContextPath) + } + } +} diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakApplication.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakApplication.kt new file mode 100644 index 000000000..fb07560e7 --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakApplication.kt @@ -0,0 +1,79 @@ +package co.nilin.mixchange.auth.gateway.config + +import org.keycloak.Config +import org.keycloak.representations.idm.RealmRepresentation + +import org.keycloak.util.JsonSerialization + +import org.springframework.core.io.ClassPathResource + +import org.keycloak.services.managers.RealmManager + +import org.keycloak.services.managers.ApplianceBootstrap + +import java.util.NoSuchElementException + +import org.keycloak.services.util.JsonConfigProviderFactory + +import org.keycloak.services.resources.KeycloakApplication +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.core.io.Resource +import java.lang.Exception + + +class EmbeddedKeycloakApplication() : KeycloakApplication() { + private val LOG: Logger = LoggerFactory.getLogger(EmbeddedKeycloakApplication::class.java) + + companion object { + var keycloakServerProperties: KeycloakServerProperties? = null + } + + override fun loadConfig() { + val factory: JsonConfigProviderFactory = RegularJsonConfigProviderFactory() + Config.init(factory.create() + .orElseThrow { NoSuchElementException("No value present") }) + } + + init { + createMasterRealmAdminUser() + createMixchangeRealm() + } + + private fun createMasterRealmAdminUser() { + val session = getSessionFactory().create() + val applianceBootstrap = ApplianceBootstrap(session) + val admin = keycloakServerProperties!!.adminUser + try { + session.transactionManager.begin() + applianceBootstrap.createMasterRealmUser(admin.username, admin.password) + session.transactionManager.commit() + } catch (ex: Exception) { + LOG.warn("Couldn't create keycloak master admin user: {}", ex.message) + session.transactionManager.rollback() + } + session.close() + } + + private fun createMixchangeRealm() { + val session = getSessionFactory().create() + try { + session.transactionManager.begin() + val manager = RealmManager(session) + val lessonRealmImportFile: Resource = ClassPathResource( + keycloakServerProperties!!.realmImportFile + ) + manager.importRealm( + JsonSerialization.readValue( + lessonRealmImportFile.getInputStream(), + RealmRepresentation::class.java + ) + ) + session.transactionManager.commit() + } catch (ex: Exception) { + LOG.warn("Failed to import Realm json file: {}", ex.message) + session.transactionManager.rollback() + } + session.close() + } +} \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakConfig.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakConfig.kt new file mode 100644 index 000000000..b0892e728 --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakConfig.kt @@ -0,0 +1,77 @@ +package co.nilin.mixchange.auth.gateway.config + +import org.jboss.resteasy.plugins.server.servlet.HttpServlet30Dispatcher +import org.jboss.resteasy.plugins.server.servlet.ResteasyContextParameters +import org.springframework.boot.web.servlet.FilterRegistrationBean +import org.springframework.boot.web.servlet.ServletRegistrationBean +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import java.util.* +import java.util.concurrent.Executors +import javax.naming.* +import javax.naming.spi.InitialContextFactory +import javax.naming.spi.NamingManager +import javax.sql.DataSource + +@Configuration +class EmbeddedKeycloakConfig { + @Bean + @Throws(Exception::class) + fun keycloakJaxRsApplication( + keycloakServerProperties: KeycloakServerProperties, dataSource: DataSource + ): ServletRegistrationBean? { + mockJndiEnvironment(dataSource) + EmbeddedKeycloakApplication.keycloakServerProperties = keycloakServerProperties + val servlet = ServletRegistrationBean( + HttpServlet30Dispatcher() + ) + servlet.addInitParameter("javax.ws.rs.Application", EmbeddedKeycloakApplication::class.java.getName()) + servlet.addInitParameter( + ResteasyContextParameters.RESTEASY_SERVLET_MAPPING_PREFIX, + keycloakServerProperties.contextPath + ) + servlet.addInitParameter(ResteasyContextParameters.RESTEASY_USE_CONTAINER_FORM_PARAMS, "true") + servlet.addUrlMappings(keycloakServerProperties.contextPath + "/*") + servlet.setLoadOnStartup(1) + servlet.setAsyncSupported(true) + return servlet + } + + @Bean + fun keycloakSessionManagement(keycloakServerProperties: KeycloakServerProperties): FilterRegistrationBean? { + val filter: FilterRegistrationBean = + FilterRegistrationBean() + filter.setName("Keycloak Session Management") + filter.setFilter(EmbeddedKeycloakRequestFilter()) + filter.addUrlPatterns(keycloakServerProperties.contextPath + "/*") + return filter + } + + @Throws(NamingException::class) + private fun mockJndiEnvironment(dataSource: DataSource) { + NamingManager.setInitialContextFactoryBuilder { env: Hashtable<*, *>? -> + InitialContextFactory { environment: Hashtable<*, *>? -> + object : InitialContext() { + @Throws(NamingException::class) + fun KeycloakInitialContext(environment: Hashtable<*, *>?) { + } + + @Throws(NamingException::class) + override fun lookup(name: Name): Any? { + return lookup(name.toString()) + } + + @Throws(NamingException::class) + override fun lookup(name: String): Any? { + return Optional.ofNullable(environment!![name]) + .orElseThrow { NamingException("Name $name not found") } + } + + override fun getNameParser(name: String?): NameParser { + return NameParser { CompositeName() } + } + } + } + } + } +} \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakRequestFilter.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakRequestFilter.kt new file mode 100644 index 000000000..f6f434f68 --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakRequestFilter.kt @@ -0,0 +1,52 @@ +package co.nilin.mixchange.auth.gateway.config + +import org.keycloak.common.ClientConnection +import org.keycloak.models.KeycloakSession +import org.keycloak.services.filters.AbstractRequestFilter +import java.io.UnsupportedEncodingException +import java.lang.Exception +import javax.servlet.Filter +import javax.servlet.FilterChain +import javax.servlet.ServletRequest +import javax.servlet.ServletResponse +import javax.servlet.http.HttpServletRequest + + +class EmbeddedKeycloakRequestFilter : AbstractRequestFilter(), Filter { + @Throws(UnsupportedEncodingException::class) + override fun doFilter(servletRequest: ServletRequest, servletResponse: ServletResponse?, filterChain: FilterChain) { + servletRequest.setCharacterEncoding("UTF-8") + val clientConnection = createConnection(servletRequest as HttpServletRequest) + filter(clientConnection) { session: KeycloakSession? -> + try { + filterChain.doFilter(servletRequest, servletResponse) + } catch (e: Exception) { + throw RuntimeException(e) + } + } + } + + private fun createConnection(request: HttpServletRequest): ClientConnection { + return object : ClientConnection { + override fun getRemoteAddr(): String { + return request.getRemoteAddr() + } + + override fun getRemoteHost(): String { + return request.getRemoteHost() + } + + override fun getRemotePort(): Int { + return request.getRemotePort() + } + + override fun getLocalAddr(): String { + return request.getLocalAddr() + } + + override fun getLocalPort(): Int { + return request.getLocalPort() + } + } + } +} \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/KafkaConfig.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/KafkaConfig.kt new file mode 100644 index 000000000..aa6abae40 --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/KafkaConfig.kt @@ -0,0 +1,47 @@ +package co.nilin.mixchange.auth.gateway.config + +import co.nilin.mixchange.auth.gateway.model.AuthEvent +import org.apache.kafka.clients.admin.NewTopic +import org.apache.kafka.common.serialization.StringSerializer +import org.apache.kafka.clients.producer.ProducerConfig +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.support.GenericApplicationContext +import org.springframework.kafka.core.DefaultKafkaProducerFactory +import org.springframework.kafka.core.KafkaTemplate +import org.springframework.kafka.core.ProducerFactory +import org.springframework.kafka.support.serializer.JsonSerializer +import java.util.HashMap + +@Configuration +class KafkaConfig { + @Value("\${spring.kafka.bootstrap-servers}") + private lateinit var bootstrapServers: String + + @Bean("authProducerConfigs") + fun producerConfigs(): Map? { + val props: MutableMap = HashMap() + props[ProducerConfig.BOOTSTRAP_SERVERS_CONFIG] = bootstrapServers + props[ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG] = StringSerializer::class.java + props[ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG] = JsonSerializer::class.java + return props + } + + @Bean("authProducerFactory") + fun producerFactory(@Qualifier("authProducerConfigs") producerConfigs: Map): ProducerFactory { + return DefaultKafkaProducerFactory(producerConfigs) + } + + @Bean("authKafkaTemplate") + fun kafkaTemplate(@Qualifier("authProducerFactory") producerFactory: ProducerFactory): KafkaTemplate { + return KafkaTemplate(producerFactory) + } + + @Autowired + fun createUserCreatedTopics(applicationContext: GenericApplicationContext){ + applicationContext.registerBean("topic_auth_user_created", NewTopic::class.java, "auth_user_created", 1 ,1) + } +} \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/KeycloakServerProperties.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/KeycloakServerProperties.kt new file mode 100644 index 000000000..732153bc3 --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/KeycloakServerProperties.kt @@ -0,0 +1,18 @@ +package co.nilin.mixchange.auth.gateway.config + +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.context.annotation.Configuration + +@Configuration +@ConfigurationProperties(prefix = "keycloak.server") +class KeycloakServerProperties { + var contextPath = "/auth" + var realmImportFile = "opex-realm.json" + var adminUser = AdminUser() + + class AdminUser { + var username = "admin" + var password = "admin" + } +} + diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/RegularJsonConfigProviderFactory.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/RegularJsonConfigProviderFactory.kt new file mode 100644 index 000000000..6283ff4d3 --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/RegularJsonConfigProviderFactory.kt @@ -0,0 +1,6 @@ +package co.nilin.mixchange.auth.gateway.config + +import org.keycloak.services.util.JsonConfigProviderFactory + +class RegularJsonConfigProviderFactory: JsonConfigProviderFactory() { +} \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/Resteasy3Provider.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/Resteasy3Provider.kt new file mode 100644 index 000000000..99615d022 --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/Resteasy3Provider.kt @@ -0,0 +1,24 @@ +package co.nilin.mixchange.auth.gateway.config + +import org.jboss.resteasy.core.Dispatcher +import org.jboss.resteasy.spi.ResteasyProviderFactory +import org.keycloak.common.util.ResteasyProvider + +class Resteasy3Provider: ResteasyProvider { + override fun getContextData(type: Class?): R { + return ResteasyProviderFactory.getContextData(type) + } + + override fun pushDefaultContextObject(type: Class<*>?, instance: Any?) { + getContextData(Dispatcher::class.java).defaultContextObjects[type] = + instance + } + + override fun pushContext(type: Class, instance: Any) { + ResteasyProviderFactory.pushContext(type, instance) + } + + override fun clearContextData() { + ResteasyProviderFactory.clearContextData() + } +} \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/SimplePlatformProvider.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/SimplePlatformProvider.kt new file mode 100644 index 000000000..474164204 --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/SimplePlatformProvider.kt @@ -0,0 +1,30 @@ +package co.nilin.mixchange.auth.gateway.config + +import org.keycloak.services.ServicesLogger + +import org.keycloak.platform.PlatformProvider + + +class SimplePlatformProvider : PlatformProvider { + var shutdownHook: Runnable? = null + override fun onStartup(startupHook: Runnable) { + startupHook.run() + } + + override fun onShutdown(shutdownHook: Runnable) { + this.shutdownHook = shutdownHook + } + + override fun exit(cause: Throwable) { + ServicesLogger.LOGGER.fatal(cause) + exit(1) + } + + private fun exit(status: Int) { + object : Thread() { + override fun run() { + System.exit(status) + } + }.start() + } +} \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/extension/ExtendedEventListenerProvider.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/extension/ExtendedEventListenerProvider.kt new file mode 100644 index 000000000..1567a0b03 --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/extension/ExtendedEventListenerProvider.kt @@ -0,0 +1,121 @@ +package co.nilin.mixchange.auth.gateway.extension + +import co.nilin.mixchange.auth.gateway.ApplicationContextHolder +import co.nilin.mixchange.auth.gateway.model.AuthEvent +import co.nilin.mixchange.auth.gateway.model.UserCreatedEvent +import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.ObjectMapper +import org.keycloak.events.Event +import org.keycloak.events.EventListenerProvider +import org.keycloak.events.EventType +import org.keycloak.events.admin.AdminEvent +import org.keycloak.events.admin.OperationType +import org.keycloak.events.admin.ResourceType +import org.keycloak.models.AbstractKeycloakTransaction +import org.keycloak.models.KeycloakSession +import org.keycloak.models.RealmProvider +import org.slf4j.LoggerFactory +import org.springframework.kafka.core.KafkaTemplate + +class ExtendedEventListenerProvider(private val session: KeycloakSession) : EventListenerProvider { + val logger = LoggerFactory.getLogger(ExtendedEventListenerProvider::class.java) + private val model: RealmProvider + val objectMapper = com.fasterxml.jackson.module.kotlin.jacksonObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + + data class UserData( + val username: String, + val enabled: Boolean, + val emailVerified: Boolean, + val firstName: String, + val lastName: String, + val email: String + ) + + data class UserUuidDto(val type: String, val uuid: String, val email: String) + + + override fun onEvent(event: Event) { + logger.info("## NEW %s EVENT", event.getType()) + logger.info("-----------------------------------------------------------") + event.getDetails().forEach { key, value -> logger.info(key.toString() + ": " + value) } + + // USE CASE SCENARIO, I'm sure there are better use case scenario's :p + // + // Let's assume for whatever reason you only want the user + // to be able to verify his account if a transaction we make succeeds. + // Let's say an external call to a service needs to return a 200 response code or we throw an exception. + + // When the user tries to login after a failed attempt, + // the user remains unverified and when trying to login will receive another verify account email. + if (EventType.VERIFY_EMAIL.equals(event.getType())) { + val realm = model.getRealm(event.getRealmId()) + val user = session.users().getUserById(event.getUserId(), realm) + if (user != null && user.email != null && user.isEmailVerified) { + logger.info("USER HAS VERIFIED EMAIL : " + event.getUserId()) + + // Example of adding an attribute when this event happens + user.setSingleAttribute("attribute-key", "attribute-value") + val userUuidDto = UserUuidDto(event.getType().name, event.getUserId(), user.email) + val userVerifiedTransaction = UserVerifiedTransaction(userUuidDto) + + // enlistPrepare -> if our transaction fails than the user is NOT verified + // enlist -> if our transaction fails than the user is still verified + // enlistAfterCompletion -> if our transaction fails our user is still verified + session.transactionManager.enlistPrepare(userVerifiedTransaction) + } + } + logger.info("-----------------------------------------------------------") + } + + override fun onEvent(adminEvent: AdminEvent, b: Boolean) { + logger.info("## NEW ADMIN EVENT") + logger.info("-----------------------------------------------------------") + logger.info("Resource path" + ": " + adminEvent.resourcePath) + logger.info("Resource type" + ": " + adminEvent.resourceType) + logger.info("Operation type" + ": " + adminEvent.operationType) + if (ResourceType.USER.equals(adminEvent.resourceType) + && OperationType.CREATE.equals(adminEvent.operationType) + ) { + logger.info("A new user has been created") + val userData = objectMapper.readValue(adminEvent.representation, UserData::class.java) + val uuid = adminEvent.resourcePath.substringAfter("/") + (ApplicationContextHolder.getCurrentContext()!! + .getBean("authKafkaTemplate") as KafkaTemplate) + .send("auth_user_created", UserCreatedEvent(uuid, userData.firstName, userData.lastName, userData.email)) + } + logger.info("-----------------------------------------------------------") + } + + override fun close() { + // Nothing to close + } + + + init { + model = session.realms() + } + + class UserVerifiedTransaction(private val userUuidDto: UserUuidDto) : AbstractKeycloakTransaction() { + override fun commitImpl() { + logger.info("## USER VERIFIED TRANSACTION") + logger.info("-----------------------------------------------------------") + logger.info(userUuidDto.toString()) + logger.info("-----------------------------------------------------------") + + // You could make a http call here and send the object. + // When we throw an exception here, the user would not be verified when using .enlistPrepare + //throw new RuntimeException("External call failed!"); + try { + + } catch (e: Exception) { + throw RuntimeException("##### USER VERIFIED TRANSACTION FAILED !", e) + } + } + + override fun rollbackImpl() { + // + } + + } +} \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/extension/ExtendedEventListenerProviderFactory.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/extension/ExtendedEventListenerProviderFactory.kt new file mode 100644 index 000000000..18bae2d5b --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/extension/ExtendedEventListenerProviderFactory.kt @@ -0,0 +1,28 @@ +package co.nilin.mixchange.auth.gateway.extension + +import org.keycloak.Config +import org.keycloak.events.EventListenerProviderFactory +import org.keycloak.models.KeycloakSession +import org.keycloak.models.KeycloakSessionFactory + +class ExtendedEventListenerProviderFactory : EventListenerProviderFactory { + override fun create(keycloakSession: KeycloakSession): ExtendedEventListenerProvider { + return ExtendedEventListenerProvider(keycloakSession) + } + + override fun init(scope: Config.Scope) { + // + } + + override fun postInit(keycloakSessionFactory: KeycloakSessionFactory) { + // + } + + override fun close() { + // + } + + override fun getId(): String { + return "pl_event_listener" + } +} \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/AuthEvent.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/AuthEvent.kt new file mode 100644 index 000000000..8f774787a --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/AuthEvent.kt @@ -0,0 +1,7 @@ +package co.nilin.mixchange.auth.gateway.model + +import java.time.LocalDateTime + +open class AuthEvent { + var eventDate: LocalDateTime = LocalDateTime.now() +} \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/UserCreatedEvent.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/UserCreatedEvent.kt new file mode 100644 index 000000000..ffabaabf2 --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/UserCreatedEvent.kt @@ -0,0 +1,18 @@ +package co.nilin.mixchange.auth.gateway.model + +class UserCreatedEvent: AuthEvent { + lateinit var uuid: String + lateinit var firstName: String + lateinit var lastName: String + lateinit var email: String + + + constructor(uuid: String, firstName: String, lastName: String, email: String) : super() { + this.uuid = uuid + this.firstName = firstName + this.lastName = lastName + this.email = email + } + + constructor() : super() +} \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/resources/META-INF/keycloak-server.json b/UserManagement/keycloak-gateway/src/main/resources/META-INF/keycloak-server.json new file mode 100644 index 000000000..cf9a761ff --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/resources/META-INF/keycloak-server.json @@ -0,0 +1,240 @@ +{ + + "hostname": { + "provider": "${keycloak.hostname.provider:default}", + + "fixed": { + "hostname": "${keycloak.hostname.fixed.hostname:localhost}", + "httpPort": "${keycloak.hostname.fixed.httpPort:-1}", + "httpsPort": "${keycloak.hostname.fixed.httpsPort:-1}", + "alwaysHttps": "${keycloak.hostname.fixed.alwaysHttps:false}" + }, + + "default": { + "frontendUrl": "${keycloak.frontendUrl:}", + "adminUrl": "${keycloak.adminUrl:}", + "forceBackendUrlToFrontendUrl": "${keycloak.hostname.default.forceBackendUrlToFrontendUrl:false}" + } + }, + + "admin": { + "realm": "master" + }, + + "eventsStore": { + "provider": "${keycloak.eventsStore.provider:jpa}", + "jpa": { + "max-detail-length": "${keycloak.eventsStore.maxDetailLength:1000}" + } + }, + + "eventsListener": { + "jboss-logging" : { + "success-level": "debug", + "error-level": "warn" + }, + "event-queue": {} + }, + + "realm": { + "provider": "${keycloak.realm.provider:jpa}" + }, + + "user": { + "provider": "${keycloak.user.provider:jpa}" + }, + + "client": { + "provider": "${keycloak.client.provider:jpa}" + }, + + "clientScope": { + "provider": "${keycloak.clientScope.provider:jpa}" + }, + + "group": { + "provider": "${keycloak.group.provider:jpa}" + }, + + "role": { + "provider": "${keycloak.role.provider:jpa}" + }, + + "authenticationSessions": { + "provider": "${keycloak.authSession.provider:infinispan}" + }, + + "mapStorage": { + "provider": "${keycloak.mapStorage.provider:concurrenthashmap}", + "concurrenthashmap": { + "dir": "${project.build.directory:target}" + } + }, + + "userFederatedStorage": { + "provider": "${keycloak.userFederatedStorage.provider:jpa}" + }, + + "userSessionPersister": { + "provider": "${keycloak.userSessionPersister.provider:jpa}" + }, + + "authorizationPersister": { + "provider": "${keycloak.authorization.provider:jpa}" + }, + + "userCache": { + "provider": "${keycloak.user.cache.provider:default}", + "default" : { + "enabled": true + }, + "mem": { + "maxSize": 20000 + } + }, + + "userSessions": { + "provider" : "${keycloak.userSessions.provider:infinispan}" + }, + + "timer": { + "provider": "basic" + }, + + "theme": { + "staticMaxAge": "${keycloak.theme.staticMaxAge:2592000}", + "cacheTemplates": "${keycloak.theme.cacheTemplates:true}", + "cacheThemes": "${keycloak.theme.cacheThemes:true}", + "folder": { + "dir": "${keycloak.theme.dir}" + } + }, + + "login": { + "provider": "freemarker" + }, + + "account": { + "provider": "freemarker" + }, + + "email": { + "provider": "freemarker" + }, + + "scheduled": { + "interval": 900 + }, + + "connectionsHttpClient": { + "default": { + "reuse-connections": false + } + }, + + + "connectionsJpa": { + "default": { + "url": "jdbc:postgresql://postgres-auth/opex_auth", + "driver": "org.postgresql.Driver", + "driverDialect": "org.hibernate.dialect.PostgreSQLDialect", + "user": "opex", + "password": "hiopex", + "initializeEmpty": true, + "migrationStrategy": "update", + "showSql": "true", + "formatSql": "true", + "globalStatsInterval": "-1" + } + }, + + "realmCache": { + "provider": "${keycloak.realm.cache.provider:default}", + "default" : { + "enabled": true + } + }, + + "connectionsInfinispan": { + "default": { + "jgroupsUdpMcastAddr": "${keycloak.connectionsInfinispan.jgroupsUdpMcastAddr:234.56.78.90}", + "nodeName": "${keycloak.connectionsInfinispan.nodeName,jboss.node.name:}", + "siteName": "${keycloak.connectionsInfinispan.siteName,jboss.site.name:}", + "clustered": "${keycloak.connectionsInfinispan.clustered:false}", + "async": "${keycloak.connectionsInfinispan.async:false}", + "sessionsOwners": "${keycloak.connectionsInfinispan.sessionsOwners:1}", + "l1Lifespan": "${keycloak.connectionsInfinispan.l1Lifespan:600000}", + "remoteStoreEnabled": "${keycloak.connectionsInfinispan.remoteStoreEnabled:false}", + "remoteStoreHost": "${keycloak.connectionsInfinispan.remoteStoreServer:localhost}", + "remoteStorePort": "${keycloak.connectionsInfinispan.remoteStorePort:11222}", + "hotrodProtocolVersion": "${keycloak.connectionsInfinispan.hotrodProtocolVersion}", + "embedded": "${keycloak.connectionsInfinispan.embedded:true}" + } + }, + + "truststore": { + "file": { + "disabled": "${keycloak.truststore.disabled:true}" + } + }, + + "jta-lookup": { + "provider": "${keycloak.jta.lookup.provider:jboss}", + "jboss" : { + "enabled": true + } + + }, + + "login-protocol": { + "saml": { + "knownProtocols": [ + "http=${auth.server.http.port}", + "https=${auth.server.https.port}" + ] + } + }, + + "userProfile": { + "legacy-user-profile": { + "read-only-attributes": [ "deniedFoo", "deniedBar*", "deniedSome/thing", "deniedsome*thing" ], + "admin-read-only-attributes": [ "deniedSomeAdmin" ] + } + }, + + "x509cert-lookup": { + "provider": "${keycloak.x509cert.lookup.provider:default}", + "default": { + "enabled": true + }, + "haproxy": { + "enabled": true, + "sslClientCert": "x-ssl-client-cert", + "sslCertChainPrefix": "x-ssl-client-cert-chain", + "certificateChainLength": 1 + }, + "apache": { + "enabled": true, + "sslClientCert": "x-ssl-client-cert", + "sslCertChainPrefix": "x-ssl-client-cert-chain", + "certificateChainLength": 1 + }, + "nginx": { + "enabled": true, + "sslClientCert": "x-ssl-client-cert", + "sslCertChainPrefix": "x-ssl-client-cert-chain", + "certificateChainLength": 1 + } + }, + + "vault": { + "files-plaintext": { + "dir": "target/dependency/vault", + "enabled": "${keycloak.vault.files-plaintext.provider.enabled:false}" + } + }, + + "saml-artifact-resolver": { + "provider": "${keycloak.saml-artifact-resolver.provider:default}" + } +} diff --git a/UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.common.util.ResteasyProvider b/UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.common.util.ResteasyProvider new file mode 100644 index 000000000..0f0d10a36 --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.common.util.ResteasyProvider @@ -0,0 +1 @@ +co.nilin.mixchange.auth.gateway.config.Resteasy3Provider \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory b/UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory new file mode 100644 index 000000000..40f8f13db --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory @@ -0,0 +1 @@ +co.nilin.mixchange.auth.gateway.extension.ExtendedEventListenerProviderFactory \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.platform.PlatformProvider b/UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.platform.PlatformProvider new file mode 100644 index 000000000..e4a4f5850 --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.platform.PlatformProvider @@ -0,0 +1 @@ +co.nilin.mixchange.auth.gateway.config.SimplePlatformProvider \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/resources/application-docker.yml b/UserManagement/keycloak-gateway/src/main/resources/application-docker.yml new file mode 100644 index 000000000..974a904c8 --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/resources/application-docker.yml @@ -0,0 +1,40 @@ +server.port: 8083 +spring: + application: + name: opex-auth + main: + allow-bean-definition-overriding: false + kafka: + bootstrap-servers: ${KAFKA_IP_PORT} + consumer: + group-id: auth + datasource: + platform: postgres + driver-class-name: org.postgresql.Driver + url: jdbc:postgresql://${DB_IP_PORT}/opex_auth + username: opex + password: hiopex + initialization-mode: always + jpa: + hibernate: + ddl-auto: none + properties.hibernate.jdbc.lob.non_contextual_creation: true + open-in-view: false + cloud: + bootstrap: + enabled: true + consul: + host: ${CONSUL_HOST} + port: 8500 + discovery: + #healthCheckPath: ${management.context-path}/health + instance-id: ${spring.application.name}:${server.port} + healthCheckInterval: 20s + prefer-ip-address: true +keycloak: + server: + contextPath: /auth + adminUser: + username: opex-admin + password: hiopex + realmImportFile: opex-realm.json diff --git a/UserManagement/keycloak-gateway/src/main/resources/application.yml b/UserManagement/keycloak-gateway/src/main/resources/application.yml new file mode 100644 index 000000000..57a3b588a --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/resources/application.yml @@ -0,0 +1,35 @@ +server.port: 8083 +spring: + application: + name: opex-auth + main: + allow-bean-definition-overriding: false + datasource: + platform: postgres + driver-class-name: org.postgresql.Driver + url: jdbc:postgresql://localhost/opex_auth + username: opex + password: hiopex + initialization-mode: always + jpa: + hibernate: + ddl-auto: none + properties.hibernate.jdbc.lob.non_contextual_creation: true + open-in-view: false + cloud: + bootstrap: + enabled: true + consul: + port: 8500 + discovery: + #healthCheckPath: ${management.context-path}/health + instance-id: ${spring.application.name}:${server.port} + healthCheckInterval: 20s + prefer-ip-address: true +keycloak: + server: + contextPath: /auth + adminUser: + username: opex-admin + password: hiopex + realmImportFile: opex-realm.json diff --git a/UserManagement/keycloak-gateway/src/main/resources/opex-realm.json b/UserManagement/keycloak-gateway/src/main/resources/opex-realm.json new file mode 100644 index 000000000..083c9f34a --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/resources/opex-realm.json @@ -0,0 +1,1615 @@ +{ + "id" : "mixchange", + "realm" : "mixchange", + "notBefore" : 0, + "revokeRefreshToken" : false, + "refreshTokenMaxReuse" : 0, + "accessTokenLifespan" : 300, + "accessTokenLifespanForImplicitFlow" : 900, + "ssoSessionIdleTimeout" : 1800, + "ssoSessionMaxLifespan" : 36000, + "ssoSessionIdleTimeoutRememberMe" : 0, + "ssoSessionMaxLifespanRememberMe" : 0, + "offlineSessionIdleTimeout" : 2592000, + "offlineSessionMaxLifespanEnabled" : false, + "offlineSessionMaxLifespan" : 5184000, + "accessCodeLifespan" : 60, + "accessCodeLifespanUserAction" : 300, + "accessCodeLifespanLogin" : 1800, + "actionTokenGeneratedByAdminLifespan" : 43200, + "actionTokenGeneratedByUserLifespan" : 300, + "enabled" : true, + "sslRequired" : "external", + "registrationAllowed" : false, + "registrationEmailAsUsername" : false, + "rememberMe" : false, + "verifyEmail" : false, + "loginWithEmailAllowed" : true, + "duplicateEmailsAllowed" : false, + "resetPasswordAllowed" : false, + "editUsernameAllowed" : false, + "bruteForceProtected" : false, + "permanentLockout" : false, + "maxFailureWaitSeconds" : 900, + "minimumQuickLoginWaitSeconds" : 60, + "waitIncrementSeconds" : 60, + "quickLoginCheckMilliSeconds" : 1000, + "maxDeltaTimeSeconds" : 43200, + "failureFactor" : 30, + "roles" : { + "realm" : [ { + "id" : "3b6109f5-6e5a-4578-83c3-791ec3e2bf9e", + "name" : "offline_access", + "description" : "${role_offline-access}", + "composite" : false, + "clientRole" : false, + "containerId" : "mixchange", + "attributes" : { } + }, { + "id" : "0dd6a8c7-d669-4941-9ea1-521980e9c53f", + "name" : "uma_authorization", + "description" : "${role_uma_authorization}", + "composite" : false, + "clientRole" : false, + "containerId" : "mixchange", + "attributes" : { } + }, { + "id" : "ca962095-7f9b-49e2-a190-e391a0d4b704", + "name" : "user", + "composite" : false, + "clientRole" : false, + "containerId" : "mixchange", + "attributes" : { } + } ], + "client" : { + "newClient" : [ ], + "realm-management" : [ { + "id" : "5d00243f-ceec-4b0c-995e-d86d5b8a0ae6", + "name" : "view-clients", + "description" : "${role_view-clients}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "query-clients" ] + } + }, + "clientRole" : true, + "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes" : { } + }, { + "id" : "941612de-bd85-47a5-8dfa-37c270dde28c", + "name" : "view-authorization", + "description" : "${role_view-authorization}", + "composite" : false, + "clientRole" : true, + "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes" : { } + }, { + "id" : "5ea9810d-63cc-4277-9b32-ba8a3d3c6091", + "name" : "manage-realm", + "description" : "${role_manage-realm}", + "composite" : false, + "clientRole" : true, + "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes" : { } + }, { + "id" : "8b7b0dd8-350b-473e-b8cd-8acad34f1358", + "name" : "query-clients", + "description" : "${role_query-clients}", + "composite" : false, + "clientRole" : true, + "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes" : { } + }, { + "id" : "0f8e5ee8-b014-4b7c-9b69-50f46abcba5f", + "name" : "query-groups", + "description" : "${role_query-groups}", + "composite" : false, + "clientRole" : true, + "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes" : { } + }, { + "id" : "911b1489-9383-4734-b134-bf49bf992ce9", + "name" : "manage-clients", + "description" : "${role_manage-clients}", + "composite" : false, + "clientRole" : true, + "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes" : { } + }, { + "id" : "5d48274c-bd6b-4c26-ad54-f1a2254beac0", + "name" : "view-realm", + "description" : "${role_view-realm}", + "composite" : false, + "clientRole" : true, + "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes" : { } + }, { + "id" : "3ea43b64-316f-4693-8346-9ee78b24adaf", + "name" : "manage-identity-providers", + "description" : "${role_manage-identity-providers}", + "composite" : false, + "clientRole" : true, + "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes" : { } + }, { + "id" : "49735614-96ec-49b2-98fe-3af9bcd1a33a", + "name" : "create-client", + "description" : "${role_create-client}", + "composite" : false, + "clientRole" : true, + "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes" : { } + }, { + "id" : "e8f8c3cc-0ff1-4f72-a271-db6821a3cdb6", + "name" : "manage-users", + "description" : "${role_manage-users}", + "composite" : false, + "clientRole" : true, + "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes" : { } + }, { + "id" : "387418b1-4f80-4b00-b9dd-805ca041f805", + "name" : "view-identity-providers", + "description" : "${role_view-identity-providers}", + "composite" : false, + "clientRole" : true, + "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes" : { } + }, { + "id" : "427c27d4-521a-464b-a0df-16d7f537e8d5", + "name" : "realm-admin", + "description" : "${role_realm-admin}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "view-clients", "view-authorization", "query-groups", "manage-realm", "query-clients", "manage-clients", "view-realm", "manage-identity-providers", "create-client", "manage-users", "view-identity-providers", "query-users", "query-realms", "view-users", "impersonation", "manage-authorization", "manage-events", "view-events" ] + } + }, + "clientRole" : true, + "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes" : { } + }, { + "id" : "a574cf01-03e4-4573-ab9e-276d13a1ce8d", + "name" : "query-users", + "description" : "${role_query-users}", + "composite" : false, + "clientRole" : true, + "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes" : { } + }, { + "id" : "c3a253a8-a1b6-4d38-9677-f728f32482ad", + "name" : "query-realms", + "description" : "${role_query-realms}", + "composite" : false, + "clientRole" : true, + "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes" : { } + }, { + "id" : "f3cb93da-273e-419a-b2f4-93f09896abcf", + "name" : "view-users", + "description" : "${role_view-users}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "query-users", "query-groups" ] + } + }, + "clientRole" : true, + "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes" : { } + }, { + "id" : "6eedf2b7-50ef-4495-a89b-54aef751b7fa", + "name" : "manage-authorization", + "description" : "${role_manage-authorization}", + "composite" : false, + "clientRole" : true, + "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes" : { } + }, { + "id" : "0332e99b-3dfc-4193-9e13-5728f8f3e6d6", + "name" : "impersonation", + "description" : "${role_impersonation}", + "composite" : false, + "clientRole" : true, + "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes" : { } + }, { + "id" : "b690cb9c-0f4a-4be5-ade0-b40443d8149d", + "name" : "view-events", + "description" : "${role_view-events}", + "composite" : false, + "clientRole" : true, + "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes" : { } + }, { + "id" : "aac3def5-f193-4a6c-9065-1667a0746a8a", + "name" : "manage-events", + "description" : "${role_manage-events}", + "composite" : false, + "clientRole" : true, + "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes" : { } + } ], + "security-admin-console" : [ ], + "admin-cli" : [ ], + "broker" : [ { + "id" : "397b5703-4c81-48fd-a24c-a7e8177ef657", + "name" : "read-token", + "description" : "${role_read-token}", + "composite" : false, + "clientRole" : true, + "containerId" : "4b9609f0-48d1-4e71-9381-2ecec08616f9", + "attributes" : { } + } ], + "account" : [ { + "id" : "8daa8096-d14e-4d1c-ad1f-83f822016aa1", + "name" : "manage-account", + "description" : "${role_manage-account}", + "composite" : true, + "composites" : { + "client" : { + "account" : [ "manage-account-links" ] + } + }, + "clientRole" : true, + "containerId" : "12eebf0b-a3eb-49f8-9ecf-173cf8a00145", + "attributes" : { } + }, { + "id" : "948269c7-a69c-4c82-a7f3-88868713dfd9", + "name" : "manage-account-links", + "description" : "${role_manage-account-links}", + "composite" : false, + "clientRole" : true, + "containerId" : "12eebf0b-a3eb-49f8-9ecf-173cf8a00145", + "attributes" : { } + }, { + "id" : "aed18201-2433-4998-8fa3-0979b0b31c10", + "name" : "view-profile", + "description" : "${role_view-profile}", + "composite" : false, + "clientRole" : true, + "containerId" : "12eebf0b-a3eb-49f8-9ecf-173cf8a00145", + "attributes" : { } + } ] + } + }, + "groups" : [ ], + "defaultRoles" : [ "uma_authorization", "offline_access" ], + "requiredCredentials" : [ "password" ], + "otpPolicyType" : "totp", + "otpPolicyAlgorithm" : "HmacSHA1", + "otpPolicyInitialCounter" : 0, + "otpPolicyDigits" : 6, + "otpPolicyLookAheadWindow" : 1, + "otpPolicyPeriod" : 30, + "otpSupportedApplications" : [ "FreeOTP", "Google Authenticator" ], + "webAuthnPolicyRpEntityName" : "keycloak", + "webAuthnPolicySignatureAlgorithms" : [ "ES256" ], + "webAuthnPolicyRpId" : "", + "webAuthnPolicyAttestationConveyancePreference" : "not specified", + "webAuthnPolicyAuthenticatorAttachment" : "not specified", + "webAuthnPolicyRequireResidentKey" : "not specified", + "webAuthnPolicyUserVerificationRequirement" : "not specified", + "webAuthnPolicyCreateTimeout" : 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister" : false, + "webAuthnPolicyAcceptableAaguids" : [ ], + "users" : [ { + "id" : "a5461470-33eb-4b2d-82d4-b0484e96ad7f", + "createdTimestamp" : 1574174706812, + "username" : "john@test.com", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "credentials" : [ { + "id" : "024b62f1-3af0-49fc-a15f-e843b72e00df", + "type" : "password", + "createdDate" : 1574174719522, + "secretData" : "{\"value\":\"tfHk9estQfa27osLCY2QelpkL6Sm6rPS+iQ33ytPSD4p10Fk7ophI7ChERAnzDjVSmvdsZAttsg2Yr+8F4Gzfw==\",\"salt\":\"goh99WgI7T1ZuGdrKzNAeQ==\"}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\"}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "offline_access", "uma_authorization" ], + "clientRoles" : { + "account" : [ "manage-account", "view-profile" ] + }, + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "22a4d9fe-194c-4c6e-841a-8a55b402459f", + "createdTimestamp" : 1580237252522, + "username" : "mike@other.com", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "credentials" : [ { + "id" : "73c8d1ca-d548-4e1a-a82d-2c1d0d3ee017", + "type" : "password", + "createdDate" : 1580237291489, + "secretData" : "{\"value\":\"fea4QiAzqmOctQGtJrKvoPRIUKLdQ5UKFIEPEQ7PYjTjxvucDZVk8yzXNfsDHvgpZ2rWiwsACgvsymdHnmru9w==\",\"salt\":\"nu5yJh98jz8cGLnLx36uvg==\"}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\"}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "offline_access", "uma_authorization" ], + "clientRoles" : { + "account" : [ "manage-account", "view-profile" ] + }, + "notBefore" : 0, + "groups" : [ ] + } ], + "scopeMappings" : [ { + "clientScope" : "offline_access", + "roles" : [ "offline_access" ] + } ], + "clients" : [ { + "id" : "12eebf0b-a3eb-49f8-9ecf-173cf8a00145", + "clientId" : "account", + "name" : "${client_account}", + "rootUrl" : "${authBaseUrl}", + "baseUrl" : "/realms/mixchange/account/", + "surrogateAuthRequired" : false, + "enabled" : true, + "clientAuthenticatorType" : "client-secret", + "secret" : "**********", + "defaultRoles" : [ "manage-account", "view-profile" ], + "redirectUris" : [ "/realms/mixchange/account/*" ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "13d76feb-d762-4409-bb84-7a75bc395a61", + "clientId" : "admin-cli", + "name" : "${client_admin-cli}", + "surrogateAuthRequired" : false, + "enabled" : true, + "clientAuthenticatorType" : "client-secret", + "secret" : "**********", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : false, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : true, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "4b9609f0-48d1-4e71-9381-2ecec08616f9", + "clientId" : "broker", + "name" : "${client_broker}", + "surrogateAuthRequired" : false, + "enabled" : true, + "clientAuthenticatorType" : "client-secret", + "secret" : "**********", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "b88ce206-63d6-43b6-87c9-ea09d8c02f32", + "clientId" : "newClient", + "surrogateAuthRequired" : false, + "enabled" : true, + "clientAuthenticatorType" : "client-secret", + "secret" : "newClientSecret", + "redirectUris" : [ "http://localhost:8082/new-client/login/oauth2/code/custom", + "http://localhost:8089/", + "http://localhost:8089/auth/redirect/"], + "webOrigins" : [ "+" ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : true, + "directAccessGrantsEnabled" : true, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "saml.assertion.signature" : "false", + "saml.force.post.binding" : "false", + "saml.multivalued.roles" : "false", + "saml.encrypt" : "false", + "saml.server.signature" : "false", + "saml.server.signature.keyinfo.ext" : "false", + "exclude.session.state.from.auth.response" : "false", + "saml_force_name_id_format" : "false", + "saml.client.signature" : "false", + "tls.client.certificate.bound.access.tokens" : "false", + "saml.authnstatement" : "false", + "display.on.consent.screen" : "false", + "saml.onetimeuse.condition" : "false" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : true, + "nodeReRegistrationTimeout" : -1, + "defaultClientScopes" : [ "role_list", "profile" ], + "optionalClientScopes" : [ "web-origins", "address", "read", "phone", "roles", "offline_access", "microprofile-jwt", "write", "email" ] + }, { + "id" : "6a4bfbd0-576d-4778-af56-56f876647355", + "clientId" : "realm-management", + "name" : "${client_realm-management}", + "surrogateAuthRequired" : false, + "enabled" : true, + "clientAuthenticatorType" : "client-secret", + "secret" : "**********", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : true, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "8e358d2f-b085-4243-8e6e-c175431e5eeb", + "clientId" : "security-admin-console", + "name" : "${client_security-admin-console}", + "rootUrl" : "${authAdminUrl}", + "baseUrl" : "/admin/mixchange/console/", + "surrogateAuthRequired" : false, + "enabled" : true, + "clientAuthenticatorType" : "client-secret", + "secret" : "**********", + "redirectUris" : [ "/admin/mixchange/console/*" ], + "webOrigins" : [ "+" ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "protocolMappers" : [ { + "id" : "9cfca9ee-493d-4b5e-8170-2d364149de59", + "name" : "locale", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "locale", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "locale", + "jsonType.label" : "String" + } + } ], + "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + } ], + "clientScopes" : [ { + "id" : "77c7e29d-1a22-4419-bbfb-4a62bb033449", + "name" : "address", + "description" : "OpenID Connect built-in scope: address", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${addressScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "94e1879d-b49e-4178-96e0-bf8d7f32c160", + "name" : "address", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-address-mapper", + "consentRequired" : false, + "config" : { + "user.attribute.formatted" : "formatted", + "user.attribute.country" : "country", + "user.attribute.postal_code" : "postal_code", + "userinfo.token.claim" : "true", + "user.attribute.street" : "street", + "id.token.claim" : "true", + "user.attribute.region" : "region", + "access.token.claim" : "true", + "user.attribute.locality" : "locality" + } + } ] + }, { + "id" : "b3526ac1-10e2-4344-8621-9c5a0853e97a", + "name" : "email", + "description" : "OpenID Connect built-in scope: email", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${emailScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "d30270dc-baa6-455a-8ff6-ddccf8a78d86", + "name" : "email verified", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "emailVerified", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "email_verified", + "jsonType.label" : "boolean" + } + }, { + "id" : "f5b1684d-e479-4134-8578-457fa64717da", + "name" : "email", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "email", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "email", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "c658ae14-e96a-4745-b21b-2ed5c4c63f5f", + "name" : "microprofile-jwt", + "description" : "Microprofile - JWT built-in scope", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "959521bc-5ffd-465b-95f2-5b0c20d1909c", + "name" : "upn", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "username", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "upn", + "jsonType.label" : "String" + } + }, { + "id" : "07b8550c-b298-4cce-9ffb-900182575b76", + "name" : "groups", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-realm-role-mapper", + "consentRequired" : false, + "config" : { + "multivalued" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "foo", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "groups", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "569b3d44-4ecd-4768-a58c-70ff38f4b4fe", + "name" : "offline_access", + "description" : "OpenID Connect built-in scope: offline_access", + "protocol" : "openid-connect", + "attributes" : { + "consent.screen.text" : "${offlineAccessScopeConsentText}", + "display.on.consent.screen" : "true" + } + }, { + "id" : "a3e7b19d-df6c-437e-9eea-06fec1becb2f", + "name" : "phone", + "description" : "OpenID Connect built-in scope: phone", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${phoneScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "72a070f7-4363-4c88-8153-6fd2d12b9b04", + "name" : "phone number verified", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "phoneNumberVerified", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "phone_number_verified", + "jsonType.label" : "boolean" + } + }, { + "id" : "24b42c6d-a93c-4aa1-9a03-2a2b55954c13", + "name" : "phone number", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "phoneNumber", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "phone_number", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "ba8c9950-fd0b-4434-8be6-b58456d7b6d4", + "name" : "profile", + "description" : "OpenID Connect built-in scope: profile", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${profileScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "0a9ddd71-309c-40f0-8ea6-a0791070c6ed", + "name" : "profile", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "profile", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "profile", + "jsonType.label" : "String" + } + }, { + "id" : "fbf53bbd-1ad0-4bf8-8030-50f81696d8ee", + "name" : "nickname", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "nickname", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "nickname", + "jsonType.label" : "String" + } + }, { + "id" : "423be2cd-42c0-462e-9030-18f9b28ff2d3", + "name" : "gender", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "gender", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "gender", + "jsonType.label" : "String" + } + }, { + "id" : "53eb9006-4b81-474a-8b60-80f775d54b63", + "name" : "picture", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "picture", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "picture", + "jsonType.label" : "String" + } + }, { + "id" : "4d8bc82a-eaeb-499e-8eb2-0f1dcbe91699", + "name" : "locale", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "locale", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "locale", + "jsonType.label" : "String" + } + }, { + "id" : "d3b25485-4042-419d-afff-cfd63a76e229", + "name" : "full name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-full-name-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "true", + "access.token.claim" : "true", + "userinfo.token.claim" : "true" + } + }, { + "id" : "422cfa5a-f2f4-4f36-82df-91b47ae1ea50", + "name" : "given name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "firstName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "given_name", + "jsonType.label" : "String" + } + }, { + "id" : "3f2863c1-d98d-45b5-b08f-af9c4d9c10f8", + "name" : "website", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "website", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "website", + "jsonType.label" : "String" + } + }, { + "id" : "c98c063d-eee4-41a0-9130-595afd709d1f", + "name" : "middle name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "middleName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "middle_name", + "jsonType.label" : "String" + } + }, { + "id" : "8dbed80a-d672-4185-8dda-4bba2a56ec83", + "name" : "family name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "lastName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "family_name", + "jsonType.label" : "String" + } + }, { + "id" : "5e5c690c-93cf-489d-a054-b109eab8911b", + "name" : "updated at", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "updatedAt", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "updated_at", + "jsonType.label" : "String" + } + }, { + "id" : "3b985202-af8a-42f1-ac5f-0966a404f5d7", + "name" : "birthdate", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "birthdate", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "birthdate", + "jsonType.label" : "String" + } + }, { + "id" : "6eafd1b3-7121-4919-ad1e-039fa58acc32", + "name" : "username", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "username", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "preferred_username", + "jsonType.label" : "String" + } + }, { + "id" : "73cba925-8c31-443f-9601-b1514e6396c1", + "name" : "zoneinfo", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "zoneinfo", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "zoneinfo", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "c1a2eb23-25c6-4be7-a791-bbdca99c83f7", + "name" : "read", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true" + } + }, { + "id" : "18e141bf-dabe-4858-879c-dbc439cdead4", + "name" : "role_list", + "description" : "SAML role list", + "protocol" : "saml", + "attributes" : { + "consent.screen.text" : "${samlRoleListScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "10cbe37f-0198-4d65-bc8a-bfe5ad8145d1", + "name" : "role list", + "protocol" : "saml", + "protocolMapper" : "saml-role-list-mapper", + "consentRequired" : false, + "config" : { + "single" : "false", + "attribute.nameformat" : "Basic", + "attribute.name" : "Role" + } + } ] + }, { + "id" : "111ed87a-5fd3-4cee-96df-8dbfb88cfdc0", + "name" : "roles", + "description" : "OpenID Connect scope for add user roles to the access token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${rolesScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "24924d8d-6071-4a93-b40f-326176cb335e", + "name" : "realm roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-realm-role-mapper", + "consentRequired" : false, + "config" : { + "user.attribute" : "foo", + "access.token.claim" : "true", + "claim.name" : "realm_access.roles", + "jsonType.label" : "String", + "multivalued" : "true" + } + }, { + "id" : "2f6a9bdf-3758-484c-996d-e4f93555559f", + "name" : "audience resolve", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-resolve-mapper", + "consentRequired" : false, + "config" : { } + }, { + "id" : "804d4798-d9a3-4fd3-8b28-d12142e8cb3d", + "name" : "client roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-client-role-mapper", + "consentRequired" : false, + "config" : { + "user.attribute" : "foo", + "access.token.claim" : "true", + "claim.name" : "resource_access.${client_id}.roles", + "jsonType.label" : "String", + "multivalued" : "true" + } + } ] + }, { + "id" : "51d49314-b511-43e0-9258-bfb873758a78", + "name" : "web-origins", + "description" : "OpenID Connect scope for add allowed web origins to the access token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "false", + "consent.screen.text" : "" + }, + "protocolMappers" : [ { + "id" : "2b384cd0-9e85-4a87-8eeb-2b480b0587b7", + "name" : "allowed web origins", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-allowed-origins-mapper", + "consentRequired" : false, + "config" : { } + } ] + }, { + "id" : "c3e253fb-7361-47cf-9d4a-86245686fdf1", + "name" : "write", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true" + } + } ], + "defaultDefaultClientScopes" : [ "roles", "role_list", "web-origins", "email", "profile" ], + "defaultOptionalClientScopes" : [ "offline_access", "address", "phone", "microprofile-jwt" ], + "browserSecurityHeaders" : { + "contentSecurityPolicyReportOnly" : "", + "xContentTypeOptions" : "nosniff", + "xRobotsTag" : "none", + "xFrameOptions" : "SAMEORIGIN", + "contentSecurityPolicy" : "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection" : "1; mode=block", + "strictTransportSecurity" : "max-age=31536000; includeSubDomains" + }, + "smtpServer" : { }, + "eventsEnabled" : false, + "eventsListeners" : [ "jboss-logging" ], + "enabledEventTypes" : [ ], + "adminEventsEnabled" : false, + "adminEventsDetailsEnabled" : false, + "components" : { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" : [ { + "id" : "84305f42-4b6d-4b0a-ac7c-53e406e3ac63", + "name" : "Allowed Client Scopes", + "providerId" : "allowed-client-templates", + "subType" : "authenticated", + "subComponents" : { }, + "config" : { + "allow-default-scopes" : [ "true" ] + } + }, { + "id" : "c7c38a95-744f-4558-a403-9cf692fe1944", + "name" : "Allowed Client Scopes", + "providerId" : "allowed-client-templates", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "allow-default-scopes" : [ "true" ] + } + }, { + "id" : "365b2899-befe-4417-b89b-562650ec4446", + "name" : "Full Scope Disabled", + "providerId" : "scope", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "81c32244-7921-43e9-9356-a3469259b78c", + "name" : "Trusted Hosts", + "providerId" : "trusted-hosts", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "host-sending-registration-request-must-match" : [ "true" ], + "client-uris-must-match" : [ "true" ] + } + }, { + "id" : "d09b2147-afea-4f7f-a49c-0aec7eee10de", + "name" : "Max Clients Limit", + "providerId" : "max-clients", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "max-clients" : [ "200" ] + } + }, { + "id" : "41ffde1b-72a2-416f-87a7-94989e940dc0", + "name" : "Allowed Protocol Mapper Types", + "providerId" : "allowed-protocol-mappers", + "subType" : "authenticated", + "subComponents" : { }, + "config" : { + "allowed-protocol-mapper-types" : [ "saml-user-attribute-mapper", "oidc-usermodel-property-mapper", "saml-role-list-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-user-property-mapper", "oidc-address-mapper", "oidc-usermodel-attribute-mapper", "oidc-full-name-mapper" ] + } + }, { + "id" : "76075388-2782-4656-a986-313493239a9f", + "name" : "Consent Required", + "providerId" : "consent-required", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "3caaf57a-9cd7-48c1-b709-b40b887414f7", + "name" : "Allowed Protocol Mapper Types", + "providerId" : "allowed-protocol-mappers", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "allowed-protocol-mapper-types" : [ "oidc-usermodel-property-mapper", "saml-user-property-mapper", "oidc-address-mapper", "saml-role-list-mapper", "oidc-usermodel-attribute-mapper", "saml-user-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-full-name-mapper" ] + } + } ], + "org.keycloak.keys.KeyProvider" : [ { + "id" : "d67a940a-52e4-44a5-9f69-6ffdd67a188f", + "name" : "rsa-generated", + "providerId" : "rsa-generated", + "subComponents" : { }, + "config" : { + "privateKey" : [ "MIIEpQIBAAKCAQEAsw+lmV2HbpgXllKS+ccyCerlWDir32Y3yFvXF3CbzYRKVg/4FddA8itMCPpPjVOAo26bi/0WImHHYPyGxSH3QQIQaSdS5AFUNarpFc9M/W4Bu7k5h8sKb+vzZA3hMLFyvmQ2JtY8Zuqi1BBB33OAlkOHprJFTrrt1xa4a6KJjKXlDp5o5hOFgvZbsW+k/fBU0CLCbL3PlXQLhqHgEncpL9L9FF/xDbPIGftxjEWcBPFG/SuyygT35xRFMxB7ifR+wJZwVnoC4RhbhgJdL2EiUVT7wq5MTsxr2j4nAAJYatK/7OTecs6y/7bGNAGEoqh4Q3X3D95YFex0scmHgY1hWwIDAQABAoIBAQCAesmnsaRrhlXmVnl/H56X3yOQmZk5Qm7kJIHBH9urKzDrb6niggDnwolcJ77M+Q1kNwkE+SPYp7AXtxo8eJKg4opD6SNg1lNF7swwCf6EGVrFB7Jv7RAgHn0VXaAnACwE0ILKew38Cn+rCb86RfYN24aJ5YA1bYsaugK5uHZZo7kLnv5PUwOwu4PqhAKLPyQt2P1gN3rzf0TGmL5Ylqoxx6X6QBKTwOqAzsWPshMxOB+GzIDDegvzQ6UIV/FzsxIZFLVAPtAWxa77TgT8LeBxDz/UF3H5cWEP5LJ3108ZUOa/EyPiIAaMsLtgVycf7gtsOEsQ4q/OqqJrL4NdNUaBAoGBAPVeguDaUwZIMOYGH2sjYaShaddMFsR2u93dj/GI/T8A4/iXz2yxfHl0LEmxQpZkzyVFpYkYO54w59zLdhzpFOkuGOMT+1j4FnMEjSnzQhZzLVxwuQ9cX0VhYRf+gpwhUOrmbPZB3tYWIiDGm823c6J4QCi0kG5R7pPb/nowpIulAoGBALrRr6RdL/lqrt/REtVW2KSdL6r38sN12vmgjBzIQFLWQFSzDxjaPLeZY87uIhpAWAuZJW+NZ7WfF85WuKDwSbuoZ1ln1yj3FkJkYZmdOid8VpEam2Z9sCncvbAq/pvhgSKMYZeXQ2+dPM0N1HiDb9SEuPIbS3hvcYwNAcLyWqj/AoGBAOfAmMVf8MMiNG1OoyZCiNtCSgG8MFToAJGRz39G8EstwCTw3k2/Zd4hSCNidY4vMSf3HF7csJK9hoIY+jpcPA/yJjd0jBaAXFPOnLZeuLEToGiLX3+Os72IOHi9PwfQv+jeM1R06tAyn5FthYNMHr/57D+GLFTGthyZ0UX/46qxAoGAJ7ENRDqYSsGjzeG5wqHk/XR4ADcV2PldQNQfcK4LHI5wtI4mkv0rEUcBsaFelX0+N5ieH4lHk4rtn+VE7MygncI10wUA7a8xh4GUSvLgvCrqqYGhqrDhkMNZeehol+3dZd21jmOQ7FHX7SkXD1O9msVoFeg+rKPg2ASbbzPWlzkCgYEAgVQKaTCgh3E2z3E9OhzeMIq7qyJuSKsP5FHsTT17U46/rPk+XBhLB/VBkY0Hn29NW4CWTmvEaJrJiejKJaPap8DBjMpJ7KnMidEeHaRykCuPmi9VUT2Q+FE5lQaPi8fYIlHI7BsIY+wTVlKRezkKpuFezocyP5YR34XDkM0BhRI=" ], + "certificate" : [ "MIICnzCCAYcCBgFuhB77/jANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhiYWVsZHVuZzAeFw0xOTExMTkxNDQyMzNaFw0yOTExMTkxNDQ0MTNaMBMxETAPBgNVBAMMCGJhZWxkdW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsw+lmV2HbpgXllKS+ccyCerlWDir32Y3yFvXF3CbzYRKVg/4FddA8itMCPpPjVOAo26bi/0WImHHYPyGxSH3QQIQaSdS5AFUNarpFc9M/W4Bu7k5h8sKb+vzZA3hMLFyvmQ2JtY8Zuqi1BBB33OAlkOHprJFTrrt1xa4a6KJjKXlDp5o5hOFgvZbsW+k/fBU0CLCbL3PlXQLhqHgEncpL9L9FF/xDbPIGftxjEWcBPFG/SuyygT35xRFMxB7ifR+wJZwVnoC4RhbhgJdL2EiUVT7wq5MTsxr2j4nAAJYatK/7OTecs6y/7bGNAGEoqh4Q3X3D95YFex0scmHgY1hWwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCLf01IsqWCKF2vw5y/NPCgKFEP4vtlXvr1XusMr9sAMSgbAVKLm5wZg4Ue8Q7iUq1LJzZ7EOeaXfIHxk33vjf0RlJQAaVn2K/kZJ4rFXuU6g+mpoRgiFsCPNexXXMdG294osyJ60g5OWCt5KrdocFjkKbF9EYm2CBj20G5MxCf+VDGDIcNvgzp478CZODiFVb6JgQqRrE3CGzQKdKiHG9cbXAledMJuWw3h0dzAqM46nl7GRkNK2cmVJO+cDD7xMrD83Q84PoG1ixMsQ/Tv5CqfREC4Dp9/j4XaoEOJ5S7oBaiRqTMzxk/lPjmbUrdyLM0Kokrrwc2yF74m9NuwHh2" ], + "priority" : [ "100" ] + } + }, { + "id" : "48d40de3-6234-42e8-9449-f68f56abb54b", + "name" : "aes-generated", + "providerId" : "aes-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "a1643e07-7de0-4265-a42b-21152b16f2a5" ], + "secret" : [ "KH8ObwyJF_YZ6pJ5v9YPuA" ], + "priority" : [ "100" ] + } + }, { + "id" : "52ea1c5d-2a30-459f-b66a-249f298b32f8", + "name" : "hmac-generated", + "providerId" : "hmac-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "0d90cbd7-7164-42cc-88e2-d215e79ae8ea" ], + "secret" : [ "AFOsLJGAelu021azV_tqcT4qhune3A0xfvGZiqfdTCBcC2UAAErphg1Q3FC4eET83w8uDyi3CMtD1v-6lXqhqw" ], + "priority" : [ "100" ], + "algorithm" : [ "HS256" ] + } + } ] + }, + "internationalizationEnabled" : false, + "supportedLocales" : [ ], + "authenticationFlows" : [ { + "id" : "1dca477c-12fa-47df-a6e2-65fa08ed9565", + "alias" : "Handle Existing Account", + "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-confirm-link", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "idp-email-verification", + "requirement" : "ALTERNATIVE", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "requirement" : "ALTERNATIVE", + "priority" : 30, + "flowAlias" : "Verify Existing Account by Re-authentication", + "userSetupAllowed" : false, + "autheticatorFlow" : true + } ] + }, { + "id" : "a216230b-1eb5-4066-aa48-8556e4a50f72", + "alias" : "Verify Existing Account by Re-authentication", + "description" : "Reauthentication of existing account", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-username-password-form", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "requirement" : "CONDITIONAL", + "priority" : 20, + "flowAlias" : "Verify Existing Account by Re-authentication - auth-otp-form - Conditional", + "userSetupAllowed" : false, + "autheticatorFlow" : true + } ] + }, { + "id" : "c8e26747-1aca-4d11-8ce6-8a99a334aad5", + "alias" : "Verify Existing Account by Re-authentication - auth-otp-form - Conditional", + "description" : "Flow to determine if the auth-otp-form authenticator should be used or not.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "auth-otp-form", + "requirement" : "REQUIRED", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + }, { + "id" : "5823f307-af3c-4b0f-83f6-a7231a57789a", + "alias" : "browser", + "description" : "browser based authentication", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "auth-cookie", + "requirement" : "ALTERNATIVE", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "auth-spnego", + "requirement" : "DISABLED", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "identity-provider-redirector", + "requirement" : "ALTERNATIVE", + "priority" : 25, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "requirement" : "ALTERNATIVE", + "priority" : 30, + "flowAlias" : "forms", + "userSetupAllowed" : false, + "autheticatorFlow" : true + } ] + }, { + "id" : "c8b61763-3346-42a7-b9c1-e8d33d2d883e", + "alias" : "clients", + "description" : "Base authentication for clients", + "providerId" : "client-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "client-secret", + "requirement" : "ALTERNATIVE", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "client-jwt", + "requirement" : "ALTERNATIVE", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "client-secret-jwt", + "requirement" : "ALTERNATIVE", + "priority" : 30, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "client-x509", + "requirement" : "ALTERNATIVE", + "priority" : 40, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + }, { + "id" : "cb9be161-629e-4402-b6eb-d102b5a6f03b", + "alias" : "direct grant", + "description" : "OpenID Connect Resource Owner Grant", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "direct-grant-validate-username", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "direct-grant-validate-password", + "requirement" : "REQUIRED", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "requirement" : "CONDITIONAL", + "priority" : 30, + "flowAlias" : "direct grant - direct-grant-validate-otp - Conditional", + "userSetupAllowed" : false, + "autheticatorFlow" : true + } ] + }, { + "id" : "5dd842c9-1322-450f-aa65-c868cdca213e", + "alias" : "direct grant - direct-grant-validate-otp - Conditional", + "description" : "Flow to determine if the direct-grant-validate-otp authenticator should be used or not.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "direct-grant-validate-otp", + "requirement" : "REQUIRED", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + }, { + "id" : "9a38ae9d-c67c-43aa-a86a-44fce0394aec", + "alias" : "docker auth", + "description" : "Used by Docker clients to authenticate against the IDP", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "docker-http-basic-authenticator", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + }, { + "id" : "77a8fa1c-689e-421c-b6de-d8d78a8a19bb", + "alias" : "first broker login", + "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticatorConfig" : "review profile config", + "authenticator" : "idp-review-profile", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticatorConfig" : "create unique user config", + "authenticator" : "idp-create-user-if-unique", + "requirement" : "ALTERNATIVE", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "requirement" : "ALTERNATIVE", + "priority" : 30, + "flowAlias" : "Handle Existing Account", + "userSetupAllowed" : false, + "autheticatorFlow" : true + } ] + }, { + "id" : "6c3afd8e-1bca-45a8-bb82-f779b2cd62f7", + "alias" : "forms", + "description" : "Username, password, otp and other auth forms.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "auth-username-password-form", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "requirement" : "CONDITIONAL", + "priority" : 20, + "flowAlias" : "forms - auth-otp-form - Conditional", + "userSetupAllowed" : false, + "autheticatorFlow" : true + } ] + }, { + "id" : "8dca43f9-2dda-4ed9-9b07-ac731c610ec3", + "alias" : "forms - auth-otp-form - Conditional", + "description" : "Flow to determine if the auth-otp-form authenticator should be used or not.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "auth-otp-form", + "requirement" : "REQUIRED", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + }, { + "id" : "bd7f98ba-7a67-48ba-af12-e667e13df447", + "alias" : "http challenge", + "description" : "An authentication flow based on challenge-response HTTP Authentication Schemes", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "no-cookie-redirect", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "basic-auth", + "requirement" : "REQUIRED", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "basic-auth-otp", + "requirement" : "DISABLED", + "priority" : 30, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "auth-spnego", + "requirement" : "DISABLED", + "priority" : 40, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + }, { + "id" : "f36eff20-a2a9-4652-bb0c-9b4bd3c100a8", + "alias" : "registration", + "description" : "registration flow", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "registration-page-form", + "requirement" : "REQUIRED", + "priority" : 10, + "flowAlias" : "registration form", + "userSetupAllowed" : false, + "autheticatorFlow" : true + } ] + }, { + "id" : "d1536e05-0a6a-4c07-8d48-6124fd2fe835", + "alias" : "registration form", + "description" : "registration form", + "providerId" : "form-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "registration-user-creation", + "requirement" : "REQUIRED", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "registration-profile-action", + "requirement" : "REQUIRED", + "priority" : 40, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "registration-password-action", + "requirement" : "REQUIRED", + "priority" : 50, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "registration-recaptcha-action", + "requirement" : "DISABLED", + "priority" : 60, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + }, { + "id" : "24504d0d-93e5-4476-b203-ba997abbd689", + "alias" : "reset credentials", + "description" : "Reset credentials for a user if they forgot their password or something", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "reset-credentials-choose-user", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "reset-credential-email", + "requirement" : "REQUIRED", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "reset-password", + "requirement" : "REQUIRED", + "priority" : 30, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "requirement" : "CONDITIONAL", + "priority" : 40, + "flowAlias" : "reset credentials - reset-otp - Conditional", + "userSetupAllowed" : false, + "autheticatorFlow" : true + } ] + }, { + "id" : "91ff889c-e767-4988-beff-2e3437f870aa", + "alias" : "reset credentials - reset-otp - Conditional", + "description" : "Flow to determine if the reset-otp authenticator should be used or not.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "reset-otp", + "requirement" : "REQUIRED", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + }, { + "id" : "2c6aa578-0d3a-4ad4-aa3c-f0ede99afc44", + "alias" : "saml ecp", + "description" : "SAML ECP Profile Authentication Flow", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "http-basic-authenticator", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + } ], + "authenticatorConfig" : [ { + "id" : "f235f13d-e935-45b3-b87b-0d6faf1eebc1", + "alias" : "create unique user config", + "config" : { + "require.password.update.after.registration" : "false" + } + }, { + "id" : "edfa789a-564c-41bd-b145-0c8493a70593", + "alias" : "review profile config", + "config" : { + "update.profile.on.first.login" : "missing" + } + } ], + "requiredActions" : [ { + "alias" : "CONFIGURE_TOTP", + "name" : "Configure OTP", + "providerId" : "CONFIGURE_TOTP", + "enabled" : true, + "defaultAction" : false, + "priority" : 10, + "config" : { } + }, { + "alias" : "terms_and_conditions", + "name" : "Terms and Conditions", + "providerId" : "terms_and_conditions", + "enabled" : false, + "defaultAction" : false, + "priority" : 20, + "config" : { } + }, { + "alias" : "UPDATE_PASSWORD", + "name" : "Update Password", + "providerId" : "UPDATE_PASSWORD", + "enabled" : true, + "defaultAction" : false, + "priority" : 30, + "config" : { } + }, { + "alias" : "UPDATE_PROFILE", + "name" : "Update Profile", + "providerId" : "UPDATE_PROFILE", + "enabled" : true, + "defaultAction" : false, + "priority" : 40, + "config" : { } + }, { + "alias" : "VERIFY_EMAIL", + "name" : "Verify Email", + "providerId" : "VERIFY_EMAIL", + "enabled" : true, + "defaultAction" : false, + "priority" : 50, + "config" : { } + } ], + "browserFlow" : "browser", + "registrationFlow" : "registration", + "directGrantFlow" : "direct grant", + "resetCredentialsFlow" : "reset credentials", + "clientAuthenticationFlow" : "clients", + "dockerAuthenticationFlow" : "docker auth", + "attributes" : { + "webAuthnPolicyAuthenticatorAttachment" : "not specified", + "_browser_header.xRobotsTag" : "none", + "webAuthnPolicyRpEntityName" : "keycloak", + "failureFactor" : "30", + "actionTokenGeneratedByUserLifespan" : "300", + "maxDeltaTimeSeconds" : "43200", + "webAuthnPolicySignatureAlgorithms" : "ES256", + "offlineSessionMaxLifespan" : "5184000", + "_browser_header.contentSecurityPolicyReportOnly" : "", + "bruteForceProtected" : "false", + "_browser_header.contentSecurityPolicy" : "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "_browser_header.xXSSProtection" : "1; mode=block", + "_browser_header.xFrameOptions" : "SAMEORIGIN", + "_browser_header.strictTransportSecurity" : "max-age=31536000; includeSubDomains", + "webAuthnPolicyUserVerificationRequirement" : "not specified", + "permanentLockout" : "false", + "quickLoginCheckMilliSeconds" : "1000", + "webAuthnPolicyCreateTimeout" : "0", + "webAuthnPolicyRequireResidentKey" : "not specified", + "webAuthnPolicyRpId" : "", + "webAuthnPolicyAttestationConveyancePreference" : "not specified", + "maxFailureWaitSeconds" : "900", + "minimumQuickLoginWaitSeconds" : "60", + "webAuthnPolicyAvoidSameAuthenticatorRegister" : "false", + "_browser_header.xContentTypeOptions" : "nosniff", + "actionTokenGeneratedByAdminLifespan" : "43200", + "waitIncrementSeconds" : "60", + "offlineSessionMaxLifespanEnabled" : "false" + }, + "keycloakVersion" : "8.0.0", + "userManagedAccessAllowed" : false +} \ No newline at end of file diff --git a/UserManagement/pom.xml b/UserManagement/pom.xml new file mode 100644 index 000000000..17d86d7e6 --- /dev/null +++ b/UserManagement/pom.xml @@ -0,0 +1,14 @@ + + + 4.0.0 + co.nilin.mixchange + user-management-root + 0.0.1-SNAPSHOT + user-management-root + pom + User Management root of Mixchange + + keycloak-gateway + + diff --git a/Wallet/.gitignore b/Wallet/.gitignore new file mode 100644 index 000000000..785786ed7 --- /dev/null +++ b/Wallet/.gitignore @@ -0,0 +1,47 @@ +# Created by .ignore support plugin (hsz.mobi) +### Kotlin template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### JetBrains template +.idea/** +.idea +.idea/ +*.iml +*.ipr + +# File-based project format +*.iws + +# IntelliJ +out/ + +target/ + + + + +!/.idea/ + +.DS_Store diff --git a/Wallet/pom.xml b/Wallet/pom.xml new file mode 100644 index 000000000..96b92f1c3 --- /dev/null +++ b/Wallet/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + co.nilin.mixchange + wallets + 0.0.1-SNAPSHOT + wallets + pom + Wallet managment root of Mixchange + + + wallet-core + wallet-app + wallet-ports/wallet-persister-postgres + wallet-ports/wallet-eventlistener-kafka + + diff --git a/Wallet/wallet-app/.gitignore b/Wallet/wallet-app/.gitignore new file mode 100644 index 000000000..0d6c2228e --- /dev/null +++ b/Wallet/wallet-app/.gitignore @@ -0,0 +1,35 @@ +HELP.md +target/ +.mvn/ +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +.DS_Store diff --git a/Wallet/wallet-app/Dockerfile b/Wallet/wallet-app/Dockerfile new file mode 100644 index 000000000..f2cbd4c26 --- /dev/null +++ b/Wallet/wallet-app/Dockerfile @@ -0,0 +1,5 @@ +FROM openjdk:8-jdk-alpine +VOLUME /tmp +ARG JAR_FILE=target/*.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"] \ No newline at end of file diff --git a/Wallet/wallet-app/mvnw b/Wallet/wallet-app/mvnw new file mode 100755 index 000000000..a16b5431b --- /dev/null +++ b/Wallet/wallet-app/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/Wallet/wallet-app/mvnw.cmd b/Wallet/wallet-app/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/Wallet/wallet-app/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/Wallet/wallet-app/pom.xml b/Wallet/wallet-app/pom.xml new file mode 100644 index 000000000..433e3d3e8 --- /dev/null +++ b/Wallet/wallet-app/pom.xml @@ -0,0 +1,223 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + wallet-app + 0.0.1-SNAPSHOT + wallet-app + Wallet managment app of Mixchange + + + 1.8 + 1.4.31 + ${version} + 2020.0.2 + + + + + org.springframework.boot + spring-boot-starter-webflux + + + com.fasterxml.jackson.module + jackson-module-kotlin + + + + org.jetbrains.kotlin + kotlin-stdlib + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + io.projectreactor + reactor-test + test + + + co.nilin.mixchange + wallet-core + ${wallet.version} + + + co.nilin.mixchange + wallet-persister-postgres + ${wallet.version} + + + co.nilin.mixchange + wallet-eventlistener-kafka + ${wallet.version} + + + org.springframework.cloud + spring-cloud-starter-consul-all + + + org.springframework.boot + spring-boot-starter-actuator + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18 + + + ${skip.unit.tests} + + + **/*IntegrationTest.java + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-test-source + generate-test-sources + + add-test-source + + + + src/test/java + + + + + compile + + add-source + + + + src/main/java + + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + + -Xjsr305=strict + + + spring + + 1.8 + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + compile + compile + + compile + + + + testCompile + test-compile + + testCompile + + + + + + opex-wallet + + + diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/WalletApp.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/WalletApp.kt new file mode 100644 index 000000000..a02438a63 --- /dev/null +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/WalletApp.kt @@ -0,0 +1,13 @@ +package co.nilin.mixchange.wallet.app + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.runApplication +import org.springframework.context.annotation.ComponentScan + +@SpringBootApplication +@ComponentScan("co.nilin.mixchange") +class WalletApp + +fun main(args: Array) { + runApplication(*args) +} diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/AppConfig.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/AppConfig.kt new file mode 100644 index 000000000..044d24af9 --- /dev/null +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/AppConfig.kt @@ -0,0 +1,55 @@ +package co.nilin.mixchange.wallet.app.config + +import co.nilin.mixchange.auth.gateway.model.UserCreatedEvent +import co.nilin.mixchange.port.wallet.consumer.UserCreatedKafkaListener +import co.nilin.mixchange.port.wallet.spi.UserCreatedEventListener +import co.nilin.mixchange.wallet.app.controller.Symbol +import co.nilin.mixchange.wallet.app.service.UserRegistrationService +import co.nilin.mixchange.wallet.core.model.Amount +import co.nilin.mixchange.wallet.core.spi.WalletManager +import co.nilin.mixchange.wallet.core.spi.WalletOwnerManager +import kotlinx.coroutines.runBlocking +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional +import java.math.BigDecimal + +@Configuration +class AppConfig { + + @Value("\${app.gift.symbol}") + val symbol: String? = null + + @Value("\${app.gift.amount}") + val amount: BigDecimal? = null + + + @Autowired + fun configureUserCreatedEventListener( + useCreatedKafkaListener: UserCreatedKafkaListener, + userCreatedEventListener: UserCreatedEventListener + ) { + useCreatedKafkaListener.addEventListener(userCreatedEventListener) + } + + @Component + class WalletUserCreatedEventListener( + val userRegistrationService: UserRegistrationService + ) : UserCreatedEventListener { + + override fun id(): String { + return "UserCreatedEventListener" + } + + override fun onEvent(event: UserCreatedEvent, partition: Int, offset: Long, timestamp: Long) { + println("UserCreatedEvent " + event) + runBlocking(AppDispatchers.kafkaExecutor) { + userRegistrationService.registerNewUser(event) + } + println("onUserCreatedEvent") + } + } +} \ No newline at end of file diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/AppDispatchers.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/AppDispatchers.kt new file mode 100644 index 000000000..ec376a62d --- /dev/null +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/AppDispatchers.kt @@ -0,0 +1,8 @@ +package co.nilin.mixchange.wallet.app.config + +import kotlinx.coroutines.asCoroutineDispatcher +import java.util.concurrent.Executors + +object AppDispatchers { + val kafkaExecutor = Executors.newSingleThreadExecutor().asCoroutineDispatcher() +} \ No newline at end of file diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/Symbol.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/Symbol.kt new file mode 100644 index 000000000..80d8174f0 --- /dev/null +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/Symbol.kt @@ -0,0 +1,17 @@ +package co.nilin.mixchange.wallet.app.controller + +import co.nilin.mixchange.wallet.core.model.Currency + +class Symbol(val symbol_: String): Currency { + override fun getSymbol(): String { + TODO("Not yet implemented") + } + + override fun getName(): String { + return symbol_ + } + + override fun getPrecision(): Int { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/TransferController.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/TransferController.kt new file mode 100644 index 000000000..5da36373b --- /dev/null +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/TransferController.kt @@ -0,0 +1,47 @@ +package co.nilin.mixchange.wallet.app.controller + +import co.nilin.mixchange.wallet.core.inout.TransferCommand +import co.nilin.mixchange.wallet.core.inout.TransferResult +import co.nilin.mixchange.wallet.core.model.Amount +import co.nilin.mixchange.wallet.core.service.TransferService +import co.nilin.mixchange.wallet.core.spi.WalletManager +import co.nilin.mixchange.wallet.core.spi.WalletOwnerManager +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RestController +import java.lang.IllegalArgumentException +import java.math.BigDecimal + +@RestController +class TransferController( + val transferService: TransferService, val walletManager: WalletManager, val walletOwnerManager: WalletOwnerManager +) { + @PostMapping("/transfer/{amount}_{symbol}/from/{senderUuid}_{senderWalletType}/to/{receiverUuid}_{receiverWalletType}") + suspend fun transfer( + @PathVariable("symbol") symbol: String, + @PathVariable("senderWalletType") senderWalletType: String, + @PathVariable("senderUuid") senderUuid: String, + @PathVariable("receiverWalletType") receiverWalletType: String, + @PathVariable("receiverUuid") receiverUuid: String, + @PathVariable("amount") amount: BigDecimal, + @PathVariable("description") description: String?, + @PathVariable("transferRef") transferRef: String? + ): TransferResult { + val sourceOwner = walletOwnerManager.findWalletOwner(senderUuid) ?: throw IllegalArgumentException() + val sourceWallet = + walletManager.findWalletByOwnerAndCurrencyAndType(sourceOwner, senderWalletType, Symbol(symbol)) + ?: throw IllegalArgumentException() + val receiverOwner = walletOwnerManager.findWalletOwner(receiverUuid) ?: walletOwnerManager.createWalletOwner(senderUuid, "noset", "") + val receiverWallet = walletManager.findWalletByOwnerAndCurrencyAndType( + receiverOwner, receiverWalletType, Symbol(symbol) + ) ?: walletManager.createWallet(receiverOwner, Amount(Symbol(symbol), BigDecimal.ZERO), Symbol(symbol), receiverWalletType) + return transferService.transfer( + TransferCommand( + sourceWallet, + receiverWallet, + Amount(sourceWallet.currency(), amount), + description, transferRef + ) + ) + } +} \ No newline at end of file diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/WalletController.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/WalletController.kt new file mode 100644 index 000000000..3c0843959 --- /dev/null +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/WalletController.kt @@ -0,0 +1,40 @@ +package co.nilin.mixchange.wallet.app.controller + +import co.nilin.mixchange.wallet.core.model.Amount +import co.nilin.mixchange.wallet.core.model.Currency +import co.nilin.mixchange.wallet.core.spi.WalletManager +import co.nilin.mixchange.wallet.core.spi.WalletOwnerManager +import org.slf4j.LoggerFactory +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RestController +import java.lang.RuntimeException +import java.math.BigDecimal + +@RestController +class WalletController( + val walletManager: WalletManager, val walletOwnerManager: WalletOwnerManager +) { + val logger = LoggerFactory.getLogger(WalletController::class.java) + + data class BooleanResponse(val result: Boolean) + @GetMapping("{uuid}/wallet_type/{wallet_type}/can_withdraw/{amount}_{currency}") + suspend fun canFulfill( + @PathVariable("uuid") uuid: String, + @PathVariable("currency") currency: String, + @PathVariable("wallet_type") walletType: String, + @PathVariable("amount") amount: BigDecimal + ): BooleanResponse { + logger.info("canFullFill: {} {} {} {}", uuid, currency, walletType, amount) + val owner = walletOwnerManager.findWalletOwner(uuid) + if (owner != null) { + val wallet = walletManager.findWalletByOwnerAndCurrencyAndType(owner, walletType, Symbol(currency)) + if (wallet != null) { + return BooleanResponse( + walletManager.isWithdrawAllowed(wallet, amount) + && walletOwnerManager.isWithdrawAllowed(owner, Amount(wallet.currency(), amount)) ) + } + } + return BooleanResponse(false) + } +} \ No newline at end of file diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/listener/WalletListenerImpl.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/listener/WalletListenerImpl.kt new file mode 100644 index 000000000..0f06e3d4d --- /dev/null +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/listener/WalletListenerImpl.kt @@ -0,0 +1,24 @@ +package co.nilin.mixchange.wallet.app.listener + +import co.nilin.mixchange.wallet.core.model.Amount +import co.nilin.mixchange.wallet.core.model.Wallet +import co.nilin.mixchange.wallet.core.spi.WalletListener +import org.springframework.stereotype.Component +import java.math.BigDecimal + +@Component +class WalletListenerImpl: WalletListener { + override fun onDeposit( + me: Wallet, + sourceWallet: Wallet, + amount: Amount, + finalAmount: BigDecimal, + transaction: String + ) { + + } + + override fun onWithdraw(me: Wallet, destWallet: Wallet, amount: Amount, transaction: String) { + + } +} \ No newline at end of file diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/service/UserRegistrationService.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/service/UserRegistrationService.kt new file mode 100644 index 000000000..c9dac724e --- /dev/null +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/service/UserRegistrationService.kt @@ -0,0 +1,34 @@ +package co.nilin.mixchange.wallet.app.service + +import co.nilin.mixchange.auth.gateway.model.UserCreatedEvent +import co.nilin.mixchange.wallet.app.controller.Symbol +import co.nilin.mixchange.wallet.core.model.Amount +import co.nilin.mixchange.wallet.core.model.Wallet +import co.nilin.mixchange.wallet.core.spi.WalletManager +import co.nilin.mixchange.wallet.core.spi.WalletOwnerManager +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional +import java.math.BigDecimal + +@Component +class UserRegistrationService( + val walletOwnerManager: WalletOwnerManager, + val walletManager: WalletManager, + @Value("\${app.gift.symbol}") + val symbol: Symbol, + @Value("\${app.gift.amount}") + val amount: BigDecimal +) { + @Transactional + suspend fun registerNewUser(event: UserCreatedEvent): Wallet { + val owner = + walletOwnerManager.createWalletOwner(event.uuid, "${event.firstName} ${event.lastName}", "1") + return walletManager.createWallet( + owner, + Amount(symbol, amount), + symbol, + "main" + ) + } +} \ No newline at end of file diff --git a/Wallet/wallet-app/src/main/resources/application-docker.yml b/Wallet/wallet-app/src/main/resources/application-docker.yml new file mode 100644 index 000000000..2bbefb080 --- /dev/null +++ b/Wallet/wallet-app/src/main/resources/application-docker.yml @@ -0,0 +1,30 @@ +server.port: 8091 +spring: + application: + name: opex-wallet + main: + allow-bean-definition-overriding: false + kafka: + bootstrap-servers: ${KAFKA_IP_PORT} + consumer: + group-id: wallet + redis: + hostname: ${REDIS_HOST} + port: 6379 + r2dbc: + url: r2dbc:postgresql://${DB_IP_PORT}/opex_wallet + username: opex + password: hiopex + initialization-mode: always + cloud: + bootstrap: + enabled: true + consul: + host: ${CONSUL_HOST} + port: 8500 + discovery: + #healthCheckPath: ${management.context-path}/health + instance-id: ${spring.application.name}:${server.port} + healthCheckInterval: 20s + prefer-ip-address: true + diff --git a/Wallet/wallet-app/src/main/resources/application.yml b/Wallet/wallet-app/src/main/resources/application.yml new file mode 100644 index 000000000..21e431458 --- /dev/null +++ b/Wallet/wallet-app/src/main/resources/application.yml @@ -0,0 +1,32 @@ +server.port: 8091 +spring: + application: + name: opex-wallet + main: + allow-bean-definition-overriding: false + kafka: + bootstrap-servers: localhost:2181 + consumer: + group-id: wallet + redis: + hostname: 127.0.0.1 + port: 6379 + r2dbc: + url: r2dbc:postgresql://localhost/opex_wallet + username: opex + password: hiopex + initialization-mode: always + cloud: + bootstrap: + enabled: true + consul: + port: 8500 + discovery: + #healthCheckPath: ${management.context-path}/health + instance-id: ${spring.application.name}:${server.port} + healthCheckInterval: 20s + prefer-ip-address: true +app: + gift: + symbol: usdt + amount: 1000 diff --git a/Wallet/wallet-core/.gitignore b/Wallet/wallet-core/.gitignore new file mode 100644 index 000000000..8e22e6c21 --- /dev/null +++ b/Wallet/wallet-core/.gitignore @@ -0,0 +1,35 @@ +HELP.md +target/ +.mvn +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +.DS_Store diff --git a/Wallet/wallet-core/mvnw b/Wallet/wallet-core/mvnw new file mode 100755 index 000000000..a16b5431b --- /dev/null +++ b/Wallet/wallet-core/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/Wallet/wallet-core/mvnw.cmd b/Wallet/wallet-core/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/Wallet/wallet-core/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/Wallet/wallet-core/pom.xml b/Wallet/wallet-core/pom.xml new file mode 100644 index 000000000..91aa36820 --- /dev/null +++ b/Wallet/wallet-core/pom.xml @@ -0,0 +1,75 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + wallet-core + 0.0.1-SNAPSHOT + wallet-core + Wallet managment of Mixchange + + + 1.8 + 1.4.31 + + + + + org.springframework.boot + spring-boot-starter + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/CurrencyNotMatchedException.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/CurrencyNotMatchedException.kt new file mode 100644 index 000000000..61bb70455 --- /dev/null +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/CurrencyNotMatchedException.kt @@ -0,0 +1,3 @@ +package co.nilin.mixchange.wallet.core.exc + +class CurrencyNotMatchedException: Exception() \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/DepositLimitExceededException.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/DepositLimitExceededException.kt new file mode 100644 index 000000000..54c6233cd --- /dev/null +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/DepositLimitExceededException.kt @@ -0,0 +1,3 @@ +package co.nilin.mixchange.wallet.core.exc + +class DepositLimitExceededException: Exception() \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/NotEnoughBalanceException.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/NotEnoughBalanceException.kt new file mode 100644 index 000000000..0cb069595 --- /dev/null +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/NotEnoughBalanceException.kt @@ -0,0 +1,3 @@ +package co.nilin.mixchange.wallet.core.exc + +class NotEnoughBalanceException: Exception() \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/WithdrawLimitExceededException.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/WithdrawLimitExceededException.kt new file mode 100644 index 000000000..da9425748 --- /dev/null +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/WithdrawLimitExceededException.kt @@ -0,0 +1,3 @@ +package co.nilin.mixchange.wallet.core.exc + +class WithdrawLimitExceededException: Exception() \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/inout/TransferCommand.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/inout/TransferCommand.kt new file mode 100644 index 000000000..1dd5fece5 --- /dev/null +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/inout/TransferCommand.kt @@ -0,0 +1,6 @@ +package co.nilin.mixchange.wallet.core.inout + +import co.nilin.mixchange.wallet.core.model.Amount +import co.nilin.mixchange.wallet.core.model.Wallet + +data class TransferCommand(val sourceWallet: Wallet, val destWallet: Wallet, val amount: Amount, val description: String?, val transferRef: String?) diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/inout/TransferResult.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/inout/TransferResult.kt new file mode 100644 index 000000000..c1358da19 --- /dev/null +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/inout/TransferResult.kt @@ -0,0 +1,6 @@ +package co.nilin.mixchange.wallet.core.inout + +import co.nilin.mixchange.wallet.core.model.Amount +import java.time.LocalDateTime + +data class TransferResult(val date: LocalDateTime, val sourceBalanceBeforeAction: Amount, val sourceBalanceAfterAction: Amount, val amount: Amount) \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Amount.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Amount.kt new file mode 100644 index 000000000..7cc47c151 --- /dev/null +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Amount.kt @@ -0,0 +1,5 @@ +package co.nilin.mixchange.wallet.core.model + +import java.math.BigDecimal + +data class Amount(val currency: Currency, val amount: BigDecimal) \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Currency.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Currency.kt new file mode 100644 index 000000000..7ecc994b7 --- /dev/null +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Currency.kt @@ -0,0 +1,7 @@ +package co.nilin.mixchange.wallet.core.model + +interface Currency { + fun getSymbol(): String + fun getName(): String + fun getPrecision(): Int +} \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Transaction.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Transaction.kt new file mode 100644 index 000000000..9becfa021 --- /dev/null +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Transaction.kt @@ -0,0 +1,5 @@ +package co.nilin.mixchange.wallet.core.model + +import java.math.BigDecimal + +data class Transaction(val sourceWallet: Wallet, val destWallet: Wallet, val sourceAmount: BigDecimal, val destAmount: BigDecimal, val description: String? , val transferRef: String?) \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Wallet.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Wallet.kt new file mode 100644 index 000000000..5340af5d3 --- /dev/null +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Wallet.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.wallet.core.model + +interface Wallet { + fun id(): Long? + fun owner(): WalletOwner + fun balance(): Amount + fun currency(): Currency + fun type(): String + } \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/WalletOwner.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/WalletOwner.kt new file mode 100644 index 000000000..6196a1053 --- /dev/null +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/WalletOwner.kt @@ -0,0 +1,8 @@ +package co.nilin.mixchange.wallet.core.model + +interface WalletOwner { + fun id(): Long? + fun uuid(): String + fun title(): String + fun level(): String +} \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/service/TransferService.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/service/TransferService.kt new file mode 100644 index 000000000..0ddc3d107 --- /dev/null +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/service/TransferService.kt @@ -0,0 +1,74 @@ +package co.nilin.mixchange.wallet.core.service + +import co.nilin.mixchange.wallet.core.exc.CurrencyNotMatchedException +import co.nilin.mixchange.wallet.core.exc.DepositLimitExceededException +import co.nilin.mixchange.wallet.core.exc.NotEnoughBalanceException +import co.nilin.mixchange.wallet.core.exc.WithdrawLimitExceededException +import co.nilin.mixchange.wallet.core.inout.TransferCommand +import co.nilin.mixchange.wallet.core.inout.TransferResult +import co.nilin.mixchange.wallet.core.model.Amount +import co.nilin.mixchange.wallet.core.model.Transaction +import co.nilin.mixchange.wallet.core.spi.CurrencyRateService +import co.nilin.mixchange.wallet.core.spi.TransactionManager +import co.nilin.mixchange.wallet.core.spi.WalletListener +import co.nilin.mixchange.wallet.core.spi.WalletManager +import co.nilin.mixchange.wallet.core.spi.WalletOwnerManager +import org.springframework.stereotype.Service +import java.time.LocalDateTime + +@Service +class TransferService( + val currencyRateService: CurrencyRateService, + val walletManager: WalletManager, + val walletListener: WalletListener, + val walletOwnerManager: WalletOwnerManager, + val transactionManager: TransactionManager +) { + + suspend fun transfer(transferCommand: TransferCommand): TransferResult { + //pre transfer hook (dispatch pre transfer event) + val srcWallet = transferCommand.sourceWallet + val srcWalletOwner = srcWallet.owner() + val srcWalletBalance = srcWallet.balance() + if (srcWallet.currency() != transferCommand.amount.currency) + throw CurrencyNotMatchedException() + if (srcWalletBalance.amount < transferCommand.amount.amount) + throw NotEnoughBalanceException() + if (!walletOwnerManager.isWithdrawAllowed(srcWalletOwner, transferCommand.amount)) + throw WithdrawLimitExceededException() + if (!walletManager.isWithdrawAllowed(srcWallet, transferCommand.amount.amount)) + throw WithdrawLimitExceededException() + + val destWallet = transferCommand.destWallet + val destWalletOwner = destWallet.owner() + val balance = destWallet.balance() + //check wallet if can accept the value type + val amountToTransfer = currencyRateService.convert(transferCommand.amount, destWallet.currency()) + + if (!walletOwnerManager.isDepositAllowed(destWalletOwner, Amount(destWallet.currency(), amountToTransfer))) + throw DepositLimitExceededException() + if (!walletManager.isDepositAllowed(destWallet, amountToTransfer)) + throw DepositLimitExceededException() + + walletManager.decreaseBalance(srcWallet, transferCommand.amount.amount) + walletManager.increaseBalance(destWallet, amountToTransfer) + val tx = transactionManager.save( + Transaction( + srcWallet, + destWallet, + transferCommand.amount.amount, + amountToTransfer, + transferCommand.description, + transferCommand.transferRef + ) + ) + //get the result and add to return result type + walletListener.onDeposit(destWallet, srcWallet, transferCommand.amount, amountToTransfer, tx) + walletListener.onWithdraw(srcWallet, destWallet, transferCommand.amount, tx) + + //post transfer hook(dispatch post transfer event) + + //notify balance change + return TransferResult(LocalDateTime.now(), srcWalletBalance, srcWallet.balance(), balance) + } +} \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/CurrencyRateService.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/CurrencyRateService.kt new file mode 100644 index 000000000..cd1928a49 --- /dev/null +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/CurrencyRateService.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.wallet.core.spi + +import co.nilin.mixchange.wallet.core.model.Currency +import co.nilin.mixchange.wallet.core.model.Amount +import java.math.BigDecimal + +interface CurrencyRateService { + suspend fun convert(amount: Amount, targetCurrency: Currency): BigDecimal +} diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/TransactionManager.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/TransactionManager.kt new file mode 100644 index 000000000..66c88c240 --- /dev/null +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/TransactionManager.kt @@ -0,0 +1,7 @@ +package co.nilin.mixchange.wallet.core.spi + +import co.nilin.mixchange.wallet.core.model.Transaction + +interface TransactionManager { + suspend fun save(transaction: Transaction): String +} \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletListener.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletListener.kt new file mode 100644 index 000000000..0635765d6 --- /dev/null +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletListener.kt @@ -0,0 +1,10 @@ +package co.nilin.mixchange.wallet.core.spi + +import co.nilin.mixchange.wallet.core.model.Amount +import co.nilin.mixchange.wallet.core.model.Wallet +import java.math.BigDecimal + +interface WalletListener { + fun onDeposit(me: Wallet, sourceWallet: Wallet, amount: Amount, finalAmount: BigDecimal, transaction: String) + fun onWithdraw(me: Wallet, destWallet: Wallet, amount: Amount, transaction: String) +} \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletManager.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletManager.kt new file mode 100644 index 000000000..e32df70ff --- /dev/null +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletManager.kt @@ -0,0 +1,21 @@ +package co.nilin.mixchange.wallet.core.spi + +import co.nilin.mixchange.wallet.core.model.Amount +import co.nilin.mixchange.wallet.core.model.Currency +import co.nilin.mixchange.wallet.core.model.Wallet +import co.nilin.mixchange.wallet.core.model.WalletOwner +import java.math.BigDecimal + +interface WalletManager { + suspend fun isDepositAllowed(wallet: Wallet, amount: BigDecimal): Boolean + suspend fun isWithdrawAllowed(wallet: Wallet, amount: BigDecimal): Boolean + suspend fun increaseBalance(wallet: Wallet, amount: BigDecimal) + suspend fun decreaseBalance(wallet: Wallet, amount: BigDecimal) + suspend fun findWalletByOwnerAndCurrencyAndType(owner: WalletOwner, walletType: String, currency: Currency): Wallet? + suspend fun createWallet( + owner: WalletOwner, + balance: Amount, + currency: Currency, + type: String + ): Wallet +} \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletOwnerManager.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletOwnerManager.kt new file mode 100644 index 000000000..14201c4cf --- /dev/null +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletOwnerManager.kt @@ -0,0 +1,11 @@ +package co.nilin.mixchange.wallet.core.spi + +import co.nilin.mixchange.wallet.core.model.Amount +import co.nilin.mixchange.wallet.core.model.WalletOwner + +interface WalletOwnerManager { + suspend fun isDepositAllowed(owner: WalletOwner, amount: Amount): Boolean + suspend fun isWithdrawAllowed(owner: WalletOwner, amount: Amount): Boolean + suspend fun findWalletOwner(uuid: String): WalletOwner? + suspend fun createWalletOwner(uuid: String, title: String, userLevel: String): WalletOwner +} diff --git a/Wallet/wallet-core/src/main/resources/application.properties b/Wallet/wallet-core/src/main/resources/application.properties new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/Wallet/wallet-core/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/Wallet/wallet-ports/wallet-eventlistener-kafka/.gitignore b/Wallet/wallet-ports/wallet-eventlistener-kafka/.gitignore new file mode 100644 index 000000000..f4e066ca5 --- /dev/null +++ b/Wallet/wallet-ports/wallet-eventlistener-kafka/.gitignore @@ -0,0 +1,36 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ +!/.mvn/ + +.DS_Store diff --git a/Wallet/wallet-ports/wallet-eventlistener-kafka/mvnw b/Wallet/wallet-ports/wallet-eventlistener-kafka/mvnw new file mode 100755 index 000000000..a16b5431b --- /dev/null +++ b/Wallet/wallet-ports/wallet-eventlistener-kafka/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/Wallet/wallet-ports/wallet-eventlistener-kafka/mvnw.cmd b/Wallet/wallet-ports/wallet-eventlistener-kafka/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/Wallet/wallet-ports/wallet-eventlistener-kafka/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/Wallet/wallet-ports/wallet-eventlistener-kafka/pom.xml b/Wallet/wallet-ports/wallet-eventlistener-kafka/pom.xml new file mode 100644 index 000000000..9defe940e --- /dev/null +++ b/Wallet/wallet-ports/wallet-eventlistener-kafka/pom.xml @@ -0,0 +1,102 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + wallet-eventlistener-kafka + 0.0.1-SNAPSHOT + wallet-eventlistener-kafka + Wallet kafka listener of Mixchange + + + 1.8 + 1.4.31 + ${version} + ${version} + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-webflux + + + + org.springframework.kafka + spring-kafka + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + org.springframework.kafka + spring-kafka-test + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + diff --git a/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/AuthEvent.kt b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/AuthEvent.kt new file mode 100644 index 000000000..8f774787a --- /dev/null +++ b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/AuthEvent.kt @@ -0,0 +1,7 @@ +package co.nilin.mixchange.auth.gateway.model + +import java.time.LocalDateTime + +open class AuthEvent { + var eventDate: LocalDateTime = LocalDateTime.now() +} \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/UserCreatedEvent.kt b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/UserCreatedEvent.kt new file mode 100644 index 000000000..ffabaabf2 --- /dev/null +++ b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/UserCreatedEvent.kt @@ -0,0 +1,18 @@ +package co.nilin.mixchange.auth.gateway.model + +class UserCreatedEvent: AuthEvent { + lateinit var uuid: String + lateinit var firstName: String + lateinit var lastName: String + lateinit var email: String + + + constructor(uuid: String, firstName: String, lastName: String, email: String) : super() { + this.uuid = uuid + this.firstName = firstName + this.lastName = lastName + this.email = email + } + + constructor() : super() +} \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/config/WalletKafkaConfig.kt b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/config/WalletKafkaConfig.kt new file mode 100644 index 000000000..6a6b9b625 --- /dev/null +++ b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/config/WalletKafkaConfig.kt @@ -0,0 +1,89 @@ +package co.nilin.mixchange.port.wallet.kafka.config + + +import co.nilin.mixchange.auth.gateway.model.UserCreatedEvent +import co.nilin.mixchange.port.wallet.consumer.UserCreatedKafkaListener +import org.apache.kafka.clients.admin.NewTopic +import org.apache.kafka.clients.consumer.ConsumerConfig +import org.apache.kafka.clients.producer.ProducerConfig +import org.apache.kafka.common.serialization.StringDeserializer +import org.apache.kafka.common.serialization.StringSerializer +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.support.GenericApplicationContext +import org.springframework.kafka.core.* +import org.springframework.kafka.listener.ConcurrentMessageListenerContainer +import org.springframework.kafka.listener.ContainerProperties +import org.springframework.kafka.support.serializer.JsonDeserializer +import org.springframework.kafka.support.serializer.JsonSerializer +import java.util.* +import java.util.regex.Pattern + + +@Configuration +class WalletKafkaConfig { + @Value("\${spring.kafka.bootstrap-servers}") + private val bootstrapServers: String? = null + + @Value("\${spring.kafka.consumer.group-id}") + private val groupId: String? = null + + @Bean("walletConsumerConfig") + fun consumerConfigs(): Map? { + val props: MutableMap = HashMap() + props[ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG] = bootstrapServers + props[ConsumerConfig.GROUP_ID_CONFIG] = groupId + props[ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG] = StringDeserializer::class.java + props[ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG] = JsonDeserializer::class.java + props[JsonDeserializer.TRUSTED_PACKAGES] = "co.nilin.mixchange.*" + return props + } + + @Bean("walletConsumerFactory") + fun consumerFactory(@Qualifier("walletConsumerConfig")consumerConfigs: Map): ConsumerFactory { + return DefaultKafkaConsumerFactory(consumerConfigs) + } + + @Bean("walletProducerConfig") + fun producerConfigs(): Map { + val props: MutableMap = HashMap() + props[ProducerConfig.BOOTSTRAP_SERVERS_CONFIG] = bootstrapServers + props[ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG] = StringSerializer::class.java + props[ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG] = JsonSerializer::class.java + return props + } + + @Bean("walletProducerFactory") + fun producerFactory(@Qualifier("walletProducerConfig") producerConfigs: Map): ProducerFactory { + return DefaultKafkaProducerFactory(producerConfigs) + } + + @Bean("walletKafkaTemplate") + fun kafkaTemplate(@Qualifier("walletProducerFactory") producerFactory: ProducerFactory): KafkaTemplate { + return KafkaTemplate(producerFactory) + } + + + @Autowired + @ConditionalOnBean(UserCreatedKafkaListener::class) + fun configureUserCreatedListener(listener: UserCreatedKafkaListener + , @Qualifier("walletConsumerFactory") consumerFactory: ConsumerFactory) { + val containerProps = ContainerProperties(Pattern.compile("auth_user_created")) + containerProps.messageListener = listener + val container = ConcurrentMessageListenerContainer(consumerFactory, containerProps) + container.setBeanName("UserCreatedKafkaListenerContainer") + container.start() + } + + + + @Autowired + fun createUserCreatedTopics(applicationContext: GenericApplicationContext){ + applicationContext.registerBean("topic_auth_user_created", NewTopic::class.java, "auth_user_created", 1 ,1) + } + +} \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/consumer/UserCreatedKafkaListener.kt b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/consumer/UserCreatedKafkaListener.kt new file mode 100644 index 000000000..edfcebff8 --- /dev/null +++ b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/consumer/UserCreatedKafkaListener.kt @@ -0,0 +1,28 @@ +package co.nilin.mixchange.port.wallet.consumer + + +import co.nilin.mixchange.auth.gateway.model.UserCreatedEvent +import co.nilin.mixchange.port.wallet.spi.UserCreatedEventListener +import org.apache.kafka.clients.consumer.ConsumerRecord +import org.springframework.kafka.listener.MessageListener +import org.springframework.stereotype.Component + +@Component +class UserCreatedKafkaListener: MessageListener { + val eventListeners = arrayListOf() + override fun onMessage(data: ConsumerRecord) { + eventListeners.forEach{ + tl -> tl.onEvent(data.value(), data.partition(), data.offset(), data.timestamp()) + } + } + + fun addEventListener(tl: UserCreatedEventListener){ + eventListeners.add(tl) + } + + fun removeEventListener(tl: UserCreatedEventListener){ + eventListeners.removeIf { + item -> item.id() == tl.id() + } + } +} \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/spi/UserCreatedEventListener.kt b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/spi/UserCreatedEventListener.kt new file mode 100644 index 000000000..ca62aa313 --- /dev/null +++ b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/spi/UserCreatedEventListener.kt @@ -0,0 +1,8 @@ +package co.nilin.mixchange.port.wallet.spi + +import co.nilin.mixchange.auth.gateway.model.UserCreatedEvent + +interface UserCreatedEventListener { + fun id(): String + fun onEvent(event: UserCreatedEvent, partition: Int, offset: Long, timestamp: Long) +} \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/.gitignore b/Wallet/wallet-ports/wallet-persister-postgres/.gitignore new file mode 100644 index 000000000..f4e066ca5 --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/.gitignore @@ -0,0 +1,36 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ +!/.mvn/ + +.DS_Store diff --git a/Wallet/wallet-ports/wallet-persister-postgres/mvnw b/Wallet/wallet-ports/wallet-persister-postgres/mvnw new file mode 100755 index 000000000..a16b5431b --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/Wallet/wallet-ports/wallet-persister-postgres/mvnw.cmd b/Wallet/wallet-ports/wallet-persister-postgres/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/Wallet/wallet-ports/wallet-persister-postgres/pom.xml b/Wallet/wallet-ports/wallet-persister-postgres/pom.xml new file mode 100644 index 000000000..035c3c114 --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/pom.xml @@ -0,0 +1,105 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + wallet-persister-postgres + 0.0.1-SNAPSHOT + wallet-persister-postgres + Persist items of Mixchange wallet on Postgres + + + 1.8 + 1.4.31 + ${version} + + + + + co.nilin.mixchange + wallet-core + ${wallet.version} + provided + + + org.springframework.boot + spring-boot-starter-data-r2dbc + + + io.r2dbc + r2dbc-postgresql + runtime + + + org.postgresql + postgresql + runtime + + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + io.projectreactor + reactor-test + test + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/config/PostgresConfig.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/config/PostgresConfig.kt new file mode 100644 index 000000000..7b8b7179d --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/config/PostgresConfig.kt @@ -0,0 +1,109 @@ +package co.nilin.mixchange.port.order.kafka.config + + +import org.springframework.context.annotation.Configuration +import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories +import org.springframework.r2dbc.core.DatabaseClient + + +@Configuration +@EnableR2dbcRepositories(basePackages = ["co.nilin.mixchange"]) +class PostgresConfig(db: DatabaseClient) { + + init { + val initDb = db.sql { + """ + drop table IF EXISTS wallet_owner; + drop table IF EXISTS wallet; + drop table IF EXISTS transaction; + CREATE TABLE IF NOT EXISTS currency ( + name VARCHAR(25) PRIMARY KEY, + symbol VARCHAR(25) NOT NULL UNIQUE, + precision numeric NOT NULL + ); + + CREATE TABLE IF NOT EXISTS currency_rate ( + id SERIAL PRIMARY KEY, + source_currency VARCHAR(25) NOT NULL, + dest_currency VARCHAR(25) NOT NULL, + rate decimal + ); + + CREATE TABLE IF NOT EXISTS transaction ( + id SERIAL PRIMARY KEY, + source_wallet numeric NOT NULL, + dest_wallet numeric NOT NULL, + source_amount decimal NOT NULL, + dest_amount decimal NOT NULL, + description VARCHAR(100), + transfer_ref VARCHAR(25), + transaction_date DATE NOT NULL DEFAULT CURRENT_DATE + ); + + CREATE TABLE IF NOT EXISTS wallet_owner ( + id SERIAL PRIMARY KEY, + uuid VARCHAR(36) NOT NULL UNIQUE, + title VARCHAR(70) NOT NULL, + level VARCHAR(10) NOT NULL + ); + + CREATE TABLE IF NOT EXISTS wallet ( + id SERIAL PRIMARY KEY, + owner numeric NOT NULL, + wallet_type VARCHAR(10), + currency VARCHAR(25) NOT NULL, + balance decimal + ); + + CREATE TABLE IF NOT EXISTS user_limits ( + id SERIAL PRIMARY KEY, + level VARCHAR(10), + owner numeric, + action VARCHAR(25) NOT NULL, + wallet_type VARCHAR(10) NOT NULL, + daily_total decimal, + daily_count numeric, + monthly_total decimal, + monthly_count numeric + ); + + CREATE TABLE IF NOT EXISTS wallet_limits ( + id SERIAL PRIMARY KEY, + level VARCHAR(10), + owner numeric, + action VARCHAR(25) NOT NULL, + currency VARCHAR(25) NOT NULL, + wallet_type VARCHAR(10) NOT NULL, + wallet_id numeric, + daily_total decimal, + daily_count numeric, + monthly_total decimal, + monthly_count numeric + ); + + CREATE TABLE IF NOT EXISTS wallet_config ( + name VARCHAR(20) PRIMARY KEY, + main_currency VARCHAR(25) NOT NULL + ); + + insert into wallet_owner(id, uuid, title, level) values(1, '1', 'system', 'basic') ON CONFLICT DO NOTHING; + insert into currency(name, symbol, precision) values('btc', 'btc', 0.000001) ON CONFLICT DO NOTHING;; + insert into currency(name, symbol, precision) values('usdt', 'usdt', 0.01) ON CONFLICT DO NOTHING;; + insert into currency(name, symbol, precision) values('nln', 'nln', 1) ON CONFLICT DO NOTHING;; + insert into currency_rate(id, source_currency, dest_currency, rate) values(1, 'btc', 'nln', 5500000) ON CONFLICT DO NOTHING; + insert into currency_rate(id, source_currency, dest_currency, rate) values(1, 'usdt', 'nln', 100) ON CONFLICT DO NOTHING; + insert into currency_rate(id, source_currency, dest_currency, rate) values(1, 'btc', 'usdt', 55000) ON CONFLICT DO NOTHING; + insert into wallet(id, owner, wallet_type, currency, balance) values(1, 1, 'main', 'btc', 10) ON CONFLICT DO NOTHING; + insert into wallet(id, owner, wallet_type, currency, balance) values(2, 1, 'exchange', 'btc', 0) ON CONFLICT DO NOTHING; + insert into wallet(id, owner, wallet_type, currency, balance) values(3, 1, 'main', 'usdt', 550000) ON CONFLICT DO NOTHING; + insert into wallet(id, owner, wallet_type, currency, balance) values(4, 1, 'exchange', 'usdt', 0) ON CONFLICT DO NOTHING; + insert into wallet(id, owner, wallet_type, currency, balance) values(5, 1, 'main', 'nln', 100000000) ON CONFLICT DO NOTHING; + insert into wallet(id, owner, wallet_type, currency, balance) values(6, 1, 'exchange', 'nln', 0) ON CONFLICT DO NOTHING; + insert into user_limits (id, level, owner, action, wallet_type, daily_total, daily_count, monthly_total, monthly_count)values(1, null, 1, 'withdraw', 'main', 1000, 100, 10000, 1000) ON CONFLICT DO NOTHING; + """ + } + initDb // initialize the database + .then() + .subscribe() // execute + } +} diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/CurrencyRateRepository.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/CurrencyRateRepository.kt new file mode 100644 index 000000000..69dd7e96d --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/CurrencyRateRepository.kt @@ -0,0 +1,18 @@ +package co.nilin.mixchange.port.wallet.postgres.dao + +import co.nilin.mixchange.port.wallet.postgres.model.CurrencyModel +import co.nilin.mixchange.port.wallet.postgres.model.CurrencyRateModel +import kotlinx.coroutines.flow.Flow +import org.springframework.data.domain.Pageable +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono + +@Repository +interface CurrencyRateRepository: ReactiveCrudRepository { + @Query("select * from currency_rate where source_currency = :sourceCurrency and dest_currency = :destCurrency") + fun findBySourceAndDest(@Param("source") sourceCurrency: String + , @Param("dest") destCurrency: String): Mono +} \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/CurrencyRepository.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/CurrencyRepository.kt new file mode 100644 index 000000000..1adb8409e --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/CurrencyRepository.kt @@ -0,0 +1,16 @@ +package co.nilin.mixchange.port.wallet.postgres.dao + +import co.nilin.mixchange.port.wallet.postgres.model.CurrencyModel +import co.nilin.mixchange.port.wallet.postgres.model.CurrencyRateModel +import kotlinx.coroutines.flow.Flow +import org.springframework.data.domain.Pageable +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono + +@Repository +interface CurrencyRepository: ReactiveCrudRepository { + +} \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/TransactionRepository.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/TransactionRepository.kt new file mode 100644 index 000000000..b8519e363 --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/TransactionRepository.kt @@ -0,0 +1,73 @@ +package co.nilin.mixchange.port.wallet.postgres.dao + +import co.nilin.mixchange.port.wallet.postgres.dto.TransactionStat +import co.nilin.mixchange.port.wallet.postgres.model.CurrencyModel +import co.nilin.mixchange.port.wallet.postgres.model.CurrencyRateModel +import co.nilin.mixchange.port.wallet.postgres.model.TransactionModel +import kotlinx.coroutines.flow.Flow +import org.springframework.core.ParameterizedTypeReference +import org.springframework.data.domain.Pageable +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono +import java.time.LocalDateTime +import java.util.* + + +@Repository +interface TransactionRepository: ReactiveCrudRepository { + @Query("SELECT count(1) cnt, COALESCE(sum(source_amount * crm.rate), 0) total" + + " FROM transaction tm " + + " join wallet wm on wm.id = tm.source_wallet " + + " join currency_rate crm on wm.currency = crm.source_currency " + + " WHERE wm.owner = :owner " + + " and wm.wallet_type = :walletType " + + " and crm.dest_currency = :currency " + + " and tm.transaction_date >= :startDate " + + " and tm.transaction_date <= :endDate") + fun calculateWithdrawStatisticsBasedOnCurrency(@Param("owner") owner: Long + ,@Param("walletType") walletType: String + ,@Param("startDate") startDate: LocalDateTime + ,@Param("endDate") endDate: LocalDateTime + ,@Param("currency") currency: String): Mono + @Query("SELECT count(1) cnt, COALESCE(sum(source_amount), 0) total " + + " FROM TransactionModel tm " + + " join WalletModel wm on wm.id = tm.sourceWallet " + + " WHERE wm.owner = :owner " + + " and wm.id = :id " + + " and tm.transaction_date >= :startDate " + + " and tm.transaction_date <= :endDate") + fun calculateWithdrawStatistics(@Param("owner") owner: Long + ,@Param("walletId") wallet: Long + ,@Param("startDate") startDate: LocalDateTime + ,@Param("endDate") endDate: LocalDateTime): Mono + + @Query("SELECT count(1) cnt, COALESCE(sum(dest_amount),0) total " + + " FROM transaction tm " + + " join wallet wm on wm.id = tm.dest_wallet " + + " join currency_rate crm on wm.currency = crm.source_currency " + + " WHERE wm.owner = :owner " + + " and wm.wallet_type = :walletType " + + " and crm.dest_currency = :currency " + + " and tm.transaction_date >= :startDate " + + " and tm.transaction_date <= :endDate") + fun calculateDepositStatisticsBasedOnCurrency(@Param("owner") owner: Long + ,@Param("walletType") walletType: String + ,@Param("startDate") startDate: LocalDateTime + ,@Param("endDate") endDate: LocalDateTime + ,@Param("currency") currency: String): Mono + @Query("SELECT count(1) cnt, COALESCE(sum(dest_amount * crm.rate), 0) total" + + " FROM transaction tm " + + " join wallet wm on wm.id = tm.dest_wallet " + + " WHERE wm.owner = :owner " + + " and wm.id = :walletId " + + " and tm.transaction_date >= :startDate " + + " and tm.transaction_date <= :endDate") + fun calculateDepositStatistics(@Param("owner") owner: Long + ,@Param("walletId") wallet: Long + ,@Param("startDate") startDate: LocalDateTime + ,@Param("endDate") endDate: LocalDateTime): Mono + +} \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/UserLimitsRepository.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/UserLimitsRepository.kt new file mode 100644 index 000000000..aedd2f484 --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/UserLimitsRepository.kt @@ -0,0 +1,26 @@ +package co.nilin.mixchange.port.wallet.postgres.dao + +import co.nilin.mixchange.port.wallet.postgres.model.UserLimitsModel +import co.nilin.mixchange.port.wallet.postgres.model.WalletLimitsModel +import kotlinx.coroutines.flow.Flow +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono + +@Repository +interface UserLimitsRepository : ReactiveCrudRepository { + @Query("select * from user_limits where level = :level and action = :action and owner is null") + fun findByLevelAndAction( + @Param("level") level: String, + @Param("action") action: String + ): Flow + + @Query("select * from user_limits where owner = :owner and action = :action") + fun findByOwnerAndAction( + @Param("owner") owner: Long, + @Param("action") action: String + ): Flow + +} \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletConfigRepository.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletConfigRepository.kt new file mode 100644 index 000000000..fb34b6084 --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletConfigRepository.kt @@ -0,0 +1,12 @@ +package co.nilin.mixchange.port.wallet.postgres.dao + +import co.nilin.mixchange.port.wallet.postgres.model.WalletConfigModel +import co.nilin.mixchange.port.wallet.postgres.model.WalletModel +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono + +@Repository +interface WalletConfigRepository : ReactiveCrudRepository \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletLimitsRepository.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletLimitsRepository.kt new file mode 100644 index 000000000..0a2b381e1 --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletLimitsRepository.kt @@ -0,0 +1,35 @@ +package co.nilin.mixchange.port.wallet.postgres.dao + +import co.nilin.mixchange.port.wallet.postgres.model.WalletLimitsModel +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono + +@Repository +interface WalletLimitsRepository : ReactiveCrudRepository { + @Query("select * from wallet_limits where level = :level and currency = :currency and action = :action and wallet_type = :walletType") + fun findByLevelAndCurrencyAndActionAndWalletType( + @Param("level") level: String, + @Param("currency") currency: String, + @Param("action") action: String, + @Param("walletType") walletType: String + ): Mono + + @Query("select * from wallet_Limits where owner = :owner and currency = :currency and action = :action and wallet_type = :walletType") + fun findByOwnerAndCurrencyAndActionAndWalletType( + @Param("owner") owner: Long, + @Param("currency") currency: String, + @Param("action") action: String, + @Param("walletType") walletType: String + ): Mono + + @Query("select * from wallet_limits where owner = :owner and currency = :currency and action = :action and wallet_id = :wallet") + fun findByOwnerAndCurrencyAndWalletAndAction( + @Param("owner") owner: Long, + @Param("currency") currency: String, + @Param("wallet") wallet: Long, + @Param("action") action: String + ): Mono +} \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletOwnerRepository.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletOwnerRepository.kt new file mode 100644 index 000000000..f942d8b45 --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletOwnerRepository.kt @@ -0,0 +1,14 @@ +package co.nilin.mixchange.port.wallet.postgres.dao + +import co.nilin.mixchange.port.wallet.postgres.model.WalletOwnerModel +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono + +@Repository +interface WalletOwnerRepository : ReactiveCrudRepository { + @Query("select * from wallet_owner where uuid = :uuid") + fun findByUuid(@Param("uuid") uuid: String): Mono +} \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletRepository.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletRepository.kt new file mode 100644 index 000000000..02ad5db93 --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletRepository.kt @@ -0,0 +1,22 @@ +package co.nilin.mixchange.port.wallet.postgres.dao + +import co.nilin.mixchange.port.wallet.postgres.model.WalletModel +import org.springframework.data.r2dbc.repository.Modifying +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono +import java.math.BigDecimal + +@Repository +interface WalletRepository : ReactiveCrudRepository { + @Query("select * from wallet where owner = :owner and wallet_type = :type and currency = :currency ") + fun findByOwnerAndTypeAndCurrency(@Param("owner") owner: Long, + @Param("type") type: String, + @Param("currency") currency: String): Mono + + @Modifying + @Query("update wallet set balance = balance + :balance where id = :id") + fun updateBalance(id: Long, delta: BigDecimal): Mono +} \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dto/SavedWallet.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dto/SavedWallet.kt new file mode 100644 index 000000000..21b19de22 --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dto/SavedWallet.kt @@ -0,0 +1,30 @@ +package co.nilin.mixchange.port.wallet.postgres.dto + +import co.nilin.mixchange.wallet.core.model.Amount +import co.nilin.mixchange.wallet.core.model.Currency +import co.nilin.mixchange.wallet.core.model.Wallet +import co.nilin.mixchange.wallet.core.model.WalletOwner + +class SavedWallet( + val id_: Long, val owner_: WalletOwner, val balance_: Amount, val currency_: Currency, val type_: String +) : Wallet { + override fun id(): Long { + return id_ + } + + override fun owner(): WalletOwner { + return owner_ + } + + override fun balance(): Amount { + return balance_ + } + + override fun currency(): Currency { + return currency_ + } + + override fun type(): String { + return type_ + } +} \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dto/TransactionStat.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dto/TransactionStat.kt new file mode 100644 index 000000000..386b5d23c --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dto/TransactionStat.kt @@ -0,0 +1,7 @@ +package co.nilin.mixchange.port.wallet.postgres.dto + +import java.math.BigDecimal + +class TransactionStat(val cnt: Long?, + val total: BigDecimal? +) \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/CurrencyRateServiceImpl.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/CurrencyRateServiceImpl.kt new file mode 100644 index 000000000..1d50e374e --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/CurrencyRateServiceImpl.kt @@ -0,0 +1,32 @@ +package co.nilin.mixchange.port.wallet.postgres.impl + +import co.nilin.mixchange.port.wallet.postgres.dao.CurrencyRateRepository +import co.nilin.mixchange.wallet.core.model.Amount +import co.nilin.mixchange.wallet.core.model.Currency +import co.nilin.mixchange.wallet.core.spi.CurrencyRateService +import kotlinx.coroutines.reactive.awaitFirstOrDefault +import kotlinx.coroutines.reactive.awaitFirstOrNull +import org.springframework.stereotype.Service +import java.math.BigDecimal + +@Service +class CurrencyRateServiceImpl(val currencyRateRepository: CurrencyRateRepository) : CurrencyRateService { + override suspend fun convert(amount: Amount, targetCurrency: Currency): BigDecimal { + if (amount.currency.getName() == targetCurrency.getName()) + return amount.amount + + var rate = currencyRateRepository.findBySourceAndDest( + amount.currency.getName(), targetCurrency.getName() + ) + .map { BigDecimal.valueOf(it!!.rate) } + .awaitFirstOrNull() + if (rate != null) { + rate = currencyRateRepository.findBySourceAndDest( + targetCurrency.getName(), amount.currency.getName() + ) + .map { BigDecimal.valueOf(it!!.rate) } + .awaitFirstOrDefault(BigDecimal.ZERO) + } + return amount.amount.multiply(rate) + } +} \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/TransactionManagerImpl.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/TransactionManagerImpl.kt new file mode 100644 index 000000000..8aa8d43e1 --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/TransactionManagerImpl.kt @@ -0,0 +1,27 @@ +package co.nilin.mixchange.port.wallet.postgres.impl + +import co.nilin.mixchange.port.wallet.postgres.dao.TransactionRepository +import co.nilin.mixchange.port.wallet.postgres.model.TransactionModel +import co.nilin.mixchange.wallet.core.model.Transaction +import co.nilin.mixchange.wallet.core.spi.TransactionManager +import kotlinx.coroutines.reactive.awaitSingle +import org.springframework.stereotype.Service +import java.time.LocalDateTime + +@Service +class TransactionManagerImpl(val transactionRepository: TransactionRepository) : TransactionManager { + override suspend fun save(transaction: Transaction): String { + return transactionRepository.save( + TransactionModel( + null, + transaction.sourceWallet.id()!!, + transaction.destWallet.id()!!, + transaction.sourceAmount, + transaction.destAmount, + transaction.description, + transaction.transferRef, + LocalDateTime.now() + ) + ).awaitSingle().id.toString() + } +} \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/WalletManagerImpl.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/WalletManagerImpl.kt new file mode 100644 index 000000000..8a3a33032 --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/WalletManagerImpl.kt @@ -0,0 +1,154 @@ +package co.nilin.mixchange.port.wallet.postgres.impl + +import co.nilin.mixchange.port.wallet.postgres.dao.* +import co.nilin.mixchange.port.wallet.postgres.dto.SavedWallet +import co.nilin.mixchange.port.wallet.postgres.model.WalletModel +import co.nilin.mixchange.wallet.core.model.Amount +import co.nilin.mixchange.wallet.core.model.Currency +import co.nilin.mixchange.wallet.core.model.Wallet +import co.nilin.mixchange.wallet.core.model.WalletOwner +import co.nilin.mixchange.wallet.core.spi.WalletManager +import kotlinx.coroutines.reactive.awaitFirst +import kotlinx.coroutines.reactive.awaitFirstOrNull +import org.springframework.stereotype.Service +import java.math.BigDecimal +import java.time.LocalDateTime + +@Service +class WalletManagerImpl( + val walletLimitsRepository: WalletLimitsRepository, + val transactionRepository: TransactionRepository, + val walletRepository: WalletRepository, + val walletOwnerRepository: WalletOwnerRepository, + val currencyRepository: CurrencyRepository +) : WalletManager { + + override suspend fun isDepositAllowed(wallet: Wallet, amount: BigDecimal): Boolean { + var limit = walletLimitsRepository.findByOwnerAndCurrencyAndWalletAndAction( + wallet.owner().id()!!, wallet.currency().getName(), wallet.id()!!, "deposit" + ).awaitFirstOrNull() + if (limit == null) { + limit = walletLimitsRepository.findByOwnerAndCurrencyAndActionAndWalletType( + wallet.owner().id()!!, wallet.currency().getName(), wallet.type(), "deposit" + ).awaitFirstOrNull() + if (limit == null) { + limit = walletLimitsRepository.findByLevelAndCurrencyAndActionAndWalletType( + wallet.owner().level(), wallet.currency().getName(), wallet.type(), "deposit" + ).awaitFirstOrNull() + } + } + var evaluate = true + if (limit != null) { + if (limit.dailyCount != null || limit.dailyTotal != null) { + val ts = transactionRepository.calculateDepositStatistics( + wallet.owner().id()!!, wallet.id()!!, LocalDateTime.now().minusDays(1) + .withHour(0).withMinute(0).withSecond(0), LocalDateTime.now() + ).awaitFirstOrNull() + if (ts != null) { + evaluate = (limit.dailyCount != null && ts.cnt!! >= limit.dailyCount!!) + || (limit.dailyTotal != null && ts.total!! >= limit.dailyTotal) + } + } + + if (evaluate && (limit.monthlyCount != null || limit.monthlyTotal != null)) { + val ts = transactionRepository.calculateDepositStatistics( + wallet.owner().id()!!, wallet.id()!!, LocalDateTime.now().minusMonths(1) + .withDayOfMonth(1) + .withHour(0).withMinute(0).withSecond(0), LocalDateTime.now() + ).awaitFirstOrNull() + if (ts != null) { + evaluate = (limit.dailyCount != null && ts.cnt!! >= limit.dailyCount!!) + || (limit.dailyTotal != null && ts.total!! >= limit.dailyTotal) + } + } + } + return evaluate + } + + override suspend fun isWithdrawAllowed(wallet: Wallet, amount: BigDecimal): Boolean { + var evaluate = wallet.balance().amount >= amount + if (evaluate) { + var limit = walletLimitsRepository.findByOwnerAndCurrencyAndWalletAndAction( + wallet.owner().id()!!, wallet.currency().getName(), wallet.id()!!, "withdraw" + ).awaitFirstOrNull() + if (limit == null) { + limit = walletLimitsRepository.findByOwnerAndCurrencyAndActionAndWalletType( + wallet.owner().id()!!, wallet.currency().getName(), wallet.type(), "withdraw" + ).awaitFirstOrNull() + if (limit == null) { + limit = walletLimitsRepository.findByLevelAndCurrencyAndActionAndWalletType( + wallet.owner().level(), wallet.currency().getName(), wallet.type(), "withdraw" + ).awaitFirstOrNull() + } + } + + if (limit != null) { + if (limit.dailyCount != null || limit.dailyTotal != null) { + val ts = transactionRepository.calculateWithdrawStatistics( + wallet.owner().id()!!, wallet.id()!!, LocalDateTime.now().minusDays(1) + .withHour(0).withMinute(0).withSecond(0), LocalDateTime.now() + ).awaitFirstOrNull() + if (ts != null) { + evaluate = (limit.dailyCount != null && ts.cnt!! >= limit.dailyCount!!) + || (limit.dailyTotal != null && ts.total!! >= limit.dailyTotal) + } + } + + if (evaluate && (limit.monthlyCount != null || limit.monthlyTotal != null)) { + val ts = transactionRepository.calculateWithdrawStatistics( + wallet.owner().id()!!, wallet.id()!!, LocalDateTime.now().minusMonths(1) + .withDayOfMonth(1) + .withHour(0).withMinute(0).withSecond(0), LocalDateTime.now() + ).awaitFirstOrNull() + if (ts != null) { + evaluate = (limit.dailyCount != null && ts.cnt!! >= limit.dailyCount!!) + || (limit.dailyTotal != null && ts.total!! >= limit.dailyTotal) + } + } + } + } + return evaluate + } + + override suspend fun increaseBalance(wallet: Wallet, amount: BigDecimal) { + walletRepository.updateBalance(wallet.id()!!, amount).awaitFirst() + } + + override suspend fun decreaseBalance(wallet: Wallet, amount: BigDecimal) { + walletRepository.updateBalance(wallet.id()!!, -amount).awaitFirst() + } + + override suspend fun findWalletByOwnerAndCurrencyAndType( + owner: WalletOwner, + walletType: String, + currency: Currency + ): Wallet? { + val walletModel = walletRepository.findByOwnerAndTypeAndCurrency( + owner.id()!!, walletType, currency.getName() + ).awaitFirstOrNull() + if (walletModel == null) + return null + val existingCurrency = currencyRepository.findById(walletModel.currency).awaitFirst() + return SavedWallet( + walletModel.id!!, + walletOwnerRepository.findById(walletModel.owner).awaitFirst(), + Amount(existingCurrency, walletModel.balance), + existingCurrency, + walletModel.type + ) + } + + override suspend fun createWallet(owner: WalletOwner, balance: Amount, currency: Currency, type: String): Wallet { + val walletModel = walletRepository.save(WalletModel(null, owner.id()!!, type, currency.getName(), balance.amount)) + .awaitFirst() + val wallet = SavedWallet( + walletModel.id!!, + owner, + Amount(currency, walletModel.balance), + currency, + walletModel.type + ) + return wallet + + } +} \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/WalletOwnerManagerImpl.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/WalletOwnerManagerImpl.kt new file mode 100644 index 000000000..1e4256e6a --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/WalletOwnerManagerImpl.kt @@ -0,0 +1,149 @@ +package co.nilin.mixchange.port.wallet.postgres.impl + +import co.nilin.mixchange.port.wallet.postgres.dao.TransactionRepository +import co.nilin.mixchange.port.wallet.postgres.dao.UserLimitsRepository +import co.nilin.mixchange.port.wallet.postgres.dao.WalletConfigRepository +import co.nilin.mixchange.port.wallet.postgres.dao.WalletOwnerRepository +import co.nilin.mixchange.port.wallet.postgres.model.UserLimitsModel +import co.nilin.mixchange.port.wallet.postgres.model.WalletConfigModel +import co.nilin.mixchange.port.wallet.postgres.model.WalletOwnerModel +import co.nilin.mixchange.wallet.core.model.Amount +import co.nilin.mixchange.wallet.core.model.WalletOwner +import co.nilin.mixchange.wallet.core.spi.WalletOwnerManager +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEmpty +import kotlinx.coroutines.flow.reduce +import kotlinx.coroutines.reactive.awaitFirst +import kotlinx.coroutines.reactive.awaitFirstOrDefault +import kotlinx.coroutines.reactive.awaitFirstOrNull +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service +import java.time.LocalDateTime + +@Service +class WalletOwnerManagerImpl( + val limitsRepository: UserLimitsRepository, + val transactionRepository: TransactionRepository, + val walletConfigRepository: WalletConfigRepository, + val walletOwnerRepository: WalletOwnerRepository +) : WalletOwnerManager { + + val logger = LoggerFactory.getLogger(WalletOwnerManager::class.java) + + override suspend fun isDepositAllowed(owner: WalletOwner, amount: Amount): Boolean { + var evaluate: Boolean? = limitsRepository.findByOwnerAndAction( + owner.id()!!, + "deposit" + ).map { limit -> + evaluateLimit(limit, owner, true) + }.onEmpty { + emit(true) + } + .reduce { a, b -> + a && b + } + if (evaluate == null) { + evaluate = limitsRepository.findByLevelAndAction( + owner.level(), + "deposit" + ) + .map { limit -> + evaluateLimit(limit, owner, true) + }.onEmpty { + emit(true) + }.reduce { a, b -> + a && b + } + } + logger.info("isDepositAllowed: {} {}{} {}", owner.uuid(), amount.amount, amount.currency.getName(), evaluate) + return evaluate + } + + private suspend fun evaluateLimit( + limit: UserLimitsModel?, + owner: WalletOwner, + deposit: Boolean + ): Boolean { + var evaluate = true + if (limit != null) { + val mainCurrency = walletConfigRepository.findAll() + .map { t: WalletConfigModel -> t.mainCurrency } + .awaitFirstOrDefault("BTC") + if (limit.dailyCount != null || limit.dailyTotal != null) { + val ts = if (deposit) { + transactionRepository.calculateDepositStatisticsBasedOnCurrency( + owner.id()!!, limit.walletType, LocalDateTime.now().minusDays(1) + .withHour(0).withMinute(0).withSecond(0), LocalDateTime.now(), mainCurrency + ) + } else { + transactionRepository.calculateWithdrawStatisticsBasedOnCurrency( + owner.id()!!, limit.walletType, LocalDateTime.now().minusDays(1) + .withHour(0).withMinute(0).withSecond(0), LocalDateTime.now(), mainCurrency + ) + }.awaitFirstOrNull() + if (ts != null) { + evaluate = !((limit.dailyCount != null && ts.cnt!! >= limit.dailyCount) + || (limit.dailyTotal != null && ts.total!! >= limit.dailyTotal)) + } + } + if (evaluate) { + if (limit.monthlyCount != null || limit.monthlyTotal != null) { + val ts = if (deposit) { + transactionRepository.calculateDepositStatisticsBasedOnCurrency( + owner.id()!!, limit.walletType, LocalDateTime.now().minusMonths(1).withDayOfMonth(1) + .withHour(0).withMinute(0).withSecond(0), LocalDateTime.now(), mainCurrency + ) + } else { + transactionRepository.calculateWithdrawStatisticsBasedOnCurrency( + owner.id()!!, limit.walletType, LocalDateTime.now().minusMonths(1).withDayOfMonth(1) + .withHour(0).withMinute(0).withSecond(0), LocalDateTime.now(), mainCurrency + ) + }.awaitFirstOrNull() + if (ts != null) { + evaluate = !((limit.monthlyCount != null && ts.cnt!! >= limit.monthlyCount) + || (limit.monthlyTotal != null && ts.total!! >= limit.monthlyTotal)) + } + } + } + } + return evaluate + } + + + override suspend fun isWithdrawAllowed(owner: WalletOwner, amount: Amount): Boolean { + var evaluate: Boolean? = limitsRepository.findByOwnerAndAction( + owner.id()!!, + "withdraw" + ) + .map { limit -> + evaluateLimit(limit, owner, false) + }.onEmpty { + emit(true) + }.reduce { a, b -> + a && b + } + if (evaluate == null) { + evaluate = limitsRepository.findByLevelAndAction( + owner.level(), + "withdraw" + ) + .map { limit -> + evaluateLimit(limit, owner, false) + }.onEmpty { + emit(true) + }.reduce { a, b -> + a && b + } + } + logger.info("isWithdrawAllowed: {} {}{} {}", owner.uuid(), amount.amount, amount.currency.getName(), evaluate) + return evaluate + } + + override suspend fun findWalletOwner(uuid: String): WalletOwner? { + return walletOwnerRepository.findByUuid(uuid).awaitFirstOrNull() + } + + override suspend fun createWalletOwner(uuid: String, title: String, userLevel: String): WalletOwner { + return walletOwnerRepository.save(WalletOwnerModel(null, uuid, title, userLevel)).awaitFirst() + } +} \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/CurrencyModel.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/CurrencyModel.kt new file mode 100644 index 000000000..8121d32ad --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/CurrencyModel.kt @@ -0,0 +1,27 @@ +package co.nilin.mixchange.port.wallet.postgres.model + + +import co.nilin.mixchange.wallet.core.model.Currency +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table + +@Table("currency") +class CurrencyModel( + @Id @Column("name") val name_: String, + @Column("symbol") val symbol_: String, + @Column("precision") val precision_: Int +): Currency { + override fun getSymbol(): String { + return symbol_ + } + + override fun getName(): String { + return name_ + } + + override fun getPrecision(): Int { + return precision_ + } +} + diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/CurrencyRateModel.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/CurrencyRateModel.kt new file mode 100644 index 000000000..59c765d7e --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/CurrencyRateModel.kt @@ -0,0 +1,14 @@ +package co.nilin.mixchange.port.wallet.postgres.model + + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table + +@Table("currency_rate") +class CurrencyRateModel( + @Id val id: Long?, + @Column("source_currency") val sourceCurrency: String, + @Column("dest_currency") val destCurrency: String, + @Column("rate") val rate: Double +) \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/TransactionModel.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/TransactionModel.kt new file mode 100644 index 000000000..27395fa18 --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/TransactionModel.kt @@ -0,0 +1,19 @@ +package co.nilin.mixchange.port.wallet.postgres.model + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.math.BigDecimal +import java.time.LocalDateTime + +@Table("transaction") +class TransactionModel( + @Id var id: Long?, + @Column("source_wallet") val sourceWallet: Long, + @Column("dest_wallet") val destWallet: Long, + @Column("source_amount") val sourceAmount: BigDecimal, + @Column("dest_amount") val destAmount: BigDecimal, + val description: String?, + @Column("transfer_ref") val transferRef: String?, + @Column("transaction_date") val txDate: LocalDateTime +) \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/UserLimitsModel.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/UserLimitsModel.kt new file mode 100644 index 000000000..07e3af446 --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/UserLimitsModel.kt @@ -0,0 +1,19 @@ +package co.nilin.mixchange.port.wallet.postgres.model + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.math.BigDecimal + +@Table("user_limits") +class UserLimitsModel( + @Id val id: Long?, + val level: String?, + val owner: Long?, + val action: String, //withdraw or deposit + val walletType: String, + @Column("daily_total") val dailyTotal: BigDecimal?, + @Column("daily_count") val dailyCount: Int?, + @Column("monthly_total") val monthlyTotal: BigDecimal?, + @Column("monthly_count") val monthlyCount: Int? +) \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletConfigModel.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletConfigModel.kt new file mode 100644 index 000000000..92eedc8b8 --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletConfigModel.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.port.wallet.postgres.model + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table + +@Table("wallet_config") +class WalletConfigModel(@Id val name: String +, @Column("main_currency") val mainCurrency: String) \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletLimitsModel.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletLimitsModel.kt new file mode 100644 index 000000000..917e56e6b --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletLimitsModel.kt @@ -0,0 +1,21 @@ +package co.nilin.mixchange.port.wallet.postgres.model + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.math.BigDecimal + +@Table("wallet_limits") +class WalletLimitsModel( + @Id val id: Long?, + val level: String?, + val owner: Long?, + val action: String, //withdraw or deposit + val currency: String, + @Column("wallet_type") val walletType: String, + @Column("wallet_id") val walletId: Long?, + @Column("daily_total") val dailyTotal: BigDecimal?, + @Column("daily_count") val dailyCount: Int?, + @Column("monthly_total") val monthlyTotal: BigDecimal?, + @Column("monthly_count") val monthlyCount: Int? +) \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletModel.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletModel.kt new file mode 100644 index 000000000..07736fc02 --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletModel.kt @@ -0,0 +1,15 @@ +package co.nilin.mixchange.port.wallet.postgres.model + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.math.BigDecimal + +@Table("wallet") +data class WalletModel( + @Id @Column("id") val id: Long?, + @Column("owner") val owner: Long, + @Column("wallet_type") val type: String, + @Column("currency") val currency: String, + @Column("balance") val balance: BigDecimal +) \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletOwnerModel.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletOwnerModel.kt new file mode 100644 index 000000000..8733c4bae --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletOwnerModel.kt @@ -0,0 +1,30 @@ +package co.nilin.mixchange.port.wallet.postgres.model + +import co.nilin.mixchange.wallet.core.model.WalletOwner +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table + +@Table("wallet_owner") +class WalletOwnerModel( + @Id @Column("id") var id_: Long?, + @Column("uuid") var uuid_: String, + @Column("title") var title_: String, + @Column("level") var level_: String +): WalletOwner { + override fun id(): Long? { + return id_ + } + + override fun uuid(): String { + return uuid_ + } + + override fun title(): String { + return title_ + } + + override fun level(): String { + return level_ + } +} \ No newline at end of file From e88d023e5a41c384d75933da5dd93f52f5bd6950 Mon Sep 17 00:00:00 2001 From: maryarm Date: Wed, 26 May 2021 19:58:33 +0300 Subject: [PATCH 02/59] close #2-Add a rest endpoint for balance inquiry --- Wallet/wallet-app/pom.xml | 13 ++++++ .../wallet/app/config/SecurityConfig.kt | 43 +++++++++++++++++++ .../app/controller/BalanceController.kt | 33 ++++++++++++++ ...lletController.kt => InquiryController.kt} | 6 +-- .../wallet-app/src/main/resources/public.cert | 3 ++ 5 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/SecurityConfig.kt create mode 100644 Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/BalanceController.kt rename Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/{WalletController.kt => InquiryController.kt} (89%) create mode 100644 Wallet/wallet-app/src/main/resources/public.cert diff --git a/Wallet/wallet-app/pom.xml b/Wallet/wallet-app/pom.xml index 433e3d3e8..59a665e79 100644 --- a/Wallet/wallet-app/pom.xml +++ b/Wallet/wallet-app/pom.xml @@ -94,6 +94,19 @@ org.springframework.boot spring-boot-starter-actuator + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + + org.bouncycastle + bcprov-jdk15on + 1.60 + diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/SecurityConfig.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/SecurityConfig.kt new file mode 100644 index 000000000..a94a4225f --- /dev/null +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/SecurityConfig.kt @@ -0,0 +1,43 @@ +package co.nilin.mixchange.wallet.app.config + +import org.springframework.core.io.ClassPathResource +import org.springframework.core.io.Resource +import org.springframework.context.annotation.Bean +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity +import org.springframework.security.config.web.server.ServerHttpSecurity +import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder +import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder +import org.springframework.security.web.server.SecurityWebFilterChain +import org.springframework.util.Base64Utils +import org.springframework.util.FileCopyUtils +import java.security.KeyFactory +import java.security.interfaces.RSAPublicKey +import java.security.spec.X509EncodedKeySpec + +@EnableWebFluxSecurity +class SecurityConfig { + @Bean + fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain? { + http.csrf().disable() + .authorizeExchange() + .pathMatchers("/balanceOf/**").hasAuthority("SCOPE_trust") + .pathMatchers("/**").permitAll() + .anyExchange().authenticated() + .and() + .oauth2ResourceServer() + .jwt() + return http.build() + } + + @Bean + @Throws(Exception::class) + fun reactiveJwtDecoder(): ReactiveJwtDecoder? { + val resource: Resource = ClassPathResource("/public.cert") + val publicKey = String(FileCopyUtils.copyToByteArray(resource.inputStream)) + .replace("-----BEGIN PUBLIC KEY-----\n", "") + .replace("\n-----END PUBLIC KEY-----", "") + val spec = X509EncodedKeySpec(Base64Utils.decodeFromString(publicKey)) + val kf = KeyFactory.getInstance("RSA") + return NimbusReactiveJwtDecoder(kf.generatePublic(spec) as RSAPublicKey) + } +} diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/BalanceController.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/BalanceController.kt new file mode 100644 index 000000000..4ab005ffc --- /dev/null +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/BalanceController.kt @@ -0,0 +1,33 @@ +package co.nilin.mixchange.wallet.app.controller + +import co.nilin.mixchange.wallet.core.spi.WalletManager +import co.nilin.mixchange.wallet.core.spi.WalletOwnerManager +import org.slf4j.LoggerFactory +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RestController +import java.math.BigDecimal +import java.security.Principal + +@RestController +class BalanceController( + val walletManager: WalletManager, val walletOwnerManager: WalletOwnerManager +) { + val logger = LoggerFactory.getLogger(BalanceController::class.java) + + data class BalanceResponse(val balance: BigDecimal) + + @GetMapping("/balanceOf/wallet_type/{wallet_type}/currency/{currency}") + suspend fun getBalance( + principal: Principal, + @PathVariable("currency") currency: String, + @PathVariable("wallet_type") walletType: String + ): BalanceResponse { + val owner = walletOwnerManager.findWalletOwner(principal.name) + if (owner != null) { + val wallet = walletManager.findWalletByOwnerAndCurrencyAndType(owner, walletType, Symbol(currency)) + return BalanceResponse(wallet?.balance()?.amount ?: BigDecimal.ZERO) + } + return BalanceResponse(BigDecimal.ZERO) + } +} \ No newline at end of file diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/WalletController.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/InquiryController.kt similarity index 89% rename from Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/WalletController.kt rename to Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/InquiryController.kt index 3c0843959..97e5b044b 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/WalletController.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/InquiryController.kt @@ -1,21 +1,19 @@ package co.nilin.mixchange.wallet.app.controller import co.nilin.mixchange.wallet.core.model.Amount -import co.nilin.mixchange.wallet.core.model.Currency import co.nilin.mixchange.wallet.core.spi.WalletManager import co.nilin.mixchange.wallet.core.spi.WalletOwnerManager import org.slf4j.LoggerFactory import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RestController -import java.lang.RuntimeException import java.math.BigDecimal @RestController -class WalletController( +class InquiryController( val walletManager: WalletManager, val walletOwnerManager: WalletOwnerManager ) { - val logger = LoggerFactory.getLogger(WalletController::class.java) + val logger = LoggerFactory.getLogger(InquiryController::class.java) data class BooleanResponse(val result: Boolean) @GetMapping("{uuid}/wallet_type/{wallet_type}/can_withdraw/{amount}_{currency}") diff --git a/Wallet/wallet-app/src/main/resources/public.cert b/Wallet/wallet-app/src/main/resources/public.cert new file mode 100644 index 000000000..8589de065 --- /dev/null +++ b/Wallet/wallet-app/src/main/resources/public.cert @@ -0,0 +1,3 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsw+lmV2HbpgXllKS+ccyCerlWDir32Y3yFvXF3CbzYRKVg/4FddA8itMCPpPjVOAo26bi/0WImHHYPyGxSH3QQIQaSdS5AFUNarpFc9M/W4Bu7k5h8sKb+vzZA3hMLFyvmQ2JtY8Zuqi1BBB33OAlkOHprJFTrrt1xa4a6KJjKXlDp5o5hOFgvZbsW+k/fBU0CLCbL3PlXQLhqHgEncpL9L9FF/xDbPIGftxjEWcBPFG/SuyygT35xRFMxB7ifR+wJZwVnoC4RhbhgJdL2EiUVT7wq5MTsxr2j4nAAJYatK/7OTecs6y/7bGNAGEoqh4Q3X3D95YFex0scmHgY1hWwIDAQAB +-----END PUBLIC KEY----- \ No newline at end of file From 2e47bd0f8b1995bcdae417d5122e2e7d3dc3d90c Mon Sep 17 00:00:00 2001 From: maryarm Date: Sun, 6 Jun 2021 21:40:52 +0200 Subject: [PATCH 03/59] close #4, #5: disable ssl for realms, add demo smtp config --- UserManagement/keycloak-gateway/Dockerfile | 1 + .../auth/gateway/KeycloakGatewayApp.kt | 1 + .../config/EmbeddedKeycloakApplication.kt | 14 +++--- .../config/KeycloakServerProperties.kt | 2 +- .../gateway/config/SystemPropertyConfig.kt | 47 +++++++++++++++++++ .../resources/META-INF/keycloak-server.json | 10 ++-- .../src/main/resources/application-docker.yml | 31 +----------- .../src/main/resources/application.yml | 15 +++++- .../src/main/resources/opex-master-realm.json | 47 +++++++++++++++++++ .../src/main/resources/opex-realm.json | 40 +++++++++++----- 10 files changed, 152 insertions(+), 56 deletions(-) create mode 100644 UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/SystemPropertyConfig.kt create mode 100644 UserManagement/keycloak-gateway/src/main/resources/opex-master-realm.json diff --git a/UserManagement/keycloak-gateway/Dockerfile b/UserManagement/keycloak-gateway/Dockerfile index f2cbd4c26..99b369c3c 100644 --- a/UserManagement/keycloak-gateway/Dockerfile +++ b/UserManagement/keycloak-gateway/Dockerfile @@ -2,4 +2,5 @@ FROM openjdk:8-jdk-alpine VOLUME /tmp ARG JAR_FILE=target/*.jar COPY ${JAR_FILE} app.jar +COPY target/classes/opex-master-realm.json opex-master-realm.json ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"] \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/KeycloakGatewayApp.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/KeycloakGatewayApp.kt index 8aa639743..f858f6ab0 100644 --- a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/KeycloakGatewayApp.kt +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/KeycloakGatewayApp.kt @@ -13,6 +13,7 @@ import org.springframework.boot.runApplication import org.springframework.context.ApplicationListener import org.springframework.context.annotation.Bean import org.springframework.context.annotation.ComponentScan +import org.springframework.core.io.ClassPathResource @SpringBootApplication(exclude = [LiquibaseAutoConfiguration::class]) @ComponentScan(basePackages = arrayOf("co.nilin.mixchange.auth.gateway")) diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakApplication.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakApplication.kt index fb07560e7..43fbcb57f 100644 --- a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakApplication.kt +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakApplication.kt @@ -29,17 +29,17 @@ class EmbeddedKeycloakApplication() : KeycloakApplication() { var keycloakServerProperties: KeycloakServerProperties? = null } + init { + createMasterRealmAdminUser() + createMixchangeRealm() + } + override fun loadConfig() { val factory: JsonConfigProviderFactory = RegularJsonConfigProviderFactory() Config.init(factory.create() .orElseThrow { NoSuchElementException("No value present") }) } - init { - createMasterRealmAdminUser() - createMixchangeRealm() - } - private fun createMasterRealmAdminUser() { val session = getSessionFactory().create() val applianceBootstrap = ApplianceBootstrap(session) @@ -60,12 +60,12 @@ class EmbeddedKeycloakApplication() : KeycloakApplication() { try { session.transactionManager.begin() val manager = RealmManager(session) - val lessonRealmImportFile: Resource = ClassPathResource( + val realmImportFile: Resource = ClassPathResource( keycloakServerProperties!!.realmImportFile ) manager.importRealm( JsonSerialization.readValue( - lessonRealmImportFile.getInputStream(), + realmImportFile.getInputStream(), RealmRepresentation::class.java ) ) diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/KeycloakServerProperties.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/KeycloakServerProperties.kt index 732153bc3..d7fc0fb32 100644 --- a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/KeycloakServerProperties.kt +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/KeycloakServerProperties.kt @@ -7,7 +7,7 @@ import org.springframework.context.annotation.Configuration @ConfigurationProperties(prefix = "keycloak.server") class KeycloakServerProperties { var contextPath = "/auth" - var realmImportFile = "opex-realm.json" + var realmImportFile = "/opex-realm.json" var adminUser = AdminUser() class AdminUser { diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/SystemPropertyConfig.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/SystemPropertyConfig.kt new file mode 100644 index 000000000..e308ad26a --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/SystemPropertyConfig.kt @@ -0,0 +1,47 @@ +package co.nilin.mixchange.auth.gateway.config + +import org.springframework.beans.factory.BeanFactory +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.config.ConfigurableBeanFactory +import org.springframework.context.annotation.Configuration +import org.springframework.core.env.StandardEnvironment +import org.springframework.core.env.MapPropertySource +import org.springframework.core.io.ClassPathResource +import java.io.File +import java.util.* + + +@Configuration +class SystemPropertyConfig { + @Autowired + fun addYmlToSystemProperty( + env: StandardEnvironment, + beanFactory: ConfigurableBeanFactory + ) { + for (propertySource in env.propertySources) { + if (propertySource is MapPropertySource) { + val propertySourceName = propertySource.getName() + val sysProperties: Properties = System.getProperties() + println("setting sysprops from $propertySourceName") + val mapPropertySource = propertySource + println("source.name: ${mapPropertySource.name}") + for (key in mapPropertySource.propertyNames) { + val value = mapPropertySource.getProperty(key) + if (sysProperties[key] == null) { + if (value is String) { + var resolvedValue = beanFactory.resolveEmbeddedValue("\${$key}") ?: value + if (resolvedValue.startsWith("classpath:")) { + resolvedValue = + ClassPathResource(resolvedValue.substring("classpath:".length)).file.absolutePath + } + sysProperties[key] = resolvedValue + } else { + sysProperties[key] = value + } + println("$key -> ${sysProperties[key]}") + } + } + } + } + } +} diff --git a/UserManagement/keycloak-gateway/src/main/resources/META-INF/keycloak-server.json b/UserManagement/keycloak-gateway/src/main/resources/META-INF/keycloak-server.json index cf9a761ff..45b4bfc91 100644 --- a/UserManagement/keycloak-gateway/src/main/resources/META-INF/keycloak-server.json +++ b/UserManagement/keycloak-gateway/src/main/resources/META-INF/keycloak-server.json @@ -135,11 +135,11 @@ "connectionsJpa": { "default": { - "url": "jdbc:postgresql://postgres-auth/opex_auth", - "driver": "org.postgresql.Driver", - "driverDialect": "org.hibernate.dialect.PostgreSQLDialect", - "user": "opex", - "password": "hiopex", + "url": "${spring.datasource.url}", + "driver": "${spring.datasource.driver-class-name}", + "driverDialect": "${spring.jpa.properties.hibernate.dialect}", + "user": "${spring.datasource.username}", + "password": "${spring.datasource.password}", "initializeEmpty": true, "migrationStrategy": "update", "showSql": "true", diff --git a/UserManagement/keycloak-gateway/src/main/resources/application-docker.yml b/UserManagement/keycloak-gateway/src/main/resources/application-docker.yml index 974a904c8..94ccde81d 100644 --- a/UserManagement/keycloak-gateway/src/main/resources/application-docker.yml +++ b/UserManagement/keycloak-gateway/src/main/resources/application-docker.yml @@ -1,40 +1,13 @@ -server.port: 8083 spring: - application: - name: opex-auth - main: - allow-bean-definition-overriding: false kafka: bootstrap-servers: ${KAFKA_IP_PORT} - consumer: - group-id: auth datasource: - platform: postgres - driver-class-name: org.postgresql.Driver url: jdbc:postgresql://${DB_IP_PORT}/opex_auth username: opex password: hiopex - initialization-mode: always - jpa: - hibernate: - ddl-auto: none - properties.hibernate.jdbc.lob.non_contextual_creation: true - open-in-view: false cloud: - bootstrap: - enabled: true consul: host: ${CONSUL_HOST} - port: 8500 - discovery: - #healthCheckPath: ${management.context-path}/health - instance-id: ${spring.application.name}:${server.port} - healthCheckInterval: 20s - prefer-ip-address: true keycloak: - server: - contextPath: /auth - adminUser: - username: opex-admin - password: hiopex - realmImportFile: opex-realm.json + migration: + file: /opex-master-realm.json diff --git a/UserManagement/keycloak-gateway/src/main/resources/application.yml b/UserManagement/keycloak-gateway/src/main/resources/application.yml index 57a3b588a..d51e1ef4c 100644 --- a/UserManagement/keycloak-gateway/src/main/resources/application.yml +++ b/UserManagement/keycloak-gateway/src/main/resources/application.yml @@ -4,6 +4,10 @@ spring: name: opex-auth main: allow-bean-definition-overriding: false + kafka: + bootstrap-servers: localhost:9092 + consumer: + group-id: auth datasource: platform: postgres driver-class-name: org.postgresql.Driver @@ -14,7 +18,9 @@ spring: jpa: hibernate: ddl-auto: none - properties.hibernate.jdbc.lob.non_contextual_creation: true + properties.hibernate: + jdbc.lob.non_contextual_creation: true + dialect: org.hibernate.dialect.PostgreSQLDialect open-in-view: false cloud: bootstrap: @@ -32,4 +38,9 @@ keycloak: adminUser: username: opex-admin password: hiopex - realmImportFile: opex-realm.json + realmImportFile: /opex-realm.json + migration: + action: import + provider: singleFile + file: classpath:/opex-master-realm.json + strategy: OVERWRITE_EXISTING diff --git a/UserManagement/keycloak-gateway/src/main/resources/opex-master-realm.json b/UserManagement/keycloak-gateway/src/main/resources/opex-master-realm.json new file mode 100644 index 000000000..7ee034e06 --- /dev/null +++ b/UserManagement/keycloak-gateway/src/main/resources/opex-master-realm.json @@ -0,0 +1,47 @@ +{ + "id" : "master", + "realm" : "master", + "notBefore" : 0, + "revokeRefreshToken" : false, + "refreshTokenMaxReuse" : 0, + "accessTokenLifespan" : 300, + "accessTokenLifespanForImplicitFlow" : 900, + "ssoSessionIdleTimeout" : 1800, + "ssoSessionMaxLifespan" : 36000, + "ssoSessionIdleTimeoutRememberMe" : 0, + "ssoSessionMaxLifespanRememberMe" : 0, + "offlineSessionIdleTimeout" : 2592000, + "offlineSessionMaxLifespanEnabled" : false, + "offlineSessionMaxLifespan" : 5184000, + "accessCodeLifespan" : 60, + "accessCodeLifespanUserAction" : 300, + "accessCodeLifespanLogin" : 1800, + "actionTokenGeneratedByAdminLifespan" : 43200, + "actionTokenGeneratedByUserLifespan" : 300, + "enabled" : true, + "sslRequired" : "none", + "registrationAllowed" : true, + "registrationEmailAsUsername" : false, + "rememberMe" : false, + "verifyEmail" : false, + "loginWithEmailAllowed" : true, + "duplicateEmailsAllowed" : false, + "resetPasswordAllowed" : true, + "editUsernameAllowed" : false, + "bruteForceProtected" : true, + "permanentLockout" : false, + "maxFailureWaitSeconds" : 900, + "minimumQuickLoginWaitSeconds" : 60, + "waitIncrementSeconds" : 60, + "quickLoginCheckMilliSeconds" : 1000, + "maxDeltaTimeSeconds" : 43200, + "failureFactor" : 30, + "smtpServer" : { + "host": "smtp.elasticemail.com", + "port": 2525, + "from": "for.demo.purpose.only@opex.dev", + "auth": true, + "user": "for.demo.purpose.only@opex.dev", + "password": "642467973026C6F093FB1E39C4BFC0D15042" + } +} \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/resources/opex-realm.json b/UserManagement/keycloak-gateway/src/main/resources/opex-realm.json index 083c9f34a..802eb8c84 100644 --- a/UserManagement/keycloak-gateway/src/main/resources/opex-realm.json +++ b/UserManagement/keycloak-gateway/src/main/resources/opex-realm.json @@ -19,16 +19,16 @@ "actionTokenGeneratedByAdminLifespan" : 43200, "actionTokenGeneratedByUserLifespan" : 300, "enabled" : true, - "sslRequired" : "external", - "registrationAllowed" : false, + "sslRequired" : "none", + "registrationAllowed" : true, "registrationEmailAsUsername" : false, "rememberMe" : false, "verifyEmail" : false, "loginWithEmailAllowed" : true, "duplicateEmailsAllowed" : false, - "resetPasswordAllowed" : false, + "resetPasswordAllowed" : true, "editUsernameAllowed" : false, - "bruteForceProtected" : false, + "bruteForceProtected" : true, "permanentLockout" : false, "maxFailureWaitSeconds" : 900, "minimumQuickLoginWaitSeconds" : 60, @@ -370,7 +370,7 @@ "authenticationFlowBindingOverrides" : { }, "fullScopeAllowed" : false, "nodeReRegistrationTimeout" : 0, - "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email" ], + "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email", "trust" ], "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] }, { "id" : "13d76feb-d762-4409-bb84-7a75bc395a61", @@ -396,7 +396,7 @@ "authenticationFlowBindingOverrides" : { }, "fullScopeAllowed" : false, "nodeReRegistrationTimeout" : 0, - "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email" ], + "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email", "trust" ], "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] }, { "id" : "4b9609f0-48d1-4e71-9381-2ecec08616f9", @@ -422,7 +422,7 @@ "authenticationFlowBindingOverrides" : { }, "fullScopeAllowed" : false, "nodeReRegistrationTimeout" : 0, - "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email" ], + "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email", "trust" ], "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] }, { "id" : "b88ce206-63d6-43b6-87c9-ea09d8c02f32", @@ -463,7 +463,7 @@ "authenticationFlowBindingOverrides" : { }, "fullScopeAllowed" : true, "nodeReRegistrationTimeout" : -1, - "defaultClientScopes" : [ "role_list", "profile" ], + "defaultClientScopes" : [ "role_list", "profile", "trust" ], "optionalClientScopes" : [ "web-origins", "address", "read", "phone", "roles", "offline_access", "microprofile-jwt", "write", "email" ] }, { "id" : "6a4bfbd0-576d-4778-af56-56f876647355", @@ -992,8 +992,17 @@ "include.in.token.scope" : "true", "display.on.consent.screen" : "true" } - } ], - "defaultDefaultClientScopes" : [ "roles", "role_list", "web-origins", "email", "profile" ], + }, + { + "id" : "d4e253fb-7361-47cf-9d4a-86245686fdf2", + "name" : "trust", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true" + } + }], + "defaultDefaultClientScopes" : [ "roles", "role_list", "web-origins", "email", "profile", "trust" ], "defaultOptionalClientScopes" : [ "offline_access", "address", "phone", "microprofile-jwt" ], "browserSecurityHeaders" : { "contentSecurityPolicyReportOnly" : "", @@ -1004,9 +1013,16 @@ "xXSSProtection" : "1; mode=block", "strictTransportSecurity" : "max-age=31536000; includeSubDomains" }, - "smtpServer" : { }, + "smtpServer" : { + "host": "smtp.elasticemail.com", + "port": 2525, + "from": "for.demo.purpose.only@opex.dev", + "auth": true, + "user": "for.demo.purpose.only@opex.dev", + "password": "642467973026C6F093FB1E39C4BFC0D15042" + }, "eventsEnabled" : false, - "eventsListeners" : [ "jboss-logging" ], + "eventsListeners" : [ "jboss-logging", "pl_event_listener" ], "enabledEventTypes" : [ ], "adminEventsEnabled" : false, "adminEventsDetailsEnabled" : false, From 51bc96b5c36fc482a3a497b5c14cfc19c316dd69 Mon Sep 17 00:00:00 2001 From: maryarm Date: Sun, 6 Jun 2021 21:41:17 +0200 Subject: [PATCH 04/59] close #3: add nginx to docker-compose.yml --- Deployment/docker-compose.yml | 61 +++++++++++++++++++++-------------- Deployment/nginx.conf | 37 +++++++++++++++++++++ 2 files changed, 74 insertions(+), 24 deletions(-) create mode 100644 Deployment/nginx.conf diff --git a/Deployment/docker-compose.yml b/Deployment/docker-compose.yml index 5990f9373..f9ad44965 100644 --- a/Deployment/docker-compose.yml +++ b/Deployment/docker-compose.yml @@ -3,7 +3,7 @@ services: zookeeper: image: 'docker.io/bitnami/zookeeper:3-debian-10' ports: - - '2181:2181' + - '127.0.0.1:2181:2181' volumes: - $PWD/runtime/zookeeper_data:/bitnami environment: @@ -16,13 +16,11 @@ services: kafka: image: 'docker.io/bitnami/kafka:2-debian-10' ports: - - '9092:9092' + - '127.0.0.1:9092:9092' volumes: - $PWD/runtime/kafka-data:/bitnami - links: - - zookeeper:zk environment: - - KAFKA_CFG_ZOOKEEPER_CONNECT=zk:2181 + - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181 - ALLOW_PLAINTEXT_LISTENER=yes - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092 depends_on: @@ -35,9 +33,9 @@ services: consul: image: 'consul' ports: - - '8500:8500' - - '8300:8300' - - '8600:8600' + - '127.0.0.1:8500:8500' + - '127.0.0.1:8300:8300' + - '127.0.0.1:8600:8600' environment: - CONSUL_BIND_INTERFACE=eth0 networks: @@ -45,14 +43,13 @@ services: deploy: restart_policy: condition: on-failure - redis: image: "redis:alpine" command: redis-server ports: - - "6379:6379" + - "127.0.0.1:6379:6379" volumes: - $PWD/runtime/redis-data:/var/lib/redis @@ -68,7 +65,7 @@ services: postgres-accountant: image: "postgres" ports: - - 5433:5432 + - 127.0.0.1:5433:5432 environment: - POSTGRES_USER=opex - POSTGRES_PASSWORD=hiopex @@ -80,7 +77,7 @@ services: postgres-eventlog: image: "postgres" ports: - - 5434:5432 + - 127.0.0.1:5434:5432 environment: - POSTGRES_USER=opex - POSTGRES_PASSWORD=hiopex @@ -92,7 +89,7 @@ services: postgres-auth: image: "postgres" ports: - - 5435:5432 + - 127.0.0.1:5435:5432 environment: - POSTGRES_USER=opex - POSTGRES_PASSWORD=hiopex @@ -107,7 +104,7 @@ services: postgres-wallet: image: "postgres" ports: - - 5436:5432 + - 127.0.0.1:5436:5432 environment: - POSTGRES_USER=opex - POSTGRES_PASSWORD=hiopex @@ -125,7 +122,7 @@ services: context: ../Accountant/accountant-app dockerfile: Dockerfile ports: - - 8089:8089 + - 127.0.0.1:8089:8089 environment: - JAVA_OPTS=-Xmx256m - SPRING_PROFILES_ACTIVE=docker @@ -147,7 +144,7 @@ services: context: ../EventLog/eventlog-app dockerfile: Dockerfile ports: - - 8090:8090 + - 127.0.0.1:8090:8090 environment: - JAVA_OPTS=-Xmx256m - SPRING_PROFILES_ACTIVE=docker @@ -169,8 +166,8 @@ services: context: ../MatchingEngine/matching-app dockerfile: Dockerfile ports: - - 8092:8092 - - 1046:1044 + - 127.0.0.1:8092:8092 + - 127.0.0.1:1046:1044 environment: - JAVA_OPTS=-Xmx256m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044 - SPRING_PROFILES_ACTIVE=docker @@ -188,8 +185,8 @@ services: context: ../MatchingGateway/gateway-app dockerfile: Dockerfile ports: - - 8093:8093 - - 1047:1044 + - 127.0.0.1:8093:8093 + - 127.0.0.1:1047:1044 environment: - JAVA_OPTS=-Xmx256m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044 - SPRING_PROFILES_ACTIVE=docker @@ -208,8 +205,8 @@ services: context: ../UserManagement/keycloak-gateway dockerfile: Dockerfile ports: - - 8083:8083 - - 1048:1044 + - 127.0.0.1:8083:8083 + - 127.0.0.1:1048:1044 environment: - JAVA_OPTS=-Xmx256m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044 - SPRING_PROFILES_ACTIVE=docker @@ -217,6 +214,8 @@ services: - REDIS_HOST=redis - CONSUL_HOST=consul - DB_IP_PORT=postgres-auth + - PROXY_ADDRESS_FORWARDING=true + - WORKING_DIR=$PWD networks: - opex depends_on: @@ -234,8 +233,8 @@ services: context: ../Wallet/wallet-app dockerfile: Dockerfile ports: - - 8091:8091 - - 1049:1044 + - 127.0.0.1:8091:8091 + - 127.0.0.1:1049:1044 environment: - JAVA_OPTS=-Xmx256m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044 - SPRING_PROFILES_ACTIVE=docker @@ -254,6 +253,20 @@ services: deploy: restart_policy: condition: on-failure + nginx: + image: nginx:latest + container_name: opex_nginx + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf + - $PWD/runtime/www:/data/www + ports: + - 80:80 + depends_on: + - wallet + - auth + - matching-gateway + networks: + - opex networks: opex: driver: bridge \ No newline at end of file diff --git a/Deployment/nginx.conf b/Deployment/nginx.conf new file mode 100644 index 000000000..ef6ecaa3b --- /dev/null +++ b/Deployment/nginx.conf @@ -0,0 +1,37 @@ +worker_processes 1; +events { worker_connections 1024; } +http { + sendfile on; + upstream docker-wallet { + server wallet:8091; + } + upstream docker-auth { + server auth:8083; + } + upstream docker-matching-gateway { + server matching-gateway:8093; + } + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + + server { + server_name api.opex.dev; + + location /auth { + proxy_pass http://docker-auth; + } + + location /wallet { + proxy_pass http://docker-wallet; + rewrite ^/wallet(.*)$ $1 break; + } + + location /gateway { + proxy_pass http://docker-matching-gateway; + rewrite ^/gateway(.*)$ $1 break; + } + } +} \ No newline at end of file From 6540562a891eee399f588d5ac8045118f970cd4b Mon Sep 17 00:00:00 2001 From: maryarm Date: Fri, 11 Jun 2021 22:10:05 +0200 Subject: [PATCH 05/59] Fix #6, override adminUrl of keycloak for docker --- .../keycloak-gateway/src/main/resources/application-docker.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UserManagement/keycloak-gateway/src/main/resources/application-docker.yml b/UserManagement/keycloak-gateway/src/main/resources/application-docker.yml index 94ccde81d..0c88c6422 100644 --- a/UserManagement/keycloak-gateway/src/main/resources/application-docker.yml +++ b/UserManagement/keycloak-gateway/src/main/resources/application-docker.yml @@ -11,3 +11,5 @@ spring: keycloak: migration: file: /opex-master-realm.json + adminUrl: https://api.opex.dev/auth + frontendUrl: https://api.opex.dev/auth From f1725a33925f55f4f692c1f52b9193911b5183b9 Mon Sep 17 00:00:00 2001 From: maryarm Date: Sat, 17 Jul 2021 18:52:51 +0200 Subject: [PATCH 06/59] Fix #7, replaced \r with "" in public.cert --- .../kotlin/co/nilin/mixchange/app/config/SecurityConfig.kt | 3 ++- .../co/nilin/mixchange/wallet/app/config/SecurityConfig.kt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/SecurityConfig.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/SecurityConfig.kt index f458ebfc3..2048a7616 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/SecurityConfig.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/SecurityConfig.kt @@ -1,8 +1,8 @@ package co.nilin.mixchange.app.config +import org.springframework.context.annotation.Bean import org.springframework.core.io.ClassPathResource import org.springframework.core.io.Resource -import org.springframework.context.annotation.Bean import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity import org.springframework.security.config.web.server.ServerHttpSecurity import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder @@ -35,6 +35,7 @@ class SecurityConfig { fun reactiveJwtDecoder(): ReactiveJwtDecoder? { val resource: Resource = ClassPathResource("/public.cert") val publicKey = String(FileCopyUtils.copyToByteArray(resource.inputStream)) + .replace("\r", "") .replace("-----BEGIN PUBLIC KEY-----\n", "") .replace("\n-----END PUBLIC KEY-----", "") val spec = X509EncodedKeySpec(Base64Utils.decodeFromString(publicKey)) diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/SecurityConfig.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/SecurityConfig.kt index a94a4225f..76331b91f 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/SecurityConfig.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/SecurityConfig.kt @@ -1,8 +1,8 @@ package co.nilin.mixchange.wallet.app.config +import org.springframework.context.annotation.Bean import org.springframework.core.io.ClassPathResource import org.springframework.core.io.Resource -import org.springframework.context.annotation.Bean import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity import org.springframework.security.config.web.server.ServerHttpSecurity import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder @@ -34,6 +34,7 @@ class SecurityConfig { fun reactiveJwtDecoder(): ReactiveJwtDecoder? { val resource: Resource = ClassPathResource("/public.cert") val publicKey = String(FileCopyUtils.copyToByteArray(resource.inputStream)) + .replace("\r", "") .replace("-----BEGIN PUBLIC KEY-----\n", "") .replace("\n-----END PUBLIC KEY-----", "") val spec = X509EncodedKeySpec(Base64Utils.decodeFromString(publicKey)) From 8fbcb1490c96e4a27d13b66a98deac07541dc956 Mon Sep 17 00:00:00 2001 From: maryarm Date: Sat, 17 Jul 2021 19:05:52 +0200 Subject: [PATCH 07/59] Fix #8, #9: add api module and change accountant module accordingly --- Accountant/accountant-app/pom.xml | 2 +- .../nilin/mixchange/app/config/AppConfig.kt | 88 +++-- .../app/scheduler/FinancialActionsJob.kt | 13 +- .../mixchange/app/scheduler/TempEventsJob.kt | 34 ++ .../src/main/resources/application-docker.yml | 24 +- Accountant/accountant-core/mvnw | 0 .../accountant/core/inout/OrderStatus.kt | 11 + .../accountant/core/inout/RichOrder.kt | 68 ++++ .../accountant/core/inout/RichTrade.kt | 85 +++++ .../mixchange/accountant/core/model/Order.kt | 41 ++- .../accountant/core/model/TempEvent.kt | 7 + .../service/FinancialActionJobManagerImpl.kt | 28 +- .../core/service/OrderManagerImpl.kt | 193 +++++++---- .../core/service/TradeManagerImpl.kt | 239 +++++++------ .../accountant/core/spi/RichOrderPublisher.kt | 7 + .../accountant/core/spi/RichTradePublisher.kt | 7 + .../accountant/core/spi/TempEventPersister.kt | 3 + .../core/service/OrderManagerImplTest.kt | 90 ++--- .../core/service/TradeManagerImplTest.kt | 94 +++-- .../accountant-eventlistener-kafka/mvnw | 0 .../kafka/consumer/OrderKafkaListener.kt | 5 +- .../accountant-persister-postgres/mvnw | 0 .../postgres/config/PostgresConfig.kt | 17 +- .../postgres/dao/TempEventRepository.kt | 11 +- .../postgres/impl/OrderPersisterImpl.kt | 77 +++-- .../postgres/impl/TempEventPersisterImpl.kt | 39 ++- .../accountant/postgres/model/OrderModel.kt | 47 +-- .../.gitignore | 0 .../mvnw | 0 .../mvnw.cmd | 0 .../pom.xml | 62 ++-- .../kafka/config/SubmitterKafkaConfig.kt | 78 +++++ .../kafka/service/RichOrderSubmitter.kt | 24 ++ .../kafka/service/RichTradeSubmitter.kt | 24 ++ .../kafka/service/TempEventSubmitter.kt | 13 +- .../kafka/config/SubmitterKafkaConfig.kt | 53 --- .../accountant-wallet-proxy/mvnw | 0 Accountant/pom.xml | 2 +- Api/.gitignore | 3 + Api/.idea/.gitignore | 8 + Api/api-app/.gitignore | 80 +++++ Api/api-app/Dockerfile | 5 + Api/api-app/pom.xml | 195 +++++++++++ .../kotlin/co/nilin/mixchange/app/ApiApp.kt | 13 + .../nilin/mixchange/app/config/AppConfig.kt | 61 ++++ .../mixchange/app/config/AppDispatchers.kt | 9 + .../src/main/resources/application-docker.yml | 13 + .../src/main/resources/application.yml | 28 ++ Api/api-app/src/main/resources/public.cert | 3 + Api/api-core/.gitignore | 4 + Api/api-core/mvnw | 322 ++++++++++++++++++ Api/api-core/mvnw.cmd | 182 ++++++++++ Api/api-core/pom.xml | 105 ++++++ .../api/core/inout/AllOrderRequest.kt | 10 + .../api/core/inout/CreateOrderRequest.kt | 19 ++ .../api/core/inout/CreateOrderResponse.kt | 21 ++ .../mixchange/api/core/inout/OrderEnums.kt | 37 ++ .../api/core/inout/OrderTradeData.kt | 10 + .../api/core/inout/QueryOrderRequest.kt | 7 + .../api/core/inout/QueryOrderResponse.kt | 25 ++ .../mixchange/api/core/inout/TradeRequest.kt | 10 + .../mixchange/api/core/inout/TradeResponse.kt | 19 ++ .../mixchange/api/core/spi/MEGatewayProxy.kt | 22 ++ .../mixchange/api/core/spi/OrderPersister.kt | 7 + .../mixchange/api/core/spi/TradePersister.kt | 7 + .../api/core/spi/UserQueryHandler.kt | 12 + .../mixchange/api/core/spi/WalletProxy.kt | 4 + Api/api-ports/.gitignore | 4 + Api/api-ports/api-binance-rest/.gitignore | 4 + Api/api-ports/api-binance-rest/mvnw | 322 ++++++++++++++++++ Api/api-ports/api-binance-rest/mvnw.cmd | 182 ++++++++++ Api/api-ports/api-binance-rest/pom.xml | 153 +++++++++ .../port/api/binance/config/RestConfig.kt | 23 ++ .../port/api/binance/config/SecurityConfig.kt | 45 +++ .../api/binance/config/WebClientConfig.kt | 25 ++ .../binance/controller/AccountController.kt | 245 +++++++++++++ .../binance/controller/FiltersController.kt | 7 + .../binance/controller/MarketController.kt | 8 + .../port/api/binance/proxy/WalletProxyImpl.kt | 59 ++++ .../api-eventlistener-kafka/.gitignore | 34 ++ Api/api-ports/api-eventlistener-kafka/mvnw | 322 ++++++++++++++++++ .../api-eventlistener-kafka/mvnw.cmd | 182 ++++++++++ Api/api-ports/api-eventlistener-kafka/pom.xml | 120 +++++++ .../port/api/kafka/config/ApiKafkaConfig.kt | 109 ++++++ .../api/kafka/consumer/EventKafkaListener.kt | 28 ++ .../api/kafka/consumer/OrderKafkaListener.kt | 29 ++ .../api/kafka/consumer/TradeKafkaListener.kt | 28 ++ .../port/api/kafka/spi/EventListener.kt | 9 + .../port/api/kafka/spi/RichOrderListener.kt | 8 + .../port/api/kafka/spi/RichTradeListener.kt | 8 + .../api-persister-postgres/.gitignore | 34 ++ Api/api-ports/api-persister-postgres/mvnw | 322 ++++++++++++++++++ Api/api-ports/api-persister-postgres/mvnw.cmd | 182 ++++++++++ Api/api-ports/api-persister-postgres/pom.xml | 124 +++++++ .../api/postgres/config/PostgresConfig.kt | 64 ++++ .../port/api/postgres/dao/OrderRepository.kt | 55 +++ .../port/api/postgres/dao/TradeRepository.kt | 35 ++ .../api/postgres/impl/OrderPersisterImpl.kt | 53 +++ .../api/postgres/impl/TradePersisterImpl.kt | 124 +++++++ .../api/postgres/impl/UserQueryHandlerImpl.kt | 133 ++++++++ .../port/api/postgres/model/OrderModel.kt | 38 +++ .../port/api/postgres/model/TradeModel.kt | 27 ++ .../port/api/postgres/util/EnumExtensions.kt | 45 +++ Api/pom.xml | 18 + Deployment/docker-compose.yml | 163 ++++++--- Deployment/nginx.conf | 66 ++-- .../eventlog-eventlistener-kafka/mvnw | 0 .../eventlog-persister-postgres/mvnw | 0 .../src/main/resources/application-docker.yml | 2 +- .../src/main/resources/application.yml | 2 +- MatchingEngine/matching-core/mvnw | 0 .../matching/core/engine/SimpleOrderBook.kt | 5 +- .../matching/core/eventh/events/TradeEvent.kt | 5 +- .../matching-eventlistener-kafka/mvnw | 0 .../matching-snapshots-redis/mvnw | 0 .../matching-submitter-kafka/mvnw | 0 .../mixchange/app/service/OrderService.kt | 70 ++-- .../gateway-port/order-submitter-kafka/mvnw | 0 .../ExtendedEventListenerProvider.kt | 7 +- .../auth/gateway/model/UserCreatedEvent.kt | 5 + Wallet/wallet-app/mvnw | 0 .../src/main/resources/application-docker.yml | 13 +- .../src/main/resources/application.yml | 5 +- Wallet/wallet-core/mvnw | 0 .../wallet-eventlistener-kafka/mvnw | 0 .../auth/gateway/model/UserCreatedEvent.kt | 4 + .../wallet/kafka/config/WalletKafkaConfig.kt | 11 +- .../consumer/UserCreatedKafkaListener.kt | 1 + .../wallet-persister-postgres/mvnw | 0 .../wallet/postgres/config/PostgresConfig.kt | 3 - 130 files changed, 5628 insertions(+), 669 deletions(-) create mode 100644 Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/scheduler/TempEventsJob.kt mode change 100755 => 100644 Accountant/accountant-core/mvnw create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/inout/OrderStatus.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/inout/RichOrder.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/inout/RichTrade.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/TempEvent.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/RichOrderPublisher.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/RichTradePublisher.kt mode change 100755 => 100644 Accountant/accountant-ports/accountant-eventlistener-kafka/mvnw mode change 100755 => 100644 Accountant/accountant-ports/accountant-persister-postgres/mvnw rename Accountant/accountant-ports/{accountant-tempsubmitter-kafka => accountant-submitter-kafka}/.gitignore (100%) rename Accountant/accountant-ports/{accountant-tempsubmitter-kafka => accountant-submitter-kafka}/mvnw (100%) mode change 100755 => 100644 rename Accountant/accountant-ports/{accountant-tempsubmitter-kafka => accountant-submitter-kafka}/mvnw.cmd (100%) rename Accountant/accountant-ports/{accountant-tempsubmitter-kafka => accountant-submitter-kafka}/pom.xml (65%) create mode 100644 Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/config/SubmitterKafkaConfig.kt create mode 100644 Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/RichOrderSubmitter.kt create mode 100644 Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/RichTradeSubmitter.kt rename Accountant/accountant-ports/{accountant-tempsubmitter-kafka => accountant-submitter-kafka}/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/TempEventSubmitter.kt (75%) delete mode 100644 Accountant/accountant-ports/accountant-tempsubmitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/config/SubmitterKafkaConfig.kt mode change 100755 => 100644 Accountant/accountant-ports/accountant-wallet-proxy/mvnw create mode 100644 Api/.gitignore create mode 100644 Api/.idea/.gitignore create mode 100644 Api/api-app/.gitignore create mode 100644 Api/api-app/Dockerfile create mode 100644 Api/api-app/pom.xml create mode 100644 Api/api-app/src/main/kotlin/co/nilin/mixchange/app/ApiApp.kt create mode 100644 Api/api-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt create mode 100644 Api/api-app/src/main/kotlin/co/nilin/mixchange/app/config/AppDispatchers.kt create mode 100644 Api/api-app/src/main/resources/application-docker.yml create mode 100644 Api/api-app/src/main/resources/application.yml create mode 100644 Api/api-app/src/main/resources/public.cert create mode 100644 Api/api-core/.gitignore create mode 100644 Api/api-core/mvnw create mode 100644 Api/api-core/mvnw.cmd create mode 100644 Api/api-core/pom.xml create mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/AllOrderRequest.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/CreateOrderRequest.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/CreateOrderResponse.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OrderEnums.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OrderTradeData.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/QueryOrderRequest.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/QueryOrderResponse.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/TradeRequest.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/TradeResponse.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/MEGatewayProxy.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/OrderPersister.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/TradePersister.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/UserQueryHandler.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/WalletProxy.kt create mode 100644 Api/api-ports/.gitignore create mode 100644 Api/api-ports/api-binance-rest/.gitignore create mode 100644 Api/api-ports/api-binance-rest/mvnw create mode 100644 Api/api-ports/api-binance-rest/mvnw.cmd create mode 100644 Api/api-ports/api-binance-rest/pom.xml create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/RestConfig.kt create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/SecurityConfig.kt create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/WebClientConfig.kt create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/AccountController.kt create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/FiltersController.kt create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/MarketController.kt create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/proxy/WalletProxyImpl.kt create mode 100644 Api/api-ports/api-eventlistener-kafka/.gitignore create mode 100644 Api/api-ports/api-eventlistener-kafka/mvnw create mode 100644 Api/api-ports/api-eventlistener-kafka/mvnw.cmd create mode 100644 Api/api-ports/api-eventlistener-kafka/pom.xml create mode 100644 Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/config/ApiKafkaConfig.kt create mode 100644 Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/consumer/EventKafkaListener.kt create mode 100644 Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/consumer/OrderKafkaListener.kt create mode 100644 Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/consumer/TradeKafkaListener.kt create mode 100644 Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/spi/EventListener.kt create mode 100644 Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/spi/RichOrderListener.kt create mode 100644 Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/spi/RichTradeListener.kt create mode 100644 Api/api-ports/api-persister-postgres/.gitignore create mode 100644 Api/api-ports/api-persister-postgres/mvnw create mode 100644 Api/api-ports/api-persister-postgres/mvnw.cmd create mode 100644 Api/api-ports/api-persister-postgres/pom.xml create mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/config/PostgresConfig.kt create mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/dao/OrderRepository.kt create mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/dao/TradeRepository.kt create mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/OrderPersisterImpl.kt create mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/TradePersisterImpl.kt create mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/UserQueryHandlerImpl.kt create mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/model/OrderModel.kt create mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/model/TradeModel.kt create mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/util/EnumExtensions.kt create mode 100644 Api/pom.xml mode change 100755 => 100644 EventLog/eventlog-ports/eventlog-eventlistener-kafka/mvnw mode change 100755 => 100644 EventLog/eventlog-ports/eventlog-persister-postgres/mvnw mode change 100755 => 100644 MatchingEngine/matching-core/mvnw mode change 100755 => 100644 MatchingEngine/matching-ports/matching-eventlistener-kafka/mvnw mode change 100755 => 100644 MatchingEngine/matching-ports/matching-snapshots-redis/mvnw mode change 100755 => 100644 MatchingEngine/matching-ports/matching-submitter-kafka/mvnw mode change 100755 => 100644 MatchingGateway/gateway-port/order-submitter-kafka/mvnw mode change 100755 => 100644 Wallet/wallet-app/mvnw mode change 100755 => 100644 Wallet/wallet-core/mvnw mode change 100755 => 100644 Wallet/wallet-ports/wallet-eventlistener-kafka/mvnw mode change 100755 => 100644 Wallet/wallet-ports/wallet-persister-postgres/mvnw diff --git a/Accountant/accountant-app/pom.xml b/Accountant/accountant-app/pom.xml index a77a86ad9..5a81b4930 100644 --- a/Accountant/accountant-app/pom.xml +++ b/Accountant/accountant-app/pom.xml @@ -53,7 +53,7 @@ co.nilin.mixchange - accountant-temp-submitter-kafka + accountant-submitter-kafka ${accountant.version} diff --git a/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt b/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt index 400b2cd24..7cf38b406 100644 --- a/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt +++ b/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt @@ -23,7 +23,6 @@ import org.springframework.beans.factory.annotation.Value import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.scheduling.annotation.EnableScheduling -import java.lang.IllegalArgumentException @Configuration @EnableScheduling @@ -39,43 +38,47 @@ class AppConfig { @Bean fun orderManager( - pairConfigLoader: PairConfigLoader, - financialActionPersister: FinancialActionPersister, - financeActionLoader: FinancialActionLoader, - orderPersister: OrderPersister, - tempEventPersister: TempEventPersister, - tempEventRepublisher: TempEventRepublisher + pairConfigLoader: PairConfigLoader, + financialActionPersister: FinancialActionPersister, + financeActionLoader: FinancialActionLoader, + orderPersister: OrderPersister, + tempEventPersister: TempEventPersister, + tempEventRepublisher: TempEventRepublisher, + richOrderPublisher: RichOrderPublisher ): OrderManager { return OrderManagerImpl( - pairConfigLoader, - financialActionPersister, - financeActionLoader, - orderPersister, - tempEventPersister, - tempEventRepublisher + pairConfigLoader, + financialActionPersister, + financeActionLoader, + orderPersister, + tempEventPersister, + tempEventRepublisher, + richOrderPublisher ) } @Bean fun tradeManager( - pairStaticRateLoader: PairStaticRateLoader, - financeActionPersister: FinancialActionPersister, - financeActionLoader: FinancialActionLoader, - orderPersister: OrderPersister, - tempEventPersister: TempEventPersister, - walletProxy: WalletProxy, - @Value("\${app.coin}") platformCoin: String, - @Value("\${app.address}") platformAddress: String + pairStaticRateLoader: PairStaticRateLoader, + financeActionPersister: FinancialActionPersister, + financeActionLoader: FinancialActionLoader, + orderPersister: OrderPersister, + tempEventPersister: TempEventPersister, + richTradePublisher: RichTradePublisher, + walletProxy: WalletProxy, + @Value("\${app.coin}") platformCoin: String, + @Value("\${app.address}") platformAddress: String ): TradeManager { return TradeManagerImpl( - pairStaticRateLoader, - financeActionPersister, - financeActionLoader, - orderPersister, - tempEventPersister, - walletProxy, - platformCoin, - platformAddress + pairStaticRateLoader, + financeActionPersister, + financeActionLoader, + orderPersister, + tempEventPersister, + richTradePublisher, + walletProxy, + platformCoin, + platformAddress ) } @@ -104,11 +107,6 @@ class AppConfig { return AccountantTempEventListener(orderManager, tradeManager) } - @Bean - fun orderKafkaListener(): OrderKafkaListener { - return OrderKafkaListener() - } - @Autowired fun configureOrderListener(orderKafkaListener: OrderKafkaListener, orderListener: OrderListener) { orderKafkaListener.addOrderListener(orderListener) @@ -148,16 +146,16 @@ class AppConfig { runBlocking(AppDispatchers.kafkaExecutor) { orderManager.handleRequestOrder( SubmitOrderEvent( - order.ouid, - order.uuid, - order.orderId, - order.pair, - order.price, - order.quantity, - 0, - order.direction, - order.matchConstraint, - order.orderType + order.ouid, + order.uuid, + order.orderId, + order.pair, + order.price, + order.quantity, + order.quantity, + order.direction, + order.matchConstraint, + order.orderType ) ) } @@ -196,7 +194,7 @@ class AppConfig { else if (coreEvent is CancelOrderEvent) orderManager.handleCancelOrder(coreEvent) else { - throw IllegalArgumentException("Event is not accepted ${coreEvent::class.java}") + println("Event is not accepted ${coreEvent::class.java}") } } println("onEvent") diff --git a/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/scheduler/FinancialActionsJob.kt b/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/scheduler/FinancialActionsJob.kt index e0d206482..be5c80564 100644 --- a/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/scheduler/FinancialActionsJob.kt +++ b/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/scheduler/FinancialActionsJob.kt @@ -2,19 +2,26 @@ package co.nilin.mixchange.app.scheduler import co.nilin.mixchange.accountant.core.api.FinancialActionJobManager import kotlinx.coroutines.runBlocking +import org.slf4j.LoggerFactory import org.springframework.scheduling.annotation.Scheduled import org.springframework.stereotype.Service @Service class FinancialActionsJob( - val financialActionJobManager: FinancialActionJobManager + val financialActionJobManager: FinancialActionJobManager ) { + private val log = LoggerFactory.getLogger(FinancialActionsJob::class.java) + @Scheduled(fixedDelay = 10000) fun processFinancialActions() { runBlocking { - //read unprocessed fa records and call transfer - financialActionJobManager.processFinancialActions(0, 100) + try { + //read unprocessed fa records and call transfer + financialActionJobManager.processFinancialActions(0, 100) + } catch (e: Exception) { + log.error("Job error!", e) + } } } diff --git a/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/scheduler/TempEventsJob.kt b/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/scheduler/TempEventsJob.kt new file mode 100644 index 000000000..27a5d8e77 --- /dev/null +++ b/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/scheduler/TempEventsJob.kt @@ -0,0 +1,34 @@ +package co.nilin.mixchange.app.scheduler + +import co.nilin.mixchange.accountant.core.spi.TempEventPersister +import co.nilin.mixchange.accountant.core.spi.TempEventRepublisher +import kotlinx.coroutines.runBlocking +import org.slf4j.LoggerFactory +import org.springframework.scheduling.annotation.Scheduled +import org.springframework.stereotype.Service + +@Service +class TempEventsJob( + val tempEventPersister: TempEventPersister, + val tempEventRepublisher: TempEventRepublisher, +) { + + private val log = LoggerFactory.getLogger(TempEventsJob::class.java) + + @Scheduled(fixedDelay = 1000) + fun processTempEventJobs() { + runBlocking { + try { + //read unprocessed temp event and call republish + val tempEvents = tempEventPersister.fetchTempEvents(0, 100); + if (tempEvents.isNotEmpty()) { + tempEventRepublisher.republish(tempEvents.map { event -> event.eventBody }) + tempEventPersister.removeTempEvents(tempEvents) + } + } catch (e: Exception) { + log.error("Job error!", e) + } + } + } + +} \ No newline at end of file diff --git a/Accountant/accountant-app/src/main/resources/application-docker.yml b/Accountant/accountant-app/src/main/resources/application-docker.yml index d679f9dd3..238e144fa 100644 --- a/Accountant/accountant-app/src/main/resources/application-docker.yml +++ b/Accountant/accountant-app/src/main/resources/application-docker.yml @@ -1,34 +1,12 @@ -server.port: 8089 spring: - application: - name: opex-accountant - main: - allow-bean-definition-overriding: false kafka: bootstrap-servers: ${KAFKA_IP_PORT} - consumer: - group-id: accountant redis: - hostname: ${REDIS_HOST} - port: 6379 + host: ${REDIS_HOST} r2dbc: url: r2dbc:postgresql://${DB_IP_PORT}/opex_accountant username: opex password: hiopex - initialization-mode: always cloud: - bootstrap: - enabled: true consul: host: ${CONSUL_HOST} - port: 8500 - discovery: - #healthCheckPath: ${management.context-path}/health - instance-id: ${spring.application.name}:${server.port} - healthCheckInterval: 20s - prefer-ip-address: true -app: - coin: nln - address: 1 - wallet: - url: lb://opex-wallet/ diff --git a/Accountant/accountant-core/mvnw b/Accountant/accountant-core/mvnw old mode 100755 new mode 100644 diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/inout/OrderStatus.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/inout/OrderStatus.kt new file mode 100644 index 000000000..40b6b9dbb --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/inout/OrderStatus.kt @@ -0,0 +1,11 @@ +package co.nilin.mixchange.accountant.core.inout + +enum class OrderStatus(val code: Int) { + REQUESTED(0), + NEW(1), //The order has been accepted by the engine. + PARTIALLY_FILLED(4), //A part of the order has been filled. + FILLED(5), //The order has been completed. + CANCELED(2), //The order has been canceled by the user. + REJECTED(3), //The order was not accepted by the engine and not processed. + EXPIRED(6) //The order was canceled according to the order type's rules (e.g. LIMIT FOK orders with no fill, LIMIT IOC or MARKET orders that partially fill) or by the exchange, (e.g. orders canceled during liquidation, orders canceled during maintenance) +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/inout/RichOrder.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/inout/RichOrder.kt new file mode 100644 index 000000000..d0181cc23 --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/inout/RichOrder.kt @@ -0,0 +1,68 @@ +package co.nilin.mixchange.accountant.core.inout + +import co.nilin.mixchange.matching.core.model.MatchConstraint +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.OrderType +import java.math.BigDecimal + +class RichOrder() { + var orderId: Long? = 0 + lateinit var pair: String + lateinit var ouid: String + lateinit var uuid: String + lateinit var userLevel: String + lateinit var makerFee: BigDecimal + lateinit var takerFee: BigDecimal + lateinit var leftSideFraction: BigDecimal + lateinit var rightSideFraction: BigDecimal + lateinit var direction: OrderDirection + lateinit var constraint: MatchConstraint + lateinit var type: OrderType + lateinit var price: BigDecimal; + lateinit var quantity: BigDecimal; + lateinit var executedQuantity: BigDecimal; + lateinit var accumulativeQuoteQty: BigDecimal; + lateinit var quoteQuantity: BigDecimal; + var status: Int = 0; + + constructor( + orderId: Long?, + pair: String, + ouid: String, + uuid: String, + userLevel: String, + makerFee: BigDecimal, + takerFee: BigDecimal, + leftSideFraction: BigDecimal, + rightSideFraction: BigDecimal, + direction: OrderDirection, + constraint: MatchConstraint, + type: OrderType, + price: BigDecimal, + quantity: BigDecimal, + quoteQuantity: BigDecimal, + executedQuantity: BigDecimal, + accumulativeQuoteQty: BigDecimal, + + status: Int + ) : this() { + this.orderId = orderId + this.pair = pair + this.ouid = ouid + this.uuid = uuid + this.userLevel = userLevel + this.makerFee = makerFee + this.takerFee = takerFee + this.leftSideFraction = leftSideFraction + this.rightSideFraction = rightSideFraction + this.direction = direction + this.constraint = constraint + this.type = type + this.price = price + this.quantity = quantity + this.executedQuantity = executedQuantity + this.accumulativeQuoteQty = accumulativeQuoteQty + this.quoteQuantity = quoteQuantity + this.status = status + } +} diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/inout/RichTrade.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/inout/RichTrade.kt new file mode 100644 index 000000000..5c6b6ac5a --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/inout/RichTrade.kt @@ -0,0 +1,85 @@ +package co.nilin.mixchange.accountant.core.inout + +import co.nilin.mixchange.matching.core.model.OrderDirection +import java.math.BigDecimal +import java.time.LocalDateTime + +class RichTrade() { + var id: Long = 0 + lateinit var pair: String + lateinit var takerOuid: String + lateinit var takerUuid: String + var takerOrderId: Long = 0 + lateinit var takerDirection: OrderDirection + lateinit var takerPrice: BigDecimal + lateinit var takerQuantity: BigDecimal + lateinit var takerQuoteQuantity: BigDecimal + lateinit var takerRemainedQuantity: BigDecimal + lateinit var takerCommision: BigDecimal + lateinit var takerCommisionAsset: String + lateinit var makerOuid: String + lateinit var makerUuid: String + var makerOrderId: Long = 0 + lateinit var makerDirection: OrderDirection + lateinit var makerPrice: BigDecimal + lateinit var makerQuantity: BigDecimal + lateinit var makerQuoteQuantity: BigDecimal + lateinit var makerRemainedQuantity: BigDecimal + lateinit var matchedQuantity: BigDecimal + lateinit var makerCommision: BigDecimal + lateinit var makerCommisionAsset: String + + lateinit var tradeDateTime: LocalDateTime + + constructor( + id: Long, + pair: String, + takerOuid: String, + takerUuid: String, + takerOrderId: Long, + takerDirection: OrderDirection, + takerPrice: BigDecimal, + takerQuantity: BigDecimal, + takerQuoteQuantity: BigDecimal, + takerRemainedQuantity: BigDecimal, + takerCommision: BigDecimal, + takerCommisionAsset: String, + makerOuid: String, + makerUuid: String, + makerOrderId: Long, + makerDirection: OrderDirection, + makerPrice: BigDecimal, + makerQuantity: BigDecimal, + makerQuoteQuantity: BigDecimal, + makerRemainedQuantity: BigDecimal, + makerCommision: BigDecimal, + makerCommisionAsset: String, + matchedQuantity: BigDecimal, + tradeDateTime: LocalDateTime + ) : this() { + this.id = id + this.pair = pair + this.takerOuid = takerOuid + this.takerUuid = takerUuid + this.takerOrderId = takerOrderId + this.takerDirection = takerDirection + this.takerPrice = takerPrice + this.takerQuantity = takerQuantity + this.takerQuoteQuantity = takerQuoteQuantity + this.takerRemainedQuantity = takerRemainedQuantity + this.takerCommision = takerCommision + this.takerCommisionAsset = takerCommisionAsset + this.makerOuid = makerOuid + this.makerUuid = makerUuid + this.makerOrderId = makerOrderId + this.makerDirection = makerDirection + this.makerPrice = makerPrice + this.makerQuantity = makerQuantity + this.makerQuoteQuantity = makerQuoteQuantity + this.makerRemainedQuantity = makerRemainedQuantity + this.matchedQuantity = matchedQuantity + this.makerCommision = makerCommision + this.makerCommisionAsset = makerCommisionAsset + this.tradeDateTime = tradeDateTime + } +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/Order.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/Order.kt index 1a3216988..b333bb14a 100644 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/Order.kt +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/Order.kt @@ -1,24 +1,31 @@ package co.nilin.mixchange.accountant.core.model +import co.nilin.mixchange.matching.core.model.MatchConstraint import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.OrderType import java.math.BigDecimal data class Order( - val pair: String, - val ouid: String, - var matchingEngineId: Long?, - val makerFee: Double, - val takerFee: Double, - val leftSideFraction: Double, - val rightSideFraction: Double, - val uuid: String, - val userLevel: String, - val direction: OrderDirection, - val price: Long, - val quantity: Long, - val filledQuantity: Long, - val firstTransferAmount: BigDecimal, - var remainedTransferAmount: BigDecimal, - var status: Int, - val id: Long? = null + val pair: String, + val ouid: String, + var matchingEngineId: Long?, + val makerFee: Double, + val takerFee: Double, + val leftSideFraction: Double, + val rightSideFraction: Double, + val uuid: String, + val userLevel: String, + val direction: OrderDirection, + val matchConstraint: MatchConstraint, + val orderType: OrderType, + val price: Long, + val quantity: Long, + val filledQuantity: Long, + val origPrice: BigDecimal, + val origQuantity: BigDecimal, + val filledOrigQuantity: BigDecimal, + val firstTransferAmount: BigDecimal, + var remainedTransferAmount: BigDecimal, + var status: Int, + val id: Long? = null ) \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/TempEvent.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/TempEvent.kt new file mode 100644 index 000000000..92b978848 --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/TempEvent.kt @@ -0,0 +1,7 @@ +package co.nilin.mixchange.accountant.core.model + +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +import java.time.LocalDateTime + +data class TempEvent(val id: Long, val ouid: String, val eventBody: CoreEvent, val eventDate: +LocalDateTime) \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/FinancialActionJobManagerImpl.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/FinancialActionJobManagerImpl.kt index d447bd48d..a3fabf21d 100644 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/FinancialActionJobManagerImpl.kt +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/FinancialActionJobManagerImpl.kt @@ -2,29 +2,31 @@ package co.nilin.mixchange.accountant.core.service import co.nilin.mixchange.accountant.core.api.FinancialActionJobManager import co.nilin.mixchange.accountant.core.model.FinancialActionStatus -import co.nilin.mixchange.accountant.core.spi.FinancialActionPersister import co.nilin.mixchange.accountant.core.spi.FinancialActionLoader +import co.nilin.mixchange.accountant.core.spi.FinancialActionPersister import co.nilin.mixchange.accountant.core.spi.WalletProxy +import org.slf4j.LoggerFactory + -class FinancialActionJobManagerImpl(val financialActionLoader: FinancialActionLoader - , val financialActionPersister: FinancialActionPersister - , val walletProxy: WalletProxy): FinancialActionJobManager { +class FinancialActionJobManagerImpl(val financialActionLoader: FinancialActionLoader, val financialActionPersister: FinancialActionPersister, val walletProxy: WalletProxy) : FinancialActionJobManager { + private val log = LoggerFactory.getLogger(FinancialActionJobManagerImpl::class.java) override suspend fun processFinancialActions(offset: Long, size: Long) { val factions = financialActionLoader.loadUnprocessed(offset, size) factions.forEach { faction -> try { walletProxy.transfer( - faction.symbol, - faction.senderWalletType, - faction.sender, - faction.receiverWalletType, - faction.receiver, - faction.amount, - faction.eventType + faction.pointer, - null + faction.symbol, + faction.senderWalletType, + faction.sender, + faction.receiverWalletType, + faction.receiver, + faction.amount, + faction.eventType + faction.pointer, + null ) financialActionPersister.updateStatus(faction, FinancialActionStatus.PROCESSED) - } catch (e: Exception){ + } catch (e: Exception) { + log.error("financial job error", e) financialActionPersister.updateStatus(faction, FinancialActionStatus.ERROR) } } diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImpl.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImpl.kt index 3b3842454..637abda1d 100644 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImpl.kt +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImpl.kt @@ -1,6 +1,8 @@ package co.nilin.mixchange.accountant.core.service import co.nilin.mixchange.accountant.core.api.OrderManager +import co.nilin.mixchange.accountant.core.inout.OrderStatus +import co.nilin.mixchange.accountant.core.inout.RichOrder import co.nilin.mixchange.accountant.core.model.FinancialAction import co.nilin.mixchange.accountant.core.model.Order import co.nilin.mixchange.accountant.core.spi.* @@ -11,12 +13,13 @@ import java.math.BigDecimal import java.time.LocalDateTime open class OrderManagerImpl( - val pairConfigLoader: PairConfigLoader, - val financialActionPersister: FinancialActionPersister, - val financeActionLoader: FinancialActionLoader, - val orderPersister: OrderPersister, - val tempEventPersister: TempEventPersister, - val tempEventRepublisher: TempEventRepublisher + val pairConfigLoader: PairConfigLoader, + val financialActionPersister: FinancialActionPersister, + val financeActionLoader: FinancialActionLoader, + val orderPersister: OrderPersister, + val tempEventPersister: TempEventPersister, + val tempEventRepublisher: TempEventRepublisher, + val richOrderPublisher: RichOrderPublisher ) : OrderManager { @Transactional override suspend fun handleRequestOrder(submitOrderEvent: SubmitOrderEvent): List { @@ -38,73 +41,114 @@ open class OrderManagerImpl( amount for buy (bid): quantity * price */ val financialAction = - FinancialAction( - null, - SubmitOrderEvent::class.simpleName!!, - submitOrderEvent.ouid, - symbol, - if (submitOrderEvent.direction == OrderDirection.ASK) { - BigDecimal(submitOrderEvent.quantity).multiply(pairFeeConfig.pairConfig.leftSideFraction.toBigDecimal()) - } else { - BigDecimal(submitOrderEvent.quantity).multiply(pairFeeConfig.pairConfig.leftSideFraction.toBigDecimal()) - .multiply(submitOrderEvent.price.toBigDecimal()) - .multiply(pairFeeConfig.pairConfig.rightSideFraction.toBigDecimal()) - }, - submitOrderEvent.uuid, - "main", - submitOrderEvent.uuid, - "exchange", - LocalDateTime.now() - ) + FinancialAction( + null, + SubmitOrderEvent::class.simpleName!!, + submitOrderEvent.ouid, + symbol, + if (submitOrderEvent.direction == OrderDirection.ASK) { + BigDecimal(submitOrderEvent.quantity).multiply(pairFeeConfig.pairConfig.leftSideFraction.toBigDecimal()) + } else { + BigDecimal(submitOrderEvent.quantity).multiply(pairFeeConfig.pairConfig.leftSideFraction.toBigDecimal()) + .multiply(submitOrderEvent.price.toBigDecimal()) + .multiply(pairFeeConfig.pairConfig.rightSideFraction.toBigDecimal()) + }, + submitOrderEvent.uuid, + "main", + submitOrderEvent.uuid, + "exchange", + LocalDateTime.now() + ) //store order (ouid, uuid, fees, userlevel, pair, direction, price, quantity, filledQ, status, transfered) orderPersister.save( - Order( - submitOrderEvent.pair.toString(), - submitOrderEvent.ouid, - null, - makerFee, - takerFee, - pairFeeConfig.pairConfig.leftSideFraction, - pairFeeConfig.pairConfig.rightSideFraction, - submitOrderEvent.uuid, - "", - submitOrderEvent.direction, - submitOrderEvent.price, - submitOrderEvent.quantity, - submitOrderEvent.quantity - submitOrderEvent.remainedQuantity, - financialAction.amount, - financialAction.amount, - 0 - ) + Order( + submitOrderEvent.pair.toString(), + submitOrderEvent.ouid, + null, + makerFee, + takerFee, + pairFeeConfig.pairConfig.leftSideFraction, + pairFeeConfig.pairConfig.rightSideFraction, + submitOrderEvent.uuid, + "", + submitOrderEvent.direction, + submitOrderEvent.matchConstraint, + submitOrderEvent.orderType, + submitOrderEvent.price, + submitOrderEvent.quantity, + submitOrderEvent.quantity - submitOrderEvent.remainedQuantity, + submitOrderEvent.price.toBigDecimal() + .multiply(pairFeeConfig.pairConfig.rightSideFraction.toBigDecimal()), + submitOrderEvent.quantity.toBigDecimal() + .multiply(pairFeeConfig.pairConfig.leftSideFraction.toBigDecimal()), + BigDecimal(submitOrderEvent.quantity - submitOrderEvent.remainedQuantity).multiply(pairFeeConfig.pairConfig.leftSideFraction.toBigDecimal()), + financialAction.amount, + financialAction.amount, + OrderStatus.REQUESTED.code + ) ) - val ret = financialActionPersister.persist(listOf(financialAction)) - println("republish " + submitOrderEvent.ouid) - tempEventRepublisher.republish(tempEventPersister.loadTempEvents(submitOrderEvent.ouid)) - println("remove temp " + submitOrderEvent.ouid) - tempEventPersister.removeTempEvents(submitOrderEvent.ouid) - return ret + return financialActionPersister.persist(listOf(financialAction)) } + @Transactional override suspend fun handleNewOrder(createOrderEvent: CreateOrderEvent): List { //update order add id to other fields val order = orderPersister.load(createOrderEvent.ouid) - if ( order != null) { + if (order != null) { order.matchingEngineId = createOrderEvent.orderId orderPersister.save(order) + //new order accepted by engine + publishRichOrder(order, createOrderEvent.remainedQuantity.toBigDecimal()) } else { tempEventPersister.saveTempEvent(createOrderEvent.ouid, createOrderEvent) } return emptyList() } + private suspend fun publishRichOrder( + order: Order, remainedQuantity: BigDecimal, status: OrderStatus? = null + ) { + richOrderPublisher.publish( + RichOrder( + order.id, + order.pair, + order.ouid, + order.uuid, + order.userLevel, + order.makerFee.toBigDecimal(), + order.takerFee.toBigDecimal(), + order.leftSideFraction.toBigDecimal(), + order.rightSideFraction.toBigDecimal(), + order.direction, + order.matchConstraint, + order.orderType, + order.origPrice, + order.origQuantity, + order.origPrice.multiply(order.origQuantity), + order.quantity.toBigDecimal().subtract(remainedQuantity).multiply(order.leftSideFraction.toBigDecimal()), + order.origPrice.multiply( + order.quantity.toBigDecimal().subtract(remainedQuantity) + ), + (status ?: if (remainedQuantity.compareTo(BigDecimal.ZERO) == 0) { + OrderStatus.FILLED.code + } else if (remainedQuantity.compareTo(order.quantity.toBigDecimal()) == 0) { + OrderStatus.NEW.code + } else { + OrderStatus.PARTIALLY_FILLED.code + }) as Int + ) + ) + } + override suspend fun handleUpdateOrder(updatedOrderEvent: UpdatedOrderEvent): List { TODO("Not yet implemented") } + @Transactional override suspend fun handleRejectOrder(rejectOrderEvent: RejectOrderEvent): List { //order by ouid val order = orderPersister.load(rejectOrderEvent.ouid) - if ( order == null ){ + if (order == null) { tempEventPersister.saveTempEvent(rejectOrderEvent.ouid, rejectOrderEvent) return emptyList() } @@ -118,27 +162,29 @@ open class OrderManagerImpl( val parentFinancialAction = financeActionLoader.findLast(rejectOrderEvent.uuid, rejectOrderEvent.ouid) //create fa for transfer remaining transfered uuid symbol exchange wallet to uuid main exchange wallet val financialAction = FinancialAction( - parentFinancialAction, - RejectOrderEvent::class.simpleName!!, - rejectOrderEvent.ouid, - symbol, - order.remainedTransferAmount, - rejectOrderEvent.uuid, - "exchange", - rejectOrderEvent.uuid, - "main", - LocalDateTime.now() + parentFinancialAction, + RejectOrderEvent::class.simpleName!!, + rejectOrderEvent.ouid, + symbol, + order.remainedTransferAmount, + rejectOrderEvent.uuid, + "exchange", + rejectOrderEvent.uuid, + "main", + LocalDateTime.now() ) //update order status - order.status = 3 + order.status = OrderStatus.REJECTED.code orderPersister.save(order) + publishRichOrder(order, order.quantity.toBigDecimal(), OrderStatus.REJECTED) return financialActionPersister.persist(listOf(financialAction)) } + @Transactional override suspend fun handleCancelOrder(cancelOrderEvent: CancelOrderEvent): List { //order by ouid val order = orderPersister.load(cancelOrderEvent.ouid) - if ( order == null ){ + if (order == null) { tempEventPersister.saveTempEvent(cancelOrderEvent.ouid, cancelOrderEvent) return emptyList() } @@ -152,20 +198,21 @@ open class OrderManagerImpl( val parentFinancialAction = financeActionLoader.findLast(cancelOrderEvent.uuid, cancelOrderEvent.ouid) //create fa for transfer remaining transfered uuid symbol exchange wallet to uuid main exchange wallet val financialAction = FinancialAction( - parentFinancialAction, - RejectOrderEvent::class.simpleName!!, - cancelOrderEvent.ouid, - symbol, - order.remainedTransferAmount, - cancelOrderEvent.uuid, - "exchange", - cancelOrderEvent.uuid, - "main", - LocalDateTime.now() + parentFinancialAction, + RejectOrderEvent::class.simpleName!!, + cancelOrderEvent.ouid, + symbol, + order.remainedTransferAmount, + cancelOrderEvent.uuid, + "exchange", + cancelOrderEvent.uuid, + "main", + LocalDateTime.now() ) //update order status - order.status = 2 + order.status = OrderStatus.CANCELED.code orderPersister.save(order) + publishRichOrder(order, cancelOrderEvent.quantity.toBigDecimal(), OrderStatus.CANCELED) return financialActionPersister.persist(listOf(financialAction)) } } \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/TradeManagerImpl.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/TradeManagerImpl.kt index 1c675fc9b..7cdd2054b 100644 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/TradeManagerImpl.kt +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/TradeManagerImpl.kt @@ -1,25 +1,26 @@ package co.nilin.mixchange.accountant.core.service import co.nilin.mixchange.accountant.core.api.TradeManager +import co.nilin.mixchange.accountant.core.inout.RichTrade import co.nilin.mixchange.accountant.core.model.FinancialAction import co.nilin.mixchange.accountant.core.spi.* import co.nilin.mixchange.matching.core.eventh.events.TradeEvent import co.nilin.mixchange.matching.core.model.OrderDirection import org.slf4j.LoggerFactory import org.springframework.transaction.annotation.Transactional -import java.lang.RuntimeException import java.math.BigDecimal import java.time.LocalDateTime open class TradeManagerImpl( - val pairStaticRateLoader: PairStaticRateLoader, - val financeActionPersister: FinancialActionPersister, - val financeActionLoader: FinancialActionLoader, - val orderPersister: OrderPersister, - val tempEventPersister: TempEventPersister, - val walletProxy: WalletProxy, - val platformCoin: String, - val platformAddress: String + val pairStaticRateLoader: PairStaticRateLoader, + val financeActionPersister: FinancialActionPersister, + val financeActionLoader: FinancialActionLoader, + val orderPersister: OrderPersister, + val tempEventPersister: TempEventPersister, + val richTradePublisher: RichTradePublisher, + val walletProxy: WalletProxy, + val platformCoin: String, + val platformAddress: String ) : TradeManager { private val log = LoggerFactory.getLogger(TradeManagerImpl::class.java) @@ -32,11 +33,11 @@ open class TradeManagerImpl( val takerOrder = orderPersister.load(trade.takerOuid) //maker order by ouid val makerOrder = orderPersister.load(trade.makerOuid) - if ( takerOrder == null || makerOrder == null ){ - if ( takerOrder == null ){ + if (takerOrder == null || makerOrder == null) { + if (takerOrder == null) { tempEventPersister.saveTempEvent(trade.takerOuid, trade) } - if ( makerOrder == null ){ + if (makerOrder == null) { tempEventPersister.saveTempEvent(trade.makerOuid, trade) } return emptyList() @@ -52,7 +53,7 @@ open class TradeManagerImpl( trade.matchedQuantity.toBigDecimal().multiply(takerOrder.leftSideFraction.toBigDecimal()) } else { trade.matchedQuantity.toBigDecimal().multiply(takerOrder.leftSideFraction.toBigDecimal()) - .multiply(trade.makerPrice.toBigDecimal()).multiply(takerOrder.rightSideFraction.toBigDecimal()) + .multiply(trade.makerPrice.toBigDecimal()).multiply(takerOrder.rightSideFraction.toBigDecimal()) } val takerPCFeeCoefficient: Double = if (takerOrder.direction == OrderDirection.ASK) { @@ -65,7 +66,7 @@ open class TradeManagerImpl( trade.matchedQuantity.toBigDecimal().multiply(makerOrder.leftSideFraction.toBigDecimal()) } else { trade.matchedQuantity.toBigDecimal().multiply(makerOrder.leftSideFraction.toBigDecimal()) - .multiply(trade.takerPrice.toBigDecimal()).multiply(makerOrder.rightSideFraction.toBigDecimal()) + .multiply(trade.takerPrice.toBigDecimal()).multiply(makerOrder.rightSideFraction.toBigDecimal()) } val makerPCFeeCoefficient: Double = if (makerOrder.direction == OrderDirection.ASK) { @@ -85,8 +86,8 @@ open class TradeManagerImpl( //calculate maker fee val makerFee = makerOrder.makerFee val makerTotalFeeWithPlatformCoin = takerMatchedAmount - .multiply(makerFee.toBigDecimal()) - .multiply(makerPCFeeCoefficient.toBigDecimal()) + .multiply(makerFee.toBigDecimal()) + .multiply(makerPCFeeCoefficient.toBigDecimal()) //check if maker uuid can pay the fee with platform coin //create fa for transfer taker uuid symbol exchange wallet to maker symbol main wallet /* @@ -94,59 +95,58 @@ open class TradeManagerImpl( amount for buy (bid): match_quantity * maker price (if not pay by platform coin then - maker fee) */ val takerTransferAction = FinancialAction( - takerParentFinancialAction, - TradeEvent::class.simpleName!!, - trade.takerOuid, - if (takerOrder.direction == OrderDirection.ASK) { - trade.pair.leftSideName - } else { - trade.pair.rightSideName - }, - takerMatchedAmount, - trade.takerUuid, - "exchange", - trade.makerUuid, - "main", - LocalDateTime.now() - ) - log.info("trade event takerTransferAction {}", takerTransferAction) - financialActions.add(takerTransferAction) - if (makerTotalFeeWithPlatformCoin > BigDecimal.ZERO && - walletProxy.canFulfil(platformCoin, "main", trade.makerUuid, makerTotalFeeWithPlatformCoin)) { - val makerFeeAction = FinancialAction( - makerParentFinancialAction, - TradeEvent::class.simpleName!!, - trade.makerOuid, - platformCoin, - makerTotalFeeWithPlatformCoin, - trade.makerUuid, - "main", - platformAddress, - "exchange", - LocalDateTime.now() - ) - log.info("trade event makerFeeAction {}", makerFeeAction) - financialActions.add(makerFeeAction) - } else { - val makerFeeAction = FinancialAction( - makerParentFinancialAction, + takerParentFinancialAction, TradeEvent::class.simpleName!!, - trade.makerOuid, + trade.takerOuid, if (takerOrder.direction == OrderDirection.ASK) { trade.pair.leftSideName } else { trade.pair.rightSideName }, - takerMatchedAmount.multiply(makerFee.toBigDecimal()), + takerMatchedAmount, + trade.takerUuid, + "exchange", trade.makerUuid, "main", - platformAddress, - "exchange", LocalDateTime.now() + ) + log.info("trade event takerTransferAction {}", takerTransferAction) + financialActions.add(takerTransferAction) + val makerFeeAction = if (makerTotalFeeWithPlatformCoin > BigDecimal.ZERO && + walletProxy.canFulfil(platformCoin, "main", trade.makerUuid, makerTotalFeeWithPlatformCoin) + ) { + FinancialAction( + makerParentFinancialAction, + TradeEvent::class.simpleName!!, + trade.takerOuid, + platformCoin, + makerTotalFeeWithPlatformCoin, + trade.makerUuid, + "main", + platformAddress, + "exchange", + LocalDateTime.now() + ) + } else { + FinancialAction( + makerParentFinancialAction, + TradeEvent::class.simpleName!!, + trade.takerOuid, + if (takerOrder.direction == OrderDirection.ASK) { + trade.pair.leftSideName + } else { + trade.pair.rightSideName + }, + takerMatchedAmount.multiply(makerFee.toBigDecimal()), + trade.makerUuid, + "main", + platformAddress, + "exchange", + LocalDateTime.now() ) - log.info("trade event makerFeeAction {}", makerFeeAction) - financialActions.add(makerFeeAction) } + log.info("trade event makerFeeAction {}", makerFeeAction) + financialActions.add(makerFeeAction) //update taker order status takerOrder.remainedTransferAmount -= takerMatchedAmount if (takerOrder.filledQuantity == takerOrder.quantity) { @@ -158,8 +158,8 @@ open class TradeManagerImpl( //calculate taker fee val takerFee = takerOrder.takerFee val takerTotalFeeWithPlatformCoin = takerMatchedAmount - .multiply(takerFee.toBigDecimal()) - .multiply(takerPCFeeCoefficient.toBigDecimal()) + .multiply(takerFee.toBigDecimal()) + .multiply(takerPCFeeCoefficient.toBigDecimal()) //create fa for transfer makeruuid symbol exchange wallet to taker symbol main wallet /* @@ -167,66 +167,64 @@ open class TradeManagerImpl( amount for buy (bid): match_quantity * maker price (if not pay by platform coin then - taker fee) */ val makerTransferAction = FinancialAction( - makerParentFinancialAction, - TradeEvent::class.simpleName!!, - trade.takerOuid, - if (makerOrder.direction == OrderDirection.ASK) { - trade.pair.leftSideName - } else { - trade.pair.rightSideName - }, - makerMatchedAmount, - trade.makerUuid, - "exchange", - trade.takerUuid, - "main", - LocalDateTime.now() - ) - log.info("trade event makerTransferAction {}", makerTransferAction) - financialActions.add(makerTransferAction) - //check if taker uuid can pay the fee with platform coin - if (takerTotalFeeWithPlatformCoin > BigDecimal.ZERO && - walletProxy.canFulfil(platformCoin, "main", trade.takerUuid, takerTotalFeeWithPlatformCoin)) { - val takerFeeAction = FinancialAction( - takerParentFinancialAction, + makerParentFinancialAction, TradeEvent::class.simpleName!!, - trade.takerOuid, + trade.makerOuid, if (makerOrder.direction == OrderDirection.ASK) { trade.pair.leftSideName } else { trade.pair.rightSideName }, - takerTotalFeeWithPlatformCoin, + makerMatchedAmount, + trade.makerUuid, + "exchange", trade.takerUuid, "main", - platformAddress, - "", LocalDateTime.now() + ) + log.info("trade event makerTransferAction {}", makerTransferAction) + financialActions.add(makerTransferAction) + //check if taker uuid can pay the fee with platform coin + val takerFeeAction = if (takerTotalFeeWithPlatformCoin > BigDecimal.ZERO && + walletProxy.canFulfil(platformCoin, "main", trade.takerUuid, takerTotalFeeWithPlatformCoin) + ) { + FinancialAction( + takerParentFinancialAction, + TradeEvent::class.simpleName!!, + trade.makerOuid, + if (makerOrder.direction == OrderDirection.ASK) { + trade.pair.leftSideName + } else { + trade.pair.rightSideName + }, + takerTotalFeeWithPlatformCoin, + trade.takerUuid, + "main", + platformAddress, + "", + LocalDateTime.now() ) - log.info("trade event takerFeeAction {}", takerFeeAction) - financialActions.add(takerFeeAction) - } else { - val takerFeeAction = FinancialAction( - takerParentFinancialAction, - TradeEvent::class.simpleName!!, - trade.takerOuid, - if (makerOrder.direction == OrderDirection.ASK) { - trade.pair.leftSideName - } else { - trade.pair.rightSideName - }, - makerMatchedAmount.multiply(takerFee.toBigDecimal()), - trade.takerUuid, - "main", - platformAddress, - "exchange", - LocalDateTime.now() + FinancialAction( + takerParentFinancialAction, + TradeEvent::class.simpleName!!, + trade.makerOuid, + if (makerOrder.direction == OrderDirection.ASK) { + trade.pair.leftSideName + } else { + trade.pair.rightSideName + }, + makerMatchedAmount.multiply(takerFee.toBigDecimal()), + trade.takerUuid, + "main", + platformAddress, + "exchange", + LocalDateTime.now() ) - log.info("trade event takerFeeAction {}", takerFeeAction) - financialActions.add(takerFeeAction) } + log.info("trade event takerFeeAction {}", takerFeeAction) + financialActions.add(takerFeeAction) //update maker order status makerOrder.remainedTransferAmount -= makerMatchedAmount if (makerOrder.filledQuantity == makerOrder.quantity) { @@ -234,6 +232,35 @@ open class TradeManagerImpl( } orderPersister.save(makerOrder) log.info("maker order saved {}", makerOrder) + richTradePublisher.publish( + RichTrade( + trade.tradeId, + trade.pair.toString(), + trade.takerOuid, + trade.takerUuid, + trade.takerOrderId, + trade.takerDirection, + trade.takerPrice.toBigDecimal().multiply(takerOrder.rightSideFraction.toBigDecimal()), + takerOrder.origQuantity, + takerOrder.origPrice.multiply(takerOrder.origQuantity), + trade.takerRemainedQuantity.toBigDecimal().multiply(takerOrder.leftSideFraction.toBigDecimal()), + takerFeeAction.amount, + takerFeeAction.symbol, + trade.makerOuid, + trade.makerUuid, + trade.makerOrderId, + trade.makerDirection, + trade.makerPrice.toBigDecimal().multiply(makerOrder.rightSideFraction.toBigDecimal()), + makerOrder.origQuantity, + makerOrder.origPrice.multiply(makerOrder.origQuantity), + trade.makerRemainedQuantity.toBigDecimal().multiply(makerOrder.leftSideFraction.toBigDecimal()), + makerFeeAction.amount, + makerFeeAction.symbol, + trade.matchedQuantity.toBigDecimal().multiply(makerOrder.leftSideFraction.toBigDecimal()), + trade.eventDate + ) + + ) return financeActionPersister.persist(financialActions) } } \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/RichOrderPublisher.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/RichOrderPublisher.kt new file mode 100644 index 000000000..612386203 --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/RichOrderPublisher.kt @@ -0,0 +1,7 @@ +package co.nilin.mixchange.accountant.core.spi + +import co.nilin.mixchange.accountant.core.inout.RichOrder + +interface RichOrderPublisher { + suspend fun publish(order: RichOrder) +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/RichTradePublisher.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/RichTradePublisher.kt new file mode 100644 index 000000000..ed52dd46d --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/RichTradePublisher.kt @@ -0,0 +1,7 @@ +package co.nilin.mixchange.accountant.core.spi + +import co.nilin.mixchange.accountant.core.inout.RichTrade + +interface RichTradePublisher { + suspend fun publish(trade: RichTrade) +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/TempEventPersister.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/TempEventPersister.kt index ac1c2cd48..569c1f9ec 100644 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/TempEventPersister.kt +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/TempEventPersister.kt @@ -1,9 +1,12 @@ package co.nilin.mixchange.accountant.core.spi +import co.nilin.mixchange.accountant.core.model.TempEvent import co.nilin.mixchange.matching.core.eventh.events.CoreEvent interface TempEventPersister { suspend fun saveTempEvent(ouid: String, event: CoreEvent) suspend fun loadTempEvents(ouid: String): List suspend fun removeTempEvents(ouid: String) + suspend fun removeTempEvents(tempEvents: List) + suspend fun fetchTempEvents(offset: Long, size: Long): List } \ No newline at end of file diff --git a/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImplTest.kt b/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImplTest.kt index f50f6eaaa..34faa9e47 100644 --- a/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImplTest.kt +++ b/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImplTest.kt @@ -11,14 +11,12 @@ import co.nilin.mixchange.matching.core.model.OrderDirection import co.nilin.mixchange.matching.core.model.OrderType import co.nilin.mixchange.matching.core.model.Pair import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test - -import org.junit.jupiter.api.Assertions.* -import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.Mockito import org.mockito.MockitoAnnotations -import org.mockito.stubbing.Answer import java.time.LocalDateTime internal class OrderManagerImplTest() { @@ -40,33 +38,39 @@ internal class OrderManagerImplTest() { @Mock lateinit var pairConfigLoader: PairConfigLoader + + @Mock + lateinit var richOrderPublisher: RichOrderPublisher + val orderManager: OrderManager init { MockitoAnnotations.openMocks(this) orderManager = OrderManagerImpl( - pairConfigLoader, financialActionPersister, financialActionLoader, orderPersister, tempEventPersister, tempEventRepublisher + pairConfigLoader, financialActionPersister, financialActionLoader, orderPersister, tempEventPersister, tempEventRepublisher, richOrderPublisher ) + runBlocking { + Mockito.`when`(tempEventPersister.loadTempEvents(anyString())).thenReturn(emptyList()) + } } - @Test fun givenAskOrder_whenHandleRequestOrder_thenFAMatch() { runBlocking { //given val pair = Pair("eth", "btc") val pairConfig = PairConfig( - pair.toString(), pair.leftSideName, pair.rightSideName, 1.0, 0.001 + pair.toString(), pair.leftSideName, pair.rightSideName, 1.0, 0.001 ) val submitOrderEvent = SubmitOrderEvent( - "ouid", "uuid", null, pair, 30, 60, 0, OrderDirection.ASK, MatchConstraint.GTC, OrderType.LIMIT_ORDER + "ouid", "uuid", null, pair, 30, 60, 0, OrderDirection.ASK, MatchConstraint.GTC, OrderType.LIMIT_ORDER ) Mockito.`when`(pairConfigLoader.load(pair.toString(), submitOrderEvent.direction, "")) - .thenReturn(PairFeeConfig(pairConfig, submitOrderEvent.direction.toString(), "", 0.1, 0.12)) + .thenReturn(PairFeeConfig(pairConfig, submitOrderEvent.direction.toString(), "", 0.1, 0.12)) Mockito.`when`(financialActionPersister.persist(MockitoHelper.anyObject())) - .then { - return@then it.getArgument>(0) - } + .then { + return@then it.getArgument>(0) + } //when val financialActions = orderManager.handleRequestOrder(submitOrderEvent) @@ -74,16 +78,16 @@ internal class OrderManagerImplTest() { //then assertEquals(1, financialActions.size) val expectedFinancialAction = FinancialAction( - null, - SubmitOrderEvent::class.simpleName!!, - submitOrderEvent.ouid, - pair.leftSideName, - pairConfig.leftSideFraction.toBigDecimal().multiply(submitOrderEvent.quantity.toBigDecimal()), - submitOrderEvent.uuid, - "main", - submitOrderEvent.uuid, - "exchange", - LocalDateTime.now() + null, + SubmitOrderEvent::class.simpleName!!, + submitOrderEvent.ouid, + pair.leftSideName, + pairConfig.leftSideFraction.toBigDecimal().multiply(submitOrderEvent.quantity.toBigDecimal()), + submitOrderEvent.uuid, + "main", + submitOrderEvent.uuid, + "exchange", + LocalDateTime.now() ) assertEquals(expectedFinancialAction.eventType, financialActions[0].eventType) assertEquals(expectedFinancialAction.symbol, financialActions[0].symbol) @@ -101,21 +105,21 @@ internal class OrderManagerImplTest() { //given val pair = Pair("eth", "btc") val pairConfig = PairConfig( - pair.toString(), pair.leftSideName, pair.rightSideName, 1.0, 0.001 + pair.toString(), pair.leftSideName, pair.rightSideName, 1.0, 0.001 ) val submitOrderEvent = SubmitOrderEvent( - "ouid", "uuid", null, pair, 35, 14, 0, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER + "ouid", "uuid", null, pair, 35, 14, 0, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER ) Mockito.`when`(pairConfigLoader.load(pair.toString(), submitOrderEvent.direction, "")) - .thenReturn( - PairFeeConfig( - pairConfig, submitOrderEvent.direction.toString(), "", 0.08, 0.1 + .thenReturn( + PairFeeConfig( + pairConfig, submitOrderEvent.direction.toString(), "", 0.08, 0.1 + ) ) - ) Mockito.`when`(financialActionPersister.persist(MockitoHelper.anyObject())) - .then { - return@then it.getArgument>(0) - } + .then { + return@then it.getArgument>(0) + } //when val financialActions = runBlocking { @@ -125,18 +129,18 @@ internal class OrderManagerImplTest() { //then assertEquals(1, financialActions.size) val expectedFinancialAction = FinancialAction( - null, - SubmitOrderEvent::class.simpleName!!, - submitOrderEvent.ouid, - pair.rightSideName, - pairConfig.leftSideFraction.toBigDecimal().multiply(submitOrderEvent.quantity.toBigDecimal()) - .multiply(pairConfig.rightSideFraction.toBigDecimal()) - .multiply(submitOrderEvent.price.toBigDecimal()), - submitOrderEvent.uuid, - "main", - submitOrderEvent.uuid, - "exchange", - LocalDateTime.now() + null, + SubmitOrderEvent::class.simpleName!!, + submitOrderEvent.ouid, + pair.rightSideName, + pairConfig.leftSideFraction.toBigDecimal().multiply(submitOrderEvent.quantity.toBigDecimal()) + .multiply(pairConfig.rightSideFraction.toBigDecimal()) + .multiply(submitOrderEvent.price.toBigDecimal()), + submitOrderEvent.uuid, + "main", + submitOrderEvent.uuid, + "exchange", + LocalDateTime.now() ) assertEquals(expectedFinancialAction.eventType, financialActions[0].eventType) assertEquals(expectedFinancialAction.symbol, financialActions[0].symbol) diff --git a/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/TradeManagerImplTest.kt b/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/TradeManagerImplTest.kt index 8f2a00c96..4155492a9 100644 --- a/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/TradeManagerImplTest.kt +++ b/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/TradeManagerImplTest.kt @@ -16,6 +16,7 @@ import co.nilin.mixchange.matching.core.model.Pair import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test +import org.mockito.ArgumentMatchers import org.mockito.Mock import org.mockito.Mockito @@ -46,6 +47,12 @@ internal class TradeManagerImplTest() { @Mock lateinit var tempEventRepublisher: TempEventRepublisher + @Mock + lateinit var richOrderPublisher: RichOrderPublisher + + @Mock + lateinit var richTradePublisher: RichTradePublisher + val orderManager: OrderManager val tradeManager: TradeManager @@ -54,11 +61,28 @@ internal class TradeManagerImplTest() { init { MockitoAnnotations.openMocks(this) orderManager = OrderManagerImpl( - pairConfigLoader, financialActionPersister, financeActionLoader, orderPersister, tempEventPersister, tempEventRepublisher + pairConfigLoader, + financialActionPersister, + financeActionLoader, + orderPersister, + tempEventPersister, + tempEventRepublisher, + richOrderPublisher ) tradeManager = TradeManagerImpl( - pairStaticRateLoader, financialActionPersister, financeActionLoader, orderPersister, tempEventPersister, walletProxy, "pcoin", "0x0" + pairStaticRateLoader, + financialActionPersister, + financeActionLoader, + orderPersister, + tempEventPersister, + richTradePublisher, + walletProxy, + "pcoin", + "0x0" ) + runBlocking { + Mockito.`when`(tempEventPersister.loadTempEvents(ArgumentMatchers.anyString())).thenReturn(emptyList()) + } } @Test @@ -137,20 +161,21 @@ internal class TradeManagerImplTest() { makerSubmitOrderEvent: SubmitOrderEvent ): TradeEvent { val tradeEvent = TradeEvent( - pair, - takerSubmitOrderEvent.ouid, - takerSubmitOrderEvent.uuid, - takerSubmitOrderEvent.orderId ?: -1, - takerSubmitOrderEvent.direction, - takerSubmitOrderEvent.price, - 0, - makerSubmitOrderEvent.ouid, - makerSubmitOrderEvent.uuid, - makerSubmitOrderEvent.orderId ?: 1, - makerSubmitOrderEvent.direction, - makerSubmitOrderEvent.price, - makerSubmitOrderEvent.quantity - takerSubmitOrderEvent.quantity, - takerSubmitOrderEvent.quantity + 0, + pair, + takerSubmitOrderEvent.ouid, + takerSubmitOrderEvent.uuid, + takerSubmitOrderEvent.orderId ?: -1, + takerSubmitOrderEvent.direction, + takerSubmitOrderEvent.price, + 0, + makerSubmitOrderEvent.ouid, + makerSubmitOrderEvent.uuid, + makerSubmitOrderEvent.orderId ?: 1, + makerSubmitOrderEvent.direction, + makerSubmitOrderEvent.price, + makerSubmitOrderEvent.quantity - takerSubmitOrderEvent.quantity, + takerSubmitOrderEvent.quantity ) return tradeEvent } @@ -178,22 +203,27 @@ internal class TradeManagerImplTest() { val orderTakerFee = orderPairFeeConfig.takerFee * 1 //user level formula Mockito.`when`(orderPersister.load(submitOrderEvent.ouid)).thenReturn( Order( - submitOrderEvent.pair.toString(), - submitOrderEvent.ouid, - null, - orderMakerFee, - orderTakerFee, - orderPairFeeConfig.pairConfig.leftSideFraction, - orderPairFeeConfig.pairConfig.rightSideFraction, - submitOrderEvent.uuid, - "", - submitOrderEvent.direction, - submitOrderEvent.price, - submitOrderEvent.quantity, - submitOrderEvent.quantity - submitOrderEvent.remainedQuantity, - financialActions[0].amount, - financialActions[0].amount, - 0 + submitOrderEvent.pair.toString(), + submitOrderEvent.ouid, + null, + orderMakerFee, + orderTakerFee, + orderPairFeeConfig.pairConfig.leftSideFraction, + orderPairFeeConfig.pairConfig.rightSideFraction, + submitOrderEvent.uuid, + "", + submitOrderEvent.direction, + submitOrderEvent.matchConstraint, + submitOrderEvent.orderType, + submitOrderEvent.price, + submitOrderEvent.quantity, + submitOrderEvent.quantity - submitOrderEvent.remainedQuantity, + submitOrderEvent.price.toBigDecimal(), + submitOrderEvent.quantity.toBigDecimal(), + (submitOrderEvent.quantity - submitOrderEvent.remainedQuantity).toBigDecimal(), + financialActions[0].amount, + financialActions[0].amount, + 0 ) ) } diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/mvnw b/Accountant/accountant-ports/accountant-eventlistener-kafka/mvnw old mode 100755 new mode 100644 diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/OrderKafkaListener.kt b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/OrderKafkaListener.kt index c8a5b920e..e8be743cb 100644 --- a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/OrderKafkaListener.kt +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/OrderKafkaListener.kt @@ -2,14 +2,11 @@ package co.nilin.mixchange.port.accountant.kafka.consumer import co.nilin.mixchange.port.accountant.kafka.spi.OrderSubmitRequestListener import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest -import kotlinx.coroutines.ExecutorCoroutineDispatcher -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withContext import org.apache.kafka.clients.consumer.ConsumerRecord import org.springframework.kafka.listener.MessageListener import org.springframework.stereotype.Component - +@Component class OrderKafkaListener() : MessageListener { val orderListeners = arrayListOf() diff --git a/Accountant/accountant-ports/accountant-persister-postgres/mvnw b/Accountant/accountant-ports/accountant-persister-postgres/mvnw old mode 100755 new mode 100644 diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/config/PostgresConfig.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/config/PostgresConfig.kt index 43c013fee..9d114eb90 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/config/PostgresConfig.kt +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/config/PostgresConfig.kt @@ -1,15 +1,9 @@ package co.nilin.mixchange.port.order.kafka.config -import co.nilin.mixchange.matching.core.model.OrderDirection import org.springframework.context.annotation.Configuration -import org.springframework.data.annotation.Id import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories -import org.springframework.data.relational.core.mapping.Column -import org.springframework.data.relational.core.mapping.Table import org.springframework.r2dbc.core.DatabaseClient -import java.math.BigDecimal -import java.time.LocalDateTime @Configuration @@ -19,12 +13,6 @@ class PostgresConfig(db: DatabaseClient) { init { val sql = """ - drop table IF EXISTS orders; - drop table IF EXISTS fi_actions; - drop table IF EXISTS pair_config; - drop table IF EXISTS pair_fee_config; - drop table IF EXISTS temp_events; - CREATE TABLE IF NOT EXISTS orders ( id SERIAL PRIMARY KEY, ouid VARCHAR(72) NOT NULL UNIQUE, @@ -37,9 +25,14 @@ class PostgresConfig(db: DatabaseClient) { right_side_fraction decimal, user_level VARCHAR(20), direction VARCHAR(20), + match_constraint VARCHAR(30), + order_type VARCHAR(30), price numeric, quantity numeric, filled_quantity numeric, + orig_price decimal, + orig_quantity decimal, + filled_orig_quantity decimal, first_transfer_amount numeric, remained_transfer_amount numeric, status integer, diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/TempEventRepository.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/TempEventRepository.kt index 99ebfc0fe..a0bb738cb 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/TempEventRepository.kt +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/TempEventRepository.kt @@ -2,12 +2,19 @@ package co.nilin.mixchange.port.accountant.postgres.dao import co.nilin.mixchange.port.accountant.postgres.model.TempEventModel import kotlinx.coroutines.flow.Flow +import org.springframework.data.domain.Pageable +import org.springframework.data.r2dbc.repository.Query import org.springframework.data.repository.reactive.ReactiveCrudRepository import org.springframework.stereotype.Repository import reactor.core.publisher.Mono @Repository interface TempEventRepository: ReactiveCrudRepository { - fun findByOuid(ouid:String): Flow - fun deleteByOuid(ouid:String): Mono + fun findByOuid(ouid: String): Flow + fun deleteByOuid(ouid: String): Mono + + @Query("select * from temp_events") + fun findAll( + paging: Pageable + ): Flow } \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/OrderPersisterImpl.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/OrderPersisterImpl.kt index bf7578fb4..379ec4e6a 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/OrderPersisterImpl.kt +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/OrderPersisterImpl.kt @@ -4,46 +4,69 @@ import co.nilin.mixchange.accountant.core.model.Order import co.nilin.mixchange.accountant.core.spi.OrderPersister import co.nilin.mixchange.port.accountant.postgres.dao.OrderRepository import co.nilin.mixchange.port.accountant.postgres.model.OrderModel -import kotlinx.coroutines.reactive.awaitFirstOrElse import kotlinx.coroutines.reactive.awaitFirstOrNull import org.springframework.stereotype.Component -import java.lang.IllegalArgumentException import java.time.LocalDateTime @Component class OrderPersisterImpl(val orderRepository: OrderRepository) : OrderPersister { override suspend fun load(ouid: String): Order? { val model = orderRepository.findByOuid(ouid) - .awaitFirstOrNull() ?: return null - return Order(model.pair, model.ouid, model.matchingEngineId, model.makerFee, model.takerFee - , model.leftSideFraction, model.rightSideFraction, model.uuid, model.userLevel - , model.direction, model.price, model.quantity, model.filledQuantity - , model.firstTransferAmount, model.remainedTransferAmount, model.status, model.id) + .awaitFirstOrNull() ?: return null + return Order( + model.pair, + model.ouid, + model.matchingEngineId, + model.makerFee, + model.takerFee, + model.leftSideFraction, + model.rightSideFraction, + model.uuid, + model.userLevel, + model.direction, + model.matchConstraint, + model.orderType, + model.price, + model.quantity, + model.filledQuantity, + model.origPrice, + model.origQuantity, + model.filledOrigQuantity, + model.firstTransferAmount, + model.remainedTransferAmount, + model.status, + model.id + ) } override suspend fun save(order: Order): Order { orderRepository.save( OrderModel( - order.id, - order.ouid, - order.uuid, - order.pair, - order.matchingEngineId, - order.makerFee, - order.takerFee, - order.leftSideFraction, - order.rightSideFraction, - order.userLevel, - order.direction, - order.price, - order.quantity, - order.filledQuantity, - order.firstTransferAmount, - order.remainedTransferAmount, - order.status, - "", - "", - LocalDateTime.now() + order.id, + order.ouid, + order.uuid, + order.pair, + order.matchingEngineId, + order.makerFee, + order.takerFee, + order.leftSideFraction, + order.rightSideFraction, + order.userLevel, + order.direction, + order.matchConstraint, + order.orderType, + order.price, + order.quantity, + order.filledQuantity, + order.origPrice, + order.origQuantity, + order.filledOrigQuantity, + order.firstTransferAmount, + order.remainedTransferAmount, + order.status, + "", + "", + LocalDateTime.now() ) ).awaitFirstOrNull() return order diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/TempEventPersisterImpl.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/TempEventPersisterImpl.kt index 603ab1582..c394ffb0d 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/TempEventPersisterImpl.kt +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/TempEventPersisterImpl.kt @@ -1,5 +1,6 @@ package co.nilin.mixchange.port.accountant.postgres.impl +import co.nilin.mixchange.accountant.core.model.TempEvent import co.nilin.mixchange.accountant.core.spi.TempEventPersister import co.nilin.mixchange.matching.core.eventh.events.CoreEvent import co.nilin.mixchange.port.accountant.postgres.dao.TempEventRepository @@ -9,6 +10,8 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.toList import kotlinx.coroutines.reactive.awaitFirstOrNull import kotlinx.coroutines.reactive.awaitSingle +import org.springframework.data.domain.PageRequest +import org.springframework.data.domain.Sort import org.springframework.stereotype.Component import java.time.LocalDateTime @@ -16,23 +19,41 @@ import java.time.LocalDateTime class TempEventPersisterImpl(val tempEventRepository: TempEventRepository) : TempEventPersister { override suspend fun saveTempEvent(ouid: String, event: CoreEvent) { tempEventRepository.save( - TempEventModel( - null, ouid, event.javaClass.name, - Gson().toJson(event), LocalDateTime.now() - ) + TempEventModel( + null, ouid, event.javaClass.name, + Gson().toJson(event), LocalDateTime.now() + ) ).awaitSingle() } override suspend fun loadTempEvents(ouid: String): List { return tempEventRepository - .findByOuid(ouid) - .map { value: TempEventModel -> - Gson().fromJson(value.eventBody, Class.forName(value.eventType)) as CoreEvent - } - .toList() + .findByOuid(ouid) + .map { value: TempEventModel -> + Gson().fromJson(value.eventBody, Class.forName(value.eventType)) as CoreEvent + } + .toList() } override suspend fun removeTempEvents(ouid: String) { tempEventRepository.deleteByOuid(ouid).awaitFirstOrNull() } + + override suspend fun removeTempEvents(tempEvents: List) { + tempEventRepository.deleteAll(tempEvents.map { event -> + TempEventModel(event.id, event.ouid, event.eventBody.javaClass.name, + "", event.eventDate) + }).awaitFirstOrNull() + } + + override suspend fun fetchTempEvents(offset: Long, size: Long): List { + return tempEventRepository + .findAll(PageRequest.of(offset.toInt(), size.toInt(), Sort.by(Sort.Direction.ASC, "eventDate"))) + .map { value: TempEventModel -> + TempEvent(value.id!!, value.ouid, Gson().fromJson(value.eventBody, Class.forName(value.eventType)) + as + CoreEvent, value.eventDate) + } + .toList() + } } \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/OrderModel.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/OrderModel.kt index 19543132d..50eec7693 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/OrderModel.kt +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/OrderModel.kt @@ -1,7 +1,9 @@ package co.nilin.mixchange.port.accountant.postgres.model +import co.nilin.mixchange.matching.core.model.MatchConstraint import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.OrderType import org.springframework.data.annotation.Id import org.springframework.data.relational.core.mapping.Column import org.springframework.data.relational.core.mapping.Table @@ -10,24 +12,29 @@ import java.time.LocalDateTime @Table("orders") class OrderModel( - @Id var id: Long?, - val ouid: String, - val uuid: String, - val pair: String, - @Column(value = "matching_engine_id") val matchingEngineId: Long?, - @Column("maker_fee") val makerFee: Double, - @Column("taker_fee") val takerFee: Double, - @Column("left_side_fraction") val leftSideFraction: Double, - @Column("right_side_fraction") val rightSideFraction: Double, - @Column("user_level") val userLevel: String, - @Column("direction") val direction: OrderDirection, - @Column("price") val price: Long, - @Column("quantity") val quantity: Long, - @Column("filled_quantity") val filledQuantity: Long, - @Column("first_transfer_amount") val firstTransferAmount: BigDecimal, - @Column("remained_transfer_amount") val remainedTransferAmount: BigDecimal, - @Column("status") val status: Int, - val agent: String, - val ip: String, - @Column("create_date") val createDate: LocalDateTime + @Id var id: Long?, + val ouid: String, + val uuid: String, + val pair: String, + @Column(value = "matching_engine_id") val matchingEngineId: Long?, + @Column("maker_fee") val makerFee: Double, + @Column("taker_fee") val takerFee: Double, + @Column("left_side_fraction") val leftSideFraction: Double, + @Column("right_side_fraction") val rightSideFraction: Double, + @Column("user_level") val userLevel: String, + @Column("direction") val direction: OrderDirection, + @Column("match_constraint") val matchConstraint: MatchConstraint, + @Column("order_type") val orderType: OrderType, + @Column("price") val price: Long, + @Column("quantity") val quantity: Long, + @Column("filled_quantity") val filledQuantity: Long, + @Column("orig_price") val origPrice: BigDecimal, + @Column("orig_quantity") val origQuantity: BigDecimal, + @Column("filled_orig_quantity") val filledOrigQuantity: BigDecimal, + @Column("first_transfer_amount") val firstTransferAmount: BigDecimal, + @Column("remained_transfer_amount") val remainedTransferAmount: BigDecimal, + @Column("status") val status: Int, + val agent: String, + val ip: String, + @Column("create_date") val createDate: LocalDateTime ) \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-tempsubmitter-kafka/.gitignore b/Accountant/accountant-ports/accountant-submitter-kafka/.gitignore similarity index 100% rename from Accountant/accountant-ports/accountant-tempsubmitter-kafka/.gitignore rename to Accountant/accountant-ports/accountant-submitter-kafka/.gitignore diff --git a/Accountant/accountant-ports/accountant-tempsubmitter-kafka/mvnw b/Accountant/accountant-ports/accountant-submitter-kafka/mvnw old mode 100755 new mode 100644 similarity index 100% rename from Accountant/accountant-ports/accountant-tempsubmitter-kafka/mvnw rename to Accountant/accountant-ports/accountant-submitter-kafka/mvnw diff --git a/Accountant/accountant-ports/accountant-tempsubmitter-kafka/mvnw.cmd b/Accountant/accountant-ports/accountant-submitter-kafka/mvnw.cmd similarity index 100% rename from Accountant/accountant-ports/accountant-tempsubmitter-kafka/mvnw.cmd rename to Accountant/accountant-ports/accountant-submitter-kafka/mvnw.cmd diff --git a/Accountant/accountant-ports/accountant-tempsubmitter-kafka/pom.xml b/Accountant/accountant-ports/accountant-submitter-kafka/pom.xml similarity index 65% rename from Accountant/accountant-ports/accountant-tempsubmitter-kafka/pom.xml rename to Accountant/accountant-ports/accountant-submitter-kafka/pom.xml index 8f3620258..94ceab7c1 100644 --- a/Accountant/accountant-ports/accountant-tempsubmitter-kafka/pom.xml +++ b/Accountant/accountant-ports/accountant-submitter-kafka/pom.xml @@ -1,38 +1,38 @@ - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.4.4 - - - co.nilin.mixchange - accountant-temp-submitter-kafka - 0.0.1-SNAPSHOT - accountant-temp-submitter-kafka - Accountant kafka temp event submitter of Mixchange + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + accountant-submitter-kafka + 0.0.1-SNAPSHOT + accountant-temp-submitter-kafka + Accountant kafka event submitter of Mixchange - - 1.8 - 1.4.31 - ${version} - ${version} - + + 1.8 + 1.4.31 + ${version} + ${version} + - - - org.springframework.boot - spring-boot-starter - - - org.springframework.boot - spring-boot-starter-webflux - - - co.nilin.mixchange - matching-core + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-webflux + + + co.nilin.mixchange + matching-core ${matching.version} provided diff --git a/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/config/SubmitterKafkaConfig.kt b/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/config/SubmitterKafkaConfig.kt new file mode 100644 index 000000000..2ecd1901b --- /dev/null +++ b/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/config/SubmitterKafkaConfig.kt @@ -0,0 +1,78 @@ +package co.nilin.mixchange.port.accountant.kafka.config + + +import co.nilin.mixchange.accountant.core.inout.RichOrder +import co.nilin.mixchange.accountant.core.inout.RichTrade +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +import org.apache.kafka.clients.admin.NewTopic +import org.apache.kafka.clients.producer.ProducerConfig +import org.apache.kafka.common.serialization.StringSerializer +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.support.GenericApplicationContext +import org.springframework.kafka.core.DefaultKafkaProducerFactory +import org.springframework.kafka.core.KafkaTemplate +import org.springframework.kafka.core.ProducerFactory +import org.springframework.kafka.support.serializer.JsonSerializer + + +@Configuration +class SubmitterKafkaConfig() { + @Value("\${spring.kafka.bootstrap-servers}") + private lateinit var bootstrapServers: String + + @Value("\${spring.kafka.consumer.group-id}") + private val groupId: String? = null + + @Autowired + private val applicationContext: GenericApplicationContext? = null + + + @Bean("accountantProducerConfigs") + fun producerConfigs(): Map? { + val props: MutableMap = HashMap() + props[ProducerConfig.BOOTSTRAP_SERVERS_CONFIG] = bootstrapServers + props[ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG] = StringSerializer::class.java + props[ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG] = JsonSerializer::class.java + return props + } + + @Bean("accountantEventProducerFactory") + fun producerFactory(@Qualifier("accountantProducerConfigs") producerConfigs: Map): ProducerFactory { + return DefaultKafkaProducerFactory(producerConfigs) + } + + @Bean("accountantEventKafkaTemplate") + fun kafkaTemplate(@Qualifier("accountantEventProducerFactory") producerFactory: ProducerFactory): KafkaTemplate { + return KafkaTemplate(producerFactory) + } + + @Bean("richTradeProducerFactory") + fun richTradeProducerFactory(@Qualifier("accountantProducerConfigs") producerConfigs: Map): ProducerFactory { + return DefaultKafkaProducerFactory(producerConfigs) + } + + @Bean("richTradeKafkaTemplate") + fun richTradeKafkaTemplate(@Qualifier("richTradeProducerFactory") producerFactory: ProducerFactory): KafkaTemplate { + return KafkaTemplate(producerFactory) + } + + @Bean("richOrderProducerFactory") + fun richOrderProducerFactory(@Qualifier("accountantProducerConfigs") producerConfigs: Map): ProducerFactory { + return DefaultKafkaProducerFactory(producerConfigs) + } + + @Bean("richOrderKafkaTemplate") + fun richOrderKafkaTemplate(@Qualifier("richOrderProducerFactory") producerFactory: ProducerFactory): KafkaTemplate { + return KafkaTemplate(producerFactory) + } + + @Autowired + fun createTopics(applicationContext: GenericApplicationContext) { + applicationContext.registerBean("topic_richOrder", NewTopic::class.java, "richOrder", 10, 1) + applicationContext.registerBean("topic_richTrade", NewTopic::class.java, "richTrade", 10, 1) + } +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/RichOrderSubmitter.kt b/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/RichOrderSubmitter.kt new file mode 100644 index 000000000..50fef59c3 --- /dev/null +++ b/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/RichOrderSubmitter.kt @@ -0,0 +1,24 @@ +package co.nilin.mixchange.port.accountant.kafka.service + +import co.nilin.mixchange.accountant.core.inout.RichOrder +import co.nilin.mixchange.accountant.core.spi.RichOrderPublisher +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.kafka.core.KafkaTemplate +import org.springframework.stereotype.Component +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine + +@Component +class RichOrderSubmitter(@Qualifier("richOrderKafkaTemplate") val kafkaTemplate: KafkaTemplate) : + RichOrderPublisher { + override suspend fun publish(order: RichOrder): Unit = suspendCoroutine { cont -> + println("richOrderSubmit!") + val sendFuture = kafkaTemplate.send("richOrder", order) + sendFuture.addCallback({ sendResult -> + cont.resume(Unit) + }, { exception -> + cont.resumeWithException(exception) + }) + } +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/RichTradeSubmitter.kt b/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/RichTradeSubmitter.kt new file mode 100644 index 000000000..92223b510 --- /dev/null +++ b/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/RichTradeSubmitter.kt @@ -0,0 +1,24 @@ +package co.nilin.mixchange.port.accountant.kafka.service + +import co.nilin.mixchange.accountant.core.inout.RichTrade +import co.nilin.mixchange.accountant.core.spi.RichTradePublisher +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.kafka.core.KafkaTemplate +import org.springframework.stereotype.Component +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine + +@Component +class RichTradeSubmitter(@Qualifier("richTradeKafkaTemplate") val kafkaTemplate: KafkaTemplate) : + RichTradePublisher { + override suspend fun publish(trade: RichTrade): Unit = suspendCoroutine { cont -> + println("richTradeSubmit!") + val sendFuture = kafkaTemplate.send("richTrade", trade) + sendFuture.addCallback({ sendResult -> + cont.resume(Unit) + }, { exception -> + cont.resumeWithException(exception) + }) + } +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-tempsubmitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/TempEventSubmitter.kt b/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/TempEventSubmitter.kt similarity index 75% rename from Accountant/accountant-ports/accountant-tempsubmitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/TempEventSubmitter.kt rename to Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/TempEventSubmitter.kt index 647f82f7a..0e21bafc3 100644 --- a/Accountant/accountant-ports/accountant-tempsubmitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/TempEventSubmitter.kt +++ b/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/TempEventSubmitter.kt @@ -10,20 +10,17 @@ import kotlin.coroutines.resumeWithException import kotlin.coroutines.suspendCoroutine @Component -class TempEventSubmitter(@Qualifier("tempeventKafkaTemplate") val kafkaTemplate: KafkaTemplate): TempEventRepublisher { +class TempEventSubmitter(@Qualifier("accountantEventKafkaTemplate") val kafkaTemplate: KafkaTemplate) : + TempEventRepublisher { override suspend fun republish(events: List): Unit = suspendCoroutine { cont -> - println("TempEventSubmit!") - events.forEach { event -> + println("accountantEventSubmit!") + events.forEach { event -> val sendFuture = kafkaTemplate.send("tempevents", event) sendFuture.addCallback({ sendResult -> + cont.resume(Unit) }, { exception -> cont.resumeWithException(exception) }) } - cont.resume(Unit) } - - - - } \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-tempsubmitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/config/SubmitterKafkaConfig.kt b/Accountant/accountant-ports/accountant-tempsubmitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/config/SubmitterKafkaConfig.kt deleted file mode 100644 index d402cf1f2..000000000 --- a/Accountant/accountant-ports/accountant-tempsubmitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/config/SubmitterKafkaConfig.kt +++ /dev/null @@ -1,53 +0,0 @@ -package co.nilin.mixchange.port.accountant.kafka.config - - - -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent -import org.apache.kafka.clients.producer.ProducerConfig -import org.apache.kafka.common.serialization.StringSerializer -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.beans.factory.annotation.Value -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.context.support.GenericApplicationContext -import org.springframework.kafka.core.DefaultKafkaProducerFactory -import org.springframework.kafka.core.KafkaTemplate -import org.springframework.kafka.core.ProducerFactory -import org.springframework.kafka.support.serializer.JsonSerializer -import java.util.* - - -@Configuration -class SubmitterKafkaConfig() { - @Value("\${spring.kafka.bootstrap-servers}") - private lateinit var bootstrapServers: String - - @Value("\${spring.kafka.consumer.group-id}") - private val groupId: String? = null - - @Autowired - private val applicationContext: GenericApplicationContext? = null - - - @Bean("tempeventProducerConfigs") - fun producerConfigs(): Map? { - val props: MutableMap = HashMap() - props[ProducerConfig.BOOTSTRAP_SERVERS_CONFIG] = bootstrapServers - props[ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG] = StringSerializer::class.java - props[ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG] = JsonSerializer::class.java - return props - } - - @Bean("tempeventProducerFactory") - fun producerFactory(@Qualifier("tempeventProducerConfigs") producerConfigs: Map): ProducerFactory { - return DefaultKafkaProducerFactory(producerConfigs) - } - - @Bean("tempeventKafkaTemplate") - fun kafkaTemplate(@Qualifier("tempeventProducerFactory") producerFactory: ProducerFactory): KafkaTemplate { - return KafkaTemplate(producerFactory) - } - - -} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-wallet-proxy/mvnw b/Accountant/accountant-ports/accountant-wallet-proxy/mvnw old mode 100755 new mode 100644 diff --git a/Accountant/pom.xml b/Accountant/pom.xml index 025f2562b..bb61c0a3e 100644 --- a/Accountant/pom.xml +++ b/Accountant/pom.xml @@ -13,7 +13,7 @@ accountant-app accountant-ports/accountant-persister-postgres accountant-ports/accountant-wallet-proxy - accountant-ports/accountant-tempsubmitter-kafka + accountant-ports/accountant-submitter-kafka accountant-ports/accountant-eventlistener-kafka diff --git a/Api/.gitignore b/Api/.gitignore new file mode 100644 index 000000000..d6953c444 --- /dev/null +++ b/Api/.gitignore @@ -0,0 +1,3 @@ +*.iml +.idea +/.idea/ diff --git a/Api/.idea/.gitignore b/Api/.idea/.gitignore new file mode 100644 index 000000000..73f69e095 --- /dev/null +++ b/Api/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/Api/api-app/.gitignore b/Api/api-app/.gitignore new file mode 100644 index 000000000..e7c460346 --- /dev/null +++ b/Api/api-app/.gitignore @@ -0,0 +1,80 @@ +# Created by .ignore support plugin (hsz.mobi) +### Kotlin template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +.idea/artifacts +.idea/compiler.xml +.idea/jarRepositories.xml +.idea/modules.xml +.idea/*.iml +.idea/modules +*.iml +*.ipr +.mvn/ + +# File-based project format +*.iws + +# IntelliJ +out/ + +target/ + +.DS_Store + + + + diff --git a/Api/api-app/Dockerfile b/Api/api-app/Dockerfile new file mode 100644 index 000000000..f2cbd4c26 --- /dev/null +++ b/Api/api-app/Dockerfile @@ -0,0 +1,5 @@ +FROM openjdk:8-jdk-alpine +VOLUME /tmp +ARG JAR_FILE=target/*.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"] \ No newline at end of file diff --git a/Api/api-app/pom.xml b/Api/api-app/pom.xml new file mode 100644 index 000000000..8cca739d5 --- /dev/null +++ b/Api/api-app/pom.xml @@ -0,0 +1,195 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + api-app + 0.0.1-SNAPSHOT + api-app + Api app Mixchange + + + 1.8 + 1.4.31 + ${version} + ${version} + ${version} + + + + + org.springframework.boot + spring-boot-starter + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + com.fasterxml.jackson.module + jackson-module-kotlin + + + org.springframework.boot + spring-boot-starter + + + co.nilin.mixchange + accountant-core + ${accountant.version} + + + co.nilin.mixchange + api-core + ${api.version} + + + co.nilin.mixchange + api-eventlistener-kafka + ${api.version} + + + co.nilin.mixchange + api-binance-rest + ${api.version} + + + co.nilin.mixchange + api-persister-postgres + ${api.version} + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18 + + + ${skip.unit.tests} + + + **/*IntegrationTest.java + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-test-source + generate-test-sources + + add-test-source + + + + src/test/java + + + + + compile + + add-source + + + + src/main/java + + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + + -Xjsr305=strict + + + spring + + 1.8 + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + compile + compile + + compile + + + + testCompile + test-compile + + testCompile + + + + + + opex-accountant + + diff --git a/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/ApiApp.kt b/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/ApiApp.kt new file mode 100644 index 000000000..8a9752dba --- /dev/null +++ b/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/ApiApp.kt @@ -0,0 +1,13 @@ +package co.nilin.mixchange.app + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.runApplication +import org.springframework.context.annotation.ComponentScan + +@SpringBootApplication +@ComponentScan("co.nilin.mixchange") +class AccountantApp + +fun main(args: Array) { + runApplication(*args) +} \ No newline at end of file diff --git a/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt b/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt new file mode 100644 index 000000000..77190e1f0 --- /dev/null +++ b/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt @@ -0,0 +1,61 @@ +package co.nilin.mixchange.app.config + +import co.nilin.mixchange.accountant.core.inout.RichOrder +import co.nilin.mixchange.api.core.spi.OrderPersister +import co.nilin.mixchange.api.core.spi.TradePersister +import co.nilin.mixchange.port.api.kafka.consumer.OrderKafkaListener +import co.nilin.mixchange.port.api.kafka.spi.RichOrderListener +import co.nilin.mixchange.port.trade.consumer.TradeKafkaListener +import co.nilin.mixchange.port.trade.spi.RichTradeListener +import kotlinx.coroutines.runBlocking +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +class AppConfig { + + @Bean + fun apiListener( + richOrderPersister: OrderPersister, richTradePersister: TradePersister + ): ApiListenerImpl { + return ApiListenerImpl(richOrderPersister, richTradePersister) + } + + @Autowired + fun configureListeners( + orderKafkaListener: OrderKafkaListener, tradeKafkaListener: TradeKafkaListener, appListener: ApiListenerImpl + ) { + orderKafkaListener.addOrderListener(appListener) + tradeKafkaListener.addTradeListener(appListener) + } + + class ApiListenerImpl( + val richOrderPersister: OrderPersister, val richTradePersister: TradePersister + ) : RichTradeListener, RichOrderListener { + + override fun id(): String { + return "AppListener" + } + + override fun onTrade( + trade: co.nilin.mixchange.accountant.core.inout.RichTrade, + partition: Int, + offset: Long, + timestamp: Long + ) { + println("RichTrade received") + runBlocking(AppDispatchers.kafkaExecutor) { + richTradePersister.save(trade) + } + } + + override fun onOrder(order: RichOrder, partition: Int, offset: Long, timestamp: Long) { + runBlocking(AppDispatchers.kafkaExecutor) { + richOrderPersister.save(order) + } + } + } + + +} \ No newline at end of file diff --git a/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/config/AppDispatchers.kt b/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/config/AppDispatchers.kt new file mode 100644 index 000000000..073195cc3 --- /dev/null +++ b/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/config/AppDispatchers.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.app.config + +import kotlinx.coroutines.asCoroutineDispatcher +import java.util.concurrent.Executors + +object AppDispatchers { + val apiExecutor = Executors.newSingleThreadExecutor().asCoroutineDispatcher() + val kafkaExecutor = Executors.newSingleThreadExecutor().asCoroutineDispatcher() +} \ No newline at end of file diff --git a/Api/api-app/src/main/resources/application-docker.yml b/Api/api-app/src/main/resources/application-docker.yml new file mode 100644 index 000000000..b1d77a0de --- /dev/null +++ b/Api/api-app/src/main/resources/application-docker.yml @@ -0,0 +1,13 @@ +spring: + kafka: + bootstrap-servers: ${KAFKA_IP_PORT} + redis: + host: ${REDIS_HOST} + r2dbc: + url: r2dbc:postgresql://${DB_IP_PORT}/opex_api + username: opex + password: hiopex + cloud: + consul: + host: ${CONSUL_HOST} + port: 8500 diff --git a/Api/api-app/src/main/resources/application.yml b/Api/api-app/src/main/resources/application.yml new file mode 100644 index 000000000..8da3619aa --- /dev/null +++ b/Api/api-app/src/main/resources/application.yml @@ -0,0 +1,28 @@ +server.port: 8094 +spring: + application: + name: opex-api + main: + allow-bean-definition-overriding: false + kafka: + bootstrap-servers: 192.168.178.29:9092 + consumer: + group-id: api + redis: + host: 127.0.0.1 + port: 6379 + r2dbc: + url: r2dbc:postgresql://localhost/opex_api + username: opex + password: hiopex + initialization-mode: always + cloud: + bootstrap: + enabled: true + consul: + port: 8500 + discovery: + #healthCheckPath: ${management.context-path}/health + instance-id: ${spring.application.name}:${server.port} + healthCheckInterval: 20s + prefer-ip-address: true diff --git a/Api/api-app/src/main/resources/public.cert b/Api/api-app/src/main/resources/public.cert new file mode 100644 index 000000000..8589de065 --- /dev/null +++ b/Api/api-app/src/main/resources/public.cert @@ -0,0 +1,3 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsw+lmV2HbpgXllKS+ccyCerlWDir32Y3yFvXF3CbzYRKVg/4FddA8itMCPpPjVOAo26bi/0WImHHYPyGxSH3QQIQaSdS5AFUNarpFc9M/W4Bu7k5h8sKb+vzZA3hMLFyvmQ2JtY8Zuqi1BBB33OAlkOHprJFTrrt1xa4a6KJjKXlDp5o5hOFgvZbsW+k/fBU0CLCbL3PlXQLhqHgEncpL9L9FF/xDbPIGftxjEWcBPFG/SuyygT35xRFMxB7ifR+wJZwVnoC4RhbhgJdL2EiUVT7wq5MTsxr2j4nAAJYatK/7OTecs6y/7bGNAGEoqh4Q3X3D95YFex0scmHgY1hWwIDAQAB +-----END PUBLIC KEY----- \ No newline at end of file diff --git a/Api/api-core/.gitignore b/Api/api-core/.gitignore new file mode 100644 index 000000000..f3a8317d6 --- /dev/null +++ b/Api/api-core/.gitignore @@ -0,0 +1,4 @@ +*.iml +target/ +.mvn/ +.idea/ \ No newline at end of file diff --git a/Api/api-core/mvnw b/Api/api-core/mvnw new file mode 100644 index 000000000..3c8a55373 --- /dev/null +++ b/Api/api-core/mvnw @@ -0,0 +1,322 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ]; then + + if [ -f /etc/mavenrc ]; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ]; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false +darwin=false +mingw=false +case "$(uname)" in +CYGWIN*) cygwin=true ;; +MINGW*) mingw=true ;; +Darwin*) + darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="$(/usr/libexec/java_home)" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ]; then + if [ -r /etc/gentoo-release ]; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +if [ -z "$M2_HOME" ]; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ]; do + ls=$(ls -ld "$PRG") + link=$(expr "$ls" : '.*-> \(.*\)$') + if expr "$link" : '/.*' >/dev/null; then + PRG="$link" + else + PRG="$(dirname "$PRG")/$link" + fi + done + + saveddir=$(pwd) + + M2_HOME=$(dirname "$PRG")/.. + + # make it fully qualified + M2_HOME=$(cd "$M2_HOME" && pwd) + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=$(cygpath --unix "$M2_HOME") + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw; then + [ -n "$M2_HOME" ] && + M2_HOME="$( ( + cd "$M2_HOME" + pwd + ))" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="$( ( + cd "$JAVA_HOME" + pwd + ))" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr \"$javaExecutable\" : '\([^ ]*\)')" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=$(which readlink) + if [ ! $(expr "$readLink" : '\([^ ]*\)') = "no" ]; then + if $darwin; then + javaHome="$(dirname \"$javaExecutable\")" + javaExecutable="$(cd \"$javaHome\" && pwd -P)/javac" + else + javaExecutable="$(readlink -f \"$javaExecutable\")" + fi + javaHome="$(dirname \"$javaExecutable\")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ]; then + if [ -n "$JAVA_HOME" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="$(which java)" + fi +fi + +if [ ! -x "$JAVACMD" ]; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ]; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ]; then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ]; do + if [ -d "$wdir"/.mvn ]; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$( + cd "$wdir/.." + pwd + ) + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' <"$1")" + fi +} + +BASE_DIR=$(find_maven_basedir "$(pwd)") +if [ -z "$BASE_DIR" ]; then + exit 1 +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in wrapperUrl) + jarUrl="$value" + break + ;; + esac + done <"$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget >/dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl >/dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=$(cygpath --path --windows "$M2_HOME") + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/Api/api-core/mvnw.cmd b/Api/api-core/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/Api/api-core/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/Api/api-core/pom.xml b/Api/api-core/pom.xml new file mode 100644 index 000000000..fa58533d5 --- /dev/null +++ b/Api/api-core/pom.xml @@ -0,0 +1,105 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + api-core + 0.0.1-SNAPSHOT + api-core + Api logic of Mixchange + + + 1.8 + 1.4.31 + ${version} + ${version} + + + + + org.springframework.boot + spring-boot-starter + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + co.nilin.mixchange + matching-core + ${matching.version} + + + co.nilin.mixchange + accountant-core + ${accountant.version} + provided + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + org.springframework + spring-tx + provided + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/AllOrderRequest.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/AllOrderRequest.kt new file mode 100644 index 000000000..880b1c57c --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/AllOrderRequest.kt @@ -0,0 +1,10 @@ +package co.nilin.mixchange.api.core.inout + +import java.util.* + +class AllOrderRequest( + val symbol: String?, + val startTime: Date?, + val endTime: Date?, + val limit: Int? = 500, //Default 500; max 1000. +) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/CreateOrderRequest.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/CreateOrderRequest.kt new file mode 100644 index 000000000..8f3715618 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/CreateOrderRequest.kt @@ -0,0 +1,19 @@ +package co.nilin.mixchange.api.core.inout + +import java.math.BigDecimal + +data class CreateOrderRequest( + val symbol: String, + val side: OrderSide, + val type: OrderType, + val timeInForce: TimeInForce?, + val quantity: BigDecimal?, + val quoteOrderQty: BigDecimal?, + val price: BigDecimal?, + val newClientOrderId: String?, /* A unique id among open orders. Automatically generated if not sent. + Orders with the same newClientOrderID can be accepted only when the previous one is filled, otherwise the order will be rejected. + */ + val stopPrice: BigDecimal?, //Used with STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, and TAKE_PROFIT_LIMIT orders. + val icebergQty: BigDecimal?, //Used with LIMIT, STOP_LOSS_LIMIT, and TAKE_PROFIT_LIMIT to create an iceberg order. + val newOrderRespType: OrderResponseType?, //Set the response JSON. ACK, RESULT, or FULL; MARKET and LIMIT order types default to FULL, all other orders default to ACK. +) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/CreateOrderResponse.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/CreateOrderResponse.kt new file mode 100644 index 000000000..ba237241a --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/CreateOrderResponse.kt @@ -0,0 +1,21 @@ +package co.nilin.mixchange.api.core.inout + +import java.math.BigDecimal +import java.util.* + +data class CreateOrderResponse( + val symbol: String, + val orderId: Long, + val orderListId: Long, //Unless OCO, value will be -1 + val clientOrderId: String, + val transactTime: Date, + val price: BigDecimal?, + val origQty: BigDecimal?, + val executedQty: BigDecimal?, + val cummulativeQuoteQty: BigDecimal, + val status: OrderStatus?, + val timeInForce: TimeInForce?, + val type: OrderType?, + val side: OrderSide?, + val fills: List? +) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OrderEnums.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OrderEnums.kt new file mode 100644 index 000000000..99c1b9804 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OrderEnums.kt @@ -0,0 +1,37 @@ +package co.nilin.mixchange.api.core.inout + +enum class TimeInForce { + GTC, //Good Til Canceled, An order will be on the book unless the order is canceled. + IOC, //Immediate Or Cancel, An order will try to fill the order as much as it can before the order expires. + FOK, //Fill or Kill, An order will expire if the full order cannot be filled upon execution. +} + +enum class OrderStatus { + + NEW, //The order has been accepted by the engine. + PARTIALLY_FILLED, //A part of the order has been filled. + FILLED, //The order has been completed. + CANCELED, //The order has been canceled by the user. + PENDING_CANCEL, //Currently unused + REJECTED, //The order was not accepted by the engine and not processed. + EXPIRED //The order was canceled according to the order type's rules (e.g. LIMIT FOK orders with no fill, LIMIT IOC or MARKET orders that partially fill) or by the exchange, (e.g. orders canceled during liquidation, orders canceled during maintenance) +} + +enum class OrderType { + LIMIT, // timeInForce, quantity, price + MARKET, // quantity or quoteOrderQty + STOP_LOSS, // quantity, stopPrice + STOP_LOSS_LIMIT, // timeInForce, quantity, price, stopPrice + TAKE_PROFIT, // quantity, stopPrice + TAKE_PROFIT_LIMIT, // timeInForce, quantity, price, stopPrice + LIMIT_MAKER, // quantity, price +} + +enum class OrderSide { + BUY, + SELL +} + +enum class OrderResponseType { + ACK, RESULT, FULL +} \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OrderTradeData.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OrderTradeData.kt new file mode 100644 index 000000000..a1d468639 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OrderTradeData.kt @@ -0,0 +1,10 @@ +package co.nilin.mixchange.api.core.inout + +import java.math.BigDecimal + +data class OrderTradeData( + val price: BigDecimal, + val qty: BigDecimal, + val commission: BigDecimal, + val commissionAsset: String +) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/QueryOrderRequest.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/QueryOrderRequest.kt new file mode 100644 index 000000000..4d5039e61 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/QueryOrderRequest.kt @@ -0,0 +1,7 @@ +package co.nilin.mixchange.api.core.inout + +data class QueryOrderRequest( + val symbol: String, + val orderId: Long?, + val origClientOrderId: String? +) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/QueryOrderResponse.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/QueryOrderResponse.kt new file mode 100644 index 000000000..2da014ce1 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/QueryOrderResponse.kt @@ -0,0 +1,25 @@ +package co.nilin.mixchange.api.core.inout + +import java.math.BigDecimal +import java.util.* + +data class QueryOrderResponse( + val symbol: String, + val orderId: Long, + val orderListId: Long, //Unless part of an OCO, the value will always be -1. + val clientOrderId: String, + val price: BigDecimal, + val origQty: BigDecimal, + val executedQty: BigDecimal, + val cummulativeQuoteQty: BigDecimal, + val status: OrderStatus, + val timeInForce: TimeInForce, + val type: OrderType, + val side: OrderSide, + val stopPrice: BigDecimal?, + val icebergQty: BigDecimal?, + val time: Date, + val updateTime: Date, + val isWorking: Boolean, + val origQuoteOrderQty: BigDecimal +) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/TradeRequest.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/TradeRequest.kt new file mode 100644 index 000000000..0baff32a0 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/TradeRequest.kt @@ -0,0 +1,10 @@ +package co.nilin.mixchange.api.core.inout + +import java.util.* + +class TradeRequest(val symbol: String?, + val fromTrade: Long?, + val startTime: Date?, + val endTime: Date?, + val limit: Int? = 500 //Default 500; max 1000. +) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/TradeResponse.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/TradeResponse.kt new file mode 100644 index 000000000..59ded6428 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/TradeResponse.kt @@ -0,0 +1,19 @@ +package co.nilin.mixchange.api.core.inout + +import java.math.BigDecimal +import java.util.* + +data class TradeResponse( + val symbol: String, + val id: Long, + val orderId: Long, + val orderListId: Long = -1, + val price: BigDecimal, + val qty: BigDecimal, + val quoteQty: BigDecimal, + val commission: BigDecimal, + val commissionAsset: String, + val time: Date, + val isBuyer: Boolean, + val isMaker: Boolean, + val isBestMatch: Boolean) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/MEGatewayProxy.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/MEGatewayProxy.kt new file mode 100644 index 000000000..2dd6b2a17 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/MEGatewayProxy.kt @@ -0,0 +1,22 @@ +package co.nilin.mixchange.api.core.spi + +import co.nilin.mixchange.matching.core.model.MatchConstraint +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.OrderType +import java.math.BigDecimal + +interface MEGatewayProxy { + data class CreateOrderRequest( + var uuid: String?, + val pair: String, + val price: BigDecimal, + val quantity: BigDecimal, + val direction: OrderDirection, + val matchConstraint: MatchConstraint, + val orderType: OrderType + ) + + class OrderSubmitResult(offset: Long?) + + fun createNewOrder(order: CreateOrderRequest): OrderSubmitResult; +} \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/OrderPersister.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/OrderPersister.kt new file mode 100644 index 000000000..dcc404034 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/OrderPersister.kt @@ -0,0 +1,7 @@ +package co.nilin.mixchange.api.core.spi + +import co.nilin.mixchange.accountant.core.inout.RichOrder + +interface OrderPersister { + suspend fun save(order: RichOrder) +} \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/TradePersister.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/TradePersister.kt new file mode 100644 index 000000000..5574ac065 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/TradePersister.kt @@ -0,0 +1,7 @@ +package co.nilin.mixchange.api.core.spi + +import co.nilin.mixchange.accountant.core.inout.RichTrade + +interface TradePersister { + suspend fun save(trade: RichTrade) +} \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/UserQueryHandler.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/UserQueryHandler.kt new file mode 100644 index 000000000..a2ad706da --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/UserQueryHandler.kt @@ -0,0 +1,12 @@ +package co.nilin.mixchange.api.core.spi + +import co.nilin.mixchange.api.core.inout.* +import kotlinx.coroutines.flow.Flow +import java.security.Principal + +interface UserQueryHandler { + suspend fun queryOrder(principal: Principal, request: QueryOrderRequest): QueryOrderResponse? + suspend fun openOrders(principal: Principal, symbol: String?): Flow + suspend fun allOrders(principal: Principal, allOrderRequest: AllOrderRequest): Flow + suspend fun allTrades(principal: Principal, request: TradeRequest): Flow +} \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/WalletProxy.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/WalletProxy.kt new file mode 100644 index 000000000..0c4e2c72b --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/WalletProxy.kt @@ -0,0 +1,4 @@ +package co.nilin.mixchange.api.core.spi + +interface WalletProxy { +} \ No newline at end of file diff --git a/Api/api-ports/.gitignore b/Api/api-ports/.gitignore new file mode 100644 index 000000000..f3a8317d6 --- /dev/null +++ b/Api/api-ports/.gitignore @@ -0,0 +1,4 @@ +*.iml +target/ +.mvn/ +.idea/ \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/.gitignore b/Api/api-ports/api-binance-rest/.gitignore new file mode 100644 index 000000000..f3a8317d6 --- /dev/null +++ b/Api/api-ports/api-binance-rest/.gitignore @@ -0,0 +1,4 @@ +*.iml +target/ +.mvn/ +.idea/ \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/mvnw b/Api/api-ports/api-binance-rest/mvnw new file mode 100644 index 000000000..3c8a55373 --- /dev/null +++ b/Api/api-ports/api-binance-rest/mvnw @@ -0,0 +1,322 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ]; then + + if [ -f /etc/mavenrc ]; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ]; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false +darwin=false +mingw=false +case "$(uname)" in +CYGWIN*) cygwin=true ;; +MINGW*) mingw=true ;; +Darwin*) + darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="$(/usr/libexec/java_home)" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ]; then + if [ -r /etc/gentoo-release ]; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +if [ -z "$M2_HOME" ]; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ]; do + ls=$(ls -ld "$PRG") + link=$(expr "$ls" : '.*-> \(.*\)$') + if expr "$link" : '/.*' >/dev/null; then + PRG="$link" + else + PRG="$(dirname "$PRG")/$link" + fi + done + + saveddir=$(pwd) + + M2_HOME=$(dirname "$PRG")/.. + + # make it fully qualified + M2_HOME=$(cd "$M2_HOME" && pwd) + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=$(cygpath --unix "$M2_HOME") + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw; then + [ -n "$M2_HOME" ] && + M2_HOME="$( ( + cd "$M2_HOME" + pwd + ))" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="$( ( + cd "$JAVA_HOME" + pwd + ))" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr \"$javaExecutable\" : '\([^ ]*\)')" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=$(which readlink) + if [ ! $(expr "$readLink" : '\([^ ]*\)') = "no" ]; then + if $darwin; then + javaHome="$(dirname \"$javaExecutable\")" + javaExecutable="$(cd \"$javaHome\" && pwd -P)/javac" + else + javaExecutable="$(readlink -f \"$javaExecutable\")" + fi + javaHome="$(dirname \"$javaExecutable\")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ]; then + if [ -n "$JAVA_HOME" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="$(which java)" + fi +fi + +if [ ! -x "$JAVACMD" ]; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ]; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ]; then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ]; do + if [ -d "$wdir"/.mvn ]; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$( + cd "$wdir/.." + pwd + ) + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' <"$1")" + fi +} + +BASE_DIR=$(find_maven_basedir "$(pwd)") +if [ -z "$BASE_DIR" ]; then + exit 1 +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in wrapperUrl) + jarUrl="$value" + break + ;; + esac + done <"$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget >/dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl >/dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=$(cygpath --path --windows "$M2_HOME") + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/Api/api-ports/api-binance-rest/mvnw.cmd b/Api/api-ports/api-binance-rest/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/Api/api-ports/api-binance-rest/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/Api/api-ports/api-binance-rest/pom.xml b/Api/api-ports/api-binance-rest/pom.xml new file mode 100644 index 000000000..b0952e230 --- /dev/null +++ b/Api/api-ports/api-binance-rest/pom.xml @@ -0,0 +1,153 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + api-binance-rest + 0.0.1-SNAPSHOT + api-binance-rest + Api Binance Rest + + + 1.8 + 1.4.31 + ${version} + ${version} + 2020.0.2 + + + + + co.nilin.mixchange + matching-core + ${matching.version} + provided + + + co.nilin.mixchange + api-core + ${api.version} + provided + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework.cloud + spring-cloud-starter-consul-all + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.boot + spring-boot-starter-data-r2dbc + + + io.r2dbc + r2dbc-postgresql + runtime + + + org.postgresql + postgresql + runtime + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + + org.bouncycastle + bcprov-jdk15on + 1.60 + + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + io.projectreactor + reactor-test + test + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/RestConfig.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/RestConfig.kt new file mode 100644 index 000000000..63e5ec49b --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/RestConfig.kt @@ -0,0 +1,23 @@ +package co.nilin.mixchange.port.api.binance.config + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.format.Formatter +import java.util.* + + +@Configuration +class RestConfig { + @Bean + fun dateFormatter(): Formatter? { + return object : Formatter { + override fun print(date: Date, locale: Locale): String { + return date.time.toString() + } + + override fun parse(date: String, locale: Locale): Date { + return Date(date.toLong()) + } + } + } +} \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/SecurityConfig.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/SecurityConfig.kt new file mode 100644 index 000000000..c6843a4e5 --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/SecurityConfig.kt @@ -0,0 +1,45 @@ +package co.nilin.mixchange.port.api.binance.config + +import org.springframework.context.annotation.Bean +import org.springframework.core.io.ClassPathResource +import org.springframework.core.io.Resource +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity +import org.springframework.security.config.web.server.ServerHttpSecurity +import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder +import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder +import org.springframework.security.web.server.SecurityWebFilterChain +import org.springframework.util.Base64Utils +import org.springframework.util.FileCopyUtils +import java.security.KeyFactory +import java.security.interfaces.RSAPublicKey +import java.security.spec.X509EncodedKeySpec + +@EnableWebFluxSecurity +class SecurityConfig { + @Bean + fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain? { + http.csrf().disable() + .authorizeExchange() + .pathMatchers("/hello").permitAll() + .pathMatchers("/actuator/**").permitAll() + .pathMatchers("/**").hasAuthority("SCOPE_trust") + .anyExchange().authenticated() + .and() + .oauth2ResourceServer() + .jwt() + return http.build() + } + + @Bean + @Throws(Exception::class) + fun reactiveJwtDecoder(): ReactiveJwtDecoder? { + val resource: Resource = ClassPathResource("/public.cert") + val publicKey = String(FileCopyUtils.copyToByteArray(resource.inputStream)) + .replace("\r", "") + .replace("-----BEGIN PUBLIC KEY-----\n", "") + .replace("\n-----END PUBLIC KEY-----", "") + val spec = X509EncodedKeySpec(Base64Utils.decodeFromString(publicKey)) + val kf = KeyFactory.getInstance("RSA") + return NimbusReactiveJwtDecoder(kf.generatePublic(spec) as RSAPublicKey) + } +} diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/WebClientConfig.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/WebClientConfig.kt new file mode 100644 index 000000000..76e029a40 --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/WebClientConfig.kt @@ -0,0 +1,25 @@ +package co.nilin.mixchange.port.api.binance.config + +import org.springframework.cloud.client.ServiceInstance +import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties +import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer +import org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerExchangeFilterFunction +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.web.reactive.function.client.WebClient + +@Configuration +class WebClientConfig { + + @Bean + fun webClient(loadBalancerFactory: ReactiveLoadBalancer.Factory): WebClient { + return WebClient.builder() + .filter( + ReactorLoadBalancerExchangeFilterFunction( + loadBalancerFactory, LoadBalancerProperties(), emptyList() + ) + ) + .build() + } + +} diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/AccountController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/AccountController.kt new file mode 100644 index 000000000..5740cde1a --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/AccountController.kt @@ -0,0 +1,245 @@ +package co.nilin.mixchange.port.api.binance.controller + +import co.nilin.mixchange.api.core.inout.* +import co.nilin.mixchange.api.core.spi.UserQueryHandler +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import org.springframework.http.MediaType +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController +import reactor.core.publisher.Mono +import java.math.BigDecimal +import java.security.Principal +import java.util.* + +@RestController +class AccountController(val queryHandler: UserQueryHandler) { + + data class FillsData( + val price: BigDecimal, + val qty: BigDecimal, + val commission: BigDecimal, + val commissionAsset: String + ) + + data class NewOrderResponse( + val symbol: String, + val orderId: Long, + val orderListId: Long, //Unless OCO, value will be -1 + val clientOrderId: String, + val transactTime: Date, + val price: BigDecimal?, + val origQty: BigDecimal?, + val executedQty: BigDecimal?, + val cummulativeQuoteQty: BigDecimal, + val status: OrderStatus?, + val timeInForce: TimeInForce?, + val type: OrderType?, + val side: OrderSide?, + val fills: List? + ) + + data class QueryOrderResponse( + val symbol: String, + val orderId: Long, + val orderListId: Long, //Unless part of an OCO, the value will always be -1. + val clientOrderId: String, + val price: BigDecimal, + val origQty: BigDecimal, + val executedQty: BigDecimal, + val cummulativeQuoteQty: BigDecimal, + val status: OrderStatus, + val timeInForce: TimeInForce, + val type: OrderType, + val side: OrderSide, + val stopPrice: BigDecimal?, + val icebergQty: BigDecimal?, + val time: Date, + val updateTime: Date, + val isWorking: Boolean, + val origQuoteOrderQty: BigDecimal + ) + + data class TradeResponse( + val symbol: String, + val id: Long, + val orderId: Long, + val orderListId: Long = -1, + val price: BigDecimal, + val qty: BigDecimal, + val quoteQty: BigDecimal, + val commission: BigDecimal, + val commissionAsset: String, + val time: Date, + val isBuyer: Boolean, + val isMaker: Boolean, + val isBestMatch: Boolean) + + /* + Send in a new order. + Weight: 1 + Data Source: Matching Engine + */ + @PostMapping( + "/api/v3/order", + consumes = arrayOf(MediaType.APPLICATION_FORM_URLENCODED_VALUE), + produces = arrayOf(MediaType.APPLICATION_JSON_VALUE) + ) + fun createNewOrder( + @RequestParam(name = "symbol") + symbol: String, + @RequestParam(name = "side") + side: OrderSide, + @RequestParam(name = "type") + type: OrderType, + @RequestParam(name = "timeInForce", required = false) + timeInForce: TimeInForce?, + @RequestParam(name = "quantity", required = false) + quantity: BigDecimal?, + @RequestParam(name = "quoteOrderQty", required = false) + quoteOrderQty: BigDecimal?, + @RequestParam(name = "price", required = false) + price: BigDecimal?, + @RequestParam(name = "newClientOrderId", required = false) + newClientOrderId: String?, /* A unique id among open orders. Automatically generated if not sent. + Orders with the same newClientOrderID can be accepted only when the previous one is filled, otherwise the order will be rejected. + */ + @RequestParam(name = "stopPrice", required = false) + stopPrice: BigDecimal?, //Used with STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, and TAKE_PROFIT_LIMIT orders. + @RequestParam(name = "icebergQty", required = false) + icebergQty: BigDecimal?, //Used with LIMIT, STOP_LOSS_LIMIT, and TAKE_PROFIT_LIMIT to create an iceberg order. + @RequestParam(name = "newOrderRespType", required = false) + newOrderRespType: OrderResponseType?, //Set the response JSON. ACK, RESULT, or FULL; MARKET and LIMIT order types default to FULL, all other orders default to ACK. + @RequestParam(name = "recvWindow", required = false) + recvWindow: Long?, //The value cannot be greater than 60000 + @RequestParam(name = "timestamp") + timestamp: Long + ): Mono { + TODO("Implement create order") + } + + /* + Check an order's status. + + Weight: 2 + Data Source: Database + */ + @GetMapping( + "/api/v3/order", + consumes = arrayOf(MediaType.APPLICATION_FORM_URLENCODED_VALUE), + produces = arrayOf(MediaType.APPLICATION_JSON_VALUE) + ) + suspend fun queryOrder(principal: Principal, + @RequestParam(name = "symbol") + symbol: String, + @RequestParam(name = "orderId", required = false) + orderId: Long?, + @RequestParam(name = "origClientOrderId", required = false) + origClientOrderId: String?, + @RequestParam(name = "recvWindow", required = false) + recvWindow: Long?, //The value cannot be greater than 60000 + @RequestParam(name = "timestamp") + timestamp: Long + ): QueryOrderResponse { + val response = queryHandler.queryOrder(principal, QueryOrderRequest(symbol, orderId, origClientOrderId)) + if (response == null) + throw IllegalArgumentException("no order found") + return QueryOrderResponse(response.symbol, response.orderId, response.orderListId, response.clientOrderId, response.price, response.origQty, response.executedQty, response.cummulativeQuoteQty, response.status, response.timeInForce, response.type, response.side, response.stopPrice, response.icebergQty, response.time, response.updateTime, response.isWorking, response.origQuoteOrderQty) + } + + + /* + Get all open orders on a symbol. Careful when accessing this with no symbol. + + Weight: 3 for a single symbol; 40 when the symbol parameter is omitted + + Data Source: Database + */ + @GetMapping( + "/api/v3/openOrders", + consumes = arrayOf(MediaType.APPLICATION_FORM_URLENCODED_VALUE), + produces = arrayOf(MediaType.APPLICATION_JSON_VALUE) + ) + suspend fun fetchOpenOrders(principal: Principal, + @RequestParam(name = "symbol", required = false) + symbol: String?, + @RequestParam(name = "recvWindow", required = false) + recvWindow: Long?, //The value cannot be greater than 60000 + @RequestParam(name = "timestamp") + timestamp: Long + ): Flow { + return queryHandler.openOrders(principal, symbol) + .map { response -> + QueryOrderResponse(response.symbol, response.orderId, response.orderListId, response.clientOrderId, response.price, response.origQty, response.executedQty, response.cummulativeQuoteQty, response.status, response.timeInForce, response.type, response.side, response.stopPrice, response.icebergQty, response.time, response.updateTime, response.isWorking, response.origQuoteOrderQty) + } + } + + /* + Get all account orders; active, canceled, or filled. + Weight: 10 with symbol + Data Source: Database + */ + @GetMapping( + "/api/v3/allOrders", + consumes = arrayOf(MediaType.APPLICATION_FORM_URLENCODED_VALUE), + produces = arrayOf(MediaType.APPLICATION_JSON_VALUE) + ) + suspend fun fetchAllOrders(principal: Principal, + @RequestParam(name = "symbol", required = false) + symbol: String?, + @RequestParam(name = "startTime", required = false) + startTime: Date?, + @RequestParam(name = "endTime", required = false) + endTime: Date?, + @RequestParam(name = "limit", required = false) + limit: Int? = 500, //Default 500; max 1000. + @RequestParam(name = "recvWindow", required = false) + recvWindow: Long?, //The value cannot be greater than 60000 + @RequestParam(name = "timestamp") + timestamp: Long + ): Flow { + return queryHandler.allOrders(principal, AllOrderRequest(symbol, startTime, endTime, limit)) + .map { response -> + QueryOrderResponse(response.symbol, response.orderId, response.orderListId, response.clientOrderId, response.price, response.origQty, response.executedQty, response.cummulativeQuoteQty, response.status, response.timeInForce, response.type, response.side, response.stopPrice, response.icebergQty, response.time, response.updateTime, response.isWorking, response.origQuoteOrderQty) + } + } + + /* + Get trades for a specific account and symbol. + If fromId is set, it will get trades >= that fromId. Otherwise most recent trades are returned. + Weight: 10 with symbol + Data Source: Database + */ + @GetMapping( + "/api/v3/myTrades", + consumes = arrayOf(MediaType.APPLICATION_FORM_URLENCODED_VALUE), + produces = arrayOf(MediaType.APPLICATION_JSON_VALUE) + ) + suspend fun fetchAllTrades(principal: Principal, + @RequestParam(name = "symbol") + symbol: String?, + @RequestParam(name = "startTime", required = false) + startTime: Date?, + @RequestParam(name = "endTime", required = false) + endTime: Date?, + @RequestParam(name = "fromId", required = false) + fromId: Long?,//TradeId to fetch from. Default gets most recent trades. + @RequestParam(name = "limit", required = false) + limit: Int? = 500, //Default 500; max 1000. + @RequestParam(name = "recvWindow", required = false) + recvWindow: Long?, //The value cannot be greater than 60000 + @RequestParam(name = "timestamp") + timestamp: Long + ): Flow { + return queryHandler.allTrades(principal, TradeRequest(symbol, fromId, startTime, endTime, limit)) + .map { response -> + TradeResponse(response.symbol, response.id, + response.orderId, -1, response.price, response.qty, response.quoteQty, + response.commission, response.commissionAsset, response.time, response.isBuyer, + response.isMaker, response.isBestMatch) + } + } + +} \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/FiltersController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/FiltersController.kt new file mode 100644 index 000000000..f0b6dbd9d --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/FiltersController.kt @@ -0,0 +1,7 @@ +package co.nilin.mixchange.port.api.binance.controller + +import org.springframework.web.bind.annotation.RestController + +@RestController +class FiltersController { +} \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/MarketController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/MarketController.kt new file mode 100644 index 000000000..625179066 --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/MarketController.kt @@ -0,0 +1,8 @@ +package co.nilin.mixchange.port.api.binance.controller + +import org.springframework.web.bind.annotation.RestController + +@RestController +class MarketController { + +} \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/proxy/WalletProxyImpl.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/proxy/WalletProxyImpl.kt new file mode 100644 index 000000000..7406db078 --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/proxy/WalletProxyImpl.kt @@ -0,0 +1,59 @@ +package co.nilin.mixchange.port.api.binance.proxy + +import co.nilin.mixchange.api.core.spi.WalletProxy +import org.springframework.core.ParameterizedTypeReference +import org.springframework.stereotype.Component +import org.springframework.web.reactive.function.client.WebClient +import java.math.BigDecimal +import java.time.LocalDateTime + +inline fun typeRef(): ParameterizedTypeReference = object : ParameterizedTypeReference() {} +data class TransferResult(val date: LocalDateTime, val sourceBalanceBeforeAction: Amount, val sourceBalanceAfterAction: Amount, val amount: Amount) +data class Amount(val currency: Currency, val amount: BigDecimal) +data class Currency(val name: String, val symbol: String, val precision: Int) + +@Component +class WalletProxyImpl(val webClient: WebClient +) : WalletProxy { + /*override suspend fun transfer( + symbol: String, + senderWalletType: String, + senderUuid: String, + receiverWalletType: String, + receiverUuid: String, + amount: BigDecimal, + description: String?, + transferRef: String? + ) { + webClient.post() + .uri(URI.create("$walletBaseUrl/transfer/${amount}_${symbol}/from/${senderUuid}_${senderWalletType}/to/${receiverUuid}_${receiverWalletType}")) + .header("Content-Type", "application/json") + .retrieve() + .onStatus({ t -> t.isError }, { p -> + *//* + p.bodyToMono(typeRef>()).map { t -> KycSejamException(p.statusCode().value().toString(), t.error?.errorCode.toString() + + "-" + t.error?.customMessage) } + *//* + throw RuntimeException() + }) + .bodyToMono(typeRef()) + .log() + .awaitFirst() + + } + + override suspend fun canFulfil(symbol: String, walletType: String, uuid: String, amount: BigDecimal): Boolean { + data class BooleanResponse(val result: Boolean) + return webClient.get() + .uri(URI.create("$walletBaseUrl/$uuid/wallet_type/${walletType}/can_withdraw/${amount}_${symbol}")) + .header("Content-Type", "application/json") + .retrieve() + .onStatus({ t -> t.isError }, { p -> + throw RuntimeException() + }) + .bodyToMono(typeRef()) + .log() + .awaitFirst() + .result + }*/ +} \ No newline at end of file diff --git a/Api/api-ports/api-eventlistener-kafka/.gitignore b/Api/api-ports/api-eventlistener-kafka/.gitignore new file mode 100644 index 000000000..bb9840a17 --- /dev/null +++ b/Api/api-ports/api-eventlistener-kafka/.gitignore @@ -0,0 +1,34 @@ +HELP.md +target/ +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +.mvn/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ + + +### VS Code ### +.vscode/ + +.DS_Store diff --git a/Api/api-ports/api-eventlistener-kafka/mvnw b/Api/api-ports/api-eventlistener-kafka/mvnw new file mode 100644 index 000000000..3c8a55373 --- /dev/null +++ b/Api/api-ports/api-eventlistener-kafka/mvnw @@ -0,0 +1,322 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ]; then + + if [ -f /etc/mavenrc ]; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ]; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false +darwin=false +mingw=false +case "$(uname)" in +CYGWIN*) cygwin=true ;; +MINGW*) mingw=true ;; +Darwin*) + darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="$(/usr/libexec/java_home)" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ]; then + if [ -r /etc/gentoo-release ]; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +if [ -z "$M2_HOME" ]; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ]; do + ls=$(ls -ld "$PRG") + link=$(expr "$ls" : '.*-> \(.*\)$') + if expr "$link" : '/.*' >/dev/null; then + PRG="$link" + else + PRG="$(dirname "$PRG")/$link" + fi + done + + saveddir=$(pwd) + + M2_HOME=$(dirname "$PRG")/.. + + # make it fully qualified + M2_HOME=$(cd "$M2_HOME" && pwd) + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=$(cygpath --unix "$M2_HOME") + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw; then + [ -n "$M2_HOME" ] && + M2_HOME="$( ( + cd "$M2_HOME" + pwd + ))" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="$( ( + cd "$JAVA_HOME" + pwd + ))" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr \"$javaExecutable\" : '\([^ ]*\)')" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=$(which readlink) + if [ ! $(expr "$readLink" : '\([^ ]*\)') = "no" ]; then + if $darwin; then + javaHome="$(dirname \"$javaExecutable\")" + javaExecutable="$(cd \"$javaHome\" && pwd -P)/javac" + else + javaExecutable="$(readlink -f \"$javaExecutable\")" + fi + javaHome="$(dirname \"$javaExecutable\")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ]; then + if [ -n "$JAVA_HOME" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="$(which java)" + fi +fi + +if [ ! -x "$JAVACMD" ]; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ]; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ]; then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ]; do + if [ -d "$wdir"/.mvn ]; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$( + cd "$wdir/.." + pwd + ) + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' <"$1")" + fi +} + +BASE_DIR=$(find_maven_basedir "$(pwd)") +if [ -z "$BASE_DIR" ]; then + exit 1 +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in wrapperUrl) + jarUrl="$value" + break + ;; + esac + done <"$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget >/dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl >/dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=$(cygpath --path --windows "$M2_HOME") + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/Api/api-ports/api-eventlistener-kafka/mvnw.cmd b/Api/api-ports/api-eventlistener-kafka/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/Api/api-ports/api-eventlistener-kafka/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/Api/api-ports/api-eventlistener-kafka/pom.xml b/Api/api-ports/api-eventlistener-kafka/pom.xml new file mode 100644 index 000000000..36ac78c3c --- /dev/null +++ b/Api/api-ports/api-eventlistener-kafka/pom.xml @@ -0,0 +1,120 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + api-eventlistener-kafka + 0.0.1-SNAPSHOT + api-eventlistener-kafka + Api kafka listener of Mixchange + + + 1.8 + 1.4.31 + ${version} + ${version} + ${version} + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-webflux + + + co.nilin.mixchange + matching-core + ${matching.version} + provided + + + co.nilin.mixchange + accountant-core + ${accountant.version} + provided + + + co.nilin.mixchange + api-core + ${api.version} + provided + + + org.springframework.kafka + spring-kafka + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + org.springframework.kafka + spring-kafka-test + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + diff --git a/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/config/ApiKafkaConfig.kt b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/config/ApiKafkaConfig.kt new file mode 100644 index 000000000..634c18db7 --- /dev/null +++ b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/config/ApiKafkaConfig.kt @@ -0,0 +1,109 @@ +package co.nilin.mixchange.port.api.kafka.config + + +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +import co.nilin.mixchange.port.api.kafka.consumer.OrderKafkaListener +import co.nilin.mixchange.port.trade.consumer.EventKafkaListener +import co.nilin.mixchange.port.trade.consumer.TradeKafkaListener +import org.apache.kafka.clients.admin.NewTopic +import org.apache.kafka.clients.consumer.ConsumerConfig +import org.apache.kafka.clients.producer.ProducerConfig +import org.apache.kafka.common.serialization.StringDeserializer +import org.apache.kafka.common.serialization.StringSerializer +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.support.GenericApplicationContext +import org.springframework.kafka.core.* +import org.springframework.kafka.listener.ConcurrentMessageListenerContainer +import org.springframework.kafka.listener.ContainerProperties +import org.springframework.kafka.support.serializer.JsonDeserializer +import org.springframework.kafka.support.serializer.JsonSerializer +import java.util.regex.Pattern + + +@Configuration +class ApiKafkaConfig { + @Value("\${spring.kafka.bootstrap-servers}") + private val bootstrapServers: String? = null + + @Value("\${spring.kafka.consumer.group-id}") + private val groupId: String? = null + + @Bean("apiConsumerConfig") + fun consumerConfigs(): Map? { + val props: MutableMap = HashMap() + props[ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG] = bootstrapServers + props[ConsumerConfig.GROUP_ID_CONFIG] = groupId + props[ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG] = StringDeserializer::class.java + props[ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG] = JsonDeserializer::class.java + props[JsonDeserializer.TRUSTED_PACKAGES] = "co.nilin.mixchange.*" + return props + } + + @Bean("apiConsumerFactory") + fun consumerFactory(@Qualifier("apiConsumerConfig") consumerConfigs: Map): ConsumerFactory { + return DefaultKafkaConsumerFactory(consumerConfigs) + } + + @Bean("apiProducerConfig") + fun producerConfigs(): Map { + val props: MutableMap = HashMap() + props[ProducerConfig.BOOTSTRAP_SERVERS_CONFIG] = bootstrapServers + props[ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG] = StringSerializer::class.java + props[ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG] = JsonSerializer::class.java + return props + } + + @Bean("apiProducerFactory") + fun producerFactory(@Qualifier("apiProducerConfig") producerConfigs: Map): ProducerFactory { + return DefaultKafkaProducerFactory(producerConfigs) + } + + @Bean("apiKafkaTemplate") + fun kafkaTemplate(@Qualifier("apiProducerFactory") producerFactory: ProducerFactory): KafkaTemplate { + return KafkaTemplate(producerFactory) + } + + + @Autowired + @ConditionalOnBean(TradeKafkaListener::class) + fun configureTradeListener(tradeListener: TradeKafkaListener, @Qualifier("apiConsumerFactory") consumerFactory: ConsumerFactory) { + val containerProps = ContainerProperties(Pattern.compile("richTrade")) + containerProps.messageListener = tradeListener + val container = ConcurrentMessageListenerContainer(consumerFactory, containerProps) + container.setBeanName("ApiTradeKafkaListenerContainer") + container.start() + } + + @Autowired + @ConditionalOnBean(EventKafkaListener::class) + fun configureEventListener(eventListener: EventKafkaListener, @Qualifier("apiConsumerFactory") consumerFactory: ConsumerFactory) { + val containerProps = ContainerProperties(Pattern.compile("events_.*")) + containerProps.messageListener = eventListener + val container = ConcurrentMessageListenerContainer(consumerFactory, containerProps) + container.setBeanName("ApiEventKafkaListenerContainer") + container.start() + } + + @Autowired + @ConditionalOnBean(OrderKafkaListener::class) + fun configureOrderListener(orderListener: OrderKafkaListener, @Qualifier("apiConsumerFactory") consumerFactory: ConsumerFactory) { + val containerProps = ContainerProperties(Pattern.compile("richOrder")) + containerProps.messageListener = orderListener + val container = ConcurrentMessageListenerContainer(consumerFactory, containerProps) + container.setBeanName("ApiOrderKafkaListenerContainer") + container.start() + } + + @Autowired + fun createTopics(applicationContext: GenericApplicationContext) { + applicationContext.registerBean("topic_richOrder", NewTopic::class.java, "richOrder", 10, 1) + applicationContext.registerBean("topic_richTrade", NewTopic::class.java, "richTrade", 10, 1) + } + + +} \ No newline at end of file diff --git a/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/consumer/EventKafkaListener.kt b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/consumer/EventKafkaListener.kt new file mode 100644 index 000000000..a2dbd173f --- /dev/null +++ b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/consumer/EventKafkaListener.kt @@ -0,0 +1,28 @@ +package co.nilin.mixchange.port.trade.consumer + + +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +import co.nilin.mixchange.port.trade.spi.EventListener +import org.apache.kafka.clients.consumer.ConsumerRecord +import org.springframework.kafka.listener.MessageListener +import org.springframework.stereotype.Component + +@Component +class EventKafkaListener : MessageListener { + val eventListeners = arrayListOf() + override fun onMessage(data: ConsumerRecord) { + eventListeners.forEach { tl -> + tl.onEvent(data.value(), data.partition(), data.offset(), data.timestamp()) + } + } + + fun addEventListener(tl: EventListener) { + eventListeners.add(tl) + } + + fun removeEventListener(tl: EventListener) { + eventListeners.removeIf { item -> + item.id() == tl.id() + } + } +} \ No newline at end of file diff --git a/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/consumer/OrderKafkaListener.kt b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/consumer/OrderKafkaListener.kt new file mode 100644 index 000000000..a8e8a64d2 --- /dev/null +++ b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/consumer/OrderKafkaListener.kt @@ -0,0 +1,29 @@ +package co.nilin.mixchange.port.api.kafka.consumer + +import co.nilin.mixchange.accountant.core.inout.RichOrder +import co.nilin.mixchange.port.api.kafka.spi.RichOrderListener +import org.apache.kafka.clients.consumer.ConsumerRecord +import org.springframework.kafka.listener.MessageListener +import org.springframework.stereotype.Component + +@Component +class OrderKafkaListener() : + MessageListener { + val orderListeners = arrayListOf() + override fun onMessage(data: ConsumerRecord) { + orderListeners.forEach { tl -> + tl.onOrder(data.value(), data.partition(), data.offset(), data.timestamp()) + } + + } + + fun addOrderListener(tl: RichOrderListener) { + orderListeners.add(tl) + } + + fun removeOrderListener(tl: RichOrderListener) { + orderListeners.removeIf { item -> + item.id() == tl.id() + } + } +} \ No newline at end of file diff --git a/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/consumer/TradeKafkaListener.kt b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/consumer/TradeKafkaListener.kt new file mode 100644 index 000000000..51f8b8b1b --- /dev/null +++ b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/consumer/TradeKafkaListener.kt @@ -0,0 +1,28 @@ +package co.nilin.mixchange.port.trade.consumer + + +import co.nilin.mixchange.accountant.core.inout.RichTrade +import co.nilin.mixchange.port.trade.spi.RichTradeListener +import org.apache.kafka.clients.consumer.ConsumerRecord +import org.springframework.kafka.listener.MessageListener +import org.springframework.stereotype.Component + +@Component +class TradeKafkaListener : MessageListener { + val tradeListeners = arrayListOf() + override fun onMessage(data: ConsumerRecord) { + tradeListeners.forEach { tl -> + tl.onTrade(data.value(), data.partition(), data.offset(), data.timestamp()) + } + } + + fun addTradeListener(tl: RichTradeListener) { + tradeListeners.add(tl) + } + + fun removeTradeListener(tl: RichTradeListener) { + tradeListeners.removeIf { item -> + item.id() == tl.id() + } + } +} \ No newline at end of file diff --git a/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/spi/EventListener.kt b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/spi/EventListener.kt new file mode 100644 index 000000000..0f522ca46 --- /dev/null +++ b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/spi/EventListener.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.port.trade.spi + +import co.nilin.mixchange.matching.core.eventh.events.CoreEvent + + +interface EventListener { + fun id(): String + fun onEvent(coreEvent: CoreEvent, partition: Int, offset: Long, timestamp: Long) +} \ No newline at end of file diff --git a/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/spi/RichOrderListener.kt b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/spi/RichOrderListener.kt new file mode 100644 index 000000000..c757f523e --- /dev/null +++ b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/spi/RichOrderListener.kt @@ -0,0 +1,8 @@ +package co.nilin.mixchange.port.api.kafka.spi + +import co.nilin.mixchange.accountant.core.inout.RichOrder + +interface RichOrderListener { + fun id(): String + fun onOrder(order: RichOrder, partition: Int, offset: Long, timestamp: Long) +} \ No newline at end of file diff --git a/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/spi/RichTradeListener.kt b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/spi/RichTradeListener.kt new file mode 100644 index 000000000..e8b490335 --- /dev/null +++ b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/spi/RichTradeListener.kt @@ -0,0 +1,8 @@ +package co.nilin.mixchange.port.trade.spi + +import co.nilin.mixchange.accountant.core.inout.RichTrade + +interface RichTradeListener { + fun id(): String + fun onTrade(trade: RichTrade, partition: Int, offset: Long, timestamp: Long) +} \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/.gitignore b/Api/api-ports/api-persister-postgres/.gitignore new file mode 100644 index 000000000..de5a9214d --- /dev/null +++ b/Api/api-ports/api-persister-postgres/.gitignore @@ -0,0 +1,34 @@ +HELP.md +target/ +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +.mvn/ +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +.DS_Store diff --git a/Api/api-ports/api-persister-postgres/mvnw b/Api/api-ports/api-persister-postgres/mvnw new file mode 100644 index 000000000..3c8a55373 --- /dev/null +++ b/Api/api-ports/api-persister-postgres/mvnw @@ -0,0 +1,322 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ]; then + + if [ -f /etc/mavenrc ]; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ]; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false +darwin=false +mingw=false +case "$(uname)" in +CYGWIN*) cygwin=true ;; +MINGW*) mingw=true ;; +Darwin*) + darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="$(/usr/libexec/java_home)" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ]; then + if [ -r /etc/gentoo-release ]; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +if [ -z "$M2_HOME" ]; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ]; do + ls=$(ls -ld "$PRG") + link=$(expr "$ls" : '.*-> \(.*\)$') + if expr "$link" : '/.*' >/dev/null; then + PRG="$link" + else + PRG="$(dirname "$PRG")/$link" + fi + done + + saveddir=$(pwd) + + M2_HOME=$(dirname "$PRG")/.. + + # make it fully qualified + M2_HOME=$(cd "$M2_HOME" && pwd) + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=$(cygpath --unix "$M2_HOME") + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw; then + [ -n "$M2_HOME" ] && + M2_HOME="$( ( + cd "$M2_HOME" + pwd + ))" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="$( ( + cd "$JAVA_HOME" + pwd + ))" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr \"$javaExecutable\" : '\([^ ]*\)')" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=$(which readlink) + if [ ! $(expr "$readLink" : '\([^ ]*\)') = "no" ]; then + if $darwin; then + javaHome="$(dirname \"$javaExecutable\")" + javaExecutable="$(cd \"$javaHome\" && pwd -P)/javac" + else + javaExecutable="$(readlink -f \"$javaExecutable\")" + fi + javaHome="$(dirname \"$javaExecutable\")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ]; then + if [ -n "$JAVA_HOME" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="$(which java)" + fi +fi + +if [ ! -x "$JAVACMD" ]; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ]; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ]; then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ]; do + if [ -d "$wdir"/.mvn ]; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$( + cd "$wdir/.." + pwd + ) + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' <"$1")" + fi +} + +BASE_DIR=$(find_maven_basedir "$(pwd)") +if [ -z "$BASE_DIR" ]; then + exit 1 +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in wrapperUrl) + jarUrl="$value" + break + ;; + esac + done <"$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget >/dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl >/dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=$(cygpath --path --windows "$M2_HOME") + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/Api/api-ports/api-persister-postgres/mvnw.cmd b/Api/api-ports/api-persister-postgres/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/Api/api-ports/api-persister-postgres/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/Api/api-ports/api-persister-postgres/pom.xml b/Api/api-ports/api-persister-postgres/pom.xml new file mode 100644 index 000000000..44a605a22 --- /dev/null +++ b/Api/api-ports/api-persister-postgres/pom.xml @@ -0,0 +1,124 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.mixchange + api-persister-postgres + 0.0.1-SNAPSHOT + api-persister-postgres + Persist items of Mixchange api on Postgres + + + 1.8 + 1.4.31 + ${version} + ${version} + ${version} + + + + + co.nilin.mixchange + matching-core + ${matching.version} + provided + + + co.nilin.mixchange + api-core + ${api.version} + provided + + + co.nilin.mixchange + accountant-core + ${accountant.version} + provided + + + org.springframework.boot + spring-boot-starter-data-r2dbc + + + io.r2dbc + r2dbc-postgresql + runtime + + + org.postgresql + postgresql + runtime + + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + com.google.code.gson + gson + + + io.projectreactor + reactor-test + test + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/config/PostgresConfig.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/config/PostgresConfig.kt new file mode 100644 index 000000000..db30f4ae8 --- /dev/null +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/config/PostgresConfig.kt @@ -0,0 +1,64 @@ +package co.nilin.mixchange.port.order.kafka.config + + +import org.springframework.context.annotation.Configuration +import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories +import org.springframework.r2dbc.core.DatabaseClient + + +@Configuration +@EnableR2dbcRepositories(basePackages = ["co.nilin.mixchange"]) +class PostgresConfig(db: DatabaseClient) { + + init { + + val sql = """ + CREATE TABLE IF NOT EXISTS orders ( + id SERIAL PRIMARY KEY, + ouid VARCHAR(72) NOT NULL UNIQUE, + uuid VARCHAR(72) NOT NULL, + client_order_id VARCHAR(72), + symbol VARCHAR(20), + order_id numeric, + maker_fee decimal, + taker_fee decimal, + left_side_fraction decimal, + right_side_fraction decimal, + user_level VARCHAR(20), + side VARCHAR(20), + match_constraint VARCHAR(20), + order_type VARCHAR(20), + price decimal, + quantity decimal, + quote_quantity decimal, + executed_qty decimal, + accumulative_quote_qty decimal, + status integer, + create_date TIMESTAMP, + update_date TIMESTAMP + ); + CREATE TABLE IF NOT EXISTS trades ( + id SERIAL PRIMARY KEY, + trade_id numeric, + symbol VARCHAR(20), + matched_quantity decimal, + taker_price decimal, + maker_price decimal, + taker_commision decimal, + maker_commision decimal, + taker_commision_asset VARCHAR(20), + maker_commision_asset VARCHAR(20), + trade_date TIMESTAMP, + maker_ouid VARCHAR(72) NOT NULL, + taker_ouid VARCHAR(72) NOT NULL, + maker_uuid VARCHAR(72) NOT NULL, + taker_uuid VARCHAR(72) NOT NULL, + create_date TIMESTAMP + ); + """ + val initDb = db.sql { sql } + initDb // initialize the database + .then() + .subscribe() // execute + } +} diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/dao/OrderRepository.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/dao/OrderRepository.kt new file mode 100644 index 000000000..c55efd2c0 --- /dev/null +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/dao/OrderRepository.kt @@ -0,0 +1,55 @@ +package co.nilin.mixchange.port.api.postgres.dao + +import co.nilin.mixchange.port.api.postgres.model.OrderModel +import kotlinx.coroutines.flow.Flow +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono +import java.util.* + +@Repository +interface OrderRepository : ReactiveCrudRepository { + @Query("select * from orders where ouid = :ouid") + fun findByOuid(@Param("ouid") ouid: String): Mono + + @Query("select * from orders where symbol = :symbol and order_id = :orderId") + fun findBySymbolAndOrderId( + @Param("symbol") + symbol: String, @Param("orderId") + orderId: Long + ): Mono + + @Query("select * from orders where symbol = :symbol and client_order_id = :origClientOrderId") + fun findBySymbolAndClientOrderId( + @Param("symbol") + symbol: String, @Param("origClientOrderId") + origClientOrderId: String + ): Mono + + @Query("select * from orders where uuid = :uuid and (:symbol is null or symbol = :symbol) and status in (:statuses)") + fun findByUuidAndSymbolAndStatus( + @Param("uuid") + uuid: String, + @Param("symbol") + symbol: String?, @Param("statuses") + status: Collection + ): Flow + + @Query("select * from orders where uuid = :uuid " + + "and (:symbol is null or symbol = :symbol) " + + "and (:startTime is null or update_date >= :startTime)" + + "and (:endTime is null or update_date < :endTime)" + ) + fun findByUuidAndSymbolAndTimeBetween( + @Param("uuid") + uuid: String, + @Param("symbol") + symbol: String?, + @Param("startTime") + startTime: Date?, + @Param("endTime") + endTime: Date? + ): Flow +} \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/dao/TradeRepository.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/dao/TradeRepository.kt new file mode 100644 index 000000000..921e1d7a4 --- /dev/null +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/dao/TradeRepository.kt @@ -0,0 +1,35 @@ +package co.nilin.mixchange.port.api.postgres.dao + +import co.nilin.mixchange.port.api.postgres.model.TradeModel +import kotlinx.coroutines.flow.Flow +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository +import java.util.* + +@Repository +interface TradeRepository : ReactiveCrudRepository { + + @Query("select * from trades where :ouid in (taker_ouid, maker_ouid) ") + fun findByOuid(@Param("ouid") ouid: String): Flow + + @Query("select * from trades where :uuid in (taker_uuid, maker_uuid) " + + "and (:fromTrade is null or id > :fromTrade) " + + "and (:symbol is null or symbol = :symbol) " + + "and (:startTime is null or trade_date >= :startTime) " + + "and (:endTime is null or trade_date < :endTime)" + ) + fun findByUuidAndSymbolAndTimeBetweenAndTradeIdGreaterThan( + @Param("uuid") + uuid: String, + @Param("symbol") + symbol: String?, + @Param("fromTrade") + fromTrade: Long?, + @Param("startTime") + startTime: Date?, + @Param("endTime") + endTime: Date? + ): Flow +} \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/OrderPersisterImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/OrderPersisterImpl.kt new file mode 100644 index 000000000..09faa2c34 --- /dev/null +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/OrderPersisterImpl.kt @@ -0,0 +1,53 @@ +package co.nilin.mixchange.port.api.postgres.impl + +import co.nilin.mixchange.accountant.core.inout.OrderStatus +import co.nilin.mixchange.accountant.core.inout.RichOrder +import co.nilin.mixchange.api.core.spi.OrderPersister +import co.nilin.mixchange.port.api.postgres.dao.OrderRepository +import co.nilin.mixchange.port.api.postgres.model.OrderModel +import kotlinx.coroutines.reactive.awaitFirstOrNull +import org.springframework.stereotype.Component +import java.time.LocalDateTime + +@Component +class OrderPersisterImpl(val orderRepository: OrderRepository) : OrderPersister { + override suspend fun save(order: RichOrder) { + val existingOrder = orderRepository + .findByOuid(order.ouid) + .awaitFirstOrNull() + if (existingOrder == null + || existingOrder.executedQuantity?.compareTo(order.executedQuantity.toDouble()) == -1 + || order.status == OrderStatus.REJECTED.code + || order.status == OrderStatus.CANCELED.code + || order.status == OrderStatus.EXPIRED.code + || order.status == OrderStatus.FILLED.code + || existingOrder.type == null + ) + orderRepository.save( + OrderModel( + existingOrder?.id, + order.ouid, + order.uuid, + null, + order.pair, + order.orderId, + order.makerFee.toDouble(), + order.takerFee.toDouble(), + order.leftSideFraction.toDouble(), + order.rightSideFraction.toDouble(), + order.userLevel, + order.direction, + order.constraint, + order.type, + order.price.toDouble(), + order.quantity.toDouble(), + order.quoteQuantity.toDouble(), + order.executedQuantity.toDouble(), + order.accumulativeQuoteQty.toDouble(), + order.status, + existingOrder?.createDate ?: LocalDateTime.now(), + LocalDateTime.now() + ) + ).awaitFirstOrNull() + } +} \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/TradePersisterImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/TradePersisterImpl.kt new file mode 100644 index 000000000..7260d76b1 --- /dev/null +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/TradePersisterImpl.kt @@ -0,0 +1,124 @@ +package co.nilin.mixchange.port.api.postgres.impl + +import co.nilin.mixchange.accountant.core.inout.OrderStatus +import co.nilin.mixchange.accountant.core.inout.RichTrade +import co.nilin.mixchange.api.core.spi.TradePersister +import co.nilin.mixchange.port.api.postgres.dao.OrderRepository +import co.nilin.mixchange.port.api.postgres.dao.TradeRepository +import co.nilin.mixchange.port.api.postgres.model.OrderModel +import co.nilin.mixchange.port.api.postgres.model.TradeModel +import kotlinx.coroutines.reactive.awaitFirstOrNull +import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional +import java.math.BigDecimal +import java.time.LocalDateTime + +@Component +class TradePersisterImpl(val tradeRepository: TradeRepository, val orderRepository: OrderRepository) : TradePersister { + + @Transactional + override suspend fun save(trade: RichTrade) { + println("RichTrade save") + tradeRepository.save( + TradeModel( + null, + trade.id, + trade.pair, + trade.matchedQuantity.toDouble(), + trade.takerPrice.toDouble(), + trade.makerPrice.toDouble(), + trade.takerCommision.toDouble(), + trade.makerCommision.toDouble(), + trade.takerCommisionAsset, + trade.makerCommisionAsset, + trade.tradeDateTime, + trade.makerOuid, + trade.takerOuid, + trade.makerUuid, + trade.takerUuid, + LocalDateTime.now() + ) + ).awaitFirstOrNull() + println("RichTrade save/update maker order") + saveMakerOrder(trade) + println("RichTrade save/update taker order") + saveTakerOrder(trade) + + } + + private suspend fun saveTakerOrder(trade: RichTrade) { + val existingOrder = orderRepository + .findByOuid(trade.takerOuid) + .awaitFirstOrNull() + orderRepository.save( + OrderModel( + existingOrder?.id, + trade.takerOuid, + trade.takerUuid, + null, + trade.pair, + trade.takerOrderId, + existingOrder?.makerFee, + existingOrder?.takerFee, + existingOrder?.leftSideFraction, + existingOrder?.rightSideFraction, + existingOrder?.userLevel, + trade.takerDirection, + existingOrder?.constraint, + existingOrder?.type, + trade.takerPrice.toDouble(), + trade.takerQuantity.toDouble(), + trade.takerQuoteQuantity.toDouble(), + (trade.takerQuantity.minus(trade.takerRemainedQuantity)).toDouble(), + trade.takerPrice.multiply( + (trade.takerQuantity.minus(trade.takerRemainedQuantity)) + ).toDouble(), + if (trade.takerRemainedQuantity == BigDecimal.ZERO) { + OrderStatus.PARTIALLY_FILLED.code + } else { + OrderStatus.FILLED.code + }, + existingOrder?.createDate, + LocalDateTime.now() + ) + ).awaitFirstOrNull() + } + + private suspend fun saveMakerOrder(trade: RichTrade) { + val existingOrder = orderRepository + .findByOuid(trade.makerOuid) + .awaitFirstOrNull() + orderRepository.save( + OrderModel( + existingOrder?.id, + trade.makerOuid, + trade.makerUuid, + null, + trade.pair, + trade.makerOrderId, + existingOrder?.makerFee, + existingOrder?.takerFee, + existingOrder?.leftSideFraction, + existingOrder?.rightSideFraction, + existingOrder?.userLevel, + trade.makerDirection, + existingOrder?.constraint, + existingOrder?.type, + trade.makerPrice.toDouble(), + trade.makerQuantity.toDouble(), + trade.makerQuoteQuantity.toDouble(), + (trade.makerQuantity.minus(trade.makerRemainedQuantity)).toDouble(), + trade.makerPrice.multiply( + (trade.makerQuantity.minus(trade.makerRemainedQuantity)) + ).toDouble(), + if (trade.makerRemainedQuantity == BigDecimal.ZERO) { + OrderStatus.PARTIALLY_FILLED.code + } else { + OrderStatus.FILLED.code + }, + existingOrder?.createDate ?: LocalDateTime.now(), + LocalDateTime.now() + ) + ).awaitFirstOrNull() + } +} \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/UserQueryHandlerImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/UserQueryHandlerImpl.kt new file mode 100644 index 000000000..cb94b7068 --- /dev/null +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/UserQueryHandlerImpl.kt @@ -0,0 +1,133 @@ +package co.nilin.mixchange.port.api.postgres.impl + +import co.nilin.mixchange.api.core.inout.* +import co.nilin.mixchange.api.core.spi.UserQueryHandler +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.port.api.postgres.dao.OrderRepository +import co.nilin.mixchange.port.api.postgres.dao.TradeRepository +import co.nilin.mixchange.port.api.postgres.model.OrderModel +import co.nilin.mixchange.port.api.postgres.util.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.reactive.awaitFirst +import kotlinx.coroutines.reactive.awaitFirstOrNull +import org.springframework.stereotype.Component +import java.math.BigDecimal +import java.security.Principal +import java.time.ZoneId +import java.util.* + +@Component +class UserQueryHandlerImpl( + val orderRepository: OrderRepository, val tradeRepository: TradeRepository +) : UserQueryHandler { + override suspend fun queryOrder(principal: Principal, request: QueryOrderRequest): QueryOrderResponse? { + val order = (if (request.origClientOrderId != null) { + orderRepository.findBySymbolAndClientOrderId(request.symbol, request.origClientOrderId!!) + } else { + orderRepository.findBySymbolAndOrderId(request.symbol, request.orderId!!) + + }).awaitFirstOrNull() + if (order?.constraint != null) { + if (order.uuid != principal.name) + throw RuntimeException("Forbidden") + return orderToQueryResponse(order) + } + return null + } + + override suspend fun openOrders(principal: Principal, symbol: String?): Flow { + return orderRepository.findByUuidAndSymbolAndStatus( + principal.name, + symbol, + listOf(OrderStatus.NEW.ordinal, OrderStatus.PARTIALLY_FILLED.ordinal) + ).filter { orderModel -> orderModel.constraint != null } + .map { order -> orderToQueryResponse(order) } + } + + override suspend fun allOrders(principal: Principal, allOrderRequest: AllOrderRequest): Flow { + return orderRepository.findByUuidAndSymbolAndTimeBetween( + principal.name, + allOrderRequest.symbol, + allOrderRequest.startTime, + allOrderRequest.endTime + ).filter { orderModel -> orderModel.constraint != null } + .map { order -> orderToQueryResponse(order) } + } + + override suspend fun allTrades(principal: Principal, request: TradeRequest): Flow { + return tradeRepository.findByUuidAndSymbolAndTimeBetweenAndTradeIdGreaterThan( + principal.name, request.symbol, request.fromTrade, request.startTime, request.endTime + ).map { trade -> + val takerOrder = orderRepository.findByOuid(trade.takerOuid).awaitFirst() + val makerOrder = orderRepository.findByOuid(trade.makerOuid).awaitFirst() + val isMakerBuyer = makerOrder.direction == OrderDirection.ASK + TradeResponse( + trade.symbol, + trade.tradeId, + if (trade.takerUuid == principal.name) { + takerOrder.orderId!! + } else { + makerOrder.orderId!! + }, + -1, + if (trade.takerUuid == principal.name) { + trade.takerPrice.toBigDecimal() + } else { + trade.makerPrice.toBigDecimal() + }, + trade.matchedQuantity.toBigDecimal(), + if (isMakerBuyer) { + makerOrder.quoteQuantity!!.toBigDecimal() + } else { + takerOrder.quoteQuantity!!.toBigDecimal() + }, + if (trade.takerUuid == principal.name) { + trade.takerCommision!!.toBigDecimal() + } else { + trade.makerCommision!!.toBigDecimal() + }, + if (trade.takerUuid == principal.name) { + trade.takerCommisionAsset!! + } else { + trade.makerCommisionAsset!! + }, + Date.from( + trade.createDate.atZone(ZoneId.systemDefault()).toInstant() + ), + if (trade.takerUuid == principal.name) { + OrderDirection.ASK == takerOrder.direction + } else { + OrderDirection.ASK == makerOrder.direction + }, + trade.makerUuid == principal.name, + true + ) + } + } + + + private fun orderToQueryResponse(order: OrderModel) = QueryOrderResponse( + order.symbol, + order.orderId ?: -1, + -1, + order.clientOrderId ?: "", + BigDecimal(order.price!!), + BigDecimal(order.quantity!!), + BigDecimal(order.executedQuantity!!), + BigDecimal(order.accumulativeQuoteQty ?: 0.0), + order.status!!.toOrderStatus(), + order.constraint!!.toTimeInForce(), + order.type!!.toApiOrderType(), + order.direction!!.toOrderSide(), + null, + null, + Date.from( + order.createDate!!.atZone(ZoneId.systemDefault()).toInstant() + ), + Date.from( + order.updateDate.atZone(ZoneId.systemDefault()).toInstant() + ), order.status.toOrderStatus().isWorking(), order.quoteQuantity!!.toBigDecimal() + ) +} \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/model/OrderModel.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/model/OrderModel.kt new file mode 100644 index 000000000..28b7b7be9 --- /dev/null +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/model/OrderModel.kt @@ -0,0 +1,38 @@ +package co.nilin.mixchange.port.api.postgres.model + + +import co.nilin.mixchange.matching.core.model.MatchConstraint +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.OrderType +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.time.LocalDateTime + +@Table("orders") +class OrderModel( + @Id var id: Long?, + @Column(value = "ouid") + val ouid: String, + val uuid: String, + @Column(value = "client_order_id") + val clientOrderId: String?, + val symbol: String, + @Column(value = "order_id") val orderId: Long?, + @Column("maker_fee") val makerFee: Double?, + @Column("taker_fee") val takerFee: Double?, + @Column("left_side_fraction") val leftSideFraction: Double?, + @Column("right_side_fraction") val rightSideFraction: Double?, + @Column("user_level") val userLevel: String?, + @Column("side") val direction: OrderDirection?, + @Column("match_constraint") val constraint: MatchConstraint?, + @Column("order_type") val type: OrderType?, + @Column("price") val price: Double?, + @Column("quantity") val quantity: Double?, + @Column("quote_quantity") val quoteQuantity: Double?, + @Column("executed_qty") val executedQuantity: Double?, + @Column("accumulative_quote_qty") val accumulativeQuoteQty: Double?, + @Column("status") val status: Int?, + @Column("create_date") val createDate: LocalDateTime?, + @Column("update_date") val updateDate: LocalDateTime +) \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/model/TradeModel.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/model/TradeModel.kt new file mode 100644 index 000000000..3b6b1eef8 --- /dev/null +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/model/TradeModel.kt @@ -0,0 +1,27 @@ +package co.nilin.mixchange.port.api.postgres.model + + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.time.LocalDateTime + +@Table("trades") +class TradeModel( + @Id var id: Long?, + @Column("trade_id") val tradeId: Long, + val symbol: String, + @Column("matched_quantity") val matchedQuantity: Double, + @Column("taker_price") val takerPrice: Double, + @Column("maker_price") val makerPrice: Double, + @Column("taker_commision") val takerCommision: Double?, + @Column("maker_commision") val makerCommision: Double?, + @Column("taker_commision_asset") val takerCommisionAsset: String?, + @Column("maker_commision_asset") val makerCommisionAsset: String?, + @Column("trade_date") val tradeDate: LocalDateTime, + @Column("maker_ouid") val makerOuid: String, + @Column("taker_ouid") val takerOuid: String, + @Column("maker_uuid") val makerUuid: String, + @Column("taker_uuid") val takerUuid: String, + @Column("create_date") val createDate: LocalDateTime +) \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/util/EnumExtensions.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/util/EnumExtensions.kt new file mode 100644 index 000000000..a8db1d1b2 --- /dev/null +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/util/EnumExtensions.kt @@ -0,0 +1,45 @@ +package co.nilin.mixchange.port.api.postgres.util + +import co.nilin.mixchange.api.core.inout.OrderSide +import co.nilin.mixchange.api.core.inout.OrderStatus +import co.nilin.mixchange.api.core.inout.TimeInForce +import co.nilin.mixchange.matching.core.model.MatchConstraint +import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.mixchange.matching.core.model.OrderType + +fun MatchConstraint.toTimeInForce(): TimeInForce { + if (this == MatchConstraint.FOK_BUDGET) + return TimeInForce.FOK + if (this == MatchConstraint.IOC_BUDGET) + return TimeInForce.IOC + return TimeInForce.valueOf(this.name) +} + + +fun TimeInForce.toMatchConstraint(): MatchConstraint { + return MatchConstraint.valueOf(this.name) +} + +fun OrderType.toApiOrderType(): co.nilin.mixchange.api.core.inout.OrderType { + if (this == OrderType.LIMIT_ORDER) + return co.nilin.mixchange.api.core.inout.OrderType.LIMIT + if (this == OrderType.MARKET_ORDER) + return co.nilin.mixchange.api.core.inout.OrderType.MARKET + throw IllegalArgumentException("OrderType $this is not supported!") +} + +fun OrderDirection.toOrderSide(): OrderSide { + if (this == OrderDirection.ASK) + return OrderSide.BUY + return OrderSide.SELL +} + +fun OrderStatus.isWorking(): Boolean { + return listOf(OrderStatus.NEW, OrderStatus.PARTIALLY_FILLED).contains(this) +} + +fun Int.toOrderStatus(): OrderStatus { + val status = co.nilin.mixchange.accountant.core.inout.OrderStatus.values() + .find { s -> s.code == this } + return OrderStatus.valueOf(status!!.name) +} \ No newline at end of file diff --git a/Api/pom.xml b/Api/pom.xml new file mode 100644 index 000000000..f37494325 --- /dev/null +++ b/Api/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + co.nilin.mixchange + api-root + 0.0.1-SNAPSHOT + api-root + pom + Api root of Mixchange + + api-core + api-app + api-ports/api-persister-postgres + api-ports/api-binance-rest + api-ports/api-eventlistener-kafka + + diff --git a/Deployment/docker-compose.yml b/Deployment/docker-compose.yml index f9ad44965..56c671748 100644 --- a/Deployment/docker-compose.yml +++ b/Deployment/docker-compose.yml @@ -1,11 +1,11 @@ -version: '3.3' +version: '3.8' services: zookeeper: image: 'docker.io/bitnami/zookeeper:3-debian-10' ports: - '127.0.0.1:2181:2181' - volumes: - - $PWD/runtime/zookeeper_data:/bitnami + # volumes: + # - $PWD/runtime/zookeeper_data:/bitnami environment: - ALLOW_ANONYMOUS_LOGIN=yes networks: @@ -13,12 +13,30 @@ services: deploy: restart_policy: condition: on-failure + # kafka: + # image: 'wurstmeister/kafka' + # ports: + # - '29092:29092' + # environment: + # - KAFKA_BROKER_ID=0 + # - KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181 + # - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT + # - KAFKA_ADVERTISED_LISTENERS=INSIDE://kafka:9092,OUTSIDE://192.168.178.26:29092 + # - KAFKA_LISTENERS=INSIDE://:9092,OUTSIDE://:29092 + # - KAFKA_INTER_BROKER_LISTENER_NAME=INSIDE + # depends_on: + # - zookeeper + # networks: + # - opex + # deploy: + # restart_policy: + # condition: on-failure kafka: image: 'docker.io/bitnami/kafka:2-debian-10' ports: - '127.0.0.1:9092:9092' - volumes: - - $PWD/runtime/kafka-data:/bitnami + # volumes: + # - $PWD/runtime/kafka-data:/bitnami environment: - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181 - ALLOW_PLAINTEXT_LISTENER=yes @@ -74,18 +92,18 @@ services: - $PWD/runtime/accountant-data:/var/lib/postgresql/data/ networks: - opex - postgres-eventlog: - image: "postgres" - ports: - - 127.0.0.1:5434:5432 - environment: - - POSTGRES_USER=opex - - POSTGRES_PASSWORD=hiopex - - POSTGRES_DB=opex_eventlog - volumes: - - $PWD/runtime/eventlog-data:/var/lib/postgresql/data/ - networks: - - opex + # postgres-eventlog: + # image: "postgres" + # ports: + # - 127.0.0.1:5434:5432 + # environment: + # - POSTGRES_USER=opex + # - POSTGRES_PASSWORD=hiopex + # - POSTGRES_DB=opex_eventlog + # volumes: + # - $PWD/runtime/eventlog-data:/var/lib/postgresql/data/ + # networks: + # - opex postgres-auth: image: "postgres" ports: @@ -116,6 +134,21 @@ services: deploy: restart_policy: condition: on-failure + postgres-api: + image: "postgres" + ports: + - 127.0.0.1:5437:5432 + environment: + - POSTGRES_USER=opex + - POSTGRES_PASSWORD=hiopex + - POSTGRES_DB=opex_api + volumes: + - $PWD/runtime/api-data:/var/lib/postgresql/data/ + networks: + - opex + deploy: + restart_policy: + condition: on-failure accountant: container_name: accountant build: @@ -123,8 +156,9 @@ services: dockerfile: Dockerfile ports: - 127.0.0.1:8089:8089 + - 127.0.0.1:1051:1044 environment: - - JAVA_OPTS=-Xmx256m + - JAVA_OPTS=-Xmx256m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044 - SPRING_PROFILES_ACTIVE=docker - KAFKA_IP_PORT=kafka:9092 - REDIS_HOST=redis @@ -138,28 +172,28 @@ services: - redis - consul - postgres-accountant - eventlog: - container_name: eventlog - build: - context: ../EventLog/eventlog-app - dockerfile: Dockerfile - ports: - - 127.0.0.1:8090:8090 - environment: - - JAVA_OPTS=-Xmx256m - - SPRING_PROFILES_ACTIVE=docker - - KAFKA_IP_PORT=kafka:9092 - - REDIS_HOST=redis - - CONSUL_HOST=consul - - DB_IP_PORT=postgres-eventlog - networks: - - opex - depends_on: - - zookeeper - - kafka - - redis - - consul - - postgres-eventlog + # eventlog: + # container_name: eventlog + # build: + # context: ../EventLog/eventlog-app + # dockerfile: Dockerfile + # ports: + # - 127.0.0.1:8090:8090 + # environment: + # - JAVA_OPTS=-Xmx256m + # - SPRING_PROFILES_ACTIVE=docker + # - KAFKA_IP_PORT=kafka:9092 + # - REDIS_HOST=redis + # - CONSUL_HOST=consul + # - DB_IP_PORT=postgres-eventlog + # networks: + # - opex + # depends_on: + # - zookeeper + # - kafka + # - redis + # - consul + # - postgres-eventlog matching-engine: container_name: matching-engine build: @@ -253,20 +287,47 @@ services: deploy: restart_policy: condition: on-failure - nginx: - image: nginx:latest - container_name: opex_nginx - volumes: - - ./nginx.conf:/etc/nginx/nginx.conf - - $PWD/runtime/www:/data/www + api: + container_name: API + build: + context: ../Api/api-app + dockerfile: Dockerfile ports: - - 80:80 - depends_on: - - wallet - - auth - - matching-gateway + - 127.0.0.1:8094:8094 + - 127.0.0.1:1050:1044 + environment: + - JAVA_OPTS=-Xmx256m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044 + - SPRING_PROFILES_ACTIVE=docker + - KAFKA_IP_PORT=kafka:9092 + - REDIS_HOST=redis + - CONSUL_HOST=consul + - DB_IP_PORT=postgres-api networks: - opex + depends_on: + - zookeeper + - kafka + - redis + - consul + - postgres-api + deploy: + restart_policy: + condition: on-failure +# nginx: +# image: nginx:latest +# container_name: opex_nginx +# volumes: +# - ./nginx.conf:/etc/nginx/nginx.conf +# - $PWD/runtime/www:/data/www +# ports: +# - 80:80 +# depends_on: +# - wallet +# - auth +# - matching-gateway +# - api +# networks: +# - opex networks: opex: driver: bridge \ No newline at end of file diff --git a/Deployment/nginx.conf b/Deployment/nginx.conf index ef6ecaa3b..687bd16a8 100644 --- a/Deployment/nginx.conf +++ b/Deployment/nginx.conf @@ -1,37 +1,45 @@ worker_processes 1; events { worker_connections 1024; } -http { - sendfile on; - upstream docker-wallet { - server wallet:8091; - } - upstream docker-auth { - server auth:8083; - } - upstream docker-matching-gateway { - server matching-gateway:8093; - } + http { + sendfile on; + upstream docker-wallet { + server wallet:8091; +} + upstream docker-auth { + server auth:8083; +} + upstream docker-matching-gateway { + server matching-gateway:8093; +} + upstream docker-api { + server api:8094; +} - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; - server { - server_name api.opex.dev; + server { + server_name api.opex.dev; - location /auth { - proxy_pass http://docker-auth; - } + location /auth { + proxy_pass http://docker-auth; +} - location /wallet { - proxy_pass http://docker-wallet; - rewrite ^/wallet(.*)$ $1 break; - } + location /wallet { + proxy_pass http://docker-wallet; + rewrite ^/wallet(.*)$ $1 break; +} - location /gateway { - proxy_pass http://docker-matching-gateway; - rewrite ^/gateway(.*)$ $1 break; - } - } + location /gateway { + proxy_pass http://docker-matching-gateway; + rewrite ^/gateway(.*)$ $1 break; +} + + location /api { + proxy_pass http://docker-api; + rewrite ^/api(.*)$ $1 break; +} +} } \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/mvnw b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/mvnw old mode 100755 new mode 100644 diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/mvnw b/EventLog/eventlog-ports/eventlog-persister-postgres/mvnw old mode 100755 new mode 100644 diff --git a/MatchingEngine/matching-app/src/main/resources/application-docker.yml b/MatchingEngine/matching-app/src/main/resources/application-docker.yml index 4e47d2249..d2bddd8e0 100644 --- a/MatchingEngine/matching-app/src/main/resources/application-docker.yml +++ b/MatchingEngine/matching-app/src/main/resources/application-docker.yml @@ -7,7 +7,7 @@ spring: consumer: group-id: engine redis: - hostname: ${REDIS_HOST} + host: ${REDIS_HOST} port: 6379 app: symbols: btc_usdt,eth_usdt,eth_btc \ No newline at end of file diff --git a/MatchingEngine/matching-app/src/main/resources/application.yml b/MatchingEngine/matching-app/src/main/resources/application.yml index cc1bcacd1..13eeed8ad 100644 --- a/MatchingEngine/matching-app/src/main/resources/application.yml +++ b/MatchingEngine/matching-app/src/main/resources/application.yml @@ -7,7 +7,7 @@ spring: consumer: group-id: engine redis: - hostname: 127.0.0.1 + host: 127.0.0.1 port: 6379 app: symbols: btc_usdt,eth_usdt,eth_btc \ No newline at end of file diff --git a/MatchingEngine/matching-core/mvnw b/MatchingEngine/matching-core/mvnw old mode 100755 new mode 100644 diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/engine/SimpleOrderBook.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/engine/SimpleOrderBook.kt index 623fa9188..396c09428 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/engine/SimpleOrderBook.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/engine/SimpleOrderBook.kt @@ -18,6 +18,7 @@ class SimpleOrderBook(val pair: Pair, var replayMode: Boolean) : OrderBook { var bestBidOrder: SimpleOrder? = null val orderCounter = AtomicLong() + val tradeCounter = AtomicLong() var lastOrder: SimpleOrder? = null @@ -52,7 +53,7 @@ class SimpleOrderBook(val pair: Pair, var replayMode: Boolean) : OrderBook { } // try to match instantly val queueOrder = matchInstantly(order) - //if remained quantity > 0 add to queue + // if remained quantity > 0 add to queue if (queueOrder.filledQuantity != queueOrder.quantity) { putGtcInQueue(queueOrder) } @@ -260,7 +261,7 @@ class SimpleOrderBook(val pair: Pair, var replayMode: Boolean) : OrderBook { currentMaker.filledQuantity += instantMatchQuantity currentMaker.bucket!!.totalQuantity -= instantMatchQuantity if (!replayMode) { - EventDispatcher.emit(TradeEvent(pair, order.ouid, order.uuid, order.id + EventDispatcher.emit(TradeEvent(tradeCounter.incrementAndGet(), pair, order.ouid, order.uuid, order.id ?: 0, order.direction, order.price, order.remainedQuantity(), currentMaker.ouid, currentMaker.uuid, currentMaker.id!!, currentMaker.direction, currentMaker.price, currentMaker.remainedQuantity(), instantMatchQuantity)) } if (currentMaker.remainedQuantity() == 0L) { diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/TradeEvent.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/TradeEvent.kt index 298e7060b..66a925aea 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/TradeEvent.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/TradeEvent.kt @@ -3,6 +3,7 @@ package co.nilin.mixchange.matching.core.eventh.events import co.nilin.mixchange.matching.core.model.OrderDirection class TradeEvent() : CoreEvent() { + var tradeId: Long = 0 var takerOuid: String = "" var takerUuid: String = "" var takerOrderId: Long = 0 @@ -18,7 +19,8 @@ class TradeEvent() : CoreEvent() { var matchedQuantity: Long = 0 - constructor(pair: co.nilin.mixchange.matching.core.model.Pair, + constructor(tradeId: Long, + pair: co.nilin.mixchange.matching.core.model.Pair, takerOuid: String, takerUuid: String, takerOrderId: Long, @@ -34,6 +36,7 @@ class TradeEvent() : CoreEvent() { matchedQuantity: Long ) : this() { + this.tradeId = tradeId this.takerOuid = takerOuid this.takerUuid = takerUuid this.pair = pair diff --git a/MatchingEngine/matching-ports/matching-eventlistener-kafka/mvnw b/MatchingEngine/matching-ports/matching-eventlistener-kafka/mvnw old mode 100755 new mode 100644 diff --git a/MatchingEngine/matching-ports/matching-snapshots-redis/mvnw b/MatchingEngine/matching-ports/matching-snapshots-redis/mvnw old mode 100755 new mode 100644 diff --git a/MatchingEngine/matching-ports/matching-submitter-kafka/mvnw b/MatchingEngine/matching-ports/matching-submitter-kafka/mvnw old mode 100755 new mode 100644 diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/service/OrderService.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/service/OrderService.kt index 5bd07735a..b61d8ca67 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/service/OrderService.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/service/OrderService.kt @@ -2,8 +2,8 @@ package co.nilin.mixchange.app.service import co.nilin.mixchange.app.exception.NotAllowedToSubmitOrderException import co.nilin.mixchange.app.inout.CreateOrderRequest -import co.nilin.mixchange.app.spi.PairConfigLoader import co.nilin.mixchange.app.spi.AccountantApiProxy +import co.nilin.mixchange.app.spi.PairConfigLoader import co.nilin.mixchange.matching.core.model.OrderDirection import co.nilin.mixchange.matching.core.model.Pair import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest @@ -13,50 +13,50 @@ import org.springframework.stereotype.Service import java.util.* @Service -class OrderService(val accountantApiProxy: AccountantApiProxy -, val orderSubmitter: OrderSubmitter -, val pairConfigLoader: PairConfigLoader +class OrderService( + val accountantApiProxy: AccountantApiProxy, + val orderSubmitter: OrderSubmitter, + val pairConfigLoader: PairConfigLoader ) { suspend fun submitNewOrder(createOrderRequest: CreateOrderRequest): OrderSubmitResult { val symbolSides = createOrderRequest.pair.split("_") - val symbol = if ( createOrderRequest.direction == OrderDirection.ASK ) + val symbol = if (createOrderRequest.direction == OrderDirection.ASK) symbolSides[0] else symbolSides[1] if (!accountantApiProxy.canCreateOrder( - createOrderRequest.uuid!!, - symbol, - if ( createOrderRequest.direction == OrderDirection.ASK ) - createOrderRequest.quantity - else - createOrderRequest.quantity.multiply(createOrderRequest.price) - )) { + createOrderRequest.uuid!!, + symbol, + if (createOrderRequest.direction == OrderDirection.ASK) + createOrderRequest.quantity + else + createOrderRequest.quantity.multiply(createOrderRequest.price) + ) + ) { throw NotAllowedToSubmitOrderException() } - val pairFeeConfig = pairConfigLoader.load(createOrderRequest.pair - , createOrderRequest.direction, "") + val pairFeeConfig = pairConfigLoader.load( + createOrderRequest.pair, createOrderRequest.direction, "" + ) val orderSubmitRequest = OrderSubmitRequest( - UUID.randomUUID().toString() - , createOrderRequest.uuid!!//get from auth2 - , null - , Pair(symbolSides[0], symbolSides[1]) - , createOrderRequest.price.divide( - //(if ( createOrderRequest.direction == OrderDirection.ASK ){ - pairFeeConfig.pairConfig.rightSideFraction - /*} else { - pairFeeConfig.pairConfig.leftSideFraction - })*/.toBigDecimal() - ).longValueExact() - , createOrderRequest.quantity.divide( - ///(if ( createOrderRequest.direction == OrderDirection.ASK ){ - pairFeeConfig.pairConfig.leftSideFraction - /*} else { - pairFeeConfig.pairConfig.rightSideFraction - })*/.toBigDecimal()) - .longValueExact() - , createOrderRequest.direction - , createOrderRequest.matchConstraint - , createOrderRequest.orderType) + UUID.randomUUID().toString(), + createOrderRequest.uuid!!//get from auth2 + , + null, + Pair(symbolSides[0], symbolSides[1]), + createOrderRequest.price.divide( + pairFeeConfig.pairConfig.rightSideFraction + .toBigDecimal() + ).longValueExact(), + createOrderRequest.quantity.divide( + pairFeeConfig.pairConfig.leftSideFraction + .toBigDecimal() + ) + .longValueExact(), + createOrderRequest.direction, + createOrderRequest.matchConstraint, + createOrderRequest.orderType + ) return orderSubmitter.submit(orderSubmitRequest) } } \ No newline at end of file diff --git a/MatchingGateway/gateway-port/order-submitter-kafka/mvnw b/MatchingGateway/gateway-port/order-submitter-kafka/mvnw old mode 100755 new mode 100644 diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/extension/ExtendedEventListenerProvider.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/extension/ExtendedEventListenerProvider.kt index 1567a0b03..1ddaab25a 100644 --- a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/extension/ExtendedEventListenerProvider.kt +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/extension/ExtendedEventListenerProvider.kt @@ -4,7 +4,6 @@ import co.nilin.mixchange.auth.gateway.ApplicationContextHolder import co.nilin.mixchange.auth.gateway.model.AuthEvent import co.nilin.mixchange.auth.gateway.model.UserCreatedEvent import com.fasterxml.jackson.databind.DeserializationFeature -import com.fasterxml.jackson.databind.ObjectMapper import org.keycloak.events.Event import org.keycloak.events.EventListenerProvider import org.keycloak.events.EventType @@ -80,9 +79,11 @@ class ExtendedEventListenerProvider(private val session: KeycloakSession) : Even logger.info("A new user has been created") val userData = objectMapper.readValue(adminEvent.representation, UserData::class.java) val uuid = adminEvent.resourcePath.substringAfter("/") + val kafkaEvent = UserCreatedEvent(uuid, userData.firstName, userData.lastName, userData.email) (ApplicationContextHolder.getCurrentContext()!! - .getBean("authKafkaTemplate") as KafkaTemplate) - .send("auth_user_created", UserCreatedEvent(uuid, userData.firstName, userData.lastName, userData.email)) + .getBean("authKafkaTemplate") as KafkaTemplate) + .send("auth_user_created", kafkaEvent) + logger.info("{} produced in kafka topic", kafkaEvent) } logger.info("-----------------------------------------------------------") } diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/UserCreatedEvent.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/UserCreatedEvent.kt index ffabaabf2..cf2bd4213 100644 --- a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/UserCreatedEvent.kt +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/UserCreatedEvent.kt @@ -15,4 +15,9 @@ class UserCreatedEvent: AuthEvent { } constructor() : super() + + override fun toString(): String { + return "UserCreatedEvent(uuid='$uuid', firstName='$firstName', lastName='$lastName', email='$email')" + } + } \ No newline at end of file diff --git a/Wallet/wallet-app/mvnw b/Wallet/wallet-app/mvnw old mode 100755 new mode 100644 diff --git a/Wallet/wallet-app/src/main/resources/application-docker.yml b/Wallet/wallet-app/src/main/resources/application-docker.yml index 2bbefb080..f07453be0 100644 --- a/Wallet/wallet-app/src/main/resources/application-docker.yml +++ b/Wallet/wallet-app/src/main/resources/application-docker.yml @@ -2,14 +2,10 @@ server.port: 8091 spring: application: name: opex-wallet - main: - allow-bean-definition-overriding: false kafka: bootstrap-servers: ${KAFKA_IP_PORT} - consumer: - group-id: wallet redis: - hostname: ${REDIS_HOST} + host: ${REDIS_HOST} port: 6379 r2dbc: url: r2dbc:postgresql://${DB_IP_PORT}/opex_wallet @@ -17,14 +13,7 @@ spring: password: hiopex initialization-mode: always cloud: - bootstrap: - enabled: true consul: host: ${CONSUL_HOST} port: 8500 - discovery: - #healthCheckPath: ${management.context-path}/health - instance-id: ${spring.application.name}:${server.port} - healthCheckInterval: 20s - prefer-ip-address: true diff --git a/Wallet/wallet-app/src/main/resources/application.yml b/Wallet/wallet-app/src/main/resources/application.yml index 21e431458..daaf189e5 100644 --- a/Wallet/wallet-app/src/main/resources/application.yml +++ b/Wallet/wallet-app/src/main/resources/application.yml @@ -9,7 +9,7 @@ spring: consumer: group-id: wallet redis: - hostname: 127.0.0.1 + host: 127.0.0.1 port: 6379 r2dbc: url: r2dbc:postgresql://localhost/opex_wallet @@ -30,3 +30,6 @@ app: gift: symbol: usdt amount: 1000 +logging: + level: + org.apache.kafka: DEBUG diff --git a/Wallet/wallet-core/mvnw b/Wallet/wallet-core/mvnw old mode 100755 new mode 100644 diff --git a/Wallet/wallet-ports/wallet-eventlistener-kafka/mvnw b/Wallet/wallet-ports/wallet-eventlistener-kafka/mvnw old mode 100755 new mode 100644 diff --git a/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/UserCreatedEvent.kt b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/UserCreatedEvent.kt index ffabaabf2..b186f6979 100644 --- a/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/UserCreatedEvent.kt +++ b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/UserCreatedEvent.kt @@ -15,4 +15,8 @@ class UserCreatedEvent: AuthEvent { } constructor() : super() + + override fun toString(): String { + return "UserCreatedEvent(uuid='$uuid', firstName='$firstName', lastName='$lastName', email='$email')" + } } \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/config/WalletKafkaConfig.kt b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/config/WalletKafkaConfig.kt index 6a6b9b625..8ad72a1cb 100644 --- a/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/config/WalletKafkaConfig.kt +++ b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/config/WalletKafkaConfig.kt @@ -20,7 +20,6 @@ import org.springframework.kafka.listener.ConcurrentMessageListenerContainer import org.springframework.kafka.listener.ContainerProperties import org.springframework.kafka.support.serializer.JsonDeserializer import org.springframework.kafka.support.serializer.JsonSerializer -import java.util.* import java.util.regex.Pattern @@ -44,7 +43,7 @@ class WalletKafkaConfig { } @Bean("walletConsumerFactory") - fun consumerFactory(@Qualifier("walletConsumerConfig")consumerConfigs: Map): ConsumerFactory { + fun consumerFactory(@Qualifier("walletConsumerConfig") consumerConfigs: Map): ConsumerFactory { return DefaultKafkaConsumerFactory(consumerConfigs) } @@ -70,8 +69,7 @@ class WalletKafkaConfig { @Autowired @ConditionalOnBean(UserCreatedKafkaListener::class) - fun configureUserCreatedListener(listener: UserCreatedKafkaListener - , @Qualifier("walletConsumerFactory") consumerFactory: ConsumerFactory) { + fun configureUserCreatedListener(listener: UserCreatedKafkaListener, @Qualifier("walletConsumerFactory") consumerFactory: ConsumerFactory) { val containerProps = ContainerProperties(Pattern.compile("auth_user_created")) containerProps.messageListener = listener val container = ConcurrentMessageListenerContainer(consumerFactory, containerProps) @@ -80,10 +78,9 @@ class WalletKafkaConfig { } - @Autowired - fun createUserCreatedTopics(applicationContext: GenericApplicationContext){ - applicationContext.registerBean("topic_auth_user_created", NewTopic::class.java, "auth_user_created", 1 ,1) + fun createUserCreatedTopics(applicationContext: GenericApplicationContext) { + applicationContext.registerBean("topic_auth_user_created", NewTopic::class.java, "auth_user_created", 1, 1) } } \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/consumer/UserCreatedKafkaListener.kt b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/consumer/UserCreatedKafkaListener.kt index edfcebff8..1c4a77e22 100644 --- a/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/consumer/UserCreatedKafkaListener.kt +++ b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/consumer/UserCreatedKafkaListener.kt @@ -10,6 +10,7 @@ import org.springframework.stereotype.Component @Component class UserCreatedKafkaListener: MessageListener { val eventListeners = arrayListOf() + override fun onMessage(data: ConsumerRecord) { eventListeners.forEach{ tl -> tl.onEvent(data.value(), data.partition(), data.offset(), data.timestamp()) diff --git a/Wallet/wallet-ports/wallet-persister-postgres/mvnw b/Wallet/wallet-ports/wallet-persister-postgres/mvnw old mode 100755 new mode 100644 diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/config/PostgresConfig.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/config/PostgresConfig.kt index 7b8b7179d..66c425a26 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/config/PostgresConfig.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/config/PostgresConfig.kt @@ -13,9 +13,6 @@ class PostgresConfig(db: DatabaseClient) { init { val initDb = db.sql { """ - drop table IF EXISTS wallet_owner; - drop table IF EXISTS wallet; - drop table IF EXISTS transaction; CREATE TABLE IF NOT EXISTS currency ( name VARCHAR(25) PRIMARY KEY, symbol VARCHAR(25) NOT NULL UNIQUE, From 5ae234b3b9c5ea4bd9b39cd02165f62496709779 Mon Sep 17 00:00:00 2001 From: maryarm Date: Sat, 17 Jul 2021 20:58:35 +0200 Subject: [PATCH 08/59] close #13: cleaned up docker-compose.yml --- Deployment/docker-compose.yml | 124 +++++++++++++++------------------- 1 file changed, 53 insertions(+), 71 deletions(-) diff --git a/Deployment/docker-compose.yml b/Deployment/docker-compose.yml index 56c671748..0dda34600 100644 --- a/Deployment/docker-compose.yml +++ b/Deployment/docker-compose.yml @@ -4,8 +4,8 @@ services: image: 'docker.io/bitnami/zookeeper:3-debian-10' ports: - '127.0.0.1:2181:2181' - # volumes: - # - $PWD/runtime/zookeeper_data:/bitnami + volumes: + - $PWD/runtime/zookeeper_data:/bitnami environment: - ALLOW_ANONYMOUS_LOGIN=yes networks: @@ -13,30 +13,12 @@ services: deploy: restart_policy: condition: on-failure - # kafka: - # image: 'wurstmeister/kafka' - # ports: - # - '29092:29092' - # environment: - # - KAFKA_BROKER_ID=0 - # - KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181 - # - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT - # - KAFKA_ADVERTISED_LISTENERS=INSIDE://kafka:9092,OUTSIDE://192.168.178.26:29092 - # - KAFKA_LISTENERS=INSIDE://:9092,OUTSIDE://:29092 - # - KAFKA_INTER_BROKER_LISTENER_NAME=INSIDE - # depends_on: - # - zookeeper - # networks: - # - opex - # deploy: - # restart_policy: - # condition: on-failure kafka: image: 'docker.io/bitnami/kafka:2-debian-10' ports: - '127.0.0.1:9092:9092' - # volumes: - # - $PWD/runtime/kafka-data:/bitnami + volumes: + - $PWD/runtime/kafka-data:/bitnami environment: - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181 - ALLOW_PLAINTEXT_LISTENER=yes @@ -92,18 +74,18 @@ services: - $PWD/runtime/accountant-data:/var/lib/postgresql/data/ networks: - opex - # postgres-eventlog: - # image: "postgres" - # ports: - # - 127.0.0.1:5434:5432 - # environment: - # - POSTGRES_USER=opex - # - POSTGRES_PASSWORD=hiopex - # - POSTGRES_DB=opex_eventlog - # volumes: - # - $PWD/runtime/eventlog-data:/var/lib/postgresql/data/ - # networks: - # - opex + postgres-eventlog: + image: "postgres" + ports: + - 127.0.0.1:5434:5432 + environment: + - POSTGRES_USER=opex + - POSTGRES_PASSWORD=hiopex + - POSTGRES_DB=opex_eventlog + volumes: + - $PWD/runtime/eventlog-data:/var/lib/postgresql/data/ + networks: + - opex postgres-auth: image: "postgres" ports: @@ -172,28 +154,28 @@ services: - redis - consul - postgres-accountant - # eventlog: - # container_name: eventlog - # build: - # context: ../EventLog/eventlog-app - # dockerfile: Dockerfile - # ports: - # - 127.0.0.1:8090:8090 - # environment: - # - JAVA_OPTS=-Xmx256m - # - SPRING_PROFILES_ACTIVE=docker - # - KAFKA_IP_PORT=kafka:9092 - # - REDIS_HOST=redis - # - CONSUL_HOST=consul - # - DB_IP_PORT=postgres-eventlog - # networks: - # - opex - # depends_on: - # - zookeeper - # - kafka - # - redis - # - consul - # - postgres-eventlog + eventlog: + container_name: eventlog + build: + context: ../EventLog/eventlog-app + dockerfile: Dockerfile + ports: + - 127.0.0.1:8090:8090 + environment: + - JAVA_OPTS=-Xmx256m + - SPRING_PROFILES_ACTIVE=docker + - KAFKA_IP_PORT=kafka:9092 + - REDIS_HOST=redis + - CONSUL_HOST=consul + - DB_IP_PORT=postgres-eventlog + networks: + - opex + depends_on: + - zookeeper + - kafka + - redis + - consul + - postgres-eventlog matching-engine: container_name: matching-engine build: @@ -313,21 +295,21 @@ services: deploy: restart_policy: condition: on-failure -# nginx: -# image: nginx:latest -# container_name: opex_nginx -# volumes: -# - ./nginx.conf:/etc/nginx/nginx.conf -# - $PWD/runtime/www:/data/www -# ports: -# - 80:80 -# depends_on: -# - wallet -# - auth -# - matching-gateway -# - api -# networks: -# - opex + nginx: + image: nginx:latest + container_name: opex_nginx + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf + - $PWD/runtime/www:/data/www + ports: + - 80:80 + depends_on: + - wallet + - auth + - matching-gateway + - api + networks: + - opex networks: opex: driver: bridge \ No newline at end of file From 90f152bad426ba448ffd70dfab85499d4badb664 Mon Sep 17 00:00:00 2001 From: Peyman Date: Wed, 28 Jul 2021 15:14:15 +0430 Subject: [PATCH 09/59] close #11: create order API implemented --- .gitignore | 3 + .../src/main/resources/application-docker.yml | 4 + .../src/main/resources/application.yml | 4 + .../api/core/inout/OrderSubmitResult.kt | 3 + .../mixchange/api/core/spi/MEGatewayProxy.kt | 20 +- .../port/api/binance/config/SecurityConfig.kt | 2 + .../binance/controller/AccountController.kt | 410 +++++++++++------- .../api/binance/proxy/MEGatewayProxyImpl.kt | 40 ++ .../port/api/binance/proxy/WalletProxyImpl.kt | 2 +- .../security/AuthenticationConverter.kt | 30 ++ .../api/binance/security/CustomAuthToken.kt | 26 ++ .../port/api/binance/util/EnumExtensions.kt | 29 ++ .../port/api/binance/util/LoggerDelegate.kt | 24 + 13 files changed, 433 insertions(+), 164 deletions(-) create mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OrderSubmitResult.kt create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/proxy/MEGatewayProxyImpl.kt create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/security/AuthenticationConverter.kt create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/security/CustomAuthToken.kt create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/util/EnumExtensions.kt create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/util/LoggerDelegate.kt diff --git a/.gitignore b/.gitignore index a1c2a238a..4fde0c05c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,9 @@ # BlueJ files *.ctxt +# Idea folders +.idea + # Mobile Tools for Java (J2ME) .mtj.tmp/ diff --git a/Api/api-app/src/main/resources/application-docker.yml b/Api/api-app/src/main/resources/application-docker.yml index b1d77a0de..9eb36d4b3 100644 --- a/Api/api-app/src/main/resources/application-docker.yml +++ b/Api/api-app/src/main/resources/application-docker.yml @@ -11,3 +11,7 @@ spring: consul: host: ${CONSUL_HOST} port: 8500 + +app: + matching-gateway: + url: lb://opex-gateway diff --git a/Api/api-app/src/main/resources/application.yml b/Api/api-app/src/main/resources/application.yml index 8da3619aa..c1a8d953e 100644 --- a/Api/api-app/src/main/resources/application.yml +++ b/Api/api-app/src/main/resources/application.yml @@ -26,3 +26,7 @@ spring: instance-id: ${spring.application.name}:${server.port} healthCheckInterval: 20s prefer-ip-address: true + +app: + matching-gateway: + url: lb://opex-gateway \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OrderSubmitResult.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OrderSubmitResult.kt new file mode 100644 index 000000000..283877d47 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OrderSubmitResult.kt @@ -0,0 +1,3 @@ +package co.nilin.mixchange.api.core.inout + +data class OrderSubmitResult(val offset: Long?) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/MEGatewayProxy.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/MEGatewayProxy.kt index 2dd6b2a17..6474f78e6 100644 --- a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/MEGatewayProxy.kt +++ b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/MEGatewayProxy.kt @@ -1,22 +1,22 @@ package co.nilin.mixchange.api.core.spi +import co.nilin.mixchange.api.core.inout.OrderSubmitResult import co.nilin.mixchange.matching.core.model.MatchConstraint import co.nilin.mixchange.matching.core.model.OrderDirection import co.nilin.mixchange.matching.core.model.OrderType +import reactor.core.publisher.Mono import java.math.BigDecimal interface MEGatewayProxy { data class CreateOrderRequest( - var uuid: String?, - val pair: String, - val price: BigDecimal, - val quantity: BigDecimal, - val direction: OrderDirection, - val matchConstraint: MatchConstraint, - val orderType: OrderType + var uuid: String?, + val pair: String, + val price: BigDecimal, + val quantity: BigDecimal, + val direction: OrderDirection, + val matchConstraint: MatchConstraint?, + val orderType: OrderType ) - class OrderSubmitResult(offset: Long?) - - fun createNewOrder(order: CreateOrderRequest): OrderSubmitResult; + suspend fun createNewOrder(order: CreateOrderRequest, token: String?): OrderSubmitResult? } \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/SecurityConfig.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/SecurityConfig.kt index c6843a4e5..fb383b506 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/SecurityConfig.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/SecurityConfig.kt @@ -1,5 +1,6 @@ package co.nilin.mixchange.port.api.binance.config +import co.nilin.mixchange.port.api.binance.security.AuthenticationConverter import org.springframework.context.annotation.Bean import org.springframework.core.io.ClassPathResource import org.springframework.core.io.Resource @@ -27,6 +28,7 @@ class SecurityConfig { .and() .oauth2ResourceServer() .jwt() + .jwtAuthenticationConverter(AuthenticationConverter()) return http.build() } diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/AccountController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/AccountController.kt index 5740cde1a..f1fef18b8 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/AccountController.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/AccountController.kt @@ -1,10 +1,18 @@ package co.nilin.mixchange.port.api.binance.controller import co.nilin.mixchange.api.core.inout.* +import co.nilin.mixchange.api.core.spi.MEGatewayProxy import co.nilin.mixchange.api.core.spi.UserQueryHandler +import co.nilin.mixchange.port.api.binance.security.CustomAuthToken +import co.nilin.mixchange.port.api.binance.util.asMatchConstraint +import co.nilin.mixchange.port.api.binance.util.asMatchingOrderType +import co.nilin.mixchange.port.api.binance.util.asOrderDirection +import com.fasterxml.jackson.annotation.JsonInclude import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map +import org.slf4j.LoggerFactory import org.springframework.http.MediaType +import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestParam @@ -15,67 +23,73 @@ import java.security.Principal import java.util.* @RestController -class AccountController(val queryHandler: UserQueryHandler) { +class AccountController(val queryHandler: UserQueryHandler, val matchingGatewayProxy: MEGatewayProxy) { data class FillsData( - val price: BigDecimal, - val qty: BigDecimal, - val commission: BigDecimal, - val commissionAsset: String + val price: BigDecimal, + val qty: BigDecimal, + val commission: BigDecimal, + val commissionAsset: String ) + @JsonInclude(JsonInclude.Include.NON_NULL) data class NewOrderResponse( - val symbol: String, - val orderId: Long, - val orderListId: Long, //Unless OCO, value will be -1 - val clientOrderId: String, - val transactTime: Date, - val price: BigDecimal?, - val origQty: BigDecimal?, - val executedQty: BigDecimal?, - val cummulativeQuoteQty: BigDecimal, - val status: OrderStatus?, - val timeInForce: TimeInForce?, - val type: OrderType?, - val side: OrderSide?, - val fills: List? + val symbol: String, + val orderId: Long, + val orderListId: Long, //Unless OCO, value will be -1 + val clientOrderId: String?, + val transactTime: Date, + val price: BigDecimal?, + val origQty: BigDecimal?, + val executedQty: BigDecimal?, + val cummulativeQuoteQty: BigDecimal?, + val status: OrderStatus?, + val timeInForce: TimeInForce?, + val type: OrderType?, + val side: OrderSide?, + val fills: List? ) + @JsonInclude(JsonInclude.Include.NON_NULL) data class QueryOrderResponse( - val symbol: String, - val orderId: Long, - val orderListId: Long, //Unless part of an OCO, the value will always be -1. - val clientOrderId: String, - val price: BigDecimal, - val origQty: BigDecimal, - val executedQty: BigDecimal, - val cummulativeQuoteQty: BigDecimal, - val status: OrderStatus, - val timeInForce: TimeInForce, - val type: OrderType, - val side: OrderSide, - val stopPrice: BigDecimal?, - val icebergQty: BigDecimal?, - val time: Date, - val updateTime: Date, - val isWorking: Boolean, - val origQuoteOrderQty: BigDecimal + val symbol: String, + val orderId: Long, + val orderListId: Long, //Unless part of an OCO, the value will always be -1. + val clientOrderId: String, + val price: BigDecimal, + val origQty: BigDecimal, + val executedQty: BigDecimal, + val cummulativeQuoteQty: BigDecimal, + val status: OrderStatus, + val timeInForce: TimeInForce, + val type: OrderType, + val side: OrderSide, + val stopPrice: BigDecimal?, + val icebergQty: BigDecimal?, + val time: Date, + val updateTime: Date, + val isWorking: Boolean, + val origQuoteOrderQty: BigDecimal ) + @JsonInclude(JsonInclude.Include.NON_NULL) data class TradeResponse( - val symbol: String, - val id: Long, - val orderId: Long, - val orderListId: Long = -1, - val price: BigDecimal, - val qty: BigDecimal, - val quoteQty: BigDecimal, - val commission: BigDecimal, - val commissionAsset: String, - val time: Date, - val isBuyer: Boolean, - val isMaker: Boolean, - val isBestMatch: Boolean) + val symbol: String, + val id: Long, + val orderId: Long, + val orderListId: Long = -1, + val price: BigDecimal, + val qty: BigDecimal, + val quoteQty: BigDecimal, + val commission: BigDecimal, + val commissionAsset: String, + val time: Date, + val isBuyer: Boolean, + val isMaker: Boolean, + val isBestMatch: Boolean + ) + + private val logger = LoggerFactory.getLogger(AccountController::class.java) /* Send in a new order. @@ -83,41 +97,68 @@ class AccountController(val queryHandler: UserQueryHandler) { Data Source: Matching Engine */ @PostMapping( - "/api/v3/order", - consumes = arrayOf(MediaType.APPLICATION_FORM_URLENCODED_VALUE), - produces = arrayOf(MediaType.APPLICATION_JSON_VALUE) + "/api/v3/order", + consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE], + produces = [MediaType.APPLICATION_JSON_VALUE] ) - fun createNewOrder( - @RequestParam(name = "symbol") - symbol: String, - @RequestParam(name = "side") - side: OrderSide, - @RequestParam(name = "type") - type: OrderType, - @RequestParam(name = "timeInForce", required = false) - timeInForce: TimeInForce?, - @RequestParam(name = "quantity", required = false) - quantity: BigDecimal?, - @RequestParam(name = "quoteOrderQty", required = false) - quoteOrderQty: BigDecimal?, - @RequestParam(name = "price", required = false) - price: BigDecimal?, - @RequestParam(name = "newClientOrderId", required = false) - newClientOrderId: String?, /* A unique id among open orders. Automatically generated if not sent. + suspend fun createNewOrder( + @RequestParam(name = "symbol") + symbol: String, + @RequestParam(name = "side") + side: OrderSide, + @RequestParam(name = "type") + type: OrderType, + @RequestParam(name = "timeInForce", required = false) + timeInForce: TimeInForce?, + @RequestParam(name = "quantity", required = false) + quantity: BigDecimal?, + @RequestParam(name = "quoteOrderQty", required = false) + quoteOrderQty: BigDecimal?, + @RequestParam(name = "price", required = false) + price: BigDecimal?, + @RequestParam(name = "newClientOrderId", required = false) + newClientOrderId: String?, /* A unique id among open orders. Automatically generated if not sent. Orders with the same newClientOrderID can be accepted only when the previous one is filled, otherwise the order will be rejected. */ - @RequestParam(name = "stopPrice", required = false) - stopPrice: BigDecimal?, //Used with STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, and TAKE_PROFIT_LIMIT orders. - @RequestParam(name = "icebergQty", required = false) - icebergQty: BigDecimal?, //Used with LIMIT, STOP_LOSS_LIMIT, and TAKE_PROFIT_LIMIT to create an iceberg order. - @RequestParam(name = "newOrderRespType", required = false) - newOrderRespType: OrderResponseType?, //Set the response JSON. ACK, RESULT, or FULL; MARKET and LIMIT order types default to FULL, all other orders default to ACK. - @RequestParam(name = "recvWindow", required = false) - recvWindow: Long?, //The value cannot be greater than 60000 - @RequestParam(name = "timestamp") - timestamp: Long - ): Mono { - TODO("Implement create order") + @RequestParam(name = "stopPrice", required = false) + stopPrice: BigDecimal?, //Used with STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, and TAKE_PROFIT_LIMIT orders. + @RequestParam(name = "icebergQty", required = false) + icebergQty: BigDecimal?, //Used with LIMIT, STOP_LOSS_LIMIT, and TAKE_PROFIT_LIMIT to create an iceberg order. + @RequestParam(name = "newOrderRespType", required = false) + newOrderRespType: OrderResponseType?, //Set the response JSON. ACK, RESULT, or FULL; MARKET and LIMIT order types default to FULL, all other orders default to ACK. + @RequestParam(name = "recvWindow", required = false) + recvWindow: Long?, //The value cannot be greater than 60000 + @RequestParam(name = "timestamp") + timestamp: Long, + @AuthenticationPrincipal auth: CustomAuthToken + ): NewOrderResponse { + val request = MEGatewayProxy.CreateOrderRequest( + auth.uuid, + symbol, + price ?: BigDecimal.ZERO, // Maybe make this nullable as well? + quantity ?: BigDecimal.ZERO, + side.asOrderDirection(), + timeInForce?.asMatchConstraint(), + type.asMatchingOrderType() + ) + + matchingGatewayProxy.createNewOrder(request, auth.tokenValue) + return NewOrderResponse( + symbol, + -1, + -1, + null, + Date(), + null, + null, + null, + null, + null, + null, + null, + null, + null + ) } /* @@ -127,26 +168,46 @@ class AccountController(val queryHandler: UserQueryHandler) { Data Source: Database */ @GetMapping( - "/api/v3/order", - consumes = arrayOf(MediaType.APPLICATION_FORM_URLENCODED_VALUE), - produces = arrayOf(MediaType.APPLICATION_JSON_VALUE) + "/api/v3/order", + consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE], + produces = [MediaType.APPLICATION_JSON_VALUE] ) - suspend fun queryOrder(principal: Principal, - @RequestParam(name = "symbol") - symbol: String, - @RequestParam(name = "orderId", required = false) - orderId: Long?, - @RequestParam(name = "origClientOrderId", required = false) - origClientOrderId: String?, - @RequestParam(name = "recvWindow", required = false) - recvWindow: Long?, //The value cannot be greater than 60000 - @RequestParam(name = "timestamp") - timestamp: Long + suspend fun queryOrder( + principal: Principal, + @RequestParam(name = "symbol") + symbol: String, + @RequestParam(name = "orderId", required = false) + orderId: Long?, + @RequestParam(name = "origClientOrderId", required = false) + origClientOrderId: String?, + @RequestParam(name = "recvWindow", required = false) + recvWindow: Long?, //The value cannot be greater than 60000 + @RequestParam(name = "timestamp") + timestamp: Long ): QueryOrderResponse { val response = queryHandler.queryOrder(principal, QueryOrderRequest(symbol, orderId, origClientOrderId)) if (response == null) throw IllegalArgumentException("no order found") - return QueryOrderResponse(response.symbol, response.orderId, response.orderListId, response.clientOrderId, response.price, response.origQty, response.executedQty, response.cummulativeQuoteQty, response.status, response.timeInForce, response.type, response.side, response.stopPrice, response.icebergQty, response.time, response.updateTime, response.isWorking, response.origQuoteOrderQty) + return QueryOrderResponse( + response.symbol, + response.orderId, + response.orderListId, + response.clientOrderId, + response.price, + response.origQty, + response.executedQty, + response.cummulativeQuoteQty, + response.status, + response.timeInForce, + response.type, + response.side, + response.stopPrice, + response.icebergQty, + response.time, + response.updateTime, + response.isWorking, + response.origQuoteOrderQty + ) } @@ -158,22 +219,42 @@ class AccountController(val queryHandler: UserQueryHandler) { Data Source: Database */ @GetMapping( - "/api/v3/openOrders", - consumes = arrayOf(MediaType.APPLICATION_FORM_URLENCODED_VALUE), - produces = arrayOf(MediaType.APPLICATION_JSON_VALUE) + "/api/v3/openOrders", + consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE], + produces = [MediaType.APPLICATION_JSON_VALUE] ) - suspend fun fetchOpenOrders(principal: Principal, - @RequestParam(name = "symbol", required = false) - symbol: String?, - @RequestParam(name = "recvWindow", required = false) - recvWindow: Long?, //The value cannot be greater than 60000 - @RequestParam(name = "timestamp") - timestamp: Long + suspend fun fetchOpenOrders( + principal: Principal, + @RequestParam(name = "symbol", required = false) + symbol: String?, + @RequestParam(name = "recvWindow", required = false) + recvWindow: Long?, //The value cannot be greater than 60000 + @RequestParam(name = "timestamp") + timestamp: Long ): Flow { return queryHandler.openOrders(principal, symbol) - .map { response -> - QueryOrderResponse(response.symbol, response.orderId, response.orderListId, response.clientOrderId, response.price, response.origQty, response.executedQty, response.cummulativeQuoteQty, response.status, response.timeInForce, response.type, response.side, response.stopPrice, response.icebergQty, response.time, response.updateTime, response.isWorking, response.origQuoteOrderQty) - } + .map { response -> + QueryOrderResponse( + response.symbol, + response.orderId, + response.orderListId, + response.clientOrderId, + response.price, + response.origQty, + response.executedQty, + response.cummulativeQuoteQty, + response.status, + response.timeInForce, + response.type, + response.side, + response.stopPrice, + response.icebergQty, + response.time, + response.updateTime, + response.isWorking, + response.origQuoteOrderQty + ) + } } /* @@ -182,28 +263,48 @@ class AccountController(val queryHandler: UserQueryHandler) { Data Source: Database */ @GetMapping( - "/api/v3/allOrders", - consumes = arrayOf(MediaType.APPLICATION_FORM_URLENCODED_VALUE), - produces = arrayOf(MediaType.APPLICATION_JSON_VALUE) + "/api/v3/allOrders", + consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE], + produces = [MediaType.APPLICATION_JSON_VALUE] ) - suspend fun fetchAllOrders(principal: Principal, - @RequestParam(name = "symbol", required = false) - symbol: String?, - @RequestParam(name = "startTime", required = false) - startTime: Date?, - @RequestParam(name = "endTime", required = false) - endTime: Date?, - @RequestParam(name = "limit", required = false) - limit: Int? = 500, //Default 500; max 1000. - @RequestParam(name = "recvWindow", required = false) - recvWindow: Long?, //The value cannot be greater than 60000 - @RequestParam(name = "timestamp") - timestamp: Long + suspend fun fetchAllOrders( + principal: Principal, + @RequestParam(name = "symbol", required = false) + symbol: String?, + @RequestParam(name = "startTime", required = false) + startTime: Date?, + @RequestParam(name = "endTime", required = false) + endTime: Date?, + @RequestParam(name = "limit", required = false) + limit: Int? = 500, //Default 500; max 1000. + @RequestParam(name = "recvWindow", required = false) + recvWindow: Long?, //The value cannot be greater than 60000 + @RequestParam(name = "timestamp") + timestamp: Long ): Flow { return queryHandler.allOrders(principal, AllOrderRequest(symbol, startTime, endTime, limit)) - .map { response -> - QueryOrderResponse(response.symbol, response.orderId, response.orderListId, response.clientOrderId, response.price, response.origQty, response.executedQty, response.cummulativeQuoteQty, response.status, response.timeInForce, response.type, response.side, response.stopPrice, response.icebergQty, response.time, response.updateTime, response.isWorking, response.origQuoteOrderQty) - } + .map { response -> + QueryOrderResponse( + response.symbol, + response.orderId, + response.orderListId, + response.clientOrderId, + response.price, + response.origQty, + response.executedQty, + response.cummulativeQuoteQty, + response.status, + response.timeInForce, + response.type, + response.side, + response.stopPrice, + response.icebergQty, + response.time, + response.updateTime, + response.isWorking, + response.origQuoteOrderQty + ) + } } /* @@ -213,33 +314,36 @@ class AccountController(val queryHandler: UserQueryHandler) { Data Source: Database */ @GetMapping( - "/api/v3/myTrades", - consumes = arrayOf(MediaType.APPLICATION_FORM_URLENCODED_VALUE), - produces = arrayOf(MediaType.APPLICATION_JSON_VALUE) + "/api/v3/myTrades", + consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE], + produces = [MediaType.APPLICATION_JSON_VALUE] ) - suspend fun fetchAllTrades(principal: Principal, - @RequestParam(name = "symbol") - symbol: String?, - @RequestParam(name = "startTime", required = false) - startTime: Date?, - @RequestParam(name = "endTime", required = false) - endTime: Date?, - @RequestParam(name = "fromId", required = false) - fromId: Long?,//TradeId to fetch from. Default gets most recent trades. - @RequestParam(name = "limit", required = false) - limit: Int? = 500, //Default 500; max 1000. - @RequestParam(name = "recvWindow", required = false) - recvWindow: Long?, //The value cannot be greater than 60000 - @RequestParam(name = "timestamp") - timestamp: Long + suspend fun fetchAllTrades( + principal: Principal, + @RequestParam(name = "symbol") + symbol: String?, + @RequestParam(name = "startTime", required = false) + startTime: Date?, + @RequestParam(name = "endTime", required = false) + endTime: Date?, + @RequestParam(name = "fromId", required = false) + fromId: Long?,//TradeId to fetch from. Default gets most recent trades. + @RequestParam(name = "limit", required = false) + limit: Int? = 500, //Default 500; max 1000. + @RequestParam(name = "recvWindow", required = false) + recvWindow: Long?, //The value cannot be greater than 60000 + @RequestParam(name = "timestamp") + timestamp: Long ): Flow { return queryHandler.allTrades(principal, TradeRequest(symbol, fromId, startTime, endTime, limit)) - .map { response -> - TradeResponse(response.symbol, response.id, - response.orderId, -1, response.price, response.qty, response.quoteQty, - response.commission, response.commissionAsset, response.time, response.isBuyer, - response.isMaker, response.isBestMatch) - } + .map { response -> + TradeResponse( + response.symbol, response.id, + response.orderId, -1, response.price, response.qty, response.quoteQty, + response.commission, response.commissionAsset, response.time, response.isBuyer, + response.isMaker, response.isBestMatch + ) + } } } \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/proxy/MEGatewayProxyImpl.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/proxy/MEGatewayProxyImpl.kt new file mode 100644 index 000000000..f228bace8 --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/proxy/MEGatewayProxyImpl.kt @@ -0,0 +1,40 @@ +package co.nilin.mixchange.port.api.binance.proxy + +import co.nilin.mixchange.api.core.inout.OrderSubmitResult +import co.nilin.mixchange.api.core.spi.MEGatewayProxy +import co.nilin.mixchange.port.api.binance.util.LoggerDelegate +import kotlinx.coroutines.reactive.awaitSingleOrNull +import org.springframework.beans.factory.annotation.Value +import org.springframework.core.ParameterizedTypeReference +import org.springframework.http.MediaType +import org.springframework.stereotype.Component +import org.springframework.web.reactive.function.client.WebClient +import org.springframework.web.reactive.function.client.body +import reactor.core.publisher.Mono +import java.net.URI + +private inline fun typeRef(): ParameterizedTypeReference = + object : ParameterizedTypeReference() {} + +@Component +class MEGatewayProxyImpl(private val client: WebClient) : MEGatewayProxy { + + private val logger by LoggerDelegate() + + @Value("\${app.matching-gateway.url}") + private lateinit var baseUrl: String + + override suspend fun createNewOrder(order: MEGatewayProxy.CreateOrderRequest, token: String?): OrderSubmitResult? { + logger.info("calling matching-gateway order create") + return client.post() + .uri(URI.create("$baseUrl/order")) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer $token") + .body(Mono.just(order)) + .retrieve() + .onStatus({ t -> t.isError }, { throw RuntimeException() }) + .bodyToMono(typeRef()) + .awaitSingleOrNull() + } +} \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/proxy/WalletProxyImpl.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/proxy/WalletProxyImpl.kt index 7406db078..b1b5817f6 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/proxy/WalletProxyImpl.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/proxy/WalletProxyImpl.kt @@ -7,7 +7,7 @@ import org.springframework.web.reactive.function.client.WebClient import java.math.BigDecimal import java.time.LocalDateTime -inline fun typeRef(): ParameterizedTypeReference = object : ParameterizedTypeReference() {} +private inline fun typeRef(): ParameterizedTypeReference = object : ParameterizedTypeReference() {} data class TransferResult(val date: LocalDateTime, val sourceBalanceBeforeAction: Amount, val sourceBalanceAfterAction: Amount, val amount: Amount) data class Amount(val currency: Currency, val amount: BigDecimal) data class Currency(val name: String, val symbol: String, val precision: Int) diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/security/AuthenticationConverter.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/security/AuthenticationConverter.kt new file mode 100644 index 000000000..4768433b4 --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/security/AuthenticationConverter.kt @@ -0,0 +1,30 @@ +package co.nilin.mixchange.port.api.binance.security + +import org.springframework.core.convert.converter.Converter +import org.springframework.security.authentication.AbstractAuthenticationToken +import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames +import org.springframework.security.oauth2.jwt.Jwt +import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter +import reactor.core.publisher.Mono + +class AuthenticationConverter : Converter> { + + private val authoritiesConverter = JwtGrantedAuthoritiesConverter() + + override fun convert(source: Jwt): Mono? { + return try { + Mono.just( + CustomAuthToken( + source.claims[IdTokenClaimNames.SUB] as String?, + source.claims["name"] as String?, + source.claims["preferred_username"] as String?, + source.claims["email"] as String?, + source.tokenValue, + authoritiesConverter.convert(source) ?: arrayListOf() + ) + ) + } catch (e: Exception) { + null + } + } +} \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/security/CustomAuthToken.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/security/CustomAuthToken.kt new file mode 100644 index 000000000..492987506 --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/security/CustomAuthToken.kt @@ -0,0 +1,26 @@ +package co.nilin.mixchange.port.api.binance.security + +import org.springframework.security.authentication.AbstractAuthenticationToken +import org.springframework.security.core.GrantedAuthority + +class CustomAuthToken( + val uuid: String?, + val fullName: String?, + val username: String?, + val email: String?, + val tokenValue: String?, + authorities: Collection = arrayListOf() +) : AbstractAuthenticationToken(authorities) { + + init { + isAuthenticated = true + } + + override fun getCredentials(): Any { + return "N/A" + } + + override fun getPrincipal(): Any? { + return uuid + } +} \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/util/EnumExtensions.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/util/EnumExtensions.kt new file mode 100644 index 000000000..e38ea4ffe --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/util/EnumExtensions.kt @@ -0,0 +1,29 @@ +package co.nilin.mixchange.port.api.binance.util + +import co.nilin.mixchange.api.core.inout.OrderSide +import co.nilin.mixchange.api.core.inout.TimeInForce +import co.nilin.mixchange.matching.core.model.MatchConstraint +import co.nilin.mixchange.matching.core.model.OrderType +import co.nilin.mixchange.matching.core.model.OrderDirection + +fun OrderSide.asOrderDirection(): OrderDirection { + if (this == OrderSide.BUY) + return OrderDirection.ASK + return OrderDirection.BID +} + +fun TimeInForce.asMatchConstraint(): MatchConstraint { + return when (this) { + TimeInForce.GTC -> MatchConstraint.GTC + TimeInForce.IOC -> MatchConstraint.IOC + TimeInForce.FOK -> MatchConstraint.FOK + } +} + +fun co.nilin.mixchange.api.core.inout.OrderType.asMatchingOrderType(): OrderType { + return when (this) { + co.nilin.mixchange.api.core.inout.OrderType.LIMIT -> OrderType.LIMIT_ORDER + co.nilin.mixchange.api.core.inout.OrderType.MARKET -> OrderType.MARKET_ORDER + else -> OrderType.LIMIT_ORDER + } +} \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/util/LoggerDelegate.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/util/LoggerDelegate.kt new file mode 100644 index 000000000..cd6645cde --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/util/LoggerDelegate.kt @@ -0,0 +1,24 @@ +package co.nilin.mixchange.port.api.binance.util + +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import kotlin.properties.ReadOnlyProperty +import kotlin.reflect.KProperty + +class LoggerDelegate : ReadOnlyProperty { + + companion object { + private fun createLogger(clazz: Class): Logger { + return LoggerFactory.getLogger(clazz) + } + } + + private var logger: Logger? = null + + override operator fun getValue(thisRef: Any?, property: KProperty<*>): Logger { + if (logger == null) { + logger = createLogger(thisRef!!.javaClass) + } + return logger!! + } +} \ No newline at end of file From a98c1c87a1eb9ac1c63c555f263892ef68205614 Mon Sep 17 00:00:00 2001 From: Peyman Date: Mon, 2 Aug 2021 13:11:34 +0430 Subject: [PATCH 10/59] close #10: Added account info endpoint --- .../src/main/resources/application-docker.yml | 2 + .../src/main/resources/application.yml | 4 +- .../api/core/inout/OwnerLimitsResponse.kt | 7 ++ .../nilin/mixchange/api/core/inout/Wallet.kt | 9 +++ .../mixchange/api/core/spi/AccountantProxy.kt | 5 ++ .../mixchange/api/core/spi/WalletProxy.kt | 8 ++ .../binance/controller/AccountController.kt | 44 ++++++++++- .../api/binance/data/AccountInfoResponse.kt | 18 +++++ .../port/api/binance/data/BalanceResponse.kt | 9 +++ .../port/api/binance/proxy/WalletProxyImpl.kt | 77 +++++++++---------- .../security/AuthenticationConverter.kt | 16 ++-- .../api/binance/security/CustomAuthToken.kt | 12 +-- .../port/api/binance/util/BalanceParser.kt | 32 ++++++++ .../wallet/app/config/SecurityConfig.kt | 1 + .../app/controller/WalletOwnerController.kt | 49 ++++++++++++ .../wallet/core/model/WalletOwner.kt | 11 ++- .../wallet/core/spi/WalletManager.kt | 2 + .../wallet/postgres/config/PostgresConfig.kt | 5 +- .../wallet/postgres/dao/WalletRepository.kt | 19 ++++- .../wallet/postgres/impl/WalletManagerImpl.kt | 38 ++++++++- .../wallet/postgres/model/WalletOwnerModel.kt | 22 +++++- 21 files changed, 319 insertions(+), 71 deletions(-) create mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OwnerLimitsResponse.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/Wallet.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/AccountantProxy.kt create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/data/AccountInfoResponse.kt create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/data/BalanceResponse.kt create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/util/BalanceParser.kt create mode 100644 Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/WalletOwnerController.kt diff --git a/Api/api-app/src/main/resources/application-docker.yml b/Api/api-app/src/main/resources/application-docker.yml index 9eb36d4b3..8ac0e8eaf 100644 --- a/Api/api-app/src/main/resources/application-docker.yml +++ b/Api/api-app/src/main/resources/application-docker.yml @@ -15,3 +15,5 @@ spring: app: matching-gateway: url: lb://opex-gateway + wallet: + url: lb://opex-wallet diff --git a/Api/api-app/src/main/resources/application.yml b/Api/api-app/src/main/resources/application.yml index c1a8d953e..66f9231c5 100644 --- a/Api/api-app/src/main/resources/application.yml +++ b/Api/api-app/src/main/resources/application.yml @@ -29,4 +29,6 @@ spring: app: matching-gateway: - url: lb://opex-gateway \ No newline at end of file + url: lb://opex-gateway + wallet: + url: lb://opex-wallet \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OwnerLimitsResponse.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OwnerLimitsResponse.kt new file mode 100644 index 000000000..de9981d9a --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OwnerLimitsResponse.kt @@ -0,0 +1,7 @@ +package co.nilin.mixchange.api.core.inout + +data class OwnerLimitsResponse( + val canTrade: Boolean, + val canWithdraw: Boolean, + val canDeposit: Boolean +) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/Wallet.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/Wallet.kt new file mode 100644 index 000000000..4e8eea1ed --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/Wallet.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.api.core.inout + +import java.math.BigDecimal + +data class Wallet( + val asset: String, + val balance: BigDecimal, + val type: String +) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/AccountantProxy.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/AccountantProxy.kt new file mode 100644 index 000000000..587a0a458 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/AccountantProxy.kt @@ -0,0 +1,5 @@ +package co.nilin.mixchange.api.core.spi + +interface AccountantProxy { + +} \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/WalletProxy.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/WalletProxy.kt index 0c4e2c72b..dab7ea894 100644 --- a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/WalletProxy.kt +++ b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/WalletProxy.kt @@ -1,4 +1,12 @@ package co.nilin.mixchange.api.core.spi +import co.nilin.mixchange.api.core.inout.OwnerLimitsResponse +import co.nilin.mixchange.api.core.inout.Wallet + interface WalletProxy { + + suspend fun getWallets(uuid: String?, token: String?): List + + suspend fun getOwnerLimits(uuid: String?, token: String?): OwnerLimitsResponse + } \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/AccountController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/AccountController.kt index f1fef18b8..71f1e5959 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/AccountController.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/AccountController.kt @@ -3,7 +3,10 @@ package co.nilin.mixchange.port.api.binance.controller import co.nilin.mixchange.api.core.inout.* import co.nilin.mixchange.api.core.spi.MEGatewayProxy import co.nilin.mixchange.api.core.spi.UserQueryHandler +import co.nilin.mixchange.api.core.spi.WalletProxy +import co.nilin.mixchange.port.api.binance.data.AccountInfoResponse import co.nilin.mixchange.port.api.binance.security.CustomAuthToken +import co.nilin.mixchange.port.api.binance.util.BalanceParser import co.nilin.mixchange.port.api.binance.util.asMatchConstraint import co.nilin.mixchange.port.api.binance.util.asMatchingOrderType import co.nilin.mixchange.port.api.binance.util.asOrderDirection @@ -17,13 +20,16 @@ import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController -import reactor.core.publisher.Mono import java.math.BigDecimal import java.security.Principal import java.util.* @RestController -class AccountController(val queryHandler: UserQueryHandler, val matchingGatewayProxy: MEGatewayProxy) { +class AccountController( + val queryHandler: UserQueryHandler, + val matchingGatewayProxy: MEGatewayProxy, + val walletProxy: WalletProxy +) { data class FillsData( val price: BigDecimal, @@ -346,4 +352,38 @@ class AccountController(val queryHandler: UserQueryHandler, val matchingGatewayP } } + @GetMapping( + "/api/v3/account", + consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE], + produces = [MediaType.APPLICATION_JSON_VALUE] + ) + suspend fun accountInfo( + @AuthenticationPrincipal + auth: CustomAuthToken, + @RequestParam(name = "recvWindow", required = false) + recvWindow: Long?, //The value cannot be greater than 60000 + @RequestParam(name = "timestamp") + timestamp: Long + ): AccountInfoResponse { + val wallets = walletProxy.getWallets(auth.uuid, auth.tokenValue) + val limits = walletProxy.getOwnerLimits(auth.uuid, auth.tokenValue) + val parsedBalances = BalanceParser.parse(wallets) + val accountType = "SPOT" + + //TODO replace commissions and accountType with actual data + return AccountInfoResponse( + 0, + 0, + 0, + 0, + limits.canTrade, + limits.canWithdraw, + limits.canDeposit, + Date().time, + accountType, + parsedBalances, + listOf(accountType) + ) + } + } \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/data/AccountInfoResponse.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/data/AccountInfoResponse.kt new file mode 100644 index 000000000..eb565cf90 --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/data/AccountInfoResponse.kt @@ -0,0 +1,18 @@ +package co.nilin.mixchange.port.api.binance.data + +import com.fasterxml.jackson.annotation.JsonInclude + +@JsonInclude(JsonInclude.Include.NON_NULL) +data class AccountInfoResponse( + val makerCommission: Long, + val takerCommission: Long, + val buyerCommission: Long, + val sellerCommission: Long, + val canTrade: Boolean, + val canWithdraw: Boolean, + val canDeposit: Boolean, + val updateTime: Long, + val accountType: String, // Enum + val balances: List, + val permissions: List // Enum +) \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/data/BalanceResponse.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/data/BalanceResponse.kt new file mode 100644 index 000000000..0094d936e --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/data/BalanceResponse.kt @@ -0,0 +1,9 @@ +package co.nilin.mixchange.port.api.binance.data + +import java.math.BigDecimal + +data class BalanceResponse( + var asset: String, + var free: BigDecimal, + var locked: BigDecimal +) \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/proxy/WalletProxyImpl.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/proxy/WalletProxyImpl.kt index b1b5817f6..a371d9c53 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/proxy/WalletProxyImpl.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/proxy/WalletProxyImpl.kt @@ -1,59 +1,52 @@ package co.nilin.mixchange.port.api.binance.proxy +import co.nilin.mixchange.api.core.inout.OwnerLimitsResponse +import co.nilin.mixchange.api.core.inout.Wallet import co.nilin.mixchange.api.core.spi.WalletProxy +import co.nilin.mixchange.port.api.binance.util.LoggerDelegate +import kotlinx.coroutines.reactive.awaitSingle +import org.springframework.beans.factory.annotation.Value import org.springframework.core.ParameterizedTypeReference +import org.springframework.http.HttpHeaders +import org.springframework.http.MediaType import org.springframework.stereotype.Component import org.springframework.web.reactive.function.client.WebClient import java.math.BigDecimal import java.time.LocalDateTime -private inline fun typeRef(): ParameterizedTypeReference = object : ParameterizedTypeReference() {} -data class TransferResult(val date: LocalDateTime, val sourceBalanceBeforeAction: Amount, val sourceBalanceAfterAction: Amount, val amount: Amount) -data class Amount(val currency: Currency, val amount: BigDecimal) -data class Currency(val name: String, val symbol: String, val precision: Int) +private inline fun typeRef(): ParameterizedTypeReference = + object : ParameterizedTypeReference() {} @Component -class WalletProxyImpl(val webClient: WebClient -) : WalletProxy { - /*override suspend fun transfer( - symbol: String, - senderWalletType: String, - senderUuid: String, - receiverWalletType: String, - receiverUuid: String, - amount: BigDecimal, - description: String?, - transferRef: String? - ) { - webClient.post() - .uri(URI.create("$walletBaseUrl/transfer/${amount}_${symbol}/from/${senderUuid}_${senderWalletType}/to/${receiverUuid}_${receiverWalletType}")) - .header("Content-Type", "application/json") - .retrieve() - .onStatus({ t -> t.isError }, { p -> - *//* - p.bodyToMono(typeRef>()).map { t -> KycSejamException(p.statusCode().value().toString(), t.error?.errorCode.toString() - + "-" + t.error?.customMessage) } - *//* - throw RuntimeException() - }) - .bodyToMono(typeRef()) - .log() - .awaitFirst() +class WalletProxyImpl(private val webClient: WebClient) : WalletProxy { + + private val logger by LoggerDelegate() + + @Value("\${app.wallet.url}") + private lateinit var baseUrl: String + override suspend fun getWallets(uuid: String?, token: String?): List { + logger.info("fetching wallets for $uuid") + return webClient.get() + .uri("$baseUrl/owner/wallet/all") + .accept(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, "Bearer $token") + .retrieve() + .onStatus({ t -> t.isError }, { throw RuntimeException() }) + .bodyToFlux(typeRef()) + .collectList() + .awaitSingle() } - override suspend fun canFulfil(symbol: String, walletType: String, uuid: String, amount: BigDecimal): Boolean { - data class BooleanResponse(val result: Boolean) + override suspend fun getOwnerLimits(uuid: String?, token: String?): OwnerLimitsResponse { + logger.info("fetching owner limits for $uuid") return webClient.get() - .uri(URI.create("$walletBaseUrl/$uuid/wallet_type/${walletType}/can_withdraw/${amount}_${symbol}")) - .header("Content-Type", "application/json") + .uri("$baseUrl/owner/limits") + .accept(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, "Bearer $token") .retrieve() - .onStatus({ t -> t.isError }, { p -> - throw RuntimeException() - }) - .bodyToMono(typeRef()) - .log() - .awaitFirst() - .result - }*/ + .onStatus({ t -> t.isError }, { throw RuntimeException() }) + .bodyToMono(typeRef()) + .awaitSingle() + } } \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/security/AuthenticationConverter.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/security/AuthenticationConverter.kt index 4768433b4..6b8ecf67b 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/security/AuthenticationConverter.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/security/AuthenticationConverter.kt @@ -14,14 +14,14 @@ class AuthenticationConverter : Converter? { return try { Mono.just( - CustomAuthToken( - source.claims[IdTokenClaimNames.SUB] as String?, - source.claims["name"] as String?, - source.claims["preferred_username"] as String?, - source.claims["email"] as String?, - source.tokenValue, - authoritiesConverter.convert(source) ?: arrayListOf() - ) + CustomAuthToken( + source.claims[IdTokenClaimNames.SUB] as String?, + source.claims["name"] as String?, + source.claims["preferred_username"] as String?, + source.claims["email"] as String?, + source.tokenValue, + authoritiesConverter.convert(source) ?: arrayListOf() + ) ) } catch (e: Exception) { null diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/security/CustomAuthToken.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/security/CustomAuthToken.kt index 492987506..f5acd1c88 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/security/CustomAuthToken.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/security/CustomAuthToken.kt @@ -4,12 +4,12 @@ import org.springframework.security.authentication.AbstractAuthenticationToken import org.springframework.security.core.GrantedAuthority class CustomAuthToken( - val uuid: String?, - val fullName: String?, - val username: String?, - val email: String?, - val tokenValue: String?, - authorities: Collection = arrayListOf() + val uuid: String?, + val fullName: String?, + val username: String?, + val email: String?, + val tokenValue: String?, + authorities: Collection = arrayListOf() ) : AbstractAuthenticationToken(authorities) { init { diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/util/BalanceParser.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/util/BalanceParser.kt new file mode 100644 index 000000000..099ba4987 --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/util/BalanceParser.kt @@ -0,0 +1,32 @@ +package co.nilin.mixchange.port.api.binance.util + +import co.nilin.mixchange.api.core.inout.Wallet +import co.nilin.mixchange.port.api.binance.data.BalanceResponse +import java.math.BigDecimal + +object BalanceParser { + + fun parse(list: List): List { + val result = arrayListOf() + + for (w in list) { + result.addOrGet(w.asset).apply { + if (w.type == "exchange") + locked = w.balance + else + free = w.balance + } + } + return result + } + + private fun ArrayList.addOrGet(symbol: String): BalanceResponse { + for (w in this) + if (w.asset == symbol) + return w + + add(BalanceResponse(symbol, BigDecimal.ZERO, BigDecimal.ZERO)) + return this.last() + } + +} \ No newline at end of file diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/SecurityConfig.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/SecurityConfig.kt index 76331b91f..3ab9013d9 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/SecurityConfig.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/SecurityConfig.kt @@ -21,6 +21,7 @@ class SecurityConfig { http.csrf().disable() .authorizeExchange() .pathMatchers("/balanceOf/**").hasAuthority("SCOPE_trust") + .pathMatchers("/owner/**").hasAuthority("SCOPE_trust") .pathMatchers("/**").permitAll() .anyExchange().authenticated() .and() diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/WalletOwnerController.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/WalletOwnerController.kt new file mode 100644 index 000000000..f4e0f854c --- /dev/null +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/WalletOwnerController.kt @@ -0,0 +1,49 @@ +package co.nilin.mixchange.wallet.app.controller + +import co.nilin.mixchange.wallet.core.spi.WalletManager +import co.nilin.mixchange.wallet.core.spi.WalletOwnerManager +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RestController +import java.math.BigDecimal +import java.security.Principal + +@RestController +class WalletOwnerController( + val walletManager: WalletManager, + val walletOwnerManager: WalletOwnerManager +) { + + data class WalletData( + val asset: String, + val balance: BigDecimal, + val type: String + ) + + data class OwnerLimitsResponse( + val canTrade: Boolean, + val canWithdraw: Boolean, + val canDeposit: Boolean + ) + + @GetMapping("/owner/wallet/all") + suspend fun getAllWallets(principal: Principal): List { + val owner = walletOwnerManager.findWalletOwner(principal.name) + if (owner != null) { + val wallets = walletManager.findWalletsByOwner(owner) + return wallets.map { + WalletData(it.currency().getSymbol(), it.balance().amount, it.type()) + } + } + return arrayListOf() + } + + @GetMapping("/owner/limits") + suspend fun getWalletOwnerLimits(principal: Principal): OwnerLimitsResponse { + val owner = walletOwnerManager.findWalletOwner(principal.name) + return if (owner != null) + OwnerLimitsResponse(owner.isTradeAllowed(), owner.isWithdrawAllowed(), owner.isDepositAllowed()) + else + OwnerLimitsResponse(canTrade = false, canWithdraw = false, canDeposit = false) + } + +} \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/WalletOwner.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/WalletOwner.kt index 6196a1053..6c5556060 100644 --- a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/WalletOwner.kt +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/WalletOwner.kt @@ -1,8 +1,11 @@ package co.nilin.mixchange.wallet.core.model interface WalletOwner { - fun id(): Long? - fun uuid(): String - fun title(): String - fun level(): String + fun id(): Long? + fun uuid(): String + fun title(): String + fun level(): String + fun isTradeAllowed(): Boolean + fun isWithdrawAllowed(): Boolean + fun isDepositAllowed(): Boolean } \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletManager.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletManager.kt index e32df70ff..f65c0c08f 100644 --- a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletManager.kt +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletManager.kt @@ -12,6 +12,8 @@ interface WalletManager { suspend fun increaseBalance(wallet: Wallet, amount: BigDecimal) suspend fun decreaseBalance(wallet: Wallet, amount: BigDecimal) suspend fun findWalletByOwnerAndCurrencyAndType(owner: WalletOwner, walletType: String, currency: Currency): Wallet? + suspend fun findWalletsByOwnerAndType(owner: WalletOwner, walletType: String): List + suspend fun findWalletsByOwner(owner: WalletOwner): List suspend fun createWallet( owner: WalletOwner, balance: Amount, diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/config/PostgresConfig.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/config/PostgresConfig.kt index 66c425a26..94b0fb2c2 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/config/PostgresConfig.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/config/PostgresConfig.kt @@ -41,7 +41,10 @@ class PostgresConfig(db: DatabaseClient) { id SERIAL PRIMARY KEY, uuid VARCHAR(36) NOT NULL UNIQUE, title VARCHAR(70) NOT NULL, - level VARCHAR(10) NOT NULL + level VARCHAR(10) NOT NULL, + trade_allowed BOOLEAN NOT NULL DEFAULT true, + withdraw_allowed BOOLEAN NOT NULL DEFAULT true, + deposit_allowed BOOLEAN NOT NULL DEFAULT true ); CREATE TABLE IF NOT EXISTS wallet ( diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletRepository.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletRepository.kt index 02ad5db93..7456175a6 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletRepository.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletRepository.kt @@ -6,15 +6,28 @@ import org.springframework.data.r2dbc.repository.Query import org.springframework.data.repository.query.Param import org.springframework.data.repository.reactive.ReactiveCrudRepository import org.springframework.stereotype.Repository +import reactor.core.publisher.Flux import reactor.core.publisher.Mono import java.math.BigDecimal @Repository interface WalletRepository : ReactiveCrudRepository { + @Query("select * from wallet where owner = :owner and wallet_type = :type and currency = :currency ") - fun findByOwnerAndTypeAndCurrency(@Param("owner") owner: Long, - @Param("type") type: String, - @Param("currency") currency: String): Mono + fun findByOwnerAndTypeAndCurrency( + @Param("owner") owner: Long, + @Param("type") type: String, + @Param("currency") currency: String + ): Mono + + @Query("select * from wallet where owner = :owner and wallet_type = :type") + fun findByOwnerAndType( + @Param("owner") owner: Long, + @Param("type") type: String, + ): Flux + + @Query("select * from wallet where owner = :owner") + fun findByOwner(@Param("owner") owner: Long): Flux @Modifying @Query("update wallet set balance = balance + :balance where id = :id") diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/WalletManagerImpl.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/WalletManagerImpl.kt index 8a3a33032..24a537cdf 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/WalletManagerImpl.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/WalletManagerImpl.kt @@ -10,6 +10,7 @@ import co.nilin.mixchange.wallet.core.model.WalletOwner import co.nilin.mixchange.wallet.core.spi.WalletManager import kotlinx.coroutines.reactive.awaitFirst import kotlinx.coroutines.reactive.awaitFirstOrNull +import kotlinx.coroutines.reactive.awaitSingle import org.springframework.stereotype.Service import java.math.BigDecimal import java.time.LocalDateTime @@ -138,8 +139,43 @@ class WalletManagerImpl( ) } + override suspend fun findWalletsByOwnerAndType(owner: WalletOwner, walletType: String): List { + val ownerModel = walletOwnerRepository.findById(owner.id()!!).awaitFirst() + return walletRepository.findByOwnerAndType(owner.id()!!, walletType) + .collectList() + .awaitSingle() + .map { + val currency = currencyRepository.findById(it.currency).awaitFirst() + SavedWallet( + it.id!!, + ownerModel, + Amount(currency, it.balance), + currency, + it.type + ) + } + } + + override suspend fun findWalletsByOwner(owner: WalletOwner): List { + val ownerModel = walletOwnerRepository.findById(owner.id()!!).awaitFirst() + return walletRepository.findByOwner(owner.id()!!) + .collectList() + .awaitSingle() + .map { + val currency = currencyRepository.findById(it.currency).awaitFirst() + SavedWallet( + it.id!!, + ownerModel, + Amount(currency, it.balance), + currency, + it.type + ) + } + } + override suspend fun createWallet(owner: WalletOwner, balance: Amount, currency: Currency, type: String): Wallet { - val walletModel = walletRepository.save(WalletModel(null, owner.id()!!, type, currency.getName(), balance.amount)) + val walletModel = walletRepository + .save(WalletModel(null, owner.id()!!, type, currency.getName(), balance.amount)) .awaitFirst() val wallet = SavedWallet( walletModel.id!!, diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletOwnerModel.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletOwnerModel.kt index 8733c4bae..0728505da 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletOwnerModel.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletOwnerModel.kt @@ -10,8 +10,12 @@ class WalletOwnerModel( @Id @Column("id") var id_: Long?, @Column("uuid") var uuid_: String, @Column("title") var title_: String, - @Column("level") var level_: String -): WalletOwner { + @Column("level") var level_: String, + @Column("trade_allowed") var isTradeAllowed_: Boolean = true, + @Column("withdraw_allowed") var isWithdrawAllowed_: Boolean = true, + @Column("deposit_allowed") var isDepositAllowed_: Boolean = true, +) : WalletOwner { + override fun id(): Long? { return id_ } @@ -21,10 +25,22 @@ class WalletOwnerModel( } override fun title(): String { - return title_ + return title_ } override fun level(): String { return level_ } + + override fun isTradeAllowed(): Boolean { + return isTradeAllowed_ + } + + override fun isWithdrawAllowed(): Boolean { + return isWithdrawAllowed_ + } + + override fun isDepositAllowed(): Boolean { + return isDepositAllowed_ + } } \ No newline at end of file From 78f070bc8b8e71b48642c3d4e31ad8bab7d5bab1 Mon Sep 17 00:00:00 2001 From: metalicn20 Date: Tue, 3 Aug 2021 15:31:44 +0430 Subject: [PATCH 11/59] Add raw springfox swagger to api, wallet and matching gateway modules --- Api/api-app/pom.xml | 9 ++--- .../kotlin/co/nilin/mixchange/app/ApiApp.kt | 35 +++++++++++++++++-- .../port/api/binance/config/SecurityConfig.kt | 3 ++ MatchingGateway/gateway-app/pom.xml | 5 +++ .../nilin/mixchange/app/MatchingGatewayApp.kt | 31 ++++++++++++++-- .../mixchange/app/config/SecurityConfig.kt | 3 ++ Wallet/wallet-app/pom.xml | 5 +++ .../nilin/mixchange/wallet/app/WalletApp.kt | 30 ++++++++++++++-- 8 files changed, 110 insertions(+), 11 deletions(-) diff --git a/Api/api-app/pom.xml b/Api/api-app/pom.xml index 8cca739d5..29f2c0749 100644 --- a/Api/api-app/pom.xml +++ b/Api/api-app/pom.xml @@ -23,10 +23,6 @@ - - org.springframework.boot - spring-boot-starter - org.jetbrains.kotlin kotlin-reflect @@ -68,6 +64,11 @@ api-persister-postgres ${api.version} + + io.springfox + springfox-boot-starter + 3.0.0 + org.springframework.boot diff --git a/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/ApiApp.kt b/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/ApiApp.kt index 8a9752dba..c63b8987c 100644 --- a/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/ApiApp.kt +++ b/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/ApiApp.kt @@ -2,12 +2,41 @@ package co.nilin.mixchange.app import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication +import org.springframework.context.annotation.Bean import org.springframework.context.annotation.ComponentScan +import springfox.documentation.builders.ApiInfoBuilder +import springfox.documentation.builders.PathSelectors.regex +import springfox.documentation.service.ApiInfo +import springfox.documentation.spi.DocumentationType +import springfox.documentation.spring.web.plugins.Docket +import springfox.documentation.swagger2.annotations.EnableSwagger2 + @SpringBootApplication +@EnableSwagger2 @ComponentScan("co.nilin.mixchange") -class AccountantApp +class ApiApp { + @Bean + fun opexApi(): Docket? { + return Docket(DocumentationType.SWAGGER_2) + .groupName("opex-api") + .apiInfo(apiInfo()) + .select() + .paths(regex("^/api/v3.*")) + .build() + } + + private fun apiInfo(): ApiInfo? { + return ApiInfoBuilder() + .title("OPEX API") + .description("Backend for opex exchange.") + .license("MIT License") + .licenseUrl("https://github.com/opexdev/Back-end/blob/feature/1-MVP/LICENSE") + .version("0.1") + .build() + } +} fun main(args: Array) { - runApplication(*args) -} \ No newline at end of file + runApplication(*args) +} diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/SecurityConfig.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/SecurityConfig.kt index fb383b506..2186dfb3a 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/SecurityConfig.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/SecurityConfig.kt @@ -23,6 +23,9 @@ class SecurityConfig { .authorizeExchange() .pathMatchers("/hello").permitAll() .pathMatchers("/actuator/**").permitAll() + .pathMatchers("/swagger-ui/**").permitAll() + .pathMatchers("/swagger-resources/**").permitAll() + .pathMatchers("/v2/api-docs").permitAll() .pathMatchers("/**").hasAuthority("SCOPE_trust") .anyExchange().authenticated() .and() diff --git a/MatchingGateway/gateway-app/pom.xml b/MatchingGateway/gateway-app/pom.xml index d34882ea5..b289589e2 100644 --- a/MatchingGateway/gateway-app/pom.xml +++ b/MatchingGateway/gateway-app/pom.xml @@ -84,6 +84,11 @@ bcprov-jdk15on 1.60 + + io.springfox + springfox-boot-starter + 3.0.0 + diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/MatchingGatewayApp.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/MatchingGatewayApp.kt index 81b16fdda..d8f76c7a8 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/MatchingGatewayApp.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/MatchingGatewayApp.kt @@ -2,11 +2,38 @@ package co.nilin.mixchange.app import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication +import org.springframework.context.annotation.Bean import org.springframework.context.annotation.ComponentScan +import springfox.documentation.builders.ApiInfoBuilder +import springfox.documentation.builders.PathSelectors.regex +import springfox.documentation.service.ApiInfo +import springfox.documentation.spi.DocumentationType +import springfox.documentation.spring.web.plugins.Docket @SpringBootApplication @ComponentScan("co.nilin.mixchange") -class MatchingGatewayApp +class MatchingGatewayApp { + @Bean + fun opexMatchingGateway(): Docket? { + return Docket(DocumentationType.SWAGGER_2) + .groupName("opex-matching-gateway") + .apiInfo(apiInfo()) + .select() + .paths(regex("^/actuator.*").negate()) + .build() + } + + private fun apiInfo(): ApiInfo? { + return ApiInfoBuilder() + .title("OPEX API") + .description("Backend for opex exchange.") + .license("MIT License") + .licenseUrl("https://github.com/opexdev/Back-end/blob/feature/1-MVP/LICENSE") + .version("0.1") + .build() + } +} + fun main(args: Array) { - runApplication(*args) + runApplication(*args) } \ No newline at end of file diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/SecurityConfig.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/SecurityConfig.kt index 2048a7616..4b8efc7e5 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/SecurityConfig.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/SecurityConfig.kt @@ -22,6 +22,9 @@ class SecurityConfig { .authorizeExchange() .pathMatchers("/hello").permitAll() .pathMatchers("/actuator/**").permitAll() + .pathMatchers("/swagger-ui/**").permitAll() + .pathMatchers("/swagger-resources/**").permitAll() + .pathMatchers("/v2/api-docs").permitAll() .pathMatchers("/**").hasAuthority("SCOPE_trust") .anyExchange().authenticated() .and() diff --git a/Wallet/wallet-app/pom.xml b/Wallet/wallet-app/pom.xml index 59a665e79..49ba353d7 100644 --- a/Wallet/wallet-app/pom.xml +++ b/Wallet/wallet-app/pom.xml @@ -107,6 +107,11 @@ bcprov-jdk15on 1.60 + + io.springfox + springfox-boot-starter + 3.0.0 + diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/WalletApp.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/WalletApp.kt index a02438a63..8932d6730 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/WalletApp.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/WalletApp.kt @@ -2,12 +2,38 @@ package co.nilin.mixchange.wallet.app import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication +import org.springframework.context.annotation.Bean import org.springframework.context.annotation.ComponentScan +import springfox.documentation.builders.ApiInfoBuilder +import springfox.documentation.builders.PathSelectors.regex +import springfox.documentation.service.ApiInfo +import springfox.documentation.spi.DocumentationType +import springfox.documentation.spring.web.plugins.Docket @SpringBootApplication @ComponentScan("co.nilin.mixchange") -class WalletApp +class WalletApp { + @Bean + fun opexWallet(): Docket? { + return Docket(DocumentationType.SWAGGER_2) + .groupName("opex-wallet") + .apiInfo(apiInfo()) + .select() + .paths(regex("^/actuator.*").negate()) + .build() + } + + private fun apiInfo(): ApiInfo? { + return ApiInfoBuilder() + .title("OPEX API") + .description("Backend for opex exchange.") + .license("MIT License") + .licenseUrl("https://github.com/opexdev/Back-end/blob/feature/1-MVP/LICENSE") + .version("0.1") + .build() + } +} fun main(args: Array) { - runApplication(*args) + runApplication(*args) } From 8e286701113752e9f27f5fed4a3661a44b55877c Mon Sep 17 00:00:00 2001 From: Peyman Date: Wed, 4 Aug 2021 16:44:10 +0430 Subject: [PATCH 12/59] close #15: Converted "mixchange" to "opex" --- Accountant/accountant-app/pom.xml | 14 +- .../{mixchange => opex}/app/AccountantApp.kt | 7 +- .../app/config/AppConfig.kt | 45 ++-- .../app/config/AppDispatchers.kt | 2 +- .../app/controller/AccountantController.kt | 14 +- .../app/scheduler/FinancialActionsJob.kt | 6 +- .../app/scheduler/TempEventsJob.kt | 10 +- Accountant/accountant-core/pom.xml | 6 +- .../accountant/core/api/TradeManager.kt | 8 - .../mixchange/accountant/core/model/Order.kt | 31 --- .../accountant/core/model/TempEvent.kt | 7 - .../service/FinancialActionJobManagerImpl.kt | 34 --- .../core/service/OrderManagerImpl.kt | 218 --------------- .../core/spi/FinancialActionPersister.kt | 9 - .../accountant/core/spi/PairConfigLoader.kt | 8 - .../accountant/core/spi/RichOrderPublisher.kt | 7 - .../accountant/core/spi/RichTradePublisher.kt | 7 - .../core/spi/TempEventRepublisher.kt | 7 - .../core/api/FinancialActionJobManager.kt | 2 +- .../accountant/core/api/OrderManager.kt | 6 +- .../opex/accountant/core/api/TradeManager.kt | 8 + .../accountant/core/inout/OrderStatus.kt | 2 +- .../accountant/core/inout/RichOrder.kt | 9 +- .../accountant/core/inout/RichTrade.kt | 4 +- .../accountant/core/model/FinancialAction.kt | 28 +- .../nilin/opex/accountant/core/model/Order.kt | 31 +++ .../accountant/core/model/PairConfig.kt | 2 +- .../accountant/core/model/PairFeeConfig.kt | 2 +- .../opex/accountant/core/model/TempEvent.kt | 6 + .../service/FinancialActionJobManagerImpl.kt | 39 +++ .../core/service/OrderManagerImpl.kt | 220 +++++++++++++++ .../core/service/TradeManagerImpl.kt | 252 +++++++++--------- .../core/spi/FinancialActionLoader.kt | 4 +- .../core/spi/FinancialActionPersister.kt | 9 + .../accountant/core/spi/OrderPersister.kt | 4 +- .../accountant/core/spi/PairConfigLoader.kt | 8 + .../core/spi/PairStaticRateLoader.kt | 2 +- .../accountant/core/spi/RichOrderPublisher.kt | 7 + .../accountant/core/spi/RichTradePublisher.kt | 7 + .../accountant/core/spi/TempEventPersister.kt | 6 +- .../core/spi/TempEventRepublisher.kt | 7 + .../accountant/core/spi/WalletProxy.kt | 3 +- .../accountant/core/service/MockitoHelper.kt | 2 +- .../core/service/OrderManagerImplTest.kt | 88 +++--- .../core/service/TradeManagerImplTest.kt | 88 +++--- .../accountant-eventlistener-kafka/pom.xml | 8 +- .../order/kafka/inout/OrderSubmitRequest.kt | 39 --- .../kafka/config/AccountantKafkaConfig.kt | 14 +- .../kafka/consumer/EventKafkaListener.kt | 9 +- .../kafka/consumer/OrderKafkaListener.kt | 11 +- .../kafka/consumer/TempEventKafkaListener.kt | 9 +- .../kafka/consumer/TradeKafkaListener.kt | 10 +- .../accountant}/kafka/spi/EventListener.kt | 5 +- .../kafka/spi/OrderSubmitRequestListener.kt | 4 +- .../accountant/kafka/spi/TempEventListener.kt | 5 +- .../accountant}/kafka/spi/TradeListener.kt | 5 +- .../order/kafka/inout/OrderSubmitRequest.kt | 43 +++ .../accountant-persister-postgres/pom.xml | 8 +- .../postgres/dao/PairFeeConfigRepository.kt | 19 -- .../postgres/impl/OrderPersisterImpl.kt | 74 ----- .../postgres/impl/TempEventPersisterImpl.kt | 59 ---- .../postgres/model/TempEventModel.kt | 14 - .../postgres/config/PostgresConfig.kt | 7 +- .../postgres/dao/FinancialActionRepository.kt | 9 +- .../postgres/dao/OrderRepository.kt | 4 +- .../postgres/dao/PairConfigRepository.kt | 4 +- .../postgres/dao/PairFeeConfigRepository.kt | 20 ++ .../postgres/dao/TempEventRepository.kt | 11 +- .../impl/FinancialActionLoaderImpl.kt | 18 +- .../impl/FinancialActionPersisterImpl.kt | 15 +- .../postgres/impl/OrderPersisterImpl.kt | 74 +++++ .../postgres/impl/PairConfigLoaderImpl.kt | 23 +- .../postgres/impl/PairStaticRateLoaderImpl.kt | 9 +- .../postgres/impl/TempEventPersisterImpl.kt | 67 +++++ .../postgres/model/FinancialActionModel.kt | 8 +- .../accountant/postgres/model/OrderModel.kt | 9 +- .../postgres/model/PairConfigModel.kt | 2 +- .../postgres/model/PairFeeConfigModel.kt | 5 +- .../postgres/model/TempEventModel.kt | 16 ++ .../accountant-submitter-kafka/pom.xml | 8 +- .../kafka/config/SubmitterKafkaConfig.kt | 10 +- .../kafka/service/RichOrderSubmitter.kt | 8 +- .../kafka/service/RichTradeSubmitter.kt | 8 +- .../kafka/service/TempEventSubmitter.kt | 6 +- .../accountant-wallet-proxy/pom.xml | 8 +- .../wallet/config/WebClientConfig.kt | 2 +- .../wallet/proxy/WalletProxyImpl.kt | 4 +- Accountant/pom.xml | 4 +- Api/api-app/pom.xml | 14 +- .../nilin/mixchange/app/config/AppConfig.kt | 61 ----- .../nilin/{mixchange => opex}/app/ApiApp.kt | 4 +- .../co/nilin/opex/app/config/AppConfig.kt | 71 +++++ .../app/config/AppDispatchers.kt | 2 +- Api/api-core/pom.xml | 8 +- .../api/core/inout/AllOrderRequest.kt | 10 - .../api/core/inout/CreateOrderRequest.kt | 19 -- .../api/core/inout/CreateOrderResponse.kt | 21 -- .../api/core/inout/OrderTradeData.kt | 10 - .../api/core/inout/QueryOrderRequest.kt | 7 - .../api/core/inout/QueryOrderResponse.kt | 25 -- .../mixchange/api/core/inout/TradeRequest.kt | 10 - .../mixchange/api/core/inout/TradeResponse.kt | 19 -- .../mixchange/api/core/spi/AccountantProxy.kt | 5 - .../mixchange/api/core/spi/OrderPersister.kt | 7 - .../mixchange/api/core/spi/TradePersister.kt | 7 - .../opex/api/core/inout/AllOrderRequest.kt | 10 + .../opex/api/core/inout/CreateOrderRequest.kt | 19 ++ .../api/core/inout/CreateOrderResponse.kt | 21 ++ .../api/core/inout/OrderEnums.kt | 2 +- .../api/core/inout/OrderSubmitResult.kt | 2 +- .../opex/api/core/inout/OrderTradeData.kt | 10 + .../api/core/inout/OwnerLimitsResponse.kt | 2 +- .../opex/api/core/inout/QueryOrderRequest.kt | 7 + .../opex/api/core/inout/QueryOrderResponse.kt | 25 ++ .../nilin/opex/api/core/inout/TradeRequest.kt | 11 + .../opex/api/core/inout/TradeResponse.kt | 20 ++ .../api/core/inout/Wallet.kt | 2 +- .../opex/api/core/spi/AccountantProxy.kt | 5 + .../api/core/spi/MEGatewayProxy.kt | 10 +- .../nilin/opex/api/core/spi/OrderPersister.kt | 7 + .../nilin/opex/api/core/spi/TradePersister.kt | 7 + .../api/core/spi/UserQueryHandler.kt | 5 +- .../api/core/spi/WalletProxy.kt | 6 +- Api/api-ports/api-binance-rest/pom.xml | 6 +- .../port/api/binance/util/EnumExtensions.kt | 29 -- .../port/api/binance/config/RestConfig.kt | 3 +- .../port/api/binance/config/SecurityConfig.kt | 28 +- .../api/binance/config/WebClientConfig.kt | 12 +- .../binance/controller/AccountController.kt | 23 +- .../binance/controller/FiltersController.kt | 2 +- .../binance/controller/MarketController.kt | 2 +- .../api/binance/data/AccountInfoResponse.kt | 2 +- .../port/api/binance/data/BalanceResponse.kt | 2 +- .../api/binance/proxy/MEGatewayProxyImpl.kt | 8 +- .../port/api/binance/proxy/WalletProxyImpl.kt | 10 +- .../security/AuthenticationConverter.kt | 2 +- .../api/binance/security/CustomAuthToken.kt | 2 +- .../port/api/binance/util/BalanceParser.kt | 6 +- .../port/api/binance/util/EnumExtensions.kt | 29 ++ .../port/api/binance/util/LoggerDelegate.kt | 2 +- Api/api-ports/api-eventlistener-kafka/pom.xml | 10 +- .../port/api/kafka/config/ApiKafkaConfig.kt | 14 +- .../api/kafka/consumer/EventKafkaListener.kt | 6 +- .../api/kafka/consumer/OrderKafkaListener.kt | 9 +- .../api/kafka/consumer/TradeKafkaListener.kt | 7 +- .../opex/port/api}/kafka/spi/EventListener.kt | 4 +- .../port/api/kafka/spi/RichOrderListener.kt | 4 +- .../port/api/kafka/spi/RichTradeListener.kt | 4 +- Api/api-ports/api-persister-postgres/pom.xml | 10 +- .../api/postgres/impl/OrderPersisterImpl.kt | 53 ---- .../api/postgres/impl/TradePersisterImpl.kt | 124 --------- .../api/postgres/impl/UserQueryHandlerImpl.kt | 133 --------- .../port/api/postgres/model/OrderModel.kt | 38 --- .../port/api/postgres/model/TradeModel.kt | 27 -- .../api/postgres/config/PostgresConfig.kt | 6 +- .../port/api/postgres/dao/OrderRepository.kt | 51 ++-- .../port/api/postgres/dao/TradeRepository.kt | 4 +- .../api/postgres/impl/OrderPersisterImpl.kt | 53 ++++ .../api/postgres/impl/TradePersisterImpl.kt | 124 +++++++++ .../api/postgres/impl/UserQueryHandlerImpl.kt | 136 ++++++++++ .../port/api/postgres/model/OrderModel.kt | 38 +++ .../port/api/postgres/model/TradeModel.kt | 27 ++ .../port/api/postgres/util/EnumExtensions.kt | 24 +- Api/pom.xml | 4 +- EventLog/eventlog-app/pom.xml | 12 +- .../eventlog/app/EventLogApp.kt | 4 +- .../eventlog/app/config/AppConfig.kt | 26 +- EventLog/eventlog-core/pom.xml | 6 +- .../co/nilin/mixchange/eventlog/spi/Event.kt | 4 - .../mixchange/eventlog/spi/EventPersister.kt | 7 - .../co/nilin/mixchange/eventlog/spi/Order.kt | 4 - .../co/nilin/mixchange/eventlog/spi/Trade.kt | 4 - .../mixchange/eventlog/spi/TradePersister.kt | 7 - .../co/nilin/opex/eventlog/spi/Event.kt | 4 + .../nilin/opex/eventlog/spi/EventPersister.kt | 7 + .../co/nilin/opex/eventlog/spi/Order.kt | 4 + .../eventlog/spi/OrderPersister.kt | 4 +- .../co/nilin/opex/eventlog/spi/Trade.kt | 4 + .../nilin/opex/eventlog/spi/TradePersister.kt | 7 + .../eventlog-eventlistener-kafka/pom.xml | 6 +- .../kafka/config/EventlogKafkaConfig.kt | 12 +- .../kafka/consumer/EventKafkaListener.kt | 6 +- .../kafka/consumer/OrderKafkaListener.kt | 6 +- .../kafka/consumer/TradeKafkaListener.kt | 6 +- .../port/eventlog/kafka/spi/EventListener.kt | 5 +- .../kafka/spi/OrderSubmitRequestListener.kt | 4 +- .../port/eventlog}/kafka/spi/TradeListener.kt | 5 +- .../order/kafka/inout/OrderSubmitRequest.kt | 12 +- .../eventlog-persister-postgres/pom.xml | 8 +- .../postgres/model/OrderEventsModel.kt | 20 -- .../eventlog/postgres/model/OrderModel.kt | 22 -- .../eventlog/postgres/model/TradeModel.kt | 43 --- .../postgres/config/PostgresConfig.kt | 6 +- .../eventlog/postgres/dao/EventRepository.kt | 4 +- .../postgres/dao/OrderEventRepository.kt | 6 +- .../eventlog/postgres/dao/OrderRepository.kt | 4 +- .../eventlog/postgres/dao/TradeRepository.kt | 4 +- .../postgres/impl/EventPersisterImpl.kt | 12 +- .../postgres/impl/OrderPersisterImpl.kt | 14 +- .../postgres/impl/TradePersisterImpl.kt | 12 +- .../eventlog/postgres/model/EventModel.kt | 6 +- .../postgres/model/OrderEventsModel.kt | 22 ++ .../eventlog/postgres/model/OrderModel.kt | 23 ++ .../eventlog/postgres/model/TradeModel.kt | 45 ++++ EventLog/pom.xml | 4 +- MatchingEngine/matching-app/pom.xml | 12 +- .../app/MatchingEngineApp.kt | 7 +- .../app/bl/ExchangeEventHandler.kt | 8 +- .../{mixchange => opex}/app/bl/OrderBooks.kt | 10 +- .../app/config/AppConfig.kt | 18 +- .../app/config/AppSchedulers.kt | 2 +- MatchingEngine/matching-core/pom.xml | 4 +- .../matching/core/inout/OrderCancelCommand.kt | 5 - .../matching/core/inout/OrderEditCommand.kt | 5 - .../matching/core/engine/SimpleOrderBook.kt | 77 +++++- .../matching/core/eventh/EventDispatcher.kt | 4 +- .../core/eventh/events/CancelOrderEvent.kt | 10 +- .../matching/core/eventh/events/CoreEvent.kt | 4 +- .../core/eventh/events/CreateOrderEvent.kt | 10 +- .../core/eventh/events/OneOrderEvent.kt | 2 +- .../core/eventh/events/RejectOrderEvent.kt | 18 +- .../core/eventh/events/SubmitOrderEvent.kt | 10 +- .../matching/core/eventh/events/TradeEvent.kt | 6 +- .../core/eventh/events/UpdatedOrderEvent.kt | 10 +- .../matching/core/factory/OrderBookFactory.kt | 10 +- .../matching/core/inout/OrderCancelCommand.kt | 5 + .../matching/core/inout/OrderCreateCommand.kt | 10 +- .../matching/core/inout/OrderEditCommand.kt | 12 + .../matching/core/inout/RejectReason.kt | 2 +- .../matching/core/inout/RequestedOperation.kt | 2 +- .../matching/core/model/Order.kt | 2 +- .../matching/core/model/OrderBook.kt | 8 +- .../matching/core/model/OrderMetaData.kt | 2 +- .../matching/core/model/Pair.kt | 7 +- .../matching/core/model/PersistentOrder.kt | 2 +- .../core/model/PersistentOrderBook.kt | 2 +- .../matching/core/spi/OrderBookPersister.kt | 4 +- .../core/engine/SimpleOrderBookUnitTest.kt | 18 +- .../matching-eventlistener-kafka/pom.xml | 6 +- .../order/kafka/inout/OrderSubmitResult.kt | 3 - .../order/kafka/config/OrderKafkaConfig.kt | 10 +- .../kafka/consumer/OrderKafkaListener.kt | 6 +- .../order/kafka/inout/OrderSubmitRequest.kt | 10 +- .../order/kafka/inout/OrderSubmitResult.kt | 3 + .../kafka/spi/OrderSubmitRequestListener.kt | 4 +- .../matching-snapshots-redis/pom.xml | 6 +- .../order/redis/service/OrderBookPersister.kt | 24 -- .../port/order/redis/config/RedisConfig.kt | 7 +- .../order/redis/service/OrderBookPersister.kt | 26 ++ .../matching-submitter-kafka/pom.xml | 6 +- .../order/kafka/config/EventsKafkaConfig.kt | 4 +- .../order/kafka/service/EventsSubmitter.kt | 6 +- MatchingEngine/pom.xml | 4 +- MatchingGateway/gateway-app/pom.xml | 8 +- .../mixchange/app/spi/PairConfigLoader.kt | 8 - .../app/MatchingGatewayApp.kt | 4 +- .../app/config/AppConfig.kt | 12 +- .../app/config/SecurityConfig.kt | 2 +- .../app/config/WebClientConfig.kt | 2 +- .../controller/ControllerExceptionHandler.kt | 63 +++-- .../app/controller/OrderController.kt | 20 +- .../NotAllowedToSubmitOrderException.kt | 2 +- .../app/inout/CreateOrderRequest.kt | 8 +- .../app/inout/PairConfig.kt | 2 +- .../app/inout/PairFeeConfig.kt | 2 +- .../app/proxy/AccountantProxyImpl.kt | 9 +- .../app/service/OrderService.kt | 20 +- .../app/spi/AccountantApiProxy.kt | 6 +- .../co/nilin/opex/app/spi/PairConfigLoader.kt | 8 + .../order-submitter-kafka/pom.xml | 6 +- .../order/kafka/inout/OrderSubmitResult.kt | 3 - .../order/kafka/config/OrderKafkaConfig.kt | 4 +- .../order/kafka/inout/OrderSubmitRequest.kt | 10 +- .../order/kafka/inout/OrderSubmitResult.kt | 3 + .../order/kafka/service/OrderSubmitter.kt | 6 +- MatchingGateway/pom.xml | 4 +- UserManagement/keycloak-gateway/pom.xml | 4 +- .../auth/gateway/ApplicationContextHolder.kt | 4 +- .../auth/gateway/KeycloakGatewayApp.kt | 8 +- .../auth/gateway/config/AppConfig.kt | 17 +- .../config/EmbeddedKeycloakApplication.kt | 6 +- .../gateway/config/EmbeddedKeycloakConfig.kt | 2 +- .../config/EmbeddedKeycloakRequestFilter.kt | 2 +- .../auth/gateway/config/KafkaConfig.kt | 4 +- .../config/KeycloakServerProperties.kt | 2 +- .../RegularJsonConfigProviderFactory.kt | 2 +- .../auth/gateway/config/Resteasy3Provider.kt | 2 +- .../gateway/config/SimplePlatformProvider.kt | 2 +- .../gateway/config/SystemPropertyConfig.kt | 2 +- .../ExtendedEventListenerProvider.kt | 8 +- .../ExtendedEventListenerProviderFactory.kt | 2 +- .../opex}/auth/gateway/model/AuthEvent.kt | 2 +- .../auth/gateway/model/UserCreatedEvent.kt | 2 +- .../org.keycloak.common.util.ResteasyProvider | 2 +- ...ycloak.events.EventListenerProviderFactory | 2 +- .../org.keycloak.platform.PlatformProvider | 2 +- .../src/main/resources/opex-realm.json | 18 +- UserManagement/pom.xml | 4 +- Wallet/pom.xml | 4 +- Wallet/wallet-app/pom.xml | 10 +- .../wallet/app/WalletApp.kt | 4 +- .../wallet/app/config/AppConfig.kt | 18 +- .../wallet/app/config/AppDispatchers.kt | 2 +- .../wallet/app/config/SecurityConfig.kt | 2 +- .../app/controller/BalanceController.kt | 6 +- .../app/controller/InquiryController.kt | 8 +- .../wallet/app/controller/Symbol.kt | 4 +- .../app/controller/TransferController.kt | 14 +- .../app/controller/WalletOwnerController.kt | 6 +- .../wallet/app/listener/WalletListenerImpl.kt | 8 +- .../app/service/UserRegistrationService.kt | 14 +- Wallet/wallet-core/pom.xml | 4 +- .../wallet/core/spi/CurrencyRateService.kt | 9 - .../wallet/core/spi/TransactionManager.kt | 7 - .../core/exc/CurrencyNotMatchedException.kt | 2 +- .../core/exc/DepositLimitExceededException.kt | 2 +- .../core/exc/NotEnoughBalanceException.kt | 2 +- .../exc/WithdrawLimitExceededException.kt | 2 +- .../wallet/core/inout/TransferCommand.kt | 6 +- .../wallet/core/inout/TransferResult.kt | 4 +- .../wallet/core/model/Amount.kt | 2 +- .../wallet/core/model/Currency.kt | 2 +- .../wallet/core/model/Transaction.kt | 4 +- .../wallet/core/model/Wallet.kt | 2 +- .../wallet/core/model/WalletOwner.kt | 2 +- .../wallet/core/service/TransferService.kt | 28 +- .../wallet/core/spi/CurrencyRateService.kt | 9 + .../wallet/core/spi/TransactionManager.kt | 7 + .../wallet/core/spi/WalletListener.kt | 6 +- .../wallet/core/spi/WalletManager.kt | 10 +- .../wallet/core/spi/WalletOwnerManager.kt | 6 +- .../wallet-eventlistener-kafka/pom.xml | 4 +- .../opex}/auth/gateway/model/AuthEvent.kt | 2 +- .../auth/gateway/model/UserCreatedEvent.kt | 2 +- .../wallet/kafka/config/WalletKafkaConfig.kt | 10 +- .../consumer/UserCreatedKafkaListener.kt | 6 +- .../kafka/spi/UserCreatedEventListener.kt | 4 +- .../wallet-persister-postgres/pom.xml | 6 +- .../wallet/postgres/dto/TransactionStat.kt | 7 - .../wallet/postgres/config/PostgresConfig.kt | 6 +- .../postgres/dao/CurrencyRateRepository.kt | 6 +- .../wallet/postgres/dao/CurrencyRepository.kt | 6 +- .../postgres/dao/TransactionRepository.kt | 11 +- .../postgres/dao/UserLimitsRepository.kt | 6 +- .../postgres/dao/WalletConfigRepository.kt | 6 +- .../postgres/dao/WalletLimitsRepository.kt | 4 +- .../postgres/dao/WalletOwnerRepository.kt | 4 +- .../wallet/postgres/dao/WalletRepository.kt | 4 +- .../port/wallet/postgres/dto/SavedWallet.kt | 10 +- .../wallet/postgres/dto/TransactionStat.kt | 8 + .../postgres/impl/CurrencyRateServiceImpl.kt | 10 +- .../postgres/impl/TransactionManagerImpl.kt | 10 +- .../wallet/postgres/impl/WalletManagerImpl.kt | 18 +- .../postgres/impl/WalletOwnerManagerImpl.kt | 22 +- .../wallet/postgres/model/CurrencyModel.kt | 4 +- .../postgres/model/CurrencyRateModel.kt | 2 +- .../wallet/postgres/model/TransactionModel.kt | 2 +- .../wallet/postgres/model/UserLimitsModel.kt | 2 +- .../postgres/model/WalletConfigModel.kt | 5 +- .../postgres/model/WalletLimitsModel.kt | 2 +- .../port/wallet/postgres/model/WalletModel.kt | 2 +- .../wallet/postgres/model/WalletOwnerModel.kt | 4 +- 362 files changed, 2604 insertions(+), 2502 deletions(-) rename Accountant/accountant-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/AccountantApp.kt (70%) rename Accountant/accountant-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/config/AppConfig.kt (85%) rename Accountant/accountant-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/config/AppDispatchers.kt (83%) rename Accountant/accountant-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/controller/AccountantController.kt (76%) rename Accountant/accountant-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/scheduler/FinancialActionsJob.kt (78%) rename Accountant/accountant-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/scheduler/TempEventsJob.kt (75%) delete mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/api/TradeManager.kt delete mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/Order.kt delete mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/TempEvent.kt delete mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/FinancialActionJobManagerImpl.kt delete mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImpl.kt delete mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/FinancialActionPersister.kt delete mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/PairConfigLoader.kt delete mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/RichOrderPublisher.kt delete mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/RichTradePublisher.kt delete mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/TempEventRepublisher.kt rename Accountant/accountant-core/src/main/kotlin/co/nilin/{mixchange => opex}/accountant/core/api/FinancialActionJobManager.kt (69%) rename Accountant/accountant-core/src/main/kotlin/co/nilin/{mixchange => opex}/accountant/core/api/OrderManager.kt (74%) create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/api/TradeManager.kt rename Accountant/accountant-core/src/main/kotlin/co/nilin/{mixchange => opex}/accountant/core/inout/OrderStatus.kt (92%) rename Accountant/accountant-core/src/main/kotlin/co/nilin/{mixchange => opex}/accountant/core/inout/RichOrder.kt (89%) rename Accountant/accountant-core/src/main/kotlin/co/nilin/{mixchange => opex}/accountant/core/inout/RichTrade.kt (96%) rename Accountant/accountant-core/src/main/kotlin/co/nilin/{mixchange => opex}/accountant/core/model/FinancialAction.kt (58%) create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/model/Order.kt rename Accountant/accountant-core/src/main/kotlin/co/nilin/{mixchange => opex}/accountant/core/model/PairConfig.kt (83%) rename Accountant/accountant-core/src/main/kotlin/co/nilin/{mixchange => opex}/accountant/core/model/PairFeeConfig.kt (76%) create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/model/TempEvent.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/service/FinancialActionJobManagerImpl.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/service/OrderManagerImpl.kt rename Accountant/accountant-core/src/main/kotlin/co/nilin/{mixchange => opex}/accountant/core/service/TradeManagerImpl.kt (55%) rename Accountant/accountant-core/src/main/kotlin/co/nilin/{mixchange => opex}/accountant/core/spi/FinancialActionLoader.kt (73%) create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/FinancialActionPersister.kt rename Accountant/accountant-core/src/main/kotlin/co/nilin/{mixchange => opex}/accountant/core/spi/OrderPersister.kt (53%) create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/PairConfigLoader.kt rename Accountant/accountant-core/src/main/kotlin/co/nilin/{mixchange => opex}/accountant/core/spi/PairStaticRateLoader.kt (71%) create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/RichOrderPublisher.kt create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/RichTradePublisher.kt rename Accountant/accountant-core/src/main/kotlin/co/nilin/{mixchange => opex}/accountant/core/spi/TempEventPersister.kt (66%) create mode 100644 Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/TempEventRepublisher.kt rename Accountant/accountant-core/src/main/kotlin/co/nilin/{mixchange => opex}/accountant/core/spi/WalletProxy.kt (90%) rename Accountant/accountant-core/src/test/kotlin/co/nilin/{mixchange => opex}/accountant/core/service/MockitoHelper.kt (81%) rename Accountant/accountant-core/src/test/kotlin/co/nilin/{mixchange => opex}/accountant/core/service/OrderManagerImplTest.kt (68%) rename Accountant/accountant-core/src/test/kotlin/co/nilin/{mixchange => opex}/accountant/core/service/TradeManagerImplTest.kt (72%) delete mode 100644 Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt rename Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/kafka/config/AccountantKafkaConfig.kt (92%) rename Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/kafka/consumer/EventKafkaListener.kt (80%) rename Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/kafka/consumer/OrderKafkaListener.kt (71%) rename Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/kafka/consumer/TempEventKafkaListener.kt (77%) rename {EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog => Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant}/kafka/consumer/TradeKafkaListener.kt (76%) rename {Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api => Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant}/kafka/spi/EventListener.kt (55%) rename Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/kafka/spi/OrderSubmitRequestListener.kt (55%) rename Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/kafka/spi/TempEventListener.kt (56%) rename {EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog => Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant}/kafka/spi/TradeListener.kt (55%) create mode 100644 Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/inout/OrderSubmitRequest.kt delete mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/PairFeeConfigRepository.kt delete mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/OrderPersisterImpl.kt delete mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/TempEventPersisterImpl.kt delete mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/TempEventModel.kt rename Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/postgres/config/PostgresConfig.kt (97%) rename Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/postgres/dao/FinancialActionRepository.kt (80%) rename Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/postgres/dao/OrderRepository.kt (79%) rename Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/postgres/dao/PairConfigRepository.kt (63%) create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/dao/PairFeeConfigRepository.kt rename Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/postgres/dao/TempEventRepository.kt (61%) rename Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/postgres/impl/FinancialActionLoaderImpl.kt (80%) rename Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/postgres/impl/FinancialActionPersisterImpl.kt (81%) create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/OrderPersisterImpl.kt rename Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/postgres/impl/PairConfigLoaderImpl.kt (74%) rename Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/postgres/impl/PairStaticRateLoaderImpl.kt (69%) create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/TempEventPersisterImpl.kt rename Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/postgres/model/FinancialActionModel.kt (77%) rename Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/postgres/model/OrderModel.kt (87%) rename Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/postgres/model/PairConfigModel.kt (91%) rename Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/postgres/model/PairFeeConfigModel.kt (87%) create mode 100644 Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/model/TempEventModel.kt rename Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/kafka/config/SubmitterKafkaConfig.kt (93%) rename Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/kafka/service/RichOrderSubmitter.kt (78%) rename Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/kafka/service/RichTradeSubmitter.kt (78%) rename Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/kafka/service/TempEventSubmitter.kt (82%) rename Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/wallet/config/WebClientConfig.kt (94%) rename Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/{mixchange => opex}/port/accountant/wallet/proxy/WalletProxyImpl.kt (95%) delete mode 100644 Api/api-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt rename Api/api-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/ApiApp.kt (81%) create mode 100644 Api/api-app/src/main/kotlin/co/nilin/opex/app/config/AppConfig.kt rename Api/api-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/config/AppDispatchers.kt (88%) delete mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/AllOrderRequest.kt delete mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/CreateOrderRequest.kt delete mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/CreateOrderResponse.kt delete mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OrderTradeData.kt delete mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/QueryOrderRequest.kt delete mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/QueryOrderResponse.kt delete mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/TradeRequest.kt delete mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/TradeResponse.kt delete mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/AccountantProxy.kt delete mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/OrderPersister.kt delete mode 100644 Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/TradePersister.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/AllOrderRequest.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/CreateOrderRequest.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/CreateOrderResponse.kt rename Api/api-core/src/main/kotlin/co/nilin/{mixchange => opex}/api/core/inout/OrderEnums.kt (97%) rename Api/api-core/src/main/kotlin/co/nilin/{mixchange => opex}/api/core/inout/OrderSubmitResult.kt (53%) create mode 100644 Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OrderTradeData.kt rename Api/api-core/src/main/kotlin/co/nilin/{mixchange => opex}/api/core/inout/OwnerLimitsResponse.kt (73%) create mode 100644 Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/QueryOrderRequest.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/QueryOrderResponse.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/TradeRequest.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/TradeResponse.kt rename Api/api-core/src/main/kotlin/co/nilin/{mixchange => opex}/api/core/inout/Wallet.kt (74%) create mode 100644 Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/AccountantProxy.kt rename Api/api-core/src/main/kotlin/co/nilin/{mixchange => opex}/api/core/spi/MEGatewayProxy.kt (62%) create mode 100644 Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/OrderPersister.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/TradePersister.kt rename Api/api-core/src/main/kotlin/co/nilin/{mixchange => opex}/api/core/spi/UserQueryHandler.kt (81%) rename Api/api-core/src/main/kotlin/co/nilin/{mixchange => opex}/api/core/spi/WalletProxy.kt (55%) delete mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/util/EnumExtensions.kt rename Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/binance/config/RestConfig.kt (91%) rename Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/binance/config/SecurityConfig.kt (66%) rename Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/binance/config/WebClientConfig.kt (71%) rename Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/binance/controller/AccountController.kt (95%) rename Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/binance/controller/FiltersController.kt (66%) rename Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/binance/controller/MarketController.kt (66%) rename Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/binance/data/AccountInfoResponse.kt (90%) rename Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/binance/data/BalanceResponse.kt (73%) rename Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/binance/proxy/MEGatewayProxyImpl.kt (86%) rename Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/binance/proxy/WalletProxyImpl.kt (86%) rename Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/binance/security/AuthenticationConverter.kt (95%) rename Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/binance/security/CustomAuthToken.kt (91%) rename Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/binance/util/BalanceParser.kt (81%) create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/util/EnumExtensions.kt rename Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/binance/util/LoggerDelegate.kt (92%) rename Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/kafka/config/ApiKafkaConfig.kt (92%) rename Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/kafka/consumer/EventKafkaListener.kt (81%) rename Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/kafka/consumer/OrderKafkaListener.kt (73%) rename Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/kafka/consumer/TradeKafkaListener.kt (81%) rename {Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant => Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/api}/kafka/spi/EventListener.kt (56%) rename Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/kafka/spi/RichOrderListener.kt (56%) rename Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/kafka/spi/RichTradeListener.kt (57%) delete mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/OrderPersisterImpl.kt delete mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/TradePersisterImpl.kt delete mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/UserQueryHandlerImpl.kt delete mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/model/OrderModel.kt delete mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/model/TradeModel.kt rename Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/postgres/config/PostgresConfig.kt (94%) rename Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/postgres/dao/OrderRepository.kt (52%) rename Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/postgres/dao/TradeRepository.kt (91%) create mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/OrderPersisterImpl.kt create mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/TradePersisterImpl.kt create mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt create mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/OrderModel.kt create mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/TradeModel.kt rename Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/api/postgres/util/EnumExtensions.kt (54%) rename EventLog/eventlog-app/src/main/kotlin/co/nilin/{mixchange => opex}/eventlog/app/EventLogApp.kt (80%) rename EventLog/eventlog-app/src/main/kotlin/co/nilin/{mixchange => opex}/eventlog/app/config/AppConfig.kt (83%) delete mode 100644 EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Event.kt delete mode 100644 EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/EventPersister.kt delete mode 100644 EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Order.kt delete mode 100644 EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Trade.kt delete mode 100644 EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/TradePersister.kt create mode 100644 EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/Event.kt create mode 100644 EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/EventPersister.kt create mode 100644 EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/Order.kt rename EventLog/eventlog-core/src/main/kotlin/co/nilin/{mixchange => opex}/eventlog/spi/OrderPersister.kt (76%) create mode 100644 EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/Trade.kt create mode 100644 EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/TradePersister.kt rename EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/eventlog/kafka/config/EventlogKafkaConfig.kt (92%) rename EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/eventlog/kafka/consumer/EventKafkaListener.kt (81%) rename EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/eventlog/kafka/consumer/OrderKafkaListener.kt (84%) rename {Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant => EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog}/kafka/consumer/TradeKafkaListener.kt (81%) rename EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/eventlog/kafka/spi/EventListener.kt (55%) rename {MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order => EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog}/kafka/spi/OrderSubmitRequestListener.kt (58%) rename {Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant => EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog}/kafka/spi/TradeListener.kt (55%) rename EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/order/kafka/inout/OrderSubmitRequest.kt (72%) delete mode 100644 EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/OrderEventsModel.kt delete mode 100644 EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/OrderModel.kt delete mode 100644 EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/TradeModel.kt rename EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/eventlog/postgres/config/PostgresConfig.kt (96%) rename EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/eventlog/postgres/dao/EventRepository.kt (63%) rename EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/eventlog/postgres/dao/OrderEventRepository.kt (53%) rename EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/eventlog/postgres/dao/OrderRepository.kt (63%) rename EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/eventlog/postgres/dao/TradeRepository.kt (63%) rename EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/eventlog/postgres/impl/EventPersisterImpl.kt (86%) rename EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/eventlog/postgres/impl/OrderPersisterImpl.kt (86%) rename EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/eventlog/postgres/impl/TradePersisterImpl.kt (75%) rename EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/eventlog/postgres/model/EventModel.kt (80%) create mode 100644 EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/model/OrderEventsModel.kt create mode 100644 EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/model/OrderModel.kt create mode 100644 EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/model/TradeModel.kt rename MatchingEngine/matching-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/MatchingEngineApp.kt (69%) rename MatchingEngine/matching-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/bl/ExchangeEventHandler.kt (79%) rename MatchingEngine/matching-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/bl/OrderBooks.kt (75%) rename MatchingEngine/matching-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/config/AppConfig.kt (85%) rename MatchingEngine/matching-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/config/AppSchedulers.kt (83%) delete mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderCancelCommand.kt delete mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderEditCommand.kt rename MatchingEngine/matching-core/src/main/kotlin/co/nilin/{mixchange => opex}/matching/core/engine/SimpleOrderBook.kt (88%) rename MatchingEngine/matching-core/src/main/kotlin/co/nilin/{mixchange => opex}/matching/core/eventh/EventDispatcher.kt (92%) rename MatchingEngine/matching-core/src/main/kotlin/co/nilin/{mixchange => opex}/matching/core/eventh/events/CancelOrderEvent.kt (79%) rename MatchingEngine/matching-core/src/main/kotlin/co/nilin/{mixchange => opex}/matching/core/eventh/events/CoreEvent.kt (56%) rename MatchingEngine/matching-core/src/main/kotlin/co/nilin/{mixchange => opex}/matching/core/eventh/events/CreateOrderEvent.kt (79%) rename MatchingEngine/matching-core/src/main/kotlin/co/nilin/{mixchange => opex}/matching/core/eventh/events/OneOrderEvent.kt (57%) rename MatchingEngine/matching-core/src/main/kotlin/co/nilin/{mixchange => opex}/matching/core/eventh/events/RejectOrderEvent.kt (79%) rename MatchingEngine/matching-core/src/main/kotlin/co/nilin/{mixchange => opex}/matching/core/eventh/events/SubmitOrderEvent.kt (80%) rename MatchingEngine/matching-core/src/main/kotlin/co/nilin/{mixchange => opex}/matching/core/eventh/events/TradeEvent.kt (90%) rename MatchingEngine/matching-core/src/main/kotlin/co/nilin/{mixchange => opex}/matching/core/eventh/events/UpdatedOrderEvent.kt (82%) rename MatchingEngine/matching-core/src/main/kotlin/co/nilin/{mixchange => opex}/matching/core/factory/OrderBookFactory.kt (56%) create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/inout/OrderCancelCommand.kt rename MatchingEngine/matching-core/src/main/kotlin/co/nilin/{mixchange => opex}/matching/core/inout/OrderCreateCommand.kt (60%) create mode 100644 MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/inout/OrderEditCommand.kt rename MatchingEngine/matching-core/src/main/kotlin/co/nilin/{mixchange => opex}/matching/core/inout/RejectReason.kt (69%) rename MatchingEngine/matching-core/src/main/kotlin/co/nilin/{mixchange => opex}/matching/core/inout/RequestedOperation.kt (61%) rename MatchingEngine/matching-core/src/main/kotlin/co/nilin/{mixchange => opex}/matching/core/model/Order.kt (61%) rename MatchingEngine/matching-core/src/main/kotlin/co/nilin/{mixchange => opex}/matching/core/model/OrderBook.kt (60%) rename MatchingEngine/matching-core/src/main/kotlin/co/nilin/{mixchange => opex}/matching/core/model/OrderMetaData.kt (90%) rename MatchingEngine/matching-core/src/main/kotlin/co/nilin/{mixchange => opex}/matching/core/model/Pair.kt (66%) rename MatchingEngine/matching-core/src/main/kotlin/co/nilin/{mixchange => opex}/matching/core/model/PersistentOrder.kt (95%) rename MatchingEngine/matching-core/src/main/kotlin/co/nilin/{mixchange => opex}/matching/core/model/PersistentOrderBook.kt (83%) rename MatchingEngine/matching-core/src/main/kotlin/co/nilin/{mixchange => opex}/matching/core/spi/OrderBookPersister.kt (59%) rename MatchingEngine/matching-core/src/test/kotlin/co/nilin/{mixchange => opex}/matching/core/engine/SimpleOrderBookUnitTest.kt (96%) delete mode 100644 MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitResult.kt rename MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/order/kafka/config/OrderKafkaConfig.kt (94%) rename MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/order/kafka/consumer/OrderKafkaListener.kt (81%) rename MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/order/kafka/inout/OrderSubmitRequest.kt (77%) create mode 100644 MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/inout/OrderSubmitResult.kt rename {EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog => MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order}/kafka/spi/OrderSubmitRequestListener.kt (57%) delete mode 100644 MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/mixchange/port/order/redis/service/OrderBookPersister.kt rename MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/{mixchange => opex}/port/order/redis/config/RedisConfig.kt (89%) create mode 100644 MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/opex/port/order/redis/service/OrderBookPersister.kt rename MatchingEngine/matching-ports/matching-submitter-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/order/kafka/config/EventsKafkaConfig.kt (96%) rename MatchingEngine/matching-ports/matching-submitter-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/order/kafka/service/EventsSubmitter.kt (76%) delete mode 100644 MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/spi/PairConfigLoader.kt rename MatchingGateway/gateway-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/MatchingGatewayApp.kt (81%) rename MatchingGateway/gateway-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/config/AppConfig.kt (82%) rename MatchingGateway/gateway-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/config/SecurityConfig.kt (98%) rename MatchingGateway/gateway-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/config/WebClientConfig.kt (95%) rename MatchingGateway/gateway-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/controller/ControllerExceptionHandler.kt (60%) rename MatchingGateway/gateway-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/controller/OrderController.kt (52%) rename MatchingGateway/gateway-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/exception/NotAllowedToSubmitOrderException.kt (70%) rename MatchingGateway/gateway-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/inout/CreateOrderRequest.kt (55%) rename MatchingGateway/gateway-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/inout/PairConfig.kt (87%) rename MatchingGateway/gateway-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/inout/PairFeeConfig.kt (81%) rename MatchingGateway/gateway-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/proxy/AccountantProxyImpl.kt (90%) rename MatchingGateway/gateway-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/service/OrderService.kt (77%) rename MatchingGateway/gateway-app/src/main/kotlin/co/nilin/{mixchange => opex}/app/spi/AccountantApiProxy.kt (64%) create mode 100644 MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/spi/PairConfigLoader.kt delete mode 100644 MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitResult.kt rename MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/order/kafka/config/OrderKafkaConfig.kt (93%) rename MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/order/kafka/inout/OrderSubmitRequest.kt (77%) create mode 100644 MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/inout/OrderSubmitResult.kt rename MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/order/kafka/service/OrderSubmitter.kt (82%) rename UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/{mixchange => opex}/auth/gateway/ApplicationContextHolder.kt (77%) rename UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/{mixchange => opex}/auth/gateway/KeycloakGatewayApp.kt (78%) rename UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/{mixchange => opex}/auth/gateway/config/AppConfig.kt (54%) rename UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/{mixchange => opex}/auth/gateway/config/EmbeddedKeycloakApplication.kt (95%) rename UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/{mixchange => opex}/auth/gateway/config/EmbeddedKeycloakConfig.kt (98%) rename UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/{mixchange => opex}/auth/gateway/config/EmbeddedKeycloakRequestFilter.kt (97%) rename UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/{mixchange => opex}/auth/gateway/config/KafkaConfig.kt (95%) rename UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/{mixchange => opex}/auth/gateway/config/KeycloakServerProperties.kt (90%) rename UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/{mixchange => opex}/auth/gateway/config/RegularJsonConfigProviderFactory.kt (73%) rename UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/{mixchange => opex}/auth/gateway/config/Resteasy3Provider.kt (93%) rename UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/{mixchange => opex}/auth/gateway/config/SimplePlatformProvider.kt (93%) rename UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/{mixchange => opex}/auth/gateway/config/SystemPropertyConfig.kt (97%) rename UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/{mixchange => opex}/auth/gateway/extension/ExtendedEventListenerProvider.kt (95%) rename UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/{mixchange => opex}/auth/gateway/extension/ExtendedEventListenerProviderFactory.kt (93%) rename {Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange => UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex}/auth/gateway/model/AuthEvent.kt (70%) rename UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/{mixchange => opex}/auth/gateway/model/UserCreatedEvent.kt (92%) rename Wallet/wallet-app/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/app/WalletApp.kt (79%) rename Wallet/wallet-app/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/app/config/AppConfig.kt (66%) rename Wallet/wallet-app/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/app/config/AppDispatchers.kt (81%) rename Wallet/wallet-app/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/app/config/SecurityConfig.kt (97%) rename Wallet/wallet-app/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/app/controller/BalanceController.kt (87%) rename Wallet/wallet-app/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/app/controller/InquiryController.kt (86%) rename Wallet/wallet-app/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/app/controller/Symbol.kt (73%) rename Wallet/wallet-app/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/app/controller/TransferController.kt (83%) rename Wallet/wallet-app/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/app/controller/WalletOwnerController.kt (89%) rename Wallet/wallet-app/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/app/listener/WalletListenerImpl.kt (66%) rename Wallet/wallet-app/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/app/service/UserRegistrationService.kt (68%) delete mode 100644 Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/CurrencyRateService.kt delete mode 100644 Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/TransactionManager.kt rename Wallet/wallet-core/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/core/exc/CurrencyNotMatchedException.kt (52%) rename Wallet/wallet-core/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/core/exc/DepositLimitExceededException.kt (53%) rename Wallet/wallet-core/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/core/exc/NotEnoughBalanceException.kt (51%) rename Wallet/wallet-core/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/core/exc/WithdrawLimitExceededException.kt (53%) rename Wallet/wallet-core/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/core/inout/TransferCommand.kt (50%) rename Wallet/wallet-core/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/core/inout/TransferResult.kt (65%) rename Wallet/wallet-core/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/core/model/Amount.kt (67%) rename Wallet/wallet-core/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/core/model/Currency.kt (70%) rename Wallet/wallet-core/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/core/model/Transaction.kt (61%) rename Wallet/wallet-core/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/core/model/Wallet.kt (76%) rename Wallet/wallet-core/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/core/model/WalletOwner.kt (83%) rename Wallet/wallet-core/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/core/service/TransferService.kt (75%) create mode 100644 Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/spi/CurrencyRateService.kt create mode 100644 Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/spi/TransactionManager.kt rename Wallet/wallet-core/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/core/spi/WalletListener.kt (64%) rename Wallet/wallet-core/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/core/spi/WalletManager.kt (75%) rename Wallet/wallet-core/src/main/kotlin/co/nilin/{mixchange => opex}/wallet/core/spi/WalletOwnerManager.kt (69%) rename {UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange => Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/opex}/auth/gateway/model/AuthEvent.kt (70%) rename Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/auth/gateway/model/UserCreatedEvent.kt (92%) rename Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/kafka/config/WalletKafkaConfig.kt (93%) rename Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/kafka/consumer/UserCreatedKafkaListener.kt (81%) rename Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/kafka/spi/UserCreatedEventListener.kt (58%) delete mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dto/TransactionStat.kt rename Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/postgres/config/PostgresConfig.kt (97%) rename Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/postgres/dao/CurrencyRateRepository.kt (78%) rename Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/postgres/dao/CurrencyRepository.kt (70%) rename Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/postgres/dao/TransactionRepository.kt (92%) rename Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/postgres/dao/UserLimitsRepository.kt (81%) rename Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/postgres/dao/WalletConfigRepository.kt (66%) rename Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/postgres/dao/WalletLimitsRepository.kt (92%) rename Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/postgres/dao/WalletOwnerRepository.kt (80%) rename Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/postgres/dao/WalletRepository.kt (91%) rename Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/postgres/dto/SavedWallet.kt (64%) create mode 100644 Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dto/TransactionStat.kt rename Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/postgres/impl/CurrencyRateServiceImpl.kt (78%) rename Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/postgres/impl/TransactionManagerImpl.kt (70%) rename Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/postgres/impl/WalletManagerImpl.kt (94%) rename Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/postgres/impl/WalletOwnerManagerImpl.kt (88%) rename Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/postgres/model/CurrencyModel.kt (84%) rename Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/postgres/model/CurrencyRateModel.kt (88%) rename Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/postgres/model/TransactionModel.kt (92%) rename Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/postgres/model/UserLimitsModel.kt (91%) rename Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/postgres/model/WalletConfigModel.kt (56%) rename Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/postgres/model/WalletLimitsModel.kt (92%) rename Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/postgres/model/WalletModel.kt (89%) rename Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/{mixchange => opex}/port/wallet/postgres/model/WalletOwnerModel.kt (90%) diff --git a/Accountant/accountant-app/pom.xml b/Accountant/accountant-app/pom.xml index 5a81b4930..acee7567f 100644 --- a/Accountant/accountant-app/pom.xml +++ b/Accountant/accountant-app/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex accountant-app 0.0.1-SNAPSHOT accountant-app - Accountant app Mixchange + Accountant app Opex 1.8 @@ -42,27 +42,27 @@ spring-boot-starter - co.nilin.mixchange + co.nilin.opex accountant-core ${accountant.version} - co.nilin.mixchange + co.nilin.opex accountant-eventlistener-kafka ${accountant.version} - co.nilin.mixchange + co.nilin.opex accountant-submitter-kafka ${accountant.version} - co.nilin.mixchange + co.nilin.opex accountant-persister-postgres ${accountant.version} - co.nilin.mixchange + co.nilin.opex accountant-wallet-proxy ${accountant.version} diff --git a/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/AccountantApp.kt b/Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/AccountantApp.kt similarity index 70% rename from Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/AccountantApp.kt rename to Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/AccountantApp.kt index 26b20dad6..9c797d8e3 100644 --- a/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/AccountantApp.kt +++ b/Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/AccountantApp.kt @@ -1,12 +1,13 @@ -package co.nilin.mixchange.app +package co.nilin.opex.app import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.annotation.ComponentScan @SpringBootApplication -@ComponentScan("co.nilin.mixchange") +@ComponentScan("co.nilin.opex") class AccountantApp + fun main(args: Array) { - runApplication(*args) + runApplication(*args) } \ No newline at end of file diff --git a/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt b/Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/config/AppConfig.kt similarity index 85% rename from Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt rename to Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/config/AppConfig.kt index 7cf38b406..1ddca4bdd 100644 --- a/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt +++ b/Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/config/AppConfig.kt @@ -1,22 +1,22 @@ -package co.nilin.mixchange.app.config - -import co.nilin.mixchange.accountant.core.api.FinancialActionJobManager -import co.nilin.mixchange.accountant.core.api.OrderManager -import co.nilin.mixchange.accountant.core.api.TradeManager -import co.nilin.mixchange.accountant.core.service.FinancialActionJobManagerImpl -import co.nilin.mixchange.accountant.core.service.OrderManagerImpl -import co.nilin.mixchange.accountant.core.service.TradeManagerImpl -import co.nilin.mixchange.accountant.core.spi.* -import co.nilin.mixchange.matching.core.eventh.events.* -import co.nilin.mixchange.port.accountant.kafka.consumer.OrderKafkaListener -import co.nilin.mixchange.port.accountant.kafka.spi.OrderSubmitRequestListener -import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest -import co.nilin.mixchange.port.trade.consumer.EventKafkaListener -import co.nilin.mixchange.port.trade.consumer.TempEventKafkaListener -import co.nilin.mixchange.port.trade.consumer.TradeKafkaListener -import co.nilin.mixchange.port.trade.spi.EventListener -import co.nilin.mixchange.port.trade.spi.TempEventListener -import co.nilin.mixchange.port.trade.spi.TradeListener +package co.nilin.opex.app.config + +import co.nilin.opex.accountant.core.api.FinancialActionJobManager +import co.nilin.opex.accountant.core.api.OrderManager +import co.nilin.opex.accountant.core.api.TradeManager +import co.nilin.opex.accountant.core.service.FinancialActionJobManagerImpl +import co.nilin.opex.accountant.core.service.OrderManagerImpl +import co.nilin.opex.accountant.core.service.TradeManagerImpl +import co.nilin.opex.accountant.core.spi.* +import co.nilin.opex.matching.core.eventh.events.* +import co.nilin.opex.port.accountant.kafka.consumer.OrderKafkaListener +import co.nilin.opex.port.accountant.kafka.spi.OrderSubmitRequestListener +import co.nilin.opex.port.order.kafka.inout.OrderSubmitRequest +import co.nilin.opex.port.accountant.kafka.consumer.EventKafkaListener +import co.nilin.opex.port.accountant.kafka.consumer.TempEventKafkaListener +import co.nilin.opex.port.accountant.kafka.consumer.TradeKafkaListener +import co.nilin.opex.port.accountant.kafka.spi.EventListener +import co.nilin.opex.port.accountant.kafka.spi.TempEventListener +import co.nilin.opex.port.accountant.kafka.spi.TradeListener import kotlinx.coroutines.runBlocking import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Value @@ -27,13 +27,18 @@ import org.springframework.scheduling.annotation.EnableScheduling @Configuration @EnableScheduling class AppConfig { + @Bean fun getFinancialActionJobManager( financialActionLoader: FinancialActionLoader, financialActionPersister: FinancialActionPersister, walletProxy: WalletProxy ): FinancialActionJobManager { - return FinancialActionJobManagerImpl(financialActionLoader, financialActionPersister, walletProxy) + return FinancialActionJobManagerImpl( + financialActionLoader, + financialActionPersister, + walletProxy + ) } @Bean diff --git a/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/config/AppDispatchers.kt b/Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/config/AppDispatchers.kt similarity index 83% rename from Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/config/AppDispatchers.kt rename to Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/config/AppDispatchers.kt index a247a4012..a94b76fb9 100644 --- a/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/config/AppDispatchers.kt +++ b/Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/config/AppDispatchers.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.app.config +package co.nilin.opex.app.config import kotlinx.coroutines.asCoroutineDispatcher import java.util.concurrent.Executors diff --git a/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/controller/AccountantController.kt b/Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/controller/AccountantController.kt similarity index 76% rename from Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/controller/AccountantController.kt rename to Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/controller/AccountantController.kt index c690fb3c6..0acdd99f2 100644 --- a/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/controller/AccountantController.kt +++ b/Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/controller/AccountantController.kt @@ -1,11 +1,11 @@ -package co.nilin.mixchange.app.controller +package co.nilin.opex.app.controller -import co.nilin.mixchange.accountant.core.model.PairFeeConfig -import co.nilin.mixchange.accountant.core.spi.FinancialActionLoader -import co.nilin.mixchange.accountant.core.spi.PairConfigLoader -import co.nilin.mixchange.accountant.core.spi.WalletProxy -import co.nilin.mixchange.matching.core.eventh.events.SubmitOrderEvent -import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.opex.accountant.core.model.PairFeeConfig +import co.nilin.opex.accountant.core.spi.FinancialActionLoader +import co.nilin.opex.accountant.core.spi.PairConfigLoader +import co.nilin.opex.accountant.core.spi.WalletProxy +import co.nilin.opex.matching.core.eventh.events.SubmitOrderEvent +import co.nilin.opex.matching.core.model.OrderDirection import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestMapping diff --git a/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/scheduler/FinancialActionsJob.kt b/Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/scheduler/FinancialActionsJob.kt similarity index 78% rename from Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/scheduler/FinancialActionsJob.kt rename to Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/scheduler/FinancialActionsJob.kt index be5c80564..d4e02be81 100644 --- a/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/scheduler/FinancialActionsJob.kt +++ b/Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/scheduler/FinancialActionsJob.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.app.scheduler +package co.nilin.opex.app.scheduler -import co.nilin.mixchange.accountant.core.api.FinancialActionJobManager +import co.nilin.opex.accountant.core.api.FinancialActionJobManager import kotlinx.coroutines.runBlocking import org.slf4j.LoggerFactory import org.springframework.scheduling.annotation.Scheduled @@ -8,7 +8,7 @@ import org.springframework.stereotype.Service @Service class FinancialActionsJob( - val financialActionJobManager: FinancialActionJobManager + val financialActionJobManager: FinancialActionJobManager ) { private val log = LoggerFactory.getLogger(FinancialActionsJob::class.java) diff --git a/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/scheduler/TempEventsJob.kt b/Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/scheduler/TempEventsJob.kt similarity index 75% rename from Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/scheduler/TempEventsJob.kt rename to Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/scheduler/TempEventsJob.kt index 27a5d8e77..6956089d2 100644 --- a/Accountant/accountant-app/src/main/kotlin/co/nilin/mixchange/app/scheduler/TempEventsJob.kt +++ b/Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/scheduler/TempEventsJob.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.app.scheduler +package co.nilin.opex.app.scheduler -import co.nilin.mixchange.accountant.core.spi.TempEventPersister -import co.nilin.mixchange.accountant.core.spi.TempEventRepublisher +import co.nilin.opex.accountant.core.spi.TempEventPersister +import co.nilin.opex.accountant.core.spi.TempEventRepublisher import kotlinx.coroutines.runBlocking import org.slf4j.LoggerFactory import org.springframework.scheduling.annotation.Scheduled @@ -9,8 +9,8 @@ import org.springframework.stereotype.Service @Service class TempEventsJob( - val tempEventPersister: TempEventPersister, - val tempEventRepublisher: TempEventRepublisher, + val tempEventPersister: TempEventPersister, + val tempEventRepublisher: TempEventRepublisher, ) { private val log = LoggerFactory.getLogger(TempEventsJob::class.java) diff --git a/Accountant/accountant-core/pom.xml b/Accountant/accountant-core/pom.xml index 789723bb8..9dbe36835 100644 --- a/Accountant/accountant-core/pom.xml +++ b/Accountant/accountant-core/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex accountant-core 0.0.1-SNAPSHOT accountant-core - Accountant logic of Mixchange + Accountant logic of Opex 1.8 @@ -53,7 +53,7 @@ - co.nilin.mixchange + co.nilin.opex matching-core ${matching.version} diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/api/TradeManager.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/api/TradeManager.kt deleted file mode 100644 index 3853cab1a..000000000 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/api/TradeManager.kt +++ /dev/null @@ -1,8 +0,0 @@ -package co.nilin.mixchange.accountant.core.api - -import co.nilin.mixchange.accountant.core.model.FinancialAction -import co.nilin.mixchange.matching.core.eventh.events.TradeEvent - -interface TradeManager { - suspend fun handleTrade(trade:TradeEvent): List -} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/Order.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/Order.kt deleted file mode 100644 index b333bb14a..000000000 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/Order.kt +++ /dev/null @@ -1,31 +0,0 @@ -package co.nilin.mixchange.accountant.core.model - -import co.nilin.mixchange.matching.core.model.MatchConstraint -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.OrderType -import java.math.BigDecimal - -data class Order( - val pair: String, - val ouid: String, - var matchingEngineId: Long?, - val makerFee: Double, - val takerFee: Double, - val leftSideFraction: Double, - val rightSideFraction: Double, - val uuid: String, - val userLevel: String, - val direction: OrderDirection, - val matchConstraint: MatchConstraint, - val orderType: OrderType, - val price: Long, - val quantity: Long, - val filledQuantity: Long, - val origPrice: BigDecimal, - val origQuantity: BigDecimal, - val filledOrigQuantity: BigDecimal, - val firstTransferAmount: BigDecimal, - var remainedTransferAmount: BigDecimal, - var status: Int, - val id: Long? = null -) \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/TempEvent.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/TempEvent.kt deleted file mode 100644 index 92b978848..000000000 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/TempEvent.kt +++ /dev/null @@ -1,7 +0,0 @@ -package co.nilin.mixchange.accountant.core.model - -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent -import java.time.LocalDateTime - -data class TempEvent(val id: Long, val ouid: String, val eventBody: CoreEvent, val eventDate: -LocalDateTime) \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/FinancialActionJobManagerImpl.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/FinancialActionJobManagerImpl.kt deleted file mode 100644 index a3fabf21d..000000000 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/FinancialActionJobManagerImpl.kt +++ /dev/null @@ -1,34 +0,0 @@ -package co.nilin.mixchange.accountant.core.service - -import co.nilin.mixchange.accountant.core.api.FinancialActionJobManager -import co.nilin.mixchange.accountant.core.model.FinancialActionStatus -import co.nilin.mixchange.accountant.core.spi.FinancialActionLoader -import co.nilin.mixchange.accountant.core.spi.FinancialActionPersister -import co.nilin.mixchange.accountant.core.spi.WalletProxy -import org.slf4j.LoggerFactory - - -class FinancialActionJobManagerImpl(val financialActionLoader: FinancialActionLoader, val financialActionPersister: FinancialActionPersister, val walletProxy: WalletProxy) : FinancialActionJobManager { - private val log = LoggerFactory.getLogger(FinancialActionJobManagerImpl::class.java) - override suspend fun processFinancialActions(offset: Long, size: Long) { - val factions = financialActionLoader.loadUnprocessed(offset, size) - factions.forEach { faction -> - try { - walletProxy.transfer( - faction.symbol, - faction.senderWalletType, - faction.sender, - faction.receiverWalletType, - faction.receiver, - faction.amount, - faction.eventType + faction.pointer, - null - ) - financialActionPersister.updateStatus(faction, FinancialActionStatus.PROCESSED) - } catch (e: Exception) { - log.error("financial job error", e) - financialActionPersister.updateStatus(faction, FinancialActionStatus.ERROR) - } - } - } -} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImpl.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImpl.kt deleted file mode 100644 index 637abda1d..000000000 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImpl.kt +++ /dev/null @@ -1,218 +0,0 @@ -package co.nilin.mixchange.accountant.core.service - -import co.nilin.mixchange.accountant.core.api.OrderManager -import co.nilin.mixchange.accountant.core.inout.OrderStatus -import co.nilin.mixchange.accountant.core.inout.RichOrder -import co.nilin.mixchange.accountant.core.model.FinancialAction -import co.nilin.mixchange.accountant.core.model.Order -import co.nilin.mixchange.accountant.core.spi.* -import co.nilin.mixchange.matching.core.eventh.events.* -import co.nilin.mixchange.matching.core.model.OrderDirection -import org.springframework.transaction.annotation.Transactional -import java.math.BigDecimal -import java.time.LocalDateTime - -open class OrderManagerImpl( - val pairConfigLoader: PairConfigLoader, - val financialActionPersister: FinancialActionPersister, - val financeActionLoader: FinancialActionLoader, - val orderPersister: OrderPersister, - val tempEventPersister: TempEventPersister, - val tempEventRepublisher: TempEventRepublisher, - val richOrderPublisher: RichOrderPublisher -) : OrderManager { - @Transactional - override suspend fun handleRequestOrder(submitOrderEvent: SubmitOrderEvent): List { - //pair + dir -> symbol - //user level? - //pair config.makerFee and takerFee - val symbol = if (submitOrderEvent.direction == OrderDirection.ASK) { - submitOrderEvent.pair.leftSideName - } else { - submitOrderEvent.pair.rightSideName - } - val pairFeeConfig = pairConfigLoader.load(submitOrderEvent.pair.toString(), submitOrderEvent.direction, "") - val makerFee = pairFeeConfig.makerFee * 1 //user level formula - val takerFee = pairFeeConfig.takerFee * 1 //user level formula - - //create fa for transfer uuid symbol main wallet to uuid symbol exchange wallet - /* - amount for sell (ask): quantity - amount for buy (bid): quantity * price - */ - val financialAction = - FinancialAction( - null, - SubmitOrderEvent::class.simpleName!!, - submitOrderEvent.ouid, - symbol, - if (submitOrderEvent.direction == OrderDirection.ASK) { - BigDecimal(submitOrderEvent.quantity).multiply(pairFeeConfig.pairConfig.leftSideFraction.toBigDecimal()) - } else { - BigDecimal(submitOrderEvent.quantity).multiply(pairFeeConfig.pairConfig.leftSideFraction.toBigDecimal()) - .multiply(submitOrderEvent.price.toBigDecimal()) - .multiply(pairFeeConfig.pairConfig.rightSideFraction.toBigDecimal()) - }, - submitOrderEvent.uuid, - "main", - submitOrderEvent.uuid, - "exchange", - LocalDateTime.now() - ) - //store order (ouid, uuid, fees, userlevel, pair, direction, price, quantity, filledQ, status, transfered) - orderPersister.save( - Order( - submitOrderEvent.pair.toString(), - submitOrderEvent.ouid, - null, - makerFee, - takerFee, - pairFeeConfig.pairConfig.leftSideFraction, - pairFeeConfig.pairConfig.rightSideFraction, - submitOrderEvent.uuid, - "", - submitOrderEvent.direction, - submitOrderEvent.matchConstraint, - submitOrderEvent.orderType, - submitOrderEvent.price, - submitOrderEvent.quantity, - submitOrderEvent.quantity - submitOrderEvent.remainedQuantity, - submitOrderEvent.price.toBigDecimal() - .multiply(pairFeeConfig.pairConfig.rightSideFraction.toBigDecimal()), - submitOrderEvent.quantity.toBigDecimal() - .multiply(pairFeeConfig.pairConfig.leftSideFraction.toBigDecimal()), - BigDecimal(submitOrderEvent.quantity - submitOrderEvent.remainedQuantity).multiply(pairFeeConfig.pairConfig.leftSideFraction.toBigDecimal()), - financialAction.amount, - financialAction.amount, - OrderStatus.REQUESTED.code - ) - ) - return financialActionPersister.persist(listOf(financialAction)) - } - - @Transactional - override suspend fun handleNewOrder(createOrderEvent: CreateOrderEvent): List { - //update order add id to other fields - val order = orderPersister.load(createOrderEvent.ouid) - if (order != null) { - order.matchingEngineId = createOrderEvent.orderId - orderPersister.save(order) - //new order accepted by engine - publishRichOrder(order, createOrderEvent.remainedQuantity.toBigDecimal()) - } else { - tempEventPersister.saveTempEvent(createOrderEvent.ouid, createOrderEvent) - } - return emptyList() - } - - private suspend fun publishRichOrder( - order: Order, remainedQuantity: BigDecimal, status: OrderStatus? = null - ) { - richOrderPublisher.publish( - RichOrder( - order.id, - order.pair, - order.ouid, - order.uuid, - order.userLevel, - order.makerFee.toBigDecimal(), - order.takerFee.toBigDecimal(), - order.leftSideFraction.toBigDecimal(), - order.rightSideFraction.toBigDecimal(), - order.direction, - order.matchConstraint, - order.orderType, - order.origPrice, - order.origQuantity, - order.origPrice.multiply(order.origQuantity), - order.quantity.toBigDecimal().subtract(remainedQuantity).multiply(order.leftSideFraction.toBigDecimal()), - order.origPrice.multiply( - order.quantity.toBigDecimal().subtract(remainedQuantity) - ), - (status ?: if (remainedQuantity.compareTo(BigDecimal.ZERO) == 0) { - OrderStatus.FILLED.code - } else if (remainedQuantity.compareTo(order.quantity.toBigDecimal()) == 0) { - OrderStatus.NEW.code - } else { - OrderStatus.PARTIALLY_FILLED.code - }) as Int - ) - ) - } - - override suspend fun handleUpdateOrder(updatedOrderEvent: UpdatedOrderEvent): List { - TODO("Not yet implemented") - } - - @Transactional - override suspend fun handleRejectOrder(rejectOrderEvent: RejectOrderEvent): List { - //order by ouid - val order = orderPersister.load(rejectOrderEvent.ouid) - if (order == null) { - tempEventPersister.saveTempEvent(rejectOrderEvent.ouid, rejectOrderEvent) - return emptyList() - } - val symbol = if (rejectOrderEvent.direction == OrderDirection.ASK) { - rejectOrderEvent.pair.leftSideName - } else { - rejectOrderEvent.pair.rightSideName - } - //check uuid - //lookup for parent fa - val parentFinancialAction = financeActionLoader.findLast(rejectOrderEvent.uuid, rejectOrderEvent.ouid) - //create fa for transfer remaining transfered uuid symbol exchange wallet to uuid main exchange wallet - val financialAction = FinancialAction( - parentFinancialAction, - RejectOrderEvent::class.simpleName!!, - rejectOrderEvent.ouid, - symbol, - order.remainedTransferAmount, - rejectOrderEvent.uuid, - "exchange", - rejectOrderEvent.uuid, - "main", - LocalDateTime.now() - ) - //update order status - order.status = OrderStatus.REJECTED.code - orderPersister.save(order) - publishRichOrder(order, order.quantity.toBigDecimal(), OrderStatus.REJECTED) - return financialActionPersister.persist(listOf(financialAction)) - } - - @Transactional - override suspend fun handleCancelOrder(cancelOrderEvent: CancelOrderEvent): List { - //order by ouid - val order = orderPersister.load(cancelOrderEvent.ouid) - if (order == null) { - tempEventPersister.saveTempEvent(cancelOrderEvent.ouid, cancelOrderEvent) - return emptyList() - } - val symbol = if (cancelOrderEvent.direction == OrderDirection.ASK) { - cancelOrderEvent.pair.leftSideName - } else { - cancelOrderEvent.pair.rightSideName - } - //check uuid - //lookup for parent fa - val parentFinancialAction = financeActionLoader.findLast(cancelOrderEvent.uuid, cancelOrderEvent.ouid) - //create fa for transfer remaining transfered uuid symbol exchange wallet to uuid main exchange wallet - val financialAction = FinancialAction( - parentFinancialAction, - RejectOrderEvent::class.simpleName!!, - cancelOrderEvent.ouid, - symbol, - order.remainedTransferAmount, - cancelOrderEvent.uuid, - "exchange", - cancelOrderEvent.uuid, - "main", - LocalDateTime.now() - ) - //update order status - order.status = OrderStatus.CANCELED.code - orderPersister.save(order) - publishRichOrder(order, cancelOrderEvent.quantity.toBigDecimal(), OrderStatus.CANCELED) - return financialActionPersister.persist(listOf(financialAction)) - } -} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/FinancialActionPersister.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/FinancialActionPersister.kt deleted file mode 100644 index e93ef7915..000000000 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/FinancialActionPersister.kt +++ /dev/null @@ -1,9 +0,0 @@ -package co.nilin.mixchange.accountant.core.spi - -import co.nilin.mixchange.accountant.core.model.FinancialAction -import co.nilin.mixchange.accountant.core.model.FinancialActionStatus - -interface FinancialActionPersister { - suspend fun persist(financialActions: List): List - suspend fun updateStatus(financialAction: FinancialAction, status: FinancialActionStatus ) -} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/PairConfigLoader.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/PairConfigLoader.kt deleted file mode 100644 index bcc7a449e..000000000 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/PairConfigLoader.kt +++ /dev/null @@ -1,8 +0,0 @@ -package co.nilin.mixchange.accountant.core.spi - -import co.nilin.mixchange.accountant.core.model.PairFeeConfig -import co.nilin.mixchange.matching.core.model.OrderDirection - -interface PairConfigLoader { - suspend fun load(pair: String, direction: OrderDirection, userLevel: String):PairFeeConfig -} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/RichOrderPublisher.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/RichOrderPublisher.kt deleted file mode 100644 index 612386203..000000000 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/RichOrderPublisher.kt +++ /dev/null @@ -1,7 +0,0 @@ -package co.nilin.mixchange.accountant.core.spi - -import co.nilin.mixchange.accountant.core.inout.RichOrder - -interface RichOrderPublisher { - suspend fun publish(order: RichOrder) -} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/RichTradePublisher.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/RichTradePublisher.kt deleted file mode 100644 index ed52dd46d..000000000 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/RichTradePublisher.kt +++ /dev/null @@ -1,7 +0,0 @@ -package co.nilin.mixchange.accountant.core.spi - -import co.nilin.mixchange.accountant.core.inout.RichTrade - -interface RichTradePublisher { - suspend fun publish(trade: RichTrade) -} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/TempEventRepublisher.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/TempEventRepublisher.kt deleted file mode 100644 index 1bdf2a6ec..000000000 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/TempEventRepublisher.kt +++ /dev/null @@ -1,7 +0,0 @@ -package co.nilin.mixchange.accountant.core.spi - -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent - -interface TempEventRepublisher { - suspend fun republish(events: List) -} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/api/FinancialActionJobManager.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/api/FinancialActionJobManager.kt similarity index 69% rename from Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/api/FinancialActionJobManager.kt rename to Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/api/FinancialActionJobManager.kt index 6aa3635fe..bb9c26062 100644 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/api/FinancialActionJobManager.kt +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/api/FinancialActionJobManager.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.accountant.core.api +package co.nilin.opex.accountant.core.api interface FinancialActionJobManager { suspend fun processFinancialActions(offset: Long, size: Long) diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/api/OrderManager.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/api/OrderManager.kt similarity index 74% rename from Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/api/OrderManager.kt rename to Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/api/OrderManager.kt index 7fb754533..fb947ea8d 100644 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/api/OrderManager.kt +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/api/OrderManager.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.accountant.core.api +package co.nilin.opex.accountant.core.api -import co.nilin.mixchange.accountant.core.model.FinancialAction -import co.nilin.mixchange.matching.core.eventh.events.* +import co.nilin.opex.accountant.core.model.FinancialAction +import co.nilin.opex.matching.core.eventh.events.* interface OrderManager { suspend fun handleRequestOrder(submitOrderEvent: SubmitOrderEvent): List diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/api/TradeManager.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/api/TradeManager.kt new file mode 100644 index 000000000..003e4fca2 --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/api/TradeManager.kt @@ -0,0 +1,8 @@ +package co.nilin.opex.accountant.core.api + +import co.nilin.opex.accountant.core.model.FinancialAction +import co.nilin.opex.matching.core.eventh.events.TradeEvent + +interface TradeManager { + suspend fun handleTrade(trade:TradeEvent): List +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/inout/OrderStatus.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/inout/OrderStatus.kt similarity index 92% rename from Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/inout/OrderStatus.kt rename to Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/inout/OrderStatus.kt index 40b6b9dbb..f7bf2f5fa 100644 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/inout/OrderStatus.kt +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/inout/OrderStatus.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.accountant.core.inout +package co.nilin.opex.accountant.core.inout enum class OrderStatus(val code: Int) { REQUESTED(0), diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/inout/RichOrder.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/inout/RichOrder.kt similarity index 89% rename from Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/inout/RichOrder.kt rename to Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/inout/RichOrder.kt index d0181cc23..dd5a98893 100644 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/inout/RichOrder.kt +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/inout/RichOrder.kt @@ -1,8 +1,8 @@ -package co.nilin.mixchange.accountant.core.inout +package co.nilin.opex.accountant.core.inout -import co.nilin.mixchange.matching.core.model.MatchConstraint -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.OrderType +import co.nilin.opex.matching.core.model.MatchConstraint +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderType import java.math.BigDecimal class RichOrder() { @@ -43,7 +43,6 @@ class RichOrder() { quoteQuantity: BigDecimal, executedQuantity: BigDecimal, accumulativeQuoteQty: BigDecimal, - status: Int ) : this() { this.orderId = orderId diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/inout/RichTrade.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/inout/RichTrade.kt similarity index 96% rename from Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/inout/RichTrade.kt rename to Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/inout/RichTrade.kt index 5c6b6ac5a..964b5b5e1 100644 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/inout/RichTrade.kt +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/inout/RichTrade.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.accountant.core.inout +package co.nilin.opex.accountant.core.inout -import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderDirection import java.math.BigDecimal import java.time.LocalDateTime diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/FinancialAction.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/model/FinancialAction.kt similarity index 58% rename from Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/FinancialAction.kt rename to Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/model/FinancialAction.kt index 94bb96a7d..e6f57e4d7 100644 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/FinancialAction.kt +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/model/FinancialAction.kt @@ -1,9 +1,8 @@ -package co.nilin.mixchange.accountant.core.model +package co.nilin.opex.accountant.core.model import java.math.BigDecimal import java.time.LocalDateTime - class FinancialAction( val id: Long? = null, val parent: FinancialAction?, @@ -17,18 +16,18 @@ class FinancialAction( val receiverWalletType: String, val createDate: LocalDateTime ) { - constructor( parent: FinancialAction?, - eventType: String, - pointer: String, - symbol: String, - amount: BigDecimal, - sender: String, - senderWalletType: String, - receiver: String, - receiverWalletType: String, - createDate: LocalDateTime) : this(null, parent, eventType, pointer, symbol, amount, sender, senderWalletType, receiver, receiverWalletType, createDate) { - - } + constructor( + parent: FinancialAction?, + eventType: String, + pointer: String, + symbol: String, + amount: BigDecimal, + sender: String, + senderWalletType: String, + receiver: String, + receiverWalletType: String, + createDate: LocalDateTime + ) : this(null, parent, eventType, pointer, symbol, amount, sender, senderWalletType, receiver, receiverWalletType, createDate) override fun toString(): String { return "FinancialAction(id=$id, parent=$parent, eventType='$eventType', pointer='$pointer', symbol='$symbol', amount=$amount, sender='$sender', senderWalletType='$senderWalletType', receiver='$receiver', receiverWalletType='$receiverWalletType', createDate=$createDate)" @@ -36,6 +35,7 @@ class FinancialAction( } + enum class FinancialActionStatus { CREATED, PROCESSED, ERROR } \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/model/Order.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/model/Order.kt new file mode 100644 index 000000000..ca311f1a6 --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/model/Order.kt @@ -0,0 +1,31 @@ +package co.nilin.opex.accountant.core.model + +import co.nilin.opex.matching.core.model.MatchConstraint +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderType +import java.math.BigDecimal + +data class Order( + val pair: String, + val ouid: String, + var matchingEngineId: Long?, + val makerFee: Double, + val takerFee: Double, + val leftSideFraction: Double, + val rightSideFraction: Double, + val uuid: String, + val userLevel: String, + val direction: OrderDirection, + val matchConstraint: MatchConstraint, + val orderType: OrderType, + val price: Long, + val quantity: Long, + val filledQuantity: Long, + val origPrice: BigDecimal, + val origQuantity: BigDecimal, + val filledOrigQuantity: BigDecimal, + val firstTransferAmount: BigDecimal, + var remainedTransferAmount: BigDecimal, + var status: Int, + val id: Long? = null +) \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/PairConfig.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/model/PairConfig.kt similarity index 83% rename from Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/PairConfig.kt rename to Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/model/PairConfig.kt index 1c8a0f2fa..6df4d4e82 100644 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/PairConfig.kt +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/model/PairConfig.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.accountant.core.model +package co.nilin.opex.accountant.core.model class PairConfig( val pair: String, diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/PairFeeConfig.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/model/PairFeeConfig.kt similarity index 76% rename from Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/PairFeeConfig.kt rename to Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/model/PairFeeConfig.kt index 29c28508c..ba8e3fbf8 100644 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/model/PairFeeConfig.kt +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/model/PairFeeConfig.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.accountant.core.model +package co.nilin.opex.accountant.core.model class PairFeeConfig( val pairConfig: PairConfig, diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/model/TempEvent.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/model/TempEvent.kt new file mode 100644 index 000000000..144cddf19 --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/model/TempEvent.kt @@ -0,0 +1,6 @@ +package co.nilin.opex.accountant.core.model + +import co.nilin.opex.matching.core.eventh.events.CoreEvent +import java.time.LocalDateTime + +data class TempEvent(val id: Long, val ouid: String, val eventBody: CoreEvent, val eventDate: LocalDateTime) \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/service/FinancialActionJobManagerImpl.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/service/FinancialActionJobManagerImpl.kt new file mode 100644 index 000000000..b2c9faf6c --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/service/FinancialActionJobManagerImpl.kt @@ -0,0 +1,39 @@ +package co.nilin.opex.accountant.core.service + +import co.nilin.opex.accountant.core.api.FinancialActionJobManager +import co.nilin.opex.accountant.core.model.FinancialActionStatus +import co.nilin.opex.accountant.core.spi.FinancialActionLoader +import co.nilin.opex.accountant.core.spi.FinancialActionPersister +import co.nilin.opex.accountant.core.spi.WalletProxy +import org.slf4j.LoggerFactory + +class FinancialActionJobManagerImpl( + val financialActionLoader: FinancialActionLoader, + val financialActionPersister: FinancialActionPersister, + val walletProxy: WalletProxy +) : FinancialActionJobManager { + + private val log = LoggerFactory.getLogger(FinancialActionJobManagerImpl::class.java) + + override suspend fun processFinancialActions(offset: Long, size: Long) { + val factions = financialActionLoader.loadUnprocessed(offset, size) + factions.forEach { faction -> + try { + walletProxy.transfer( + faction.symbol, + faction.senderWalletType, + faction.sender, + faction.receiverWalletType, + faction.receiver, + faction.amount, + faction.eventType + faction.pointer, + null + ) + financialActionPersister.updateStatus(faction, FinancialActionStatus.PROCESSED) + } catch (e: Exception) { + log.error("financial job error", e) + financialActionPersister.updateStatus(faction, FinancialActionStatus.ERROR) + } + } + } +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/service/OrderManagerImpl.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/service/OrderManagerImpl.kt new file mode 100644 index 000000000..c1c4c0944 --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/service/OrderManagerImpl.kt @@ -0,0 +1,220 @@ +package co.nilin.opex.accountant.core.service + +import co.nilin.opex.accountant.core.api.OrderManager +import co.nilin.opex.accountant.core.inout.OrderStatus +import co.nilin.opex.accountant.core.inout.RichOrder +import co.nilin.opex.accountant.core.model.FinancialAction +import co.nilin.opex.accountant.core.model.Order +import co.nilin.opex.accountant.core.spi.* +import co.nilin.opex.matching.core.eventh.events.* +import co.nilin.opex.matching.core.model.OrderDirection +import org.springframework.transaction.annotation.Transactional +import java.math.BigDecimal +import java.time.LocalDateTime + +open class OrderManagerImpl( + val pairConfigLoader: PairConfigLoader, + val financialActionPersister: FinancialActionPersister, + val financeActionLoader: FinancialActionLoader, + val orderPersister: OrderPersister, + val tempEventPersister: TempEventPersister, + val tempEventRepublisher: TempEventRepublisher, + val richOrderPublisher: RichOrderPublisher +) : OrderManager { + + @Transactional + override suspend fun handleRequestOrder(submitOrderEvent: SubmitOrderEvent): List { + //pair + dir -> symbol + //user level? + //pair config.makerFee and takerFee + val symbol = if (submitOrderEvent.direction == OrderDirection.ASK) { + submitOrderEvent.pair.leftSideName + } else { + submitOrderEvent.pair.rightSideName + } + val pairFeeConfig = pairConfigLoader.load(submitOrderEvent.pair.toString(), submitOrderEvent.direction, "") + val makerFee = pairFeeConfig.makerFee * 1 //user level formula + val takerFee = pairFeeConfig.takerFee * 1 //user level formula + + //create fa for transfer uuid symbol main wallet to uuid symbol exchange wallet + /* + amount for sell (ask): quantity + amount for buy (bid): quantity * price + */ + val financialAction = + FinancialAction( + null, + SubmitOrderEvent::class.simpleName!!, + submitOrderEvent.ouid, + symbol, + if (submitOrderEvent.direction == OrderDirection.ASK) { + BigDecimal(submitOrderEvent.quantity).multiply(pairFeeConfig.pairConfig.leftSideFraction.toBigDecimal()) + } else { + BigDecimal(submitOrderEvent.quantity).multiply(pairFeeConfig.pairConfig.leftSideFraction.toBigDecimal()) + .multiply(submitOrderEvent.price.toBigDecimal()) + .multiply(pairFeeConfig.pairConfig.rightSideFraction.toBigDecimal()) + }, + submitOrderEvent.uuid, + "main", + submitOrderEvent.uuid, + "exchange", + LocalDateTime.now() + ) + //store order (ouid, uuid, fees, userlevel, pair, direction, price, quantity, filledQ, status, transfered) + orderPersister.save( + Order( + submitOrderEvent.pair.toString(), + submitOrderEvent.ouid, + null, + makerFee, + takerFee, + pairFeeConfig.pairConfig.leftSideFraction, + pairFeeConfig.pairConfig.rightSideFraction, + submitOrderEvent.uuid, + "", + submitOrderEvent.direction, + submitOrderEvent.matchConstraint, + submitOrderEvent.orderType, + submitOrderEvent.price, + submitOrderEvent.quantity, + submitOrderEvent.quantity - submitOrderEvent.remainedQuantity, + submitOrderEvent.price.toBigDecimal() + .multiply(pairFeeConfig.pairConfig.rightSideFraction.toBigDecimal()), + submitOrderEvent.quantity.toBigDecimal() + .multiply(pairFeeConfig.pairConfig.leftSideFraction.toBigDecimal()), + BigDecimal(submitOrderEvent.quantity - submitOrderEvent.remainedQuantity).multiply(pairFeeConfig.pairConfig.leftSideFraction.toBigDecimal()), + financialAction.amount, + financialAction.amount, + OrderStatus.REQUESTED.code + ) + ) + return financialActionPersister.persist(listOf(financialAction)) + } + + @Transactional + override suspend fun handleNewOrder(createOrderEvent: CreateOrderEvent): List { + //update order add id to other fields + val order = orderPersister.load(createOrderEvent.ouid) + if (order != null) { + order.matchingEngineId = createOrderEvent.orderId + orderPersister.save(order) + //new order accepted by engine + publishRichOrder(order, createOrderEvent.remainedQuantity.toBigDecimal()) + } else { + tempEventPersister.saveTempEvent(createOrderEvent.ouid, createOrderEvent) + } + return emptyList() + } + + private suspend fun publishRichOrder( + order: Order, remainedQuantity: BigDecimal, status: OrderStatus? = null + ) { + richOrderPublisher.publish( + RichOrder( + order.id, + order.pair, + order.ouid, + order.uuid, + order.userLevel, + order.makerFee.toBigDecimal(), + order.takerFee.toBigDecimal(), + order.leftSideFraction.toBigDecimal(), + order.rightSideFraction.toBigDecimal(), + order.direction, + order.matchConstraint, + order.orderType, + order.origPrice, + order.origQuantity, + order.origPrice.multiply(order.origQuantity), + order.quantity.toBigDecimal().subtract(remainedQuantity) + .multiply(order.leftSideFraction.toBigDecimal()), + order.origPrice.multiply( + order.quantity.toBigDecimal().subtract(remainedQuantity) + ), + (status ?: if (remainedQuantity.compareTo(BigDecimal.ZERO) == 0) { + OrderStatus.FILLED.code + } else if (remainedQuantity.compareTo(order.quantity.toBigDecimal()) == 0) { + OrderStatus.NEW.code + } else { + OrderStatus.PARTIALLY_FILLED.code + }) as Int + ) + ) + } + + override suspend fun handleUpdateOrder(updatedOrderEvent: UpdatedOrderEvent): List { + TODO("Not yet implemented") + } + + @Transactional + override suspend fun handleRejectOrder(rejectOrderEvent: RejectOrderEvent): List { + //order by ouid + val order = orderPersister.load(rejectOrderEvent.ouid) + if (order == null) { + tempEventPersister.saveTempEvent(rejectOrderEvent.ouid, rejectOrderEvent) + return emptyList() + } + val symbol = if (rejectOrderEvent.direction == OrderDirection.ASK) { + rejectOrderEvent.pair.leftSideName + } else { + rejectOrderEvent.pair.rightSideName + } + //check uuid + //lookup for parent fa + val parentFinancialAction = financeActionLoader.findLast(rejectOrderEvent.uuid, rejectOrderEvent.ouid) + //create fa for transfer remaining transfered uuid symbol exchange wallet to uuid main exchange wallet + val financialAction = FinancialAction( + parentFinancialAction, + RejectOrderEvent::class.simpleName!!, + rejectOrderEvent.ouid, + symbol, + order.remainedTransferAmount, + rejectOrderEvent.uuid, + "exchange", + rejectOrderEvent.uuid, + "main", + LocalDateTime.now() + ) + //update order status + order.status = OrderStatus.REJECTED.code + orderPersister.save(order) + publishRichOrder(order, order.quantity.toBigDecimal(), OrderStatus.REJECTED) + return financialActionPersister.persist(listOf(financialAction)) + } + + @Transactional + override suspend fun handleCancelOrder(cancelOrderEvent: CancelOrderEvent): List { + //order by ouid + val order = orderPersister.load(cancelOrderEvent.ouid) + if (order == null) { + tempEventPersister.saveTempEvent(cancelOrderEvent.ouid, cancelOrderEvent) + return emptyList() + } + val symbol = if (cancelOrderEvent.direction == OrderDirection.ASK) { + cancelOrderEvent.pair.leftSideName + } else { + cancelOrderEvent.pair.rightSideName + } + //check uuid + //lookup for parent fa + val parentFinancialAction = financeActionLoader.findLast(cancelOrderEvent.uuid, cancelOrderEvent.ouid) + //create fa for transfer remaining transfered uuid symbol exchange wallet to uuid main exchange wallet + val financialAction = FinancialAction( + parentFinancialAction, + RejectOrderEvent::class.simpleName!!, + cancelOrderEvent.ouid, + symbol, + order.remainedTransferAmount, + cancelOrderEvent.uuid, + "exchange", + cancelOrderEvent.uuid, + "main", + LocalDateTime.now() + ) + //update order status + order.status = OrderStatus.CANCELED.code + orderPersister.save(order) + publishRichOrder(order, cancelOrderEvent.quantity.toBigDecimal(), OrderStatus.CANCELED) + return financialActionPersister.persist(listOf(financialAction)) + } +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/TradeManagerImpl.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/service/TradeManagerImpl.kt similarity index 55% rename from Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/TradeManagerImpl.kt rename to Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/service/TradeManagerImpl.kt index 7cdd2054b..bf24dfa7d 100644 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/service/TradeManagerImpl.kt +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/service/TradeManagerImpl.kt @@ -1,26 +1,26 @@ -package co.nilin.mixchange.accountant.core.service +package co.nilin.opex.accountant.core.service -import co.nilin.mixchange.accountant.core.api.TradeManager -import co.nilin.mixchange.accountant.core.inout.RichTrade -import co.nilin.mixchange.accountant.core.model.FinancialAction -import co.nilin.mixchange.accountant.core.spi.* -import co.nilin.mixchange.matching.core.eventh.events.TradeEvent -import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.opex.accountant.core.api.TradeManager +import co.nilin.opex.accountant.core.inout.RichTrade +import co.nilin.opex.accountant.core.model.FinancialAction +import co.nilin.opex.accountant.core.spi.* +import co.nilin.opex.matching.core.eventh.events.TradeEvent +import co.nilin.opex.matching.core.model.OrderDirection import org.slf4j.LoggerFactory import org.springframework.transaction.annotation.Transactional import java.math.BigDecimal import java.time.LocalDateTime open class TradeManagerImpl( - val pairStaticRateLoader: PairStaticRateLoader, - val financeActionPersister: FinancialActionPersister, - val financeActionLoader: FinancialActionLoader, - val orderPersister: OrderPersister, - val tempEventPersister: TempEventPersister, - val richTradePublisher: RichTradePublisher, - val walletProxy: WalletProxy, - val platformCoin: String, - val platformAddress: String + val pairStaticRateLoader: PairStaticRateLoader, + val financeActionPersister: FinancialActionPersister, + val financeActionLoader: FinancialActionLoader, + val orderPersister: OrderPersister, + val tempEventPersister: TempEventPersister, + val richTradePublisher: RichTradePublisher, + val walletProxy: WalletProxy, + val platformCoin: String, + val platformAddress: String ) : TradeManager { private val log = LoggerFactory.getLogger(TradeManagerImpl::class.java) @@ -53,7 +53,7 @@ open class TradeManagerImpl( trade.matchedQuantity.toBigDecimal().multiply(takerOrder.leftSideFraction.toBigDecimal()) } else { trade.matchedQuantity.toBigDecimal().multiply(takerOrder.leftSideFraction.toBigDecimal()) - .multiply(trade.makerPrice.toBigDecimal()).multiply(takerOrder.rightSideFraction.toBigDecimal()) + .multiply(trade.makerPrice.toBigDecimal()).multiply(takerOrder.rightSideFraction.toBigDecimal()) } val takerPCFeeCoefficient: Double = if (takerOrder.direction == OrderDirection.ASK) { @@ -66,7 +66,7 @@ open class TradeManagerImpl( trade.matchedQuantity.toBigDecimal().multiply(makerOrder.leftSideFraction.toBigDecimal()) } else { trade.matchedQuantity.toBigDecimal().multiply(makerOrder.leftSideFraction.toBigDecimal()) - .multiply(trade.takerPrice.toBigDecimal()).multiply(makerOrder.rightSideFraction.toBigDecimal()) + .multiply(trade.takerPrice.toBigDecimal()).multiply(makerOrder.rightSideFraction.toBigDecimal()) } val makerPCFeeCoefficient: Double = if (makerOrder.direction == OrderDirection.ASK) { @@ -86,8 +86,8 @@ open class TradeManagerImpl( //calculate maker fee val makerFee = makerOrder.makerFee val makerTotalFeeWithPlatformCoin = takerMatchedAmount - .multiply(makerFee.toBigDecimal()) - .multiply(makerPCFeeCoefficient.toBigDecimal()) + .multiply(makerFee.toBigDecimal()) + .multiply(makerPCFeeCoefficient.toBigDecimal()) //check if maker uuid can pay the fee with platform coin //create fa for transfer taker uuid symbol exchange wallet to maker symbol main wallet /* @@ -95,7 +95,41 @@ open class TradeManagerImpl( amount for buy (bid): match_quantity * maker price (if not pay by platform coin then - maker fee) */ val takerTransferAction = FinancialAction( - takerParentFinancialAction, + takerParentFinancialAction, + TradeEvent::class.simpleName!!, + trade.takerOuid, + if (takerOrder.direction == OrderDirection.ASK) { + trade.pair.leftSideName + } else { + trade.pair.rightSideName + }, + takerMatchedAmount, + trade.takerUuid, + "exchange", + trade.makerUuid, + "main", + LocalDateTime.now() + ) + log.info("trade event takerTransferAction {}", takerTransferAction) + financialActions.add(takerTransferAction) + val makerFeeAction = if (makerTotalFeeWithPlatformCoin > BigDecimal.ZERO && + walletProxy.canFulfil(platformCoin, "main", trade.makerUuid, makerTotalFeeWithPlatformCoin) + ) { + FinancialAction( + makerParentFinancialAction, + TradeEvent::class.simpleName!!, + trade.takerOuid, + platformCoin, + makerTotalFeeWithPlatformCoin, + trade.makerUuid, + "main", + platformAddress, + "exchange", + LocalDateTime.now() + ) + } else { + FinancialAction( + makerParentFinancialAction, TradeEvent::class.simpleName!!, trade.takerOuid, if (takerOrder.direction == OrderDirection.ASK) { @@ -103,46 +137,12 @@ open class TradeManagerImpl( } else { trade.pair.rightSideName }, - takerMatchedAmount, - trade.takerUuid, - "exchange", + takerMatchedAmount.multiply(makerFee.toBigDecimal()), trade.makerUuid, "main", + platformAddress, + "exchange", LocalDateTime.now() - ) - log.info("trade event takerTransferAction {}", takerTransferAction) - financialActions.add(takerTransferAction) - val makerFeeAction = if (makerTotalFeeWithPlatformCoin > BigDecimal.ZERO && - walletProxy.canFulfil(platformCoin, "main", trade.makerUuid, makerTotalFeeWithPlatformCoin) - ) { - FinancialAction( - makerParentFinancialAction, - TradeEvent::class.simpleName!!, - trade.takerOuid, - platformCoin, - makerTotalFeeWithPlatformCoin, - trade.makerUuid, - "main", - platformAddress, - "exchange", - LocalDateTime.now() - ) - } else { - FinancialAction( - makerParentFinancialAction, - TradeEvent::class.simpleName!!, - trade.takerOuid, - if (takerOrder.direction == OrderDirection.ASK) { - trade.pair.leftSideName - } else { - trade.pair.rightSideName - }, - takerMatchedAmount.multiply(makerFee.toBigDecimal()), - trade.makerUuid, - "main", - platformAddress, - "exchange", - LocalDateTime.now() ) } log.info("trade event makerFeeAction {}", makerFeeAction) @@ -158,8 +158,8 @@ open class TradeManagerImpl( //calculate taker fee val takerFee = takerOrder.takerFee val takerTotalFeeWithPlatformCoin = takerMatchedAmount - .multiply(takerFee.toBigDecimal()) - .multiply(takerPCFeeCoefficient.toBigDecimal()) + .multiply(takerFee.toBigDecimal()) + .multiply(takerPCFeeCoefficient.toBigDecimal()) //create fa for transfer makeruuid symbol exchange wallet to taker symbol main wallet /* @@ -167,7 +167,29 @@ open class TradeManagerImpl( amount for buy (bid): match_quantity * maker price (if not pay by platform coin then - taker fee) */ val makerTransferAction = FinancialAction( - makerParentFinancialAction, + makerParentFinancialAction, + TradeEvent::class.simpleName!!, + trade.makerOuid, + if (makerOrder.direction == OrderDirection.ASK) { + trade.pair.leftSideName + } else { + trade.pair.rightSideName + }, + makerMatchedAmount, + trade.makerUuid, + "exchange", + trade.takerUuid, + "main", + LocalDateTime.now() + ) + log.info("trade event makerTransferAction {}", makerTransferAction) + financialActions.add(makerTransferAction) + //check if taker uuid can pay the fee with platform coin + val takerFeeAction = if (takerTotalFeeWithPlatformCoin > BigDecimal.ZERO && + walletProxy.canFulfil(platformCoin, "main", trade.takerUuid, takerTotalFeeWithPlatformCoin) + ) { + FinancialAction( + takerParentFinancialAction, TradeEvent::class.simpleName!!, trade.makerOuid, if (makerOrder.direction == OrderDirection.ASK) { @@ -175,51 +197,29 @@ open class TradeManagerImpl( } else { trade.pair.rightSideName }, - makerMatchedAmount, - trade.makerUuid, - "exchange", + takerTotalFeeWithPlatformCoin, trade.takerUuid, "main", + platformAddress, + "", LocalDateTime.now() - ) - log.info("trade event makerTransferAction {}", makerTransferAction) - financialActions.add(makerTransferAction) - //check if taker uuid can pay the fee with platform coin - val takerFeeAction = if (takerTotalFeeWithPlatformCoin > BigDecimal.ZERO && - walletProxy.canFulfil(platformCoin, "main", trade.takerUuid, takerTotalFeeWithPlatformCoin) - ) { - FinancialAction( - takerParentFinancialAction, - TradeEvent::class.simpleName!!, - trade.makerOuid, - if (makerOrder.direction == OrderDirection.ASK) { - trade.pair.leftSideName - } else { - trade.pair.rightSideName - }, - takerTotalFeeWithPlatformCoin, - trade.takerUuid, - "main", - platformAddress, - "", - LocalDateTime.now() ) } else { FinancialAction( - takerParentFinancialAction, - TradeEvent::class.simpleName!!, - trade.makerOuid, - if (makerOrder.direction == OrderDirection.ASK) { - trade.pair.leftSideName - } else { - trade.pair.rightSideName - }, - makerMatchedAmount.multiply(takerFee.toBigDecimal()), - trade.takerUuid, - "main", - platformAddress, - "exchange", - LocalDateTime.now() + takerParentFinancialAction, + TradeEvent::class.simpleName!!, + trade.makerOuid, + if (makerOrder.direction == OrderDirection.ASK) { + trade.pair.leftSideName + } else { + trade.pair.rightSideName + }, + makerMatchedAmount.multiply(takerFee.toBigDecimal()), + trade.takerUuid, + "main", + platformAddress, + "exchange", + LocalDateTime.now() ) } @@ -233,32 +233,32 @@ open class TradeManagerImpl( orderPersister.save(makerOrder) log.info("maker order saved {}", makerOrder) richTradePublisher.publish( - RichTrade( - trade.tradeId, - trade.pair.toString(), - trade.takerOuid, - trade.takerUuid, - trade.takerOrderId, - trade.takerDirection, - trade.takerPrice.toBigDecimal().multiply(takerOrder.rightSideFraction.toBigDecimal()), - takerOrder.origQuantity, - takerOrder.origPrice.multiply(takerOrder.origQuantity), - trade.takerRemainedQuantity.toBigDecimal().multiply(takerOrder.leftSideFraction.toBigDecimal()), - takerFeeAction.amount, - takerFeeAction.symbol, - trade.makerOuid, - trade.makerUuid, - trade.makerOrderId, - trade.makerDirection, - trade.makerPrice.toBigDecimal().multiply(makerOrder.rightSideFraction.toBigDecimal()), - makerOrder.origQuantity, - makerOrder.origPrice.multiply(makerOrder.origQuantity), - trade.makerRemainedQuantity.toBigDecimal().multiply(makerOrder.leftSideFraction.toBigDecimal()), - makerFeeAction.amount, - makerFeeAction.symbol, - trade.matchedQuantity.toBigDecimal().multiply(makerOrder.leftSideFraction.toBigDecimal()), - trade.eventDate - ) + co.nilin.opex.accountant.core.inout.RichTrade( + trade.tradeId, + trade.pair.toString(), + trade.takerOuid, + trade.takerUuid, + trade.takerOrderId, + trade.takerDirection, + trade.takerPrice.toBigDecimal().multiply(takerOrder.rightSideFraction.toBigDecimal()), + takerOrder.origQuantity, + takerOrder.origPrice.multiply(takerOrder.origQuantity), + trade.takerRemainedQuantity.toBigDecimal().multiply(takerOrder.leftSideFraction.toBigDecimal()), + takerFeeAction.amount, + takerFeeAction.symbol, + trade.makerOuid, + trade.makerUuid, + trade.makerOrderId, + trade.makerDirection, + trade.makerPrice.toBigDecimal().multiply(makerOrder.rightSideFraction.toBigDecimal()), + makerOrder.origQuantity, + makerOrder.origPrice.multiply(makerOrder.origQuantity), + trade.makerRemainedQuantity.toBigDecimal().multiply(makerOrder.leftSideFraction.toBigDecimal()), + makerFeeAction.amount, + makerFeeAction.symbol, + trade.matchedQuantity.toBigDecimal().multiply(makerOrder.leftSideFraction.toBigDecimal()), + trade.eventDate + ) ) return financeActionPersister.persist(financialActions) diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/FinancialActionLoader.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/FinancialActionLoader.kt similarity index 73% rename from Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/FinancialActionLoader.kt rename to Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/FinancialActionLoader.kt index e91d43180..98721879d 100644 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/FinancialActionLoader.kt +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/FinancialActionLoader.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.accountant.core.spi +package co.nilin.opex.accountant.core.spi -import co.nilin.mixchange.accountant.core.model.FinancialAction +import co.nilin.opex.accountant.core.model.FinancialAction import kotlinx.coroutines.flow.Flow interface FinancialActionLoader { diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/FinancialActionPersister.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/FinancialActionPersister.kt new file mode 100644 index 000000000..f51e4eef9 --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/FinancialActionPersister.kt @@ -0,0 +1,9 @@ +package co.nilin.opex.accountant.core.spi + +import co.nilin.opex.accountant.core.model.FinancialAction +import co.nilin.opex.accountant.core.model.FinancialActionStatus + +interface FinancialActionPersister { + suspend fun persist(financialActions: List): List + suspend fun updateStatus(financialAction: FinancialAction, status: FinancialActionStatus) +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/OrderPersister.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/OrderPersister.kt similarity index 53% rename from Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/OrderPersister.kt rename to Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/OrderPersister.kt index 6acf4e34c..a3ae68637 100644 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/OrderPersister.kt +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/OrderPersister.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.accountant.core.spi +package co.nilin.opex.accountant.core.spi -import co.nilin.mixchange.accountant.core.model.Order +import co.nilin.opex.accountant.core.model.Order interface OrderPersister { suspend fun load(ouid: String): Order? diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/PairConfigLoader.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/PairConfigLoader.kt new file mode 100644 index 000000000..d2fade236 --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/PairConfigLoader.kt @@ -0,0 +1,8 @@ +package co.nilin.opex.accountant.core.spi + +import co.nilin.opex.accountant.core.model.PairFeeConfig +import co.nilin.opex.matching.core.model.OrderDirection + +interface PairConfigLoader { + suspend fun load(pair: String, direction: OrderDirection, userLevel: String): PairFeeConfig +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/PairStaticRateLoader.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/PairStaticRateLoader.kt similarity index 71% rename from Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/PairStaticRateLoader.kt rename to Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/PairStaticRateLoader.kt index 87c9b11de..3708a0e18 100644 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/PairStaticRateLoader.kt +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/PairStaticRateLoader.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.accountant.core.spi +package co.nilin.opex.accountant.core.spi interface PairStaticRateLoader { suspend fun calculateStaticRate(leftSide:String, rightSide: String): Double? diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/RichOrderPublisher.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/RichOrderPublisher.kt new file mode 100644 index 000000000..ae6866675 --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/RichOrderPublisher.kt @@ -0,0 +1,7 @@ +package co.nilin.opex.accountant.core.spi + +import co.nilin.opex.accountant.core.inout.RichOrder + +interface RichOrderPublisher { + suspend fun publish(order: RichOrder) +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/RichTradePublisher.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/RichTradePublisher.kt new file mode 100644 index 000000000..da9f860d3 --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/RichTradePublisher.kt @@ -0,0 +1,7 @@ +package co.nilin.opex.accountant.core.spi + +import co.nilin.opex.accountant.core.inout.RichTrade + +interface RichTradePublisher { + suspend fun publish(trade: RichTrade) +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/TempEventPersister.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/TempEventPersister.kt similarity index 66% rename from Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/TempEventPersister.kt rename to Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/TempEventPersister.kt index 569c1f9ec..a949f1c95 100644 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/TempEventPersister.kt +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/TempEventPersister.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.accountant.core.spi +package co.nilin.opex.accountant.core.spi -import co.nilin.mixchange.accountant.core.model.TempEvent -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +import co.nilin.opex.accountant.core.model.TempEvent +import co.nilin.opex.matching.core.eventh.events.CoreEvent interface TempEventPersister { suspend fun saveTempEvent(ouid: String, event: CoreEvent) diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/TempEventRepublisher.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/TempEventRepublisher.kt new file mode 100644 index 000000000..f86f23901 --- /dev/null +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/TempEventRepublisher.kt @@ -0,0 +1,7 @@ +package co.nilin.opex.accountant.core.spi + +import co.nilin.opex.matching.core.eventh.events.CoreEvent + +interface TempEventRepublisher { + suspend fun republish(events: List) +} \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/WalletProxy.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/WalletProxy.kt similarity index 90% rename from Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/WalletProxy.kt rename to Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/WalletProxy.kt index fef9a0ee8..ed27996ca 100644 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/mixchange/accountant/core/spi/WalletProxy.kt +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/WalletProxy.kt @@ -1,8 +1,9 @@ -package co.nilin.mixchange.accountant.core.spi +package co.nilin.opex.accountant.core.spi import java.math.BigDecimal interface WalletProxy { + suspend fun transfer( symbol: String, senderWalletType: String, diff --git a/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/MockitoHelper.kt b/Accountant/accountant-core/src/test/kotlin/co/nilin/opex/accountant/core/service/MockitoHelper.kt similarity index 81% rename from Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/MockitoHelper.kt rename to Accountant/accountant-core/src/test/kotlin/co/nilin/opex/accountant/core/service/MockitoHelper.kt index c2f0ca98c..d05650e71 100644 --- a/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/MockitoHelper.kt +++ b/Accountant/accountant-core/src/test/kotlin/co/nilin/opex/accountant/core/service/MockitoHelper.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.accountant.core.service +package co.nilin.opex.accountant.core.service import org.mockito.Mockito diff --git a/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImplTest.kt b/Accountant/accountant-core/src/test/kotlin/co/nilin/opex/accountant/core/service/OrderManagerImplTest.kt similarity index 68% rename from Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImplTest.kt rename to Accountant/accountant-core/src/test/kotlin/co/nilin/opex/accountant/core/service/OrderManagerImplTest.kt index 34faa9e47..15f54a86c 100644 --- a/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/OrderManagerImplTest.kt +++ b/Accountant/accountant-core/src/test/kotlin/co/nilin/opex/accountant/core/service/OrderManagerImplTest.kt @@ -1,15 +1,15 @@ -package co.nilin.mixchange.accountant.core.service - -import co.nilin.mixchange.accountant.core.api.OrderManager -import co.nilin.mixchange.accountant.core.model.FinancialAction -import co.nilin.mixchange.accountant.core.model.PairConfig -import co.nilin.mixchange.accountant.core.model.PairFeeConfig -import co.nilin.mixchange.accountant.core.spi.* -import co.nilin.mixchange.matching.core.eventh.events.SubmitOrderEvent -import co.nilin.mixchange.matching.core.model.MatchConstraint -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.OrderType -import co.nilin.mixchange.matching.core.model.Pair +package co.nilin.opex.accountant.core.service + +import co.nilin.opex.accountant.core.api.OrderManager +import co.nilin.opex.accountant.core.model.FinancialAction +import co.nilin.opex.accountant.core.model.PairConfig +import co.nilin.opex.accountant.core.model.PairFeeConfig +import co.nilin.opex.accountant.core.spi.* +import co.nilin.opex.matching.core.eventh.events.SubmitOrderEvent +import co.nilin.opex.matching.core.model.MatchConstraint +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderType +import co.nilin.opex.matching.core.model.Pair import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test @@ -60,13 +60,21 @@ internal class OrderManagerImplTest() { //given val pair = Pair("eth", "btc") val pairConfig = PairConfig( - pair.toString(), pair.leftSideName, pair.rightSideName, 1.0, 0.001 + pair.toString(), pair.leftSideName, pair.rightSideName, 1.0, 0.001 ) val submitOrderEvent = SubmitOrderEvent( "ouid", "uuid", null, pair, 30, 60, 0, OrderDirection.ASK, MatchConstraint.GTC, OrderType.LIMIT_ORDER ) Mockito.`when`(pairConfigLoader.load(pair.toString(), submitOrderEvent.direction, "")) - .thenReturn(PairFeeConfig(pairConfig, submitOrderEvent.direction.toString(), "", 0.1, 0.12)) + .thenReturn( + PairFeeConfig( + pairConfig, + submitOrderEvent.direction.toString(), + "", + 0.1, + 0.12 + ) + ) Mockito.`when`(financialActionPersister.persist(MockitoHelper.anyObject())) .then { return@then it.getArgument>(0) @@ -78,16 +86,16 @@ internal class OrderManagerImplTest() { //then assertEquals(1, financialActions.size) val expectedFinancialAction = FinancialAction( - null, - SubmitOrderEvent::class.simpleName!!, - submitOrderEvent.ouid, - pair.leftSideName, - pairConfig.leftSideFraction.toBigDecimal().multiply(submitOrderEvent.quantity.toBigDecimal()), - submitOrderEvent.uuid, - "main", - submitOrderEvent.uuid, - "exchange", - LocalDateTime.now() + null, + SubmitOrderEvent::class.simpleName!!, + submitOrderEvent.ouid, + pair.leftSideName, + pairConfig.leftSideFraction.toBigDecimal().multiply(submitOrderEvent.quantity.toBigDecimal()), + submitOrderEvent.uuid, + "main", + submitOrderEvent.uuid, + "exchange", + LocalDateTime.now() ) assertEquals(expectedFinancialAction.eventType, financialActions[0].eventType) assertEquals(expectedFinancialAction.symbol, financialActions[0].symbol) @@ -105,16 +113,16 @@ internal class OrderManagerImplTest() { //given val pair = Pair("eth", "btc") val pairConfig = PairConfig( - pair.toString(), pair.leftSideName, pair.rightSideName, 1.0, 0.001 + pair.toString(), pair.leftSideName, pair.rightSideName, 1.0, 0.001 ) val submitOrderEvent = SubmitOrderEvent( "ouid", "uuid", null, pair, 35, 14, 0, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER ) Mockito.`when`(pairConfigLoader.load(pair.toString(), submitOrderEvent.direction, "")) .thenReturn( - PairFeeConfig( - pairConfig, submitOrderEvent.direction.toString(), "", 0.08, 0.1 - ) + PairFeeConfig( + pairConfig, submitOrderEvent.direction.toString(), "", 0.08, 0.1 + ) ) Mockito.`when`(financialActionPersister.persist(MockitoHelper.anyObject())) .then { @@ -129,18 +137,18 @@ internal class OrderManagerImplTest() { //then assertEquals(1, financialActions.size) val expectedFinancialAction = FinancialAction( - null, - SubmitOrderEvent::class.simpleName!!, - submitOrderEvent.ouid, - pair.rightSideName, - pairConfig.leftSideFraction.toBigDecimal().multiply(submitOrderEvent.quantity.toBigDecimal()) - .multiply(pairConfig.rightSideFraction.toBigDecimal()) - .multiply(submitOrderEvent.price.toBigDecimal()), - submitOrderEvent.uuid, - "main", - submitOrderEvent.uuid, - "exchange", - LocalDateTime.now() + null, + SubmitOrderEvent::class.simpleName!!, + submitOrderEvent.ouid, + pair.rightSideName, + pairConfig.leftSideFraction.toBigDecimal().multiply(submitOrderEvent.quantity.toBigDecimal()) + .multiply(pairConfig.rightSideFraction.toBigDecimal()) + .multiply(submitOrderEvent.price.toBigDecimal()), + submitOrderEvent.uuid, + "main", + submitOrderEvent.uuid, + "exchange", + LocalDateTime.now() ) assertEquals(expectedFinancialAction.eventType, financialActions[0].eventType) assertEquals(expectedFinancialAction.symbol, financialActions[0].symbol) diff --git a/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/TradeManagerImplTest.kt b/Accountant/accountant-core/src/test/kotlin/co/nilin/opex/accountant/core/service/TradeManagerImplTest.kt similarity index 72% rename from Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/TradeManagerImplTest.kt rename to Accountant/accountant-core/src/test/kotlin/co/nilin/opex/accountant/core/service/TradeManagerImplTest.kt index 4155492a9..2982d59aa 100644 --- a/Accountant/accountant-core/src/test/kotlin/co/nilin/mixchange/accountant/core/service/TradeManagerImplTest.kt +++ b/Accountant/accountant-core/src/test/kotlin/co/nilin/opex/accountant/core/service/TradeManagerImplTest.kt @@ -1,18 +1,18 @@ -package co.nilin.mixchange.accountant.core.service - -import co.nilin.mixchange.accountant.core.api.OrderManager -import co.nilin.mixchange.accountant.core.api.TradeManager -import co.nilin.mixchange.accountant.core.model.FinancialAction -import co.nilin.mixchange.accountant.core.model.Order -import co.nilin.mixchange.accountant.core.model.PairConfig -import co.nilin.mixchange.accountant.core.model.PairFeeConfig -import co.nilin.mixchange.accountant.core.spi.* -import co.nilin.mixchange.matching.core.eventh.events.SubmitOrderEvent -import co.nilin.mixchange.matching.core.eventh.events.TradeEvent -import co.nilin.mixchange.matching.core.model.MatchConstraint -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.OrderType -import co.nilin.mixchange.matching.core.model.Pair +package co.nilin.opex.accountant.core.service + +import co.nilin.opex.accountant.core.api.OrderManager +import co.nilin.opex.accountant.core.api.TradeManager +import co.nilin.opex.accountant.core.model.FinancialAction +import co.nilin.opex.accountant.core.model.Order +import co.nilin.opex.accountant.core.model.PairConfig +import co.nilin.opex.accountant.core.model.PairFeeConfig +import co.nilin.opex.accountant.core.spi.* +import co.nilin.opex.matching.core.eventh.events.SubmitOrderEvent +import co.nilin.opex.matching.core.eventh.events.TradeEvent +import co.nilin.opex.matching.core.model.MatchConstraint +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderType +import co.nilin.opex.matching.core.model.Pair import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test @@ -90,7 +90,7 @@ internal class TradeManagerImplTest() { runBlocking { //given val pair = Pair("eth", "btc") - val pairConfig = PairConfig( + val pairConfig = co.nilin.opex.accountant.core.model.PairConfig( pair.toString(), pair.leftSideName, pair.rightSideName, 1.0, 0.001 ) val makerSubmitOrderEvent = SubmitOrderEvent( @@ -116,7 +116,7 @@ internal class TradeManagerImplTest() { runBlocking { //given val pair = Pair("eth", "btc") - val pairConfig = PairConfig( + val pairConfig = co.nilin.opex.accountant.core.model.PairConfig( pair.toString(), pair.leftSideName, pair.rightSideName, 0.000001, 0.000001 ) val makerSubmitOrderEvent = SubmitOrderEvent( @@ -182,14 +182,22 @@ internal class TradeManagerImplTest() { private fun prepareOrder( pair: Pair, - pairConfig: PairConfig, + pairConfig: co.nilin.opex.accountant.core.model.PairConfig, submitOrderEvent: SubmitOrderEvent, makerFee: Double, takerFee: Double ) { runBlocking { Mockito.`when`(pairConfigLoader.load(pair.toString(), submitOrderEvent.direction, "")) - .thenReturn(PairFeeConfig(pairConfig, submitOrderEvent.direction.toString(), "", makerFee, takerFee)) + .thenReturn( + co.nilin.opex.accountant.core.model.PairFeeConfig( + pairConfig, + submitOrderEvent.direction.toString(), + "", + makerFee, + takerFee + ) + ) Mockito.`when`(financialActionPersister.persist(MockitoHelper.anyObject())) .then { return@then it.getArgument>(0) @@ -203,27 +211,27 @@ internal class TradeManagerImplTest() { val orderTakerFee = orderPairFeeConfig.takerFee * 1 //user level formula Mockito.`when`(orderPersister.load(submitOrderEvent.ouid)).thenReturn( Order( - submitOrderEvent.pair.toString(), - submitOrderEvent.ouid, - null, - orderMakerFee, - orderTakerFee, - orderPairFeeConfig.pairConfig.leftSideFraction, - orderPairFeeConfig.pairConfig.rightSideFraction, - submitOrderEvent.uuid, - "", - submitOrderEvent.direction, - submitOrderEvent.matchConstraint, - submitOrderEvent.orderType, - submitOrderEvent.price, - submitOrderEvent.quantity, - submitOrderEvent.quantity - submitOrderEvent.remainedQuantity, - submitOrderEvent.price.toBigDecimal(), - submitOrderEvent.quantity.toBigDecimal(), - (submitOrderEvent.quantity - submitOrderEvent.remainedQuantity).toBigDecimal(), - financialActions[0].amount, - financialActions[0].amount, - 0 + submitOrderEvent.pair.toString(), + submitOrderEvent.ouid, + null, + orderMakerFee, + orderTakerFee, + orderPairFeeConfig.pairConfig.leftSideFraction, + orderPairFeeConfig.pairConfig.rightSideFraction, + submitOrderEvent.uuid, + "", + submitOrderEvent.direction, + submitOrderEvent.matchConstraint, + submitOrderEvent.orderType, + submitOrderEvent.price, + submitOrderEvent.quantity, + submitOrderEvent.quantity - submitOrderEvent.remainedQuantity, + submitOrderEvent.price.toBigDecimal(), + submitOrderEvent.quantity.toBigDecimal(), + (submitOrderEvent.quantity - submitOrderEvent.remainedQuantity).toBigDecimal(), + financialActions[0].amount, + financialActions[0].amount, + 0 ) ) } diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/pom.xml b/Accountant/accountant-ports/accountant-eventlistener-kafka/pom.xml index 2b423b915..09689d7ff 100644 --- a/Accountant/accountant-ports/accountant-eventlistener-kafka/pom.xml +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex accountant-eventlistener-kafka 0.0.1-SNAPSHOT accountant-eventlistener-kafka - Accountant kafka listener of Mixchange + Accountant kafka listener of Opex 1.8 @@ -31,13 +31,13 @@ spring-boot-starter-webflux - co.nilin.mixchange + co.nilin.opex matching-core ${matching.version} provided - co.nilin.mixchange + co.nilin.opex accountant-core ${accountant.version} provided diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt deleted file mode 100644 index 1613e63df..000000000 --- a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt +++ /dev/null @@ -1,39 +0,0 @@ -package co.nilin.mixchange.port.order.kafka.inout - -import co.nilin.mixchange.matching.core.model.MatchConstraint -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.OrderType - -class OrderSubmitRequest() { - lateinit var ouid: String - lateinit var uuid: String - var orderId: Long? = null - lateinit var pair: co.nilin.mixchange.matching.core.model.Pair - var price: Long = 0 - var quantity: Long = 0 - var direction: OrderDirection = OrderDirection.BID - var matchConstraint: MatchConstraint = MatchConstraint.GTC - var orderType: OrderType = OrderType.LIMIT_ORDER - - constructor(ouid: String, - uuid: String, - orderId: Long?, - pair: co.nilin.mixchange.matching.core.model.Pair, - price: Long, - quantity: Long, - direction: OrderDirection, - matchConstraint: MatchConstraint, - orderType: OrderType):this(){ - this.ouid = ouid - this.uuid = uuid - this.orderId = orderId - this.pair = pair - this.price = price - this.quantity = quantity - this.direction = direction - this.matchConstraint = matchConstraint - this.orderType = orderType - } - - -} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/config/AccountantKafkaConfig.kt b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/config/AccountantKafkaConfig.kt similarity index 92% rename from Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/config/AccountantKafkaConfig.kt rename to Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/config/AccountantKafkaConfig.kt index e714b9664..6c4781a87 100644 --- a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/config/AccountantKafkaConfig.kt +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/config/AccountantKafkaConfig.kt @@ -1,11 +1,11 @@ -package co.nilin.mixchange.port.accountant.kafka.config +package co.nilin.opex.port.accountant.kafka.config -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent -import co.nilin.mixchange.port.accountant.kafka.consumer.OrderKafkaListener -import co.nilin.mixchange.port.trade.consumer.EventKafkaListener -import co.nilin.mixchange.port.trade.consumer.TempEventKafkaListener -import co.nilin.mixchange.port.trade.consumer.TradeKafkaListener +import co.nilin.opex.matching.core.eventh.events.CoreEvent +import co.nilin.opex.port.accountant.kafka.consumer.OrderKafkaListener +import co.nilin.opex.port.accountant.kafka.consumer.EventKafkaListener +import co.nilin.opex.port.accountant.kafka.consumer.TempEventKafkaListener +import co.nilin.opex.port.accountant.kafka.consumer.TradeKafkaListener import org.apache.kafka.clients.admin.NewTopic import org.apache.kafka.clients.consumer.ConsumerConfig import org.apache.kafka.clients.producer.ProducerConfig @@ -46,7 +46,7 @@ class AccountantKafkaConfig { props[ConsumerConfig.GROUP_ID_CONFIG] = groupId props[ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG] = StringDeserializer::class.java props[ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG] = JsonDeserializer::class.java - props[JsonDeserializer.TRUSTED_PACKAGES] = "co.nilin.mixchange.*" + props[JsonDeserializer.TRUSTED_PACKAGES] = "co.nilin.opex.*" return props } diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/EventKafkaListener.kt b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/consumer/EventKafkaListener.kt similarity index 80% rename from Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/EventKafkaListener.kt rename to Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/consumer/EventKafkaListener.kt index e0e6359f3..2f86674d6 100644 --- a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/EventKafkaListener.kt +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/consumer/EventKafkaListener.kt @@ -1,15 +1,16 @@ -package co.nilin.mixchange.port.trade.consumer +package co.nilin.opex.port.accountant.kafka.consumer - -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent -import co.nilin.mixchange.port.trade.spi.EventListener +import co.nilin.opex.matching.core.eventh.events.CoreEvent +import co.nilin.opex.port.accountant.kafka.spi.EventListener import org.apache.kafka.clients.consumer.ConsumerRecord import org.springframework.kafka.listener.MessageListener import org.springframework.stereotype.Component @Component class EventKafkaListener: MessageListener { + val eventListeners = arrayListOf() + override fun onMessage(data: ConsumerRecord) { eventListeners.forEach{ tl -> tl.onEvent(data.value(), data.partition(), data.offset(), data.timestamp()) diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/OrderKafkaListener.kt b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/consumer/OrderKafkaListener.kt similarity index 71% rename from Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/OrderKafkaListener.kt rename to Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/consumer/OrderKafkaListener.kt index e8be743cb..208b7e039 100644 --- a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/OrderKafkaListener.kt +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/consumer/OrderKafkaListener.kt @@ -1,15 +1,16 @@ -package co.nilin.mixchange.port.accountant.kafka.consumer +package co.nilin.opex.port.accountant.kafka.consumer -import co.nilin.mixchange.port.accountant.kafka.spi.OrderSubmitRequestListener -import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest +import co.nilin.opex.port.accountant.kafka.spi.OrderSubmitRequestListener +import co.nilin.opex.port.order.kafka.inout.OrderSubmitRequest import org.apache.kafka.clients.consumer.ConsumerRecord import org.springframework.kafka.listener.MessageListener import org.springframework.stereotype.Component @Component -class OrderKafkaListener() : - MessageListener { +class OrderKafkaListener : MessageListener { + val orderListeners = arrayListOf() + override fun onMessage(data: ConsumerRecord) { orderListeners.forEach { tl -> tl.onOrder(data.value(), data.partition(), data.offset(), data.timestamp()) diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/TempEventKafkaListener.kt b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/consumer/TempEventKafkaListener.kt similarity index 77% rename from Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/TempEventKafkaListener.kt rename to Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/consumer/TempEventKafkaListener.kt index 8dc4e4e60..2e9ef8b6a 100644 --- a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/TempEventKafkaListener.kt +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/consumer/TempEventKafkaListener.kt @@ -1,16 +1,17 @@ -package co.nilin.mixchange.port.trade.consumer +package co.nilin.opex.port.accountant.kafka.consumer -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent -import co.nilin.mixchange.port.trade.spi.EventListener -import co.nilin.mixchange.port.trade.spi.TempEventListener +import co.nilin.opex.matching.core.eventh.events.CoreEvent +import co.nilin.opex.port.accountant.kafka.spi.TempEventListener import org.apache.kafka.clients.consumer.ConsumerRecord import org.springframework.kafka.listener.MessageListener import org.springframework.stereotype.Component @Component class TempEventKafkaListener: MessageListener { + val eventListeners = arrayListOf() + override fun onMessage(data: ConsumerRecord) { println("TempEvent onMessage") eventListeners.forEach{ diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/consumer/TradeKafkaListener.kt b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/consumer/TradeKafkaListener.kt similarity index 76% rename from EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/consumer/TradeKafkaListener.kt rename to Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/consumer/TradeKafkaListener.kt index 1d21d73b1..6d91a0c8a 100644 --- a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/consumer/TradeKafkaListener.kt +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/consumer/TradeKafkaListener.kt @@ -1,16 +1,16 @@ -package co.nilin.mixchange.port.trade.consumer +package co.nilin.opex.port.accountant.kafka.consumer - -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent -import co.nilin.mixchange.matching.core.eventh.events.TradeEvent -import co.nilin.mixchange.port.trade.spi.TradeListener +import co.nilin.opex.matching.core.eventh.events.TradeEvent +import co.nilin.opex.port.accountant.kafka.spi.TradeListener import org.apache.kafka.clients.consumer.ConsumerRecord import org.springframework.kafka.listener.MessageListener import org.springframework.stereotype.Component @Component class TradeKafkaListener: MessageListener { + val tradeListeners = arrayListOf() + override fun onMessage(data: ConsumerRecord) { tradeListeners.forEach{ tl -> tl.onTrade(data.value(), data.partition(), data.offset(), data.timestamp()) diff --git a/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/spi/EventListener.kt b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/spi/EventListener.kt similarity index 55% rename from Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/spi/EventListener.kt rename to Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/spi/EventListener.kt index 0f522ca46..4bc2c860f 100644 --- a/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/spi/EventListener.kt +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/spi/EventListener.kt @@ -1,7 +1,6 @@ -package co.nilin.mixchange.port.trade.spi - -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +package co.nilin.opex.port.accountant.kafka.spi +import co.nilin.opex.matching.core.eventh.events.CoreEvent interface EventListener { fun id(): String diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/OrderSubmitRequestListener.kt b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/spi/OrderSubmitRequestListener.kt similarity index 55% rename from Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/OrderSubmitRequestListener.kt rename to Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/spi/OrderSubmitRequestListener.kt index 28b88bea0..a956db9fe 100644 --- a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/OrderSubmitRequestListener.kt +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/spi/OrderSubmitRequestListener.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.port.accountant.kafka.spi +package co.nilin.opex.port.accountant.kafka.spi -import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest +import co.nilin.opex.port.order.kafka.inout.OrderSubmitRequest interface OrderSubmitRequestListener { fun id(): String diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/TempEventListener.kt b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/spi/TempEventListener.kt similarity index 56% rename from Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/TempEventListener.kt rename to Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/spi/TempEventListener.kt index 292b228ac..76e14cce2 100644 --- a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/TempEventListener.kt +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/spi/TempEventListener.kt @@ -1,7 +1,6 @@ -package co.nilin.mixchange.port.trade.spi - -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +package co.nilin.opex.port.accountant.kafka.spi +import co.nilin.opex.matching.core.eventh.events.CoreEvent interface TempEventListener { fun id(): String diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/spi/TradeListener.kt b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/spi/TradeListener.kt similarity index 55% rename from EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/spi/TradeListener.kt rename to Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/spi/TradeListener.kt index 7f37be88b..f4f958b97 100644 --- a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/spi/TradeListener.kt +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/spi/TradeListener.kt @@ -1,7 +1,6 @@ -package co.nilin.mixchange.port.trade.spi - -import co.nilin.mixchange.matching.core.eventh.events.TradeEvent +package co.nilin.opex.port.accountant.kafka.spi +import co.nilin.opex.matching.core.eventh.events.TradeEvent interface TradeListener { fun id(): String diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/inout/OrderSubmitRequest.kt b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/inout/OrderSubmitRequest.kt new file mode 100644 index 000000000..db9f6e74f --- /dev/null +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/inout/OrderSubmitRequest.kt @@ -0,0 +1,43 @@ +package co.nilin.opex.port.order.kafka.inout + +import co.nilin.opex.matching.core.model.MatchConstraint +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderType +import co.nilin.opex.matching.core.model.Pair + +class OrderSubmitRequest() { + + lateinit var ouid: String + lateinit var uuid: String + var orderId: Long? = null + lateinit var pair: Pair + var price: Long = 0 + var quantity: Long = 0 + var direction: OrderDirection = OrderDirection.BID + var matchConstraint: MatchConstraint = MatchConstraint.GTC + var orderType: OrderType = OrderType.LIMIT_ORDER + + constructor( + ouid: String, + uuid: String, + orderId: Long?, + pair: Pair, + price: Long, + quantity: Long, + direction: OrderDirection, + matchConstraint: MatchConstraint, + orderType: OrderType + ) : this() { + this.ouid = ouid + this.uuid = uuid + this.orderId = orderId + this.pair = pair + this.price = price + this.quantity = quantity + this.direction = direction + this.matchConstraint = matchConstraint + this.orderType = orderType + } + + +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/pom.xml b/Accountant/accountant-ports/accountant-persister-postgres/pom.xml index e3a6550e3..753b6a478 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/pom.xml +++ b/Accountant/accountant-ports/accountant-persister-postgres/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex accountant-persister-postgres 0.0.1-SNAPSHOT accountant-persister-postgres - Persist items of Mixchange accountant on Postgres + Persist items of Opex accountant on Postgres 1.8 @@ -23,13 +23,13 @@ - co.nilin.mixchange + co.nilin.opex matching-core ${matching.version} provided - co.nilin.mixchange + co.nilin.opex accountant-core ${accountant.version} provided diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/PairFeeConfigRepository.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/PairFeeConfigRepository.kt deleted file mode 100644 index 7406230c7..000000000 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/PairFeeConfigRepository.kt +++ /dev/null @@ -1,19 +0,0 @@ -package co.nilin.mixchange.port.accountant.postgres.dao - -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.port.accountant.postgres.model.PairConfigModel -import co.nilin.mixchange.port.accountant.postgres.model.PairFeeConfigModel -import org.springframework.data.r2dbc.repository.Query -import org.springframework.data.repository.query.Param -import org.springframework.data.repository.reactive.ReactiveCrudRepository -import org.springframework.stereotype.Repository -import reactor.core.publisher.Mono - -@Repository -interface PairFeeConfigRepository: ReactiveCrudRepository { - @Query("select * from pair_fee_config where pair_config_id = :pair and direction = :direction and user_level = :userLevel") - fun findByPairAndDirectionAndUserLevel( - @Param("pair") pair: String - ,@Param("direction") direction: OrderDirection - ,@Param("userLevel") userLevel: String): Mono -} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/OrderPersisterImpl.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/OrderPersisterImpl.kt deleted file mode 100644 index 379ec4e6a..000000000 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/OrderPersisterImpl.kt +++ /dev/null @@ -1,74 +0,0 @@ -package co.nilin.mixchange.port.accountant.postgres.impl - -import co.nilin.mixchange.accountant.core.model.Order -import co.nilin.mixchange.accountant.core.spi.OrderPersister -import co.nilin.mixchange.port.accountant.postgres.dao.OrderRepository -import co.nilin.mixchange.port.accountant.postgres.model.OrderModel -import kotlinx.coroutines.reactive.awaitFirstOrNull -import org.springframework.stereotype.Component -import java.time.LocalDateTime - -@Component -class OrderPersisterImpl(val orderRepository: OrderRepository) : OrderPersister { - override suspend fun load(ouid: String): Order? { - val model = orderRepository.findByOuid(ouid) - .awaitFirstOrNull() ?: return null - return Order( - model.pair, - model.ouid, - model.matchingEngineId, - model.makerFee, - model.takerFee, - model.leftSideFraction, - model.rightSideFraction, - model.uuid, - model.userLevel, - model.direction, - model.matchConstraint, - model.orderType, - model.price, - model.quantity, - model.filledQuantity, - model.origPrice, - model.origQuantity, - model.filledOrigQuantity, - model.firstTransferAmount, - model.remainedTransferAmount, - model.status, - model.id - ) - } - - override suspend fun save(order: Order): Order { - orderRepository.save( - OrderModel( - order.id, - order.ouid, - order.uuid, - order.pair, - order.matchingEngineId, - order.makerFee, - order.takerFee, - order.leftSideFraction, - order.rightSideFraction, - order.userLevel, - order.direction, - order.matchConstraint, - order.orderType, - order.price, - order.quantity, - order.filledQuantity, - order.origPrice, - order.origQuantity, - order.filledOrigQuantity, - order.firstTransferAmount, - order.remainedTransferAmount, - order.status, - "", - "", - LocalDateTime.now() - ) - ).awaitFirstOrNull() - return order - } -} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/TempEventPersisterImpl.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/TempEventPersisterImpl.kt deleted file mode 100644 index c394ffb0d..000000000 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/TempEventPersisterImpl.kt +++ /dev/null @@ -1,59 +0,0 @@ -package co.nilin.mixchange.port.accountant.postgres.impl - -import co.nilin.mixchange.accountant.core.model.TempEvent -import co.nilin.mixchange.accountant.core.spi.TempEventPersister -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent -import co.nilin.mixchange.port.accountant.postgres.dao.TempEventRepository -import co.nilin.mixchange.port.accountant.postgres.model.TempEventModel -import com.google.gson.Gson -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.toList -import kotlinx.coroutines.reactive.awaitFirstOrNull -import kotlinx.coroutines.reactive.awaitSingle -import org.springframework.data.domain.PageRequest -import org.springframework.data.domain.Sort -import org.springframework.stereotype.Component -import java.time.LocalDateTime - -@Component -class TempEventPersisterImpl(val tempEventRepository: TempEventRepository) : TempEventPersister { - override suspend fun saveTempEvent(ouid: String, event: CoreEvent) { - tempEventRepository.save( - TempEventModel( - null, ouid, event.javaClass.name, - Gson().toJson(event), LocalDateTime.now() - ) - ).awaitSingle() - } - - override suspend fun loadTempEvents(ouid: String): List { - return tempEventRepository - .findByOuid(ouid) - .map { value: TempEventModel -> - Gson().fromJson(value.eventBody, Class.forName(value.eventType)) as CoreEvent - } - .toList() - } - - override suspend fun removeTempEvents(ouid: String) { - tempEventRepository.deleteByOuid(ouid).awaitFirstOrNull() - } - - override suspend fun removeTempEvents(tempEvents: List) { - tempEventRepository.deleteAll(tempEvents.map { event -> - TempEventModel(event.id, event.ouid, event.eventBody.javaClass.name, - "", event.eventDate) - }).awaitFirstOrNull() - } - - override suspend fun fetchTempEvents(offset: Long, size: Long): List { - return tempEventRepository - .findAll(PageRequest.of(offset.toInt(), size.toInt(), Sort.by(Sort.Direction.ASC, "eventDate"))) - .map { value: TempEventModel -> - TempEvent(value.id!!, value.ouid, Gson().fromJson(value.eventBody, Class.forName(value.eventType)) - as - CoreEvent, value.eventDate) - } - .toList() - } -} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/TempEventModel.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/TempEventModel.kt deleted file mode 100644 index 40a7f501d..000000000 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/TempEventModel.kt +++ /dev/null @@ -1,14 +0,0 @@ -package co.nilin.mixchange.port.accountant.postgres.model - -import org.springframework.data.annotation.Id -import org.springframework.data.relational.core.mapping.Column -import org.springframework.data.relational.core.mapping.Table -import java.time.LocalDateTime - -@Table("temp_events") -data class TempEventModel(@Id val id: Long? -, val ouid: String -, @Column("event_type") val eventType: String -, @Column("event_body") val eventBody: String -, @Column("event_date") val eventDate: LocalDateTime) { -} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/config/PostgresConfig.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/config/PostgresConfig.kt similarity index 97% rename from Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/config/PostgresConfig.kt rename to Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/config/PostgresConfig.kt index 9d114eb90..cb04c037f 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/config/PostgresConfig.kt +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/config/PostgresConfig.kt @@ -1,17 +1,14 @@ -package co.nilin.mixchange.port.order.kafka.config - +package co.nilin.opex.port.accountant.postgres.config import org.springframework.context.annotation.Configuration import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories import org.springframework.r2dbc.core.DatabaseClient - @Configuration -@EnableR2dbcRepositories(basePackages = ["co.nilin.mixchange"]) +@EnableR2dbcRepositories(basePackages = ["co.nilin.opex"]) class PostgresConfig(db: DatabaseClient) { init { - val sql = """ CREATE TABLE IF NOT EXISTS orders ( id SERIAL PRIMARY KEY, diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/FinancialActionRepository.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/dao/FinancialActionRepository.kt similarity index 80% rename from Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/FinancialActionRepository.kt rename to Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/dao/FinancialActionRepository.kt index 2b5e30f82..3113cdcde 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/FinancialActionRepository.kt +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/dao/FinancialActionRepository.kt @@ -1,10 +1,8 @@ -package co.nilin.mixchange.port.accountant.postgres.dao +package co.nilin.opex.port.accountant.postgres.dao -import co.nilin.mixchange.accountant.core.model.FinancialAction -import co.nilin.mixchange.accountant.core.model.FinancialActionStatus -import co.nilin.mixchange.port.accountant.postgres.model.FinancialActionModel +import co.nilin.opex.accountant.core.model.FinancialActionStatus +import co.nilin.opex.port.accountant.postgres.model.FinancialActionModel import kotlinx.coroutines.flow.Flow -import org.springframework.data.domain.PageRequest import org.springframework.data.domain.Pageable import org.springframework.data.r2dbc.repository.Query import org.springframework.data.repository.query.Param @@ -15,6 +13,7 @@ import java.math.BigDecimal @Repository interface FinancialActionRepository : ReactiveCrudRepository { + @Query("select * from fi_actions fi where pointer = :ouid and :uuid in (fi.sender, fi.receiver)") fun findByOuidAndUuid( @Param("ouid") ouid: String, @Param("uuid") uuid: String, paging: Pageable diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/OrderRepository.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/dao/OrderRepository.kt similarity index 79% rename from Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/OrderRepository.kt rename to Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/dao/OrderRepository.kt index 4d937bfb9..adf817a93 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/OrderRepository.kt +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/dao/OrderRepository.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.port.accountant.postgres.dao +package co.nilin.opex.port.accountant.postgres.dao -import co.nilin.mixchange.port.accountant.postgres.model.OrderModel +import co.nilin.opex.port.accountant.postgres.model.OrderModel import org.springframework.data.r2dbc.repository.Query import org.springframework.data.repository.query.Param import org.springframework.data.repository.reactive.ReactiveCrudRepository diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/PairConfigRepository.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/dao/PairConfigRepository.kt similarity index 63% rename from Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/PairConfigRepository.kt rename to Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/dao/PairConfigRepository.kt index 017f734be..b43a4bb84 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/PairConfigRepository.kt +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/dao/PairConfigRepository.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.port.accountant.postgres.dao +package co.nilin.opex.port.accountant.postgres.dao -import co.nilin.mixchange.port.accountant.postgres.model.PairConfigModel +import co.nilin.opex.port.accountant.postgres.model.PairConfigModel import org.springframework.data.repository.reactive.ReactiveCrudRepository import org.springframework.stereotype.Repository diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/dao/PairFeeConfigRepository.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/dao/PairFeeConfigRepository.kt new file mode 100644 index 000000000..60903c0c9 --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/dao/PairFeeConfigRepository.kt @@ -0,0 +1,20 @@ +package co.nilin.opex.port.accountant.postgres.dao + +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.port.accountant.postgres.model.PairConfigModel +import co.nilin.opex.port.accountant.postgres.model.PairFeeConfigModel +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono + +@Repository +interface PairFeeConfigRepository : ReactiveCrudRepository { + @Query("select * from pair_fee_config where pair_config_id = :pair and direction = :direction and user_level = :userLevel") + fun findByPairAndDirectionAndUserLevel( + @Param("pair") pair: String, + @Param("direction") direction: OrderDirection, + @Param("userLevel") userLevel: String + ): Mono +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/TempEventRepository.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/dao/TempEventRepository.kt similarity index 61% rename from Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/TempEventRepository.kt rename to Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/dao/TempEventRepository.kt index a0bb738cb..6f89925f2 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/dao/TempEventRepository.kt +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/dao/TempEventRepository.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.port.accountant.postgres.dao +package co.nilin.opex.port.accountant.postgres.dao -import co.nilin.mixchange.port.accountant.postgres.model.TempEventModel +import co.nilin.opex.port.accountant.postgres.model.TempEventModel import kotlinx.coroutines.flow.Flow import org.springframework.data.domain.Pageable import org.springframework.data.r2dbc.repository.Query @@ -9,12 +9,11 @@ import org.springframework.stereotype.Repository import reactor.core.publisher.Mono @Repository -interface TempEventRepository: ReactiveCrudRepository { +interface TempEventRepository : ReactiveCrudRepository { + fun findByOuid(ouid: String): Flow fun deleteByOuid(ouid: String): Mono @Query("select * from temp_events") - fun findAll( - paging: Pageable - ): Flow + fun findAll(paging: Pageable): Flow } \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/FinancialActionLoaderImpl.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/FinancialActionLoaderImpl.kt similarity index 80% rename from Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/FinancialActionLoaderImpl.kt rename to Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/FinancialActionLoaderImpl.kt index b4223ec53..e7cba4d18 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/FinancialActionLoaderImpl.kt +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/FinancialActionLoaderImpl.kt @@ -1,9 +1,9 @@ -package co.nilin.mixchange.port.accountant.postgres.impl +package co.nilin.opex.port.accountant.postgres.impl -import co.nilin.mixchange.accountant.core.model.FinancialAction -import co.nilin.mixchange.accountant.core.model.FinancialActionStatus -import co.nilin.mixchange.accountant.core.spi.FinancialActionLoader -import co.nilin.mixchange.port.accountant.postgres.dao.FinancialActionRepository +import co.nilin.opex.accountant.core.model.FinancialAction +import co.nilin.opex.accountant.core.model.FinancialActionStatus +import co.nilin.opex.accountant.core.spi.FinancialActionLoader +import co.nilin.opex.port.accountant.postgres.dao.FinancialActionRepository import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.toList @@ -55,8 +55,12 @@ class FinancialActionLoaderImpl(val financialActionRepository: FinancialActionRe } override suspend fun countUnprocessed(uuid: String, symbol: String, eventType: String): Long { - return financialActionRepository.findByUuidAndSymbolAndEventTypeAndStatus(uuid, symbol, eventType, FinancialActionStatus.CREATED) - .awaitFirstOrElse { BigDecimal.ZERO } + return financialActionRepository.findByUuidAndSymbolAndEventTypeAndStatus( + uuid, + symbol, + eventType, + FinancialActionStatus.CREATED + ).awaitFirstOrElse { BigDecimal.ZERO } .toLong() } } \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/FinancialActionPersisterImpl.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/FinancialActionPersisterImpl.kt similarity index 81% rename from Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/FinancialActionPersisterImpl.kt rename to Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/FinancialActionPersisterImpl.kt index 0cfbc6c2a..6af5107d7 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/FinancialActionPersisterImpl.kt +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/FinancialActionPersisterImpl.kt @@ -1,10 +1,10 @@ -package co.nilin.mixchange.port.accountant.postgres.impl +package co.nilin.opex.port.accountant.postgres.impl -import co.nilin.mixchange.accountant.core.model.FinancialAction -import co.nilin.mixchange.accountant.core.model.FinancialActionStatus -import co.nilin.mixchange.accountant.core.spi.FinancialActionPersister -import co.nilin.mixchange.port.accountant.postgres.dao.FinancialActionRepository -import co.nilin.mixchange.port.accountant.postgres.model.FinancialActionModel +import co.nilin.opex.accountant.core.model.FinancialAction +import co.nilin.opex.accountant.core.model.FinancialActionStatus +import co.nilin.opex.accountant.core.spi.FinancialActionPersister +import co.nilin.opex.port.accountant.postgres.dao.FinancialActionRepository +import co.nilin.opex.port.accountant.postgres.model.FinancialActionModel import kotlinx.coroutines.reactive.awaitFirst import kotlinx.coroutines.reactive.awaitFirstOrElse import kotlinx.coroutines.reactive.awaitLast @@ -60,7 +60,6 @@ class FinancialActionPersisterImpl(val financialActionRepository: FinancialActio 1 + (existing.retryCount ?: 0), LocalDateTime.now() ) - ) - .awaitFirst() + ).awaitFirst() } } \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/OrderPersisterImpl.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/OrderPersisterImpl.kt new file mode 100644 index 000000000..094d49e67 --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/OrderPersisterImpl.kt @@ -0,0 +1,74 @@ +package co.nilin.opex.port.accountant.postgres.impl + +import co.nilin.opex.accountant.core.model.Order +import co.nilin.opex.accountant.core.spi.OrderPersister +import co.nilin.opex.port.accountant.postgres.dao.OrderRepository +import co.nilin.opex.port.accountant.postgres.model.OrderModel +import kotlinx.coroutines.reactive.awaitFirstOrNull +import org.springframework.stereotype.Component +import java.time.LocalDateTime + +@Component +class OrderPersisterImpl(val orderRepository: OrderRepository) : OrderPersister { + + override suspend fun load(ouid: String): Order? { + val model = orderRepository.findByOuid(ouid).awaitFirstOrNull() ?: return null + return Order( + model.pair, + model.ouid, + model.matchingEngineId, + model.makerFee, + model.takerFee, + model.leftSideFraction, + model.rightSideFraction, + model.uuid, + model.userLevel, + model.direction, + model.matchConstraint, + model.orderType, + model.price, + model.quantity, + model.filledQuantity, + model.origPrice, + model.origQuantity, + model.filledOrigQuantity, + model.firstTransferAmount, + model.remainedTransferAmount, + model.status, + model.id + ) + } + + override suspend fun save(order: Order): Order { + orderRepository.save( + OrderModel( + order.id, + order.ouid, + order.uuid, + order.pair, + order.matchingEngineId, + order.makerFee, + order.takerFee, + order.leftSideFraction, + order.rightSideFraction, + order.userLevel, + order.direction, + order.matchConstraint, + order.orderType, + order.price, + order.quantity, + order.filledQuantity, + order.origPrice, + order.origQuantity, + order.filledOrigQuantity, + order.firstTransferAmount, + order.remainedTransferAmount, + order.status, + "", + "", + LocalDateTime.now() + ) + ).awaitFirstOrNull() + return order + } +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/PairConfigLoaderImpl.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/PairConfigLoaderImpl.kt similarity index 74% rename from Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/PairConfigLoaderImpl.kt rename to Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/PairConfigLoaderImpl.kt index 4b2413577..467a379ed 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/PairConfigLoaderImpl.kt +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/PairConfigLoaderImpl.kt @@ -1,12 +1,12 @@ -package co.nilin.mixchange.port.accountant.postgres.impl +package co.nilin.opex.port.accountant.postgres.impl -import co.nilin.mixchange.accountant.core.model.PairConfig -import co.nilin.mixchange.accountant.core.model.PairFeeConfig -import co.nilin.mixchange.accountant.core.spi.PairConfigLoader -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.port.accountant.postgres.dao.PairConfigRepository -import co.nilin.mixchange.port.accountant.postgres.dao.PairFeeConfigRepository -import co.nilin.mixchange.port.accountant.postgres.model.PairFeeConfigModel +import co.nilin.opex.accountant.core.model.PairConfig +import co.nilin.opex.accountant.core.model.PairFeeConfig +import co.nilin.opex.accountant.core.spi.PairConfigLoader +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.port.accountant.postgres.dao.PairConfigRepository +import co.nilin.opex.port.accountant.postgres.dao.PairFeeConfigRepository +import co.nilin.opex.port.accountant.postgres.model.PairFeeConfigModel import kotlinx.coroutines.reactive.awaitFirstOrElse import kotlinx.coroutines.reactive.awaitFirstOrNull import org.springframework.stereotype.Component @@ -17,11 +17,12 @@ import java.lang.IllegalArgumentException class PairConfigLoaderImpl( val pairConfigRepository: PairConfigRepository, val pairFeeConfigRepository: PairFeeConfigRepository ) : PairConfigLoader { + override suspend fun load(pair: String, direction: OrderDirection, userLevel: String): PairFeeConfig { val pairConfig = pairConfigRepository .findById(pair).awaitFirstOrElse { throw IllegalArgumentException("$pair is not available") } var pairFeeConfig: PairFeeConfigModel? - if ( userLevel.isEmpty()) { + if (userLevel.isEmpty()) { pairFeeConfig = pairFeeConfigRepository .findByPairAndDirectionAndUserLevel(pair, direction, "*") .awaitFirstOrElse { throw IllegalArgumentException("$pair fee is not available") } @@ -29,15 +30,13 @@ class PairConfigLoaderImpl( pairFeeConfig = pairFeeConfigRepository .findByPairAndDirectionAndUserLevel(pair, direction, userLevel) .awaitFirstOrNull() - if ( pairFeeConfig == null ){ + if (pairFeeConfig == null) { pairFeeConfig = pairFeeConfigRepository .findByPairAndDirectionAndUserLevel(pair, direction, "*") .awaitFirstOrElse { throw IllegalArgumentException("$pair fee is not available") } } } - - return PairFeeConfig( PairConfig( pair, diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/PairStaticRateLoaderImpl.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/PairStaticRateLoaderImpl.kt similarity index 69% rename from Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/PairStaticRateLoaderImpl.kt rename to Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/PairStaticRateLoaderImpl.kt index cfab9e118..ea4345ff9 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/impl/PairStaticRateLoaderImpl.kt +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/PairStaticRateLoaderImpl.kt @@ -1,13 +1,14 @@ -package co.nilin.mixchange.port.accountant.postgres.impl +package co.nilin.opex.port.accountant.postgres.impl -import co.nilin.mixchange.accountant.core.spi.PairStaticRateLoader -import co.nilin.mixchange.port.accountant.postgres.dao.PairConfigRepository +import co.nilin.opex.accountant.core.spi.PairStaticRateLoader +import co.nilin.opex.port.accountant.postgres.dao.PairConfigRepository import kotlinx.coroutines.reactive.awaitFirstOrElse import org.springframework.stereotype.Component import java.lang.IllegalArgumentException @Component -class PairStaticRateLoaderImpl(val pairConfigRepository: PairConfigRepository): PairStaticRateLoader { +class PairStaticRateLoaderImpl(val pairConfigRepository: PairConfigRepository) : PairStaticRateLoader { + override suspend fun calculateStaticRate(leftSide: String, rightSide: String): Double? { val pairConfig = pairConfigRepository .findById("${leftSide}_$rightSide") diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/TempEventPersisterImpl.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/TempEventPersisterImpl.kt new file mode 100644 index 000000000..7c723b392 --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/TempEventPersisterImpl.kt @@ -0,0 +1,67 @@ +package co.nilin.opex.port.accountant.postgres.impl + +import co.nilin.opex.accountant.core.model.TempEvent +import co.nilin.opex.accountant.core.spi.TempEventPersister +import co.nilin.opex.matching.core.eventh.events.CoreEvent +import co.nilin.opex.port.accountant.postgres.dao.TempEventRepository +import co.nilin.opex.port.accountant.postgres.model.TempEventModel +import com.google.gson.Gson +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.reactive.awaitFirstOrNull +import kotlinx.coroutines.reactive.awaitSingle +import org.springframework.data.domain.PageRequest +import org.springframework.data.domain.Sort +import org.springframework.stereotype.Component +import java.time.LocalDateTime + +@Component +class TempEventPersisterImpl(val tempEventRepository: TempEventRepository) : TempEventPersister { + + override suspend fun saveTempEvent(ouid: String, event: CoreEvent) { + tempEventRepository.save( + TempEventModel( + null, ouid, event.javaClass.name, + Gson().toJson(event), LocalDateTime.now() + ) + ).awaitSingle() + } + + override suspend fun loadTempEvents(ouid: String): List { + return tempEventRepository + .findByOuid(ouid) + .map { value: TempEventModel -> + Gson().fromJson(value.eventBody, Class.forName(value.eventType)) as CoreEvent + } + .toList() + } + + override suspend fun removeTempEvents(ouid: String) { + tempEventRepository.deleteByOuid(ouid).awaitFirstOrNull() + } + + override suspend fun removeTempEvents(tempEvents: List) { + tempEventRepository.deleteAll(tempEvents.map { event -> + TempEventModel( + event.id, event.ouid, event.eventBody.javaClass.name, + "", event.eventDate + ) + }).awaitFirstOrNull() + } + + override suspend fun fetchTempEvents( + offset: Long, + size: Long + ): List { + return tempEventRepository + .findAll(PageRequest.of(offset.toInt(), size.toInt(), Sort.by(Sort.Direction.ASC, "eventDate"))) + .map { value: TempEventModel -> + TempEvent( + value.id!!, value.ouid, Gson().fromJson(value.eventBody, Class.forName(value.eventType)) + as + CoreEvent, value.eventDate + ) + } + .toList() + } +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/FinancialActionModel.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/model/FinancialActionModel.kt similarity index 77% rename from Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/FinancialActionModel.kt rename to Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/model/FinancialActionModel.kt index 77a6bc385..eefd9e7ad 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/FinancialActionModel.kt +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/model/FinancialActionModel.kt @@ -1,13 +1,9 @@ -package co.nilin.mixchange.port.accountant.postgres.model +package co.nilin.opex.port.accountant.postgres.model - -import co.nilin.mixchange.accountant.core.model.FinancialAction -import co.nilin.mixchange.accountant.core.model.FinancialActionStatus -import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.opex.accountant.core.model.FinancialActionStatus import org.springframework.data.annotation.Id import org.springframework.data.relational.core.mapping.Column import org.springframework.data.relational.core.mapping.Table -import java.math.BigDecimal import java.time.LocalDateTime @Table("fi_actions") diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/OrderModel.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/model/OrderModel.kt similarity index 87% rename from Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/OrderModel.kt rename to Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/model/OrderModel.kt index 50eec7693..8bf12cde4 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/OrderModel.kt +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/model/OrderModel.kt @@ -1,9 +1,8 @@ -package co.nilin.mixchange.port.accountant.postgres.model +package co.nilin.opex.port.accountant.postgres.model - -import co.nilin.mixchange.matching.core.model.MatchConstraint -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.OrderType +import co.nilin.opex.matching.core.model.MatchConstraint +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderType import org.springframework.data.annotation.Id import org.springframework.data.relational.core.mapping.Column import org.springframework.data.relational.core.mapping.Table diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/PairConfigModel.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/model/PairConfigModel.kt similarity index 91% rename from Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/PairConfigModel.kt rename to Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/model/PairConfigModel.kt index c2ca94ebe..c0c0726df 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/PairConfigModel.kt +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/model/PairConfigModel.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.port.accountant.postgres.model +package co.nilin.opex.port.accountant.postgres.model import org.springframework.data.annotation.Id import org.springframework.data.relational.core.mapping.Column diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/PairFeeConfigModel.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/model/PairFeeConfigModel.kt similarity index 87% rename from Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/PairFeeConfigModel.kt rename to Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/model/PairFeeConfigModel.kt index 9704056fc..8426f4190 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/accountant/postgres/model/PairFeeConfigModel.kt +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/model/PairFeeConfigModel.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.port.accountant.postgres.model +package co.nilin.opex.port.accountant.postgres.model import org.springframework.data.relational.core.mapping.Column import org.springframework.data.relational.core.mapping.Table @@ -11,5 +11,4 @@ class PairFeeConfigModel( @Column("user_level") val userLevel: String?, @Column("maker_fee") val makerFee: Double, @Column("taker_fee") val takerFee: Double -) { -} \ No newline at end of file +) \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/model/TempEventModel.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/model/TempEventModel.kt new file mode 100644 index 000000000..61ce47ec2 --- /dev/null +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/model/TempEventModel.kt @@ -0,0 +1,16 @@ +package co.nilin.opex.port.accountant.postgres.model + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.time.LocalDateTime + +@Table("temp_events") +data class TempEventModel( + @Id val id: Long?, + val ouid: String, + @Column("event_type") val eventType: String, + @Column("event_body") val eventBody: String, + @Column("event_date") val eventDate: LocalDateTime +) { +} \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-submitter-kafka/pom.xml b/Accountant/accountant-ports/accountant-submitter-kafka/pom.xml index 94ceab7c1..5d24cf377 100644 --- a/Accountant/accountant-ports/accountant-submitter-kafka/pom.xml +++ b/Accountant/accountant-ports/accountant-submitter-kafka/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex accountant-submitter-kafka 0.0.1-SNAPSHOT accountant-temp-submitter-kafka - Accountant kafka event submitter of Mixchange + Accountant kafka event submitter of Opex 1.8 @@ -31,13 +31,13 @@ spring-boot-starter-webflux - co.nilin.mixchange + co.nilin.opex matching-core ${matching.version} provided - co.nilin.mixchange + co.nilin.opex accountant-core ${accountant.version} provided diff --git a/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/config/SubmitterKafkaConfig.kt b/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/config/SubmitterKafkaConfig.kt similarity index 93% rename from Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/config/SubmitterKafkaConfig.kt rename to Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/config/SubmitterKafkaConfig.kt index 2ecd1901b..f643d6bc7 100644 --- a/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/config/SubmitterKafkaConfig.kt +++ b/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/config/SubmitterKafkaConfig.kt @@ -1,9 +1,8 @@ -package co.nilin.mixchange.port.accountant.kafka.config +package co.nilin.opex.port.accountant.kafka.config - -import co.nilin.mixchange.accountant.core.inout.RichOrder -import co.nilin.mixchange.accountant.core.inout.RichTrade -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +import co.nilin.opex.accountant.core.inout.RichOrder +import co.nilin.opex.accountant.core.inout.RichTrade +import co.nilin.opex.matching.core.eventh.events.CoreEvent import org.apache.kafka.clients.admin.NewTopic import org.apache.kafka.clients.producer.ProducerConfig import org.apache.kafka.common.serialization.StringSerializer @@ -18,7 +17,6 @@ import org.springframework.kafka.core.KafkaTemplate import org.springframework.kafka.core.ProducerFactory import org.springframework.kafka.support.serializer.JsonSerializer - @Configuration class SubmitterKafkaConfig() { @Value("\${spring.kafka.bootstrap-servers}") diff --git a/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/RichOrderSubmitter.kt b/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/service/RichOrderSubmitter.kt similarity index 78% rename from Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/RichOrderSubmitter.kt rename to Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/service/RichOrderSubmitter.kt index 50fef59c3..52c0ecf22 100644 --- a/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/RichOrderSubmitter.kt +++ b/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/service/RichOrderSubmitter.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.port.accountant.kafka.service +package co.nilin.opex.port.accountant.kafka.service -import co.nilin.mixchange.accountant.core.inout.RichOrder -import co.nilin.mixchange.accountant.core.spi.RichOrderPublisher +import co.nilin.opex.accountant.core.inout.RichOrder +import co.nilin.opex.accountant.core.spi.RichOrderPublisher import org.springframework.beans.factory.annotation.Qualifier import org.springframework.kafka.core.KafkaTemplate import org.springframework.stereotype.Component @@ -11,7 +11,7 @@ import kotlin.coroutines.suspendCoroutine @Component class RichOrderSubmitter(@Qualifier("richOrderKafkaTemplate") val kafkaTemplate: KafkaTemplate) : - RichOrderPublisher { + RichOrderPublisher { override suspend fun publish(order: RichOrder): Unit = suspendCoroutine { cont -> println("richOrderSubmit!") val sendFuture = kafkaTemplate.send("richOrder", order) diff --git a/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/RichTradeSubmitter.kt b/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/service/RichTradeSubmitter.kt similarity index 78% rename from Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/RichTradeSubmitter.kt rename to Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/service/RichTradeSubmitter.kt index 92223b510..0336e91b7 100644 --- a/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/RichTradeSubmitter.kt +++ b/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/service/RichTradeSubmitter.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.port.accountant.kafka.service +package co.nilin.opex.port.accountant.kafka.service -import co.nilin.mixchange.accountant.core.inout.RichTrade -import co.nilin.mixchange.accountant.core.spi.RichTradePublisher +import co.nilin.opex.accountant.core.inout.RichTrade +import co.nilin.opex.accountant.core.spi.RichTradePublisher import org.springframework.beans.factory.annotation.Qualifier import org.springframework.kafka.core.KafkaTemplate import org.springframework.stereotype.Component @@ -11,7 +11,7 @@ import kotlin.coroutines.suspendCoroutine @Component class RichTradeSubmitter(@Qualifier("richTradeKafkaTemplate") val kafkaTemplate: KafkaTemplate) : - RichTradePublisher { + RichTradePublisher { override suspend fun publish(trade: RichTrade): Unit = suspendCoroutine { cont -> println("richTradeSubmit!") val sendFuture = kafkaTemplate.send("richTrade", trade) diff --git a/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/TempEventSubmitter.kt b/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/service/TempEventSubmitter.kt similarity index 82% rename from Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/TempEventSubmitter.kt rename to Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/service/TempEventSubmitter.kt index 0e21bafc3..4c4c0a977 100644 --- a/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/service/TempEventSubmitter.kt +++ b/Accountant/accountant-ports/accountant-submitter-kafka/src/main/kotlin/co/nilin/opex/port/accountant/kafka/service/TempEventSubmitter.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.port.accountant.kafka.service +package co.nilin.opex.port.accountant.kafka.service -import co.nilin.mixchange.accountant.core.spi.TempEventRepublisher -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +import co.nilin.opex.accountant.core.spi.TempEventRepublisher +import co.nilin.opex.matching.core.eventh.events.CoreEvent import org.springframework.beans.factory.annotation.Qualifier import org.springframework.kafka.core.KafkaTemplate import org.springframework.stereotype.Component diff --git a/Accountant/accountant-ports/accountant-wallet-proxy/pom.xml b/Accountant/accountant-ports/accountant-wallet-proxy/pom.xml index bcae9b7cb..8164d4c94 100644 --- a/Accountant/accountant-ports/accountant-wallet-proxy/pom.xml +++ b/Accountant/accountant-ports/accountant-wallet-proxy/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex accountant-wallet-proxy 0.0.1-SNAPSHOT accountant-wallet-proxy - Mixchange wallet proxy + Opex wallet proxy 1.8 @@ -24,13 +24,13 @@ - co.nilin.mixchange + co.nilin.opex matching-core ${matching.version} provided - co.nilin.mixchange + co.nilin.opex accountant-core ${accountant.version} provided diff --git a/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/mixchange/port/accountant/wallet/config/WebClientConfig.kt b/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/opex/port/accountant/wallet/config/WebClientConfig.kt similarity index 94% rename from Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/mixchange/port/accountant/wallet/config/WebClientConfig.kt rename to Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/opex/port/accountant/wallet/config/WebClientConfig.kt index bf5f49c95..857143a6b 100644 --- a/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/mixchange/port/accountant/wallet/config/WebClientConfig.kt +++ b/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/opex/port/accountant/wallet/config/WebClientConfig.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.port.accountant.wallet.config +package co.nilin.opex.port.accountant.wallet.config import org.springframework.cloud.client.ServiceInstance import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties diff --git a/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/mixchange/port/accountant/wallet/proxy/WalletProxyImpl.kt b/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/opex/port/accountant/wallet/proxy/WalletProxyImpl.kt similarity index 95% rename from Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/mixchange/port/accountant/wallet/proxy/WalletProxyImpl.kt rename to Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/opex/port/accountant/wallet/proxy/WalletProxyImpl.kt index bf197ee59..69b068736 100644 --- a/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/mixchange/port/accountant/wallet/proxy/WalletProxyImpl.kt +++ b/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/opex/port/accountant/wallet/proxy/WalletProxyImpl.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.port.accountant.wallet.proxy +package co.nilin.opex.port.accountant.wallet.proxy -import co.nilin.mixchange.accountant.core.spi.WalletProxy +import co.nilin.opex.accountant.core.spi.WalletProxy import kotlinx.coroutines.reactive.awaitFirst import org.springframework.beans.factory.annotation.Value import org.springframework.core.ParameterizedTypeReference diff --git a/Accountant/pom.xml b/Accountant/pom.xml index bb61c0a3e..4d974760c 100644 --- a/Accountant/pom.xml +++ b/Accountant/pom.xml @@ -2,12 +2,12 @@ 4.0.0 - co.nilin.mixchange + co.nilin.opex accountant-root 0.0.1-SNAPSHOT accountant-root pom - Accountant root of Mixchange + Accountant root of Opex accountant-core accountant-app diff --git a/Api/api-app/pom.xml b/Api/api-app/pom.xml index 8cca739d5..8e31198ad 100644 --- a/Api/api-app/pom.xml +++ b/Api/api-app/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex api-app 0.0.1-SNAPSHOT api-app - Api app Mixchange + Api app Opex 1.8 @@ -44,27 +44,27 @@ spring-boot-starter - co.nilin.mixchange + co.nilin.opex accountant-core ${accountant.version} - co.nilin.mixchange + co.nilin.opex api-core ${api.version} - co.nilin.mixchange + co.nilin.opex api-eventlistener-kafka ${api.version} - co.nilin.mixchange + co.nilin.opex api-binance-rest ${api.version} - co.nilin.mixchange + co.nilin.opex api-persister-postgres ${api.version} diff --git a/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt b/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt deleted file mode 100644 index 77190e1f0..000000000 --- a/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt +++ /dev/null @@ -1,61 +0,0 @@ -package co.nilin.mixchange.app.config - -import co.nilin.mixchange.accountant.core.inout.RichOrder -import co.nilin.mixchange.api.core.spi.OrderPersister -import co.nilin.mixchange.api.core.spi.TradePersister -import co.nilin.mixchange.port.api.kafka.consumer.OrderKafkaListener -import co.nilin.mixchange.port.api.kafka.spi.RichOrderListener -import co.nilin.mixchange.port.trade.consumer.TradeKafkaListener -import co.nilin.mixchange.port.trade.spi.RichTradeListener -import kotlinx.coroutines.runBlocking -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration - -@Configuration -class AppConfig { - - @Bean - fun apiListener( - richOrderPersister: OrderPersister, richTradePersister: TradePersister - ): ApiListenerImpl { - return ApiListenerImpl(richOrderPersister, richTradePersister) - } - - @Autowired - fun configureListeners( - orderKafkaListener: OrderKafkaListener, tradeKafkaListener: TradeKafkaListener, appListener: ApiListenerImpl - ) { - orderKafkaListener.addOrderListener(appListener) - tradeKafkaListener.addTradeListener(appListener) - } - - class ApiListenerImpl( - val richOrderPersister: OrderPersister, val richTradePersister: TradePersister - ) : RichTradeListener, RichOrderListener { - - override fun id(): String { - return "AppListener" - } - - override fun onTrade( - trade: co.nilin.mixchange.accountant.core.inout.RichTrade, - partition: Int, - offset: Long, - timestamp: Long - ) { - println("RichTrade received") - runBlocking(AppDispatchers.kafkaExecutor) { - richTradePersister.save(trade) - } - } - - override fun onOrder(order: RichOrder, partition: Int, offset: Long, timestamp: Long) { - runBlocking(AppDispatchers.kafkaExecutor) { - richOrderPersister.save(order) - } - } - } - - -} \ No newline at end of file diff --git a/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/ApiApp.kt b/Api/api-app/src/main/kotlin/co/nilin/opex/app/ApiApp.kt similarity index 81% rename from Api/api-app/src/main/kotlin/co/nilin/mixchange/app/ApiApp.kt rename to Api/api-app/src/main/kotlin/co/nilin/opex/app/ApiApp.kt index 8a9752dba..9c797d8e3 100644 --- a/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/ApiApp.kt +++ b/Api/api-app/src/main/kotlin/co/nilin/opex/app/ApiApp.kt @@ -1,11 +1,11 @@ -package co.nilin.mixchange.app +package co.nilin.opex.app import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.annotation.ComponentScan @SpringBootApplication -@ComponentScan("co.nilin.mixchange") +@ComponentScan("co.nilin.opex") class AccountantApp fun main(args: Array) { diff --git a/Api/api-app/src/main/kotlin/co/nilin/opex/app/config/AppConfig.kt b/Api/api-app/src/main/kotlin/co/nilin/opex/app/config/AppConfig.kt new file mode 100644 index 000000000..d544cd605 --- /dev/null +++ b/Api/api-app/src/main/kotlin/co/nilin/opex/app/config/AppConfig.kt @@ -0,0 +1,71 @@ +package co.nilin.opex.app.config + +import co.nilin.opex.accountant.core.inout.RichOrder +import co.nilin.opex.accountant.core.inout.RichTrade +import co.nilin.opex.api.core.spi.OrderPersister +import co.nilin.opex.api.core.spi.TradePersister +import co.nilin.opex.port.api.kafka.consumer.OrderKafkaListener +import co.nilin.opex.port.api.kafka.spi.RichOrderListener +import co.nilin.opex.port.api.kafka.consumer.TradeKafkaListener +import co.nilin.opex.port.api.kafka.spi.RichTradeListener +import kotlinx.coroutines.runBlocking +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +class AppConfig { + + @Bean + fun apiListener( + richOrderPersister: OrderPersister, + richTradePersister: TradePersister + ): ApiListenerImpl { + return ApiListenerImpl(richOrderPersister, richTradePersister) + } + + @Autowired + fun configureListeners( + orderKafkaListener: OrderKafkaListener, + tradeKafkaListener: TradeKafkaListener, + appListener: ApiListenerImpl + ) { + orderKafkaListener.addOrderListener(appListener) + tradeKafkaListener.addTradeListener(appListener) + } + + class ApiListenerImpl( + val richOrderPersister: OrderPersister, + val richTradePersister: TradePersister + ) : RichTradeListener, RichOrderListener { + + override fun id(): String { + return "AppListener" + } + + override fun onTrade( + trade: RichTrade, + partition: Int, + offset: Long, + timestamp: Long + ) { + println("RichTrade received") + runBlocking(AppDispatchers.kafkaExecutor) { + richTradePersister.save(trade) + } + } + + override fun onOrder( + order: RichOrder, + partition: Int, + offset: Long, + timestamp: Long + ) { + runBlocking(AppDispatchers.kafkaExecutor) { + richOrderPersister.save(order) + } + } + } + + +} \ No newline at end of file diff --git a/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/config/AppDispatchers.kt b/Api/api-app/src/main/kotlin/co/nilin/opex/app/config/AppDispatchers.kt similarity index 88% rename from Api/api-app/src/main/kotlin/co/nilin/mixchange/app/config/AppDispatchers.kt rename to Api/api-app/src/main/kotlin/co/nilin/opex/app/config/AppDispatchers.kt index 073195cc3..a63f2d6a6 100644 --- a/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/config/AppDispatchers.kt +++ b/Api/api-app/src/main/kotlin/co/nilin/opex/app/config/AppDispatchers.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.app.config +package co.nilin.opex.app.config import kotlinx.coroutines.asCoroutineDispatcher import java.util.concurrent.Executors diff --git a/Api/api-core/pom.xml b/Api/api-core/pom.xml index fa58533d5..fd067d84a 100644 --- a/Api/api-core/pom.xml +++ b/Api/api-core/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex api-core 0.0.1-SNAPSHOT api-core - Api logic of Mixchange + Api logic of Opex 1.8 @@ -47,12 +47,12 @@ kotlinx-coroutines-core - co.nilin.mixchange + co.nilin.opex matching-core ${matching.version} - co.nilin.mixchange + co.nilin.opex accountant-core ${accountant.version} provided diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/AllOrderRequest.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/AllOrderRequest.kt deleted file mode 100644 index 880b1c57c..000000000 --- a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/AllOrderRequest.kt +++ /dev/null @@ -1,10 +0,0 @@ -package co.nilin.mixchange.api.core.inout - -import java.util.* - -class AllOrderRequest( - val symbol: String?, - val startTime: Date?, - val endTime: Date?, - val limit: Int? = 500, //Default 500; max 1000. -) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/CreateOrderRequest.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/CreateOrderRequest.kt deleted file mode 100644 index 8f3715618..000000000 --- a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/CreateOrderRequest.kt +++ /dev/null @@ -1,19 +0,0 @@ -package co.nilin.mixchange.api.core.inout - -import java.math.BigDecimal - -data class CreateOrderRequest( - val symbol: String, - val side: OrderSide, - val type: OrderType, - val timeInForce: TimeInForce?, - val quantity: BigDecimal?, - val quoteOrderQty: BigDecimal?, - val price: BigDecimal?, - val newClientOrderId: String?, /* A unique id among open orders. Automatically generated if not sent. - Orders with the same newClientOrderID can be accepted only when the previous one is filled, otherwise the order will be rejected. - */ - val stopPrice: BigDecimal?, //Used with STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, and TAKE_PROFIT_LIMIT orders. - val icebergQty: BigDecimal?, //Used with LIMIT, STOP_LOSS_LIMIT, and TAKE_PROFIT_LIMIT to create an iceberg order. - val newOrderRespType: OrderResponseType?, //Set the response JSON. ACK, RESULT, or FULL; MARKET and LIMIT order types default to FULL, all other orders default to ACK. -) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/CreateOrderResponse.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/CreateOrderResponse.kt deleted file mode 100644 index ba237241a..000000000 --- a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/CreateOrderResponse.kt +++ /dev/null @@ -1,21 +0,0 @@ -package co.nilin.mixchange.api.core.inout - -import java.math.BigDecimal -import java.util.* - -data class CreateOrderResponse( - val symbol: String, - val orderId: Long, - val orderListId: Long, //Unless OCO, value will be -1 - val clientOrderId: String, - val transactTime: Date, - val price: BigDecimal?, - val origQty: BigDecimal?, - val executedQty: BigDecimal?, - val cummulativeQuoteQty: BigDecimal, - val status: OrderStatus?, - val timeInForce: TimeInForce?, - val type: OrderType?, - val side: OrderSide?, - val fills: List? -) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OrderTradeData.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OrderTradeData.kt deleted file mode 100644 index a1d468639..000000000 --- a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OrderTradeData.kt +++ /dev/null @@ -1,10 +0,0 @@ -package co.nilin.mixchange.api.core.inout - -import java.math.BigDecimal - -data class OrderTradeData( - val price: BigDecimal, - val qty: BigDecimal, - val commission: BigDecimal, - val commissionAsset: String -) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/QueryOrderRequest.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/QueryOrderRequest.kt deleted file mode 100644 index 4d5039e61..000000000 --- a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/QueryOrderRequest.kt +++ /dev/null @@ -1,7 +0,0 @@ -package co.nilin.mixchange.api.core.inout - -data class QueryOrderRequest( - val symbol: String, - val orderId: Long?, - val origClientOrderId: String? -) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/QueryOrderResponse.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/QueryOrderResponse.kt deleted file mode 100644 index 2da014ce1..000000000 --- a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/QueryOrderResponse.kt +++ /dev/null @@ -1,25 +0,0 @@ -package co.nilin.mixchange.api.core.inout - -import java.math.BigDecimal -import java.util.* - -data class QueryOrderResponse( - val symbol: String, - val orderId: Long, - val orderListId: Long, //Unless part of an OCO, the value will always be -1. - val clientOrderId: String, - val price: BigDecimal, - val origQty: BigDecimal, - val executedQty: BigDecimal, - val cummulativeQuoteQty: BigDecimal, - val status: OrderStatus, - val timeInForce: TimeInForce, - val type: OrderType, - val side: OrderSide, - val stopPrice: BigDecimal?, - val icebergQty: BigDecimal?, - val time: Date, - val updateTime: Date, - val isWorking: Boolean, - val origQuoteOrderQty: BigDecimal -) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/TradeRequest.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/TradeRequest.kt deleted file mode 100644 index 0baff32a0..000000000 --- a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/TradeRequest.kt +++ /dev/null @@ -1,10 +0,0 @@ -package co.nilin.mixchange.api.core.inout - -import java.util.* - -class TradeRequest(val symbol: String?, - val fromTrade: Long?, - val startTime: Date?, - val endTime: Date?, - val limit: Int? = 500 //Default 500; max 1000. -) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/TradeResponse.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/TradeResponse.kt deleted file mode 100644 index 59ded6428..000000000 --- a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/TradeResponse.kt +++ /dev/null @@ -1,19 +0,0 @@ -package co.nilin.mixchange.api.core.inout - -import java.math.BigDecimal -import java.util.* - -data class TradeResponse( - val symbol: String, - val id: Long, - val orderId: Long, - val orderListId: Long = -1, - val price: BigDecimal, - val qty: BigDecimal, - val quoteQty: BigDecimal, - val commission: BigDecimal, - val commissionAsset: String, - val time: Date, - val isBuyer: Boolean, - val isMaker: Boolean, - val isBestMatch: Boolean) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/AccountantProxy.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/AccountantProxy.kt deleted file mode 100644 index 587a0a458..000000000 --- a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/AccountantProxy.kt +++ /dev/null @@ -1,5 +0,0 @@ -package co.nilin.mixchange.api.core.spi - -interface AccountantProxy { - -} \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/OrderPersister.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/OrderPersister.kt deleted file mode 100644 index dcc404034..000000000 --- a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/OrderPersister.kt +++ /dev/null @@ -1,7 +0,0 @@ -package co.nilin.mixchange.api.core.spi - -import co.nilin.mixchange.accountant.core.inout.RichOrder - -interface OrderPersister { - suspend fun save(order: RichOrder) -} \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/TradePersister.kt b/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/TradePersister.kt deleted file mode 100644 index 5574ac065..000000000 --- a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/TradePersister.kt +++ /dev/null @@ -1,7 +0,0 @@ -package co.nilin.mixchange.api.core.spi - -import co.nilin.mixchange.accountant.core.inout.RichTrade - -interface TradePersister { - suspend fun save(trade: RichTrade) -} \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/AllOrderRequest.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/AllOrderRequest.kt new file mode 100644 index 000000000..6eb74b1f1 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/AllOrderRequest.kt @@ -0,0 +1,10 @@ +package co.nilin.opex.api.core.inout + +import java.util.* + +class AllOrderRequest( + val symbol: String?, + val startTime: Date?, + val endTime: Date?, + val limit: Int? = 500, //Default 500; max 1000. +) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/CreateOrderRequest.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/CreateOrderRequest.kt new file mode 100644 index 000000000..7b51525c6 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/CreateOrderRequest.kt @@ -0,0 +1,19 @@ +package co.nilin.opex.api.core.inout + +import java.math.BigDecimal + +data class CreateOrderRequest( + val symbol: String, + val side: OrderSide, + val type: OrderType, + val timeInForce: TimeInForce?, + val quantity: BigDecimal?, + val quoteOrderQty: BigDecimal?, + val price: BigDecimal?, + val newClientOrderId: String?, /* A unique id among open orders. Automatically generated if not sent. + Orders with the same newClientOrderID can be accepted only when the previous one is filled, otherwise the order will be rejected. + */ + val stopPrice: BigDecimal?, //Used with STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, and TAKE_PROFIT_LIMIT orders. + val icebergQty: BigDecimal?, //Used with LIMIT, STOP_LOSS_LIMIT, and TAKE_PROFIT_LIMIT to create an iceberg order. + val newOrderRespType: OrderResponseType?, //Set the response JSON. ACK, RESULT, or FULL; MARKET and LIMIT order types default to FULL, all other orders default to ACK. +) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/CreateOrderResponse.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/CreateOrderResponse.kt new file mode 100644 index 000000000..9d2f64078 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/CreateOrderResponse.kt @@ -0,0 +1,21 @@ +package co.nilin.opex.api.core.inout + +import java.math.BigDecimal +import java.util.* + +data class CreateOrderResponse( + val symbol: String, + val orderId: Long, + val orderListId: Long, //Unless OCO, value will be -1 + val clientOrderId: String, + val transactTime: Date, + val price: BigDecimal?, + val origQty: BigDecimal?, + val executedQty: BigDecimal?, + val cummulativeQuoteQty: BigDecimal, + val status: OrderStatus?, + val timeInForce: TimeInForce?, + val type: OrderType?, + val side: OrderSide?, + val fills: List? +) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OrderEnums.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OrderEnums.kt similarity index 97% rename from Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OrderEnums.kt rename to Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OrderEnums.kt index 99c1b9804..8c0423fa8 100644 --- a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OrderEnums.kt +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OrderEnums.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.api.core.inout +package co.nilin.opex.api.core.inout enum class TimeInForce { GTC, //Good Til Canceled, An order will be on the book unless the order is canceled. diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OrderSubmitResult.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OrderSubmitResult.kt similarity index 53% rename from Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OrderSubmitResult.kt rename to Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OrderSubmitResult.kt index 283877d47..1e6d864c6 100644 --- a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OrderSubmitResult.kt +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OrderSubmitResult.kt @@ -1,3 +1,3 @@ -package co.nilin.mixchange.api.core.inout +package co.nilin.opex.api.core.inout data class OrderSubmitResult(val offset: Long?) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OrderTradeData.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OrderTradeData.kt new file mode 100644 index 000000000..86d45b08a --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OrderTradeData.kt @@ -0,0 +1,10 @@ +package co.nilin.opex.api.core.inout + +import java.math.BigDecimal + +data class OrderTradeData( + val price: BigDecimal, + val qty: BigDecimal, + val commission: BigDecimal, + val commissionAsset: String +) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OwnerLimitsResponse.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OwnerLimitsResponse.kt similarity index 73% rename from Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OwnerLimitsResponse.kt rename to Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OwnerLimitsResponse.kt index de9981d9a..84267db98 100644 --- a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/OwnerLimitsResponse.kt +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OwnerLimitsResponse.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.api.core.inout +package co.nilin.opex.api.core.inout data class OwnerLimitsResponse( val canTrade: Boolean, diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/QueryOrderRequest.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/QueryOrderRequest.kt new file mode 100644 index 000000000..5b0890e65 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/QueryOrderRequest.kt @@ -0,0 +1,7 @@ +package co.nilin.opex.api.core.inout + +data class QueryOrderRequest( + val symbol: String, + val orderId: Long?, + val origClientOrderId: String? +) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/QueryOrderResponse.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/QueryOrderResponse.kt new file mode 100644 index 000000000..6edddbe15 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/QueryOrderResponse.kt @@ -0,0 +1,25 @@ +package co.nilin.opex.api.core.inout + +import java.math.BigDecimal +import java.util.* + +data class QueryOrderResponse( + val symbol: String, + val orderId: Long, + val orderListId: Long, //Unless part of an OCO, the value will always be -1. + val clientOrderId: String, + val price: BigDecimal, + val origQty: BigDecimal, + val executedQty: BigDecimal, + val cummulativeQuoteQty: BigDecimal, + val status: OrderStatus, + val timeInForce: TimeInForce, + val type: OrderType, + val side: OrderSide, + val stopPrice: BigDecimal?, + val icebergQty: BigDecimal?, + val time: Date, + val updateTime: Date, + val isWorking: Boolean, + val origQuoteOrderQty: BigDecimal +) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/TradeRequest.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/TradeRequest.kt new file mode 100644 index 000000000..511949d47 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/TradeRequest.kt @@ -0,0 +1,11 @@ +package co.nilin.opex.api.core.inout + +import java.util.* + +class TradeRequest( + val symbol: String?, + val fromTrade: Long?, + val startTime: Date?, + val endTime: Date?, + val limit: Int? = 500 //Default 500; max 1000. +) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/TradeResponse.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/TradeResponse.kt new file mode 100644 index 000000000..745407236 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/TradeResponse.kt @@ -0,0 +1,20 @@ +package co.nilin.opex.api.core.inout + +import java.math.BigDecimal +import java.util.* + +data class TradeResponse( + val symbol: String, + val id: Long, + val orderId: Long, + val orderListId: Long = -1, + val price: BigDecimal, + val qty: BigDecimal, + val quoteQty: BigDecimal, + val commission: BigDecimal, + val commissionAsset: String, + val time: Date, + val isBuyer: Boolean, + val isMaker: Boolean, + val isBestMatch: Boolean +) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/Wallet.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/Wallet.kt similarity index 74% rename from Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/Wallet.kt rename to Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/Wallet.kt index 4e8eea1ed..72e6d2f1b 100644 --- a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/inout/Wallet.kt +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/Wallet.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.api.core.inout +package co.nilin.opex.api.core.inout import java.math.BigDecimal diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/AccountantProxy.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/AccountantProxy.kt new file mode 100644 index 000000000..8bf00731e --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/AccountantProxy.kt @@ -0,0 +1,5 @@ +package co.nilin.opex.api.core.spi + +interface AccountantProxy { + +} \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/MEGatewayProxy.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MEGatewayProxy.kt similarity index 62% rename from Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/MEGatewayProxy.kt rename to Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MEGatewayProxy.kt index 6474f78e6..2d268252d 100644 --- a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/MEGatewayProxy.kt +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MEGatewayProxy.kt @@ -1,9 +1,9 @@ -package co.nilin.mixchange.api.core.spi +package co.nilin.opex.api.core.spi -import co.nilin.mixchange.api.core.inout.OrderSubmitResult -import co.nilin.mixchange.matching.core.model.MatchConstraint -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.OrderType +import co.nilin.opex.api.core.inout.OrderSubmitResult +import co.nilin.opex.matching.core.model.MatchConstraint +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderType import reactor.core.publisher.Mono import java.math.BigDecimal diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/OrderPersister.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/OrderPersister.kt new file mode 100644 index 000000000..a8a128d04 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/OrderPersister.kt @@ -0,0 +1,7 @@ +package co.nilin.opex.api.core.spi + +import co.nilin.opex.accountant.core.inout.RichOrder + +interface OrderPersister { + suspend fun save(order: RichOrder) +} \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/TradePersister.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/TradePersister.kt new file mode 100644 index 000000000..d07d91a1f --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/TradePersister.kt @@ -0,0 +1,7 @@ +package co.nilin.opex.api.core.spi + +import co.nilin.opex.accountant.core.inout.RichTrade + +interface TradePersister { + suspend fun save(trade: RichTrade) +} \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/UserQueryHandler.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/UserQueryHandler.kt similarity index 81% rename from Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/UserQueryHandler.kt rename to Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/UserQueryHandler.kt index a2ad706da..2c986aed3 100644 --- a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/UserQueryHandler.kt +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/UserQueryHandler.kt @@ -1,6 +1,7 @@ -package co.nilin.mixchange.api.core.spi +package co.nilin.opex.api.core.spi -import co.nilin.mixchange.api.core.inout.* +import co.nilin.opex.api.core.inout.* +import co.nilin.opex.api.core.inout.* import kotlinx.coroutines.flow.Flow import java.security.Principal diff --git a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/WalletProxy.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/WalletProxy.kt similarity index 55% rename from Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/WalletProxy.kt rename to Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/WalletProxy.kt index dab7ea894..4ab3063b2 100644 --- a/Api/api-core/src/main/kotlin/co/nilin/mixchange/api/core/spi/WalletProxy.kt +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/WalletProxy.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.api.core.spi +package co.nilin.opex.api.core.spi -import co.nilin.mixchange.api.core.inout.OwnerLimitsResponse -import co.nilin.mixchange.api.core.inout.Wallet +import co.nilin.opex.api.core.inout.OwnerLimitsResponse +import co.nilin.opex.api.core.inout.Wallet interface WalletProxy { diff --git a/Api/api-ports/api-binance-rest/pom.xml b/Api/api-ports/api-binance-rest/pom.xml index b0952e230..b446d9769 100644 --- a/Api/api-ports/api-binance-rest/pom.xml +++ b/Api/api-ports/api-binance-rest/pom.xml @@ -8,7 +8,7 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex api-binance-rest 0.0.1-SNAPSHOT api-binance-rest @@ -24,13 +24,13 @@ - co.nilin.mixchange + co.nilin.opex matching-core ${matching.version} provided - co.nilin.mixchange + co.nilin.opex api-core ${api.version} provided diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/util/EnumExtensions.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/util/EnumExtensions.kt deleted file mode 100644 index e38ea4ffe..000000000 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/util/EnumExtensions.kt +++ /dev/null @@ -1,29 +0,0 @@ -package co.nilin.mixchange.port.api.binance.util - -import co.nilin.mixchange.api.core.inout.OrderSide -import co.nilin.mixchange.api.core.inout.TimeInForce -import co.nilin.mixchange.matching.core.model.MatchConstraint -import co.nilin.mixchange.matching.core.model.OrderType -import co.nilin.mixchange.matching.core.model.OrderDirection - -fun OrderSide.asOrderDirection(): OrderDirection { - if (this == OrderSide.BUY) - return OrderDirection.ASK - return OrderDirection.BID -} - -fun TimeInForce.asMatchConstraint(): MatchConstraint { - return when (this) { - TimeInForce.GTC -> MatchConstraint.GTC - TimeInForce.IOC -> MatchConstraint.IOC - TimeInForce.FOK -> MatchConstraint.FOK - } -} - -fun co.nilin.mixchange.api.core.inout.OrderType.asMatchingOrderType(): OrderType { - return when (this) { - co.nilin.mixchange.api.core.inout.OrderType.LIMIT -> OrderType.LIMIT_ORDER - co.nilin.mixchange.api.core.inout.OrderType.MARKET -> OrderType.MARKET_ORDER - else -> OrderType.LIMIT_ORDER - } -} \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/RestConfig.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/RestConfig.kt similarity index 91% rename from Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/RestConfig.kt rename to Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/RestConfig.kt index 63e5ec49b..cb717099f 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/RestConfig.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/RestConfig.kt @@ -1,11 +1,10 @@ -package co.nilin.mixchange.port.api.binance.config +package co.nilin.opex.port.api.binance.config import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.format.Formatter import java.util.* - @Configuration class RestConfig { @Bean diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/SecurityConfig.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt similarity index 66% rename from Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/SecurityConfig.kt rename to Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt index fb383b506..16a25ce47 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/SecurityConfig.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.port.api.binance.config +package co.nilin.opex.port.api.binance.config -import co.nilin.mixchange.port.api.binance.security.AuthenticationConverter +import co.nilin.opex.port.api.binance.security.AuthenticationConverter import org.springframework.context.annotation.Bean import org.springframework.core.io.ClassPathResource import org.springframework.core.io.Resource @@ -20,15 +20,15 @@ class SecurityConfig { @Bean fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain? { http.csrf().disable() - .authorizeExchange() - .pathMatchers("/hello").permitAll() - .pathMatchers("/actuator/**").permitAll() - .pathMatchers("/**").hasAuthority("SCOPE_trust") - .anyExchange().authenticated() - .and() - .oauth2ResourceServer() - .jwt() - .jwtAuthenticationConverter(AuthenticationConverter()) + .authorizeExchange() + .pathMatchers("/hello").permitAll() + .pathMatchers("/actuator/**").permitAll() + .pathMatchers("/**").hasAuthority("SCOPE_trust") + .anyExchange().authenticated() + .and() + .oauth2ResourceServer() + .jwt() + .jwtAuthenticationConverter(AuthenticationConverter()) return http.build() } @@ -37,9 +37,9 @@ class SecurityConfig { fun reactiveJwtDecoder(): ReactiveJwtDecoder? { val resource: Resource = ClassPathResource("/public.cert") val publicKey = String(FileCopyUtils.copyToByteArray(resource.inputStream)) - .replace("\r", "") - .replace("-----BEGIN PUBLIC KEY-----\n", "") - .replace("\n-----END PUBLIC KEY-----", "") + .replace("\r", "") + .replace("-----BEGIN PUBLIC KEY-----\n", "") + .replace("\n-----END PUBLIC KEY-----", "") val spec = X509EncodedKeySpec(Base64Utils.decodeFromString(publicKey)) val kf = KeyFactory.getInstance("RSA") return NimbusReactiveJwtDecoder(kf.generatePublic(spec) as RSAPublicKey) diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/WebClientConfig.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/WebClientConfig.kt similarity index 71% rename from Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/WebClientConfig.kt rename to Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/WebClientConfig.kt index 76e029a40..6f230375d 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/config/WebClientConfig.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/WebClientConfig.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.port.api.binance.config +package co.nilin.opex.port.api.binance.config import org.springframework.cloud.client.ServiceInstance import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties @@ -14,12 +14,12 @@ class WebClientConfig { @Bean fun webClient(loadBalancerFactory: ReactiveLoadBalancer.Factory): WebClient { return WebClient.builder() - .filter( - ReactorLoadBalancerExchangeFilterFunction( - loadBalancerFactory, LoadBalancerProperties(), emptyList() - ) + .filter( + ReactorLoadBalancerExchangeFilterFunction( + loadBalancerFactory, LoadBalancerProperties(), emptyList() ) - .build() + ) + .build() } } diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/AccountController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt similarity index 95% rename from Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/AccountController.kt rename to Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt index 71f1e5959..04265ba89 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/AccountController.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt @@ -1,15 +1,16 @@ -package co.nilin.mixchange.port.api.binance.controller +package co.nilin.opex.port.api.binance.controller -import co.nilin.mixchange.api.core.inout.* -import co.nilin.mixchange.api.core.spi.MEGatewayProxy -import co.nilin.mixchange.api.core.spi.UserQueryHandler -import co.nilin.mixchange.api.core.spi.WalletProxy -import co.nilin.mixchange.port.api.binance.data.AccountInfoResponse -import co.nilin.mixchange.port.api.binance.security.CustomAuthToken -import co.nilin.mixchange.port.api.binance.util.BalanceParser -import co.nilin.mixchange.port.api.binance.util.asMatchConstraint -import co.nilin.mixchange.port.api.binance.util.asMatchingOrderType -import co.nilin.mixchange.port.api.binance.util.asOrderDirection +import co.nilin.opex.api.core.inout.* +import co.nilin.opex.api.core.spi.MEGatewayProxy +import co.nilin.opex.api.core.spi.UserQueryHandler +import co.nilin.opex.api.core.spi.WalletProxy +import co.nilin.opex.port.api.binance.data.AccountInfoResponse +import co.nilin.opex.port.api.binance.security.CustomAuthToken +import co.nilin.opex.port.api.binance.util.BalanceParser +import co.nilin.opex.port.api.binance.util.asMatchConstraint +import co.nilin.opex.port.api.binance.util.asMatchingOrderType +import co.nilin.opex.port.api.binance.util.asOrderDirection +import co.nilin.opex.api.core.inout.* import com.fasterxml.jackson.annotation.JsonInclude import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/FiltersController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/FiltersController.kt similarity index 66% rename from Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/FiltersController.kt rename to Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/FiltersController.kt index f0b6dbd9d..19ef78033 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/FiltersController.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/FiltersController.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.port.api.binance.controller +package co.nilin.opex.port.api.binance.controller import org.springframework.web.bind.annotation.RestController diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/MarketController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt similarity index 66% rename from Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/MarketController.kt rename to Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt index 625179066..8afe2c0d3 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/MarketController.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.port.api.binance.controller +package co.nilin.opex.port.api.binance.controller import org.springframework.web.bind.annotation.RestController diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/data/AccountInfoResponse.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/AccountInfoResponse.kt similarity index 90% rename from Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/data/AccountInfoResponse.kt rename to Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/AccountInfoResponse.kt index eb565cf90..0ace90f4c 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/data/AccountInfoResponse.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/AccountInfoResponse.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.port.api.binance.data +package co.nilin.opex.port.api.binance.data import com.fasterxml.jackson.annotation.JsonInclude diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/data/BalanceResponse.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/BalanceResponse.kt similarity index 73% rename from Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/data/BalanceResponse.kt rename to Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/BalanceResponse.kt index 0094d936e..c81ef67b8 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/data/BalanceResponse.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/BalanceResponse.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.port.api.binance.data +package co.nilin.opex.port.api.binance.data import java.math.BigDecimal diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/proxy/MEGatewayProxyImpl.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/proxy/MEGatewayProxyImpl.kt similarity index 86% rename from Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/proxy/MEGatewayProxyImpl.kt rename to Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/proxy/MEGatewayProxyImpl.kt index f228bace8..6117db3f1 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/proxy/MEGatewayProxyImpl.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/proxy/MEGatewayProxyImpl.kt @@ -1,8 +1,8 @@ -package co.nilin.mixchange.port.api.binance.proxy +package co.nilin.opex.port.api.binance.proxy -import co.nilin.mixchange.api.core.inout.OrderSubmitResult -import co.nilin.mixchange.api.core.spi.MEGatewayProxy -import co.nilin.mixchange.port.api.binance.util.LoggerDelegate +import co.nilin.opex.api.core.inout.OrderSubmitResult +import co.nilin.opex.api.core.spi.MEGatewayProxy +import co.nilin.opex.port.api.binance.util.LoggerDelegate import kotlinx.coroutines.reactive.awaitSingleOrNull import org.springframework.beans.factory.annotation.Value import org.springframework.core.ParameterizedTypeReference diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/proxy/WalletProxyImpl.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/proxy/WalletProxyImpl.kt similarity index 86% rename from Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/proxy/WalletProxyImpl.kt rename to Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/proxy/WalletProxyImpl.kt index a371d9c53..df0b037dc 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/proxy/WalletProxyImpl.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/proxy/WalletProxyImpl.kt @@ -1,9 +1,9 @@ -package co.nilin.mixchange.port.api.binance.proxy +package co.nilin.opex.port.api.binance.proxy -import co.nilin.mixchange.api.core.inout.OwnerLimitsResponse -import co.nilin.mixchange.api.core.inout.Wallet -import co.nilin.mixchange.api.core.spi.WalletProxy -import co.nilin.mixchange.port.api.binance.util.LoggerDelegate +import co.nilin.opex.api.core.inout.OwnerLimitsResponse +import co.nilin.opex.api.core.inout.Wallet +import co.nilin.opex.api.core.spi.WalletProxy +import co.nilin.opex.port.api.binance.util.LoggerDelegate import kotlinx.coroutines.reactive.awaitSingle import org.springframework.beans.factory.annotation.Value import org.springframework.core.ParameterizedTypeReference diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/security/AuthenticationConverter.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/security/AuthenticationConverter.kt similarity index 95% rename from Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/security/AuthenticationConverter.kt rename to Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/security/AuthenticationConverter.kt index 6b8ecf67b..e7776e4ca 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/security/AuthenticationConverter.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/security/AuthenticationConverter.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.port.api.binance.security +package co.nilin.opex.port.api.binance.security import org.springframework.core.convert.converter.Converter import org.springframework.security.authentication.AbstractAuthenticationToken diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/security/CustomAuthToken.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/security/CustomAuthToken.kt similarity index 91% rename from Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/security/CustomAuthToken.kt rename to Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/security/CustomAuthToken.kt index f5acd1c88..687997c01 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/security/CustomAuthToken.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/security/CustomAuthToken.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.port.api.binance.security +package co.nilin.opex.port.api.binance.security import org.springframework.security.authentication.AbstractAuthenticationToken import org.springframework.security.core.GrantedAuthority diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/util/BalanceParser.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/util/BalanceParser.kt similarity index 81% rename from Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/util/BalanceParser.kt rename to Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/util/BalanceParser.kt index 099ba4987..8e91d8dc1 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/util/BalanceParser.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/util/BalanceParser.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.port.api.binance.util +package co.nilin.opex.port.api.binance.util -import co.nilin.mixchange.api.core.inout.Wallet -import co.nilin.mixchange.port.api.binance.data.BalanceResponse +import co.nilin.opex.api.core.inout.Wallet +import co.nilin.opex.port.api.binance.data.BalanceResponse import java.math.BigDecimal object BalanceParser { diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/util/EnumExtensions.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/util/EnumExtensions.kt new file mode 100644 index 000000000..68c8a0ba7 --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/util/EnumExtensions.kt @@ -0,0 +1,29 @@ +package co.nilin.opex.port.api.binance.util + +import co.nilin.opex.api.core.inout.OrderSide +import co.nilin.opex.api.core.inout.TimeInForce +import co.nilin.opex.matching.core.model.MatchConstraint +import co.nilin.opex.matching.core.model.OrderType +import co.nilin.opex.matching.core.model.OrderDirection + +fun OrderSide.asOrderDirection(): OrderDirection { + if (this == OrderSide.BUY) + return OrderDirection.ASK + return OrderDirection.BID +} + +fun TimeInForce.asMatchConstraint(): MatchConstraint { + return when (this) { + TimeInForce.GTC -> MatchConstraint.GTC + TimeInForce.IOC -> MatchConstraint.IOC + TimeInForce.FOK -> MatchConstraint.FOK + } +} + +fun co.nilin.opex.api.core.inout.OrderType.asMatchingOrderType(): OrderType { + return when (this) { + co.nilin.opex.api.core.inout.OrderType.LIMIT -> OrderType.LIMIT_ORDER + co.nilin.opex.api.core.inout.OrderType.MARKET -> OrderType.MARKET_ORDER + else -> OrderType.LIMIT_ORDER + } +} \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/util/LoggerDelegate.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/util/LoggerDelegate.kt similarity index 92% rename from Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/util/LoggerDelegate.kt rename to Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/util/LoggerDelegate.kt index cd6645cde..4f64aa4d2 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/util/LoggerDelegate.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/util/LoggerDelegate.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.port.api.binance.util +package co.nilin.opex.port.api.binance.util import org.slf4j.Logger import org.slf4j.LoggerFactory diff --git a/Api/api-ports/api-eventlistener-kafka/pom.xml b/Api/api-ports/api-eventlistener-kafka/pom.xml index 36ac78c3c..3ece6c8c9 100644 --- a/Api/api-ports/api-eventlistener-kafka/pom.xml +++ b/Api/api-ports/api-eventlistener-kafka/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex api-eventlistener-kafka 0.0.1-SNAPSHOT api-eventlistener-kafka - Api kafka listener of Mixchange + Api kafka listener of Opex 1.8 @@ -32,19 +32,19 @@ spring-boot-starter-webflux - co.nilin.mixchange + co.nilin.opex matching-core ${matching.version} provided - co.nilin.mixchange + co.nilin.opex accountant-core ${accountant.version} provided - co.nilin.mixchange + co.nilin.opex api-core ${api.version} provided diff --git a/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/config/ApiKafkaConfig.kt b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/api/kafka/config/ApiKafkaConfig.kt similarity index 92% rename from Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/config/ApiKafkaConfig.kt rename to Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/api/kafka/config/ApiKafkaConfig.kt index 634c18db7..5269d6acf 100644 --- a/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/config/ApiKafkaConfig.kt +++ b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/api/kafka/config/ApiKafkaConfig.kt @@ -1,10 +1,9 @@ -package co.nilin.mixchange.port.api.kafka.config +package co.nilin.opex.port.api.kafka.config - -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent -import co.nilin.mixchange.port.api.kafka.consumer.OrderKafkaListener -import co.nilin.mixchange.port.trade.consumer.EventKafkaListener -import co.nilin.mixchange.port.trade.consumer.TradeKafkaListener +import co.nilin.opex.matching.core.eventh.events.CoreEvent +import co.nilin.opex.port.api.kafka.consumer.OrderKafkaListener +import co.nilin.opex.port.api.kafka.consumer.EventKafkaListener +import co.nilin.opex.port.api.kafka.consumer.TradeKafkaListener import org.apache.kafka.clients.admin.NewTopic import org.apache.kafka.clients.consumer.ConsumerConfig import org.apache.kafka.clients.producer.ProducerConfig @@ -24,7 +23,6 @@ import org.springframework.kafka.support.serializer.JsonDeserializer import org.springframework.kafka.support.serializer.JsonSerializer import java.util.regex.Pattern - @Configuration class ApiKafkaConfig { @Value("\${spring.kafka.bootstrap-servers}") @@ -40,7 +38,7 @@ class ApiKafkaConfig { props[ConsumerConfig.GROUP_ID_CONFIG] = groupId props[ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG] = StringDeserializer::class.java props[ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG] = JsonDeserializer::class.java - props[JsonDeserializer.TRUSTED_PACKAGES] = "co.nilin.mixchange.*" + props[JsonDeserializer.TRUSTED_PACKAGES] = "co.nilin.opex.*" return props } diff --git a/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/consumer/EventKafkaListener.kt b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/api/kafka/consumer/EventKafkaListener.kt similarity index 81% rename from Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/consumer/EventKafkaListener.kt rename to Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/api/kafka/consumer/EventKafkaListener.kt index a2dbd173f..a5aafc0e0 100644 --- a/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/consumer/EventKafkaListener.kt +++ b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/api/kafka/consumer/EventKafkaListener.kt @@ -1,8 +1,8 @@ -package co.nilin.mixchange.port.trade.consumer +package co.nilin.opex.port.api.kafka.consumer -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent -import co.nilin.mixchange.port.trade.spi.EventListener +import co.nilin.opex.matching.core.eventh.events.CoreEvent +import co.nilin.opex.port.api.kafka.spi.EventListener import org.apache.kafka.clients.consumer.ConsumerRecord import org.springframework.kafka.listener.MessageListener import org.springframework.stereotype.Component diff --git a/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/consumer/OrderKafkaListener.kt b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/api/kafka/consumer/OrderKafkaListener.kt similarity index 73% rename from Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/consumer/OrderKafkaListener.kt rename to Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/api/kafka/consumer/OrderKafkaListener.kt index a8e8a64d2..6fbe90511 100644 --- a/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/consumer/OrderKafkaListener.kt +++ b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/api/kafka/consumer/OrderKafkaListener.kt @@ -1,14 +1,13 @@ -package co.nilin.mixchange.port.api.kafka.consumer +package co.nilin.opex.port.api.kafka.consumer -import co.nilin.mixchange.accountant.core.inout.RichOrder -import co.nilin.mixchange.port.api.kafka.spi.RichOrderListener +import co.nilin.opex.accountant.core.inout.RichOrder +import co.nilin.opex.port.api.kafka.spi.RichOrderListener import org.apache.kafka.clients.consumer.ConsumerRecord import org.springframework.kafka.listener.MessageListener import org.springframework.stereotype.Component @Component -class OrderKafkaListener() : - MessageListener { +class OrderKafkaListener : MessageListener { val orderListeners = arrayListOf() override fun onMessage(data: ConsumerRecord) { orderListeners.forEach { tl -> diff --git a/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/consumer/TradeKafkaListener.kt b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/api/kafka/consumer/TradeKafkaListener.kt similarity index 81% rename from Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/consumer/TradeKafkaListener.kt rename to Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/api/kafka/consumer/TradeKafkaListener.kt index 51f8b8b1b..80a798b2d 100644 --- a/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/consumer/TradeKafkaListener.kt +++ b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/api/kafka/consumer/TradeKafkaListener.kt @@ -1,8 +1,7 @@ -package co.nilin.mixchange.port.trade.consumer +package co.nilin.opex.port.api.kafka.consumer - -import co.nilin.mixchange.accountant.core.inout.RichTrade -import co.nilin.mixchange.port.trade.spi.RichTradeListener +import co.nilin.opex.accountant.core.inout.RichTrade +import co.nilin.opex.port.api.kafka.spi.RichTradeListener import org.apache.kafka.clients.consumer.ConsumerRecord import org.springframework.kafka.listener.MessageListener import org.springframework.stereotype.Component diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/EventListener.kt b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/api/kafka/spi/EventListener.kt similarity index 56% rename from Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/EventListener.kt rename to Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/api/kafka/spi/EventListener.kt index 0f522ca46..0d11ccf58 100644 --- a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/EventListener.kt +++ b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/api/kafka/spi/EventListener.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.port.trade.spi +package co.nilin.opex.port.api.kafka.spi -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +import co.nilin.opex.matching.core.eventh.events.CoreEvent interface EventListener { diff --git a/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/spi/RichOrderListener.kt b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/api/kafka/spi/RichOrderListener.kt similarity index 56% rename from Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/spi/RichOrderListener.kt rename to Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/api/kafka/spi/RichOrderListener.kt index c757f523e..75f759d06 100644 --- a/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/spi/RichOrderListener.kt +++ b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/api/kafka/spi/RichOrderListener.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.port.api.kafka.spi +package co.nilin.opex.port.api.kafka.spi -import co.nilin.mixchange.accountant.core.inout.RichOrder +import co.nilin.opex.accountant.core.inout.RichOrder interface RichOrderListener { fun id(): String diff --git a/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/spi/RichTradeListener.kt b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/api/kafka/spi/RichTradeListener.kt similarity index 57% rename from Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/spi/RichTradeListener.kt rename to Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/api/kafka/spi/RichTradeListener.kt index e8b490335..d81423890 100644 --- a/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/api/kafka/spi/RichTradeListener.kt +++ b/Api/api-ports/api-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/api/kafka/spi/RichTradeListener.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.port.trade.spi +package co.nilin.opex.port.api.kafka.spi -import co.nilin.mixchange.accountant.core.inout.RichTrade +import co.nilin.opex.accountant.core.inout.RichTrade interface RichTradeListener { fun id(): String diff --git a/Api/api-ports/api-persister-postgres/pom.xml b/Api/api-ports/api-persister-postgres/pom.xml index 44a605a22..413755120 100644 --- a/Api/api-ports/api-persister-postgres/pom.xml +++ b/Api/api-ports/api-persister-postgres/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex api-persister-postgres 0.0.1-SNAPSHOT api-persister-postgres - Persist items of Mixchange api on Postgres + Persist items of Opex api on Postgres 1.8 @@ -24,19 +24,19 @@ - co.nilin.mixchange + co.nilin.opex matching-core ${matching.version} provided - co.nilin.mixchange + co.nilin.opex api-core ${api.version} provided - co.nilin.mixchange + co.nilin.opex accountant-core ${accountant.version} provided diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/OrderPersisterImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/OrderPersisterImpl.kt deleted file mode 100644 index 09faa2c34..000000000 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/OrderPersisterImpl.kt +++ /dev/null @@ -1,53 +0,0 @@ -package co.nilin.mixchange.port.api.postgres.impl - -import co.nilin.mixchange.accountant.core.inout.OrderStatus -import co.nilin.mixchange.accountant.core.inout.RichOrder -import co.nilin.mixchange.api.core.spi.OrderPersister -import co.nilin.mixchange.port.api.postgres.dao.OrderRepository -import co.nilin.mixchange.port.api.postgres.model.OrderModel -import kotlinx.coroutines.reactive.awaitFirstOrNull -import org.springframework.stereotype.Component -import java.time.LocalDateTime - -@Component -class OrderPersisterImpl(val orderRepository: OrderRepository) : OrderPersister { - override suspend fun save(order: RichOrder) { - val existingOrder = orderRepository - .findByOuid(order.ouid) - .awaitFirstOrNull() - if (existingOrder == null - || existingOrder.executedQuantity?.compareTo(order.executedQuantity.toDouble()) == -1 - || order.status == OrderStatus.REJECTED.code - || order.status == OrderStatus.CANCELED.code - || order.status == OrderStatus.EXPIRED.code - || order.status == OrderStatus.FILLED.code - || existingOrder.type == null - ) - orderRepository.save( - OrderModel( - existingOrder?.id, - order.ouid, - order.uuid, - null, - order.pair, - order.orderId, - order.makerFee.toDouble(), - order.takerFee.toDouble(), - order.leftSideFraction.toDouble(), - order.rightSideFraction.toDouble(), - order.userLevel, - order.direction, - order.constraint, - order.type, - order.price.toDouble(), - order.quantity.toDouble(), - order.quoteQuantity.toDouble(), - order.executedQuantity.toDouble(), - order.accumulativeQuoteQty.toDouble(), - order.status, - existingOrder?.createDate ?: LocalDateTime.now(), - LocalDateTime.now() - ) - ).awaitFirstOrNull() - } -} \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/TradePersisterImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/TradePersisterImpl.kt deleted file mode 100644 index 7260d76b1..000000000 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/TradePersisterImpl.kt +++ /dev/null @@ -1,124 +0,0 @@ -package co.nilin.mixchange.port.api.postgres.impl - -import co.nilin.mixchange.accountant.core.inout.OrderStatus -import co.nilin.mixchange.accountant.core.inout.RichTrade -import co.nilin.mixchange.api.core.spi.TradePersister -import co.nilin.mixchange.port.api.postgres.dao.OrderRepository -import co.nilin.mixchange.port.api.postgres.dao.TradeRepository -import co.nilin.mixchange.port.api.postgres.model.OrderModel -import co.nilin.mixchange.port.api.postgres.model.TradeModel -import kotlinx.coroutines.reactive.awaitFirstOrNull -import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional -import java.math.BigDecimal -import java.time.LocalDateTime - -@Component -class TradePersisterImpl(val tradeRepository: TradeRepository, val orderRepository: OrderRepository) : TradePersister { - - @Transactional - override suspend fun save(trade: RichTrade) { - println("RichTrade save") - tradeRepository.save( - TradeModel( - null, - trade.id, - trade.pair, - trade.matchedQuantity.toDouble(), - trade.takerPrice.toDouble(), - trade.makerPrice.toDouble(), - trade.takerCommision.toDouble(), - trade.makerCommision.toDouble(), - trade.takerCommisionAsset, - trade.makerCommisionAsset, - trade.tradeDateTime, - trade.makerOuid, - trade.takerOuid, - trade.makerUuid, - trade.takerUuid, - LocalDateTime.now() - ) - ).awaitFirstOrNull() - println("RichTrade save/update maker order") - saveMakerOrder(trade) - println("RichTrade save/update taker order") - saveTakerOrder(trade) - - } - - private suspend fun saveTakerOrder(trade: RichTrade) { - val existingOrder = orderRepository - .findByOuid(trade.takerOuid) - .awaitFirstOrNull() - orderRepository.save( - OrderModel( - existingOrder?.id, - trade.takerOuid, - trade.takerUuid, - null, - trade.pair, - trade.takerOrderId, - existingOrder?.makerFee, - existingOrder?.takerFee, - existingOrder?.leftSideFraction, - existingOrder?.rightSideFraction, - existingOrder?.userLevel, - trade.takerDirection, - existingOrder?.constraint, - existingOrder?.type, - trade.takerPrice.toDouble(), - trade.takerQuantity.toDouble(), - trade.takerQuoteQuantity.toDouble(), - (trade.takerQuantity.minus(trade.takerRemainedQuantity)).toDouble(), - trade.takerPrice.multiply( - (trade.takerQuantity.minus(trade.takerRemainedQuantity)) - ).toDouble(), - if (trade.takerRemainedQuantity == BigDecimal.ZERO) { - OrderStatus.PARTIALLY_FILLED.code - } else { - OrderStatus.FILLED.code - }, - existingOrder?.createDate, - LocalDateTime.now() - ) - ).awaitFirstOrNull() - } - - private suspend fun saveMakerOrder(trade: RichTrade) { - val existingOrder = orderRepository - .findByOuid(trade.makerOuid) - .awaitFirstOrNull() - orderRepository.save( - OrderModel( - existingOrder?.id, - trade.makerOuid, - trade.makerUuid, - null, - trade.pair, - trade.makerOrderId, - existingOrder?.makerFee, - existingOrder?.takerFee, - existingOrder?.leftSideFraction, - existingOrder?.rightSideFraction, - existingOrder?.userLevel, - trade.makerDirection, - existingOrder?.constraint, - existingOrder?.type, - trade.makerPrice.toDouble(), - trade.makerQuantity.toDouble(), - trade.makerQuoteQuantity.toDouble(), - (trade.makerQuantity.minus(trade.makerRemainedQuantity)).toDouble(), - trade.makerPrice.multiply( - (trade.makerQuantity.minus(trade.makerRemainedQuantity)) - ).toDouble(), - if (trade.makerRemainedQuantity == BigDecimal.ZERO) { - OrderStatus.PARTIALLY_FILLED.code - } else { - OrderStatus.FILLED.code - }, - existingOrder?.createDate ?: LocalDateTime.now(), - LocalDateTime.now() - ) - ).awaitFirstOrNull() - } -} \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/UserQueryHandlerImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/UserQueryHandlerImpl.kt deleted file mode 100644 index cb94b7068..000000000 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/impl/UserQueryHandlerImpl.kt +++ /dev/null @@ -1,133 +0,0 @@ -package co.nilin.mixchange.port.api.postgres.impl - -import co.nilin.mixchange.api.core.inout.* -import co.nilin.mixchange.api.core.spi.UserQueryHandler -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.port.api.postgres.dao.OrderRepository -import co.nilin.mixchange.port.api.postgres.dao.TradeRepository -import co.nilin.mixchange.port.api.postgres.model.OrderModel -import co.nilin.mixchange.port.api.postgres.util.* -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.reactive.awaitFirst -import kotlinx.coroutines.reactive.awaitFirstOrNull -import org.springframework.stereotype.Component -import java.math.BigDecimal -import java.security.Principal -import java.time.ZoneId -import java.util.* - -@Component -class UserQueryHandlerImpl( - val orderRepository: OrderRepository, val tradeRepository: TradeRepository -) : UserQueryHandler { - override suspend fun queryOrder(principal: Principal, request: QueryOrderRequest): QueryOrderResponse? { - val order = (if (request.origClientOrderId != null) { - orderRepository.findBySymbolAndClientOrderId(request.symbol, request.origClientOrderId!!) - } else { - orderRepository.findBySymbolAndOrderId(request.symbol, request.orderId!!) - - }).awaitFirstOrNull() - if (order?.constraint != null) { - if (order.uuid != principal.name) - throw RuntimeException("Forbidden") - return orderToQueryResponse(order) - } - return null - } - - override suspend fun openOrders(principal: Principal, symbol: String?): Flow { - return orderRepository.findByUuidAndSymbolAndStatus( - principal.name, - symbol, - listOf(OrderStatus.NEW.ordinal, OrderStatus.PARTIALLY_FILLED.ordinal) - ).filter { orderModel -> orderModel.constraint != null } - .map { order -> orderToQueryResponse(order) } - } - - override suspend fun allOrders(principal: Principal, allOrderRequest: AllOrderRequest): Flow { - return orderRepository.findByUuidAndSymbolAndTimeBetween( - principal.name, - allOrderRequest.symbol, - allOrderRequest.startTime, - allOrderRequest.endTime - ).filter { orderModel -> orderModel.constraint != null } - .map { order -> orderToQueryResponse(order) } - } - - override suspend fun allTrades(principal: Principal, request: TradeRequest): Flow { - return tradeRepository.findByUuidAndSymbolAndTimeBetweenAndTradeIdGreaterThan( - principal.name, request.symbol, request.fromTrade, request.startTime, request.endTime - ).map { trade -> - val takerOrder = orderRepository.findByOuid(trade.takerOuid).awaitFirst() - val makerOrder = orderRepository.findByOuid(trade.makerOuid).awaitFirst() - val isMakerBuyer = makerOrder.direction == OrderDirection.ASK - TradeResponse( - trade.symbol, - trade.tradeId, - if (trade.takerUuid == principal.name) { - takerOrder.orderId!! - } else { - makerOrder.orderId!! - }, - -1, - if (trade.takerUuid == principal.name) { - trade.takerPrice.toBigDecimal() - } else { - trade.makerPrice.toBigDecimal() - }, - trade.matchedQuantity.toBigDecimal(), - if (isMakerBuyer) { - makerOrder.quoteQuantity!!.toBigDecimal() - } else { - takerOrder.quoteQuantity!!.toBigDecimal() - }, - if (trade.takerUuid == principal.name) { - trade.takerCommision!!.toBigDecimal() - } else { - trade.makerCommision!!.toBigDecimal() - }, - if (trade.takerUuid == principal.name) { - trade.takerCommisionAsset!! - } else { - trade.makerCommisionAsset!! - }, - Date.from( - trade.createDate.atZone(ZoneId.systemDefault()).toInstant() - ), - if (trade.takerUuid == principal.name) { - OrderDirection.ASK == takerOrder.direction - } else { - OrderDirection.ASK == makerOrder.direction - }, - trade.makerUuid == principal.name, - true - ) - } - } - - - private fun orderToQueryResponse(order: OrderModel) = QueryOrderResponse( - order.symbol, - order.orderId ?: -1, - -1, - order.clientOrderId ?: "", - BigDecimal(order.price!!), - BigDecimal(order.quantity!!), - BigDecimal(order.executedQuantity!!), - BigDecimal(order.accumulativeQuoteQty ?: 0.0), - order.status!!.toOrderStatus(), - order.constraint!!.toTimeInForce(), - order.type!!.toApiOrderType(), - order.direction!!.toOrderSide(), - null, - null, - Date.from( - order.createDate!!.atZone(ZoneId.systemDefault()).toInstant() - ), - Date.from( - order.updateDate.atZone(ZoneId.systemDefault()).toInstant() - ), order.status.toOrderStatus().isWorking(), order.quoteQuantity!!.toBigDecimal() - ) -} \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/model/OrderModel.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/model/OrderModel.kt deleted file mode 100644 index 28b7b7be9..000000000 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/model/OrderModel.kt +++ /dev/null @@ -1,38 +0,0 @@ -package co.nilin.mixchange.port.api.postgres.model - - -import co.nilin.mixchange.matching.core.model.MatchConstraint -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.OrderType -import org.springframework.data.annotation.Id -import org.springframework.data.relational.core.mapping.Column -import org.springframework.data.relational.core.mapping.Table -import java.time.LocalDateTime - -@Table("orders") -class OrderModel( - @Id var id: Long?, - @Column(value = "ouid") - val ouid: String, - val uuid: String, - @Column(value = "client_order_id") - val clientOrderId: String?, - val symbol: String, - @Column(value = "order_id") val orderId: Long?, - @Column("maker_fee") val makerFee: Double?, - @Column("taker_fee") val takerFee: Double?, - @Column("left_side_fraction") val leftSideFraction: Double?, - @Column("right_side_fraction") val rightSideFraction: Double?, - @Column("user_level") val userLevel: String?, - @Column("side") val direction: OrderDirection?, - @Column("match_constraint") val constraint: MatchConstraint?, - @Column("order_type") val type: OrderType?, - @Column("price") val price: Double?, - @Column("quantity") val quantity: Double?, - @Column("quote_quantity") val quoteQuantity: Double?, - @Column("executed_qty") val executedQuantity: Double?, - @Column("accumulative_quote_qty") val accumulativeQuoteQty: Double?, - @Column("status") val status: Int?, - @Column("create_date") val createDate: LocalDateTime?, - @Column("update_date") val updateDate: LocalDateTime -) \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/model/TradeModel.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/model/TradeModel.kt deleted file mode 100644 index 3b6b1eef8..000000000 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/model/TradeModel.kt +++ /dev/null @@ -1,27 +0,0 @@ -package co.nilin.mixchange.port.api.postgres.model - - -import org.springframework.data.annotation.Id -import org.springframework.data.relational.core.mapping.Column -import org.springframework.data.relational.core.mapping.Table -import java.time.LocalDateTime - -@Table("trades") -class TradeModel( - @Id var id: Long?, - @Column("trade_id") val tradeId: Long, - val symbol: String, - @Column("matched_quantity") val matchedQuantity: Double, - @Column("taker_price") val takerPrice: Double, - @Column("maker_price") val makerPrice: Double, - @Column("taker_commision") val takerCommision: Double?, - @Column("maker_commision") val makerCommision: Double?, - @Column("taker_commision_asset") val takerCommisionAsset: String?, - @Column("maker_commision_asset") val makerCommisionAsset: String?, - @Column("trade_date") val tradeDate: LocalDateTime, - @Column("maker_ouid") val makerOuid: String, - @Column("taker_ouid") val takerOuid: String, - @Column("maker_uuid") val makerUuid: String, - @Column("taker_uuid") val takerUuid: String, - @Column("create_date") val createDate: LocalDateTime -) \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/config/PostgresConfig.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/config/PostgresConfig.kt similarity index 94% rename from Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/config/PostgresConfig.kt rename to Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/config/PostgresConfig.kt index db30f4ae8..bc72c1ad7 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/config/PostgresConfig.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/config/PostgresConfig.kt @@ -1,13 +1,11 @@ -package co.nilin.mixchange.port.order.kafka.config - +package co.nilin.opex.port.api.postgres.config import org.springframework.context.annotation.Configuration import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories import org.springframework.r2dbc.core.DatabaseClient - @Configuration -@EnableR2dbcRepositories(basePackages = ["co.nilin.mixchange"]) +@EnableR2dbcRepositories(basePackages = ["co.nilin.opex"]) class PostgresConfig(db: DatabaseClient) { init { diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/dao/OrderRepository.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt similarity index 52% rename from Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/dao/OrderRepository.kt rename to Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt index c55efd2c0..54a10c23d 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/dao/OrderRepository.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.port.api.postgres.dao +package co.nilin.opex.port.api.postgres.dao -import co.nilin.mixchange.port.api.postgres.model.OrderModel +import co.nilin.opex.port.api.postgres.model.OrderModel import kotlinx.coroutines.flow.Flow import org.springframework.data.r2dbc.repository.Query import org.springframework.data.repository.query.Param @@ -16,40 +16,41 @@ interface OrderRepository : ReactiveCrudRepository { @Query("select * from orders where symbol = :symbol and order_id = :orderId") fun findBySymbolAndOrderId( - @Param("symbol") - symbol: String, @Param("orderId") - orderId: Long + @Param("symbol") + symbol: String, @Param("orderId") + orderId: Long ): Mono @Query("select * from orders where symbol = :symbol and client_order_id = :origClientOrderId") fun findBySymbolAndClientOrderId( - @Param("symbol") - symbol: String, @Param("origClientOrderId") - origClientOrderId: String + @Param("symbol") + symbol: String, @Param("origClientOrderId") + origClientOrderId: String ): Mono @Query("select * from orders where uuid = :uuid and (:symbol is null or symbol = :symbol) and status in (:statuses)") fun findByUuidAndSymbolAndStatus( - @Param("uuid") - uuid: String, - @Param("symbol") - symbol: String?, @Param("statuses") - status: Collection + @Param("uuid") + uuid: String, + @Param("symbol") + symbol: String?, @Param("statuses") + status: Collection ): Flow - @Query("select * from orders where uuid = :uuid " + - "and (:symbol is null or symbol = :symbol) " + - "and (:startTime is null or update_date >= :startTime)" + - "and (:endTime is null or update_date < :endTime)" + @Query( + "select * from orders where uuid = :uuid " + + "and (:symbol is null or symbol = :symbol) " + + "and (:startTime is null or update_date >= :startTime)" + + "and (:endTime is null or update_date < :endTime)" ) fun findByUuidAndSymbolAndTimeBetween( - @Param("uuid") - uuid: String, - @Param("symbol") - symbol: String?, - @Param("startTime") - startTime: Date?, - @Param("endTime") - endTime: Date? + @Param("uuid") + uuid: String, + @Param("symbol") + symbol: String?, + @Param("startTime") + startTime: Date?, + @Param("endTime") + endTime: Date? ): Flow } \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/dao/TradeRepository.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/TradeRepository.kt similarity index 91% rename from Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/dao/TradeRepository.kt rename to Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/TradeRepository.kt index 921e1d7a4..58d9ab037 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/dao/TradeRepository.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/TradeRepository.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.port.api.postgres.dao +package co.nilin.opex.port.api.postgres.dao -import co.nilin.mixchange.port.api.postgres.model.TradeModel +import co.nilin.opex.port.api.postgres.model.TradeModel import kotlinx.coroutines.flow.Flow import org.springframework.data.r2dbc.repository.Query import org.springframework.data.repository.query.Param diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/OrderPersisterImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/OrderPersisterImpl.kt new file mode 100644 index 000000000..0241c16fc --- /dev/null +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/OrderPersisterImpl.kt @@ -0,0 +1,53 @@ +package co.nilin.opex.port.api.postgres.impl + +import co.nilin.opex.accountant.core.inout.OrderStatus +import co.nilin.opex.accountant.core.inout.RichOrder +import co.nilin.opex.api.core.spi.OrderPersister +import co.nilin.opex.port.api.postgres.dao.OrderRepository +import co.nilin.opex.port.api.postgres.model.OrderModel +import kotlinx.coroutines.reactive.awaitFirstOrNull +import org.springframework.stereotype.Component +import java.time.LocalDateTime + +@Component +class OrderPersisterImpl(val orderRepository: OrderRepository) : OrderPersister { + override suspend fun save(order: RichOrder) { + val existingOrder = orderRepository + .findByOuid(order.ouid) + .awaitFirstOrNull() + if (existingOrder == null + || existingOrder.executedQuantity?.compareTo(order.executedQuantity.toDouble()) == -1 + || order.status == OrderStatus.REJECTED.code + || order.status == OrderStatus.CANCELED.code + || order.status == OrderStatus.EXPIRED.code + || order.status == OrderStatus.FILLED.code + || existingOrder.type == null + ) + orderRepository.save( + OrderModel( + existingOrder?.id, + order.ouid, + order.uuid, + null, + order.pair, + order.orderId, + order.makerFee.toDouble(), + order.takerFee.toDouble(), + order.leftSideFraction.toDouble(), + order.rightSideFraction.toDouble(), + order.userLevel, + order.direction, + order.constraint, + order.type, + order.price.toDouble(), + order.quantity.toDouble(), + order.quoteQuantity.toDouble(), + order.executedQuantity.toDouble(), + order.accumulativeQuoteQty.toDouble(), + order.status, + existingOrder?.createDate ?: LocalDateTime.now(), + LocalDateTime.now() + ) + ).awaitFirstOrNull() + } +} \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/TradePersisterImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/TradePersisterImpl.kt new file mode 100644 index 000000000..35e030d2e --- /dev/null +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/TradePersisterImpl.kt @@ -0,0 +1,124 @@ +package co.nilin.opex.port.api.postgres.impl + +import co.nilin.opex.accountant.core.inout.OrderStatus +import co.nilin.opex.accountant.core.inout.RichTrade +import co.nilin.opex.api.core.spi.TradePersister +import co.nilin.opex.port.api.postgres.dao.OrderRepository +import co.nilin.opex.port.api.postgres.dao.TradeRepository +import co.nilin.opex.port.api.postgres.model.OrderModel +import co.nilin.opex.port.api.postgres.model.TradeModel +import kotlinx.coroutines.reactive.awaitFirstOrNull +import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional +import java.math.BigDecimal +import java.time.LocalDateTime + +@Component +class TradePersisterImpl(val tradeRepository: TradeRepository, val orderRepository: OrderRepository) : TradePersister { + + @Transactional + override suspend fun save(trade: RichTrade) { + println("RichTrade save") + tradeRepository.save( + TradeModel( + null, + trade.id, + trade.pair, + trade.matchedQuantity.toDouble(), + trade.takerPrice.toDouble(), + trade.makerPrice.toDouble(), + trade.takerCommision.toDouble(), + trade.makerCommision.toDouble(), + trade.takerCommisionAsset, + trade.makerCommisionAsset, + trade.tradeDateTime, + trade.makerOuid, + trade.takerOuid, + trade.makerUuid, + trade.takerUuid, + LocalDateTime.now() + ) + ).awaitFirstOrNull() + println("RichTrade save/update maker order") + saveMakerOrder(trade) + println("RichTrade save/update taker order") + saveTakerOrder(trade) + + } + + private suspend fun saveTakerOrder(trade: RichTrade) { + val existingOrder = orderRepository + .findByOuid(trade.takerOuid) + .awaitFirstOrNull() + orderRepository.save( + OrderModel( + existingOrder?.id, + trade.takerOuid, + trade.takerUuid, + null, + trade.pair, + trade.takerOrderId, + existingOrder?.makerFee, + existingOrder?.takerFee, + existingOrder?.leftSideFraction, + existingOrder?.rightSideFraction, + existingOrder?.userLevel, + trade.takerDirection, + existingOrder?.constraint, + existingOrder?.type, + trade.takerPrice.toDouble(), + trade.takerQuantity.toDouble(), + trade.takerQuoteQuantity.toDouble(), + (trade.takerQuantity.minus(trade.takerRemainedQuantity)).toDouble(), + trade.takerPrice.multiply( + (trade.takerQuantity.minus(trade.takerRemainedQuantity)) + ).toDouble(), + if (trade.takerRemainedQuantity == BigDecimal.ZERO) { + OrderStatus.PARTIALLY_FILLED.code + } else { + OrderStatus.FILLED.code + }, + existingOrder?.createDate, + LocalDateTime.now() + ) + ).awaitFirstOrNull() + } + + private suspend fun saveMakerOrder(trade: RichTrade) { + val existingOrder = orderRepository + .findByOuid(trade.makerOuid) + .awaitFirstOrNull() + orderRepository.save( + OrderModel( + existingOrder?.id, + trade.makerOuid, + trade.makerUuid, + null, + trade.pair, + trade.makerOrderId, + existingOrder?.makerFee, + existingOrder?.takerFee, + existingOrder?.leftSideFraction, + existingOrder?.rightSideFraction, + existingOrder?.userLevel, + trade.makerDirection, + existingOrder?.constraint, + existingOrder?.type, + trade.makerPrice.toDouble(), + trade.makerQuantity.toDouble(), + trade.makerQuoteQuantity.toDouble(), + (trade.makerQuantity.minus(trade.makerRemainedQuantity)).toDouble(), + trade.makerPrice.multiply( + (trade.makerQuantity.minus(trade.makerRemainedQuantity)) + ).toDouble(), + if (trade.makerRemainedQuantity == BigDecimal.ZERO) { + OrderStatus.PARTIALLY_FILLED.code + } else { + OrderStatus.FILLED.code + }, + existingOrder?.createDate ?: LocalDateTime.now(), + LocalDateTime.now() + ) + ).awaitFirstOrNull() + } +} \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt new file mode 100644 index 000000000..636de382f --- /dev/null +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt @@ -0,0 +1,136 @@ +package co.nilin.opex.port.api.postgres.impl + +import co.nilin.opex.api.core.inout.* +import co.nilin.opex.api.core.spi.UserQueryHandler +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.port.api.postgres.dao.OrderRepository +import co.nilin.opex.port.api.postgres.dao.TradeRepository +import co.nilin.opex.port.api.postgres.model.OrderModel +import co.nilin.opex.port.api.postgres.util.* +import co.nilin.opex.api.core.inout.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.reactive.awaitFirst +import kotlinx.coroutines.reactive.awaitFirstOrNull +import org.springframework.stereotype.Component +import java.math.BigDecimal +import java.security.Principal +import java.time.ZoneId +import java.util.* + +@Component +class UserQueryHandlerImpl( + val orderRepository: OrderRepository, + val tradeRepository: TradeRepository +) : UserQueryHandler { + + override suspend fun queryOrder(principal: Principal, request: QueryOrderRequest): QueryOrderResponse? { + val order = (if (request.origClientOrderId != null) { + orderRepository.findBySymbolAndClientOrderId(request.symbol, request.origClientOrderId!!) + } else { + orderRepository.findBySymbolAndOrderId(request.symbol, request.orderId!!) + + }).awaitFirstOrNull() + if (order?.constraint != null) { + if (order.uuid != principal.name) + throw RuntimeException("Forbidden") + return orderToQueryResponse(order) + } + return null + } + + override suspend fun openOrders(principal: Principal, symbol: String?): Flow { + return orderRepository.findByUuidAndSymbolAndStatus( + principal.name, + symbol, + listOf(OrderStatus.NEW.ordinal, OrderStatus.PARTIALLY_FILLED.ordinal) + ).filter { orderModel -> orderModel.constraint != null } + .map { order -> orderToQueryResponse(order) } + } + + override suspend fun allOrders(principal: Principal, allOrderRequest: AllOrderRequest): Flow { + return orderRepository.findByUuidAndSymbolAndTimeBetween( + principal.name, + allOrderRequest.symbol, + allOrderRequest.startTime, + allOrderRequest.endTime + ).filter { orderModel -> orderModel.constraint != null } + .map { order -> orderToQueryResponse(order) } + } + + override suspend fun allTrades(principal: Principal, request: TradeRequest): Flow { + return tradeRepository.findByUuidAndSymbolAndTimeBetweenAndTradeIdGreaterThan( + principal.name, request.symbol, request.fromTrade, request.startTime, request.endTime + ).map { trade -> + val takerOrder = orderRepository.findByOuid(trade.takerOuid).awaitFirst() + val makerOrder = orderRepository.findByOuid(trade.makerOuid).awaitFirst() + val isMakerBuyer = makerOrder.direction == OrderDirection.ASK + TradeResponse( + trade.symbol, + trade.tradeId, + if (trade.takerUuid == principal.name) { + takerOrder.orderId!! + } else { + makerOrder.orderId!! + }, + -1, + if (trade.takerUuid == principal.name) { + trade.takerPrice.toBigDecimal() + } else { + trade.makerPrice.toBigDecimal() + }, + trade.matchedQuantity.toBigDecimal(), + if (isMakerBuyer) { + makerOrder.quoteQuantity!!.toBigDecimal() + } else { + takerOrder.quoteQuantity!!.toBigDecimal() + }, + if (trade.takerUuid == principal.name) { + trade.takerCommision!!.toBigDecimal() + } else { + trade.makerCommision!!.toBigDecimal() + }, + if (trade.takerUuid == principal.name) { + trade.takerCommisionAsset!! + } else { + trade.makerCommisionAsset!! + }, + Date.from( + trade.createDate.atZone(ZoneId.systemDefault()).toInstant() + ), + if (trade.takerUuid == principal.name) { + OrderDirection.ASK == takerOrder.direction + } else { + OrderDirection.ASK == makerOrder.direction + }, + trade.makerUuid == principal.name, + true + ) + } + } + + + private fun orderToQueryResponse(order: OrderModel) = QueryOrderResponse( + order.symbol, + order.orderId ?: -1, + -1, + order.clientOrderId ?: "", + BigDecimal(order.price!!), + BigDecimal(order.quantity!!), + BigDecimal(order.executedQuantity!!), + BigDecimal(order.accumulativeQuoteQty ?: 0.0), + order.status!!.toOrderStatus(), + order.constraint!!.toTimeInForce(), + order.type!!.toApiOrderType(), + order.direction!!.toOrderSide(), + null, + null, + Date.from( + order.createDate!!.atZone(ZoneId.systemDefault()).toInstant() + ), + Date.from( + order.updateDate.atZone(ZoneId.systemDefault()).toInstant() + ), order.status.toOrderStatus().isWorking(), order.quoteQuantity!!.toBigDecimal() + ) +} \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/OrderModel.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/OrderModel.kt new file mode 100644 index 000000000..291851ae8 --- /dev/null +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/OrderModel.kt @@ -0,0 +1,38 @@ +package co.nilin.opex.port.api.postgres.model + + +import co.nilin.opex.matching.core.model.MatchConstraint +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderType +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.time.LocalDateTime + +@Table("orders") +class OrderModel( + @Id var id: Long?, + @Column(value = "ouid") + val ouid: String, + val uuid: String, + @Column(value = "client_order_id") + val clientOrderId: String?, + val symbol: String, + @Column(value = "order_id") val orderId: Long?, + @Column("maker_fee") val makerFee: Double?, + @Column("taker_fee") val takerFee: Double?, + @Column("left_side_fraction") val leftSideFraction: Double?, + @Column("right_side_fraction") val rightSideFraction: Double?, + @Column("user_level") val userLevel: String?, + @Column("side") val direction: OrderDirection?, + @Column("match_constraint") val constraint: MatchConstraint?, + @Column("order_type") val type: OrderType?, + @Column("price") val price: Double?, + @Column("quantity") val quantity: Double?, + @Column("quote_quantity") val quoteQuantity: Double?, + @Column("executed_qty") val executedQuantity: Double?, + @Column("accumulative_quote_qty") val accumulativeQuoteQty: Double?, + @Column("status") val status: Int?, + @Column("create_date") val createDate: LocalDateTime?, + @Column("update_date") val updateDate: LocalDateTime +) \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/TradeModel.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/TradeModel.kt new file mode 100644 index 000000000..1fc1206cd --- /dev/null +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/TradeModel.kt @@ -0,0 +1,27 @@ +package co.nilin.opex.port.api.postgres.model + + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.time.LocalDateTime + +@Table("trades") +class TradeModel( + @Id var id: Long?, + @Column("trade_id") val tradeId: Long, + val symbol: String, + @Column("matched_quantity") val matchedQuantity: Double, + @Column("taker_price") val takerPrice: Double, + @Column("maker_price") val makerPrice: Double, + @Column("taker_commision") val takerCommision: Double?, + @Column("maker_commision") val makerCommision: Double?, + @Column("taker_commision_asset") val takerCommisionAsset: String?, + @Column("maker_commision_asset") val makerCommisionAsset: String?, + @Column("trade_date") val tradeDate: LocalDateTime, + @Column("maker_ouid") val makerOuid: String, + @Column("taker_ouid") val takerOuid: String, + @Column("maker_uuid") val makerUuid: String, + @Column("taker_uuid") val takerUuid: String, + @Column("create_date") val createDate: LocalDateTime +) \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/util/EnumExtensions.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/util/EnumExtensions.kt similarity index 54% rename from Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/util/EnumExtensions.kt rename to Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/util/EnumExtensions.kt index a8db1d1b2..9e1520e4d 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/api/postgres/util/EnumExtensions.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/util/EnumExtensions.kt @@ -1,11 +1,11 @@ -package co.nilin.mixchange.port.api.postgres.util +package co.nilin.opex.port.api.postgres.util -import co.nilin.mixchange.api.core.inout.OrderSide -import co.nilin.mixchange.api.core.inout.OrderStatus -import co.nilin.mixchange.api.core.inout.TimeInForce -import co.nilin.mixchange.matching.core.model.MatchConstraint -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.OrderType +import co.nilin.opex.api.core.inout.OrderSide +import co.nilin.opex.api.core.inout.OrderStatus +import co.nilin.opex.api.core.inout.TimeInForce +import co.nilin.opex.matching.core.model.MatchConstraint +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderType fun MatchConstraint.toTimeInForce(): TimeInForce { if (this == MatchConstraint.FOK_BUDGET) @@ -20,11 +20,11 @@ fun TimeInForce.toMatchConstraint(): MatchConstraint { return MatchConstraint.valueOf(this.name) } -fun OrderType.toApiOrderType(): co.nilin.mixchange.api.core.inout.OrderType { +fun OrderType.toApiOrderType(): co.nilin.opex.api.core.inout.OrderType { if (this == OrderType.LIMIT_ORDER) - return co.nilin.mixchange.api.core.inout.OrderType.LIMIT + return co.nilin.opex.api.core.inout.OrderType.LIMIT if (this == OrderType.MARKET_ORDER) - return co.nilin.mixchange.api.core.inout.OrderType.MARKET + return co.nilin.opex.api.core.inout.OrderType.MARKET throw IllegalArgumentException("OrderType $this is not supported!") } @@ -39,7 +39,7 @@ fun OrderStatus.isWorking(): Boolean { } fun Int.toOrderStatus(): OrderStatus { - val status = co.nilin.mixchange.accountant.core.inout.OrderStatus.values() - .find { s -> s.code == this } + val status = co.nilin.opex.accountant.core.inout.OrderStatus.values() + .find { s -> s.code == this } return OrderStatus.valueOf(status!!.name) } \ No newline at end of file diff --git a/Api/pom.xml b/Api/pom.xml index f37494325..6544168ba 100644 --- a/Api/pom.xml +++ b/Api/pom.xml @@ -2,12 +2,12 @@ 4.0.0 - co.nilin.mixchange + co.nilin.opex api-root 0.0.1-SNAPSHOT api-root pom - Api root of Mixchange + Api root of Opex api-core api-app diff --git a/EventLog/eventlog-app/pom.xml b/EventLog/eventlog-app/pom.xml index 1e59072b6..e42c46312 100644 --- a/EventLog/eventlog-app/pom.xml +++ b/EventLog/eventlog-app/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex eventlog-app 0.0.1-SNAPSHOT eventlog-app - Event log running app Mixchange + Event log running app Opex 1.8 @@ -43,22 +43,22 @@ spring-boot-starter-webflux - co.nilin.mixchange + co.nilin.opex matching-core ${matching.version} - co.nilin.mixchange + co.nilin.opex eventlog-core ${eventlog.version} - co.nilin.mixchange + co.nilin.opex eventlog-eventlistener-kafka ${eventlog.version} - co.nilin.mixchange + co.nilin.opex eventlog-persister-postgres ${eventlog.version} diff --git a/EventLog/eventlog-app/src/main/kotlin/co/nilin/mixchange/eventlog/app/EventLogApp.kt b/EventLog/eventlog-app/src/main/kotlin/co/nilin/opex/eventlog/app/EventLogApp.kt similarity index 80% rename from EventLog/eventlog-app/src/main/kotlin/co/nilin/mixchange/eventlog/app/EventLogApp.kt rename to EventLog/eventlog-app/src/main/kotlin/co/nilin/opex/eventlog/app/EventLogApp.kt index eeb579d8a..a42bd4d1f 100644 --- a/EventLog/eventlog-app/src/main/kotlin/co/nilin/mixchange/eventlog/app/EventLogApp.kt +++ b/EventLog/eventlog-app/src/main/kotlin/co/nilin/opex/eventlog/app/EventLogApp.kt @@ -1,11 +1,11 @@ -package co.nilin.mixchange.app +package co.nilin.opex.app import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.annotation.ComponentScan @SpringBootApplication -@ComponentScan("co.nilin.mixchange") +@ComponentScan("co.nilin.opex") class EventLogApp fun main(args: Array) { runApplication(*args) diff --git a/EventLog/eventlog-app/src/main/kotlin/co/nilin/mixchange/eventlog/app/config/AppConfig.kt b/EventLog/eventlog-app/src/main/kotlin/co/nilin/opex/eventlog/app/config/AppConfig.kt similarity index 83% rename from EventLog/eventlog-app/src/main/kotlin/co/nilin/mixchange/eventlog/app/config/AppConfig.kt rename to EventLog/eventlog-app/src/main/kotlin/co/nilin/opex/eventlog/app/config/AppConfig.kt index 4048aa997..19b2ffc19 100644 --- a/EventLog/eventlog-app/src/main/kotlin/co/nilin/mixchange/eventlog/app/config/AppConfig.kt +++ b/EventLog/eventlog-app/src/main/kotlin/co/nilin/opex/eventlog/app/config/AppConfig.kt @@ -1,16 +1,16 @@ -package co.nilin.mixchange.eventlog.app.config - -import co.nilin.mixchange.eventlog.spi.EventPersister -import co.nilin.mixchange.eventlog.spi.OrderPersister -import co.nilin.mixchange.eventlog.spi.TradePersister -import co.nilin.mixchange.matching.core.eventh.events.* -import co.nilin.mixchange.port.eventlog.kafka.consumer.OrderKafkaListener -import co.nilin.mixchange.port.eventlog.kafka.spi.OrderSubmitRequestListener -import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest -import co.nilin.mixchange.port.trade.consumer.EventKafkaListener -import co.nilin.mixchange.port.trade.consumer.TradeKafkaListener -import co.nilin.mixchange.port.trade.spi.EventListener -import co.nilin.mixchange.port.trade.spi.TradeListener +package co.nilin.opex.eventlog.app.config + +import co.nilin.opex.eventlog.spi.EventPersister +import co.nilin.opex.eventlog.spi.OrderPersister +import co.nilin.opex.eventlog.spi.TradePersister +import co.nilin.opex.matching.core.eventh.events.* +import co.nilin.opex.port.eventlog.kafka.consumer.OrderKafkaListener +import co.nilin.opex.port.eventlog.kafka.spi.OrderSubmitRequestListener +import co.nilin.opex.port.order.kafka.inout.OrderSubmitRequest +import co.nilin.opex.port.eventlog.kafka.consumer.EventKafkaListener +import co.nilin.opex.port.eventlog.kafka.consumer.TradeKafkaListener +import co.nilin.opex.port.eventlog.kafka.spi.EventListener +import co.nilin.opex.port.eventlog.kafka.spi.TradeListener import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.runBlocking import org.springframework.beans.factory.annotation.Autowired diff --git a/EventLog/eventlog-core/pom.xml b/EventLog/eventlog-core/pom.xml index 18abeb652..4c208e517 100644 --- a/EventLog/eventlog-core/pom.xml +++ b/EventLog/eventlog-core/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex eventlog-core 0.0.1-SNAPSHOT eventlog-core - Event log of Mixchange + Event log of Opex 1.8 @@ -22,7 +22,7 @@ - co.nilin.mixchange + co.nilin.opex matching-core ${matching.version} provided diff --git a/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Event.kt b/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Event.kt deleted file mode 100644 index a7699c871..000000000 --- a/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Event.kt +++ /dev/null @@ -1,4 +0,0 @@ -package co.nilin.mixchange.eventlog.spi - -interface Event { -} \ No newline at end of file diff --git a/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/EventPersister.kt b/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/EventPersister.kt deleted file mode 100644 index 1ad5da724..000000000 --- a/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/EventPersister.kt +++ /dev/null @@ -1,7 +0,0 @@ -package co.nilin.mixchange.eventlog.spi - -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent - -interface EventPersister { - suspend fun saveEvent(event: CoreEvent): List -} \ No newline at end of file diff --git a/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Order.kt b/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Order.kt deleted file mode 100644 index bc5b75cf8..000000000 --- a/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Order.kt +++ /dev/null @@ -1,4 +0,0 @@ -package co.nilin.mixchange.eventlog.spi - -interface Order { -} \ No newline at end of file diff --git a/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Trade.kt b/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Trade.kt deleted file mode 100644 index d2e763e20..000000000 --- a/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/Trade.kt +++ /dev/null @@ -1,4 +0,0 @@ -package co.nilin.mixchange.eventlog.spi - -interface Trade { -} \ No newline at end of file diff --git a/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/TradePersister.kt b/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/TradePersister.kt deleted file mode 100644 index 543252f93..000000000 --- a/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/TradePersister.kt +++ /dev/null @@ -1,7 +0,0 @@ -package co.nilin.mixchange.eventlog.spi - -import co.nilin.mixchange.matching.core.eventh.events.TradeEvent - -interface TradePersister { - suspend fun saveTrade(tradeEvent: TradeEvent): Trade -} \ No newline at end of file diff --git a/EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/Event.kt b/EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/Event.kt new file mode 100644 index 000000000..eecd48b7f --- /dev/null +++ b/EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/Event.kt @@ -0,0 +1,4 @@ +package co.nilin.opex.eventlog.spi + +interface Event { +} \ No newline at end of file diff --git a/EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/EventPersister.kt b/EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/EventPersister.kt new file mode 100644 index 000000000..f2e0fb35a --- /dev/null +++ b/EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/EventPersister.kt @@ -0,0 +1,7 @@ +package co.nilin.opex.eventlog.spi + +import co.nilin.opex.matching.core.eventh.events.CoreEvent + +interface EventPersister { + suspend fun saveEvent(event: CoreEvent): List +} \ No newline at end of file diff --git a/EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/Order.kt b/EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/Order.kt new file mode 100644 index 000000000..6816360ca --- /dev/null +++ b/EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/Order.kt @@ -0,0 +1,4 @@ +package co.nilin.opex.eventlog.spi + +interface Order { +} \ No newline at end of file diff --git a/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/OrderPersister.kt b/EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/OrderPersister.kt similarity index 76% rename from EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/OrderPersister.kt rename to EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/OrderPersister.kt index 58c4b56dd..2eefab154 100644 --- a/EventLog/eventlog-core/src/main/kotlin/co/nilin/mixchange/eventlog/spi/OrderPersister.kt +++ b/EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/OrderPersister.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.eventlog.spi +package co.nilin.opex.eventlog.spi -import co.nilin.mixchange.matching.core.eventh.events.* +import co.nilin.opex.matching.core.eventh.events.* interface OrderPersister { suspend fun submitOrder(orderEvent: SubmitOrderEvent) diff --git a/EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/Trade.kt b/EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/Trade.kt new file mode 100644 index 000000000..127144c2a --- /dev/null +++ b/EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/Trade.kt @@ -0,0 +1,4 @@ +package co.nilin.opex.eventlog.spi + +interface Trade { +} \ No newline at end of file diff --git a/EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/TradePersister.kt b/EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/TradePersister.kt new file mode 100644 index 000000000..cf39ae969 --- /dev/null +++ b/EventLog/eventlog-core/src/main/kotlin/co/nilin/opex/eventlog/spi/TradePersister.kt @@ -0,0 +1,7 @@ +package co.nilin.opex.eventlog.spi + +import co.nilin.opex.matching.core.eventh.events.TradeEvent + +interface TradePersister { + suspend fun saveTrade(tradeEvent: TradeEvent): Trade +} \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/pom.xml b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/pom.xml index 2bd01e040..b669e3130 100644 --- a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/pom.xml +++ b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex eventlog-eventlistener-kafka 0.0.1-SNAPSHOT eventlog-eventlistener-kafka - Matching engine kafka trade handler of Mixchange + Matching engine kafka trade handler of Opex 1.8 @@ -30,7 +30,7 @@ spring-boot-starter-webflux - co.nilin.mixchange + co.nilin.opex matching-core ${matching.version} provided diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/config/EventlogKafkaConfig.kt b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog/kafka/config/EventlogKafkaConfig.kt similarity index 92% rename from EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/config/EventlogKafkaConfig.kt rename to EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog/kafka/config/EventlogKafkaConfig.kt index 939e38497..f8c441526 100644 --- a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/config/EventlogKafkaConfig.kt +++ b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog/kafka/config/EventlogKafkaConfig.kt @@ -1,10 +1,10 @@ -package co.nilin.mixchange.port.trade.config +package co.nilin.opex.port.eventlog.kafka.config -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent -import co.nilin.mixchange.port.eventlog.kafka.consumer.OrderKafkaListener -import co.nilin.mixchange.port.trade.consumer.EventKafkaListener -import co.nilin.mixchange.port.trade.consumer.TradeKafkaListener +import co.nilin.opex.matching.core.eventh.events.CoreEvent +import co.nilin.opex.port.eventlog.kafka.consumer.OrderKafkaListener +import co.nilin.opex.port.eventlog.kafka.consumer.EventKafkaListener +import co.nilin.opex.port.eventlog.kafka.consumer.TradeKafkaListener import org.apache.kafka.clients.consumer.ConsumerConfig import org.apache.kafka.clients.producer.ProducerConfig import org.apache.kafka.common.serialization.StringDeserializer @@ -43,7 +43,7 @@ class EventlogKafkaConfig { props[ConsumerConfig.GROUP_ID_CONFIG] = groupId props[ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG] = StringDeserializer::class.java props[ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG] = JsonDeserializer::class.java - props[JsonDeserializer.TRUSTED_PACKAGES] = "co.nilin.mixchange.*" + props[JsonDeserializer.TRUSTED_PACKAGES] = "co.nilin.opex.*" return props } diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/consumer/EventKafkaListener.kt b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog/kafka/consumer/EventKafkaListener.kt similarity index 81% rename from EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/consumer/EventKafkaListener.kt rename to EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog/kafka/consumer/EventKafkaListener.kt index e0e6359f3..4e02b2665 100644 --- a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/consumer/EventKafkaListener.kt +++ b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog/kafka/consumer/EventKafkaListener.kt @@ -1,8 +1,8 @@ -package co.nilin.mixchange.port.trade.consumer +package co.nilin.opex.port.eventlog.kafka.consumer -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent -import co.nilin.mixchange.port.trade.spi.EventListener +import co.nilin.opex.matching.core.eventh.events.CoreEvent +import co.nilin.opex.port.eventlog.kafka.spi.EventListener import org.apache.kafka.clients.consumer.ConsumerRecord import org.springframework.kafka.listener.MessageListener import org.springframework.stereotype.Component diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/consumer/OrderKafkaListener.kt b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog/kafka/consumer/OrderKafkaListener.kt similarity index 84% rename from EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/consumer/OrderKafkaListener.kt rename to EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog/kafka/consumer/OrderKafkaListener.kt index c904188e7..efe994344 100644 --- a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/consumer/OrderKafkaListener.kt +++ b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog/kafka/consumer/OrderKafkaListener.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.port.eventlog.kafka.consumer +package co.nilin.opex.port.eventlog.kafka.consumer -import co.nilin.mixchange.port.eventlog.kafka.spi.OrderSubmitRequestListener -import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest +import co.nilin.opex.port.eventlog.kafka.spi.OrderSubmitRequestListener +import co.nilin.opex.port.order.kafka.inout.OrderSubmitRequest import kotlinx.coroutines.ExecutorCoroutineDispatcher import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/TradeKafkaListener.kt b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog/kafka/consumer/TradeKafkaListener.kt similarity index 81% rename from Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/TradeKafkaListener.kt rename to EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog/kafka/consumer/TradeKafkaListener.kt index 0c14cf036..9672f2ab8 100644 --- a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/consumer/TradeKafkaListener.kt +++ b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog/kafka/consumer/TradeKafkaListener.kt @@ -1,8 +1,8 @@ -package co.nilin.mixchange.port.trade.consumer +package co.nilin.opex.port.eventlog.kafka.consumer -import co.nilin.mixchange.matching.core.eventh.events.TradeEvent -import co.nilin.mixchange.port.trade.spi.TradeListener +import co.nilin.opex.matching.core.eventh.events.TradeEvent +import co.nilin.opex.port.eventlog.kafka.spi.TradeListener import org.apache.kafka.clients.consumer.ConsumerRecord import org.springframework.kafka.listener.MessageListener import org.springframework.stereotype.Component diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/spi/EventListener.kt b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog/kafka/spi/EventListener.kt similarity index 55% rename from EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/spi/EventListener.kt rename to EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog/kafka/spi/EventListener.kt index 0f522ca46..6cbf6e603 100644 --- a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/spi/EventListener.kt +++ b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog/kafka/spi/EventListener.kt @@ -1,7 +1,6 @@ -package co.nilin.mixchange.port.trade.spi - -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +package co.nilin.opex.port.eventlog.kafka.spi +import co.nilin.opex.matching.core.eventh.events.CoreEvent interface EventListener { fun id(): String diff --git a/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/spi/OrderSubmitRequestListener.kt b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog/kafka/spi/OrderSubmitRequestListener.kt similarity index 58% rename from MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/spi/OrderSubmitRequestListener.kt rename to EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog/kafka/spi/OrderSubmitRequestListener.kt index 6098ae3b5..4c90e5b03 100644 --- a/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/spi/OrderSubmitRequestListener.kt +++ b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog/kafka/spi/OrderSubmitRequestListener.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.port.order.kafka.spi +package co.nilin.opex.port.eventlog.kafka.spi -import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest +import co.nilin.opex.port.order.kafka.inout.OrderSubmitRequest interface OrderSubmitRequestListener { fun id(): String diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/TradeListener.kt b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog/kafka/spi/TradeListener.kt similarity index 55% rename from Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/TradeListener.kt rename to EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog/kafka/spi/TradeListener.kt index 7f37be88b..8d8891173 100644 --- a/Accountant/accountant-ports/accountant-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/accountant/kafka/spi/TradeListener.kt +++ b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/eventlog/kafka/spi/TradeListener.kt @@ -1,7 +1,6 @@ -package co.nilin.mixchange.port.trade.spi - -import co.nilin.mixchange.matching.core.eventh.events.TradeEvent +package co.nilin.opex.port.eventlog.kafka.spi +import co.nilin.opex.matching.core.eventh.events.TradeEvent interface TradeListener { fun id(): String diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/inout/OrderSubmitRequest.kt similarity index 72% rename from EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt rename to EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/inout/OrderSubmitRequest.kt index a25a9372a..b15b98eed 100644 --- a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt +++ b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/inout/OrderSubmitRequest.kt @@ -1,14 +1,14 @@ -package co.nilin.mixchange.port.order.kafka.inout +package co.nilin.opex.port.order.kafka.inout -import co.nilin.mixchange.matching.core.model.MatchConstraint -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.OrderType +import co.nilin.opex.matching.core.model.MatchConstraint +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderType public class OrderSubmitRequest() { lateinit var ouid: String lateinit var uuid: String var orderId: Long? = null - lateinit var pair: co.nilin.mixchange.matching.core.model.Pair + lateinit var pair: co.nilin.opex.matching.core.model.Pair var price: Long = 0 var quantity: Long = 0 var direction: OrderDirection = OrderDirection.BID @@ -18,7 +18,7 @@ public class OrderSubmitRequest() { constructor(ouid: String, uuid: String, orderId: Long?, - pair: co.nilin.mixchange.matching.core.model.Pair, + pair: co.nilin.opex.matching.core.model.Pair, price: Long, quantity: Long, direction: OrderDirection, diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/pom.xml b/EventLog/eventlog-ports/eventlog-persister-postgres/pom.xml index bcaa625a5..2fb2c4b6d 100644 --- a/EventLog/eventlog-ports/eventlog-persister-postgres/pom.xml +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex eventlog-persister-postgres 0.0.1-SNAPSHOT eventlog-persister-postgres - Persist items of Mixchange on Postgres + Persist items of Opex on Postgres 1.8 @@ -23,13 +23,13 @@ - co.nilin.mixchange + co.nilin.opex matching-core ${matching.version} provided - co.nilin.mixchange + co.nilin.opex eventlog-core ${eventlog.version} provided diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/OrderEventsModel.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/OrderEventsModel.kt deleted file mode 100644 index dda65f531..000000000 --- a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/OrderEventsModel.kt +++ /dev/null @@ -1,20 +0,0 @@ -package co.nilin.mixchange.port.eventlog.postgres.model - -import org.springframework.data.annotation.Id -import org.springframework.data.relational.core.mapping.Column -import org.springframework.data.relational.core.mapping.Table -import java.time.LocalDateTime -@Table("opex_order_events") -class OrderEventsModel(@Id var id: Long? - , var ouid: String - , val uuid: String - , @Column("matching_orderid") val matchingOrderId: Long? - , val price: Long? - , val quantity: Long? - , @Column("filled_quantity") val filledQuantity: Long? - , val event: String - , val agent: String - , val ip: String - , @Column("event_date") val eventDate: LocalDateTime - , @Column("create_date") val createDate: LocalDateTime -) \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/OrderModel.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/OrderModel.kt deleted file mode 100644 index 8700ec03a..000000000 --- a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/OrderModel.kt +++ /dev/null @@ -1,22 +0,0 @@ -package co.nilin.mixchange.port.eventlog.postgres.model - -import co.nilin.mixchange.eventlog.spi.Order -import co.nilin.mixchange.eventlog.spi.Trade -import org.springframework.data.annotation.Id -import org.springframework.data.relational.core.mapping.Column -import org.springframework.data.relational.core.mapping.Table -import java.time.LocalDateTime - -@Table("opex_orders") -class OrderModel(@Id var id: Long? -, val ouid: String -, val symbol: String -, val direction: String -, @Column("match_constraint") val matchConstraint: String -, @Column("order_type") val orderType: String -, val uuid: String -, val agent: String -, val ip: String -, @Column("order_date") val orderDate: LocalDateTime -, @Column("create_date") val createDate: LocalDateTime -): Order \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/TradeModel.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/TradeModel.kt deleted file mode 100644 index 4f953576a..000000000 --- a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/TradeModel.kt +++ /dev/null @@ -1,43 +0,0 @@ -package co.nilin.mixchange.port.eventlog.postgres.model - -import co.nilin.mixchange.eventlog.spi.Trade -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.Pair -import org.springframework.data.annotation.Id -import org.springframework.data.relational.core.mapping.Column -import org.springframework.data.relational.core.mapping.Table -import java.time.LocalDateTime - -@Table("opex_trades") -class TradeModel(@Id var id: Long?, - val symbol: String, - @Column("taker_ouid") - val takerOuid: String, - @Column("taker_uuid") - val takerUuid: String, - @Column("taker_matching_orderid") - val takerOrderId: Long, - @Column("taker_direction") - val takerDirection: String, - @Column("taker_price") - val takerPrice: Long, - @Column("taker_remained_quantity") - val takerRemainedQuantity: Long, - @Column("maker_ouid") - val makerOuid: String, - @Column("maker_uuid") - val makerUuid: String, - @Column("maker_matching_orderid") - val makerOrderId: Long, - @Column("maker_direction") - val makerDirection: String, - @Column("maker_price") - val makerPrice: Long, - @Column("maker_remained_quantity") - val makerRemainedQuantity: Long, - @Column("matched_quantity") - val matchedQuantity: Long, - @Column("trade_date") - val eventDate: LocalDateTime, - @Column("create_date") - val createDate: LocalDateTime): Trade \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/config/PostgresConfig.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/config/PostgresConfig.kt similarity index 96% rename from EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/config/PostgresConfig.kt rename to EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/config/PostgresConfig.kt index 8639692c1..8b6803afc 100644 --- a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/config/PostgresConfig.kt +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/config/PostgresConfig.kt @@ -1,13 +1,11 @@ -package co.nilin.mixchange.port.order.kafka.config - +package co.nilin.opex.port.eventlog.postgres.config import org.springframework.context.annotation.Configuration import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories import org.springframework.r2dbc.core.DatabaseClient - @Configuration -@EnableR2dbcRepositories(basePackages = ["co.nilin.mixchange"]) +@EnableR2dbcRepositories(basePackages = ["co.nilin.opex"]) class PostgresConfig(db: DatabaseClient) { init { diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/EventRepository.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/dao/EventRepository.kt similarity index 63% rename from EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/EventRepository.kt rename to EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/dao/EventRepository.kt index a7c681d41..b8f5bd05c 100644 --- a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/EventRepository.kt +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/dao/EventRepository.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.port.eventlog.postgres.dao +package co.nilin.opex.port.eventlog.postgres.dao -import co.nilin.mixchange.port.eventlog.postgres.model.EventModel +import co.nilin.opex.port.eventlog.postgres.model.EventModel import org.springframework.data.repository.reactive.ReactiveCrudRepository import org.springframework.stereotype.Repository diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/OrderEventRepository.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/dao/OrderEventRepository.kt similarity index 53% rename from EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/OrderEventRepository.kt rename to EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/dao/OrderEventRepository.kt index 20bc8cb06..329fc4bc8 100644 --- a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/OrderEventRepository.kt +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/dao/OrderEventRepository.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.port.eventlog.postgres.dao +package co.nilin.opex.port.eventlog.postgres.dao -import co.nilin.mixchange.port.eventlog.postgres.model.OrderEventsModel -import co.nilin.mixchange.port.eventlog.postgres.model.OrderModel +import co.nilin.opex.port.eventlog.postgres.model.OrderEventsModel +import co.nilin.opex.port.eventlog.postgres.model.OrderModel import org.springframework.data.repository.reactive.ReactiveCrudRepository import org.springframework.stereotype.Repository diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/OrderRepository.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/dao/OrderRepository.kt similarity index 63% rename from EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/OrderRepository.kt rename to EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/dao/OrderRepository.kt index a58984068..698335999 100644 --- a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/OrderRepository.kt +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/dao/OrderRepository.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.port.eventlog.postgres.dao +package co.nilin.opex.port.eventlog.postgres.dao -import co.nilin.mixchange.port.eventlog.postgres.model.OrderModel +import co.nilin.opex.port.eventlog.postgres.model.OrderModel import org.springframework.data.repository.reactive.ReactiveCrudRepository import org.springframework.stereotype.Repository diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/TradeRepository.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/dao/TradeRepository.kt similarity index 63% rename from EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/TradeRepository.kt rename to EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/dao/TradeRepository.kt index 819c270b9..7fa737a35 100644 --- a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/dao/TradeRepository.kt +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/dao/TradeRepository.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.port.eventlog.postgres.dao +package co.nilin.opex.port.eventlog.postgres.dao -import co.nilin.mixchange.port.eventlog.postgres.model.TradeModel +import co.nilin.opex.port.eventlog.postgres.model.TradeModel import org.springframework.data.repository.reactive.ReactiveCrudRepository import org.springframework.stereotype.Repository diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/impl/EventPersisterImpl.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/impl/EventPersisterImpl.kt similarity index 86% rename from EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/impl/EventPersisterImpl.kt rename to EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/impl/EventPersisterImpl.kt index e63c5e2b3..a21565cf7 100644 --- a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/impl/EventPersisterImpl.kt +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/impl/EventPersisterImpl.kt @@ -1,10 +1,10 @@ -package co.nilin.mixchange.port.eventlog.postgres.impl +package co.nilin.opex.port.eventlog.postgres.impl -import co.nilin.mixchange.eventlog.spi.Event -import co.nilin.mixchange.eventlog.spi.EventPersister -import co.nilin.mixchange.matching.core.eventh.events.* -import co.nilin.mixchange.port.eventlog.postgres.dao.EventRepository -import co.nilin.mixchange.port.eventlog.postgres.model.EventModel +import co.nilin.opex.eventlog.spi.Event +import co.nilin.opex.eventlog.spi.EventPersister +import co.nilin.opex.matching.core.eventh.events.* +import co.nilin.opex.port.eventlog.postgres.dao.EventRepository +import co.nilin.opex.port.eventlog.postgres.model.EventModel import kotlinx.coroutines.reactive.awaitFirst import kotlinx.coroutines.reactive.awaitFirstOrNull import org.springframework.stereotype.Component diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/impl/OrderPersisterImpl.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/impl/OrderPersisterImpl.kt similarity index 86% rename from EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/impl/OrderPersisterImpl.kt rename to EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/impl/OrderPersisterImpl.kt index e426d6a94..caf1dcf08 100644 --- a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/impl/OrderPersisterImpl.kt +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/impl/OrderPersisterImpl.kt @@ -1,11 +1,11 @@ -package co.nilin.mixchange.port.eventlog.postgres.impl +package co.nilin.opex.port.eventlog.postgres.impl -import co.nilin.mixchange.eventlog.spi.OrderPersister -import co.nilin.mixchange.matching.core.eventh.events.* -import co.nilin.mixchange.port.eventlog.postgres.dao.OrderEventRepository -import co.nilin.mixchange.port.eventlog.postgres.dao.OrderRepository -import co.nilin.mixchange.port.eventlog.postgres.model.OrderEventsModel -import co.nilin.mixchange.port.eventlog.postgres.model.OrderModel +import co.nilin.opex.eventlog.spi.OrderPersister +import co.nilin.opex.matching.core.eventh.events.* +import co.nilin.opex.port.eventlog.postgres.dao.OrderEventRepository +import co.nilin.opex.port.eventlog.postgres.dao.OrderRepository +import co.nilin.opex.port.eventlog.postgres.model.OrderEventsModel +import co.nilin.opex.port.eventlog.postgres.model.OrderModel import kotlinx.coroutines.reactive.awaitFirst import kotlinx.coroutines.reactive.awaitFirstOrNull import kotlinx.coroutines.reactive.awaitSingle diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/impl/TradePersisterImpl.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/impl/TradePersisterImpl.kt similarity index 75% rename from EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/impl/TradePersisterImpl.kt rename to EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/impl/TradePersisterImpl.kt index cb7762b04..9740a008a 100644 --- a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/impl/TradePersisterImpl.kt +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/impl/TradePersisterImpl.kt @@ -1,10 +1,10 @@ -package co.nilin.mixchange.port.eventlog.postgres.impl +package co.nilin.opex.port.eventlog.postgres.impl -import co.nilin.mixchange.eventlog.spi.Trade -import co.nilin.mixchange.eventlog.spi.TradePersister -import co.nilin.mixchange.matching.core.eventh.events.TradeEvent -import co.nilin.mixchange.port.eventlog.postgres.dao.TradeRepository -import co.nilin.mixchange.port.eventlog.postgres.model.TradeModel +import co.nilin.opex.eventlog.spi.Trade +import co.nilin.opex.eventlog.spi.TradePersister +import co.nilin.opex.matching.core.eventh.events.TradeEvent +import co.nilin.opex.port.eventlog.postgres.dao.TradeRepository +import co.nilin.opex.port.eventlog.postgres.model.TradeModel import kotlinx.coroutines.reactive.awaitFirst import org.springframework.stereotype.Component import java.time.LocalDateTime diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/EventModel.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/model/EventModel.kt similarity index 80% rename from EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/EventModel.kt rename to EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/model/EventModel.kt index cc489589d..54ced7554 100644 --- a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/eventlog/postgres/model/EventModel.kt +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/model/EventModel.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.port.eventlog.postgres.model +package co.nilin.opex.port.eventlog.postgres.model -import co.nilin.mixchange.eventlog.spi.Event -import co.nilin.mixchange.eventlog.spi.Trade +import co.nilin.opex.eventlog.spi.Event +import co.nilin.opex.eventlog.spi.Trade import org.springframework.data.annotation.Id import org.springframework.data.relational.core.mapping.Column import org.springframework.data.relational.core.mapping.Table diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/model/OrderEventsModel.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/model/OrderEventsModel.kt new file mode 100644 index 000000000..65180d4c8 --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/model/OrderEventsModel.kt @@ -0,0 +1,22 @@ +package co.nilin.opex.port.eventlog.postgres.model + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.time.LocalDateTime + +@Table("opex_order_events") +class OrderEventsModel( + @Id var id: Long?, + var ouid: String, + val uuid: String, + @Column("matching_orderid") val matchingOrderId: Long?, + val price: Long?, + val quantity: Long?, + @Column("filled_quantity") val filledQuantity: Long?, + val event: String, + val agent: String, + val ip: String, + @Column("event_date") val eventDate: LocalDateTime, + @Column("create_date") val createDate: LocalDateTime +) \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/model/OrderModel.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/model/OrderModel.kt new file mode 100644 index 000000000..3fd65a0ed --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/model/OrderModel.kt @@ -0,0 +1,23 @@ +package co.nilin.opex.port.eventlog.postgres.model + +import co.nilin.opex.eventlog.spi.Order +import co.nilin.opex.eventlog.spi.Trade +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.time.LocalDateTime + +@Table("opex_orders") +class OrderModel( + @Id var id: Long?, + val ouid: String, + val symbol: String, + val direction: String, + @Column("match_constraint") val matchConstraint: String, + @Column("order_type") val orderType: String, + val uuid: String, + val agent: String, + val ip: String, + @Column("order_date") val orderDate: LocalDateTime, + @Column("create_date") val createDate: LocalDateTime +) : Order \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/model/TradeModel.kt b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/model/TradeModel.kt new file mode 100644 index 000000000..8a0f74b8b --- /dev/null +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/src/main/kotlin/co/nilin/opex/port/eventlog/postgres/model/TradeModel.kt @@ -0,0 +1,45 @@ +package co.nilin.opex.port.eventlog.postgres.model + +import co.nilin.opex.eventlog.spi.Trade +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.Pair +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.time.LocalDateTime + +@Table("opex_trades") +class TradeModel( + @Id var id: Long?, + val symbol: String, + @Column("taker_ouid") + val takerOuid: String, + @Column("taker_uuid") + val takerUuid: String, + @Column("taker_matching_orderid") + val takerOrderId: Long, + @Column("taker_direction") + val takerDirection: String, + @Column("taker_price") + val takerPrice: Long, + @Column("taker_remained_quantity") + val takerRemainedQuantity: Long, + @Column("maker_ouid") + val makerOuid: String, + @Column("maker_uuid") + val makerUuid: String, + @Column("maker_matching_orderid") + val makerOrderId: Long, + @Column("maker_direction") + val makerDirection: String, + @Column("maker_price") + val makerPrice: Long, + @Column("maker_remained_quantity") + val makerRemainedQuantity: Long, + @Column("matched_quantity") + val matchedQuantity: Long, + @Column("trade_date") + val eventDate: LocalDateTime, + @Column("create_date") + val createDate: LocalDateTime +) : Trade \ No newline at end of file diff --git a/EventLog/pom.xml b/EventLog/pom.xml index 035449a5c..6678be074 100644 --- a/EventLog/pom.xml +++ b/EventLog/pom.xml @@ -2,12 +2,12 @@ 4.0.0 - co.nilin.mixchange + co.nilin.opex eventlog 0.0.1-SNAPSHOT eventlog pom - Event log root of Mixchange + Event log root of Opex eventlog-core diff --git a/MatchingEngine/matching-app/pom.xml b/MatchingEngine/matching-app/pom.xml index ea65e34bd..d8b64d033 100644 --- a/MatchingEngine/matching-app/pom.xml +++ b/MatchingEngine/matching-app/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex matching-app 0.0.1-SNAPSHOT matching-app - Matching engine running app Mixchange + Matching engine running app Opex 1.8 @@ -42,22 +42,22 @@ spring-boot-starter - co.nilin.mixchange + co.nilin.opex matching-core ${matching.version} - co.nilin.mixchange + co.nilin.opex matching-submitter-kafka ${matching.version} - co.nilin.mixchange + co.nilin.opex matching-eventlistener-kafka ${matching.version} - co.nilin.mixchange + co.nilin.opex matching-snapshots-redis ${matching.version} diff --git a/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/MatchingEngineApp.kt b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/MatchingEngineApp.kt similarity index 69% rename from MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/MatchingEngineApp.kt rename to MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/MatchingEngineApp.kt index c67a50220..b98a4585c 100644 --- a/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/MatchingEngineApp.kt +++ b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/MatchingEngineApp.kt @@ -1,12 +1,13 @@ -package co.nilin.mixchange.app +package co.nilin.opex.app import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.annotation.ComponentScan @SpringBootApplication -@ComponentScan("co.nilin.mixchange") +@ComponentScan("co.nilin.opex") class MatchingEngineApp + fun main(args: Array) { - runApplication(*args) + runApplication(*args) } \ No newline at end of file diff --git a/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/bl/ExchangeEventHandler.kt b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/bl/ExchangeEventHandler.kt similarity index 79% rename from MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/bl/ExchangeEventHandler.kt rename to MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/bl/ExchangeEventHandler.kt index 6cfa938d1..22bac3fbd 100644 --- a/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/bl/ExchangeEventHandler.kt +++ b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/bl/ExchangeEventHandler.kt @@ -1,8 +1,8 @@ -package co.nilin.mixchange.app.bl +package co.nilin.opex.app.bl -import co.nilin.mixchange.matching.core.eventh.EventDispatcher -import co.nilin.mixchange.matching.core.eventh.events.* -import co.nilin.mixchange.port.order.kafka.service.EventsSubmitter +import co.nilin.opex.matching.core.eventh.EventDispatcher +import co.nilin.opex.matching.core.eventh.events.* +import co.nilin.opex.port.order.kafka.service.EventsSubmitter import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch diff --git a/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/bl/OrderBooks.kt b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/bl/OrderBooks.kt similarity index 75% rename from MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/bl/OrderBooks.kt rename to MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/bl/OrderBooks.kt index 14265c6d1..53725d72b 100644 --- a/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/bl/OrderBooks.kt +++ b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/bl/OrderBooks.kt @@ -1,8 +1,8 @@ -package co.nilin.mixchange.app.bl +package co.nilin.opex.app.bl -import co.nilin.mixchange.matching.core.factory.OrderBookFactory -import co.nilin.mixchange.matching.core.model.OrderBook -import co.nilin.mixchange.matching.core.model.PersistentOrderBook +import co.nilin.opex.matching.core.factory.OrderBookFactory +import co.nilin.opex.matching.core.model.OrderBook +import co.nilin.opex.matching.core.model.PersistentOrderBook object OrderBooks { private val orderBooks = mutableMapOf() @@ -12,7 +12,7 @@ object OrderBooks { if ( orderBooks.containsKey(pair)) throw IllegalArgumentException("${pair} has an order book right now!") val pairs = pair.split("_") - orderBooks[pair] = OrderBookFactory.createOrderBook(co.nilin.mixchange.matching.core.model.Pair(pairs[0], pairs[1])) + orderBooks[pair] = OrderBookFactory.createOrderBook(co.nilin.opex.matching.core.model.Pair(pairs[0], pairs[1])) println("order book:" + pair + " added, current order books#" + orderBooks.size) } diff --git a/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/config/AppConfig.kt similarity index 85% rename from MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt rename to MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/config/AppConfig.kt index c3e4fbaa6..81427366e 100644 --- a/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt +++ b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/config/AppConfig.kt @@ -1,13 +1,13 @@ -package co.nilin.mixchange.app.config +package co.nilin.opex.app.config -import co.nilin.mixchange.app.bl.ExchangeEventHandler -import co.nilin.mixchange.app.bl.OrderBooks -import co.nilin.mixchange.matching.core.inout.OrderCreateCommand -import co.nilin.mixchange.matching.core.model.PersistentOrderBook -import co.nilin.mixchange.matching.core.spi.OrderBookPersister -import co.nilin.mixchange.port.order.kafka.consumer.OrderKafkaListener -import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest -import co.nilin.mixchange.port.order.kafka.spi.OrderSubmitRequestListener +import co.nilin.opex.app.bl.ExchangeEventHandler +import co.nilin.opex.app.bl.OrderBooks +import co.nilin.opex.matching.core.inout.OrderCreateCommand +import co.nilin.opex.matching.core.model.PersistentOrderBook +import co.nilin.opex.matching.core.spi.OrderBookPersister +import co.nilin.opex.port.order.kafka.consumer.OrderKafkaListener +import co.nilin.opex.port.order.kafka.inout.OrderSubmitRequest +import co.nilin.opex.port.order.kafka.spi.OrderSubmitRequestListener import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking diff --git a/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/config/AppSchedulers.kt b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/config/AppSchedulers.kt similarity index 83% rename from MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/config/AppSchedulers.kt rename to MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/config/AppSchedulers.kt index 1c8f69e51..5162ee071 100644 --- a/MatchingEngine/matching-app/src/main/kotlin/co/nilin/mixchange/app/config/AppSchedulers.kt +++ b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/config/AppSchedulers.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.app.config +package co.nilin.opex.app.config import kotlinx.coroutines.asCoroutineDispatcher import java.util.concurrent.Executors diff --git a/MatchingEngine/matching-core/pom.xml b/MatchingEngine/matching-core/pom.xml index 96a1cbcfb..c52250d16 100644 --- a/MatchingEngine/matching-core/pom.xml +++ b/MatchingEngine/matching-core/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex matching-core 0.0.1-SNAPSHOT matching-core - Matching engine of Mixchange + Matching engine of Opex 1.8 diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderCancelCommand.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderCancelCommand.kt deleted file mode 100644 index fe000cadd..000000000 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderCancelCommand.kt +++ /dev/null @@ -1,5 +0,0 @@ -package co.nilin.mixchange.matching.core.inout - -import co.nilin.mixchange.matching.core.model.Pair - -class OrderCancelCommand(val ouid: String, val uuid: String, val orderId: Long, val pair: Pair) \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderEditCommand.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderEditCommand.kt deleted file mode 100644 index 972d0794e..000000000 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderEditCommand.kt +++ /dev/null @@ -1,5 +0,0 @@ -package co.nilin.mixchange.matching.core.inout - -import co.nilin.mixchange.matching.core.model.Pair - -data class OrderEditCommand(val ouid: String, val uuid: String, val orderId: Long, val pair: Pair, val price: Long, val quantity: Long) \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/engine/SimpleOrderBook.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBook.kt similarity index 88% rename from MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/engine/SimpleOrderBook.kt rename to MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBook.kt index 396c09428..e5d2e8fb7 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/engine/SimpleOrderBook.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBook.kt @@ -1,9 +1,9 @@ -package co.nilin.mixchange.matching.core.engine +package co.nilin.opex.matching.core.engine -import co.nilin.mixchange.matching.core.eventh.EventDispatcher -import co.nilin.mixchange.matching.core.eventh.events.* -import co.nilin.mixchange.matching.core.inout.* -import co.nilin.mixchange.matching.core.model.* +import co.nilin.opex.matching.core.eventh.EventDispatcher +import co.nilin.opex.matching.core.eventh.events.* +import co.nilin.opex.matching.core.inout.* +import co.nilin.opex.matching.core.model.* import exchange.core2.collections.art.LongAdaptiveRadixTreeMap import java.util.* import java.util.concurrent.atomic.AtomicLong @@ -45,7 +45,20 @@ class SimpleOrderBook(val pair: Pair, var replayMode: Boolean) : OrderBook { } return null } - val order = SimpleOrder(orderCounter.incrementAndGet(), orderCommand.ouid, orderCommand.uuid, orderCommand.price, orderCommand.quantity, orderCommand.matchConstraint, orderCommand.orderType, orderCommand.direction, 0, null, null, null) + val order = SimpleOrder( + orderCounter.incrementAndGet(), + orderCommand.ouid, + orderCommand.uuid, + orderCommand.price, + orderCommand.quantity, + orderCommand.matchConstraint, + orderCommand.orderType, + orderCommand.direction, + 0, + null, + null, + null + ) if (!replayMode) { EventDispatcher.emit(CreateOrderEvent(orderCommand.ouid, orderCommand.uuid, order.id!!, orderCommand.pair, orderCommand.price, orderCommand.quantity, order.remainedQuantity(), orderCommand.direction, orderCommand.matchConstraint, orderCommand.orderType @@ -60,7 +73,20 @@ class SimpleOrderBook(val pair: Pair, var replayMode: Boolean) : OrderBook { queueOrder } MatchConstraint.IOC -> { - val order = SimpleOrder(orderCounter.incrementAndGet(), orderCommand.ouid, orderCommand.uuid,orderCommand.price, orderCommand.quantity, orderCommand.matchConstraint, orderCommand.orderType, orderCommand.direction, 0, null, null, null) + val order = SimpleOrder( + orderCounter.incrementAndGet(), + orderCommand.ouid, + orderCommand.uuid, + orderCommand.price, + orderCommand.quantity, + orderCommand.matchConstraint, + orderCommand.orderType, + orderCommand.direction, + 0, + null, + null, + null + ) if (!replayMode) { EventDispatcher.emit( CreateOrderEvent( @@ -137,7 +163,20 @@ class SimpleOrderBook(val pair: Pair, var replayMode: Boolean) : OrderBook { bestAskOrder = newBestOrder } } - val newOrder = SimpleOrder(order.id, orderCommand.ouid, orderCommand.uuid,orderCommand.price, orderCommand.quantity, order.matchConstraint, order.orderType, order.direction, order.filledQuantity, null, null, null) + val newOrder = SimpleOrder( + order.id, + orderCommand.ouid, + orderCommand.uuid, + orderCommand.price, + orderCommand.quantity, + order.matchConstraint, + order.orderType, + order.direction, + order.filledQuantity, + null, + null, + null + ) return when (order.matchConstraint) { MatchConstraint.GTC -> { @@ -316,7 +355,12 @@ class SimpleOrderBook(val pair: Pair, var replayMode: Boolean) : OrderBook { order.better = bucketLastOrder order.worse = worseOfBucketLastOrder } else { - bucket = Bucket(order.price, order.remainedQuantity(), 1, order) + bucket = Bucket( + order.price, + order.remainedQuantity(), + 1, + order + ) order.bucket = bucket queue.put(order.price, bucket) val betterBucket = betterBucketSelector(order.price, queue) @@ -365,7 +409,20 @@ class SimpleOrderBook(val pair: Pair, var replayMode: Boolean) : OrderBook { fun rebuild(persistentOrderBook: PersistentOrderBook) { persistentOrderBook.orders?.map { order -> - SimpleOrder(order.id, order.ouid, order.uuid, order.price, order.quantity, order.matchConstraint, order.orderType, order.direction, order.filledQuantity, null, null, null) + SimpleOrder( + order.id, + order.ouid, + order.uuid, + order.price, + order.quantity, + order.matchConstraint, + order.orderType, + order.direction, + order.filledQuantity, + null, + null, + null + ) }?.filter { order -> order.matchConstraint == MatchConstraint.GTC }?.forEach { order -> putGtcInQueue(order) } orderCounter.set(persistentOrderBook.lastOrder?.id?:0) diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/EventDispatcher.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/EventDispatcher.kt similarity index 92% rename from MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/EventDispatcher.kt rename to MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/EventDispatcher.kt index 022cb84df..bb620a5fd 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/EventDispatcher.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/EventDispatcher.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.matching.core.eventh +package co.nilin.opex.matching.core.eventh -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +import co.nilin.opex.matching.core.eventh.events.CoreEvent import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.launch diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/CancelOrderEvent.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/CancelOrderEvent.kt similarity index 79% rename from MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/CancelOrderEvent.kt rename to MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/CancelOrderEvent.kt index 1782a558c..300960035 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/CancelOrderEvent.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/CancelOrderEvent.kt @@ -1,8 +1,8 @@ -package co.nilin.mixchange.matching.core.eventh.events +package co.nilin.opex.matching.core.eventh.events -import co.nilin.mixchange.matching.core.model.MatchConstraint -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.OrderType +import co.nilin.opex.matching.core.model.MatchConstraint +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderType class CancelOrderEvent() : CoreEvent(), OneOrderEvent{ var ouid: String = "" @@ -18,7 +18,7 @@ class CancelOrderEvent() : CoreEvent(), OneOrderEvent{ constructor(ouid: String, uuid: String, orderId: Long, - pair: co.nilin.mixchange.matching.core.model.Pair, + pair: co.nilin.opex.matching.core.model.Pair, price: Long, quantity: Long, remainedQuantity: Long, diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/CoreEvent.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/CoreEvent.kt similarity index 56% rename from MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/CoreEvent.kt rename to MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/CoreEvent.kt index 59c4c8a1d..44dff675f 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/CoreEvent.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/CoreEvent.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.matching.core.eventh.events +package co.nilin.opex.matching.core.eventh.events -import co.nilin.mixchange.matching.core.model.Pair +import co.nilin.opex.matching.core.model.Pair import java.time.LocalDateTime open class CoreEvent { diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/CreateOrderEvent.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/CreateOrderEvent.kt similarity index 79% rename from MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/CreateOrderEvent.kt rename to MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/CreateOrderEvent.kt index 3d0ae7c4e..25a0896fe 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/CreateOrderEvent.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/CreateOrderEvent.kt @@ -1,8 +1,8 @@ -package co.nilin.mixchange.matching.core.eventh.events +package co.nilin.opex.matching.core.eventh.events -import co.nilin.mixchange.matching.core.model.MatchConstraint -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.OrderType +import co.nilin.opex.matching.core.model.MatchConstraint +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderType class CreateOrderEvent() : CoreEvent() , OneOrderEvent{ var ouid: String = "" @@ -18,7 +18,7 @@ class CreateOrderEvent() : CoreEvent() , OneOrderEvent{ constructor(ouid: String, uuid: String, orderId: Long, - pair: co.nilin.mixchange.matching.core.model.Pair, + pair: co.nilin.opex.matching.core.model.Pair, price: Long, quantity: Long, remainedQuantity: Long, diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/OneOrderEvent.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/OneOrderEvent.kt similarity index 57% rename from MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/OneOrderEvent.kt rename to MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/OneOrderEvent.kt index 9f08bdc93..21ab23dbd 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/OneOrderEvent.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/OneOrderEvent.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.matching.core.eventh.events +package co.nilin.opex.matching.core.eventh.events interface OneOrderEvent { fun ouid(): String diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/RejectOrderEvent.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/RejectOrderEvent.kt similarity index 79% rename from MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/RejectOrderEvent.kt rename to MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/RejectOrderEvent.kt index 29a55f3f0..39cd75e5c 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/RejectOrderEvent.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/RejectOrderEvent.kt @@ -1,10 +1,10 @@ -package co.nilin.mixchange.matching.core.eventh.events +package co.nilin.opex.matching.core.eventh.events -import co.nilin.mixchange.matching.core.inout.RejectReason -import co.nilin.mixchange.matching.core.inout.RequestedOperation -import co.nilin.mixchange.matching.core.model.MatchConstraint -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.OrderType +import co.nilin.opex.matching.core.inout.RejectReason +import co.nilin.opex.matching.core.inout.RequestedOperation +import co.nilin.opex.matching.core.model.MatchConstraint +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderType class RejectOrderEvent(): CoreEvent(), OneOrderEvent { var ouid: String = "" @@ -20,7 +20,7 @@ class RejectOrderEvent(): CoreEvent(), OneOrderEvent { constructor(ouid: String, uuid: String, - pair: co.nilin.mixchange.matching.core.model.Pair, + pair: co.nilin.opex.matching.core.model.Pair, price: Long, quantity: Long, direction: OrderDirection, @@ -32,14 +32,14 @@ class RejectOrderEvent(): CoreEvent(), OneOrderEvent { constructor(ouid: String, uuid: String, orderId: Long, - pair: co.nilin.mixchange.matching.core.model.Pair, + pair: co.nilin.opex.matching.core.model.Pair, requestedOperation: RequestedOperation, reason: RejectReason?) : this(ouid, uuid, orderId, pair, null, null, null, null, null, requestedOperation, reason) constructor(ouid: String, uuid: String, orderId: Long?, - pair: co.nilin.mixchange.matching.core.model.Pair, + pair: co.nilin.opex.matching.core.model.Pair, price: Long?, quantity: Long?, direction: OrderDirection?, diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/SubmitOrderEvent.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/SubmitOrderEvent.kt similarity index 80% rename from MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/SubmitOrderEvent.kt rename to MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/SubmitOrderEvent.kt index fe96595b3..8c6b7518e 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/SubmitOrderEvent.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/SubmitOrderEvent.kt @@ -1,8 +1,8 @@ -package co.nilin.mixchange.matching.core.eventh.events +package co.nilin.opex.matching.core.eventh.events -import co.nilin.mixchange.matching.core.model.MatchConstraint -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.OrderType +import co.nilin.opex.matching.core.model.MatchConstraint +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderType class SubmitOrderEvent() : CoreEvent() , OneOrderEvent{ var ouid: String = "" @@ -18,7 +18,7 @@ class SubmitOrderEvent() : CoreEvent() , OneOrderEvent{ constructor(ouid: String, uuid: String, orderId: Long?, - pair: co.nilin.mixchange.matching.core.model.Pair, + pair: co.nilin.opex.matching.core.model.Pair, price: Long, quantity: Long, remainedQuantity: Long, diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/TradeEvent.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/TradeEvent.kt similarity index 90% rename from MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/TradeEvent.kt rename to MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/TradeEvent.kt index 66a925aea..7408f7ab0 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/TradeEvent.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/TradeEvent.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.matching.core.eventh.events +package co.nilin.opex.matching.core.eventh.events -import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderDirection class TradeEvent() : CoreEvent() { var tradeId: Long = 0 @@ -20,7 +20,7 @@ class TradeEvent() : CoreEvent() { constructor(tradeId: Long, - pair: co.nilin.mixchange.matching.core.model.Pair, + pair: co.nilin.opex.matching.core.model.Pair, takerOuid: String, takerUuid: String, takerOrderId: Long, diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/UpdatedOrderEvent.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/UpdatedOrderEvent.kt similarity index 82% rename from MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/UpdatedOrderEvent.kt rename to MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/UpdatedOrderEvent.kt index c6e91a01e..744186d0a 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/eventh/events/UpdatedOrderEvent.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/eventh/events/UpdatedOrderEvent.kt @@ -1,8 +1,8 @@ -package co.nilin.mixchange.matching.core.eventh.events +package co.nilin.opex.matching.core.eventh.events -import co.nilin.mixchange.matching.core.model.MatchConstraint -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.OrderType +import co.nilin.opex.matching.core.model.MatchConstraint +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderType class UpdatedOrderEvent(): CoreEvent() , OneOrderEvent{ var ouid: String = "" @@ -20,7 +20,7 @@ class UpdatedOrderEvent(): CoreEvent() , OneOrderEvent{ constructor(ouid: String, uuid: String, orderId: Long, - pair: co.nilin.mixchange.matching.core.model.Pair, + pair: co.nilin.opex.matching.core.model.Pair, oldPrice: Long, oldQuantity: Long, price: Long, diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/factory/OrderBookFactory.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/factory/OrderBookFactory.kt similarity index 56% rename from MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/factory/OrderBookFactory.kt rename to MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/factory/OrderBookFactory.kt index d29dc06e0..ec504ecf5 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/factory/OrderBookFactory.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/factory/OrderBookFactory.kt @@ -1,13 +1,13 @@ -package co.nilin.mixchange.matching.core.factory +package co.nilin.opex.matching.core.factory -import co.nilin.mixchange.matching.core.engine.SimpleOrderBook -import co.nilin.mixchange.matching.core.model.OrderBook -import co.nilin.mixchange.matching.core.model.PersistentOrderBook +import co.nilin.opex.matching.core.engine.SimpleOrderBook +import co.nilin.opex.matching.core.model.OrderBook +import co.nilin.opex.matching.core.model.PersistentOrderBook import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext object OrderBookFactory { - fun createOrderBook(pair: co.nilin.mixchange.matching.core.model.Pair): OrderBook { + fun createOrderBook(pair: co.nilin.opex.matching.core.model.Pair): OrderBook { return SimpleOrderBook(pair, false) } diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/inout/OrderCancelCommand.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/inout/OrderCancelCommand.kt new file mode 100644 index 000000000..a62e0d4a7 --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/inout/OrderCancelCommand.kt @@ -0,0 +1,5 @@ +package co.nilin.opex.matching.core.inout + +import co.nilin.opex.matching.core.model.Pair + +class OrderCancelCommand(val ouid: String, val uuid: String, val orderId: Long, val pair: Pair) \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderCreateCommand.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/inout/OrderCreateCommand.kt similarity index 60% rename from MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderCreateCommand.kt rename to MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/inout/OrderCreateCommand.kt index 4fa036afa..ae6fdc9e7 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/OrderCreateCommand.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/inout/OrderCreateCommand.kt @@ -1,9 +1,9 @@ -package co.nilin.mixchange.matching.core.inout +package co.nilin.opex.matching.core.inout -import co.nilin.mixchange.matching.core.model.MatchConstraint -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.OrderType -import co.nilin.mixchange.matching.core.model.Pair +import co.nilin.opex.matching.core.model.MatchConstraint +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderType +import co.nilin.opex.matching.core.model.Pair data class OrderCreateCommand(val ouid: String, diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/inout/OrderEditCommand.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/inout/OrderEditCommand.kt new file mode 100644 index 000000000..beb7697eb --- /dev/null +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/inout/OrderEditCommand.kt @@ -0,0 +1,12 @@ +package co.nilin.opex.matching.core.inout + +import co.nilin.opex.matching.core.model.Pair + +data class OrderEditCommand( + val ouid: String, + val uuid: String, + val orderId: Long, + val pair: Pair, + val price: Long, + val quantity: Long +) \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/RejectReason.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/inout/RejectReason.kt similarity index 69% rename from MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/RejectReason.kt rename to MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/inout/RejectReason.kt index b31536a59..33aa2caa8 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/RejectReason.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/inout/RejectReason.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.matching.core.inout +package co.nilin.opex.matching.core.inout enum class RejectReason { ORDER_TYPE_NOT_MATCHED_MATCHC, ORDER_NOT_FOUND, OPERATION_NOT_MATCHED_MATCHC diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/RequestedOperation.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/inout/RequestedOperation.kt similarity index 61% rename from MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/RequestedOperation.kt rename to MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/inout/RequestedOperation.kt index c76af4201..fe3d61268 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/inout/RequestedOperation.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/inout/RequestedOperation.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.matching.core.inout +package co.nilin.opex.matching.core.inout enum class RequestedOperation { PLACE_ORDER, CANCEL_ORDER, EDIT_ORDER diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/Order.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/model/Order.kt similarity index 61% rename from MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/Order.kt rename to MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/model/Order.kt index a4d059ba0..cac761570 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/Order.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/model/Order.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.matching.core.model +package co.nilin.opex.matching.core.model interface Order{ fun id():Long? diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/OrderBook.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/model/OrderBook.kt similarity index 60% rename from MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/OrderBook.kt rename to MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/model/OrderBook.kt index 647048ce0..1de0a0cf8 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/OrderBook.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/model/OrderBook.kt @@ -1,8 +1,8 @@ -package co.nilin.mixchange.matching.core.model +package co.nilin.opex.matching.core.model -import co.nilin.mixchange.matching.core.inout.OrderCancelCommand -import co.nilin.mixchange.matching.core.inout.OrderEditCommand -import co.nilin.mixchange.matching.core.inout.OrderCreateCommand +import co.nilin.opex.matching.core.inout.OrderCancelCommand +import co.nilin.opex.matching.core.inout.OrderEditCommand +import co.nilin.opex.matching.core.inout.OrderCreateCommand interface OrderBook { fun pair(): Pair diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/OrderMetaData.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/model/OrderMetaData.kt similarity index 90% rename from MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/OrderMetaData.kt rename to MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/model/OrderMetaData.kt index 37a855c3d..379dcf2fa 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/OrderMetaData.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/model/OrderMetaData.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.matching.core.model +package co.nilin.opex.matching.core.model enum class OrderDirection { ASK, BID diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/Pair.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/model/Pair.kt similarity index 66% rename from MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/Pair.kt rename to MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/model/Pair.kt index f08edfb29..622d8e3fd 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/Pair.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/model/Pair.kt @@ -1,11 +1,10 @@ -package co.nilin.mixchange.matching.core.model +package co.nilin.opex.matching.core.model -class Pair(){ +class Pair() { lateinit var leftSideName: String lateinit var rightSideName: String - constructor(leftSideName: String - , rightSideName: String): this(){ + constructor(leftSideName: String, rightSideName: String) : this() { this.leftSideName = leftSideName this.rightSideName = rightSideName } diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/PersistentOrder.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/model/PersistentOrder.kt similarity index 95% rename from MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/PersistentOrder.kt rename to MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/model/PersistentOrder.kt index 5c11715d7..a5b19a5e1 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/PersistentOrder.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/model/PersistentOrder.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.matching.core.model +package co.nilin.opex.matching.core.model class PersistentOrder { var id: Long = 0 diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/PersistentOrderBook.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/model/PersistentOrderBook.kt similarity index 83% rename from MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/PersistentOrderBook.kt rename to MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/model/PersistentOrderBook.kt index 49aa3b208..48c6e6c8e 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/model/PersistentOrderBook.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/model/PersistentOrderBook.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.matching.core.model +package co.nilin.opex.matching.core.model class PersistentOrderBook { diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/spi/OrderBookPersister.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/spi/OrderBookPersister.kt similarity index 59% rename from MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/spi/OrderBookPersister.kt rename to MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/spi/OrderBookPersister.kt index 882f0db3d..f0adff00a 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/mixchange/matching/core/spi/OrderBookPersister.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/spi/OrderBookPersister.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.matching.core.spi +package co.nilin.opex.matching.core.spi -import co.nilin.mixchange.matching.core.model.PersistentOrderBook +import co.nilin.opex.matching.core.model.PersistentOrderBook interface OrderBookPersister { suspend fun storeLastState(orderBook: PersistentOrderBook) diff --git a/MatchingEngine/matching-core/src/test/kotlin/co/nilin/mixchange/matching/core/engine/SimpleOrderBookUnitTest.kt b/MatchingEngine/matching-core/src/test/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBookUnitTest.kt similarity index 96% rename from MatchingEngine/matching-core/src/test/kotlin/co/nilin/mixchange/matching/core/engine/SimpleOrderBookUnitTest.kt rename to MatchingEngine/matching-core/src/test/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBookUnitTest.kt index 101dc6bef..f442a0dd0 100644 --- a/MatchingEngine/matching-core/src/test/kotlin/co/nilin/mixchange/matching/core/engine/SimpleOrderBookUnitTest.kt +++ b/MatchingEngine/matching-core/src/test/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBookUnitTest.kt @@ -1,11 +1,11 @@ -package co.nilin.mixchange.matching.core.engine +package co.nilin.opex.matching.core.engine -import co.nilin.mixchange.matching.core.inout.OrderCancelCommand -import co.nilin.mixchange.matching.core.inout.OrderCreateCommand -import co.nilin.mixchange.matching.core.inout.OrderEditCommand -import co.nilin.mixchange.matching.core.model.MatchConstraint -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.OrderType +import co.nilin.opex.matching.core.inout.OrderCancelCommand +import co.nilin.opex.matching.core.inout.OrderCreateCommand +import co.nilin.opex.matching.core.inout.OrderEditCommand +import co.nilin.opex.matching.core.model.MatchConstraint +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderType import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.newSingleThreadContext import kotlinx.coroutines.runBlocking @@ -14,8 +14,8 @@ import org.junit.jupiter.api.Test import java.util.* class SimpleOrderBookUnitTest { - val pair = co.nilin.mixchange.matching.core.model.Pair("BTC", "USDT") - val ETH_BTC_PAIR = co.nilin.mixchange.matching.core.model.Pair("ETH", "BTC") + val pair = co.nilin.opex.matching.core.model.Pair("BTC", "USDT") + val ETH_BTC_PAIR = co.nilin.opex.matching.core.model.Pair("ETH", "BTC") val uuid = UUID.randomUUID().toString() @Test diff --git a/MatchingEngine/matching-ports/matching-eventlistener-kafka/pom.xml b/MatchingEngine/matching-ports/matching-eventlistener-kafka/pom.xml index 466667dd3..d3ec40a0a 100644 --- a/MatchingEngine/matching-ports/matching-eventlistener-kafka/pom.xml +++ b/MatchingEngine/matching-ports/matching-eventlistener-kafka/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex matching-eventlistener-kafka 0.0.1-SNAPSHOT matching-eventlistener-kafka - Matching engine kafka order submitter of Mixchange + Matching engine kafka order submitter of Opex 1.8 @@ -30,7 +30,7 @@ spring-boot-starter-webflux - co.nilin.mixchange + co.nilin.opex matching-core ${matching.version} provided diff --git a/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitResult.kt b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitResult.kt deleted file mode 100644 index 3e7c4b50c..000000000 --- a/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitResult.kt +++ /dev/null @@ -1,3 +0,0 @@ -package co.nilin.mixchange.port.order.kafka.inout - -class OrderSubmitResult(offset: Long?) \ No newline at end of file diff --git a/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/config/OrderKafkaConfig.kt b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/config/OrderKafkaConfig.kt similarity index 94% rename from MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/config/OrderKafkaConfig.kt rename to MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/config/OrderKafkaConfig.kt index 1007c132e..4ca2c9563 100644 --- a/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/config/OrderKafkaConfig.kt +++ b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/config/OrderKafkaConfig.kt @@ -1,8 +1,7 @@ -package co.nilin.mixchange.port.order.kafka.config +package co.nilin.opex.port.order.kafka.config - -import co.nilin.mixchange.port.order.kafka.consumer.OrderKafkaListener -import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest +import co.nilin.opex.port.order.kafka.consumer.OrderKafkaListener +import co.nilin.opex.port.order.kafka.inout.OrderSubmitRequest import org.apache.kafka.clients.admin.NewTopic import org.apache.kafka.clients.consumer.ConsumerConfig import org.apache.kafka.clients.producer.ProducerConfig @@ -28,7 +27,6 @@ import org.springframework.kafka.support.serializer.JsonDeserializer import org.springframework.kafka.support.serializer.JsonSerializer import java.util.* - @Configuration class OrderKafkaConfig() { @Value("\${spring.kafka.bootstrap-servers}") @@ -77,7 +75,7 @@ class OrderKafkaConfig() { props[ConsumerConfig.GROUP_ID_CONFIG] = groupId props[ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG] = StringDeserializer::class.java props[ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG] = JsonDeserializer::class.java - props[JsonDeserializer.TRUSTED_PACKAGES] = "co.nilin.mixchange.*" + props[JsonDeserializer.TRUSTED_PACKAGES] = "co.nilin.opex.*" return props } diff --git a/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/consumer/OrderKafkaListener.kt b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/consumer/OrderKafkaListener.kt similarity index 81% rename from MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/consumer/OrderKafkaListener.kt rename to MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/consumer/OrderKafkaListener.kt index 89ce006df..e888a6832 100644 --- a/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/consumer/OrderKafkaListener.kt +++ b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/consumer/OrderKafkaListener.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.port.order.kafka.consumer +package co.nilin.opex.port.order.kafka.consumer -import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest -import co.nilin.mixchange.port.order.kafka.spi.OrderSubmitRequestListener +import co.nilin.opex.port.order.kafka.inout.OrderSubmitRequest +import co.nilin.opex.port.order.kafka.spi.OrderSubmitRequestListener import kotlinx.coroutines.runBlocking import org.apache.kafka.clients.consumer.ConsumerRecord import org.springframework.kafka.listener.MessageListener diff --git a/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/inout/OrderSubmitRequest.kt similarity index 77% rename from MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt rename to MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/inout/OrderSubmitRequest.kt index 8b385472f..7768d1ef7 100644 --- a/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt +++ b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/inout/OrderSubmitRequest.kt @@ -1,9 +1,9 @@ -package co.nilin.mixchange.port.order.kafka.inout +package co.nilin.opex.port.order.kafka.inout -import co.nilin.mixchange.matching.core.model.MatchConstraint -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.OrderType -import co.nilin.mixchange.matching.core.model.Pair +import co.nilin.opex.matching.core.model.MatchConstraint +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderType +import co.nilin.opex.matching.core.model.Pair class OrderSubmitRequest() { diff --git a/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/inout/OrderSubmitResult.kt b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/inout/OrderSubmitResult.kt new file mode 100644 index 000000000..35d4430ec --- /dev/null +++ b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/inout/OrderSubmitResult.kt @@ -0,0 +1,3 @@ +package co.nilin.opex.port.order.kafka.inout + +class OrderSubmitResult(offset: Long?) \ No newline at end of file diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/spi/OrderSubmitRequestListener.kt b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/spi/OrderSubmitRequestListener.kt similarity index 57% rename from EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/spi/OrderSubmitRequestListener.kt rename to MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/spi/OrderSubmitRequestListener.kt index 7c4fcc473..ee659ae4d 100644 --- a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/eventlog/kafka/spi/OrderSubmitRequestListener.kt +++ b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/spi/OrderSubmitRequestListener.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.port.eventlog.kafka.spi +package co.nilin.opex.port.order.kafka.spi -import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest +import co.nilin.opex.port.order.kafka.inout.OrderSubmitRequest interface OrderSubmitRequestListener { fun id(): String diff --git a/MatchingEngine/matching-ports/matching-snapshots-redis/pom.xml b/MatchingEngine/matching-ports/matching-snapshots-redis/pom.xml index 61bc53f50..0a577b13e 100644 --- a/MatchingEngine/matching-ports/matching-snapshots-redis/pom.xml +++ b/MatchingEngine/matching-ports/matching-snapshots-redis/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex matching-snapshots-redis 0.0.1-SNAPSHOT matching-snapshots-redis - Persist Matching engine snapshot of Mixchange on Redis + Persist Matching engine snapshot of Opex on Redis 1.8 @@ -22,7 +22,7 @@ - co.nilin.mixchange + co.nilin.opex matching-core ${matching.version} provided diff --git a/MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/mixchange/port/order/redis/service/OrderBookPersister.kt b/MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/mixchange/port/order/redis/service/OrderBookPersister.kt deleted file mode 100644 index d9aacc2e0..000000000 --- a/MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/mixchange/port/order/redis/service/OrderBookPersister.kt +++ /dev/null @@ -1,24 +0,0 @@ -package co.nilin.mixchange.port.order.redis.service - -import co.nilin.mixchange.matching.core.model.PersistentOrderBook -import co.nilin.mixchange.matching.core.spi.OrderBookPersister -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.data.redis.core.ReactiveRedisTemplate -import org.springframework.stereotype.Component - -@Component -class OrderBookRedisPersister(@Qualifier("snapshotRedisTemplate") - val redisTemplate: ReactiveRedisTemplate) : OrderBookPersister { - - override suspend fun storeLastState(orderBook: PersistentOrderBook) { - redisTemplate.opsForHash() - .put("OrderbookSnapshots", orderBook.pair.toString(), orderBook) - .subscribe() - } - - override suspend fun loadLastState(symbol: String): PersistentOrderBook? = - redisTemplate.opsForHash() - .get("OrderbookSnapshots", symbol) - .blockOptional().orElse(null) - -} diff --git a/MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/mixchange/port/order/redis/config/RedisConfig.kt b/MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/opex/port/order/redis/config/RedisConfig.kt similarity index 89% rename from MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/mixchange/port/order/redis/config/RedisConfig.kt rename to MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/opex/port/order/redis/config/RedisConfig.kt index 72878bc4d..9f688593b 100644 --- a/MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/mixchange/port/order/redis/config/RedisConfig.kt +++ b/MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/opex/port/order/redis/config/RedisConfig.kt @@ -1,8 +1,6 @@ -package co.nilin.mixchange.port.order.kafka.config +package co.nilin.opex.port.order.redis.config - -import co.nilin.mixchange.matching.core.model.OrderBook -import co.nilin.mixchange.matching.core.model.PersistentOrderBook +import co.nilin.opex.matching.core.model.PersistentOrderBook import com.fasterxml.jackson.annotation.JsonAutoDetect import com.fasterxml.jackson.annotation.PropertyAccessor import com.fasterxml.jackson.databind.ObjectMapper @@ -14,7 +12,6 @@ import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer import org.springframework.data.redis.serializer.RedisSerializationContext import org.springframework.data.redis.serializer.StringRedisSerializer - @Configuration class RedisConfig() { @Bean("snapshotRedisTemplate") diff --git a/MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/opex/port/order/redis/service/OrderBookPersister.kt b/MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/opex/port/order/redis/service/OrderBookPersister.kt new file mode 100644 index 000000000..ed6c04d2d --- /dev/null +++ b/MatchingEngine/matching-ports/matching-snapshots-redis/src/main/kotlin/co/nilin/opex/port/order/redis/service/OrderBookPersister.kt @@ -0,0 +1,26 @@ +package co.nilin.opex.port.order.redis.service + +import co.nilin.opex.matching.core.model.PersistentOrderBook +import co.nilin.opex.matching.core.spi.OrderBookPersister +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.data.redis.core.ReactiveRedisTemplate +import org.springframework.stereotype.Component + +@Component +class OrderBookRedisPersister( + @Qualifier("snapshotRedisTemplate") + val redisTemplate: ReactiveRedisTemplate +) : OrderBookPersister { + + override suspend fun storeLastState(orderBook: PersistentOrderBook) { + redisTemplate.opsForHash() + .put("OrderbookSnapshots", orderBook.pair.toString(), orderBook) + .subscribe() + } + + override suspend fun loadLastState(symbol: String): PersistentOrderBook? = + redisTemplate.opsForHash() + .get("OrderbookSnapshots", symbol) + .blockOptional().orElse(null) + +} diff --git a/MatchingEngine/matching-ports/matching-submitter-kafka/pom.xml b/MatchingEngine/matching-ports/matching-submitter-kafka/pom.xml index 7014d2f7a..1642e2403 100644 --- a/MatchingEngine/matching-ports/matching-submitter-kafka/pom.xml +++ b/MatchingEngine/matching-ports/matching-submitter-kafka/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex matching-submitter-kafka 0.0.1-SNAPSHOT matching-submitter-kafka - Matching engine kafka order submitter of Mixchange + Matching engine kafka order submitter of Opex 1.8 @@ -30,7 +30,7 @@ spring-boot-starter-webflux - co.nilin.mixchange + co.nilin.opex matching-core ${matching.version} provided diff --git a/MatchingEngine/matching-ports/matching-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/config/EventsKafkaConfig.kt b/MatchingEngine/matching-ports/matching-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/config/EventsKafkaConfig.kt similarity index 96% rename from MatchingEngine/matching-ports/matching-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/config/EventsKafkaConfig.kt rename to MatchingEngine/matching-ports/matching-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/config/EventsKafkaConfig.kt index 73e84405a..0b272fa5b 100644 --- a/MatchingEngine/matching-ports/matching-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/config/EventsKafkaConfig.kt +++ b/MatchingEngine/matching-ports/matching-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/config/EventsKafkaConfig.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.port.order.kafka.config +package co.nilin.opex.port.order.kafka.config -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent +import co.nilin.opex.matching.core.eventh.events.CoreEvent import org.apache.kafka.clients.admin.AdminClientConfig import org.apache.kafka.clients.admin.NewTopic import org.apache.kafka.clients.producer.ProducerConfig diff --git a/MatchingEngine/matching-ports/matching-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/service/EventsSubmitter.kt b/MatchingEngine/matching-ports/matching-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/service/EventsSubmitter.kt similarity index 76% rename from MatchingEngine/matching-ports/matching-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/service/EventsSubmitter.kt rename to MatchingEngine/matching-ports/matching-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/service/EventsSubmitter.kt index 7a5eb28e1..ac88ed673 100644 --- a/MatchingEngine/matching-ports/matching-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/service/EventsSubmitter.kt +++ b/MatchingEngine/matching-ports/matching-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/service/EventsSubmitter.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.port.order.kafka.service +package co.nilin.opex.port.order.kafka.service -import co.nilin.mixchange.matching.core.eventh.events.CoreEvent -import co.nilin.mixchange.matching.core.eventh.events.TradeEvent +import co.nilin.opex.matching.core.eventh.events.CoreEvent +import co.nilin.opex.matching.core.eventh.events.TradeEvent import org.springframework.kafka.core.KafkaTemplate import org.springframework.stereotype.Component import kotlin.coroutines.suspendCoroutine diff --git a/MatchingEngine/pom.xml b/MatchingEngine/pom.xml index f45a5802c..e6838d569 100644 --- a/MatchingEngine/pom.xml +++ b/MatchingEngine/pom.xml @@ -2,12 +2,12 @@ 4.0.0 - co.nilin.mixchange + co.nilin.opex matching-engine 0.0.1-SNAPSHOT matching-engine pom - Matching Engine root of Mixchange + Matching Engine root of Opex matching-core diff --git a/MatchingGateway/gateway-app/pom.xml b/MatchingGateway/gateway-app/pom.xml index d34882ea5..06de82d8d 100644 --- a/MatchingGateway/gateway-app/pom.xml +++ b/MatchingGateway/gateway-app/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex matching-gateway-app 0.0.1-SNAPSHOT matching-gateway-app - Matching gateway running app Mixchange + Matching gateway running app Opex 1.8 @@ -43,12 +43,12 @@ spring-boot-starter-webflux - co.nilin.mixchange + co.nilin.opex matching-core ${matching.version} - co.nilin.mixchange + co.nilin.opex gateway-order-submitter-kafka ${matching.version} diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/spi/PairConfigLoader.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/spi/PairConfigLoader.kt deleted file mode 100644 index e0e8ebcc2..000000000 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/spi/PairConfigLoader.kt +++ /dev/null @@ -1,8 +0,0 @@ -package co.nilin.mixchange.app.spi - -import co.nilin.mixchange.app.inout.PairFeeConfig -import co.nilin.mixchange.matching.core.model.OrderDirection - -interface PairConfigLoader { - suspend fun load(pair: String, direction: OrderDirection, userLevel: String): PairFeeConfig -} \ No newline at end of file diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/MatchingGatewayApp.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/MatchingGatewayApp.kt similarity index 81% rename from MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/MatchingGatewayApp.kt rename to MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/MatchingGatewayApp.kt index 81b16fdda..c97f380a6 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/MatchingGatewayApp.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/MatchingGatewayApp.kt @@ -1,11 +1,11 @@ -package co.nilin.mixchange.app +package co.nilin.opex.app import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.annotation.ComponentScan @SpringBootApplication -@ComponentScan("co.nilin.mixchange") +@ComponentScan("co.nilin.opex") class MatchingGatewayApp fun main(args: Array) { runApplication(*args) diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/config/AppConfig.kt similarity index 82% rename from MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt rename to MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/config/AppConfig.kt index c125c5caf..e902db222 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/AppConfig.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/config/AppConfig.kt @@ -1,10 +1,10 @@ -package co.nilin.mixchange.app.config +package co.nilin.opex.app.config -import co.nilin.mixchange.app.inout.PairConfig -import co.nilin.mixchange.app.inout.PairFeeConfig -import co.nilin.mixchange.app.spi.PairConfigLoader -import co.nilin.mixchange.app.spi.AccountantApiProxy -import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.opex.app.inout.PairConfig +import co.nilin.opex.app.inout.PairFeeConfig +import co.nilin.opex.app.spi.PairConfigLoader +import co.nilin.opex.app.spi.AccountantApiProxy +import co.nilin.opex.matching.core.model.OrderDirection import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/SecurityConfig.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/config/SecurityConfig.kt similarity index 98% rename from MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/SecurityConfig.kt rename to MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/config/SecurityConfig.kt index 2048a7616..69ef4adc7 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/SecurityConfig.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/config/SecurityConfig.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.app.config +package co.nilin.opex.app.config import org.springframework.context.annotation.Bean import org.springframework.core.io.ClassPathResource diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/WebClientConfig.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/config/WebClientConfig.kt similarity index 95% rename from MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/WebClientConfig.kt rename to MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/config/WebClientConfig.kt index edbb7e6cf..b0c504071 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/config/WebClientConfig.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/config/WebClientConfig.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.app.config +package co.nilin.opex.app.config import org.springframework.cloud.client.ServiceInstance import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/controller/ControllerExceptionHandler.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/controller/ControllerExceptionHandler.kt similarity index 60% rename from MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/controller/ControllerExceptionHandler.kt rename to MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/controller/ControllerExceptionHandler.kt index 014a06fe6..37b0f744d 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/controller/ControllerExceptionHandler.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/controller/ControllerExceptionHandler.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.app.controller +package co.nilin.opex.app.controller -import co.nilin.mixchange.app.exception.NotAllowedToSubmitOrderException +import co.nilin.opex.app.exception.NotAllowedToSubmitOrderException import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.databind.ObjectMapper import org.slf4j.LoggerFactory @@ -14,10 +14,9 @@ import java.util.* @RestControllerAdvice class ControllerExceptionHandler { - data class ErrorResponse( val timestamp: Date - , val status: Int - , val error: String - , val message: String) + data class ErrorResponse( + val timestamp: Date, val status: Int, val error: String, val message: String + ) val logger = LoggerFactory.getLogger(ControllerExceptionHandler::class.java) @@ -25,11 +24,12 @@ class ControllerExceptionHandler { @ExceptionHandler(NotAllowedToSubmitOrderException::class) fun handle(ex: NotAllowedToSubmitOrderException): ResponseEntity { - logger.error("Trace Error {}", ex ) - val ret = ResponseEntity.status(500).body(ErrorResponse(Date() - , -1 - , ex::class.qualifiedName ?: "" - , ex.message ?: "")) + logger.error("Trace Error {}", ex) + val ret = ResponseEntity.status(500).body( + ErrorResponse( + Date(), -1, ex::class.qualifiedName ?: "", ex.message ?: "" + ) + ) logger.debug("return error response:{}", ret) return ret } @@ -57,21 +57,29 @@ class ControllerExceptionHandler { @ExceptionHandler(WebClientResponseException::class) fun handle(ex: WebClientResponseException): ResponseEntity { - logger.error("Trace Error {}", ex ) + logger.error("Trace Error {}", ex) try { - val body = objectMapper.readValue(ex.responseBodyAsByteArray.toString(StandardCharsets.UTF_8), WebClientErrorResponse::class.java) - val ret = ResponseEntity.status(body.status ?: ex.rawStatusCode).body(ErrorResponse(Date() - , body.status ?: ex.rawStatusCode - , body.error ?: ex::class.qualifiedName ?: "" - , body.message ?: "Internal Server Error")) + val body = objectMapper.readValue( + ex.responseBodyAsByteArray.toString(StandardCharsets.UTF_8), + WebClientErrorResponse::class.java + ) + val ret = ResponseEntity.status(body.status ?: ex.rawStatusCode).body( + ErrorResponse( + Date(), + body.status ?: ex.rawStatusCode, + body.error ?: ex::class.qualifiedName ?: "", + body.message ?: "Internal Server Error" + ) + ) logger.debug("return error response:{}", ret) return ret } catch (je: Exception) { - logger.error("Trace Error {}", je ) - val ret = ResponseEntity.status(ex.statusCode).body(ErrorResponse(Date() - , ex.rawStatusCode - , ex::class.qualifiedName ?: "" - , "Internal Server Error")) + logger.error("Trace Error {}", je) + val ret = ResponseEntity.status(ex.statusCode).body( + ErrorResponse( + Date(), ex.rawStatusCode, ex::class.qualifiedName ?: "", "Internal Server Error" + ) + ) logger.debug("return error response:{}", ret) return ret } @@ -79,11 +87,12 @@ class ControllerExceptionHandler { @ExceptionHandler(Throwable::class) fun handle(ex: Throwable): ResponseEntity { - logger.error("Trace Error {}", ex ) - val ret = ResponseEntity.status(500).body(ErrorResponse(Date() - , 500 - , ex::class.qualifiedName ?: "" - , "Internal Server Error")) + logger.error("Trace Error {}", ex) + val ret = ResponseEntity.status(500).body( + ErrorResponse( + Date(), 500, ex::class.qualifiedName ?: "", "Internal Server Error" + ) + ) logger.debug("return error response:{}", ret) return ret } diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/controller/OrderController.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/controller/OrderController.kt similarity index 52% rename from MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/controller/OrderController.kt rename to MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/controller/OrderController.kt index 8c5d63349..7cec99df2 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/controller/OrderController.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/controller/OrderController.kt @@ -1,14 +1,14 @@ -package co.nilin.mixchange.app.controller +package co.nilin.opex.app.controller -import co.nilin.mixchange.app.inout.CreateOrderRequest -import co.nilin.mixchange.app.service.OrderService -import co.nilin.mixchange.matching.core.model.MatchConstraint -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.OrderType -import co.nilin.mixchange.matching.core.model.Pair -import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest -import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitResult -import co.nilin.mixchange.port.order.kafka.service.OrderSubmitter +import co.nilin.opex.app.inout.CreateOrderRequest +import co.nilin.opex.app.service.OrderService +import co.nilin.opex.matching.core.model.MatchConstraint +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderType +import co.nilin.opex.matching.core.model.Pair +import co.nilin.opex.port.order.kafka.inout.OrderSubmitRequest +import co.nilin.opex.port.order.kafka.inout.OrderSubmitResult +import co.nilin.opex.port.order.kafka.service.OrderSubmitter import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/exception/NotAllowedToSubmitOrderException.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/exception/NotAllowedToSubmitOrderException.kt similarity index 70% rename from MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/exception/NotAllowedToSubmitOrderException.kt rename to MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/exception/NotAllowedToSubmitOrderException.kt index 175d08583..28cb47ced 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/exception/NotAllowedToSubmitOrderException.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/exception/NotAllowedToSubmitOrderException.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.app.exception +package co.nilin.opex.app.exception import java.lang.RuntimeException diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/inout/CreateOrderRequest.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/inout/CreateOrderRequest.kt similarity index 55% rename from MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/inout/CreateOrderRequest.kt rename to MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/inout/CreateOrderRequest.kt index fbf104120..1b9d84bc5 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/inout/CreateOrderRequest.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/inout/CreateOrderRequest.kt @@ -1,8 +1,8 @@ -package co.nilin.mixchange.app.inout +package co.nilin.opex.app.inout -import co.nilin.mixchange.matching.core.model.MatchConstraint -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.OrderType +import co.nilin.opex.matching.core.model.MatchConstraint +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderType import java.math.BigDecimal data class CreateOrderRequest( diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/inout/PairConfig.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/inout/PairConfig.kt similarity index 87% rename from MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/inout/PairConfig.kt rename to MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/inout/PairConfig.kt index 1924f2062..3cc48f34e 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/inout/PairConfig.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/inout/PairConfig.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.app.inout +package co.nilin.opex.app.inout class PairConfig( val pair: String, diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/inout/PairFeeConfig.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/inout/PairFeeConfig.kt similarity index 81% rename from MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/inout/PairFeeConfig.kt rename to MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/inout/PairFeeConfig.kt index cd232a08d..43d92f866 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/inout/PairFeeConfig.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/inout/PairFeeConfig.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.app.inout +package co.nilin.opex.app.inout class PairFeeConfig( val pairConfig: PairConfig, diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/proxy/AccountantProxyImpl.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/proxy/AccountantProxyImpl.kt similarity index 90% rename from MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/proxy/AccountantProxyImpl.kt rename to MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/proxy/AccountantProxyImpl.kt index a71ac160a..a1c4bfcd7 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/proxy/AccountantProxyImpl.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/proxy/AccountantProxyImpl.kt @@ -1,9 +1,8 @@ -package co.nilin.mixchange.port.gateway.wallet.proxy +package co.nilin.opex.app.proxy - -import co.nilin.mixchange.app.inout.PairFeeConfig -import co.nilin.mixchange.app.spi.AccountantApiProxy -import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.opex.app.inout.PairFeeConfig +import co.nilin.opex.app.spi.AccountantApiProxy +import co.nilin.opex.matching.core.model.OrderDirection import kotlinx.coroutines.reactive.awaitFirst import org.springframework.beans.factory.annotation.Value import org.springframework.core.ParameterizedTypeReference diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/service/OrderService.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/service/OrderService.kt similarity index 77% rename from MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/service/OrderService.kt rename to MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/service/OrderService.kt index b61d8ca67..530d2cd8f 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/service/OrderService.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/service/OrderService.kt @@ -1,14 +1,14 @@ -package co.nilin.mixchange.app.service +package co.nilin.opex.app.service -import co.nilin.mixchange.app.exception.NotAllowedToSubmitOrderException -import co.nilin.mixchange.app.inout.CreateOrderRequest -import co.nilin.mixchange.app.spi.AccountantApiProxy -import co.nilin.mixchange.app.spi.PairConfigLoader -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.Pair -import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest -import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitResult -import co.nilin.mixchange.port.order.kafka.service.OrderSubmitter +import co.nilin.opex.app.exception.NotAllowedToSubmitOrderException +import co.nilin.opex.app.inout.CreateOrderRequest +import co.nilin.opex.app.spi.AccountantApiProxy +import co.nilin.opex.app.spi.PairConfigLoader +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.Pair +import co.nilin.opex.port.order.kafka.inout.OrderSubmitRequest +import co.nilin.opex.port.order.kafka.inout.OrderSubmitResult +import co.nilin.opex.port.order.kafka.service.OrderSubmitter import org.springframework.stereotype.Service import java.util.* diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/spi/AccountantApiProxy.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/spi/AccountantApiProxy.kt similarity index 64% rename from MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/spi/AccountantApiProxy.kt rename to MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/spi/AccountantApiProxy.kt index 447c5cb74..3d0e8b4dd 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/spi/AccountantApiProxy.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/spi/AccountantApiProxy.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.app.spi +package co.nilin.opex.app.spi -import co.nilin.mixchange.app.inout.PairFeeConfig -import co.nilin.mixchange.matching.core.model.OrderDirection +import co.nilin.opex.app.inout.PairFeeConfig +import co.nilin.opex.matching.core.model.OrderDirection import java.math.BigDecimal diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/spi/PairConfigLoader.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/spi/PairConfigLoader.kt new file mode 100644 index 000000000..c2108a08b --- /dev/null +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/spi/PairConfigLoader.kt @@ -0,0 +1,8 @@ +package co.nilin.opex.app.spi + +import co.nilin.opex.app.inout.PairFeeConfig +import co.nilin.opex.matching.core.model.OrderDirection + +interface PairConfigLoader { + suspend fun load(pair: String, direction: OrderDirection, userLevel: String): PairFeeConfig +} \ No newline at end of file diff --git a/MatchingGateway/gateway-port/order-submitter-kafka/pom.xml b/MatchingGateway/gateway-port/order-submitter-kafka/pom.xml index fbca58507..fe578663d 100644 --- a/MatchingGateway/gateway-port/order-submitter-kafka/pom.xml +++ b/MatchingGateway/gateway-port/order-submitter-kafka/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex gateway-order-submitter-kafka 0.0.1-SNAPSHOT gateway-order-submitter-kafka - Matching gateway kafka order submitter of Mixchange + Matching gateway kafka order submitter of Opex 1.8 @@ -30,7 +30,7 @@ spring-boot-starter-webflux - co.nilin.mixchange + co.nilin.opex matching-core ${matching.version} provided diff --git a/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitResult.kt b/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitResult.kt deleted file mode 100644 index 3e7c4b50c..000000000 --- a/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitResult.kt +++ /dev/null @@ -1,3 +0,0 @@ -package co.nilin.mixchange.port.order.kafka.inout - -class OrderSubmitResult(offset: Long?) \ No newline at end of file diff --git a/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/config/OrderKafkaConfig.kt b/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/config/OrderKafkaConfig.kt similarity index 93% rename from MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/config/OrderKafkaConfig.kt rename to MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/config/OrderKafkaConfig.kt index 69c15da0b..e4900a5a5 100644 --- a/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/config/OrderKafkaConfig.kt +++ b/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/config/OrderKafkaConfig.kt @@ -1,8 +1,8 @@ -package co.nilin.mixchange.port.order.kafka.config +package co.nilin.opex.port.order.kafka.config -import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest +import co.nilin.opex.port.order.kafka.inout.OrderSubmitRequest import org.apache.kafka.clients.producer.ProducerConfig import org.apache.kafka.common.serialization.StringSerializer import org.springframework.beans.factory.annotation.Qualifier diff --git a/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt b/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/inout/OrderSubmitRequest.kt similarity index 77% rename from MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt rename to MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/inout/OrderSubmitRequest.kt index 8b385472f..7768d1ef7 100644 --- a/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/inout/OrderSubmitRequest.kt +++ b/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/inout/OrderSubmitRequest.kt @@ -1,9 +1,9 @@ -package co.nilin.mixchange.port.order.kafka.inout +package co.nilin.opex.port.order.kafka.inout -import co.nilin.mixchange.matching.core.model.MatchConstraint -import co.nilin.mixchange.matching.core.model.OrderDirection -import co.nilin.mixchange.matching.core.model.OrderType -import co.nilin.mixchange.matching.core.model.Pair +import co.nilin.opex.matching.core.model.MatchConstraint +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.matching.core.model.OrderType +import co.nilin.opex.matching.core.model.Pair class OrderSubmitRequest() { diff --git a/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/inout/OrderSubmitResult.kt b/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/inout/OrderSubmitResult.kt new file mode 100644 index 000000000..35d4430ec --- /dev/null +++ b/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/inout/OrderSubmitResult.kt @@ -0,0 +1,3 @@ +package co.nilin.opex.port.order.kafka.inout + +class OrderSubmitResult(offset: Long?) \ No newline at end of file diff --git a/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/service/OrderSubmitter.kt b/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/service/OrderSubmitter.kt similarity index 82% rename from MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/service/OrderSubmitter.kt rename to MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/service/OrderSubmitter.kt index be061b215..d8cd3d16e 100644 --- a/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/mixchange/port/order/kafka/service/OrderSubmitter.kt +++ b/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/service/OrderSubmitter.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.port.order.kafka.service +package co.nilin.opex.port.order.kafka.service -import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest -import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitResult +import co.nilin.opex.port.order.kafka.inout.OrderSubmitRequest +import co.nilin.opex.port.order.kafka.inout.OrderSubmitResult import org.springframework.kafka.core.KafkaTemplate import org.springframework.stereotype.Component import kotlin.coroutines.resume diff --git a/MatchingGateway/pom.xml b/MatchingGateway/pom.xml index bb82c2d18..90493a8c8 100644 --- a/MatchingGateway/pom.xml +++ b/MatchingGateway/pom.xml @@ -2,12 +2,12 @@ 4.0.0 - co.nilin.mixchange + co.nilin.opex matching-gateway-root 0.0.1-SNAPSHOT matching-gateway-root pom - Matching Api Gateway root of Mixchange + Matching Api Gateway root of Opex gateway-app gateway-port/order-submitter-kafka diff --git a/UserManagement/keycloak-gateway/pom.xml b/UserManagement/keycloak-gateway/pom.xml index d2a77def2..5413fa929 100644 --- a/UserManagement/keycloak-gateway/pom.xml +++ b/UserManagement/keycloak-gateway/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex keycloak-gateway 0.0.1-SNAPSHOT keycloak-gateway - Keycloak gateway app Mixchange + Keycloak gateway app Opex 13 diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/ApplicationContextHolder.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/ApplicationContextHolder.kt similarity index 77% rename from UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/ApplicationContextHolder.kt rename to UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/ApplicationContextHolder.kt index f185cb3f3..29ba3db07 100644 --- a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/ApplicationContextHolder.kt +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/ApplicationContextHolder.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.auth.gateway +package co.nilin.opex.auth.gateway import org.springframework.context.ApplicationContext @@ -6,6 +6,6 @@ class ApplicationContextHolder { companion object { var applicationContext: ApplicationContext? = null fun getCurrentContext(): ApplicationContext? { return applicationContext } - fun setCurrentContext(applicationContext: ApplicationContext) { this.applicationContext = applicationContext } + fun setCurrentContext(applicationContext: ApplicationContext) { Companion.applicationContext = applicationContext } } } \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/KeycloakGatewayApp.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/KeycloakGatewayApp.kt similarity index 78% rename from UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/KeycloakGatewayApp.kt rename to UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/KeycloakGatewayApp.kt index f858f6ab0..029650524 100644 --- a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/KeycloakGatewayApp.kt +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/KeycloakGatewayApp.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.auth.gateway +package co.nilin.opex.auth.gateway -import co.nilin.mixchange.auth.gateway.config.KeycloakServerProperties -import co.nilin.mixchange.auth.gateway.config.SimplePlatformProvider +import co.nilin.opex.auth.gateway.config.KeycloakServerProperties +import co.nilin.opex.auth.gateway.config.SimplePlatformProvider import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.boot.autoconfigure.SpringBootApplication @@ -16,7 +16,7 @@ import org.springframework.context.annotation.ComponentScan import org.springframework.core.io.ClassPathResource @SpringBootApplication(exclude = [LiquibaseAutoConfiguration::class]) -@ComponentScan(basePackages = arrayOf("co.nilin.mixchange.auth.gateway")) +@ComponentScan(basePackages = arrayOf("co.nilin.opex.auth.gateway")) @EnableConfigurationProperties class KeycloakGatewayApp diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/AppConfig.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/AppConfig.kt similarity index 54% rename from UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/AppConfig.kt rename to UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/AppConfig.kt index f87400ec7..dcd562eff 100644 --- a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/AppConfig.kt +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/AppConfig.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.auth.gateway.config +package co.nilin.opex.auth.gateway.config -import co.nilin.mixchange.auth.gateway.KeycloakGatewayApp +import co.nilin.opex.auth.gateway.KeycloakGatewayApp import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.boot.autoconfigure.web.ServerProperties @@ -15,14 +15,13 @@ class AppConfig { @Bean fun onApplicationReadyEventListener( - serverProperties:ServerProperties, - keycloakServerProperties: KeycloakServerProperties - ):ApplicationListener - { + serverProperties: ServerProperties, + keycloakServerProperties: KeycloakServerProperties + ): ApplicationListener { return ApplicationListener { evt -> - val port = serverProperties.port - val keycloakContextPath = keycloakServerProperties.contextPath - LOG.info("Embedded Keycloak started: http://localhost:{}{} to use keycloak", port, keycloakContextPath) + val port = serverProperties.port + val keycloakContextPath = keycloakServerProperties.contextPath + LOG.info("Embedded Keycloak started: http://localhost:{}{} to use keycloak", port, keycloakContextPath) } } } diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakApplication.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/EmbeddedKeycloakApplication.kt similarity index 95% rename from UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakApplication.kt rename to UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/EmbeddedKeycloakApplication.kt index 43fbcb57f..496e1d39c 100644 --- a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakApplication.kt +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/EmbeddedKeycloakApplication.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.auth.gateway.config +package co.nilin.opex.auth.gateway.config import org.keycloak.Config import org.keycloak.representations.idm.RealmRepresentation @@ -31,7 +31,7 @@ class EmbeddedKeycloakApplication() : KeycloakApplication() { init { createMasterRealmAdminUser() - createMixchangeRealm() + createOpexRealm() } override fun loadConfig() { @@ -55,7 +55,7 @@ class EmbeddedKeycloakApplication() : KeycloakApplication() { session.close() } - private fun createMixchangeRealm() { + private fun createOpexRealm() { val session = getSessionFactory().create() try { session.transactionManager.begin() diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakConfig.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/EmbeddedKeycloakConfig.kt similarity index 98% rename from UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakConfig.kt rename to UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/EmbeddedKeycloakConfig.kt index b0892e728..cf4c51004 100644 --- a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakConfig.kt +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/EmbeddedKeycloakConfig.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.auth.gateway.config +package co.nilin.opex.auth.gateway.config import org.jboss.resteasy.plugins.server.servlet.HttpServlet30Dispatcher import org.jboss.resteasy.plugins.server.servlet.ResteasyContextParameters diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakRequestFilter.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/EmbeddedKeycloakRequestFilter.kt similarity index 97% rename from UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakRequestFilter.kt rename to UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/EmbeddedKeycloakRequestFilter.kt index f6f434f68..b9c0637ae 100644 --- a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/EmbeddedKeycloakRequestFilter.kt +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/EmbeddedKeycloakRequestFilter.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.auth.gateway.config +package co.nilin.opex.auth.gateway.config import org.keycloak.common.ClientConnection import org.keycloak.models.KeycloakSession diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/KafkaConfig.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/KafkaConfig.kt similarity index 95% rename from UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/KafkaConfig.kt rename to UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/KafkaConfig.kt index aa6abae40..4c4103150 100644 --- a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/KafkaConfig.kt +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/KafkaConfig.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.auth.gateway.config +package co.nilin.opex.auth.gateway.config -import co.nilin.mixchange.auth.gateway.model.AuthEvent +import co.nilin.opex.auth.gateway.model.AuthEvent import org.apache.kafka.clients.admin.NewTopic import org.apache.kafka.common.serialization.StringSerializer import org.apache.kafka.clients.producer.ProducerConfig diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/KeycloakServerProperties.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/KeycloakServerProperties.kt similarity index 90% rename from UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/KeycloakServerProperties.kt rename to UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/KeycloakServerProperties.kt index d7fc0fb32..73f2a7645 100644 --- a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/KeycloakServerProperties.kt +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/KeycloakServerProperties.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.auth.gateway.config +package co.nilin.opex.auth.gateway.config import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.context.annotation.Configuration diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/RegularJsonConfigProviderFactory.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/RegularJsonConfigProviderFactory.kt similarity index 73% rename from UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/RegularJsonConfigProviderFactory.kt rename to UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/RegularJsonConfigProviderFactory.kt index 6283ff4d3..9d1f51313 100644 --- a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/RegularJsonConfigProviderFactory.kt +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/RegularJsonConfigProviderFactory.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.auth.gateway.config +package co.nilin.opex.auth.gateway.config import org.keycloak.services.util.JsonConfigProviderFactory diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/Resteasy3Provider.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/Resteasy3Provider.kt similarity index 93% rename from UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/Resteasy3Provider.kt rename to UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/Resteasy3Provider.kt index 99615d022..19bc13fd4 100644 --- a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/Resteasy3Provider.kt +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/Resteasy3Provider.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.auth.gateway.config +package co.nilin.opex.auth.gateway.config import org.jboss.resteasy.core.Dispatcher import org.jboss.resteasy.spi.ResteasyProviderFactory diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/SimplePlatformProvider.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/SimplePlatformProvider.kt similarity index 93% rename from UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/SimplePlatformProvider.kt rename to UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/SimplePlatformProvider.kt index 474164204..bb6cc7f4c 100644 --- a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/SimplePlatformProvider.kt +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/SimplePlatformProvider.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.auth.gateway.config +package co.nilin.opex.auth.gateway.config import org.keycloak.services.ServicesLogger diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/SystemPropertyConfig.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/SystemPropertyConfig.kt similarity index 97% rename from UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/SystemPropertyConfig.kt rename to UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/SystemPropertyConfig.kt index e308ad26a..9fe7d25c3 100644 --- a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/config/SystemPropertyConfig.kt +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/config/SystemPropertyConfig.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.auth.gateway.config +package co.nilin.opex.auth.gateway.config import org.springframework.beans.factory.BeanFactory import org.springframework.beans.factory.annotation.Autowired diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/extension/ExtendedEventListenerProvider.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/extension/ExtendedEventListenerProvider.kt similarity index 95% rename from UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/extension/ExtendedEventListenerProvider.kt rename to UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/extension/ExtendedEventListenerProvider.kt index 1ddaab25a..a29f9ffc3 100644 --- a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/extension/ExtendedEventListenerProvider.kt +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/extension/ExtendedEventListenerProvider.kt @@ -1,8 +1,8 @@ -package co.nilin.mixchange.auth.gateway.extension +package co.nilin.opex.auth.gateway.extension -import co.nilin.mixchange.auth.gateway.ApplicationContextHolder -import co.nilin.mixchange.auth.gateway.model.AuthEvent -import co.nilin.mixchange.auth.gateway.model.UserCreatedEvent +import co.nilin.opex.auth.gateway.ApplicationContextHolder +import co.nilin.opex.auth.gateway.model.AuthEvent +import co.nilin.opex.auth.gateway.model.UserCreatedEvent import com.fasterxml.jackson.databind.DeserializationFeature import org.keycloak.events.Event import org.keycloak.events.EventListenerProvider diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/extension/ExtendedEventListenerProviderFactory.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/extension/ExtendedEventListenerProviderFactory.kt similarity index 93% rename from UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/extension/ExtendedEventListenerProviderFactory.kt rename to UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/extension/ExtendedEventListenerProviderFactory.kt index 18bae2d5b..b9b381619 100644 --- a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/extension/ExtendedEventListenerProviderFactory.kt +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/extension/ExtendedEventListenerProviderFactory.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.auth.gateway.extension +package co.nilin.opex.auth.gateway.extension import org.keycloak.Config import org.keycloak.events.EventListenerProviderFactory diff --git a/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/AuthEvent.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/model/AuthEvent.kt similarity index 70% rename from Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/AuthEvent.kt rename to UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/model/AuthEvent.kt index 8f774787a..0ecf3780e 100644 --- a/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/AuthEvent.kt +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/model/AuthEvent.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.auth.gateway.model +package co.nilin.opex.auth.gateway.model import java.time.LocalDateTime diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/UserCreatedEvent.kt b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/model/UserCreatedEvent.kt similarity index 92% rename from UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/UserCreatedEvent.kt rename to UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/model/UserCreatedEvent.kt index cf2bd4213..e80ade9ca 100644 --- a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/UserCreatedEvent.kt +++ b/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/opex/auth/gateway/model/UserCreatedEvent.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.auth.gateway.model +package co.nilin.opex.auth.gateway.model class UserCreatedEvent: AuthEvent { lateinit var uuid: String diff --git a/UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.common.util.ResteasyProvider b/UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.common.util.ResteasyProvider index 0f0d10a36..86578665f 100644 --- a/UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.common.util.ResteasyProvider +++ b/UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.common.util.ResteasyProvider @@ -1 +1 @@ -co.nilin.mixchange.auth.gateway.config.Resteasy3Provider \ No newline at end of file +co.nilin.opex.auth.gateway.config.Resteasy3Provider \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory b/UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory index 40f8f13db..6cffc650f 100644 --- a/UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory +++ b/UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory @@ -1 +1 @@ -co.nilin.mixchange.auth.gateway.extension.ExtendedEventListenerProviderFactory \ No newline at end of file +co.nilin.opex.auth.gateway.extension.ExtendedEventListenerProviderFactory \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.platform.PlatformProvider b/UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.platform.PlatformProvider index e4a4f5850..a30ad70f1 100644 --- a/UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.platform.PlatformProvider +++ b/UserManagement/keycloak-gateway/src/main/resources/META-INF/services/org.keycloak.platform.PlatformProvider @@ -1 +1 @@ -co.nilin.mixchange.auth.gateway.config.SimplePlatformProvider \ No newline at end of file +co.nilin.opex.auth.gateway.config.SimplePlatformProvider \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/resources/opex-realm.json b/UserManagement/keycloak-gateway/src/main/resources/opex-realm.json index 802eb8c84..d85b18801 100644 --- a/UserManagement/keycloak-gateway/src/main/resources/opex-realm.json +++ b/UserManagement/keycloak-gateway/src/main/resources/opex-realm.json @@ -1,6 +1,6 @@ { - "id" : "mixchange", - "realm" : "mixchange", + "id" : "opex", + "realm" : "opex", "notBefore" : 0, "revokeRefreshToken" : false, "refreshTokenMaxReuse" : 0, @@ -43,7 +43,7 @@ "description" : "${role_offline-access}", "composite" : false, "clientRole" : false, - "containerId" : "mixchange", + "containerId" : "opex", "attributes" : { } }, { "id" : "0dd6a8c7-d669-4941-9ea1-521980e9c53f", @@ -51,14 +51,14 @@ "description" : "${role_uma_authorization}", "composite" : false, "clientRole" : false, - "containerId" : "mixchange", + "containerId" : "opex", "attributes" : { } }, { "id" : "ca962095-7f9b-49e2-a190-e391a0d4b704", "name" : "user", "composite" : false, "clientRole" : false, - "containerId" : "mixchange", + "containerId" : "opex", "attributes" : { } } ], "client" : { @@ -348,13 +348,13 @@ "clientId" : "account", "name" : "${client_account}", "rootUrl" : "${authBaseUrl}", - "baseUrl" : "/realms/mixchange/account/", + "baseUrl" : "/realms/opex/account/", "surrogateAuthRequired" : false, "enabled" : true, "clientAuthenticatorType" : "client-secret", "secret" : "**********", "defaultRoles" : [ "manage-account", "view-profile" ], - "redirectUris" : [ "/realms/mixchange/account/*" ], + "redirectUris" : [ "/realms/opex/account/*" ], "webOrigins" : [ ], "notBefore" : 0, "bearerOnly" : false, @@ -496,12 +496,12 @@ "clientId" : "security-admin-console", "name" : "${client_security-admin-console}", "rootUrl" : "${authAdminUrl}", - "baseUrl" : "/admin/mixchange/console/", + "baseUrl" : "/admin/opex/console/", "surrogateAuthRequired" : false, "enabled" : true, "clientAuthenticatorType" : "client-secret", "secret" : "**********", - "redirectUris" : [ "/admin/mixchange/console/*" ], + "redirectUris" : [ "/admin/opex/console/*" ], "webOrigins" : [ "+" ], "notBefore" : 0, "bearerOnly" : false, diff --git a/UserManagement/pom.xml b/UserManagement/pom.xml index 17d86d7e6..6f820e2db 100644 --- a/UserManagement/pom.xml +++ b/UserManagement/pom.xml @@ -2,12 +2,12 @@ 4.0.0 - co.nilin.mixchange + co.nilin.opex user-management-root 0.0.1-SNAPSHOT user-management-root pom - User Management root of Mixchange + User Management root of Opex keycloak-gateway diff --git a/Wallet/pom.xml b/Wallet/pom.xml index 96b92f1c3..b7d01cd20 100644 --- a/Wallet/pom.xml +++ b/Wallet/pom.xml @@ -2,12 +2,12 @@ 4.0.0 - co.nilin.mixchange + co.nilin.opex wallets 0.0.1-SNAPSHOT wallets pom - Wallet managment root of Mixchange + Wallet managment root of Opex wallet-core diff --git a/Wallet/wallet-app/pom.xml b/Wallet/wallet-app/pom.xml index 59a665e79..e8f736272 100644 --- a/Wallet/wallet-app/pom.xml +++ b/Wallet/wallet-app/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex wallet-app 0.0.1-SNAPSHOT wallet-app - Wallet managment app of Mixchange + Wallet managment app of Opex 1.8 @@ -72,17 +72,17 @@ test - co.nilin.mixchange + co.nilin.opex wallet-core ${wallet.version} - co.nilin.mixchange + co.nilin.opex wallet-persister-postgres ${wallet.version} - co.nilin.mixchange + co.nilin.opex wallet-eventlistener-kafka ${wallet.version} diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/WalletApp.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/WalletApp.kt similarity index 79% rename from Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/WalletApp.kt rename to Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/WalletApp.kt index a02438a63..2c3b013d2 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/WalletApp.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/WalletApp.kt @@ -1,11 +1,11 @@ -package co.nilin.mixchange.wallet.app +package co.nilin.opex.wallet.app import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.annotation.ComponentScan @SpringBootApplication -@ComponentScan("co.nilin.mixchange") +@ComponentScan("co.nilin.opex") class WalletApp fun main(args: Array) { diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/AppConfig.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/AppConfig.kt similarity index 66% rename from Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/AppConfig.kt rename to Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/AppConfig.kt index 044d24af9..4b7dd85ce 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/AppConfig.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/AppConfig.kt @@ -1,20 +1,14 @@ -package co.nilin.mixchange.wallet.app.config - -import co.nilin.mixchange.auth.gateway.model.UserCreatedEvent -import co.nilin.mixchange.port.wallet.consumer.UserCreatedKafkaListener -import co.nilin.mixchange.port.wallet.spi.UserCreatedEventListener -import co.nilin.mixchange.wallet.app.controller.Symbol -import co.nilin.mixchange.wallet.app.service.UserRegistrationService -import co.nilin.mixchange.wallet.core.model.Amount -import co.nilin.mixchange.wallet.core.spi.WalletManager -import co.nilin.mixchange.wallet.core.spi.WalletOwnerManager +package co.nilin.opex.wallet.app.config + +import co.nilin.opex.auth.gateway.model.UserCreatedEvent +import co.nilin.opex.port.wallet.kafka.consumer.UserCreatedKafkaListener +import co.nilin.opex.port.wallet.kafka.spi.UserCreatedEventListener +import co.nilin.opex.wallet.app.service.UserRegistrationService import kotlinx.coroutines.runBlocking import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Value -import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional import java.math.BigDecimal @Configuration diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/AppDispatchers.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/AppDispatchers.kt similarity index 81% rename from Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/AppDispatchers.kt rename to Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/AppDispatchers.kt index ec376a62d..285781ccc 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/AppDispatchers.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/AppDispatchers.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.wallet.app.config +package co.nilin.opex.wallet.app.config import kotlinx.coroutines.asCoroutineDispatcher import java.util.concurrent.Executors diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/SecurityConfig.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/SecurityConfig.kt similarity index 97% rename from Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/SecurityConfig.kt rename to Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/SecurityConfig.kt index 3ab9013d9..5763505f3 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/config/SecurityConfig.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/SecurityConfig.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.wallet.app.config +package co.nilin.opex.wallet.app.config import org.springframework.context.annotation.Bean import org.springframework.core.io.ClassPathResource diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/BalanceController.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/controller/BalanceController.kt similarity index 87% rename from Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/BalanceController.kt rename to Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/controller/BalanceController.kt index 4ab005ffc..14ef1720a 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/BalanceController.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/controller/BalanceController.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.wallet.app.controller +package co.nilin.opex.wallet.app.controller -import co.nilin.mixchange.wallet.core.spi.WalletManager -import co.nilin.mixchange.wallet.core.spi.WalletOwnerManager +import co.nilin.opex.wallet.core.spi.WalletManager +import co.nilin.opex.wallet.core.spi.WalletOwnerManager import org.slf4j.LoggerFactory import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/InquiryController.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/controller/InquiryController.kt similarity index 86% rename from Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/InquiryController.kt rename to Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/controller/InquiryController.kt index 97e5b044b..d52a520c8 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/InquiryController.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/controller/InquiryController.kt @@ -1,8 +1,8 @@ -package co.nilin.mixchange.wallet.app.controller +package co.nilin.opex.wallet.app.controller -import co.nilin.mixchange.wallet.core.model.Amount -import co.nilin.mixchange.wallet.core.spi.WalletManager -import co.nilin.mixchange.wallet.core.spi.WalletOwnerManager +import co.nilin.opex.wallet.core.model.Amount +import co.nilin.opex.wallet.core.spi.WalletManager +import co.nilin.opex.wallet.core.spi.WalletOwnerManager import org.slf4j.LoggerFactory import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/Symbol.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/controller/Symbol.kt similarity index 73% rename from Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/Symbol.kt rename to Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/controller/Symbol.kt index 80d8174f0..cff978685 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/Symbol.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/controller/Symbol.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.wallet.app.controller +package co.nilin.opex.wallet.app.controller -import co.nilin.mixchange.wallet.core.model.Currency +import co.nilin.opex.wallet.core.model.Currency class Symbol(val symbol_: String): Currency { override fun getSymbol(): String { diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/TransferController.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/controller/TransferController.kt similarity index 83% rename from Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/TransferController.kt rename to Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/controller/TransferController.kt index 5da36373b..e8958454d 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/TransferController.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/controller/TransferController.kt @@ -1,11 +1,11 @@ -package co.nilin.mixchange.wallet.app.controller +package co.nilin.opex.wallet.app.controller -import co.nilin.mixchange.wallet.core.inout.TransferCommand -import co.nilin.mixchange.wallet.core.inout.TransferResult -import co.nilin.mixchange.wallet.core.model.Amount -import co.nilin.mixchange.wallet.core.service.TransferService -import co.nilin.mixchange.wallet.core.spi.WalletManager -import co.nilin.mixchange.wallet.core.spi.WalletOwnerManager +import co.nilin.opex.wallet.core.inout.TransferCommand +import co.nilin.opex.wallet.core.inout.TransferResult +import co.nilin.opex.wallet.core.model.Amount +import co.nilin.opex.wallet.core.service.TransferService +import co.nilin.opex.wallet.core.spi.WalletManager +import co.nilin.opex.wallet.core.spi.WalletOwnerManager import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RestController diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/WalletOwnerController.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/controller/WalletOwnerController.kt similarity index 89% rename from Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/WalletOwnerController.kt rename to Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/controller/WalletOwnerController.kt index f4e0f854c..1940dd245 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/WalletOwnerController.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/controller/WalletOwnerController.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.wallet.app.controller +package co.nilin.opex.wallet.app.controller -import co.nilin.mixchange.wallet.core.spi.WalletManager -import co.nilin.mixchange.wallet.core.spi.WalletOwnerManager +import co.nilin.opex.wallet.core.spi.WalletManager +import co.nilin.opex.wallet.core.spi.WalletOwnerManager import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RestController import java.math.BigDecimal diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/listener/WalletListenerImpl.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/listener/WalletListenerImpl.kt similarity index 66% rename from Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/listener/WalletListenerImpl.kt rename to Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/listener/WalletListenerImpl.kt index 0f06e3d4d..083b9d9aa 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/listener/WalletListenerImpl.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/listener/WalletListenerImpl.kt @@ -1,8 +1,8 @@ -package co.nilin.mixchange.wallet.app.listener +package co.nilin.opex.wallet.app.listener -import co.nilin.mixchange.wallet.core.model.Amount -import co.nilin.mixchange.wallet.core.model.Wallet -import co.nilin.mixchange.wallet.core.spi.WalletListener +import co.nilin.opex.wallet.core.model.Amount +import co.nilin.opex.wallet.core.model.Wallet +import co.nilin.opex.wallet.core.spi.WalletListener import org.springframework.stereotype.Component import java.math.BigDecimal diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/service/UserRegistrationService.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/service/UserRegistrationService.kt similarity index 68% rename from Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/service/UserRegistrationService.kt rename to Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/service/UserRegistrationService.kt index c9dac724e..60160335f 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/service/UserRegistrationService.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/service/UserRegistrationService.kt @@ -1,11 +1,11 @@ -package co.nilin.mixchange.wallet.app.service +package co.nilin.opex.wallet.app.service -import co.nilin.mixchange.auth.gateway.model.UserCreatedEvent -import co.nilin.mixchange.wallet.app.controller.Symbol -import co.nilin.mixchange.wallet.core.model.Amount -import co.nilin.mixchange.wallet.core.model.Wallet -import co.nilin.mixchange.wallet.core.spi.WalletManager -import co.nilin.mixchange.wallet.core.spi.WalletOwnerManager +import co.nilin.opex.auth.gateway.model.UserCreatedEvent +import co.nilin.opex.wallet.app.controller.Symbol +import co.nilin.opex.wallet.core.model.Amount +import co.nilin.opex.wallet.core.model.Wallet +import co.nilin.opex.wallet.core.spi.WalletManager +import co.nilin.opex.wallet.core.spi.WalletOwnerManager import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Component import org.springframework.transaction.annotation.Transactional diff --git a/Wallet/wallet-core/pom.xml b/Wallet/wallet-core/pom.xml index 91aa36820..e8e983127 100644 --- a/Wallet/wallet-core/pom.xml +++ b/Wallet/wallet-core/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex wallet-core 0.0.1-SNAPSHOT wallet-core - Wallet managment of Mixchange + Wallet managment of Opex 1.8 diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/CurrencyRateService.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/CurrencyRateService.kt deleted file mode 100644 index cd1928a49..000000000 --- a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/CurrencyRateService.kt +++ /dev/null @@ -1,9 +0,0 @@ -package co.nilin.mixchange.wallet.core.spi - -import co.nilin.mixchange.wallet.core.model.Currency -import co.nilin.mixchange.wallet.core.model.Amount -import java.math.BigDecimal - -interface CurrencyRateService { - suspend fun convert(amount: Amount, targetCurrency: Currency): BigDecimal -} diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/TransactionManager.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/TransactionManager.kt deleted file mode 100644 index 66c88c240..000000000 --- a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/TransactionManager.kt +++ /dev/null @@ -1,7 +0,0 @@ -package co.nilin.mixchange.wallet.core.spi - -import co.nilin.mixchange.wallet.core.model.Transaction - -interface TransactionManager { - suspend fun save(transaction: Transaction): String -} \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/CurrencyNotMatchedException.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/exc/CurrencyNotMatchedException.kt similarity index 52% rename from Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/CurrencyNotMatchedException.kt rename to Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/exc/CurrencyNotMatchedException.kt index 61bb70455..80cee2fb6 100644 --- a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/CurrencyNotMatchedException.kt +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/exc/CurrencyNotMatchedException.kt @@ -1,3 +1,3 @@ -package co.nilin.mixchange.wallet.core.exc +package co.nilin.opex.wallet.core.exc class CurrencyNotMatchedException: Exception() \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/DepositLimitExceededException.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/exc/DepositLimitExceededException.kt similarity index 53% rename from Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/DepositLimitExceededException.kt rename to Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/exc/DepositLimitExceededException.kt index 54c6233cd..382eb31ed 100644 --- a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/DepositLimitExceededException.kt +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/exc/DepositLimitExceededException.kt @@ -1,3 +1,3 @@ -package co.nilin.mixchange.wallet.core.exc +package co.nilin.opex.wallet.core.exc class DepositLimitExceededException: Exception() \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/NotEnoughBalanceException.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/exc/NotEnoughBalanceException.kt similarity index 51% rename from Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/NotEnoughBalanceException.kt rename to Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/exc/NotEnoughBalanceException.kt index 0cb069595..9dccc85ac 100644 --- a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/NotEnoughBalanceException.kt +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/exc/NotEnoughBalanceException.kt @@ -1,3 +1,3 @@ -package co.nilin.mixchange.wallet.core.exc +package co.nilin.opex.wallet.core.exc class NotEnoughBalanceException: Exception() \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/WithdrawLimitExceededException.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/exc/WithdrawLimitExceededException.kt similarity index 53% rename from Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/WithdrawLimitExceededException.kt rename to Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/exc/WithdrawLimitExceededException.kt index da9425748..ab8fc7389 100644 --- a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/exc/WithdrawLimitExceededException.kt +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/exc/WithdrawLimitExceededException.kt @@ -1,3 +1,3 @@ -package co.nilin.mixchange.wallet.core.exc +package co.nilin.opex.wallet.core.exc class WithdrawLimitExceededException: Exception() \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/inout/TransferCommand.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/inout/TransferCommand.kt similarity index 50% rename from Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/inout/TransferCommand.kt rename to Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/inout/TransferCommand.kt index 1dd5fece5..5f0891eaa 100644 --- a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/inout/TransferCommand.kt +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/inout/TransferCommand.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.wallet.core.inout +package co.nilin.opex.wallet.core.inout -import co.nilin.mixchange.wallet.core.model.Amount -import co.nilin.mixchange.wallet.core.model.Wallet +import co.nilin.opex.wallet.core.model.Amount +import co.nilin.opex.wallet.core.model.Wallet data class TransferCommand(val sourceWallet: Wallet, val destWallet: Wallet, val amount: Amount, val description: String?, val transferRef: String?) diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/inout/TransferResult.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/inout/TransferResult.kt similarity index 65% rename from Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/inout/TransferResult.kt rename to Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/inout/TransferResult.kt index c1358da19..8ebf1ae9c 100644 --- a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/inout/TransferResult.kt +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/inout/TransferResult.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.wallet.core.inout +package co.nilin.opex.wallet.core.inout -import co.nilin.mixchange.wallet.core.model.Amount +import co.nilin.opex.wallet.core.model.Amount import java.time.LocalDateTime data class TransferResult(val date: LocalDateTime, val sourceBalanceBeforeAction: Amount, val sourceBalanceAfterAction: Amount, val amount: Amount) \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Amount.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/model/Amount.kt similarity index 67% rename from Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Amount.kt rename to Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/model/Amount.kt index 7cc47c151..28f59e99e 100644 --- a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Amount.kt +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/model/Amount.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.wallet.core.model +package co.nilin.opex.wallet.core.model import java.math.BigDecimal diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Currency.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/model/Currency.kt similarity index 70% rename from Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Currency.kt rename to Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/model/Currency.kt index 7ecc994b7..98a97b9d4 100644 --- a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Currency.kt +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/model/Currency.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.wallet.core.model +package co.nilin.opex.wallet.core.model interface Currency { fun getSymbol(): String diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Transaction.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/model/Transaction.kt similarity index 61% rename from Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Transaction.kt rename to Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/model/Transaction.kt index 9becfa021..551d563f2 100644 --- a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Transaction.kt +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/model/Transaction.kt @@ -1,5 +1,5 @@ -package co.nilin.mixchange.wallet.core.model +package co.nilin.opex.wallet.core.model import java.math.BigDecimal -data class Transaction(val sourceWallet: Wallet, val destWallet: Wallet, val sourceAmount: BigDecimal, val destAmount: BigDecimal, val description: String? , val transferRef: String?) \ No newline at end of file +data class Transaction(val sourceWallet: Wallet, val destWallet: Wallet, val sourceAmount: BigDecimal, val destAmount: BigDecimal, val description: String?, val transferRef: String?) \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Wallet.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/model/Wallet.kt similarity index 76% rename from Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Wallet.kt rename to Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/model/Wallet.kt index 5340af5d3..4f4210a94 100644 --- a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/Wallet.kt +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/model/Wallet.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.wallet.core.model +package co.nilin.opex.wallet.core.model interface Wallet { fun id(): Long? diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/WalletOwner.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/model/WalletOwner.kt similarity index 83% rename from Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/WalletOwner.kt rename to Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/model/WalletOwner.kt index 6c5556060..f054b6f69 100644 --- a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/model/WalletOwner.kt +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/model/WalletOwner.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.wallet.core.model +package co.nilin.opex.wallet.core.model interface WalletOwner { fun id(): Long? diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/service/TransferService.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/service/TransferService.kt similarity index 75% rename from Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/service/TransferService.kt rename to Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/service/TransferService.kt index 0ddc3d107..adb3fcde1 100644 --- a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/service/TransferService.kt +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/service/TransferService.kt @@ -1,18 +1,18 @@ -package co.nilin.mixchange.wallet.core.service +package co.nilin.opex.wallet.core.service -import co.nilin.mixchange.wallet.core.exc.CurrencyNotMatchedException -import co.nilin.mixchange.wallet.core.exc.DepositLimitExceededException -import co.nilin.mixchange.wallet.core.exc.NotEnoughBalanceException -import co.nilin.mixchange.wallet.core.exc.WithdrawLimitExceededException -import co.nilin.mixchange.wallet.core.inout.TransferCommand -import co.nilin.mixchange.wallet.core.inout.TransferResult -import co.nilin.mixchange.wallet.core.model.Amount -import co.nilin.mixchange.wallet.core.model.Transaction -import co.nilin.mixchange.wallet.core.spi.CurrencyRateService -import co.nilin.mixchange.wallet.core.spi.TransactionManager -import co.nilin.mixchange.wallet.core.spi.WalletListener -import co.nilin.mixchange.wallet.core.spi.WalletManager -import co.nilin.mixchange.wallet.core.spi.WalletOwnerManager +import co.nilin.opex.wallet.core.exc.CurrencyNotMatchedException +import co.nilin.opex.wallet.core.exc.DepositLimitExceededException +import co.nilin.opex.wallet.core.exc.NotEnoughBalanceException +import co.nilin.opex.wallet.core.exc.WithdrawLimitExceededException +import co.nilin.opex.wallet.core.inout.TransferCommand +import co.nilin.opex.wallet.core.inout.TransferResult +import co.nilin.opex.wallet.core.model.Amount +import co.nilin.opex.wallet.core.model.Transaction +import co.nilin.opex.wallet.core.spi.CurrencyRateService +import co.nilin.opex.wallet.core.spi.TransactionManager +import co.nilin.opex.wallet.core.spi.WalletListener +import co.nilin.opex.wallet.core.spi.WalletManager +import co.nilin.opex.wallet.core.spi.WalletOwnerManager import org.springframework.stereotype.Service import java.time.LocalDateTime diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/spi/CurrencyRateService.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/spi/CurrencyRateService.kt new file mode 100644 index 000000000..3123e1f4b --- /dev/null +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/spi/CurrencyRateService.kt @@ -0,0 +1,9 @@ +package co.nilin.opex.wallet.core.spi + +import co.nilin.opex.wallet.core.model.Currency +import co.nilin.opex.wallet.core.model.Amount +import java.math.BigDecimal + +interface CurrencyRateService { + suspend fun convert(amount: Amount, targetCurrency: Currency): BigDecimal +} diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/spi/TransactionManager.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/spi/TransactionManager.kt new file mode 100644 index 000000000..222a26308 --- /dev/null +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/spi/TransactionManager.kt @@ -0,0 +1,7 @@ +package co.nilin.opex.wallet.core.spi + +import co.nilin.opex.wallet.core.model.Transaction + +interface TransactionManager { + suspend fun save(transaction: Transaction): String +} \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletListener.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/spi/WalletListener.kt similarity index 64% rename from Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletListener.kt rename to Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/spi/WalletListener.kt index 0635765d6..da58cc6cc 100644 --- a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletListener.kt +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/spi/WalletListener.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.wallet.core.spi +package co.nilin.opex.wallet.core.spi -import co.nilin.mixchange.wallet.core.model.Amount -import co.nilin.mixchange.wallet.core.model.Wallet +import co.nilin.opex.wallet.core.model.Amount +import co.nilin.opex.wallet.core.model.Wallet import java.math.BigDecimal interface WalletListener { diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletManager.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/spi/WalletManager.kt similarity index 75% rename from Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletManager.kt rename to Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/spi/WalletManager.kt index f65c0c08f..9ebdbbce8 100644 --- a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletManager.kt +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/spi/WalletManager.kt @@ -1,9 +1,9 @@ -package co.nilin.mixchange.wallet.core.spi +package co.nilin.opex.wallet.core.spi -import co.nilin.mixchange.wallet.core.model.Amount -import co.nilin.mixchange.wallet.core.model.Currency -import co.nilin.mixchange.wallet.core.model.Wallet -import co.nilin.mixchange.wallet.core.model.WalletOwner +import co.nilin.opex.wallet.core.model.Amount +import co.nilin.opex.wallet.core.model.Currency +import co.nilin.opex.wallet.core.model.Wallet +import co.nilin.opex.wallet.core.model.WalletOwner import java.math.BigDecimal interface WalletManager { diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletOwnerManager.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/spi/WalletOwnerManager.kt similarity index 69% rename from Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletOwnerManager.kt rename to Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/spi/WalletOwnerManager.kt index 14201c4cf..11913f576 100644 --- a/Wallet/wallet-core/src/main/kotlin/co/nilin/mixchange/wallet/core/spi/WalletOwnerManager.kt +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/spi/WalletOwnerManager.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.wallet.core.spi +package co.nilin.opex.wallet.core.spi -import co.nilin.mixchange.wallet.core.model.Amount -import co.nilin.mixchange.wallet.core.model.WalletOwner +import co.nilin.opex.wallet.core.model.Amount +import co.nilin.opex.wallet.core.model.WalletOwner interface WalletOwnerManager { suspend fun isDepositAllowed(owner: WalletOwner, amount: Amount): Boolean diff --git a/Wallet/wallet-ports/wallet-eventlistener-kafka/pom.xml b/Wallet/wallet-ports/wallet-eventlistener-kafka/pom.xml index 9defe940e..e33ae0781 100644 --- a/Wallet/wallet-ports/wallet-eventlistener-kafka/pom.xml +++ b/Wallet/wallet-ports/wallet-eventlistener-kafka/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex wallet-eventlistener-kafka 0.0.1-SNAPSHOT wallet-eventlistener-kafka - Wallet kafka listener of Mixchange + Wallet kafka listener of Opex 1.8 diff --git a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/AuthEvent.kt b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/opex/auth/gateway/model/AuthEvent.kt similarity index 70% rename from UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/AuthEvent.kt rename to Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/opex/auth/gateway/model/AuthEvent.kt index 8f774787a..0ecf3780e 100644 --- a/UserManagement/keycloak-gateway/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/AuthEvent.kt +++ b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/opex/auth/gateway/model/AuthEvent.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.auth.gateway.model +package co.nilin.opex.auth.gateway.model import java.time.LocalDateTime diff --git a/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/UserCreatedEvent.kt b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/opex/auth/gateway/model/UserCreatedEvent.kt similarity index 92% rename from Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/UserCreatedEvent.kt rename to Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/opex/auth/gateway/model/UserCreatedEvent.kt index b186f6979..b15485c56 100644 --- a/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/auth/gateway/model/UserCreatedEvent.kt +++ b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/opex/auth/gateway/model/UserCreatedEvent.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.auth.gateway.model +package co.nilin.opex.auth.gateway.model class UserCreatedEvent: AuthEvent { lateinit var uuid: String diff --git a/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/config/WalletKafkaConfig.kt b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/wallet/kafka/config/WalletKafkaConfig.kt similarity index 93% rename from Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/config/WalletKafkaConfig.kt rename to Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/wallet/kafka/config/WalletKafkaConfig.kt index 8ad72a1cb..f7216abd1 100644 --- a/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/config/WalletKafkaConfig.kt +++ b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/wallet/kafka/config/WalletKafkaConfig.kt @@ -1,8 +1,7 @@ -package co.nilin.mixchange.port.wallet.kafka.config +package co.nilin.opex.port.wallet.kafka.config - -import co.nilin.mixchange.auth.gateway.model.UserCreatedEvent -import co.nilin.mixchange.port.wallet.consumer.UserCreatedKafkaListener +import co.nilin.opex.auth.gateway.model.UserCreatedEvent +import co.nilin.opex.port.wallet.kafka.consumer.UserCreatedKafkaListener import org.apache.kafka.clients.admin.NewTopic import org.apache.kafka.clients.consumer.ConsumerConfig import org.apache.kafka.clients.producer.ProducerConfig @@ -22,7 +21,6 @@ import org.springframework.kafka.support.serializer.JsonDeserializer import org.springframework.kafka.support.serializer.JsonSerializer import java.util.regex.Pattern - @Configuration class WalletKafkaConfig { @Value("\${spring.kafka.bootstrap-servers}") @@ -38,7 +36,7 @@ class WalletKafkaConfig { props[ConsumerConfig.GROUP_ID_CONFIG] = groupId props[ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG] = StringDeserializer::class.java props[ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG] = JsonDeserializer::class.java - props[JsonDeserializer.TRUSTED_PACKAGES] = "co.nilin.mixchange.*" + props[JsonDeserializer.TRUSTED_PACKAGES] = "co.nilin.opex.*" return props } diff --git a/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/consumer/UserCreatedKafkaListener.kt b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/wallet/kafka/consumer/UserCreatedKafkaListener.kt similarity index 81% rename from Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/consumer/UserCreatedKafkaListener.kt rename to Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/wallet/kafka/consumer/UserCreatedKafkaListener.kt index 1c4a77e22..4fa267011 100644 --- a/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/consumer/UserCreatedKafkaListener.kt +++ b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/wallet/kafka/consumer/UserCreatedKafkaListener.kt @@ -1,8 +1,8 @@ -package co.nilin.mixchange.port.wallet.consumer +package co.nilin.opex.port.wallet.kafka.consumer -import co.nilin.mixchange.auth.gateway.model.UserCreatedEvent -import co.nilin.mixchange.port.wallet.spi.UserCreatedEventListener +import co.nilin.opex.auth.gateway.model.UserCreatedEvent +import co.nilin.opex.port.wallet.kafka.spi.UserCreatedEventListener import org.apache.kafka.clients.consumer.ConsumerRecord import org.springframework.kafka.listener.MessageListener import org.springframework.stereotype.Component diff --git a/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/spi/UserCreatedEventListener.kt b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/wallet/kafka/spi/UserCreatedEventListener.kt similarity index 58% rename from Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/spi/UserCreatedEventListener.kt rename to Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/wallet/kafka/spi/UserCreatedEventListener.kt index ca62aa313..35168cac8 100644 --- a/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/mixchange/port/wallet/kafka/spi/UserCreatedEventListener.kt +++ b/Wallet/wallet-ports/wallet-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/wallet/kafka/spi/UserCreatedEventListener.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.port.wallet.spi +package co.nilin.opex.port.wallet.kafka.spi -import co.nilin.mixchange.auth.gateway.model.UserCreatedEvent +import co.nilin.opex.auth.gateway.model.UserCreatedEvent interface UserCreatedEventListener { fun id(): String diff --git a/Wallet/wallet-ports/wallet-persister-postgres/pom.xml b/Wallet/wallet-ports/wallet-persister-postgres/pom.xml index 035c3c114..11e0682fa 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/pom.xml +++ b/Wallet/wallet-ports/wallet-persister-postgres/pom.xml @@ -8,11 +8,11 @@ 2.4.4 - co.nilin.mixchange + co.nilin.opex wallet-persister-postgres 0.0.1-SNAPSHOT wallet-persister-postgres - Persist items of Mixchange wallet on Postgres + Persist items of Opex wallet on Postgres 1.8 @@ -22,7 +22,7 @@ - co.nilin.mixchange + co.nilin.opex wallet-core ${wallet.version} provided diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dto/TransactionStat.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dto/TransactionStat.kt deleted file mode 100644 index 386b5d23c..000000000 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dto/TransactionStat.kt +++ /dev/null @@ -1,7 +0,0 @@ -package co.nilin.mixchange.port.wallet.postgres.dto - -import java.math.BigDecimal - -class TransactionStat(val cnt: Long?, - val total: BigDecimal? -) \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/config/PostgresConfig.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/config/PostgresConfig.kt similarity index 97% rename from Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/config/PostgresConfig.kt rename to Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/config/PostgresConfig.kt index 94b0fb2c2..2d8433420 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/config/PostgresConfig.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/config/PostgresConfig.kt @@ -1,13 +1,11 @@ -package co.nilin.mixchange.port.order.kafka.config - +package co.nilin.opex.port.wallet.postgres.config import org.springframework.context.annotation.Configuration import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories import org.springframework.r2dbc.core.DatabaseClient - @Configuration -@EnableR2dbcRepositories(basePackages = ["co.nilin.mixchange"]) +@EnableR2dbcRepositories(basePackages = ["co.nilin.opex"]) class PostgresConfig(db: DatabaseClient) { init { diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/CurrencyRateRepository.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/CurrencyRateRepository.kt similarity index 78% rename from Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/CurrencyRateRepository.kt rename to Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/CurrencyRateRepository.kt index 69dd7e96d..ff1647e21 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/CurrencyRateRepository.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/CurrencyRateRepository.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.port.wallet.postgres.dao +package co.nilin.opex.port.wallet.postgres.dao -import co.nilin.mixchange.port.wallet.postgres.model.CurrencyModel -import co.nilin.mixchange.port.wallet.postgres.model.CurrencyRateModel +import co.nilin.opex.port.wallet.postgres.model.CurrencyModel +import co.nilin.opex.port.wallet.postgres.model.CurrencyRateModel import kotlinx.coroutines.flow.Flow import org.springframework.data.domain.Pageable import org.springframework.data.r2dbc.repository.Query diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/CurrencyRepository.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/CurrencyRepository.kt similarity index 70% rename from Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/CurrencyRepository.kt rename to Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/CurrencyRepository.kt index 1adb8409e..f541673c5 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/CurrencyRepository.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/CurrencyRepository.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.port.wallet.postgres.dao +package co.nilin.opex.port.wallet.postgres.dao -import co.nilin.mixchange.port.wallet.postgres.model.CurrencyModel -import co.nilin.mixchange.port.wallet.postgres.model.CurrencyRateModel +import co.nilin.opex.port.wallet.postgres.model.CurrencyModel +import co.nilin.opex.port.wallet.postgres.model.CurrencyRateModel import kotlinx.coroutines.flow.Flow import org.springframework.data.domain.Pageable import org.springframework.data.r2dbc.repository.Query diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/TransactionRepository.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/TransactionRepository.kt similarity index 92% rename from Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/TransactionRepository.kt rename to Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/TransactionRepository.kt index b8519e363..398a77157 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/TransactionRepository.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/TransactionRepository.kt @@ -1,9 +1,9 @@ -package co.nilin.mixchange.port.wallet.postgres.dao +package co.nilin.opex.port.wallet.postgres.dao -import co.nilin.mixchange.port.wallet.postgres.dto.TransactionStat -import co.nilin.mixchange.port.wallet.postgres.model.CurrencyModel -import co.nilin.mixchange.port.wallet.postgres.model.CurrencyRateModel -import co.nilin.mixchange.port.wallet.postgres.model.TransactionModel +import co.nilin.opex.port.wallet.postgres.dto.TransactionStat +import co.nilin.opex.port.wallet.postgres.model.CurrencyModel +import co.nilin.opex.port.wallet.postgres.model.CurrencyRateModel +import co.nilin.opex.port.wallet.postgres.model.TransactionModel import kotlinx.coroutines.flow.Flow import org.springframework.core.ParameterizedTypeReference import org.springframework.data.domain.Pageable @@ -15,7 +15,6 @@ import reactor.core.publisher.Mono import java.time.LocalDateTime import java.util.* - @Repository interface TransactionRepository: ReactiveCrudRepository { @Query("SELECT count(1) cnt, COALESCE(sum(source_amount * crm.rate), 0) total" + diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/UserLimitsRepository.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/UserLimitsRepository.kt similarity index 81% rename from Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/UserLimitsRepository.kt rename to Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/UserLimitsRepository.kt index aedd2f484..1430f034f 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/UserLimitsRepository.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/UserLimitsRepository.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.port.wallet.postgres.dao +package co.nilin.opex.port.wallet.postgres.dao -import co.nilin.mixchange.port.wallet.postgres.model.UserLimitsModel -import co.nilin.mixchange.port.wallet.postgres.model.WalletLimitsModel +import co.nilin.opex.port.wallet.postgres.model.UserLimitsModel +import co.nilin.opex.port.wallet.postgres.model.WalletLimitsModel import kotlinx.coroutines.flow.Flow import org.springframework.data.r2dbc.repository.Query import org.springframework.data.repository.query.Param diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletConfigRepository.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/WalletConfigRepository.kt similarity index 66% rename from Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletConfigRepository.kt rename to Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/WalletConfigRepository.kt index fb34b6084..91bd67cc1 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletConfigRepository.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/WalletConfigRepository.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.port.wallet.postgres.dao +package co.nilin.opex.port.wallet.postgres.dao -import co.nilin.mixchange.port.wallet.postgres.model.WalletConfigModel -import co.nilin.mixchange.port.wallet.postgres.model.WalletModel +import co.nilin.opex.port.wallet.postgres.model.WalletConfigModel +import co.nilin.opex.port.wallet.postgres.model.WalletModel import org.springframework.data.r2dbc.repository.Query import org.springframework.data.repository.query.Param import org.springframework.data.repository.reactive.ReactiveCrudRepository diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletLimitsRepository.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/WalletLimitsRepository.kt similarity index 92% rename from Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletLimitsRepository.kt rename to Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/WalletLimitsRepository.kt index 0a2b381e1..48388eaf4 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletLimitsRepository.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/WalletLimitsRepository.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.port.wallet.postgres.dao +package co.nilin.opex.port.wallet.postgres.dao -import co.nilin.mixchange.port.wallet.postgres.model.WalletLimitsModel +import co.nilin.opex.port.wallet.postgres.model.WalletLimitsModel import org.springframework.data.r2dbc.repository.Query import org.springframework.data.repository.query.Param import org.springframework.data.repository.reactive.ReactiveCrudRepository diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletOwnerRepository.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/WalletOwnerRepository.kt similarity index 80% rename from Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletOwnerRepository.kt rename to Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/WalletOwnerRepository.kt index f942d8b45..4504172b1 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletOwnerRepository.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/WalletOwnerRepository.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.port.wallet.postgres.dao +package co.nilin.opex.port.wallet.postgres.dao -import co.nilin.mixchange.port.wallet.postgres.model.WalletOwnerModel +import co.nilin.opex.port.wallet.postgres.model.WalletOwnerModel import org.springframework.data.r2dbc.repository.Query import org.springframework.data.repository.query.Param import org.springframework.data.repository.reactive.ReactiveCrudRepository diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletRepository.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/WalletRepository.kt similarity index 91% rename from Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletRepository.kt rename to Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/WalletRepository.kt index 7456175a6..2a3c27321 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dao/WalletRepository.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dao/WalletRepository.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.port.wallet.postgres.dao +package co.nilin.opex.port.wallet.postgres.dao -import co.nilin.mixchange.port.wallet.postgres.model.WalletModel +import co.nilin.opex.port.wallet.postgres.model.WalletModel import org.springframework.data.r2dbc.repository.Modifying import org.springframework.data.r2dbc.repository.Query import org.springframework.data.repository.query.Param diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dto/SavedWallet.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dto/SavedWallet.kt similarity index 64% rename from Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dto/SavedWallet.kt rename to Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dto/SavedWallet.kt index 21b19de22..ebc6ecf29 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/dto/SavedWallet.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dto/SavedWallet.kt @@ -1,9 +1,9 @@ -package co.nilin.mixchange.port.wallet.postgres.dto +package co.nilin.opex.port.wallet.postgres.dto -import co.nilin.mixchange.wallet.core.model.Amount -import co.nilin.mixchange.wallet.core.model.Currency -import co.nilin.mixchange.wallet.core.model.Wallet -import co.nilin.mixchange.wallet.core.model.WalletOwner +import co.nilin.opex.wallet.core.model.Amount +import co.nilin.opex.wallet.core.model.Currency +import co.nilin.opex.wallet.core.model.Wallet +import co.nilin.opex.wallet.core.model.WalletOwner class SavedWallet( val id_: Long, val owner_: WalletOwner, val balance_: Amount, val currency_: Currency, val type_: String diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dto/TransactionStat.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dto/TransactionStat.kt new file mode 100644 index 000000000..a7e0800dd --- /dev/null +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/dto/TransactionStat.kt @@ -0,0 +1,8 @@ +package co.nilin.opex.port.wallet.postgres.dto + +import java.math.BigDecimal + +class TransactionStat( + val cnt: Long?, + val total: BigDecimal? +) \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/CurrencyRateServiceImpl.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/impl/CurrencyRateServiceImpl.kt similarity index 78% rename from Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/CurrencyRateServiceImpl.kt rename to Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/impl/CurrencyRateServiceImpl.kt index 1d50e374e..151ba759b 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/CurrencyRateServiceImpl.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/impl/CurrencyRateServiceImpl.kt @@ -1,9 +1,9 @@ -package co.nilin.mixchange.port.wallet.postgres.impl +package co.nilin.opex.port.wallet.postgres.impl -import co.nilin.mixchange.port.wallet.postgres.dao.CurrencyRateRepository -import co.nilin.mixchange.wallet.core.model.Amount -import co.nilin.mixchange.wallet.core.model.Currency -import co.nilin.mixchange.wallet.core.spi.CurrencyRateService +import co.nilin.opex.port.wallet.postgres.dao.CurrencyRateRepository +import co.nilin.opex.wallet.core.model.Amount +import co.nilin.opex.wallet.core.model.Currency +import co.nilin.opex.wallet.core.spi.CurrencyRateService import kotlinx.coroutines.reactive.awaitFirstOrDefault import kotlinx.coroutines.reactive.awaitFirstOrNull import org.springframework.stereotype.Service diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/TransactionManagerImpl.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/impl/TransactionManagerImpl.kt similarity index 70% rename from Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/TransactionManagerImpl.kt rename to Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/impl/TransactionManagerImpl.kt index 8aa8d43e1..b1e94f141 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/TransactionManagerImpl.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/impl/TransactionManagerImpl.kt @@ -1,9 +1,9 @@ -package co.nilin.mixchange.port.wallet.postgres.impl +package co.nilin.opex.port.wallet.postgres.impl -import co.nilin.mixchange.port.wallet.postgres.dao.TransactionRepository -import co.nilin.mixchange.port.wallet.postgres.model.TransactionModel -import co.nilin.mixchange.wallet.core.model.Transaction -import co.nilin.mixchange.wallet.core.spi.TransactionManager +import co.nilin.opex.port.wallet.postgres.dao.TransactionRepository +import co.nilin.opex.port.wallet.postgres.model.TransactionModel +import co.nilin.opex.wallet.core.model.Transaction +import co.nilin.opex.wallet.core.spi.TransactionManager import kotlinx.coroutines.reactive.awaitSingle import org.springframework.stereotype.Service import java.time.LocalDateTime diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/WalletManagerImpl.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/impl/WalletManagerImpl.kt similarity index 94% rename from Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/WalletManagerImpl.kt rename to Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/impl/WalletManagerImpl.kt index 24a537cdf..e0d1b8d79 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/WalletManagerImpl.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/impl/WalletManagerImpl.kt @@ -1,13 +1,13 @@ -package co.nilin.mixchange.port.wallet.postgres.impl +package co.nilin.opex.port.wallet.postgres.impl -import co.nilin.mixchange.port.wallet.postgres.dao.* -import co.nilin.mixchange.port.wallet.postgres.dto.SavedWallet -import co.nilin.mixchange.port.wallet.postgres.model.WalletModel -import co.nilin.mixchange.wallet.core.model.Amount -import co.nilin.mixchange.wallet.core.model.Currency -import co.nilin.mixchange.wallet.core.model.Wallet -import co.nilin.mixchange.wallet.core.model.WalletOwner -import co.nilin.mixchange.wallet.core.spi.WalletManager +import co.nilin.opex.port.wallet.postgres.dao.* +import co.nilin.opex.port.wallet.postgres.dto.SavedWallet +import co.nilin.opex.port.wallet.postgres.model.WalletModel +import co.nilin.opex.wallet.core.model.Amount +import co.nilin.opex.wallet.core.model.Currency +import co.nilin.opex.wallet.core.model.Wallet +import co.nilin.opex.wallet.core.model.WalletOwner +import co.nilin.opex.wallet.core.spi.WalletManager import kotlinx.coroutines.reactive.awaitFirst import kotlinx.coroutines.reactive.awaitFirstOrNull import kotlinx.coroutines.reactive.awaitSingle diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/WalletOwnerManagerImpl.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/impl/WalletOwnerManagerImpl.kt similarity index 88% rename from Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/WalletOwnerManagerImpl.kt rename to Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/impl/WalletOwnerManagerImpl.kt index 1e4256e6a..5cd57367d 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/impl/WalletOwnerManagerImpl.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/impl/WalletOwnerManagerImpl.kt @@ -1,15 +1,15 @@ -package co.nilin.mixchange.port.wallet.postgres.impl +package co.nilin.opex.port.wallet.postgres.impl -import co.nilin.mixchange.port.wallet.postgres.dao.TransactionRepository -import co.nilin.mixchange.port.wallet.postgres.dao.UserLimitsRepository -import co.nilin.mixchange.port.wallet.postgres.dao.WalletConfigRepository -import co.nilin.mixchange.port.wallet.postgres.dao.WalletOwnerRepository -import co.nilin.mixchange.port.wallet.postgres.model.UserLimitsModel -import co.nilin.mixchange.port.wallet.postgres.model.WalletConfigModel -import co.nilin.mixchange.port.wallet.postgres.model.WalletOwnerModel -import co.nilin.mixchange.wallet.core.model.Amount -import co.nilin.mixchange.wallet.core.model.WalletOwner -import co.nilin.mixchange.wallet.core.spi.WalletOwnerManager +import co.nilin.opex.port.wallet.postgres.dao.TransactionRepository +import co.nilin.opex.port.wallet.postgres.dao.UserLimitsRepository +import co.nilin.opex.port.wallet.postgres.dao.WalletConfigRepository +import co.nilin.opex.port.wallet.postgres.dao.WalletOwnerRepository +import co.nilin.opex.port.wallet.postgres.model.UserLimitsModel +import co.nilin.opex.port.wallet.postgres.model.WalletConfigModel +import co.nilin.opex.port.wallet.postgres.model.WalletOwnerModel +import co.nilin.opex.wallet.core.model.Amount +import co.nilin.opex.wallet.core.model.WalletOwner +import co.nilin.opex.wallet.core.spi.WalletOwnerManager import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEmpty import kotlinx.coroutines.flow.reduce diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/CurrencyModel.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/CurrencyModel.kt similarity index 84% rename from Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/CurrencyModel.kt rename to Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/CurrencyModel.kt index 8121d32ad..4786dba55 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/CurrencyModel.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/CurrencyModel.kt @@ -1,7 +1,7 @@ -package co.nilin.mixchange.port.wallet.postgres.model +package co.nilin.opex.port.wallet.postgres.model -import co.nilin.mixchange.wallet.core.model.Currency +import co.nilin.opex.wallet.core.model.Currency import org.springframework.data.annotation.Id import org.springframework.data.relational.core.mapping.Column import org.springframework.data.relational.core.mapping.Table diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/CurrencyRateModel.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/CurrencyRateModel.kt similarity index 88% rename from Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/CurrencyRateModel.kt rename to Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/CurrencyRateModel.kt index 59c765d7e..bcaa7f5ef 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/CurrencyRateModel.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/CurrencyRateModel.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.port.wallet.postgres.model +package co.nilin.opex.port.wallet.postgres.model import org.springframework.data.annotation.Id diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/TransactionModel.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/TransactionModel.kt similarity index 92% rename from Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/TransactionModel.kt rename to Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/TransactionModel.kt index 27395fa18..cc8d75c9c 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/TransactionModel.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/TransactionModel.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.port.wallet.postgres.model +package co.nilin.opex.port.wallet.postgres.model import org.springframework.data.annotation.Id import org.springframework.data.relational.core.mapping.Column diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/UserLimitsModel.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/UserLimitsModel.kt similarity index 91% rename from Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/UserLimitsModel.kt rename to Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/UserLimitsModel.kt index 07e3af446..b37cb8169 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/UserLimitsModel.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/UserLimitsModel.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.port.wallet.postgres.model +package co.nilin.opex.port.wallet.postgres.model import org.springframework.data.annotation.Id import org.springframework.data.relational.core.mapping.Column diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletConfigModel.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/WalletConfigModel.kt similarity index 56% rename from Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletConfigModel.kt rename to Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/WalletConfigModel.kt index 92eedc8b8..0652137b0 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletConfigModel.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/WalletConfigModel.kt @@ -1,9 +1,8 @@ -package co.nilin.mixchange.port.wallet.postgres.model +package co.nilin.opex.port.wallet.postgres.model import org.springframework.data.annotation.Id import org.springframework.data.relational.core.mapping.Column import org.springframework.data.relational.core.mapping.Table @Table("wallet_config") -class WalletConfigModel(@Id val name: String -, @Column("main_currency") val mainCurrency: String) \ No newline at end of file +class WalletConfigModel(@Id val name: String, @Column("main_currency") val mainCurrency: String) \ No newline at end of file diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletLimitsModel.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/WalletLimitsModel.kt similarity index 92% rename from Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletLimitsModel.kt rename to Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/WalletLimitsModel.kt index 917e56e6b..4f5f59ab3 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletLimitsModel.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/WalletLimitsModel.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.port.wallet.postgres.model +package co.nilin.opex.port.wallet.postgres.model import org.springframework.data.annotation.Id import org.springframework.data.relational.core.mapping.Column diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletModel.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/WalletModel.kt similarity index 89% rename from Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletModel.kt rename to Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/WalletModel.kt index 07736fc02..8c60c2f02 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletModel.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/WalletModel.kt @@ -1,4 +1,4 @@ -package co.nilin.mixchange.port.wallet.postgres.model +package co.nilin.opex.port.wallet.postgres.model import org.springframework.data.annotation.Id import org.springframework.data.relational.core.mapping.Column diff --git a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletOwnerModel.kt b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/WalletOwnerModel.kt similarity index 90% rename from Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletOwnerModel.kt rename to Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/WalletOwnerModel.kt index 0728505da..2c103ab7d 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/mixchange/port/wallet/postgres/model/WalletOwnerModel.kt +++ b/Wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/port/wallet/postgres/model/WalletOwnerModel.kt @@ -1,6 +1,6 @@ -package co.nilin.mixchange.port.wallet.postgres.model +package co.nilin.opex.port.wallet.postgres.model -import co.nilin.mixchange.wallet.core.model.WalletOwner +import co.nilin.opex.wallet.core.model.WalletOwner import org.springframework.data.annotation.Id import org.springframework.data.relational.core.mapping.Column import org.springframework.data.relational.core.mapping.Table From b0fbe089629a69d014af1d206379d43d5a04be16 Mon Sep 17 00:00:00 2001 From: metalicn20 Date: Sat, 7 Aug 2021 14:24:40 +0430 Subject: [PATCH 13/59] Add authentication to swagger --- .../kotlin/co/nilin/mixchange/app/ApiApp.kt | 61 +++++++++++++++++- .../nilin/mixchange/app/MatchingGatewayApp.kt | 62 +++++++++++++++++- .../nilin/mixchange/wallet/app/WalletApp.kt | 64 ++++++++++++++++++- 3 files changed, 183 insertions(+), 4 deletions(-) diff --git a/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/ApiApp.kt b/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/ApiApp.kt index c63b8987c..e869379d3 100644 --- a/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/ApiApp.kt +++ b/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/ApiApp.kt @@ -4,12 +4,16 @@ import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.annotation.Bean import org.springframework.context.annotation.ComponentScan -import springfox.documentation.builders.ApiInfoBuilder +import springfox.documentation.builders.* import springfox.documentation.builders.PathSelectors.regex -import springfox.documentation.service.ApiInfo +import springfox.documentation.service.* import springfox.documentation.spi.DocumentationType +import springfox.documentation.spi.service.contexts.SecurityContext import springfox.documentation.spring.web.plugins.Docket +import springfox.documentation.swagger.web.SecurityConfiguration +import springfox.documentation.swagger.web.SecurityConfigurationBuilder import springfox.documentation.swagger2.annotations.EnableSwagger2 +import java.util.Collections.singletonList @SpringBootApplication @@ -24,6 +28,18 @@ class ApiApp { .select() .paths(regex("^/api/v3.*")) .build() + .globalRequestParameters( + singletonList( + RequestParameterBuilder() + .name("content-type") + .description("content-type") + .`in`(ParameterType.HEADER) + .required(true) + .build() + ) + ) + .securitySchemes(singletonList(oauth())) + .securityContexts(singletonList(securityContext())) } private fun apiInfo(): ApiInfo? { @@ -35,6 +51,47 @@ class ApiApp { .version("0.1") .build() } + + @Bean + fun oauth(): SecurityScheme? { + return OAuthBuilder() + .name("opex") + .grantTypes(grantTypes()) + .scopes(scopes()) + .build() + } + + fun scopes(): List? { + return emptyList() + } + + fun grantTypes(): List? { + val tokenUrl = "http://localhost:8083/auth/realms/mixchange/protocol/openid-connect/token" + val grantType = ResourceOwnerPasswordCredentialsGrant(tokenUrl) + return singletonList(grantType) + } + + @Bean + fun securityContext(): SecurityContext? { + val securityReference = SecurityReference.builder() + .reference("opex") + .scopes(emptyArray()) + .build() + return SecurityContext.builder() + .securityReferences(singletonList(securityReference)) + .operationSelector { true } + .build() + } + + @Bean + fun securityInfo(): SecurityConfiguration? { + return SecurityConfigurationBuilder.builder() + .clientId("admin-cli") + .realm("mixchange") + .appName("opex") + .scopeSeparator(",") + .build() + } } fun main(args: Array) { diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/MatchingGatewayApp.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/MatchingGatewayApp.kt index d8f76c7a8..900620766 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/MatchingGatewayApp.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/MatchingGatewayApp.kt @@ -5,10 +5,17 @@ import org.springframework.boot.runApplication import org.springframework.context.annotation.Bean import org.springframework.context.annotation.ComponentScan import springfox.documentation.builders.ApiInfoBuilder +import springfox.documentation.builders.OAuthBuilder import springfox.documentation.builders.PathSelectors.regex -import springfox.documentation.service.ApiInfo +import springfox.documentation.builders.RequestParameterBuilder +import springfox.documentation.service.* import springfox.documentation.spi.DocumentationType +import springfox.documentation.spi.service.contexts.SecurityContext import springfox.documentation.spring.web.plugins.Docket +import springfox.documentation.swagger.web.SecurityConfiguration +import springfox.documentation.swagger.web.SecurityConfigurationBuilder +import java.util.* +import java.util.Collections.singletonList @SpringBootApplication @ComponentScan("co.nilin.mixchange") @@ -21,6 +28,18 @@ class MatchingGatewayApp { .select() .paths(regex("^/actuator.*").negate()) .build() + .globalRequestParameters( + singletonList( + RequestParameterBuilder() + .name("content-type") + .description("content-type") + .`in`(ParameterType.HEADER) + .required(true) + .build() + ) + ) + .securitySchemes(singletonList(oauth())) + .securityContexts(singletonList(securityContext())) } private fun apiInfo(): ApiInfo? { @@ -32,6 +51,47 @@ class MatchingGatewayApp { .version("0.1") .build() } + + @Bean + fun oauth(): SecurityScheme? { + return OAuthBuilder() + .name("opex") + .grantTypes(grantTypes()) + .scopes(scopes()) + .build() + } + + fun scopes(): List? { + return emptyList() + } + + fun grantTypes(): List? { + val tokenUrl = "http://localhost:8083/auth/realms/mixchange/protocol/openid-connect/token" + val grantType = ResourceOwnerPasswordCredentialsGrant(tokenUrl) + return singletonList(grantType) + } + + @Bean + fun securityContext(): SecurityContext? { + val securityReference = SecurityReference.builder() + .reference("opex") + .scopes(emptyArray()) + .build() + return SecurityContext.builder() + .securityReferences(singletonList(securityReference)) + .operationSelector { true } + .build() + } + + @Bean + fun securityInfo(): SecurityConfiguration? { + return SecurityConfigurationBuilder.builder() + .clientId("admin-cli") + .realm("mixchange") + .appName("opex") + .scopeSeparator(",") + .build() + } } fun main(args: Array) { diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/WalletApp.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/WalletApp.kt index 8932d6730..bbf35740f 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/WalletApp.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/WalletApp.kt @@ -5,10 +5,17 @@ import org.springframework.boot.runApplication import org.springframework.context.annotation.Bean import org.springframework.context.annotation.ComponentScan import springfox.documentation.builders.ApiInfoBuilder +import springfox.documentation.builders.OAuthBuilder import springfox.documentation.builders.PathSelectors.regex -import springfox.documentation.service.ApiInfo +import springfox.documentation.builders.RequestParameterBuilder +import springfox.documentation.service.* import springfox.documentation.spi.DocumentationType +import springfox.documentation.spi.service.contexts.SecurityContext import springfox.documentation.spring.web.plugins.Docket +import springfox.documentation.swagger.web.SecurityConfiguration +import springfox.documentation.swagger.web.SecurityConfigurationBuilder +import java.util.* +import java.util.Collections.singletonList @SpringBootApplication @ComponentScan("co.nilin.mixchange") @@ -21,6 +28,18 @@ class WalletApp { .select() .paths(regex("^/actuator.*").negate()) .build() + .globalRequestParameters( + singletonList( + RequestParameterBuilder() + .name("content-type") + .description("content-type") + .`in`(ParameterType.HEADER) + .required(true) + .build() + ) + ) + .securitySchemes(singletonList(oauth())) + .securityContexts(singletonList(securityContext())) } private fun apiInfo(): ApiInfo? { @@ -32,6 +51,49 @@ class WalletApp { .version("0.1") .build() } + + @Bean + fun oauth(): SecurityScheme? { + return OAuthBuilder() + .name("opex") + .grantTypes(grantTypes()) + .scopes(scopes()) + .build() + } + + fun scopes(): List? { + return emptyList() + } + + fun grantTypes(): List? { + val tokenUrl = "http://localhost:8083/auth/realms/mixchange/protocol/openid-connect/token" + val grantType = ResourceOwnerPasswordCredentialsGrant(tokenUrl) + return singletonList(grantType) + } + + @Bean + fun securityContext(): SecurityContext? { + val securityReference = SecurityReference.builder() + .reference("opex") + .scopes(emptyArray()) + .build() + return SecurityContext.builder() + .securityReferences(singletonList(securityReference)) + .operationSelector { + it.requestMappingPattern().matches(Regex("^/(balanceOf|owner)/.*")) + } + .build() + } + + @Bean + fun securityInfo(): SecurityConfiguration? { + return SecurityConfigurationBuilder.builder() + .clientId("admin-cli") + .realm("mixchange") + .appName("opex") + .scopeSeparator(",") + .build() + } } fun main(args: Array) { From 7dd4bd11008d5eac49c5292760fcaa563b14295c Mon Sep 17 00:00:00 2001 From: metalicn20 Date: Tue, 10 Aug 2021 13:58:28 +0430 Subject: [PATCH 14/59] Add description to API swagger doc --- .../kotlin/co/nilin/mixchange/app/ApiApp.kt | 4 + Api/api-ports/api-binance-rest/pom.xml | 5 ++ .../binance/controller/AccountController.kt | 82 ++++++++++++++++++- 3 files changed, 87 insertions(+), 4 deletions(-) diff --git a/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/ApiApp.kt b/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/ApiApp.kt index e869379d3..ef9ec97a8 100644 --- a/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/ApiApp.kt +++ b/Api/api-app/src/main/kotlin/co/nilin/mixchange/app/ApiApp.kt @@ -4,6 +4,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.annotation.Bean import org.springframework.context.annotation.ComponentScan +import org.springframework.security.core.annotation.AuthenticationPrincipal import springfox.documentation.builders.* import springfox.documentation.builders.PathSelectors.regex import springfox.documentation.service.* @@ -13,6 +14,7 @@ import springfox.documentation.spring.web.plugins.Docket import springfox.documentation.swagger.web.SecurityConfiguration import springfox.documentation.swagger.web.SecurityConfigurationBuilder import springfox.documentation.swagger2.annotations.EnableSwagger2 +import java.security.Principal import java.util.Collections.singletonList @@ -38,6 +40,8 @@ class ApiApp { .build() ) ) + .ignoredParameterTypes(AuthenticationPrincipal::class.java, Principal::class.java) + .useDefaultResponseMessages(false) .securitySchemes(singletonList(oauth())) .securityContexts(singletonList(securityContext())) } diff --git a/Api/api-ports/api-binance-rest/pom.xml b/Api/api-ports/api-binance-rest/pom.xml index b0952e230..dde2681c0 100644 --- a/Api/api-ports/api-binance-rest/pom.xml +++ b/Api/api-ports/api-binance-rest/pom.xml @@ -102,6 +102,11 @@ reactor-test test + + io.swagger + swagger-annotations + 1.5.20 + diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/AccountController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/AccountController.kt index 71f1e5959..fe3171c54 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/AccountController.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/mixchange/port/api/binance/controller/AccountController.kt @@ -11,15 +11,13 @@ import co.nilin.mixchange.port.api.binance.util.asMatchConstraint import co.nilin.mixchange.port.api.binance.util.asMatchingOrderType import co.nilin.mixchange.port.api.binance.util.asOrderDirection import com.fasterxml.jackson.annotation.JsonInclude +import io.swagger.annotations.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import org.slf4j.LoggerFactory import org.springframework.http.MediaType import org.springframework.security.core.annotation.AuthenticationPrincipal -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.PostMapping -import org.springframework.web.bind.annotation.RequestParam -import org.springframework.web.bind.annotation.RestController +import org.springframework.web.bind.annotation.* import java.math.BigDecimal import java.security.Principal import java.util.* @@ -107,6 +105,16 @@ class AccountController( consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE] ) + @ApiResponse( + message = "OK", + code = 200, + examples = Example( + ExampleProperty( + value = "{ \"symbol\": \"btc_usdt\", \"orderId\": -1, \"orderListId\": -1, \"transactTime\": \"2021-08-03T11:09:23.190+00:00\" }", + mediaType = "application/json" + ) + ) + ) suspend fun createNewOrder( @RequestParam(name = "symbol") symbol: String, @@ -122,16 +130,24 @@ class AccountController( quoteOrderQty: BigDecimal?, @RequestParam(name = "price", required = false) price: BigDecimal?, + @ApiParam( + value = "A unique id among open orders. Automatically generated if not sent.\n" + + "Orders with the same newClientOrderID can be accepted only when the previous one is filled, otherwise the order will be rejected." + ) @RequestParam(name = "newClientOrderId", required = false) newClientOrderId: String?, /* A unique id among open orders. Automatically generated if not sent. Orders with the same newClientOrderID can be accepted only when the previous one is filled, otherwise the order will be rejected. */ + @ApiParam(value = "Used with STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, and TAKE_PROFIT_LIMIT orders.") @RequestParam(name = "stopPrice", required = false) stopPrice: BigDecimal?, //Used with STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, and TAKE_PROFIT_LIMIT orders. @RequestParam(name = "icebergQty", required = false) + @ApiParam(value = "Used with LIMIT, STOP_LOSS_LIMIT, and TAKE_PROFIT_LIMIT to create an iceberg order.") icebergQty: BigDecimal?, //Used with LIMIT, STOP_LOSS_LIMIT, and TAKE_PROFIT_LIMIT to create an iceberg order. @RequestParam(name = "newOrderRespType", required = false) + @ApiParam(value = "Set the response JSON. ACK, RESULT, or FULL; MARKET and LIMIT order types default to FULL, all other orders default to ACK.") newOrderRespType: OrderResponseType?, //Set the response JSON. ACK, RESULT, or FULL; MARKET and LIMIT order types default to FULL, all other orders default to ACK. + @ApiParam(value = "The value cannot be greater than 60000") @RequestParam(name = "recvWindow", required = false) recvWindow: Long?, //The value cannot be greater than 60000 @RequestParam(name = "timestamp") @@ -178,6 +194,16 @@ class AccountController( consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE] ) + @ApiResponse( + message = "OK", + code = 200, + examples = Example( + ExampleProperty( + value = "{ \"symbol\": \"btc_usdt\", \"orderId\": 12, \"orderListId\": -1, \"clientOrderId\": \"\", \"price\": 1, \"origQty\": 10, \"executedQty\": 0, \"cummulativeQuoteQty\": 0, \"status\": \"NEW\", \"timeInForce\": \"GTC\", \"type\": \"LIMIT\", \"side\": \"SELL\", \"time\": \"2021-08-04T12:10:13.488+00:00\", \"updateTime\": \"2021-08-04T12:10:13.488+00:00\", \"isWorking\": true, \"origQuoteOrderQty\": 10 }", + mediaType = "application/json" + ) + ) + ) suspend fun queryOrder( principal: Principal, @RequestParam(name = "symbol") @@ -186,6 +212,7 @@ class AccountController( orderId: Long?, @RequestParam(name = "origClientOrderId", required = false) origClientOrderId: String?, + @ApiParam(value = "The value cannot be greater than 60000") @RequestParam(name = "recvWindow", required = false) recvWindow: Long?, //The value cannot be greater than 60000 @RequestParam(name = "timestamp") @@ -229,10 +256,21 @@ class AccountController( consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE] ) + @ApiResponse( + message = "OK", + code = 200, + examples = Example( + ExampleProperty( + value = "[ { \"symbol\": \"btc_usdt\", \"orderId\": 12, \"orderListId\": -1, \"clientOrderId\": \"\", \"price\": 1, \"origQty\": 10, \"executedQty\": 0, \"cummulativeQuoteQty\": 0, \"status\": \"NEW\", \"timeInForce\": \"GTC\", \"type\": \"LIMIT\", \"side\": \"SELL\", \"time\": \"2021-08-04T12:10:13.488+00:00\", \"updateTime\": \"2021-08-04T12:10:13.488+00:00\", \"isWorking\": true, \"origQuoteOrderQty\": 10 } ]", + mediaType = "application/json" + ) + ) + ) suspend fun fetchOpenOrders( principal: Principal, @RequestParam(name = "symbol", required = false) symbol: String?, + @ApiParam(value = "The value cannot be greater than 60000") @RequestParam(name = "recvWindow", required = false) recvWindow: Long?, //The value cannot be greater than 60000 @RequestParam(name = "timestamp") @@ -273,6 +311,16 @@ class AccountController( consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE] ) + @ApiResponse( + message = "OK", + code = 200, + examples = Example( + ExampleProperty( + value = "{ }", + mediaType = "application/json" + ) + ) + ) suspend fun fetchAllOrders( principal: Principal, @RequestParam(name = "symbol", required = false) @@ -281,8 +329,10 @@ class AccountController( startTime: Date?, @RequestParam(name = "endTime", required = false) endTime: Date?, + @ApiParam(value = "Default 500; max 1000.") @RequestParam(name = "limit", required = false) limit: Int? = 500, //Default 500; max 1000. + @ApiParam(value = "The value cannot be greater than 60000") @RequestParam(name = "recvWindow", required = false) recvWindow: Long?, //The value cannot be greater than 60000 @RequestParam(name = "timestamp") @@ -324,6 +374,16 @@ class AccountController( consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE] ) + @ApiResponse( + message = "OK", + code = 200, + examples = Example( + ExampleProperty( + value = "{ }", + mediaType = "application/json" + ) + ) + ) suspend fun fetchAllTrades( principal: Principal, @RequestParam(name = "symbol") @@ -332,10 +392,13 @@ class AccountController( startTime: Date?, @RequestParam(name = "endTime", required = false) endTime: Date?, + @ApiParam(value = "TradeId to fetch from. Default gets most recent trades.") @RequestParam(name = "fromId", required = false) fromId: Long?,//TradeId to fetch from. Default gets most recent trades. + @ApiParam(value = "Default 500; max 1000.") @RequestParam(name = "limit", required = false) limit: Int? = 500, //Default 500; max 1000. + @ApiParam(value = "The value cannot be greater than 60000") @RequestParam(name = "recvWindow", required = false) recvWindow: Long?, //The value cannot be greater than 60000 @RequestParam(name = "timestamp") @@ -357,9 +420,20 @@ class AccountController( consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE] ) + @ApiResponse( + message = "OK", + code = 200, + examples = Example( + ExampleProperty( + value = "{ \"makerCommission\": 0, \"takerCommission\": 0, \"buyerCommission\": 0, \"sellerCommission\": 0, \"canTrade\": true, \"canWithdraw\": true, \"canDeposit\": true, \"updateTime\": 1628420513843, \"accountType\": \"SPOT\", \"balances\": [ { \"asset\": \"usdt\", \"free\": 1000, \"locked\": 0 } ], \"permissions\": [ \"SPOT\" ] }", + mediaType = "application/json" + ) + ) + ) suspend fun accountInfo( @AuthenticationPrincipal auth: CustomAuthToken, + @ApiParam(value = "The value cannot be greater than 60000") @RequestParam(name = "recvWindow", required = false) recvWindow: Long?, //The value cannot be greater than 60000 @RequestParam(name = "timestamp") From 51b193205523330ddc2a9526d1618b14b20f0df4 Mon Sep 17 00:00:00 2001 From: metalicn20 Date: Tue, 10 Aug 2021 14:22:19 +0430 Subject: [PATCH 15/59] Add description attributes to Wallet and MatchingGateway modules --- .../nilin/mixchange/app/MatchingGatewayApp.kt | 4 ++++ .../app/controller/OrderController.kt | 18 ++++++++++++++- .../nilin/mixchange/wallet/app/WalletApp.kt | 4 ++++ .../app/controller/BalanceController.kt | 13 +++++++++++ .../app/controller/InquiryController.kt | 13 +++++++++++ .../app/controller/TransferController.kt | 13 +++++++++++ .../app/controller/WalletOwnerController.kt | 23 +++++++++++++++++++ 7 files changed, 87 insertions(+), 1 deletion(-) diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/MatchingGatewayApp.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/MatchingGatewayApp.kt index 900620766..2563d82a6 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/MatchingGatewayApp.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/MatchingGatewayApp.kt @@ -4,6 +4,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.annotation.Bean import org.springframework.context.annotation.ComponentScan +import org.springframework.security.core.annotation.AuthenticationPrincipal import springfox.documentation.builders.ApiInfoBuilder import springfox.documentation.builders.OAuthBuilder import springfox.documentation.builders.PathSelectors.regex @@ -14,6 +15,7 @@ import springfox.documentation.spi.service.contexts.SecurityContext import springfox.documentation.spring.web.plugins.Docket import springfox.documentation.swagger.web.SecurityConfiguration import springfox.documentation.swagger.web.SecurityConfigurationBuilder +import java.security.Principal import java.util.* import java.util.Collections.singletonList @@ -38,6 +40,8 @@ class MatchingGatewayApp { .build() ) ) + .ignoredParameterTypes(AuthenticationPrincipal::class.java, Principal::class.java) + .useDefaultResponseMessages(false) .securitySchemes(singletonList(oauth())) .securityContexts(singletonList(securityContext())) } diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/controller/OrderController.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/controller/OrderController.kt index 8c5d63349..7fb688d60 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/controller/OrderController.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/mixchange/app/controller/OrderController.kt @@ -9,6 +9,9 @@ import co.nilin.mixchange.matching.core.model.Pair import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitRequest import co.nilin.mixchange.port.order.kafka.inout.OrderSubmitResult import co.nilin.mixchange.port.order.kafka.service.OrderSubmitter +import io.swagger.annotations.ApiResponse +import io.swagger.annotations.Example +import io.swagger.annotations.ExampleProperty import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody @@ -20,7 +23,20 @@ import java.util.* class OrderController(val orderService: OrderService) { @PostMapping("/order") - suspend fun submitNewOrder(principal: Principal, @RequestBody createOrderRequest: CreateOrderRequest): OrderSubmitResult { + @ApiResponse( + message = "OK", + code = 200, + examples = Example( + ExampleProperty( + value = "{ }", + mediaType = "application/json" + ) + ) + ) + suspend fun submitNewOrder( + principal: Principal, + @RequestBody createOrderRequest: CreateOrderRequest + ): OrderSubmitResult { createOrderRequest.uuid = principal.name return orderService.submitNewOrder(createOrderRequest) } diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/WalletApp.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/WalletApp.kt index bbf35740f..914f67242 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/WalletApp.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/WalletApp.kt @@ -4,6 +4,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.annotation.Bean import org.springframework.context.annotation.ComponentScan +import org.springframework.security.core.annotation.AuthenticationPrincipal import springfox.documentation.builders.ApiInfoBuilder import springfox.documentation.builders.OAuthBuilder import springfox.documentation.builders.PathSelectors.regex @@ -14,6 +15,7 @@ import springfox.documentation.spi.service.contexts.SecurityContext import springfox.documentation.spring.web.plugins.Docket import springfox.documentation.swagger.web.SecurityConfiguration import springfox.documentation.swagger.web.SecurityConfigurationBuilder +import java.security.Principal import java.util.* import java.util.Collections.singletonList @@ -38,6 +40,8 @@ class WalletApp { .build() ) ) + .ignoredParameterTypes(AuthenticationPrincipal::class.java, Principal::class.java) + .useDefaultResponseMessages(false) .securitySchemes(singletonList(oauth())) .securityContexts(singletonList(securityContext())) } diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/BalanceController.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/BalanceController.kt index 4ab005ffc..6549a7738 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/BalanceController.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/BalanceController.kt @@ -2,6 +2,9 @@ package co.nilin.mixchange.wallet.app.controller import co.nilin.mixchange.wallet.core.spi.WalletManager import co.nilin.mixchange.wallet.core.spi.WalletOwnerManager +import io.swagger.annotations.ApiResponse +import io.swagger.annotations.Example +import io.swagger.annotations.ExampleProperty import org.slf4j.LoggerFactory import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable @@ -18,6 +21,16 @@ class BalanceController( data class BalanceResponse(val balance: BigDecimal) @GetMapping("/balanceOf/wallet_type/{wallet_type}/currency/{currency}") + @ApiResponse( + message = "OK", + code = 200, + examples = Example( + ExampleProperty( + value = "{ \"balance\": 990 }", + mediaType = "application/json" + ) + ) + ) suspend fun getBalance( principal: Principal, @PathVariable("currency") currency: String, diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/InquiryController.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/InquiryController.kt index 97e5b044b..b8ab69a61 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/InquiryController.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/InquiryController.kt @@ -3,6 +3,9 @@ package co.nilin.mixchange.wallet.app.controller import co.nilin.mixchange.wallet.core.model.Amount import co.nilin.mixchange.wallet.core.spi.WalletManager import co.nilin.mixchange.wallet.core.spi.WalletOwnerManager +import io.swagger.annotations.ApiResponse +import io.swagger.annotations.Example +import io.swagger.annotations.ExampleProperty import org.slf4j.LoggerFactory import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable @@ -17,6 +20,16 @@ class InquiryController( data class BooleanResponse(val result: Boolean) @GetMapping("{uuid}/wallet_type/{wallet_type}/can_withdraw/{amount}_{currency}") + @ApiResponse( + message = "OK", + code = 200, + examples = Example( + ExampleProperty( + value = "{ }", + mediaType = "application/json" + ) + ) + ) suspend fun canFulfill( @PathVariable("uuid") uuid: String, @PathVariable("currency") currency: String, diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/TransferController.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/TransferController.kt index 5da36373b..54ccfaaef 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/TransferController.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/TransferController.kt @@ -6,6 +6,9 @@ import co.nilin.mixchange.wallet.core.model.Amount import co.nilin.mixchange.wallet.core.service.TransferService import co.nilin.mixchange.wallet.core.spi.WalletManager import co.nilin.mixchange.wallet.core.spi.WalletOwnerManager +import io.swagger.annotations.ApiResponse +import io.swagger.annotations.Example +import io.swagger.annotations.ExampleProperty import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RestController @@ -17,6 +20,16 @@ class TransferController( val transferService: TransferService, val walletManager: WalletManager, val walletOwnerManager: WalletOwnerManager ) { @PostMapping("/transfer/{amount}_{symbol}/from/{senderUuid}_{senderWalletType}/to/{receiverUuid}_{receiverWalletType}") + @ApiResponse( + message = "OK", + code = 200, + examples = Example( + ExampleProperty( + value = "{ }", + mediaType = "application/json" + ) + ) + ) suspend fun transfer( @PathVariable("symbol") symbol: String, @PathVariable("senderWalletType") senderWalletType: String, diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/WalletOwnerController.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/WalletOwnerController.kt index f4e0f854c..d01d61942 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/WalletOwnerController.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/mixchange/wallet/app/controller/WalletOwnerController.kt @@ -2,6 +2,9 @@ package co.nilin.mixchange.wallet.app.controller import co.nilin.mixchange.wallet.core.spi.WalletManager import co.nilin.mixchange.wallet.core.spi.WalletOwnerManager +import io.swagger.annotations.ApiResponse +import io.swagger.annotations.Example +import io.swagger.annotations.ExampleProperty import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RestController import java.math.BigDecimal @@ -26,6 +29,16 @@ class WalletOwnerController( ) @GetMapping("/owner/wallet/all") + @ApiResponse( + message = "OK", + code = 200, + examples = Example( + ExampleProperty( + value = "{ }", + mediaType = "application/json" + ) + ) + ) suspend fun getAllWallets(principal: Principal): List { val owner = walletOwnerManager.findWalletOwner(principal.name) if (owner != null) { @@ -38,6 +51,16 @@ class WalletOwnerController( } @GetMapping("/owner/limits") + @ApiResponse( + message = "OK", + code = 200, + examples = Example( + ExampleProperty( + value = "{ }", + mediaType = "application/json" + ) + ) + ) suspend fun getWalletOwnerLimits(principal: Principal): OwnerLimitsResponse { val owner = walletOwnerManager.findWalletOwner(principal.name) return if (owner != null) From 185d42b43bced2c18be0a91b64fa81d29507c9fd Mon Sep 17 00:00:00 2001 From: metalicn20 Date: Tue, 10 Aug 2021 15:22:06 +0430 Subject: [PATCH 16/59] Fix corrupted config files --- Deployment/docker-compose.yml | 176 +++++++++--------- .../src/main/resources/application-docker.yml | 4 +- 2 files changed, 90 insertions(+), 90 deletions(-) diff --git a/Deployment/docker-compose.yml b/Deployment/docker-compose.yml index 5a07ee778..0dda34600 100644 --- a/Deployment/docker-compose.yml +++ b/Deployment/docker-compose.yml @@ -4,8 +4,8 @@ services: image: 'docker.io/bitnami/zookeeper:3-debian-10' ports: - '127.0.0.1:2181:2181' -# volumes: -# - $PWD/runtime/zookeeper_data:/bitnami + volumes: + - $PWD/runtime/zookeeper_data:/bitnami environment: - ALLOW_ANONYMOUS_LOGIN=yes networks: @@ -17,8 +17,8 @@ services: image: 'docker.io/bitnami/kafka:2-debian-10' ports: - '127.0.0.1:9092:9092' -# volumes: -# - $PWD/runtime/kafka-data:/bitnami + volumes: + - $PWD/runtime/kafka-data:/bitnami environment: - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181 - ALLOW_PLAINTEXT_LISTENER=yes @@ -62,30 +62,30 @@ services: deploy: restart_policy: condition: on-failure -# postgres-accountant: -# image: "postgres" -# ports: -# - 127.0.0.1:5433:5432 -# environment: -# - POSTGRES_USER=opex -# - POSTGRES_PASSWORD=hiopex -# - POSTGRES_DB=opex_accountant -# volumes: -# - $PWD/runtime/accountant-data:/var/lib/postgresql/data/ -# networks: -# - opex -# postgres-eventlog: -# image: "postgres" -# ports: -# - 127.0.0.1:5434:5432 -# environment: -# - POSTGRES_USER=opex -# - POSTGRES_PASSWORD=hiopex -# - POSTGRES_DB=opex_eventlog -# volumes: -# - $PWD/runtime/eventlog-data:/var/lib/postgresql/data/ -# networks: -# - opex + postgres-accountant: + image: "postgres" + ports: + - 127.0.0.1:5433:5432 + environment: + - POSTGRES_USER=opex + - POSTGRES_PASSWORD=hiopex + - POSTGRES_DB=opex_accountant + volumes: + - $PWD/runtime/accountant-data:/var/lib/postgresql/data/ + networks: + - opex + postgres-eventlog: + image: "postgres" + ports: + - 127.0.0.1:5434:5432 + environment: + - POSTGRES_USER=opex + - POSTGRES_PASSWORD=hiopex + - POSTGRES_DB=opex_eventlog + volumes: + - $PWD/runtime/eventlog-data:/var/lib/postgresql/data/ + networks: + - opex postgres-auth: image: "postgres" ports: @@ -131,51 +131,51 @@ services: deploy: restart_policy: condition: on-failure -# accountant: -# container_name: accountant -# build: -# context: ../Accountant/accountant-app -# dockerfile: Dockerfile -# ports: -# - 127.0.0.1:8089:8089 -# - 127.0.0.1:1051:1044 -# environment: -# - JAVA_OPTS=-Xmx256m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044 -# - SPRING_PROFILES_ACTIVE=docker -# - KAFKA_IP_PORT=kafka:9092 -# - REDIS_HOST=redis -# - CONSUL_HOST=consul -# - DB_IP_PORT=postgres-accountant -# networks: -# - opex -# depends_on: -# - zookeeper -# - kafka -# - redis -# - consul -# - postgres-accountant -# eventlog: -# container_name: eventlog -# build: -# context: ../EventLog/eventlog-app -# dockerfile: Dockerfile -# ports: -# - 127.0.0.1:8090:8090 -# environment: -# - JAVA_OPTS=-Xmx256m -# - SPRING_PROFILES_ACTIVE=docker -# - KAFKA_IP_PORT=kafka:9092 -# - REDIS_HOST=redis -# - CONSUL_HOST=consul -# - DB_IP_PORT=postgres-eventlog -# networks: -# - opex -# depends_on: -# - zookeeper -# - kafka -# - redis -# - consul -# - postgres-eventlog + accountant: + container_name: accountant + build: + context: ../Accountant/accountant-app + dockerfile: Dockerfile + ports: + - 127.0.0.1:8089:8089 + - 127.0.0.1:1051:1044 + environment: + - JAVA_OPTS=-Xmx256m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044 + - SPRING_PROFILES_ACTIVE=docker + - KAFKA_IP_PORT=kafka:9092 + - REDIS_HOST=redis + - CONSUL_HOST=consul + - DB_IP_PORT=postgres-accountant + networks: + - opex + depends_on: + - zookeeper + - kafka + - redis + - consul + - postgres-accountant + eventlog: + container_name: eventlog + build: + context: ../EventLog/eventlog-app + dockerfile: Dockerfile + ports: + - 127.0.0.1:8090:8090 + environment: + - JAVA_OPTS=-Xmx256m + - SPRING_PROFILES_ACTIVE=docker + - KAFKA_IP_PORT=kafka:9092 + - REDIS_HOST=redis + - CONSUL_HOST=consul + - DB_IP_PORT=postgres-eventlog + networks: + - opex + depends_on: + - zookeeper + - kafka + - redis + - consul + - postgres-eventlog matching-engine: container_name: matching-engine build: @@ -295,21 +295,21 @@ services: deploy: restart_policy: condition: on-failure -# nginx: -# image: nginx:latest -# container_name: opex_nginx -# volumes: -# - ./nginx.conf:/etc/nginx/nginx.conf -# - $PWD/runtime/www:/data/www -# ports: -# - 80:80 -# depends_on: -# - wallet -# - auth -# - matching-gateway -# - api -# networks: -# - opex + nginx: + image: nginx:latest + container_name: opex_nginx + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf + - $PWD/runtime/www:/data/www + ports: + - 80:80 + depends_on: + - wallet + - auth + - matching-gateway + - api + networks: + - opex networks: opex: driver: bridge \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/src/main/resources/application-docker.yml b/UserManagement/keycloak-gateway/src/main/resources/application-docker.yml index f20892759..0c88c6422 100644 --- a/UserManagement/keycloak-gateway/src/main/resources/application-docker.yml +++ b/UserManagement/keycloak-gateway/src/main/resources/application-docker.yml @@ -11,5 +11,5 @@ spring: keycloak: migration: file: /opex-master-realm.json -# adminUrl: https://api.opex.dev/auth -# frontendUrl: https://api.opex.dev/auth + adminUrl: https://api.opex.dev/auth + frontendUrl: https://api.opex.dev/auth From 835760dc4f8f4ac127a14b187f8ef5de34a2be2c Mon Sep 17 00:00:00 2001 From: metalicn20 Date: Sat, 14 Aug 2021 14:18:50 +0430 Subject: [PATCH 17/59] Extract swagger auth url to config file --- Api/api-app/src/main/kotlin/co/nilin/opex/app/ApiApp.kt | 6 +++++- Api/api-app/src/main/resources/application.yml | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Api/api-app/src/main/kotlin/co/nilin/opex/app/ApiApp.kt b/Api/api-app/src/main/kotlin/co/nilin/opex/app/ApiApp.kt index 616a58bf7..a6c4103a4 100644 --- a/Api/api-app/src/main/kotlin/co/nilin/opex/app/ApiApp.kt +++ b/Api/api-app/src/main/kotlin/co/nilin/opex/app/ApiApp.kt @@ -1,5 +1,6 @@ package co.nilin.opex.app +import org.springframework.beans.factory.annotation.Value import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.annotation.Bean @@ -22,6 +23,9 @@ import java.util.Collections.singletonList @ComponentScan("co.nilin.opex") @EnableSwagger2 class ApiApp { + @Value("\${swagger.authUrl}") + val authUrl: String = "" + @Bean fun opexApi(): Docket? { return Docket(DocumentationType.SWAGGER_2) @@ -70,7 +74,7 @@ class ApiApp { } fun grantTypes(): List? { - val tokenUrl = "http://localhost:8083/auth/realms/mixchange/protocol/openid-connect/token" + val tokenUrl = "${authUrl}/auth/realms/mixchange/protocol/openid-connect/token" val grantType = ResourceOwnerPasswordCredentialsGrant(tokenUrl) return singletonList(grantType) } diff --git a/Api/api-app/src/main/resources/application.yml b/Api/api-app/src/main/resources/application.yml index 66f9231c5..941180d9b 100644 --- a/Api/api-app/src/main/resources/application.yml +++ b/Api/api-app/src/main/resources/application.yml @@ -31,4 +31,6 @@ app: matching-gateway: url: lb://opex-gateway wallet: - url: lb://opex-wallet \ No newline at end of file + url: lb://opex-wallet + +swagger.authUrl: https://api.opex.dev \ No newline at end of file From facd9823b4627c47a4d2a44d907a1b2f7b2e18f0 Mon Sep 17 00:00:00 2001 From: metalicn20 Date: Sat, 14 Aug 2021 14:28:57 +0430 Subject: [PATCH 18/59] Fix swagger auth url in all modules --- .../src/main/kotlin/co/nilin/opex/app/MatchingGatewayApp.kt | 6 +++++- .../gateway-app/src/main/resources/application.yml | 4 +++- .../src/main/kotlin/co/nilin/opex/wallet/app/WalletApp.kt | 6 +++++- Wallet/wallet-app/src/main/resources/application.yml | 2 ++ 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/MatchingGatewayApp.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/MatchingGatewayApp.kt index e1e67cbba..5b1db6af4 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/MatchingGatewayApp.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/MatchingGatewayApp.kt @@ -1,5 +1,6 @@ package co.nilin.opex.app +import org.springframework.beans.factory.annotation.Value import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.annotation.Bean @@ -21,6 +22,9 @@ import java.util.Collections.singletonList @SpringBootApplication @ComponentScan("co.nilin.opex") class MatchingGatewayApp { + @Value("\${swagger.authUrl}") + val authUrl: String = "" + @Bean fun opexMatchingGateway(): Docket? { return Docket(DocumentationType.SWAGGER_2) @@ -69,7 +73,7 @@ class MatchingGatewayApp { } fun grantTypes(): List? { - val tokenUrl = "http://localhost:8083/auth/realms/mixchange/protocol/openid-connect/token" + val tokenUrl = "${authUrl}/auth/realms/mixchange/protocol/openid-connect/token" val grantType = ResourceOwnerPasswordCredentialsGrant(tokenUrl) return singletonList(grantType) } diff --git a/MatchingGateway/gateway-app/src/main/resources/application.yml b/MatchingGateway/gateway-app/src/main/resources/application.yml index efafd9860..b42b848c3 100644 --- a/MatchingGateway/gateway-app/src/main/resources/application.yml +++ b/MatchingGateway/gateway-app/src/main/resources/application.yml @@ -20,4 +20,6 @@ spring: prefer-ip-address: true app: accountant: - url: lb://opex-accountant \ No newline at end of file + url: lb://opex-accountant + +swagger.authUrl: https://api.opex.dev \ No newline at end of file diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/WalletApp.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/WalletApp.kt index bb281db17..c512b722d 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/WalletApp.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/WalletApp.kt @@ -1,5 +1,6 @@ package co.nilin.opex.wallet.app +import org.springframework.beans.factory.annotation.Value import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.annotation.Bean @@ -21,6 +22,9 @@ import java.util.Collections.singletonList @SpringBootApplication @ComponentScan("co.nilin.opex") class WalletApp { + @Value("\${swagger.authUrl}") + val authUrl: String = "" + @Bean fun opexWallet(): Docket? { return Docket(DocumentationType.SWAGGER_2) @@ -69,7 +73,7 @@ class WalletApp { } fun grantTypes(): List? { - val tokenUrl = "http://localhost:8083/auth/realms/mixchange/protocol/openid-connect/token" + val tokenUrl = "${authUrl}/auth/realms/mixchange/protocol/openid-connect/token" val grantType = ResourceOwnerPasswordCredentialsGrant(tokenUrl) return singletonList(grantType) } diff --git a/Wallet/wallet-app/src/main/resources/application.yml b/Wallet/wallet-app/src/main/resources/application.yml index daaf189e5..fad5e6431 100644 --- a/Wallet/wallet-app/src/main/resources/application.yml +++ b/Wallet/wallet-app/src/main/resources/application.yml @@ -33,3 +33,5 @@ app: logging: level: org.apache.kafka: DEBUG + +swagger.authUrl: https://api.opex.dev From 53079a72dc82226bfaeb137531df1474f7513384 Mon Sep 17 00:00:00 2001 From: maryarm Date: Sat, 14 Aug 2021 19:28:53 +0430 Subject: [PATCH 19/59] Closed #19: Add arch/logic to deposit crypto currencies --- BlockchainGateway/.gitignore | 47 +++ BlockchainGateway/bc-gateway-app/.gitignore | 35 ++ BlockchainGateway/bc-gateway-app/Dockerfile | 5 + BlockchainGateway/bc-gateway-app/mvnw | 310 ++++++++++++++++++ BlockchainGateway/bc-gateway-app/mvnw.cmd | 182 ++++++++++ BlockchainGateway/bc-gateway-app/pom.xml | 227 +++++++++++++ .../nilin/opex/bcgateway/app/BCGatewayApp.kt | 13 + .../opex/bcgateway/app/config/AppConfig.kt | 64 ++++ .../bcgateway/app/config/AppDispatchers.kt | 9 + .../bcgateway/app/config/SecurityConfig.kt | 44 +++ .../app/controller/DepositController.kt | 22 ++ .../src/main/resources/application-docker.yml | 19 ++ .../src/main/resources/application.yml | 31 ++ .../src/main/resources/public.cert | 3 + BlockchainGateway/bc-gateway-core/.gitignore | 35 ++ BlockchainGateway/bc-gateway-core/mvnw | 310 ++++++++++++++++++ BlockchainGateway/bc-gateway-core/mvnw.cmd | 182 ++++++++++ BlockchainGateway/bc-gateway-core/pom.xml | 97 ++++++ .../core/api/AssignAddressService.kt | 8 + .../bcgateway/core/api/ChainSyncService.kt | 5 + .../opex/bcgateway/core/api/InfoService.kt | 8 + .../bcgateway/core/api/WalletSyncService.kt | 5 + .../opex/bcgateway/core/model/Address.kt | 27 ++ .../nilin/opex/bcgateway/core/model/Chain.kt | 17 + .../opex/bcgateway/core/model/Currency.kt | 17 + .../opex/bcgateway/core/model/Deposit.kt | 5 + .../opex/bcgateway/core/model/WalletSync.kt | 11 + .../core/service/AssignAddressServiceImpl.kt | 66 ++++ .../core/service/ChainSyncServiceImpl.kt | 54 +++ .../bcgateway/core/service/InfoServiceImpl.kt | 14 + .../core/service/WalletSyncServiceImpl.kt | 42 +++ .../core/spi/AssignedAddressHandler.kt | 10 + .../core/spi/CachedAddressHandler.kt | 10 + .../bcgateway/core/spi/ChainEndpointProxy.kt | 8 + .../core/spi/ChainEndpointProxyFinder.kt | 5 + .../opex/bcgateway/core/spi/ChainLoader.kt | 8 + .../opex/bcgateway/core/spi/CurrencyLoader.kt | 10 + .../bcgateway/core/spi/SyncRecordHandler.kt | 8 + .../core/spi/SyncSchedulerHandler.kt | 9 + .../opex/bcgateway/core/spi/WalletProxy.kt | 7 + .../core/spi/WalletSyncRecordHandler.kt | 8 + .../core/spi/WalletSyncSchedulerHandler.kt | 9 + .../src/main/resources/application.properties | 1 + .../AssignAddressServiceImplUnitTest.kt | 163 +++++++++ .../core/service/ChainSyncServiceImplTest.kt | 142 ++++++++ .../opex/bcgateway/test/MockTxExtension.kt | 27 ++ .../bc-persister-postgres/.gitignore | 36 ++ .../bc-persister-postgres/mvnw | 310 ++++++++++++++++++ .../bc-persister-postgres/mvnw.cmd | 182 ++++++++++ .../bc-persister-postgres/pom.xml | 105 ++++++ .../postgres/config/PostgresConfig.kt | 22 ++ .../postgres/dao/AddressTypeRepository.kt | 8 + .../dao/AssignedAddressChainRepository.kt | 15 + .../postgres/dao/AssignedAddressRepository.kt | 15 + .../bcgateway/postgres/dao/ChainRepository.kt | 12 + .../impl/AssignedAddressHandlerImpl.kt | 51 +++ .../postgres/impl/CachedAddressHandlerImpl.kt | 15 + .../bcgateway/postgres/impl/ChainHandler.kt | 13 + .../postgres/impl/CurrencyLoaderImpl.kt | 19 ++ .../postgres/impl/SyncSchedulerHandlerImpl.kt | 17 + .../impl/WalletSyncSchedulerHandlerImpl.kt | 17 + .../postgres/model/AddressTypeModel.kt | 12 + .../postgres/model/AssignedAddressModel.kt | 21 ++ .../postgres/model/CachedAddressModel.kt | 10 + .../bcgateway/postgres/model/ChainModel.kt | 21 ++ .../bcgateway/postgres/model/SyncModel.kt | 21 ++ BlockchainGateway/pom.xml | 17 + 67 files changed, 3278 insertions(+) create mode 100644 BlockchainGateway/.gitignore create mode 100644 BlockchainGateway/bc-gateway-app/.gitignore create mode 100644 BlockchainGateway/bc-gateway-app/Dockerfile create mode 100644 BlockchainGateway/bc-gateway-app/mvnw create mode 100644 BlockchainGateway/bc-gateway-app/mvnw.cmd create mode 100644 BlockchainGateway/bc-gateway-app/pom.xml create mode 100644 BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/BCGatewayApp.kt create mode 100644 BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/AppConfig.kt create mode 100644 BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/AppDispatchers.kt create mode 100644 BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/SecurityConfig.kt create mode 100644 BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/controller/DepositController.kt create mode 100644 BlockchainGateway/bc-gateway-app/src/main/resources/application-docker.yml create mode 100644 BlockchainGateway/bc-gateway-app/src/main/resources/application.yml create mode 100644 BlockchainGateway/bc-gateway-app/src/main/resources/public.cert create mode 100644 BlockchainGateway/bc-gateway-core/.gitignore create mode 100644 BlockchainGateway/bc-gateway-core/mvnw create mode 100644 BlockchainGateway/bc-gateway-core/mvnw.cmd create mode 100644 BlockchainGateway/bc-gateway-core/pom.xml create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/api/AssignAddressService.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/api/ChainSyncService.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/api/InfoService.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/api/WalletSyncService.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Address.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Chain.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Currency.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Deposit.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/WalletSync.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/AssignAddressServiceImpl.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/ChainSyncServiceImpl.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/InfoServiceImpl.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/WalletSyncServiceImpl.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/AssignedAddressHandler.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/CachedAddressHandler.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/ChainEndpointProxy.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/ChainEndpointProxyFinder.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/ChainLoader.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/CurrencyLoader.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/SyncRecordHandler.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/SyncSchedulerHandler.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/WalletProxy.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/WalletSyncRecordHandler.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/WalletSyncSchedulerHandler.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/resources/application.properties create mode 100644 BlockchainGateway/bc-gateway-core/src/test/kotlin/co/nilin/opex/bcgateway/core/service/AssignAddressServiceImplUnitTest.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/test/kotlin/co/nilin/opex/bcgateway/core/service/ChainSyncServiceImplTest.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/test/kotlin/co/nilin/opex/bcgateway/test/MockTxExtension.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/.gitignore create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/mvnw create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/mvnw.cmd create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/pom.xml create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/config/PostgresConfig.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AddressTypeRepository.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AssignedAddressChainRepository.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AssignedAddressRepository.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainRepository.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/AssignedAddressHandlerImpl.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/CachedAddressHandlerImpl.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/ChainHandler.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/CurrencyLoaderImpl.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/SyncSchedulerHandlerImpl.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/WalletSyncSchedulerHandlerImpl.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/AddressTypeModel.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/AssignedAddressModel.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/CachedAddressModel.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/ChainModel.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/SyncModel.kt create mode 100644 BlockchainGateway/pom.xml diff --git a/BlockchainGateway/.gitignore b/BlockchainGateway/.gitignore new file mode 100644 index 000000000..785786ed7 --- /dev/null +++ b/BlockchainGateway/.gitignore @@ -0,0 +1,47 @@ +# Created by .ignore support plugin (hsz.mobi) +### Kotlin template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### JetBrains template +.idea/** +.idea +.idea/ +*.iml +*.ipr + +# File-based project format +*.iws + +# IntelliJ +out/ + +target/ + + + + +!/.idea/ + +.DS_Store diff --git a/BlockchainGateway/bc-gateway-app/.gitignore b/BlockchainGateway/bc-gateway-app/.gitignore new file mode 100644 index 000000000..0d6c2228e --- /dev/null +++ b/BlockchainGateway/bc-gateway-app/.gitignore @@ -0,0 +1,35 @@ +HELP.md +target/ +.mvn/ +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +.DS_Store diff --git a/BlockchainGateway/bc-gateway-app/Dockerfile b/BlockchainGateway/bc-gateway-app/Dockerfile new file mode 100644 index 000000000..f2cbd4c26 --- /dev/null +++ b/BlockchainGateway/bc-gateway-app/Dockerfile @@ -0,0 +1,5 @@ +FROM openjdk:8-jdk-alpine +VOLUME /tmp +ARG JAR_FILE=target/*.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"] \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-app/mvnw b/BlockchainGateway/bc-gateway-app/mvnw new file mode 100644 index 000000000..a16b5431b --- /dev/null +++ b/BlockchainGateway/bc-gateway-app/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/BlockchainGateway/bc-gateway-app/mvnw.cmd b/BlockchainGateway/bc-gateway-app/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/BlockchainGateway/bc-gateway-app/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/BlockchainGateway/bc-gateway-app/pom.xml b/BlockchainGateway/bc-gateway-app/pom.xml new file mode 100644 index 000000000..ea7ba8495 --- /dev/null +++ b/BlockchainGateway/bc-gateway-app/pom.xml @@ -0,0 +1,227 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.opex.external + bc-gateway-app + 0.0.1-SNAPSHOT + bc-gateway-app + Blockchain gateway app of Opex + + + 1.8 + 1.4.31 + ${version} + ${version} + 2020.0.2 + + + + + org.springframework.boot + spring-boot-starter-webflux + + + com.fasterxml.jackson.module + jackson-module-kotlin + + + + org.jetbrains.kotlin + kotlin-stdlib + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + io.projectreactor + reactor-test + test + + + org.springframework.cloud + spring-cloud-starter-consul-all + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + + org.bouncycastle + bcprov-jdk15on + 1.60 + + + co.nilin.opex.external + bc-gateway-core + ${bc-gateway.version} + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18 + + + ${skip.unit.tests} + + + **/*IntegrationTest.java + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-test-source + generate-test-sources + + add-test-source + + + + src/test/java + + + + + compile + + add-source + + + + src/main/java + + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + + -Xjsr305=strict + + + spring + + 1.8 + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + compile + compile + + compile + + + + testCompile + test-compile + + testCompile + + + + + + opex-bc-gateway + + + diff --git a/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/BCGatewayApp.kt b/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/BCGatewayApp.kt new file mode 100644 index 000000000..3a906cb3a --- /dev/null +++ b/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/BCGatewayApp.kt @@ -0,0 +1,13 @@ +package co.nilin.opex.bcgateway.app + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.runApplication +import org.springframework.context.annotation.ComponentScan + +@SpringBootApplication +@ComponentScan("co.nilin.mixchange") +class WalletApp + +fun main(args: Array) { + runApplication(*args) +} diff --git a/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/AppConfig.kt b/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/AppConfig.kt new file mode 100644 index 000000000..fc5c07cff --- /dev/null +++ b/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/AppConfig.kt @@ -0,0 +1,64 @@ +package co.nilin.opex.bcgateway.app.config + + +import co.nilin.opex.bcgateway.core.api.AssignAddressService +import co.nilin.opex.bcgateway.core.api.ChainSyncService +import co.nilin.opex.bcgateway.core.api.InfoService +import co.nilin.opex.bcgateway.core.api.WalletSyncService +import co.nilin.opex.bcgateway.core.service.AssignAddressServiceImpl +import co.nilin.opex.bcgateway.core.service.ChainSyncServiceImpl +import co.nilin.opex.bcgateway.core.service.InfoServiceImpl +import co.nilin.opex.bcgateway.core.service.WalletSyncServiceImpl +import co.nilin.opex.bcgateway.core.spi.* +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.transaction.reactive.TransactionalOperator + +@Configuration +class AppConfig { + + @Bean + fun assignAddressService( + currencyLoader: CurrencyLoader, + assignedAddressHandler: AssignedAddressHandler, + cachedAddressHandler: CachedAddressHandler + ): AssignAddressService { + return AssignAddressServiceImpl(currencyLoader, assignedAddressHandler, cachedAddressHandler) + } + + @Bean + fun chainSyncService( + syncSchedulerHandler: SyncSchedulerHandler, + chainEndpointProxyFinder: ChainEndpointProxyFinder, + syncRecordHandler: SyncRecordHandler, + walletSyncRecordHandler: WalletSyncRecordHandler, + currencyLoader: CurrencyLoader, + operator: TransactionalOperator + ): ChainSyncService { + return ChainSyncServiceImpl( + syncSchedulerHandler, + chainEndpointProxyFinder, + syncRecordHandler, + walletSyncRecordHandler, + currencyLoader, + operator, + AppDispatchers.chainSyncExecutor + ) + } + + @Bean + fun walletSyncService( + syncSchedulerHandler: WalletSyncSchedulerHandler, + walletProxy: WalletProxy, + walletSyncRecordHandler: WalletSyncRecordHandler, + assignedAddressHandler: AssignedAddressHandler, + currencyLoader: CurrencyLoader + ): WalletSyncService { + return WalletSyncServiceImpl(syncSchedulerHandler, walletProxy, walletSyncRecordHandler, assignedAddressHandler, currencyLoader, AppDispatchers.walletSyncExecutor) + } + + @Bean + fun infoService(): InfoService { + return InfoServiceImpl() + } +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/AppDispatchers.kt b/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/AppDispatchers.kt new file mode 100644 index 000000000..f03b24dc8 --- /dev/null +++ b/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/AppDispatchers.kt @@ -0,0 +1,9 @@ +package co.nilin.opex.bcgateway.app.config + +import kotlinx.coroutines.asCoroutineDispatcher +import java.util.concurrent.Executors + +object AppDispatchers { + val chainSyncExecutor = Executors.newFixedThreadPool(5).asCoroutineDispatcher() + val walletSyncExecutor = Executors.newFixedThreadPool(2).asCoroutineDispatcher() +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/SecurityConfig.kt b/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/SecurityConfig.kt new file mode 100644 index 000000000..061ec977f --- /dev/null +++ b/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/SecurityConfig.kt @@ -0,0 +1,44 @@ +package co.nilin.opex.bcgateway.app.config + +import org.springframework.context.annotation.Bean +import org.springframework.core.io.ClassPathResource +import org.springframework.core.io.Resource +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity +import org.springframework.security.config.web.server.ServerHttpSecurity +import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder +import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder +import org.springframework.security.web.server.SecurityWebFilterChain +import org.springframework.util.Base64Utils +import org.springframework.util.FileCopyUtils +import java.security.KeyFactory +import java.security.interfaces.RSAPublicKey +import java.security.spec.X509EncodedKeySpec + +@EnableWebFluxSecurity +class SecurityConfig { + @Bean + fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain? { + http.csrf().disable() + .authorizeExchange() + .pathMatchers("/filter/**").hasAuthority("SCOPE_trust") + .pathMatchers("/**").permitAll() + .anyExchange().authenticated() + .and() + .oauth2ResourceServer() + .jwt() + return http.build() + } + + @Bean + @Throws(Exception::class) + fun reactiveJwtDecoder(): ReactiveJwtDecoder? { + val resource: Resource = ClassPathResource("/public.cert") + val publicKey = String(FileCopyUtils.copyToByteArray(resource.inputStream)) + .replace("\r", "") + .replace("-----BEGIN PUBLIC KEY-----\n", "") + .replace("\n-----END PUBLIC KEY-----", "") + val spec = X509EncodedKeySpec(Base64Utils.decodeFromString(publicKey)) + val kf = KeyFactory.getInstance("RSA") + return NimbusReactiveJwtDecoder(kf.generatePublic(spec) as RSAPublicKey) + } +} diff --git a/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/controller/DepositController.kt b/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/controller/DepositController.kt new file mode 100644 index 000000000..e67bef938 --- /dev/null +++ b/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/controller/DepositController.kt @@ -0,0 +1,22 @@ +package co.nilin.opex.bcgateway.app.controller + +import co.nilin.opex.bcgateway.core.api.AssignAddressService +import co.nilin.opex.bcgateway.core.model.AssignedAddress +import co.nilin.opex.bcgateway.core.model.Currency +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RestController + +@RestController +class DepositController(val assignAddressService: AssignAddressService) { + data class AssignAddressRequest(val uuid: String, val currency: String) + data class AssignAddressResponse(val addresses: List) + + @PostMapping("deposits/assign") + suspend fun assignAddress(@RequestBody assignAddressRequest: AssignAddressRequest): AssignAddressResponse { + val assignedAddress = assignAddressService + .assignAddress(assignAddressRequest.uuid, + Currency(assignAddressRequest.currency, assignAddressRequest.currency)) + return AssignAddressResponse(assignedAddress) + } +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-app/src/main/resources/application-docker.yml b/BlockchainGateway/bc-gateway-app/src/main/resources/application-docker.yml new file mode 100644 index 000000000..6db331761 --- /dev/null +++ b/BlockchainGateway/bc-gateway-app/src/main/resources/application-docker.yml @@ -0,0 +1,19 @@ +server.port: 8091 +spring: + application: + name: opex-bc-gateway + kafka: + bootstrap-servers: ${KAFKA_IP_PORT} + redis: + host: ${REDIS_HOST} + port: 6379 + r2dbc: + url: r2dbc:postgresql://${DB_IP_PORT}/opex_bc_gateway + username: opex + password: hiopex + initialization-mode: always + cloud: + consul: + host: ${CONSUL_HOST} + port: 8500 + diff --git a/BlockchainGateway/bc-gateway-app/src/main/resources/application.yml b/BlockchainGateway/bc-gateway-app/src/main/resources/application.yml new file mode 100644 index 000000000..2c544a1f5 --- /dev/null +++ b/BlockchainGateway/bc-gateway-app/src/main/resources/application.yml @@ -0,0 +1,31 @@ +server.port: 8091 +spring: + application: + name: opex-bc-gateway + main: + allow-bean-definition-overriding: false + kafka: + bootstrap-servers: localhost:2181 + consumer: + group-id: opex-bc-gateway + redis: + host: 127.0.0.1 + port: 6379 + r2dbc: + url: r2dbc:postgresql://localhost/opex_bc_gateway + username: opex + password: hiopex + initialization-mode: always + cloud: + bootstrap: + enabled: true + consul: + port: 8500 + discovery: + #healthCheckPath: ${management.context-path}/health + instance-id: ${spring.application.name}:${server.port} + healthCheckInterval: 20s + prefer-ip-address: true +logging: + level: + org.apache.kafka: DEBUG diff --git a/BlockchainGateway/bc-gateway-app/src/main/resources/public.cert b/BlockchainGateway/bc-gateway-app/src/main/resources/public.cert new file mode 100644 index 000000000..8589de065 --- /dev/null +++ b/BlockchainGateway/bc-gateway-app/src/main/resources/public.cert @@ -0,0 +1,3 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsw+lmV2HbpgXllKS+ccyCerlWDir32Y3yFvXF3CbzYRKVg/4FddA8itMCPpPjVOAo26bi/0WImHHYPyGxSH3QQIQaSdS5AFUNarpFc9M/W4Bu7k5h8sKb+vzZA3hMLFyvmQ2JtY8Zuqi1BBB33OAlkOHprJFTrrt1xa4a6KJjKXlDp5o5hOFgvZbsW+k/fBU0CLCbL3PlXQLhqHgEncpL9L9FF/xDbPIGftxjEWcBPFG/SuyygT35xRFMxB7ifR+wJZwVnoC4RhbhgJdL2EiUVT7wq5MTsxr2j4nAAJYatK/7OTecs6y/7bGNAGEoqh4Q3X3D95YFex0scmHgY1hWwIDAQAB +-----END PUBLIC KEY----- \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/.gitignore b/BlockchainGateway/bc-gateway-core/.gitignore new file mode 100644 index 000000000..8e22e6c21 --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/.gitignore @@ -0,0 +1,35 @@ +HELP.md +target/ +.mvn +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +.DS_Store diff --git a/BlockchainGateway/bc-gateway-core/mvnw b/BlockchainGateway/bc-gateway-core/mvnw new file mode 100644 index 000000000..a16b5431b --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/BlockchainGateway/bc-gateway-core/mvnw.cmd b/BlockchainGateway/bc-gateway-core/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/BlockchainGateway/bc-gateway-core/pom.xml b/BlockchainGateway/bc-gateway-core/pom.xml new file mode 100644 index 000000000..7c42fe00e --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/pom.xml @@ -0,0 +1,97 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.opex.external + bc-gateway-core + 0.0.1-SNAPSHOT + bc-gateway-core + Blockchain gateway core of Opex + + + 1.8 + 1.4.31 + 3.2.0 + + + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.springframework.boot + spring-boot-starter + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.springframework + spring-tx + provided + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + org.mockito.kotlin + mockito-kotlin + ${mockito-kotlin.version} + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/api/AssignAddressService.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/api/AssignAddressService.kt new file mode 100644 index 000000000..275c8ec48 --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/api/AssignAddressService.kt @@ -0,0 +1,8 @@ +package co.nilin.opex.bcgateway.core.api + +import co.nilin.opex.bcgateway.core.model.AssignedAddress +import co.nilin.opex.bcgateway.core.model.Currency + +interface AssignAddressService { + suspend fun assignAddress(user: String, currency: Currency): List +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/api/ChainSyncService.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/api/ChainSyncService.kt new file mode 100644 index 000000000..ec62c097f --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/api/ChainSyncService.kt @@ -0,0 +1,5 @@ +package co.nilin.opex.bcgateway.core.api + +interface ChainSyncService { + suspend fun startSyncWithChain() +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/api/InfoService.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/api/InfoService.kt new file mode 100644 index 000000000..6286ab830 --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/api/InfoService.kt @@ -0,0 +1,8 @@ +package co.nilin.opex.bcgateway.core.api + +import co.nilin.opex.bcgateway.core.model.CurrencyInfo + +interface InfoService { + suspend fun countCachedAddresses(): Long + suspend fun getCurrencyInfo(): CurrencyInfo +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/api/WalletSyncService.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/api/WalletSyncService.kt new file mode 100644 index 000000000..df81a410d --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/api/WalletSyncService.kt @@ -0,0 +1,5 @@ +package co.nilin.opex.bcgateway.core.api + +interface WalletSyncService { + suspend fun startSyncWithWallet() +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Address.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Address.kt new file mode 100644 index 000000000..14572e8e9 --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Address.kt @@ -0,0 +1,27 @@ +package co.nilin.opex.bcgateway.core.model + +data class AddressType(val id: Long, val type: String, val addressRegex: String, val memoRegex: String) +data class CachedAddress(val address: String, val memo: String?, val type: AddressType) +data class AssignedAddress(val uuid: String, val address: String, val memo: String?, val type: AddressType, val chains: MutableList ){ + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as AssignedAddress + + if (uuid != other.uuid) return false + if (address != other.address) return false + if (memo != other.memo) return false + if (type != other.type) return false + + return true + } + + override fun hashCode(): Int { + var result = uuid.hashCode() + result = 31 * result + address.hashCode() + result = 31 * result + (memo?.hashCode() ?: 0) + result = 31 * result + type.hashCode() + return result + } +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Chain.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Chain.kt new file mode 100644 index 000000000..73875ced3 --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Chain.kt @@ -0,0 +1,17 @@ +package co.nilin.opex.bcgateway.core.model + +import java.math.BigDecimal +import java.time.LocalDateTime + +data class Endpoint(val url: String) +data class Chain(val name: String, val addressTypes: List, val endpoints: List) +data class ChainSyncSchedule(val chainName: String, val retryTime: LocalDateTime, val delay: Long) +data class ChainSyncRecord(val chainName: String + , val time: LocalDateTime + , val endpoint: Endpoint + , val latestBlock: Long? + , val success: Boolean + , val error: String? + , val records: List +) + diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Currency.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Currency.kt new file mode 100644 index 000000000..73a9c0db5 --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Currency.kt @@ -0,0 +1,17 @@ +package co.nilin.opex.bcgateway.core.model + +import java.math.BigDecimal + +data class Currency(val symbol: String, val name: String) +data class CurrencyImplementation( + val currency: Currency, + val chain: Chain, + val token: Boolean, + val tokenAddress: String?, + val tokenName: String?, + val withdrawEnabled: Boolean, + val withdrawFee: BigDecimal, + val withdrawMin: BigDecimal +) + +data class CurrencyInfo(val currency: Currency, val implementations: List) diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Deposit.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Deposit.kt new file mode 100644 index 000000000..579803a87 --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Deposit.kt @@ -0,0 +1,5 @@ +package co.nilin.opex.bcgateway.core.model + +import java.math.BigDecimal + +data class Deposit(val depositor: String, val depositorMemo: String?, val amount: BigDecimal, val chain: String?, val tokenAddress: String?) diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/WalletSync.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/WalletSync.kt new file mode 100644 index 000000000..3f4ec00fa --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/WalletSync.kt @@ -0,0 +1,11 @@ +package co.nilin.opex.bcgateway.core.model + +import java.time.LocalDateTime + +data class WalletSyncSchedule(val retryTime: LocalDateTime, val delay: Long, val batchSize: Long?) +data class WalletSyncRecord( + val time: LocalDateTime + , val success: Boolean + , val error: String? + , val deposit: Deposit +) \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/AssignAddressServiceImpl.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/AssignAddressServiceImpl.kt new file mode 100644 index 000000000..95dddaf43 --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/AssignAddressServiceImpl.kt @@ -0,0 +1,66 @@ +package co.nilin.opex.bcgateway.core.service + +import co.nilin.opex.bcgateway.core.api.AssignAddressService +import co.nilin.opex.bcgateway.core.model.AddressType +import co.nilin.opex.bcgateway.core.model.AssignedAddress +import co.nilin.opex.bcgateway.core.model.Chain +import co.nilin.opex.bcgateway.core.model.Currency +import co.nilin.opex.bcgateway.core.spi.AssignedAddressHandler +import co.nilin.opex.bcgateway.core.spi.CachedAddressHandler +import co.nilin.opex.bcgateway.core.spi.CurrencyLoader +import java.lang.RuntimeException + +class AssignAddressServiceImpl( + val currencyLoader: CurrencyLoader, + val assignedAddressHandler: AssignedAddressHandler, + val cachedAddressHandler: CachedAddressHandler +) : AssignAddressService { + + override suspend fun assignAddress(user: String, currency: Currency): List { + val currencyInfo = currencyLoader.fetchCurrencyInfo(currency.symbol) + val chains = currencyInfo.implementations + .map { imp -> imp.chain } + val addressTypes = chains + .flatMap { chain -> chain.addressTypes } + .distinct() + val chainAddressTypeMap = HashMap>() + chains.forEach { chain -> + chain.addressTypes.forEach { addressType -> + chainAddressTypeMap.putIfAbsent(addressType, mutableListOf()) + chainAddressTypeMap.get(addressType)!!.add(chain) + } + } + val userAssignedAddresses = (assignedAddressHandler.fetchAssignedAddresses(user, addressTypes)).toMutableList() + val result = mutableSetOf() + addressTypes.forEach { addressType -> + val assigned = userAssignedAddresses.firstOrNull { assignAddress -> assignAddress.type.equals(addressType) } + if (assigned != null) { + chainAddressTypeMap.get(addressType)?.forEach { chain -> + if (!assigned.chains.contains(chain)) { + assigned.chains.add(chain) + } + } + result.add(assigned) + } else { + val cachedAddress = cachedAddressHandler.peekCachedAddress(addressType) + if (cachedAddress != null) { + val newAssigned = AssignedAddress( + user, + cachedAddress.address, + cachedAddress.memo, + addressType, + chainAddressTypeMap.get(addressType)!! + ) + cachedAddressHandler.remove(cachedAddress) + result.add(newAssigned) + } else + throw RuntimeException("No cached address available for $addressType") + + } + } + result.forEach { address -> + assignedAddressHandler.persist(address) + } + return result.toMutableList() + } +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/ChainSyncServiceImpl.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/ChainSyncServiceImpl.kt new file mode 100644 index 000000000..9bfb97219 --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/ChainSyncServiceImpl.kt @@ -0,0 +1,54 @@ +package co.nilin.opex.bcgateway.core.service + +import co.nilin.opex.bcgateway.core.api.ChainSyncService +import co.nilin.opex.bcgateway.core.spi.* +import kotlinx.coroutines.* +import org.springframework.transaction.reactive.TransactionalOperator +import org.springframework.transaction.reactive.executeAndAwait +import java.time.LocalDateTime +import java.time.temporal.ChronoUnit +import kotlin.coroutines.coroutineContext + +open class ChainSyncServiceImpl( + private val syncSchedulerHandler: SyncSchedulerHandler, + private val chainEndpointProxyFinder: ChainEndpointProxyFinder, + private val syncRecordHandler: SyncRecordHandler, + private val walletSyncRecordHandler: WalletSyncRecordHandler, + private val currencyLoader: CurrencyLoader, + private val operator: TransactionalOperator, + private val dispatcher: ExecutorCoroutineDispatcher +) : ChainSyncService { + + override suspend fun startSyncWithChain() { + withContext(coroutineContext) { + val schedules = syncSchedulerHandler.fetchActiveSchedules(currentTime()) + schedules.map { syncSchedule -> + async(dispatcher) { + val syncHandler = chainEndpointProxyFinder.findChainEndpointProxy(syncSchedule.chainName) + val lastSync = syncRecordHandler.loadLastSuccessRecord(syncSchedule.chainName) + val tokens = currencyLoader.findImplementationsWithTokenOnChain(syncSchedule.chainName) + .map { impl -> impl.tokenAddress!! } + .toList() + val syncResult = + syncHandler.syncTransfers( + ChainEndpointProxy.DepositFilter( + lastSync?.latestBlock, null, tokens + ) + ) + operator.executeAndAwait { + walletSyncRecordHandler.saveReadyToSyncTransfers(syncResult.chainName, syncResult.records) + syncRecordHandler.saveSyncRecord(syncResult) + if (syncResult.success) { + syncSchedulerHandler.prepareScheduleForNextTry( + syncSchedule, + currentTime().plus(syncSchedule.delay, ChronoUnit.SECONDS) + ) + } + } + } + } + } + } + + protected open fun currentTime() = LocalDateTime.now() +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/InfoServiceImpl.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/InfoServiceImpl.kt new file mode 100644 index 000000000..5f4d53f49 --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/InfoServiceImpl.kt @@ -0,0 +1,14 @@ +package co.nilin.opex.bcgateway.core.service + +import co.nilin.opex.bcgateway.core.api.InfoService +import co.nilin.opex.bcgateway.core.model.CurrencyInfo + +class InfoServiceImpl: InfoService { + override suspend fun countCachedAddresses(): Long { + TODO() + } + + override suspend fun getCurrencyInfo(): CurrencyInfo { + TODO() + } +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/WalletSyncServiceImpl.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/WalletSyncServiceImpl.kt new file mode 100644 index 000000000..97d80774b --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/WalletSyncServiceImpl.kt @@ -0,0 +1,42 @@ +package co.nilin.opex.bcgateway.core.service + +import co.nilin.opex.bcgateway.core.api.WalletSyncService +import co.nilin.opex.bcgateway.core.spi.* +import kotlinx.coroutines.ExecutorCoroutineDispatcher +import kotlinx.coroutines.async +import kotlinx.coroutines.withContext +import java.time.LocalDateTime +import java.time.temporal.ChronoUnit +import kotlin.coroutines.coroutineContext + +class WalletSyncServiceImpl( + private val syncSchedulerHandler: WalletSyncSchedulerHandler, + private val walletProxy: WalletProxy, + private val walletSyncRecordHandler: WalletSyncRecordHandler, + private val assignedAddressHandler: AssignedAddressHandler, + private val currencyLoader: CurrencyLoader, + private val dispatcher: ExecutorCoroutineDispatcher +) : WalletSyncService { + + override suspend fun startSyncWithWallet() { + withContext(coroutineContext) { + val schedule = syncSchedulerHandler.fetchActiveSchedule(LocalDateTime.now()) + if (schedule != null) { + val deposits = walletSyncRecordHandler.findReadyToSyncTransfers(schedule.batchSize) + deposits.map { deposit -> + async(dispatcher) { + val uuid = assignedAddressHandler.findUuid(deposit.depositor, deposit.depositorMemo) + if ( uuid != null ) { + val symbol = currencyLoader.findSymbol(deposit.chain!!, deposit.tokenAddress) + walletProxy.transfer(uuid, symbol, deposit.amount) + } + } + } + syncSchedulerHandler.prepareScheduleForNextTry( + schedule, LocalDateTime.now() + .plus(schedule.delay, ChronoUnit.SECONDS) + ) + } + } + } +} diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/AssignedAddressHandler.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/AssignedAddressHandler.kt new file mode 100644 index 000000000..1ac2cc2f8 --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/AssignedAddressHandler.kt @@ -0,0 +1,10 @@ +package co.nilin.opex.bcgateway.core.spi + +import co.nilin.opex.bcgateway.core.model.AddressType +import co.nilin.opex.bcgateway.core.model.AssignedAddress + +interface AssignedAddressHandler { + suspend fun fetchAssignedAddresses(user: String, addressTypes: List): List + suspend fun persist(assignedAddress: AssignedAddress) + suspend fun findUuid(address: String, memo: String?): String? +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/CachedAddressHandler.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/CachedAddressHandler.kt new file mode 100644 index 000000000..c4271ed2b --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/CachedAddressHandler.kt @@ -0,0 +1,10 @@ +package co.nilin.opex.bcgateway.core.spi + +import co.nilin.opex.bcgateway.core.model.AddressType +import co.nilin.opex.bcgateway.core.model.AssignedAddress +import co.nilin.opex.bcgateway.core.model.CachedAddress + +interface CachedAddressHandler { + suspend fun peekCachedAddress(addressType: AddressType): CachedAddress? + suspend fun remove(cacheAddress: CachedAddress) +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/ChainEndpointProxy.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/ChainEndpointProxy.kt new file mode 100644 index 000000000..b73d94eec --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/ChainEndpointProxy.kt @@ -0,0 +1,8 @@ +package co.nilin.opex.bcgateway.core.spi + +import co.nilin.opex.bcgateway.core.model.ChainSyncRecord + +interface ChainEndpointProxy { + data class DepositFilter(val startBlock: Long?, val endBlock: Long?, val tokenAddresses: List?) + suspend fun syncTransfers(filter: DepositFilter): ChainSyncRecord +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/ChainEndpointProxyFinder.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/ChainEndpointProxyFinder.kt new file mode 100644 index 000000000..7db1ba870 --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/ChainEndpointProxyFinder.kt @@ -0,0 +1,5 @@ +package co.nilin.opex.bcgateway.core.spi + +interface ChainEndpointProxyFinder { + suspend fun findChainEndpointProxy(chainName: String):ChainEndpointProxy +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/ChainLoader.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/ChainLoader.kt new file mode 100644 index 000000000..6834b58fa --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/ChainLoader.kt @@ -0,0 +1,8 @@ +package co.nilin.opex.bcgateway.core.spi + +import co.nilin.opex.bcgateway.core.model.Chain +import co.nilin.opex.bcgateway.core.model.CurrencyInfo + +interface ChainLoader { + suspend fun fetchChainInfo(chain: String): Chain +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/CurrencyLoader.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/CurrencyLoader.kt new file mode 100644 index 000000000..2b105b14f --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/CurrencyLoader.kt @@ -0,0 +1,10 @@ +package co.nilin.opex.bcgateway.core.spi + +import co.nilin.opex.bcgateway.core.model.CurrencyImplementation +import co.nilin.opex.bcgateway.core.model.CurrencyInfo + +interface CurrencyLoader { + suspend fun fetchCurrencyInfo(symbol: String): CurrencyInfo + suspend fun findSymbol(chain: String, address: String?): String + suspend fun findImplementationsWithTokenOnChain(chain: String): List +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/SyncRecordHandler.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/SyncRecordHandler.kt new file mode 100644 index 000000000..d7803c28a --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/SyncRecordHandler.kt @@ -0,0 +1,8 @@ +package co.nilin.opex.bcgateway.core.spi + +import co.nilin.opex.bcgateway.core.model.ChainSyncRecord + +interface SyncRecordHandler { + suspend fun loadLastSuccessRecord(chainName: String): ChainSyncRecord? + suspend fun saveSyncRecord(syncRecord: ChainSyncRecord) +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/SyncSchedulerHandler.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/SyncSchedulerHandler.kt new file mode 100644 index 000000000..be763513e --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/SyncSchedulerHandler.kt @@ -0,0 +1,9 @@ +package co.nilin.opex.bcgateway.core.spi + +import co.nilin.opex.bcgateway.core.model.ChainSyncSchedule +import java.time.LocalDateTime + +interface SyncSchedulerHandler { + suspend fun fetchActiveSchedules(time: LocalDateTime): List + suspend fun prepareScheduleForNextTry(syncSchedule: ChainSyncSchedule, time: LocalDateTime) +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/WalletProxy.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/WalletProxy.kt new file mode 100644 index 000000000..0437da445 --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/WalletProxy.kt @@ -0,0 +1,7 @@ +package co.nilin.opex.bcgateway.core.spi + +import java.math.BigDecimal + +interface WalletProxy { + fun transfer(uuid: String, symbol: String, amount: BigDecimal) +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/WalletSyncRecordHandler.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/WalletSyncRecordHandler.kt new file mode 100644 index 000000000..18437efe9 --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/WalletSyncRecordHandler.kt @@ -0,0 +1,8 @@ +package co.nilin.opex.bcgateway.core.spi + +import co.nilin.opex.bcgateway.core.model.Deposit + +interface WalletSyncRecordHandler { + suspend fun saveReadyToSyncTransfers(chainName: String, deposits: List) + suspend fun findReadyToSyncTransfers(count: Long?): List +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/WalletSyncSchedulerHandler.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/WalletSyncSchedulerHandler.kt new file mode 100644 index 000000000..637b430fe --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/WalletSyncSchedulerHandler.kt @@ -0,0 +1,9 @@ +package co.nilin.opex.bcgateway.core.spi + +import co.nilin.opex.bcgateway.core.model.WalletSyncSchedule +import java.time.LocalDateTime + +interface WalletSyncSchedulerHandler { + suspend fun fetchActiveSchedule(time: LocalDateTime): WalletSyncSchedule? + suspend fun prepareScheduleForNextTry(syncSchedule: WalletSyncSchedule, time: LocalDateTime) +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/resources/application.properties b/BlockchainGateway/bc-gateway-core/src/main/resources/application.properties new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/BlockchainGateway/bc-gateway-core/src/test/kotlin/co/nilin/opex/bcgateway/core/service/AssignAddressServiceImplUnitTest.kt b/BlockchainGateway/bc-gateway-core/src/test/kotlin/co/nilin/opex/bcgateway/core/service/AssignAddressServiceImplUnitTest.kt new file mode 100644 index 000000000..bd71823f0 --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/test/kotlin/co/nilin/opex/bcgateway/core/service/AssignAddressServiceImplUnitTest.kt @@ -0,0 +1,163 @@ +package co.nilin.opex.bcgateway.core.service + +import co.nilin.opex.bcgateway.core.model.AddressType +import co.nilin.opex.bcgateway.core.model.AssignedAddress +import co.nilin.opex.bcgateway.core.model.CachedAddress +import co.nilin.opex.bcgateway.core.model.Chain +import co.nilin.opex.bcgateway.core.model.Currency +import co.nilin.opex.bcgateway.core.model.CurrencyImplementation +import co.nilin.opex.bcgateway.core.model.CurrencyInfo +import co.nilin.opex.bcgateway.core.spi.AssignedAddressHandler +import co.nilin.opex.bcgateway.core.spi.CachedAddressHandler +import co.nilin.opex.bcgateway.core.spi.CurrencyLoader +import java.lang.RuntimeException +import java.math.BigDecimal +import java.util.UUID +import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.function.Executable +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.MockitoAnnotations + +class AssignAddressServiceImplUnitTest { + @Mock + lateinit var currencyLoader: CurrencyLoader + + @Mock + lateinit var assignedAddressHandler: AssignedAddressHandler + + @Mock + lateinit var cachedAddressHandler: CachedAddressHandler + + val assignAddressServiceImpl: AssignAddressServiceImpl + + val currency = Currency("ETH", "Ethereum") + val ethAddressType = AddressType(1, "ETH", "+*", ".*") + val ethMemoAddressType = AddressType(2, "ETH", "+*", "+*") + val ethChain = Chain("ETH_MAINNET", arrayListOf(ethAddressType), emptyList()) + val bscChain = Chain("BSC_MAINNET", arrayListOf(ethAddressType, ethMemoAddressType), emptyList()) + + + init { + MockitoAnnotations.openMocks(this) + assignAddressServiceImpl = AssignAddressServiceImpl( + currencyLoader, assignedAddressHandler, cachedAddressHandler + ) + runBlocking { + val eth = + CurrencyImplementation(currency, ethChain, false, null, null, true, BigDecimal.ONE, BigDecimal.TEN) + val wrappedEth = CurrencyImplementation( + currency, + bscChain, + false, + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "WETH", + true, + BigDecimal.ONE, + BigDecimal.ONE + ) + + Mockito.`when`(currencyLoader.fetchCurrencyInfo(currency.symbol)) + .thenReturn(CurrencyInfo(currency, listOf(eth, wrappedEth))) + } + + + } + + @Test + fun givenCachedAddressAndUserWithNoAssignedAddress_whenAssignAddress_thenCachedAddressAssigned() { + runBlocking { + val user = UUID.randomUUID().toString() + Mockito.`when`(assignedAddressHandler.fetchAssignedAddresses(user, listOf(ethAddressType, ethMemoAddressType))).thenReturn( + emptyList() + ) + Mockito.`when`(cachedAddressHandler.peekCachedAddress(ethAddressType)).thenReturn( + CachedAddress("0x1", null, ethAddressType) + ) + Mockito.`when`(cachedAddressHandler.peekCachedAddress(ethMemoAddressType)).thenReturn( + CachedAddress("0x2", "Memo", ethMemoAddressType) + ) + val assignedAddress = assignAddressServiceImpl.assignAddress(user, currency) + Assertions.assertEquals( + listOf( + AssignedAddress( + user, + "0x1", + null, + ethAddressType, + mutableListOf(ethChain, bscChain) + ), + AssignedAddress( + user, + "0x2", + "Memo", + ethMemoAddressType, + mutableListOf(bscChain) + ) + ), assignedAddress + ) + } + } + + @Test + fun givenNoCachedAddressAndUserWithNoAssignedAddress_whenAssignAddress_thenExcpetion() { + runBlocking { + val user = UUID.randomUUID().toString() + Mockito.`when`(assignedAddressHandler.fetchAssignedAddresses(user, listOf(ethAddressType, ethMemoAddressType))).thenReturn( + emptyList() + ) + Mockito.`when`(cachedAddressHandler.peekCachedAddress(ethAddressType)).thenReturn(null) + + Assertions.assertThrows(RuntimeException::class.java) { + runBlocking { + assignAddressServiceImpl.assignAddress(user, currency) + } + } + } + } + + @Test + fun givenCachedAddressAndUserOneAssignedAddress_whenAssignAddress_thenCachedAddressAssigned() { + runBlocking { + val user = UUID.randomUUID().toString() + Mockito.`when`(assignedAddressHandler.fetchAssignedAddresses(user, listOf(ethAddressType, ethMemoAddressType))).thenReturn( + mutableListOf( + AssignedAddress( user, + "0x1", + null, + ethAddressType, + mutableListOf(ethChain) + ) + ) + ) + Mockito.`when`(cachedAddressHandler.peekCachedAddress(ethAddressType)).thenReturn( + CachedAddress("0x1", null, ethAddressType) + ) + Mockito.`when`(cachedAddressHandler.peekCachedAddress(ethMemoAddressType)).thenReturn( + CachedAddress("0x2", "Memo", ethMemoAddressType) + ) + val assignedAddress = assignAddressServiceImpl.assignAddress(user, currency) + Assertions.assertEquals( + listOf( + AssignedAddress( + user, + "0x1", + null, + ethAddressType, + mutableListOf(ethChain, bscChain) + ), + AssignedAddress( + user, + "0x2", + "Memo", + ethMemoAddressType, + mutableListOf(bscChain) + ) + ), assignedAddress + ) + } + } + +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/test/kotlin/co/nilin/opex/bcgateway/core/service/ChainSyncServiceImplTest.kt b/BlockchainGateway/bc-gateway-core/src/test/kotlin/co/nilin/opex/bcgateway/core/service/ChainSyncServiceImplTest.kt new file mode 100644 index 000000000..75115b8f3 --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/test/kotlin/co/nilin/opex/bcgateway/core/service/ChainSyncServiceImplTest.kt @@ -0,0 +1,142 @@ +package co.nilin.opex.bcgateway.core.service + +import co.nilin.opex.bcgateway.core.model.ChainSyncRecord +import co.nilin.opex.bcgateway.core.model.ChainSyncSchedule +import co.nilin.opex.bcgateway.core.model.Endpoint +import co.nilin.opex.bcgateway.core.spi.ChainEndpointProxy +import co.nilin.opex.bcgateway.core.spi.ChainEndpointProxyFinder +import co.nilin.opex.bcgateway.core.spi.CurrencyLoader +import co.nilin.opex.bcgateway.core.spi.SyncRecordHandler +import co.nilin.opex.bcgateway.core.spi.SyncSchedulerHandler +import co.nilin.opex.bcgateway.core.spi.WalletSyncRecordHandler +import co.nilin.opex.bcgateway.test.OPERATOR +import java.time.LocalDateTime +import java.time.temporal.ChronoUnit +import java.util.concurrent.Executors +import kotlinx.coroutines.asCoroutineDispatcher +import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.Test +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.mock +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.verifyZeroInteractions + +internal class ChainSyncServiceImplTest { + + val ethChain = "ETH_MAINNET" + val bscChain = "BSC_MAINNET" + val time = LocalDateTime.now() + val syncService: ChainSyncServiceImpl + + @Mock + lateinit var syncSchedulerHandler: SyncSchedulerHandler + + @Mock + lateinit var chainEndpointProxyFinder: ChainEndpointProxyFinder + + @Mock + lateinit var syncRecordHandler: SyncRecordHandler + + @Mock + lateinit var walletSyncRecordHandler: WalletSyncRecordHandler + + @Mock + lateinit var currencyLoader: CurrencyLoader + + val endpointProxy: ChainEndpointProxy = mock() + + init { + MockitoAnnotations.openMocks(this) + runBlocking { + Mockito.`when`(chainEndpointProxyFinder.findChainEndpointProxy(ethChain)) + .thenReturn(endpointProxy) + Mockito.`when`(currencyLoader.findImplementationsWithTokenOnChain(ethChain)).thenReturn(emptyList()) + } + + syncService = object : ChainSyncServiceImpl( + syncSchedulerHandler, + chainEndpointProxyFinder, + syncRecordHandler, + walletSyncRecordHandler, + currencyLoader, + OPERATOR, + Executors.newFixedThreadPool(2).asCoroutineDispatcher() + ) { + override fun currentTime() = time + } + } + + @Test + fun givenNoActiveSchedules_whenStartSync_thenNoOp() { + runBlocking { + //given + Mockito.`when`(syncSchedulerHandler.fetchActiveSchedules(any())).thenReturn(emptyList()) + + //when + syncService.startSyncWithChain() + + //then + verifyZeroInteractions( + chainEndpointProxyFinder, + syncRecordHandler, + walletSyncRecordHandler, + currencyLoader + ) + } + } + + @Test + fun givenAnActiveScheduleAndChainEndpointWorking_whenStartSync_thenSyncedSuccessfully() { + runBlocking { + //given + val delay = 100L + val syncSchedule = ChainSyncSchedule(ethChain, time, delay) + Mockito.`when`(syncSchedulerHandler.fetchActiveSchedules(any())) + .thenReturn(listOf(syncSchedule)) + Mockito.`when`(endpointProxy.syncTransfers(any())).thenReturn( + ChainSyncRecord( + ethChain, LocalDateTime.now(), Endpoint(""), 100, true, null, emptyList() + ) + ) + + //when + syncService.startSyncWithChain() + + //then + verify(syncRecordHandler).saveSyncRecord(any()) + verify(walletSyncRecordHandler).saveReadyToSyncTransfers(any(), any()) + verify(syncSchedulerHandler).prepareScheduleForNextTry(syncSchedule, time.plus(delay, ChronoUnit.SECONDS)) + } + } + + @Test + fun givenAnActiveScheduleAndChainEndpointFailed_whenStartSync_thenSyncedFailed() { + runBlocking { + //given + val delay = 100L + val syncSchedule = ChainSyncSchedule(ethChain, time, delay) + Mockito.`when`(syncSchedulerHandler.fetchActiveSchedules(any())) + .thenReturn(listOf(syncSchedule)) + Mockito.`when`(endpointProxy.syncTransfers(any())).thenReturn( + ChainSyncRecord( + ethChain, LocalDateTime.now(), Endpoint(""), 100, false, "error", emptyList() + ) + ) + + //when + syncService.startSyncWithChain() + + //then + verify(syncRecordHandler).saveSyncRecord(any()) + verify(walletSyncRecordHandler).saveReadyToSyncTransfers(any(), any()) + verify(syncSchedulerHandler, times(0)).prepareScheduleForNextTry(any(), any()) + } + } + + +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/test/kotlin/co/nilin/opex/bcgateway/test/MockTxExtension.kt b/BlockchainGateway/bc-gateway-core/src/test/kotlin/co/nilin/opex/bcgateway/test/MockTxExtension.kt new file mode 100644 index 000000000..0a67d8d55 --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/test/kotlin/co/nilin/opex/bcgateway/test/MockTxExtension.kt @@ -0,0 +1,27 @@ +package co.nilin.opex.bcgateway.test + +import org.springframework.transaction.ReactiveTransaction +import org.springframework.transaction.reactive.TransactionCallback +import org.springframework.transaction.reactive.TransactionalOperator +import reactor.core.publisher.Flux +import reactor.core.publisher.Mono + + +val OPERATOR = object : TransactionalOperator { + override fun transactional(mono: Mono): Mono { + return Mono.empty() + } + + override fun execute(action: TransactionCallback): Flux { + return Flux.from(action.doInTransaction(object : ReactiveTransaction { + override fun isNewTransaction() = true + + override fun setRollbackOnly() { + } + + override fun isRollbackOnly() = false + + override fun isCompleted() = true + })) + } +} diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/.gitignore b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/.gitignore new file mode 100644 index 000000000..f4e066ca5 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/.gitignore @@ -0,0 +1,36 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ +!/.mvn/ + +.DS_Store diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/mvnw b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/mvnw new file mode 100644 index 000000000..a16b5431b --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/mvnw.cmd b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/mvnw.cmd new file mode 100644 index 000000000..c8d43372c --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/pom.xml b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/pom.xml new file mode 100644 index 000000000..2401a6b65 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/pom.xml @@ -0,0 +1,105 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.opex.external + bc-gateway-persister-postgres + 0.0.1-SNAPSHOT + bc-gateway-persister-postgres + Persist items of Opex blockchain gateway on Postgres + + + 1.8 + 1.4.31 + ${version} + ${version} + + + + + org.springframework.boot + spring-boot-starter-data-r2dbc + + + io.r2dbc + r2dbc-postgresql + runtime + + + org.postgresql + postgresql + runtime + + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + co.nilin.opex.external + bc-gateway-core + ${bc-gateway.version} + + + io.projectreactor + reactor-test + test + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/config/PostgresConfig.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/config/PostgresConfig.kt new file mode 100644 index 000000000..66357545d --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/config/PostgresConfig.kt @@ -0,0 +1,22 @@ +package co.nilin.mixchange.port.order.kafka.config + + +import org.springframework.context.annotation.Configuration +import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories +import org.springframework.r2dbc.core.DatabaseClient + + +@Configuration +@EnableR2dbcRepositories(basePackages = ["co.nilin.mixchange"]) +class PostgresConfig(db: DatabaseClient) { + + init { + val initDb = db.sql { + """ + """ + } + initDb // initialize the database + .then() + .subscribe() // execute + } +} diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AddressTypeRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AddressTypeRepository.kt new file mode 100644 index 000000000..7c1f67d0a --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AddressTypeRepository.kt @@ -0,0 +1,8 @@ +package co.nilin.opex.port.bcgateway.postgres.dao + +import co.nilin.opex.port.bcgateway.postgres.model.AddressTypeModel +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository + +@Repository +interface AddressTypeRepository: ReactiveCrudRepository \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AssignedAddressChainRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AssignedAddressChainRepository.kt new file mode 100644 index 000000000..65c973242 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AssignedAddressChainRepository.kt @@ -0,0 +1,15 @@ +package co.nilin.opex.port.bcgateway.postgres.dao + +import co.nilin.opex.port.bcgateway.postgres.model.AssignedAddressChainModel +import co.nilin.opex.port.bcgateway.postgres.model.AssignedAddressModel +import kotlinx.coroutines.flow.Flow +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository + +@Repository +interface AssignedAddressChainRepository: ReactiveCrudRepository { + @Query("select * from assigned_address_chains where assigned_address_id = :assignedAddress") + fun findByAssignedAddress(@Param("assignedAddress") type: Long): Flow +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AssignedAddressRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AssignedAddressRepository.kt new file mode 100644 index 000000000..1c0c1ba2e --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AssignedAddressRepository.kt @@ -0,0 +1,15 @@ +package co.nilin.opex.port.bcgateway.postgres.dao + +import co.nilin.opex.port.bcgateway.postgres.model.AssignedAddressModel +import kotlinx.coroutines.flow.Flow +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository + +@Repository +interface AssignedAddressRepository: ReactiveCrudRepository { + @Query("select * from assigned_addresses where uuid = :uuid and addr_type_id in (:addressTypes)") + fun findByUuidAndAddressType(@Param("uuid") uuid: String + , @Param("addressTypes") types: List): Flow +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainRepository.kt new file mode 100644 index 000000000..cafa5bbf4 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainRepository.kt @@ -0,0 +1,12 @@ +package co.nilin.opex.port.bcgateway.postgres.dao + +import co.nilin.opex.port.bcgateway.postgres.model.AssignedAddressChainModel +import co.nilin.opex.port.bcgateway.postgres.model.ChainModel +import kotlinx.coroutines.flow.Flow +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository + +interface ChainRepository: ReactiveCrudRepository { + +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/AssignedAddressHandlerImpl.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/AssignedAddressHandlerImpl.kt new file mode 100644 index 000000000..9c42d6695 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/AssignedAddressHandlerImpl.kt @@ -0,0 +1,51 @@ +package co.nilin.opex.port.bcgateway.postgres.impl + +import co.nilin.opex.bcgateway.core.model.AddressType +import co.nilin.opex.bcgateway.core.model.AssignedAddress +import co.nilin.opex.bcgateway.core.spi.AssignedAddressHandler +import co.nilin.opex.bcgateway.core.spi.ChainLoader +import co.nilin.opex.port.bcgateway.postgres.dao.AddressTypeRepository +import co.nilin.opex.port.bcgateway.postgres.dao.AssignedAddressChainRepository +import co.nilin.opex.port.bcgateway.postgres.dao.AssignedAddressRepository +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.reactive.awaitFirst +import org.springframework.stereotype.Service + +@Service +class AssignedAddressHandlerImpl( + val assignedAddressRepository: AssignedAddressRepository, + val addressTypeRepository: AddressTypeRepository, + val assignedAddressChainRepository: AssignedAddressChainRepository, + val chainLoader: ChainLoader +) : AssignedAddressHandler { + override suspend fun fetchAssignedAddresses(user: String, addressTypes: List): List { + return assignedAddressRepository.findByUuidAndAddressType( + user, addressTypes.map(AddressType::id) + ) + .map { model -> + AssignedAddress( + model.uuid, model.address, model.memo, + addressTypeRepository + .findById(model.addressTypeId) + .map { aam -> + AddressType(aam.id!!, aam.type, aam.addressRegex, aam.memoRegex) + } + .awaitFirst(), + assignedAddressChainRepository.findByAssignedAddress(model.id!!) + .map { cm -> + chainLoader.fetchChainInfo(cm.chain) + } + .toList().toMutableList() + ) + }.toList() + } + + override suspend fun persist(assignedAddress: AssignedAddress) { + TODO("Not yet implemented") + } + + override suspend fun findUuid(address: String, memo: String?): String? { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/CachedAddressHandlerImpl.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/CachedAddressHandlerImpl.kt new file mode 100644 index 000000000..52e868cfe --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/CachedAddressHandlerImpl.kt @@ -0,0 +1,15 @@ +package co.nilin.opex.port.bcgateway.postgres.impl + +import co.nilin.opex.bcgateway.core.model.AddressType +import co.nilin.opex.bcgateway.core.model.CachedAddress +import co.nilin.opex.bcgateway.core.spi.CachedAddressHandler + +class CachedAddressHandlerImpl: CachedAddressHandler { + override suspend fun peekCachedAddress(addressType: AddressType): CachedAddress? { + TODO("Not yet implemented") + } + + override suspend fun remove(cacheAddress: CachedAddress) { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/ChainHandler.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/ChainHandler.kt new file mode 100644 index 000000000..52a88cd16 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/ChainHandler.kt @@ -0,0 +1,13 @@ +package co.nilin.opex.port.bcgateway.postgres.impl + +import co.nilin.opex.bcgateway.core.model.Chain +import co.nilin.opex.bcgateway.core.spi.ChainLoader +import co.nilin.opex.port.bcgateway.postgres.dao.ChainRepository +import org.springframework.stereotype.Component + +@Component +class ChainHandler(val chainRepository: ChainRepository): ChainLoader { + override suspend fun fetchChainInfo(chain: String): Chain { + TODO("Not implemented!") + } +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/CurrencyLoaderImpl.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/CurrencyLoaderImpl.kt new file mode 100644 index 000000000..0d519f77b --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/CurrencyLoaderImpl.kt @@ -0,0 +1,19 @@ +package co.nilin.opex.port.bcgateway.postgres.impl + +import co.nilin.opex.bcgateway.core.model.CurrencyImplementation +import co.nilin.opex.bcgateway.core.model.CurrencyInfo +import co.nilin.opex.bcgateway.core.spi.CurrencyLoader + +class CurrencyLoaderImpl: CurrencyLoader { + override suspend fun fetchCurrencyInfo(symbol: String): CurrencyInfo { + TODO("Not yet implemented") + } + + override suspend fun findSymbol(chain: String, address: String?): String { + TODO("Not yet implemented") + } + + override suspend fun findImplementationsWithTokenOnChain(chain: String): List { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/SyncSchedulerHandlerImpl.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/SyncSchedulerHandlerImpl.kt new file mode 100644 index 000000000..89c6e399b --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/SyncSchedulerHandlerImpl.kt @@ -0,0 +1,17 @@ +package co.nilin.opex.port.bcgateway.postgres.impl + +import co.nilin.opex.bcgateway.core.model.ChainSyncSchedule +import co.nilin.opex.bcgateway.core.spi.SyncSchedulerHandler +import org.springframework.stereotype.Component +import java.time.LocalDateTime + +@Component +class SyncSchedulerHandlerImpl: SyncSchedulerHandler { + override suspend fun fetchActiveSchedules(time: LocalDateTime): List { + TODO("Not yet implemented") + } + + override suspend fun prepareScheduleForNextTry(syncSchedule: ChainSyncSchedule, time: LocalDateTime) { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/WalletSyncSchedulerHandlerImpl.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/WalletSyncSchedulerHandlerImpl.kt new file mode 100644 index 000000000..d4ca29623 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/WalletSyncSchedulerHandlerImpl.kt @@ -0,0 +1,17 @@ +package co.nilin.opex.port.bcgateway.postgres.impl + +import co.nilin.opex.bcgateway.core.model.WalletSyncSchedule +import co.nilin.opex.bcgateway.core.spi.WalletSyncSchedulerHandler +import org.springframework.stereotype.Component +import java.time.LocalDateTime + +@Component +class WalletSyncSchedulerHandlerImpl: WalletSyncSchedulerHandler { + override suspend fun fetchActiveSchedule(time: LocalDateTime): WalletSyncSchedule? { + TODO("Not yet implemented") + } + + override suspend fun prepareScheduleForNextTry(syncSchedule: WalletSyncSchedule, time: LocalDateTime) { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/AddressTypeModel.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/AddressTypeModel.kt new file mode 100644 index 000000000..d2e0c5aa0 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/AddressTypeModel.kt @@ -0,0 +1,12 @@ +package co.nilin.opex.port.bcgateway.postgres.model + +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table + +@Table("address_types") +data class AddressTypeModel( + val id: Long? + , @Column("address_type") val type: String + , @Column("address_regex") val addressRegex: String + , @Column("memo_regex") val memoRegex: String +) \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/AssignedAddressModel.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/AssignedAddressModel.kt new file mode 100644 index 000000000..9e3d3fd95 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/AssignedAddressModel.kt @@ -0,0 +1,21 @@ +package co.nilin.opex.port.bcgateway.postgres.model + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table + +@Table("assigned_addresses") +data class AssignedAddressModel( + val id: Long?, + val uuid: String, + val address: String, + val memo: String?, + @Column("addr_type_id") val addressTypeId: Long +) + +@Table("assigned_address_chains") +data class AssignedAddressChainModel( + @Id val id: Long?, + @Column("assigned_address_id") val addressTypeId: Long, + @Column("chain") val chain: String +) diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/CachedAddressModel.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/CachedAddressModel.kt new file mode 100644 index 000000000..8f207c68d --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/CachedAddressModel.kt @@ -0,0 +1,10 @@ +package co.nilin.opex.port.bcgateway.postgres.model + +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table + +@Table("cached_addresses") +data class CachedAddressModel(val address: String + , val memo: String? + , @Column("address_type") val type: Long +) \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/ChainModel.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/ChainModel.kt new file mode 100644 index 000000000..5204e8d15 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/ChainModel.kt @@ -0,0 +1,21 @@ +package co.nilin.opex.port.bcgateway.postgres.model + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table + +@Table("chain") +data class ChainModel(@Id val name: String) + +@Table("chain_address_types") +data class ChainAddressTypeModel(@Id val id: Long? +, @Column("chain_name") val chainName: String +, @Column("addr_type_id") val addressTypeId: Long) + +@Table("chain_endpoints") +data class ChainEndpointModel(@Id val id: Long? + , @Column("chain_name") val chainName: String + , @Column("endpoint_url") val url: String + , @Column("endpoint_user") val user: String + , @Column("endpoint_password") val password: String + ) \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/SyncModel.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/SyncModel.kt new file mode 100644 index 000000000..3a4d53ec0 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/SyncModel.kt @@ -0,0 +1,21 @@ +package co.nilin.opex.port.bcgateway.postgres.model + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.time.LocalDateTime + +@Table("chain_sync_schedule") +data class SyncScheduleModel( + @Id @Column("chain") val chain: String, @Column("retry_time") val retryTime: LocalDateTime, val delay: Long +) + +@Table("chain_sync_record") +data class SyncRecord(@Id @Column("chain") val chain: String +, val time: LocalDateTime +, @Column("endpoint_url") val endpointUrl: String +, @Column("latest_block") val latestBlock: Long? +, val success: Boolean +, val error: String?) + + diff --git a/BlockchainGateway/pom.xml b/BlockchainGateway/pom.xml new file mode 100644 index 000000000..ec4fbb8c8 --- /dev/null +++ b/BlockchainGateway/pom.xml @@ -0,0 +1,17 @@ + + + 4.0.0 + co.nilin.opex.external + bc-gateway + 0.0.1-SNAPSHOT + bc-gateway + pom + Blockchain gateway root of opex + + + bc-gateway-core + bc-gateway-app + bc-gateway-ports/bc-persister-postgres + + From 34e26e7199b4d18c7df12585c0e56e38692ed195 Mon Sep 17 00:00:00 2001 From: Peyman Date: Sun, 15 Aug 2021 13:25:47 +0430 Subject: [PATCH 20/59] close #20: Configured CORS for opex.dev and localhost:3000 close #21: Fix /api/api/* endpoints to /api/* --- .../opex/port/api/binance/config/SecurityConfig.kt | 1 + .../api/binance/controller/AccountController.kt | 13 +++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt index 72a22d040..be0365f6f 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt @@ -20,6 +20,7 @@ class SecurityConfig { @Bean fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain? { http.csrf().disable() + .cors().and() .authorizeExchange() .pathMatchers("/hello").permitAll() .pathMatchers("/actuator/**").permitAll() diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt index 09119c0de..3658c829a 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt @@ -24,6 +24,7 @@ import java.security.Principal import java.util.* @RestController +@CrossOrigin(origins = ["https://opex.dev", "http://localhost:3000"], allowedHeaders = ["*"]) class AccountController( val queryHandler: UserQueryHandler, val matchingGatewayProxy: MEGatewayProxy, @@ -102,7 +103,7 @@ class AccountController( Data Source: Matching Engine */ @PostMapping( - "/api/v3/order", + "/v3/order", consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE] ) @@ -191,7 +192,7 @@ class AccountController( Data Source: Database */ @GetMapping( - "/api/v3/order", + "/v3/order", consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE] ) @@ -253,7 +254,7 @@ class AccountController( Data Source: Database */ @GetMapping( - "/api/v3/openOrders", + "/v3/openOrders", consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE] ) @@ -308,7 +309,7 @@ class AccountController( Data Source: Database */ @GetMapping( - "/api/v3/allOrders", + "/v3/allOrders", consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE] ) @@ -371,7 +372,7 @@ class AccountController( Data Source: Database */ @GetMapping( - "/api/v3/myTrades", + "/v3/myTrades", consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE] ) @@ -417,7 +418,7 @@ class AccountController( } @GetMapping( - "/api/v3/account", + "/v3/account", consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE] ) From 7e954044d318db9c0ad2d51d0c84d780becae5ac Mon Sep 17 00:00:00 2001 From: Peyman Date: Mon, 16 Aug 2021 14:39:37 +0430 Subject: [PATCH 21/59] Fix cors 401 error concerning issue #20 --- .../co/nilin/opex/port/api/binance/config/SecurityConfig.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt index be0365f6f..833e17938 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt @@ -4,6 +4,7 @@ import co.nilin.opex.port.api.binance.security.AuthenticationConverter import org.springframework.context.annotation.Bean import org.springframework.core.io.ClassPathResource import org.springframework.core.io.Resource +import org.springframework.http.HttpMethod import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity import org.springframework.security.config.web.server.ServerHttpSecurity import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder @@ -27,6 +28,7 @@ class SecurityConfig { .pathMatchers("/swagger-ui/**").permitAll() .pathMatchers("/swagger-resources/**").permitAll() .pathMatchers("/v2/api-docs").permitAll() + .pathMatchers(HttpMethod.OPTIONS, "/**").permitAll() .pathMatchers("/**").hasAuthority("SCOPE_trust") .anyExchange().authenticated() .and() From 33b9125ef65075df4b7c84abc9a4bc2da539c3ee Mon Sep 17 00:00:00 2001 From: Peyman Date: Tue, 17 Aug 2021 12:05:38 +0430 Subject: [PATCH 22/59] close #22: Fixed ask-bid conversion mismatch --- .../co/nilin/opex/port/api/binance/util/EnumExtensions.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/util/EnumExtensions.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/util/EnumExtensions.kt index 68c8a0ba7..6ae29084e 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/util/EnumExtensions.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/util/EnumExtensions.kt @@ -8,8 +8,8 @@ import co.nilin.opex.matching.core.model.OrderDirection fun OrderSide.asOrderDirection(): OrderDirection { if (this == OrderSide.BUY) - return OrderDirection.ASK - return OrderDirection.BID + return OrderDirection.BID + return OrderDirection.ASK } fun TimeInForce.asMatchConstraint(): MatchConstraint { From 4e29389cd875a83e0c277a8c37679566e4f7f664 Mon Sep 17 00:00:00 2001 From: metalicn20 Date: Tue, 17 Aug 2021 12:47:00 +0430 Subject: [PATCH 23/59] Close #14, Extract swagger config (#24) * Extract swagger config from the application class * Fix API application class * Fix swagger class definition --- .../main/kotlin/co/nilin/opex/app/ApiApp.kt | 80 +-------------- .../co/nilin/opex/app/config/SwaggerConfig.kt | 97 ++++++++++++++++++ .../co/nilin/opex/app/MatchingGatewayApp.kt | 82 +-------------- .../co/nilin/opex/app/config/SwaggerConfig.kt | 98 ++++++++++++++++++ .../co/nilin/opex/wallet/app/WalletApp.kt | 84 +--------------- .../opex/wallet/app/config/SwaggerConfig.kt | 99 +++++++++++++++++++ 6 files changed, 301 insertions(+), 239 deletions(-) create mode 100644 Api/api-app/src/main/kotlin/co/nilin/opex/app/config/SwaggerConfig.kt create mode 100644 MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/config/SwaggerConfig.kt create mode 100644 Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/SwaggerConfig.kt diff --git a/Api/api-app/src/main/kotlin/co/nilin/opex/app/ApiApp.kt b/Api/api-app/src/main/kotlin/co/nilin/opex/app/ApiApp.kt index a6c4103a4..338e2bab1 100644 --- a/Api/api-app/src/main/kotlin/co/nilin/opex/app/ApiApp.kt +++ b/Api/api-app/src/main/kotlin/co/nilin/opex/app/ApiApp.kt @@ -22,85 +22,7 @@ import java.util.Collections.singletonList @SpringBootApplication @ComponentScan("co.nilin.opex") @EnableSwagger2 -class ApiApp { - @Value("\${swagger.authUrl}") - val authUrl: String = "" - - @Bean - fun opexApi(): Docket? { - return Docket(DocumentationType.SWAGGER_2) - .groupName("opex-api") - .apiInfo(apiInfo()) - .select() - .paths(regex("^/api/v3.*")) - .build() - .globalRequestParameters( - singletonList( - RequestParameterBuilder() - .name("content-type") - .description("content-type") - .`in`(ParameterType.HEADER) - .required(true) - .build() - ) - ) - .ignoredParameterTypes(AuthenticationPrincipal::class.java, Principal::class.java) - .useDefaultResponseMessages(false) - .securitySchemes(singletonList(oauth())) - .securityContexts(singletonList(securityContext())) - } - - private fun apiInfo(): ApiInfo? { - return ApiInfoBuilder() - .title("OPEX API") - .description("Backend for opex exchange.") - .license("MIT License") - .licenseUrl("https://github.com/opexdev/Back-end/blob/feature/1-MVP/LICENSE") - .version("0.1") - .build() - } - - @Bean - fun oauth(): SecurityScheme? { - return OAuthBuilder() - .name("opex") - .grantTypes(grantTypes()) - .scopes(scopes()) - .build() - } - - fun scopes(): List? { - return emptyList() - } - - fun grantTypes(): List? { - val tokenUrl = "${authUrl}/auth/realms/mixchange/protocol/openid-connect/token" - val grantType = ResourceOwnerPasswordCredentialsGrant(tokenUrl) - return singletonList(grantType) - } - - @Bean - fun securityContext(): SecurityContext? { - val securityReference = SecurityReference.builder() - .reference("opex") - .scopes(emptyArray()) - .build() - return SecurityContext.builder() - .securityReferences(singletonList(securityReference)) - .operationSelector { true } - .build() - } - - @Bean - fun securityInfo(): SecurityConfiguration? { - return SecurityConfigurationBuilder.builder() - .clientId("admin-cli") - .realm("mixchange") - .appName("opex") - .scopeSeparator(",") - .build() - } -} +class ApiApp fun main(args: Array) { runApplication(*args) diff --git a/Api/api-app/src/main/kotlin/co/nilin/opex/app/config/SwaggerConfig.kt b/Api/api-app/src/main/kotlin/co/nilin/opex/app/config/SwaggerConfig.kt new file mode 100644 index 000000000..c9fe3d51b --- /dev/null +++ b/Api/api-app/src/main/kotlin/co/nilin/opex/app/config/SwaggerConfig.kt @@ -0,0 +1,97 @@ +package co.nilin.opex.app.config + +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.security.core.annotation.AuthenticationPrincipal +import springfox.documentation.builders.ApiInfoBuilder +import springfox.documentation.builders.OAuthBuilder +import springfox.documentation.builders.PathSelectors +import springfox.documentation.builders.RequestParameterBuilder +import springfox.documentation.service.* +import springfox.documentation.spi.DocumentationType +import springfox.documentation.spi.service.contexts.SecurityContext +import springfox.documentation.spring.web.plugins.Docket +import springfox.documentation.swagger.web.SecurityConfiguration +import springfox.documentation.swagger.web.SecurityConfigurationBuilder +import java.security.Principal +import java.util.* + +@Configuration +class SwaggerConfig { + @Value("\${swagger.authUrl}") + val authUrl: String = "" + + @Bean + fun opexApi(): Docket { + return Docket(DocumentationType.SWAGGER_2) + .groupName("opex-api") + .apiInfo(apiInfo()) + .select() + .paths(PathSelectors.regex("^/api/v3.*")) + .build() + .globalRequestParameters( + Collections.singletonList( + RequestParameterBuilder() + .name("content-type") + .description("content-type") + .`in`(ParameterType.HEADER) + .required(true) + .build() + ) + ) + .ignoredParameterTypes(AuthenticationPrincipal::class.java, Principal::class.java) + .useDefaultResponseMessages(false) + .securitySchemes(Collections.singletonList(oauth())) + .securityContexts(Collections.singletonList(securityContext())) + } + + private fun apiInfo(): ApiInfo { + return ApiInfoBuilder() + .title("OPEX API") + .description("Backend for opex exchange.") + .license("MIT License") + .licenseUrl("https://github.com/opexdev/Back-end/blob/feature/1-MVP/LICENSE") + .version("0.1") + .build() + } + + private fun oauth(): SecurityScheme { + return OAuthBuilder() + .name("opex") + .grantTypes(grantTypes()) + .scopes(scopes()) + .build() + } + + private fun scopes(): List { + return listOf(AuthorizationScope("openid", "OpenId")) + } + + private fun grantTypes(): List { + val tokenUrl = "$authUrl/auth/realms/opex/protocol/openid-connect/token" + val grantType = ResourceOwnerPasswordCredentialsGrant(tokenUrl) + return Collections.singletonList(grantType) + } + + private fun securityContext(): SecurityContext { + val securityReference = SecurityReference.builder() + .reference("opex") + .scopes(emptyArray()) + .build() + return SecurityContext.builder() + .securityReferences(Collections.singletonList(securityReference)) + .operationSelector { true } + .build() + } + + @Bean + fun securityInfo(): SecurityConfiguration { + return SecurityConfigurationBuilder.builder() + .clientId("admin-cli") + .realm("opex") + .appName("opex") + .scopeSeparator(",") + .build() + } +} \ No newline at end of file diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/MatchingGatewayApp.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/MatchingGatewayApp.kt index 5b1db6af4..5a141ca72 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/MatchingGatewayApp.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/MatchingGatewayApp.kt @@ -16,90 +16,14 @@ import springfox.documentation.spi.service.contexts.SecurityContext import springfox.documentation.spring.web.plugins.Docket import springfox.documentation.swagger.web.SecurityConfiguration import springfox.documentation.swagger.web.SecurityConfigurationBuilder +import springfox.documentation.swagger2.annotations.EnableSwagger2 import java.security.Principal import java.util.Collections.singletonList @SpringBootApplication @ComponentScan("co.nilin.opex") -class MatchingGatewayApp { - @Value("\${swagger.authUrl}") - val authUrl: String = "" - - @Bean - fun opexMatchingGateway(): Docket? { - return Docket(DocumentationType.SWAGGER_2) - .groupName("opex-matching-gateway") - .apiInfo(apiInfo()) - .select() - .paths(regex("^/actuator.*").negate()) - .build() - .globalRequestParameters( - singletonList( - RequestParameterBuilder() - .name("content-type") - .description("content-type") - .`in`(ParameterType.HEADER) - .required(true) - .build() - ) - ) - .ignoredParameterTypes(AuthenticationPrincipal::class.java, Principal::class.java) - .useDefaultResponseMessages(false) - .securitySchemes(singletonList(oauth())) - .securityContexts(singletonList(securityContext())) - } - - private fun apiInfo(): ApiInfo? { - return ApiInfoBuilder() - .title("OPEX API") - .description("Backend for opex exchange.") - .license("MIT License") - .licenseUrl("https://github.com/opexdev/Back-end/blob/feature/1-MVP/LICENSE") - .version("0.1") - .build() - } - - @Bean - fun oauth(): SecurityScheme? { - return OAuthBuilder() - .name("opex") - .grantTypes(grantTypes()) - .scopes(scopes()) - .build() - } - - fun scopes(): List? { - return emptyList() - } - - fun grantTypes(): List? { - val tokenUrl = "${authUrl}/auth/realms/mixchange/protocol/openid-connect/token" - val grantType = ResourceOwnerPasswordCredentialsGrant(tokenUrl) - return singletonList(grantType) - } - - @Bean - fun securityContext(): SecurityContext? { - val securityReference = SecurityReference.builder() - .reference("opex") - .scopes(emptyArray()) - .build() - return SecurityContext.builder() - .securityReferences(singletonList(securityReference)) - .operationSelector { true } - .build() - } - - @Bean - fun securityInfo(): SecurityConfiguration? { - return SecurityConfigurationBuilder.builder() - .clientId("admin-cli") - .realm("mixchange") - .appName("opex") - .scopeSeparator(",") - .build() - } -} +@EnableSwagger2 +class MatchingGatewayApp fun main(args: Array) { runApplication(*args) diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/config/SwaggerConfig.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/config/SwaggerConfig.kt new file mode 100644 index 000000000..37abadf5e --- /dev/null +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/config/SwaggerConfig.kt @@ -0,0 +1,98 @@ +package co.nilin.opex.app.config + +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.security.core.annotation.AuthenticationPrincipal +import springfox.documentation.builders.ApiInfoBuilder +import springfox.documentation.builders.OAuthBuilder +import springfox.documentation.builders.PathSelectors +import springfox.documentation.builders.RequestParameterBuilder +import springfox.documentation.service.* +import springfox.documentation.spi.DocumentationType +import springfox.documentation.spi.service.contexts.SecurityContext +import springfox.documentation.spring.web.plugins.Docket +import springfox.documentation.swagger.web.SecurityConfiguration +import springfox.documentation.swagger.web.SecurityConfigurationBuilder +import java.security.Principal +import java.util.* + + +@Configuration +class SwaggerConfig { + @Value("\${swagger.authUrl}") + val authUrl: String = "" + + @Bean + fun opexMatchingGateway(): Docket { + return Docket(DocumentationType.SWAGGER_2) + .groupName("opex-matching-gateway") + .apiInfo(apiInfo()) + .select() + .paths(PathSelectors.regex("^/actuator.*").negate()) + .build() + .globalRequestParameters( + Collections.singletonList( + RequestParameterBuilder() + .name("content-type") + .description("content-type") + .`in`(ParameterType.HEADER) + .required(true) + .build() + ) + ) + .ignoredParameterTypes(AuthenticationPrincipal::class.java, Principal::class.java) + .useDefaultResponseMessages(false) + .securitySchemes(Collections.singletonList(oauth())) + .securityContexts(Collections.singletonList(securityContext())) + } + + private fun apiInfo(): ApiInfo { + return ApiInfoBuilder() + .title("OPEX API") + .description("Backend for opex exchange.") + .license("MIT License") + .licenseUrl("https://github.com/opexdev/Back-end/blob/feature/1-MVP/LICENSE") + .version("0.1") + .build() + } + + private fun oauth(): SecurityScheme { + return OAuthBuilder() + .name("opex") + .grantTypes(grantTypes()) + .scopes(scopes()) + .build() + } + + private fun scopes(): List { + return listOf(AuthorizationScope("openid", "OpenId")) + } + + private fun grantTypes(): List { + val tokenUrl = "$authUrl/auth/realms/opex/protocol/openid-connect/token" + val grantType = ResourceOwnerPasswordCredentialsGrant(tokenUrl) + return Collections.singletonList(grantType) + } + + private fun securityContext(): SecurityContext { + val securityReference = SecurityReference.builder() + .reference("opex") + .scopes(emptyArray()) + .build() + return SecurityContext.builder() + .securityReferences(Collections.singletonList(securityReference)) + .operationSelector { true } + .build() + } + + @Bean + fun securityInfo(): SecurityConfiguration { + return SecurityConfigurationBuilder.builder() + .clientId("admin-cli") + .realm("opex") + .appName("opex") + .scopeSeparator(",") + .build() + } +} \ No newline at end of file diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/WalletApp.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/WalletApp.kt index c512b722d..58904b28a 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/WalletApp.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/WalletApp.kt @@ -16,92 +16,14 @@ import springfox.documentation.spi.service.contexts.SecurityContext import springfox.documentation.spring.web.plugins.Docket import springfox.documentation.swagger.web.SecurityConfiguration import springfox.documentation.swagger.web.SecurityConfigurationBuilder +import springfox.documentation.swagger2.annotations.EnableSwagger2 import java.security.Principal import java.util.Collections.singletonList @SpringBootApplication @ComponentScan("co.nilin.opex") -class WalletApp { - @Value("\${swagger.authUrl}") - val authUrl: String = "" - - @Bean - fun opexWallet(): Docket? { - return Docket(DocumentationType.SWAGGER_2) - .groupName("opex-wallet") - .apiInfo(apiInfo()) - .select() - .paths(regex("^/actuator.*").negate()) - .build() - .globalRequestParameters( - singletonList( - RequestParameterBuilder() - .name("content-type") - .description("content-type") - .`in`(ParameterType.HEADER) - .required(true) - .build() - ) - ) - .ignoredParameterTypes(AuthenticationPrincipal::class.java, Principal::class.java) - .useDefaultResponseMessages(false) - .securitySchemes(singletonList(oauth())) - .securityContexts(singletonList(securityContext())) - } - - private fun apiInfo(): ApiInfo? { - return ApiInfoBuilder() - .title("OPEX API") - .description("Backend for opex exchange.") - .license("MIT License") - .licenseUrl("https://github.com/opexdev/Back-end/blob/feature/1-MVP/LICENSE") - .version("0.1") - .build() - } - - @Bean - fun oauth(): SecurityScheme? { - return OAuthBuilder() - .name("opex") - .grantTypes(grantTypes()) - .scopes(scopes()) - .build() - } - - fun scopes(): List? { - return emptyList() - } - - fun grantTypes(): List? { - val tokenUrl = "${authUrl}/auth/realms/mixchange/protocol/openid-connect/token" - val grantType = ResourceOwnerPasswordCredentialsGrant(tokenUrl) - return singletonList(grantType) - } - - @Bean - fun securityContext(): SecurityContext? { - val securityReference = SecurityReference.builder() - .reference("opex") - .scopes(emptyArray()) - .build() - return SecurityContext.builder() - .securityReferences(singletonList(securityReference)) - .operationSelector { - it.requestMappingPattern().matches(Regex("^/(balanceOf|owner)/.*")) - } - .build() - } - - @Bean - fun securityInfo(): SecurityConfiguration? { - return SecurityConfigurationBuilder.builder() - .clientId("admin-cli") - .realm("mixchange") - .appName("opex") - .scopeSeparator(",") - .build() - } -} +@EnableSwagger2 +class WalletApp fun main(args: Array) { runApplication(*args) diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/SwaggerConfig.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/SwaggerConfig.kt new file mode 100644 index 000000000..76b80d954 --- /dev/null +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/SwaggerConfig.kt @@ -0,0 +1,99 @@ +package co.nilin.opex.wallet.app.config + +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.security.core.annotation.AuthenticationPrincipal +import springfox.documentation.builders.ApiInfoBuilder +import springfox.documentation.builders.OAuthBuilder +import springfox.documentation.builders.PathSelectors +import springfox.documentation.builders.RequestParameterBuilder +import springfox.documentation.service.* +import springfox.documentation.spi.DocumentationType +import springfox.documentation.spi.service.contexts.SecurityContext +import springfox.documentation.spring.web.plugins.Docket +import springfox.documentation.swagger.web.SecurityConfiguration +import springfox.documentation.swagger.web.SecurityConfigurationBuilder +import java.security.Principal +import java.util.* + +@Configuration +class SwaggerConfig { + @Value("\${swagger.authUrl}") + val authUrl: String = "" + + @Bean + fun opexWallet(): Docket { + return Docket(DocumentationType.SWAGGER_2) + .groupName("opex-wallet") + .apiInfo(apiInfo()) + .select() + .paths(PathSelectors.regex("^/actuator.*").negate()) + .build() + .globalRequestParameters( + Collections.singletonList( + RequestParameterBuilder() + .name("content-type") + .description("content-type") + .`in`(ParameterType.HEADER) + .required(true) + .build() + ) + ) + .ignoredParameterTypes(AuthenticationPrincipal::class.java, Principal::class.java) + .useDefaultResponseMessages(false) + .securitySchemes(Collections.singletonList(oauth())) + .securityContexts(Collections.singletonList(securityContext())) + } + + private fun apiInfo(): ApiInfo { + return ApiInfoBuilder() + .title("OPEX API") + .description("Backend for opex exchange.") + .license("MIT License") + .licenseUrl("https://github.com/opexdev/Back-end/blob/feature/1-MVP/LICENSE") + .version("0.1") + .build() + } + + private fun oauth(): SecurityScheme { + return OAuthBuilder() + .name("opex") + .grantTypes(grantTypes()) + .scopes(scopes()) + .build() + } + + private fun scopes(): List { + return listOf(AuthorizationScope("openid", "OpenId")) + } + + private fun grantTypes(): List { + val tokenUrl = "$authUrl/auth/realms/opex/protocol/openid-connect/token" + val grantType = ResourceOwnerPasswordCredentialsGrant(tokenUrl) + return Collections.singletonList(grantType) + } + + private fun securityContext(): SecurityContext { + val securityReference = SecurityReference.builder() + .reference("opex") + .scopes(emptyArray()) + .build() + return SecurityContext.builder() + .securityReferences(Collections.singletonList(securityReference)) + .operationSelector { + it.requestMappingPattern().matches(Regex("^/(balanceOf|owner)/.*")) + } + .build() + } + + @Bean + fun securityInfo(): SecurityConfiguration { + return SecurityConfigurationBuilder.builder() + .clientId("admin-cli") + .realm("opex") + .appName("opex") + .scopeSeparator(",") + .build() + } +} \ No newline at end of file From 0285f6545b5660f21d0e3a5f5210b2febebe8d3b Mon Sep 17 00:00:00 2001 From: Peyman Date: Sat, 21 Aug 2021 14:34:46 +0430 Subject: [PATCH 24/59] close #17: Add error handling mechanism. More errors will be handled in future commits --- .../kotlin/co/nilin/opex/app/AccountantApp.kt | 2 + .../accountant-persister-postgres/pom.xml | 5 + .../postgres/impl/PairConfigLoaderImpl.kt | 19 +- .../wallet/proxy/WalletProxyImpl.kt | 18 +- Api/api-core/pom.xml | 5 + .../api/binance/config/ErrorHandlerConfig.kt | 10 + .../binance/controller/AccountController.kt | 6 +- .../api/binance/proxy/MEGatewayProxyImpl.kt | 2 +- .../port/api/binance/proxy/WalletProxyImpl.kt | 4 +- .../api/postgres/impl/UserQueryHandlerImpl.kt | 4 +- .../port/api/postgres/util/EnumExtensions.kt | 2 +- MatchingGateway/gateway-app/pom.xml | 5 + .../co/nilin/opex/app/MatchingGatewayApp.kt | 17 +- .../controller/ControllerExceptionHandler.kt | 2 +- .../opex/app/proxy/AccountantProxyImpl.kt | 8 +- .../co/nilin/opex/app/service/OrderService.kt | 5 +- Utility/.gitignore | 3 + Utility/error-handler/.gitignore | 33 ++ Utility/error-handler/pom.xml | 87 ++++ .../co/nilin/opex/utility/error/Config.kt | 24 + .../utility/error/DefaultErrorTranslator.kt | 22 + .../utility/error/EnableOpexErrorHandler.kt | 10 + .../error/controller/ExceptionController.kt | 92 ++++ .../error/data/DefaultExceptionResponse.kt | 19 + .../opex/utility/error/data/OpexError.kt | 50 +++ .../opex/utility/error/data/OpexException.kt | 11 + .../opex/utility/error/spi/ErrorTranslator.kt | 9 + .../utility/error/spi/ExceptionResponse.kt | 6 + .../src/main/resources/application.yml | 0 Utility/pom.xml | 16 + Utility/utility-root.ipr | 108 +++++ Utility/utility-root.iws | 418 ++++++++++++++++++ Wallet/wallet-app/pom.xml | 5 + .../co/nilin/opex/wallet/app/WalletApp.kt | 2 + .../app/controller/WalletOwnerController.kt | 19 +- 35 files changed, 995 insertions(+), 53 deletions(-) create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/ErrorHandlerConfig.kt create mode 100644 Utility/.gitignore create mode 100644 Utility/error-handler/.gitignore create mode 100644 Utility/error-handler/pom.xml create mode 100644 Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/Config.kt create mode 100644 Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/DefaultErrorTranslator.kt create mode 100644 Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/EnableOpexErrorHandler.kt create mode 100644 Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/controller/ExceptionController.kt create mode 100644 Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/DefaultExceptionResponse.kt create mode 100644 Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/OpexError.kt create mode 100644 Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/OpexException.kt create mode 100644 Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/spi/ErrorTranslator.kt create mode 100644 Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/spi/ExceptionResponse.kt create mode 100644 Utility/error-handler/src/main/resources/application.yml create mode 100644 Utility/pom.xml create mode 100644 Utility/utility-root.ipr create mode 100644 Utility/utility-root.iws diff --git a/Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/AccountantApp.kt b/Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/AccountantApp.kt index 9c797d8e3..8b5c89f25 100644 --- a/Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/AccountantApp.kt +++ b/Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/AccountantApp.kt @@ -1,11 +1,13 @@ package co.nilin.opex.app +import co.nilin.opex.utility.error.EnableOpexErrorHandler import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.annotation.ComponentScan @SpringBootApplication @ComponentScan("co.nilin.opex") +@EnableOpexErrorHandler class AccountantApp fun main(args: Array) { diff --git a/Accountant/accountant-ports/accountant-persister-postgres/pom.xml b/Accountant/accountant-ports/accountant-persister-postgres/pom.xml index 753b6a478..03b0e336a 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/pom.xml +++ b/Accountant/accountant-ports/accountant-persister-postgres/pom.xml @@ -74,6 +74,11 @@ com.google.code.gson gson + + co.nilin.opex + error-handler + 1.0-SNAPSHOT + io.projectreactor reactor-test diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/PairConfigLoaderImpl.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/PairConfigLoaderImpl.kt index 467a379ed..c600b0267 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/PairConfigLoaderImpl.kt +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/PairConfigLoaderImpl.kt @@ -7,11 +7,11 @@ import co.nilin.opex.matching.core.model.OrderDirection import co.nilin.opex.port.accountant.postgres.dao.PairConfigRepository import co.nilin.opex.port.accountant.postgres.dao.PairFeeConfigRepository import co.nilin.opex.port.accountant.postgres.model.PairFeeConfigModel +import co.nilin.opex.utility.error.data.OpexError +import co.nilin.opex.utility.error.data.OpexException import kotlinx.coroutines.reactive.awaitFirstOrElse import kotlinx.coroutines.reactive.awaitFirstOrNull import org.springframework.stereotype.Component -import org.springframework.util.StringUtils -import java.lang.IllegalArgumentException @Component class PairConfigLoaderImpl( @@ -20,12 +20,18 @@ class PairConfigLoaderImpl( override suspend fun load(pair: String, direction: OrderDirection, userLevel: String): PairFeeConfig { val pairConfig = pairConfigRepository - .findById(pair).awaitFirstOrElse { throw IllegalArgumentException("$pair is not available") } + .findById(pair).awaitFirstOrElse { + val error = OpexError.InvalidPair + throw OpexException(error, String.format(error.message!!, pair)) + } var pairFeeConfig: PairFeeConfigModel? if (userLevel.isEmpty()) { pairFeeConfig = pairFeeConfigRepository .findByPairAndDirectionAndUserLevel(pair, direction, "*") - .awaitFirstOrElse { throw IllegalArgumentException("$pair fee is not available") } + .awaitFirstOrElse { + val error = OpexError.InvalidPair + throw OpexException(error, String.format(error.message!!, pair)) + } } else { pairFeeConfig = pairFeeConfigRepository .findByPairAndDirectionAndUserLevel(pair, direction, userLevel) @@ -33,7 +39,10 @@ class PairConfigLoaderImpl( if (pairFeeConfig == null) { pairFeeConfig = pairFeeConfigRepository .findByPairAndDirectionAndUserLevel(pair, direction, "*") - .awaitFirstOrElse { throw IllegalArgumentException("$pair fee is not available") } + .awaitFirstOrElse { + val error = OpexError.InvalidPairFee + throw OpexException(error, String.format(error.message!!, pair)) + } } } diff --git a/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/opex/port/accountant/wallet/proxy/WalletProxyImpl.kt b/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/opex/port/accountant/wallet/proxy/WalletProxyImpl.kt index 69b068736..65fe00059 100644 --- a/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/opex/port/accountant/wallet/proxy/WalletProxyImpl.kt +++ b/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/opex/port/accountant/wallet/proxy/WalletProxyImpl.kt @@ -11,14 +11,20 @@ import java.net.URI import java.time.LocalDateTime inline fun typeRef(): ParameterizedTypeReference = object : ParameterizedTypeReference() {} -data class TransferResult(val date: LocalDateTime, val sourceBalanceBeforeAction: Amount, val sourceBalanceAfterAction: Amount, val amount: Amount) +data class TransferResult( + val date: LocalDateTime, + val sourceBalanceBeforeAction: Amount, + val sourceBalanceAfterAction: Amount, + val amount: Amount +) + data class Amount(val currency: Currency, val amount: BigDecimal) data class Currency(val name: String, val symbol: String, val precision: Int) @Component -class WalletProxyImpl(@Value("\${app.wallet.url}") val walletBaseUrl: String -, val webClient: WebClient -): WalletProxy { +class WalletProxyImpl( + @Value("\${app.wallet.url}") val walletBaseUrl: String, val webClient: WebClient +) : WalletProxy { override suspend fun transfer( symbol: String, senderWalletType: String, @@ -52,9 +58,7 @@ class WalletProxyImpl(@Value("\${app.wallet.url}") val walletBaseUrl: String .uri(URI.create("$walletBaseUrl/$uuid/wallet_type/${walletType}/can_withdraw/${amount}_${symbol}")) .header("Content-Type", "application/json") .retrieve() - .onStatus({ t -> t.isError }, { p -> - throw RuntimeException() - }) + .onStatus({ t -> t.isError }, { it.createException() }) .bodyToMono(typeRef()) .log() .awaitFirst() diff --git a/Api/api-core/pom.xml b/Api/api-core/pom.xml index fd067d84a..40ff51b14 100644 --- a/Api/api-core/pom.xml +++ b/Api/api-core/pom.xml @@ -57,6 +57,11 @@ ${accountant.version} provided + + co.nilin.opex + error-handler + 1.0-SNAPSHOT + org.springframework.boot diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/ErrorHandlerConfig.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/ErrorHandlerConfig.kt new file mode 100644 index 000000000..f500e712b --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/ErrorHandlerConfig.kt @@ -0,0 +1,10 @@ +package co.nilin.opex.port.api.binance.config + +import co.nilin.opex.utility.error.EnableOpexErrorHandler +import org.springframework.context.annotation.Configuration + +@Configuration +@EnableOpexErrorHandler +class ErrorHandlerConfig { + +} \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt index 3658c829a..0ea5e6b1a 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt @@ -11,6 +11,8 @@ import co.nilin.opex.port.api.binance.util.asMatchConstraint import co.nilin.opex.port.api.binance.util.asMatchingOrderType import co.nilin.opex.port.api.binance.util.asOrderDirection import co.nilin.opex.api.core.inout.* +import co.nilin.opex.utility.error.data.OpexError +import co.nilin.opex.utility.error.data.OpexException import com.fasterxml.jackson.annotation.JsonInclude import io.swagger.annotations.* import kotlinx.coroutines.flow.Flow @@ -221,8 +223,8 @@ class AccountController( timestamp: Long ): QueryOrderResponse { val response = queryHandler.queryOrder(principal, QueryOrderRequest(symbol, orderId, origClientOrderId)) - if (response == null) - throw IllegalArgumentException("no order found") + ?: throw OpexException(OpexError.OrderNotFound) + return QueryOrderResponse( response.symbol, response.orderId, diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/proxy/MEGatewayProxyImpl.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/proxy/MEGatewayProxyImpl.kt index 6117db3f1..ff67200ff 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/proxy/MEGatewayProxyImpl.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/proxy/MEGatewayProxyImpl.kt @@ -33,7 +33,7 @@ class MEGatewayProxyImpl(private val client: WebClient) : MEGatewayProxy { .header("Authorization", "Bearer $token") .body(Mono.just(order)) .retrieve() - .onStatus({ t -> t.isError }, { throw RuntimeException() }) + .onStatus({ t -> t.isError }, { it.createException() }) .bodyToMono(typeRef()) .awaitSingleOrNull() } diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/proxy/WalletProxyImpl.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/proxy/WalletProxyImpl.kt index df0b037dc..b5aedc3be 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/proxy/WalletProxyImpl.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/proxy/WalletProxyImpl.kt @@ -32,7 +32,7 @@ class WalletProxyImpl(private val webClient: WebClient) : WalletProxy { .accept(MediaType.APPLICATION_JSON) .header(HttpHeaders.AUTHORIZATION, "Bearer $token") .retrieve() - .onStatus({ t -> t.isError }, { throw RuntimeException() }) + .onStatus({ t -> t.isError }, { it.createException() }) .bodyToFlux(typeRef()) .collectList() .awaitSingle() @@ -45,7 +45,7 @@ class WalletProxyImpl(private val webClient: WebClient) : WalletProxy { .accept(MediaType.APPLICATION_JSON) .header(HttpHeaders.AUTHORIZATION, "Bearer $token") .retrieve() - .onStatus({ t -> t.isError }, { throw RuntimeException() }) + .onStatus({ t -> t.isError }, { it.createException() }) .bodyToMono(typeRef()) .awaitSingle() } diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt index 636de382f..4dfafad98 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt @@ -8,6 +8,8 @@ import co.nilin.opex.port.api.postgres.dao.TradeRepository import co.nilin.opex.port.api.postgres.model.OrderModel import co.nilin.opex.port.api.postgres.util.* import co.nilin.opex.api.core.inout.* +import co.nilin.opex.utility.error.data.OpexError +import co.nilin.opex.utility.error.data.OpexException import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map @@ -34,7 +36,7 @@ class UserQueryHandlerImpl( }).awaitFirstOrNull() if (order?.constraint != null) { if (order.uuid != principal.name) - throw RuntimeException("Forbidden") + throw OpexException(OpexError.Forbidden) return orderToQueryResponse(order) } return null diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/util/EnumExtensions.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/util/EnumExtensions.kt index 9e1520e4d..fce203911 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/util/EnumExtensions.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/util/EnumExtensions.kt @@ -29,7 +29,7 @@ fun OrderType.toApiOrderType(): co.nilin.opex.api.core.inout.OrderType { } fun OrderDirection.toOrderSide(): OrderSide { - if (this == OrderDirection.ASK) + if (this == OrderDirection.BID) return OrderSide.BUY return OrderSide.SELL } diff --git a/MatchingGateway/gateway-app/pom.xml b/MatchingGateway/gateway-app/pom.xml index d4efe0ee5..170c311f0 100644 --- a/MatchingGateway/gateway-app/pom.xml +++ b/MatchingGateway/gateway-app/pom.xml @@ -89,6 +89,11 @@ springfox-boot-starter 3.0.0 + + co.nilin.opex + error-handler + 1.0-SNAPSHOT + diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/MatchingGatewayApp.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/MatchingGatewayApp.kt index 5a141ca72..208a63169 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/MatchingGatewayApp.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/MatchingGatewayApp.kt @@ -1,27 +1,14 @@ package co.nilin.opex.app -import org.springframework.beans.factory.annotation.Value +import co.nilin.opex.utility.error.EnableOpexErrorHandler import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication -import org.springframework.context.annotation.Bean import org.springframework.context.annotation.ComponentScan -import org.springframework.security.core.annotation.AuthenticationPrincipal -import springfox.documentation.builders.ApiInfoBuilder -import springfox.documentation.builders.OAuthBuilder -import springfox.documentation.builders.PathSelectors.regex -import springfox.documentation.builders.RequestParameterBuilder -import springfox.documentation.service.* -import springfox.documentation.spi.DocumentationType -import springfox.documentation.spi.service.contexts.SecurityContext -import springfox.documentation.spring.web.plugins.Docket -import springfox.documentation.swagger.web.SecurityConfiguration -import springfox.documentation.swagger.web.SecurityConfigurationBuilder import springfox.documentation.swagger2.annotations.EnableSwagger2 -import java.security.Principal -import java.util.Collections.singletonList @SpringBootApplication @ComponentScan("co.nilin.opex") +@EnableOpexErrorHandler @EnableSwagger2 class MatchingGatewayApp diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/controller/ControllerExceptionHandler.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/controller/ControllerExceptionHandler.kt index 37b0f744d..f4766e4e6 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/controller/ControllerExceptionHandler.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/controller/ControllerExceptionHandler.kt @@ -11,7 +11,7 @@ import org.springframework.web.reactive.function.client.WebClientResponseExcepti import java.nio.charset.StandardCharsets import java.util.* -@RestControllerAdvice +//@RestControllerAdvice class ControllerExceptionHandler { data class ErrorResponse( diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/proxy/AccountantProxyImpl.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/proxy/AccountantProxyImpl.kt index a1c4bfcd7..621551558 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/proxy/AccountantProxyImpl.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/proxy/AccountantProxyImpl.kt @@ -23,9 +23,7 @@ class AccountantProxyImpl( .uri(URI.create("$accountantBaseUrl/$uuid/create_order/${value}_${symbol}/allowed")) .header("Content-Type", "application/json") .retrieve() - .onStatus({ t -> t.isError }, { p -> - throw RuntimeException() - }) + .onStatus({ t -> t.isError }, { it.createException()}) .bodyToMono(typeRef()) .log() .awaitFirst() @@ -45,9 +43,7 @@ class AccountantProxyImpl( ) .header("Content-Type", "application/json") .retrieve() - .onStatus({ t -> t.isError }, { p -> - throw RuntimeException() - }) + .onStatus({ t -> t.isError }, { it.createException()}) .bodyToMono(typeRef()) .log() .awaitFirst() diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/service/OrderService.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/service/OrderService.kt index 530d2cd8f..6fc179484 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/service/OrderService.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/service/OrderService.kt @@ -1,6 +1,5 @@ package co.nilin.opex.app.service -import co.nilin.opex.app.exception.NotAllowedToSubmitOrderException import co.nilin.opex.app.inout.CreateOrderRequest import co.nilin.opex.app.spi.AccountantApiProxy import co.nilin.opex.app.spi.PairConfigLoader @@ -9,6 +8,8 @@ import co.nilin.opex.matching.core.model.Pair import co.nilin.opex.port.order.kafka.inout.OrderSubmitRequest import co.nilin.opex.port.order.kafka.inout.OrderSubmitResult import co.nilin.opex.port.order.kafka.service.OrderSubmitter +import co.nilin.opex.utility.error.data.OpexError +import co.nilin.opex.utility.error.data.throwError import org.springframework.stereotype.Service import java.util.* @@ -33,7 +34,7 @@ class OrderService( createOrderRequest.quantity.multiply(createOrderRequest.price) ) ) { - throw NotAllowedToSubmitOrderException() + throwError(OpexError.SubmitOrderForbiddenByAccountant) } val pairFeeConfig = pairConfigLoader.load( createOrderRequest.pair, createOrderRequest.direction, "" diff --git a/Utility/.gitignore b/Utility/.gitignore new file mode 100644 index 000000000..d6953c444 --- /dev/null +++ b/Utility/.gitignore @@ -0,0 +1,3 @@ +*.iml +.idea +/.idea/ diff --git a/Utility/error-handler/.gitignore b/Utility/error-handler/.gitignore new file mode 100644 index 000000000..549e00a2a --- /dev/null +++ b/Utility/error-handler/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/Utility/error-handler/pom.xml b/Utility/error-handler/pom.xml new file mode 100644 index 000000000..7004e656e --- /dev/null +++ b/Utility/error-handler/pom.xml @@ -0,0 +1,87 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.opex + error-handler + 1.0-SNAPSHOT + error-handler + REST error handler + + + 1.8 + 1.4.31 + + + + + org.springframework.boot + spring-boot-starter-webflux + + + com.fasterxml.jackson.module + jackson-module-kotlin + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + + org.springframework.boot + spring-boot-starter-test + test + + + + io.projectreactor + reactor-test + test + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + diff --git a/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/Config.kt b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/Config.kt new file mode 100644 index 000000000..2ae3ebccb --- /dev/null +++ b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/Config.kt @@ -0,0 +1,24 @@ +package co.nilin.opex.utility.error + +import co.nilin.opex.utility.error.spi.ErrorTranslator +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.registerKotlinModule +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +class Config { + + @Bean + @ConditionalOnMissingBean + fun translator(): ErrorTranslator { + return DefaultErrorTranslator() + } + + @Bean + fun mapper(): ObjectMapper { + return ObjectMapper().registerKotlinModule() + } + +} \ No newline at end of file diff --git a/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/DefaultErrorTranslator.kt b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/DefaultErrorTranslator.kt new file mode 100644 index 000000000..f42f30c6f --- /dev/null +++ b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/DefaultErrorTranslator.kt @@ -0,0 +1,22 @@ +package co.nilin.opex.utility.error + +import co.nilin.opex.utility.error.data.DefaultExceptionResponse +import co.nilin.opex.utility.error.data.OpexException +import co.nilin.opex.utility.error.spi.ErrorTranslator +import co.nilin.opex.utility.error.spi.ExceptionResponse +import org.springframework.stereotype.Component + +@Component +class DefaultErrorTranslator : ErrorTranslator { + + override fun translate(ex: OpexException): ExceptionResponse { + return DefaultExceptionResponse( + ex.error.name, + ex.error.code, + ex.message ?: ex.error.message, + ex.status ?: ex.error.status, + ex.data, + ex.crimeScene + ) + } +} \ No newline at end of file diff --git a/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/EnableOpexErrorHandler.kt b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/EnableOpexErrorHandler.kt new file mode 100644 index 000000000..cee46b0d2 --- /dev/null +++ b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/EnableOpexErrorHandler.kt @@ -0,0 +1,10 @@ +package co.nilin.opex.utility.error + +import co.nilin.opex.utility.error.controller.ExceptionController +import org.springframework.context.annotation.Import + +@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +@MustBeDocumented +@Import(ExceptionController::class) +annotation class EnableOpexErrorHandler \ No newline at end of file diff --git a/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/controller/ExceptionController.kt b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/controller/ExceptionController.kt new file mode 100644 index 000000000..0e91b3367 --- /dev/null +++ b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/controller/ExceptionController.kt @@ -0,0 +1,92 @@ +package co.nilin.opex.utility.error.controller + +import co.nilin.opex.utility.error.data.DefaultExceptionResponse +import co.nilin.opex.utility.error.data.OpexError +import co.nilin.opex.utility.error.data.OpexException +import co.nilin.opex.utility.error.spi.ErrorTranslator +import co.nilin.opex.utility.error.spi.ExceptionResponse +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.databind.ObjectMapper +import org.slf4j.LoggerFactory +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.ExceptionHandler +import org.springframework.web.bind.annotation.RestControllerAdvice +import org.springframework.web.reactive.function.client.WebClientResponseException +import org.springframework.web.server.ServerWebInputException +import java.nio.charset.StandardCharsets +import java.util.* + +@RestControllerAdvice +class ExceptionController( + private val mapper: ObjectMapper, + private val translator: ErrorTranslator +) { + + @JsonIgnoreProperties(ignoreUnknown = true) + data class WebClientErrorResponse( + val timestamp: Date?, + val path: String?, + val status: Int?, + val error: String?, + val message: String?, + val code: Int? + ) + + private val logger = LoggerFactory.getLogger(ExceptionController::class.java) + + @ExceptionHandler(OpexException::class) + fun handle(e: OpexException): ResponseEntity { + val error = translator.translate(e) + if (error is DefaultExceptionResponse) + logger.error("Opex error happened at ${e.crimeScene?.name}", e) + else + logger.error("Opex error", e) + return response(error) + } + + @ExceptionHandler(WebClientResponseException::class) + fun handle(e: WebClientResponseException): ResponseEntity { + logger.error("Webclient error", e) + return try { + val body = mapper.readValue( + e.responseBodyAsByteArray.toString(StandardCharsets.UTF_8), + WebClientErrorResponse::class.java + ) + + val opexError = OpexError.findByCode(body.code) + val er = translator.translate(OpexException(opexError ?: OpexError.InternalServerError)) + response(er) + } catch (ex: Exception) { + val opEx = OpexException(OpexError.InternalServerError) + val er = translator.translate(opEx) + response(er) + } + } + + @ExceptionHandler(ServerWebInputException::class) + fun handleMissingServletRequestParameter(ex: ServerWebInputException): ResponseEntity { + logger.error("Web input error", ex) + val name = ex.methodParameter?.parameterName + + val error = OpexError.InvalidRequestParam + val er = translator.translate( + OpexException( + error, + String.format(error.message!!, name) + ) + ) + return response(er) + } + + @ExceptionHandler(Throwable::class) + fun handle(e: Throwable): ResponseEntity { + logger.error("Generic error", e) + val opexException = OpexException(OpexError.InternalServerError) + val error = translator.translate(opexException) + return response(error) + } + + private fun response(er: ExceptionResponse): ResponseEntity = + ResponseEntity.status(er.status).body(er) + +} \ No newline at end of file diff --git a/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/DefaultExceptionResponse.kt b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/DefaultExceptionResponse.kt new file mode 100644 index 000000000..13eba9b3d --- /dev/null +++ b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/DefaultExceptionResponse.kt @@ -0,0 +1,19 @@ +package co.nilin.opex.utility.error.data + +import co.nilin.opex.utility.error.spi.ExceptionResponse +import com.fasterxml.jackson.annotation.JsonIgnore +import com.fasterxml.jackson.annotation.JsonInclude +import org.springframework.http.HttpStatus +import java.util.* + +@JsonInclude(JsonInclude.Include.NON_NULL) +class DefaultExceptionResponse( + val error: String?, + val code: Int, + val message: String?, + status: HttpStatus, + val data: Any? = null, + @JsonIgnore + val crimeScene: Class<*>?, + val timestamp: Date = Date() +) : ExceptionResponse(status) \ No newline at end of file diff --git a/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/OpexError.kt b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/OpexError.kt new file mode 100644 index 000000000..498f29d2f --- /dev/null +++ b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/OpexError.kt @@ -0,0 +1,50 @@ +package co.nilin.opex.utility.error.data + +import org.springframework.http.HttpStatus + +enum class OpexError(val code: Int, val message: String?, val status: HttpStatus) { + + // Code 1000: general + Error(1000, "Generic error", HttpStatus.INTERNAL_SERVER_ERROR), + InternalServerError(1001, "Internal server error", HttpStatus.INTERNAL_SERVER_ERROR), + BadRequest(1002, "Bad request", HttpStatus.BAD_REQUEST), + UnAuthorized(1003, "Unauthorized", HttpStatus.UNAUTHORIZED), + Forbidden(1004, "Forbidden", HttpStatus.FORBIDDEN), + NotFound(1005, "Not found", HttpStatus.NOT_FOUND), + InvalidRequestParam(1020, "Parameter '%s' is either missing or invalid", HttpStatus.BAD_REQUEST), + + // code 2000: accountant + InvalidPair(2001, "%s is not available", HttpStatus.BAD_REQUEST), + InvalidPairFee(2002, "%s fee is not available", HttpStatus.BAD_REQUEST), + + // code 3000: matching-engine + + // code 4000: matching-gateway + SubmitOrderForbiddenByAccountant(4001, null, HttpStatus.BAD_REQUEST), + + // code 5000: user-management + + // code 6000: wallet + WalletOwnerNotFound(6001, null, HttpStatus.NOT_FOUND), + + // code 7000: api + OrderNotFound(7001, "No order found", HttpStatus.NOT_FOUND); + + companion object { + fun findByCode(code: Int?): OpexError? { + code ?: return null + return values().find { it.code == code } + } + } + +} + +@Throws(OpexException::class) +inline fun T.throwError( + error: OpexError, + message: String? = null, + status: HttpStatus? = null, + data: Any? = null +) { + throw OpexException(error, message, status, data, T::class.java) +} \ No newline at end of file diff --git a/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/OpexException.kt b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/OpexException.kt new file mode 100644 index 000000000..187a18d29 --- /dev/null +++ b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/OpexException.kt @@ -0,0 +1,11 @@ +package co.nilin.opex.utility.error.data + +import org.springframework.http.HttpStatus + +class OpexException( + val error: OpexError, + message: String? = null, + val status: HttpStatus? = null, + val data: Any? = null, + val crimeScene: Class<*>? = null +) : RuntimeException(message ?: error.message) \ No newline at end of file diff --git a/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/spi/ErrorTranslator.kt b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/spi/ErrorTranslator.kt new file mode 100644 index 000000000..3b596a578 --- /dev/null +++ b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/spi/ErrorTranslator.kt @@ -0,0 +1,9 @@ +package co.nilin.opex.utility.error.spi + +import co.nilin.opex.utility.error.data.OpexException + +interface ErrorTranslator { + + fun translate(ex: OpexException): ExceptionResponse + +} \ No newline at end of file diff --git a/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/spi/ExceptionResponse.kt b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/spi/ExceptionResponse.kt new file mode 100644 index 000000000..92ed722f7 --- /dev/null +++ b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/spi/ExceptionResponse.kt @@ -0,0 +1,6 @@ +package co.nilin.opex.utility.error.spi + +import com.fasterxml.jackson.annotation.JsonIgnore +import org.springframework.http.HttpStatus + +abstract class ExceptionResponse(@JsonIgnore val status: HttpStatus) \ No newline at end of file diff --git a/Utility/error-handler/src/main/resources/application.yml b/Utility/error-handler/src/main/resources/application.yml new file mode 100644 index 000000000..e69de29bb diff --git a/Utility/pom.xml b/Utility/pom.xml new file mode 100644 index 000000000..ae7327669 --- /dev/null +++ b/Utility/pom.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + co.nilin.opex + utility-root + 1.0-SNAPSHOT + utility-root + pom + Utility root of Opex + + + error-handler + + \ No newline at end of file diff --git a/Utility/utility-root.ipr b/Utility/utility-root.ipr new file mode 100644 index 000000000..eae68a1a1 --- /dev/null +++ b/Utility/utility-root.ipr @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Utility/utility-root.iws b/Utility/utility-root.iws new file mode 100644 index 000000000..03c854e98 --- /dev/null +++ b/Utility/utility-root.iws @@ -0,0 +1,418 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wallet/wallet-app/pom.xml b/Wallet/wallet-app/pom.xml index b1f90db12..d75d4ee24 100644 --- a/Wallet/wallet-app/pom.xml +++ b/Wallet/wallet-app/pom.xml @@ -112,6 +112,11 @@ springfox-boot-starter 3.0.0 + + co.nilin.opex + error-handler + 1.0-SNAPSHOT + diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/WalletApp.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/WalletApp.kt index 58904b28a..f0207afdd 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/WalletApp.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/WalletApp.kt @@ -1,5 +1,6 @@ package co.nilin.opex.wallet.app +import co.nilin.opex.utility.error.EnableOpexErrorHandler import org.springframework.beans.factory.annotation.Value import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication @@ -23,6 +24,7 @@ import java.util.Collections.singletonList @SpringBootApplication @ComponentScan("co.nilin.opex") @EnableSwagger2 +@EnableOpexErrorHandler class WalletApp fun main(args: Array) { diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/controller/WalletOwnerController.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/controller/WalletOwnerController.kt index 239ca052e..29d84605e 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/controller/WalletOwnerController.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/controller/WalletOwnerController.kt @@ -1,5 +1,8 @@ package co.nilin.opex.wallet.app.controller +import co.nilin.opex.utility.error.data.OpexError +import co.nilin.opex.utility.error.data.OpexException +import co.nilin.opex.utility.error.data.throwError import co.nilin.opex.wallet.core.spi.WalletManager import co.nilin.opex.wallet.core.spi.WalletOwnerManager import io.swagger.annotations.ApiResponse @@ -41,13 +44,11 @@ class WalletOwnerController( ) suspend fun getAllWallets(principal: Principal): List { val owner = walletOwnerManager.findWalletOwner(principal.name) - if (owner != null) { - val wallets = walletManager.findWalletsByOwner(owner) - return wallets.map { - WalletData(it.currency().getSymbol(), it.balance().amount, it.type()) - } + ?: throw OpexException(OpexError.WalletOwnerNotFound) + val wallets = walletManager.findWalletsByOwner(owner) + return wallets.map { + WalletData(it.currency().getSymbol(), it.balance().amount, it.type()) } - return arrayListOf() } @GetMapping("/owner/limits") @@ -63,9 +64,7 @@ class WalletOwnerController( ) suspend fun getWalletOwnerLimits(principal: Principal): OwnerLimitsResponse { val owner = walletOwnerManager.findWalletOwner(principal.name) - return if (owner != null) - OwnerLimitsResponse(owner.isTradeAllowed(), owner.isWithdrawAllowed(), owner.isDepositAllowed()) - else - OwnerLimitsResponse(canTrade = false, canWithdraw = false, canDeposit = false) + ?: throw OpexException(OpexError.WalletOwnerNotFound) + return OwnerLimitsResponse(owner.isTradeAllowed(), owner.isWithdrawAllowed(), owner.isDepositAllowed()) } } \ No newline at end of file From 6df58bcd6fdbe9ddfd816a2823be898a891ac137 Mon Sep 17 00:00:00 2001 From: metalicn20 Date: Sun, 22 Aug 2021 15:25:55 +0430 Subject: [PATCH 25/59] Close #16, Add symbol mapper to map binance schema to ours (#30) * Add pair map database scripts * Implement repositories * Fix table names * Fix naming issue in insert commands * Add not null to map column * Add symbol adapter * Fix SQL syntax errors * Fix return symbol in create order * Improve name conventions * Fix symbol map repository function name --- .../nilin/opex/api/core/spi/SymbolMapper.kt | 6 ++++ .../binance/controller/AccountController.kt | 30 +++++++++++-------- .../api/postgres/config/PostgresConfig.kt | 11 +++++-- .../api/postgres/dao/SymbolMapRepository.kt | 18 +++++++++++ .../api/postgres/impl/SymbolMapperImpl.kt | 19 ++++++++++++ .../port/api/postgres/model/SymbolMapModel.kt | 12 ++++++++ 6 files changed, 81 insertions(+), 15 deletions(-) create mode 100644 Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/SymbolMapper.kt create mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/SymbolMapRepository.kt create mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/SymbolMapperImpl.kt create mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/SymbolMapModel.kt diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/SymbolMapper.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/SymbolMapper.kt new file mode 100644 index 000000000..e1117590f --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/SymbolMapper.kt @@ -0,0 +1,6 @@ +package co.nilin.opex.api.core.spi + +interface SymbolMapper { + suspend fun map(symbol: String?): String? + suspend fun unmap(value: String?): String? +} \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt index 0ea5e6b1a..bcc7f623e 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt @@ -10,9 +10,9 @@ import co.nilin.opex.port.api.binance.util.BalanceParser import co.nilin.opex.port.api.binance.util.asMatchConstraint import co.nilin.opex.port.api.binance.util.asMatchingOrderType import co.nilin.opex.port.api.binance.util.asOrderDirection -import co.nilin.opex.api.core.inout.* import co.nilin.opex.utility.error.data.OpexError import co.nilin.opex.utility.error.data.OpexException +import co.nilin.opex.api.core.spi.SymbolMapper import com.fasterxml.jackson.annotation.JsonInclude import io.swagger.annotations.* import kotlinx.coroutines.flow.Flow @@ -30,7 +30,8 @@ import java.util.* class AccountController( val queryHandler: UserQueryHandler, val matchingGatewayProxy: MEGatewayProxy, - val walletProxy: WalletProxy + val walletProxy: WalletProxy, + val symbolMapper: SymbolMapper ) { data class FillsData( @@ -158,16 +159,16 @@ class AccountController( timestamp: Long, @AuthenticationPrincipal auth: CustomAuthToken ): NewOrderResponse { + val internalSymbol = symbolMapper.unmap(symbol)!! val request = MEGatewayProxy.CreateOrderRequest( auth.uuid, - symbol, + internalSymbol, price ?: BigDecimal.ZERO, // Maybe make this nullable as well? quantity ?: BigDecimal.ZERO, side.asOrderDirection(), timeInForce?.asMatchConstraint(), type.asMatchingOrderType() ) - matchingGatewayProxy.createNewOrder(request, auth.tokenValue) return NewOrderResponse( symbol, @@ -222,11 +223,11 @@ class AccountController( @RequestParam(name = "timestamp") timestamp: Long ): QueryOrderResponse { - val response = queryHandler.queryOrder(principal, QueryOrderRequest(symbol, orderId, origClientOrderId)) + val internalSymbol = symbolMapper.unmap(symbol)!! + val response = queryHandler.queryOrder(principal, QueryOrderRequest(internalSymbol, orderId, origClientOrderId)) ?: throw OpexException(OpexError.OrderNotFound) - return QueryOrderResponse( - response.symbol, + symbolMapper.map(response.symbol)!!, response.orderId, response.orderListId, response.clientOrderId, @@ -280,10 +281,11 @@ class AccountController( @RequestParam(name = "timestamp") timestamp: Long ): Flow { - return queryHandler.openOrders(principal, symbol) + val internalSymbol = symbolMapper.unmap(symbol) + return queryHandler.openOrders(principal, internalSymbol) .map { response -> QueryOrderResponse( - response.symbol, + symbolMapper.map(response.symbol)!!, response.orderId, response.orderListId, response.clientOrderId, @@ -342,10 +344,11 @@ class AccountController( @RequestParam(name = "timestamp") timestamp: Long ): Flow { - return queryHandler.allOrders(principal, AllOrderRequest(symbol, startTime, endTime, limit)) + val internalSymbol = symbolMapper.unmap(symbol) + return queryHandler.allOrders(principal, AllOrderRequest(internalSymbol, startTime, endTime, limit)) .map { response -> QueryOrderResponse( - response.symbol, + symbolMapper.map(response.symbol)!!, response.orderId, response.orderListId, response.clientOrderId, @@ -408,10 +411,11 @@ class AccountController( @RequestParam(name = "timestamp") timestamp: Long ): Flow { - return queryHandler.allTrades(principal, TradeRequest(symbol, fromId, startTime, endTime, limit)) + val internalSymbol = symbolMapper.unmap(symbol) + return queryHandler.allTrades(principal, TradeRequest(internalSymbol, fromId, startTime, endTime, limit)) .map { response -> TradeResponse( - response.symbol, response.id, + symbolMapper.map(response.symbol)!!, response.id, response.orderId, -1, response.price, response.qty, response.quoteQty, response.commission, response.commissionAsset, response.time, response.isBuyer, response.isMaker, response.isBestMatch diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/config/PostgresConfig.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/config/PostgresConfig.kt index bc72c1ad7..c88b8f921 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/config/PostgresConfig.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/config/PostgresConfig.kt @@ -53,10 +53,17 @@ class PostgresConfig(db: DatabaseClient) { taker_uuid VARCHAR(72) NOT NULL, create_date TIMESTAMP ); + CREATE TABLE IF NOT EXISTS symbol_maps ( + symbol VARCHAR(72) PRIMARY KEY, + value VARCHAR(72) UNIQUE NOT NULL + ); + INSERT INTO symbol_maps(symbol, value) VALUES('btc_usdt', 'BTCUSDT') ON CONFLICT DO NOTHING; + INSERT INTO symbol_maps(symbol, value) VALUES('eth_usdt', 'ETHUSDT') ON CONFLICT DO NOTHING; + INSERT INTO symbol_maps(symbol, value) VALUES('eth_btc', 'ETHBTC') ON CONFLICT DO NOTHING; """ val initDb = db.sql { sql } initDb // initialize the database - .then() - .subscribe() // execute + .then() + .subscribe() // execute } } diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/SymbolMapRepository.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/SymbolMapRepository.kt new file mode 100644 index 000000000..8c377a94f --- /dev/null +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/SymbolMapRepository.kt @@ -0,0 +1,18 @@ +package co.nilin.opex.port.api.postgres.dao + +import co.nilin.opex.port.api.postgres.model.SymbolMapModel +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono + +@Repository +interface SymbolMapRepository : ReactiveCrudRepository { + + @Query("select * from symbol_maps where symbol = :symbol") + fun findBySymbol(@Param("symbol") symbol: String): Mono + + @Query("select * from symbol_maps where value = :value") + fun findByValue(@Param("value") value: String): Mono +} \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/SymbolMapperImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/SymbolMapperImpl.kt new file mode 100644 index 000000000..e252214a4 --- /dev/null +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/SymbolMapperImpl.kt @@ -0,0 +1,19 @@ +package co.nilin.opex.port.api.postgres.impl + +import co.nilin.opex.api.core.spi.SymbolMapper +import co.nilin.opex.port.api.postgres.dao.SymbolMapRepository +import kotlinx.coroutines.reactive.awaitFirstOrNull +import org.springframework.stereotype.Component + +@Component +class SymbolMapperImpl(val symbolMapRepository: SymbolMapRepository) : SymbolMapper { + override suspend fun map(symbol: String?): String? { + if (symbol == null) return null + return symbolMapRepository.findBySymbol(symbol).awaitFirstOrNull()?.value + } + + override suspend fun unmap(value: String?): String? { + if (value == null) return null + return symbolMapRepository.findByValue(value).awaitFirstOrNull()?.symbol + } +} \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/SymbolMapModel.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/SymbolMapModel.kt new file mode 100644 index 000000000..d43ee4e63 --- /dev/null +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/SymbolMapModel.kt @@ -0,0 +1,12 @@ +package co.nilin.opex.port.api.postgres.model + + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table + +@Table("symbol_maps") +class SymbolMapModel( + @Id val symbol: String, + @Column("value") val value: String, +) \ No newline at end of file From ab30cf75adb395aa74cd65d2969c1160991e47ac Mon Sep 17 00:00:00 2001 From: Peyman Date: Mon, 23 Aug 2021 17:29:19 +0430 Subject: [PATCH 26/59] close #31: Added consul close #32: JWT is dynamically decoded now --- .../src/main/resources/application-docker.yml | 2 ++ .../port/api/binance/config/SecurityConfig.kt | 26 +++++++------------ .../bcgateway/app/config/SecurityConfig.kt | 19 +++++++------- .../bcgateway/app/config/WebClientConfig.kt | 25 ++++++++++++++++++ .../src/main/resources/application-docker.yml | 3 +++ .../nilin/opex/app/config/SecurityConfig.kt | 26 +++++++------------ .../src/main/resources/application-docker.yml | 6 +++-- UserManagement/keycloak-gateway/pom.xml | 18 +++++++++++++ .../src/main/resources/application-docker.yml | 2 ++ .../opex/wallet/app/config/SecurityConfig.kt | 26 +++++++------------ .../opex/wallet/app/config/WebClientConfig.kt | 25 ++++++++++++++++++ .../src/main/resources/application-docker.yml | 3 +++ 12 files changed, 122 insertions(+), 59 deletions(-) create mode 100644 BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/WebClientConfig.kt create mode 100644 Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/WebClientConfig.kt diff --git a/Api/api-app/src/main/resources/application-docker.yml b/Api/api-app/src/main/resources/application-docker.yml index 8ac0e8eaf..0b62d8ebb 100644 --- a/Api/api-app/src/main/resources/application-docker.yml +++ b/Api/api-app/src/main/resources/application-docker.yml @@ -17,3 +17,5 @@ app: url: lb://opex-gateway wallet: url: lb://opex-wallet + auth: + cert-url: lb://opex-auth/auth/realms/opex/protocol/openid-connect/certs diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt index 833e17938..3710cdc90 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt @@ -1,23 +1,22 @@ package co.nilin.opex.port.api.binance.config import co.nilin.opex.port.api.binance.security.AuthenticationConverter +import org.springframework.beans.factory.annotation.Value import org.springframework.context.annotation.Bean -import org.springframework.core.io.ClassPathResource -import org.springframework.core.io.Resource import org.springframework.http.HttpMethod import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity import org.springframework.security.config.web.server.ServerHttpSecurity import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder import org.springframework.security.web.server.SecurityWebFilterChain -import org.springframework.util.Base64Utils -import org.springframework.util.FileCopyUtils -import java.security.KeyFactory -import java.security.interfaces.RSAPublicKey -import java.security.spec.X509EncodedKeySpec +import org.springframework.web.reactive.function.client.WebClient @EnableWebFluxSecurity -class SecurityConfig { +class SecurityConfig(private val webClient: WebClient) { + + @Value("\${app.auth.cert-url}") + private lateinit var jwkUrl: String + @Bean fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain? { http.csrf().disable() @@ -41,13 +40,8 @@ class SecurityConfig { @Bean @Throws(Exception::class) fun reactiveJwtDecoder(): ReactiveJwtDecoder? { - val resource: Resource = ClassPathResource("/public.cert") - val publicKey = String(FileCopyUtils.copyToByteArray(resource.inputStream)) - .replace("\r", "") - .replace("-----BEGIN PUBLIC KEY-----\n", "") - .replace("\n-----END PUBLIC KEY-----", "") - val spec = X509EncodedKeySpec(Base64Utils.decodeFromString(publicKey)) - val kf = KeyFactory.getInstance("RSA") - return NimbusReactiveJwtDecoder(kf.generatePublic(spec) as RSAPublicKey) + return NimbusReactiveJwtDecoder.withJwkSetUri(jwkUrl) + .webClient(webClient) + .build() } } diff --git a/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/SecurityConfig.kt b/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/SecurityConfig.kt index 061ec977f..dfd54722a 100644 --- a/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/SecurityConfig.kt +++ b/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/SecurityConfig.kt @@ -1,5 +1,6 @@ package co.nilin.opex.bcgateway.app.config +import org.springframework.beans.factory.annotation.Value import org.springframework.context.annotation.Bean import org.springframework.core.io.ClassPathResource import org.springframework.core.io.Resource @@ -10,12 +11,17 @@ import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder import org.springframework.security.web.server.SecurityWebFilterChain import org.springframework.util.Base64Utils import org.springframework.util.FileCopyUtils +import org.springframework.web.reactive.function.client.WebClient import java.security.KeyFactory import java.security.interfaces.RSAPublicKey import java.security.spec.X509EncodedKeySpec @EnableWebFluxSecurity -class SecurityConfig { +class SecurityConfig(private val webClient: WebClient) { + + @Value("\${app.auth.cert-url}") + private lateinit var jwkUrl: String + @Bean fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain? { http.csrf().disable() @@ -32,13 +38,8 @@ class SecurityConfig { @Bean @Throws(Exception::class) fun reactiveJwtDecoder(): ReactiveJwtDecoder? { - val resource: Resource = ClassPathResource("/public.cert") - val publicKey = String(FileCopyUtils.copyToByteArray(resource.inputStream)) - .replace("\r", "") - .replace("-----BEGIN PUBLIC KEY-----\n", "") - .replace("\n-----END PUBLIC KEY-----", "") - val spec = X509EncodedKeySpec(Base64Utils.decodeFromString(publicKey)) - val kf = KeyFactory.getInstance("RSA") - return NimbusReactiveJwtDecoder(kf.generatePublic(spec) as RSAPublicKey) + return NimbusReactiveJwtDecoder.withJwkSetUri(jwkUrl) + .webClient(webClient) + .build() } } diff --git a/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/WebClientConfig.kt b/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/WebClientConfig.kt new file mode 100644 index 000000000..934bbd706 --- /dev/null +++ b/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/WebClientConfig.kt @@ -0,0 +1,25 @@ +package co.nilin.opex.bcgateway.app.config + +import org.springframework.cloud.client.ServiceInstance +import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties +import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer +import org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerExchangeFilterFunction +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.web.reactive.function.client.WebClient + +@Configuration +class WebClientConfig { + + @Bean + fun webClient(loadBalancerFactory: ReactiveLoadBalancer.Factory): WebClient { + return WebClient.builder() + .filter( + ReactorLoadBalancerExchangeFilterFunction( + loadBalancerFactory, LoadBalancerProperties(), emptyList() + ) + ) + .build() + } + +} diff --git a/BlockchainGateway/bc-gateway-app/src/main/resources/application-docker.yml b/BlockchainGateway/bc-gateway-app/src/main/resources/application-docker.yml index 6db331761..37996be60 100644 --- a/BlockchainGateway/bc-gateway-app/src/main/resources/application-docker.yml +++ b/BlockchainGateway/bc-gateway-app/src/main/resources/application-docker.yml @@ -17,3 +17,6 @@ spring: host: ${CONSUL_HOST} port: 8500 +app: + auth: + cert-url: lb://opex-auth/auth/realms/opex/protocol/openid-connect/certs \ No newline at end of file diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/config/SecurityConfig.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/config/SecurityConfig.kt index 0398b3bf4..4ca5c8e4b 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/config/SecurityConfig.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/config/SecurityConfig.kt @@ -1,21 +1,20 @@ package co.nilin.opex.app.config +import org.springframework.beans.factory.annotation.Value import org.springframework.context.annotation.Bean -import org.springframework.core.io.ClassPathResource -import org.springframework.core.io.Resource import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity import org.springframework.security.config.web.server.ServerHttpSecurity import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder import org.springframework.security.web.server.SecurityWebFilterChain -import org.springframework.util.Base64Utils -import org.springframework.util.FileCopyUtils -import java.security.KeyFactory -import java.security.interfaces.RSAPublicKey -import java.security.spec.X509EncodedKeySpec +import org.springframework.web.reactive.function.client.WebClient @EnableWebFluxSecurity -class SecurityConfig { +class SecurityConfig(private val webClient: WebClient) { + + @Value("\${app.auth.cert-url}") + private lateinit var jwkUrl: String + @Bean fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain? { http.csrf().disable() @@ -36,13 +35,8 @@ class SecurityConfig { @Bean @Throws(Exception::class) fun reactiveJwtDecoder(): ReactiveJwtDecoder? { - val resource: Resource = ClassPathResource("/public.cert") - val publicKey = String(FileCopyUtils.copyToByteArray(resource.inputStream)) - .replace("\r", "") - .replace("-----BEGIN PUBLIC KEY-----\n", "") - .replace("\n-----END PUBLIC KEY-----", "") - val spec = X509EncodedKeySpec(Base64Utils.decodeFromString(publicKey)) - val kf = KeyFactory.getInstance("RSA") - return NimbusReactiveJwtDecoder(kf.generatePublic(spec) as RSAPublicKey) + return NimbusReactiveJwtDecoder.withJwkSetUri(jwkUrl) + .webClient(webClient) + .build() } } diff --git a/MatchingGateway/gateway-app/src/main/resources/application-docker.yml b/MatchingGateway/gateway-app/src/main/resources/application-docker.yml index b39c85247..2d59b2e9c 100644 --- a/MatchingGateway/gateway-app/src/main/resources/application-docker.yml +++ b/MatchingGateway/gateway-app/src/main/resources/application-docker.yml @@ -3,11 +3,11 @@ spring: application: name: opex-gateway main: - allow-bean-definition-overriding: false + allow-bean-definition-overriding: false kafka: bootstrap-servers: ${KAFKA_IP_PORT} consumer: - group-id: gateway + group-id: gateway cloud: bootstrap: enabled: true @@ -22,3 +22,5 @@ spring: app: accountant: url: lb://opex-accountant + auth: + cert-url: lb://opex-auth/auth/realms/opex/protocol/openid-connect/certs \ No newline at end of file diff --git a/UserManagement/keycloak-gateway/pom.xml b/UserManagement/keycloak-gateway/pom.xml index 5413fa929..1d5c558d7 100644 --- a/UserManagement/keycloak-gateway/pom.xml +++ b/UserManagement/keycloak-gateway/pom.xml @@ -22,6 +22,7 @@ 12.0.4 3.13.2.Final 11.0.10.Final + 2020.0.2 @@ -55,6 +56,11 @@ spring-boot-starter-actuator + + org.springframework.cloud + spring-cloud-starter-consul-all + + @@ -108,6 +114,18 @@ + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + ${project.basedir}/src/main/kotlin ${project.basedir}/src/test/kotlin diff --git a/UserManagement/keycloak-gateway/src/main/resources/application-docker.yml b/UserManagement/keycloak-gateway/src/main/resources/application-docker.yml index 0c88c6422..f52df428b 100644 --- a/UserManagement/keycloak-gateway/src/main/resources/application-docker.yml +++ b/UserManagement/keycloak-gateway/src/main/resources/application-docker.yml @@ -8,6 +8,8 @@ spring: cloud: consul: host: ${CONSUL_HOST} + port: 8500 + keycloak: migration: file: /opex-master-realm.json diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/SecurityConfig.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/SecurityConfig.kt index 5763505f3..a87dd4114 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/SecurityConfig.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/SecurityConfig.kt @@ -1,21 +1,20 @@ package co.nilin.opex.wallet.app.config +import org.springframework.beans.factory.annotation.Value import org.springframework.context.annotation.Bean -import org.springframework.core.io.ClassPathResource -import org.springframework.core.io.Resource import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity import org.springframework.security.config.web.server.ServerHttpSecurity import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder import org.springframework.security.web.server.SecurityWebFilterChain -import org.springframework.util.Base64Utils -import org.springframework.util.FileCopyUtils -import java.security.KeyFactory -import java.security.interfaces.RSAPublicKey -import java.security.spec.X509EncodedKeySpec +import org.springframework.web.reactive.function.client.WebClient @EnableWebFluxSecurity -class SecurityConfig { +class SecurityConfig(private val webClient: WebClient) { + + @Value("\${app.auth.cert-url}") + private lateinit var jwkUrl: String + @Bean fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain? { http.csrf().disable() @@ -33,13 +32,8 @@ class SecurityConfig { @Bean @Throws(Exception::class) fun reactiveJwtDecoder(): ReactiveJwtDecoder? { - val resource: Resource = ClassPathResource("/public.cert") - val publicKey = String(FileCopyUtils.copyToByteArray(resource.inputStream)) - .replace("\r", "") - .replace("-----BEGIN PUBLIC KEY-----\n", "") - .replace("\n-----END PUBLIC KEY-----", "") - val spec = X509EncodedKeySpec(Base64Utils.decodeFromString(publicKey)) - val kf = KeyFactory.getInstance("RSA") - return NimbusReactiveJwtDecoder(kf.generatePublic(spec) as RSAPublicKey) + return NimbusReactiveJwtDecoder.withJwkSetUri(jwkUrl) + .webClient(webClient) + .build() } } diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/WebClientConfig.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/WebClientConfig.kt new file mode 100644 index 000000000..877b7f232 --- /dev/null +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/WebClientConfig.kt @@ -0,0 +1,25 @@ +package co.nilin.opex.wallet.app.config + +import org.springframework.cloud.client.ServiceInstance +import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties +import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer +import org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerExchangeFilterFunction +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.web.reactive.function.client.WebClient + +@Configuration +class WebClientConfig { + + @Bean + fun webClient(loadBalancerFactory: ReactiveLoadBalancer.Factory): WebClient { + return WebClient.builder() + .filter( + ReactorLoadBalancerExchangeFilterFunction( + loadBalancerFactory, LoadBalancerProperties(), emptyList() + ) + ) + .build() + } + +} diff --git a/Wallet/wallet-app/src/main/resources/application-docker.yml b/Wallet/wallet-app/src/main/resources/application-docker.yml index f07453be0..ead9d6314 100644 --- a/Wallet/wallet-app/src/main/resources/application-docker.yml +++ b/Wallet/wallet-app/src/main/resources/application-docker.yml @@ -17,3 +17,6 @@ spring: host: ${CONSUL_HOST} port: 8500 +app: + auth: + cert-url: lb://opex-auth/auth/realms/opex/protocol/openid-connect/certs \ No newline at end of file From b07ad7febffa1478ef9c6258cf5b05844211d740 Mon Sep 17 00:00:00 2001 From: metalicn20 Date: Tue, 24 Aug 2021 12:10:23 +0430 Subject: [PATCH 27/59] Close #35, Fix ID type of base class in SymbolMapRepository --- .../co/nilin/opex/port/api/postgres/dao/SymbolMapRepository.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/SymbolMapRepository.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/SymbolMapRepository.kt index 8c377a94f..1a1d7dcf6 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/SymbolMapRepository.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/SymbolMapRepository.kt @@ -8,7 +8,7 @@ import org.springframework.stereotype.Repository import reactor.core.publisher.Mono @Repository -interface SymbolMapRepository : ReactiveCrudRepository { +interface SymbolMapRepository : ReactiveCrudRepository { @Query("select * from symbol_maps where symbol = :symbol") fun findBySymbol(@Param("symbol") symbol: String): Mono From 8b4095edd1ca75d56d7af42de6a57985468fefb1 Mon Sep 17 00:00:00 2001 From: maryarm Date: Tue, 24 Aug 2021 13:58:22 +0430 Subject: [PATCH 28/59] Fix #36- Remove AuthenticationConverter and use securityContext directly --- .../port/api/binance/config/SecurityConfig.kt | 2 -- .../binance/controller/AccountController.kt | 34 +++++++++++-------- .../security/AuthenticationConverter.kt | 30 ---------------- .../api/binance/security/CustomAuthToken.kt | 26 -------------- .../api/binance/util/SecurityExtension.kt | 12 +++++++ 5 files changed, 31 insertions(+), 73 deletions(-) delete mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/security/AuthenticationConverter.kt delete mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/security/CustomAuthToken.kt create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/util/SecurityExtension.kt diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt index 3710cdc90..63cadfae7 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt @@ -1,6 +1,5 @@ package co.nilin.opex.port.api.binance.config -import co.nilin.opex.port.api.binance.security.AuthenticationConverter import org.springframework.beans.factory.annotation.Value import org.springframework.context.annotation.Bean import org.springframework.http.HttpMethod @@ -33,7 +32,6 @@ class SecurityConfig(private val webClient: WebClient) { .and() .oauth2ResourceServer() .jwt() - .jwtAuthenticationConverter(AuthenticationConverter()) return http.build() } diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt index bcc7f623e..dd538ff2c 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt @@ -2,28 +2,30 @@ package co.nilin.opex.port.api.binance.controller import co.nilin.opex.api.core.inout.* import co.nilin.opex.api.core.spi.MEGatewayProxy +import co.nilin.opex.api.core.spi.SymbolMapper import co.nilin.opex.api.core.spi.UserQueryHandler import co.nilin.opex.api.core.spi.WalletProxy import co.nilin.opex.port.api.binance.data.AccountInfoResponse -import co.nilin.opex.port.api.binance.security.CustomAuthToken import co.nilin.opex.port.api.binance.util.BalanceParser +import co.nilin.opex.port.api.binance.util.LoggerDelegate import co.nilin.opex.port.api.binance.util.asMatchConstraint import co.nilin.opex.port.api.binance.util.asMatchingOrderType import co.nilin.opex.port.api.binance.util.asOrderDirection +import co.nilin.opex.port.api.binance.util.jwtAuthentication +import co.nilin.opex.port.api.binance.util.tokenValue import co.nilin.opex.utility.error.data.OpexError import co.nilin.opex.utility.error.data.OpexException -import co.nilin.opex.api.core.spi.SymbolMapper import com.fasterxml.jackson.annotation.JsonInclude import io.swagger.annotations.* +import java.math.BigDecimal +import java.security.Principal +import java.util.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map -import org.slf4j.LoggerFactory import org.springframework.http.MediaType -import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.security.core.annotation.CurrentSecurityContext +import org.springframework.security.core.context.SecurityContext import org.springframework.web.bind.annotation.* -import java.math.BigDecimal -import java.security.Principal -import java.util.* @RestController @CrossOrigin(origins = ["https://opex.dev", "http://localhost:3000"], allowedHeaders = ["*"]) @@ -98,7 +100,7 @@ class AccountController( val isBestMatch: Boolean ) - private val logger = LoggerFactory.getLogger(AccountController::class.java) + private val logger by LoggerDelegate() /* Send in a new order. @@ -157,11 +159,12 @@ class AccountController( recvWindow: Long?, //The value cannot be greater than 60000 @RequestParam(name = "timestamp") timestamp: Long, - @AuthenticationPrincipal auth: CustomAuthToken + @CurrentSecurityContext securityContext: SecurityContext ): NewOrderResponse { + val internalSymbol = symbolMapper.unmap(symbol)!! val request = MEGatewayProxy.CreateOrderRequest( - auth.uuid, + securityContext.jwtAuthentication().name, internalSymbol, price ?: BigDecimal.ZERO, // Maybe make this nullable as well? quantity ?: BigDecimal.ZERO, @@ -169,7 +172,8 @@ class AccountController( timeInForce?.asMatchConstraint(), type.asMatchingOrderType() ) - matchingGatewayProxy.createNewOrder(request, auth.tokenValue) + + matchingGatewayProxy.createNewOrder(request, securityContext.jwtAuthentication().tokenValue()) return NewOrderResponse( symbol, -1, @@ -439,16 +443,16 @@ class AccountController( ) ) suspend fun accountInfo( - @AuthenticationPrincipal - auth: CustomAuthToken, + @CurrentSecurityContext securityContext: SecurityContext, @ApiParam(value = "The value cannot be greater than 60000") @RequestParam(name = "recvWindow", required = false) recvWindow: Long?, //The value cannot be greater than 60000 @RequestParam(name = "timestamp") timestamp: Long ): AccountInfoResponse { - val wallets = walletProxy.getWallets(auth.uuid, auth.tokenValue) - val limits = walletProxy.getOwnerLimits(auth.uuid, auth.tokenValue) + val auth = securityContext.jwtAuthentication() + val wallets = walletProxy.getWallets(auth.name, auth.tokenValue()) + val limits = walletProxy.getOwnerLimits(auth.name, auth.tokenValue()) val parsedBalances = BalanceParser.parse(wallets) val accountType = "SPOT" diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/security/AuthenticationConverter.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/security/AuthenticationConverter.kt deleted file mode 100644 index e7776e4ca..000000000 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/security/AuthenticationConverter.kt +++ /dev/null @@ -1,30 +0,0 @@ -package co.nilin.opex.port.api.binance.security - -import org.springframework.core.convert.converter.Converter -import org.springframework.security.authentication.AbstractAuthenticationToken -import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames -import org.springframework.security.oauth2.jwt.Jwt -import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter -import reactor.core.publisher.Mono - -class AuthenticationConverter : Converter> { - - private val authoritiesConverter = JwtGrantedAuthoritiesConverter() - - override fun convert(source: Jwt): Mono? { - return try { - Mono.just( - CustomAuthToken( - source.claims[IdTokenClaimNames.SUB] as String?, - source.claims["name"] as String?, - source.claims["preferred_username"] as String?, - source.claims["email"] as String?, - source.tokenValue, - authoritiesConverter.convert(source) ?: arrayListOf() - ) - ) - } catch (e: Exception) { - null - } - } -} \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/security/CustomAuthToken.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/security/CustomAuthToken.kt deleted file mode 100644 index 687997c01..000000000 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/security/CustomAuthToken.kt +++ /dev/null @@ -1,26 +0,0 @@ -package co.nilin.opex.port.api.binance.security - -import org.springframework.security.authentication.AbstractAuthenticationToken -import org.springframework.security.core.GrantedAuthority - -class CustomAuthToken( - val uuid: String?, - val fullName: String?, - val username: String?, - val email: String?, - val tokenValue: String?, - authorities: Collection = arrayListOf() -) : AbstractAuthenticationToken(authorities) { - - init { - isAuthenticated = true - } - - override fun getCredentials(): Any { - return "N/A" - } - - override fun getPrincipal(): Any? { - return uuid - } -} \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/util/SecurityExtension.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/util/SecurityExtension.kt new file mode 100644 index 000000000..5b043512f --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/util/SecurityExtension.kt @@ -0,0 +1,12 @@ +package co.nilin.opex.port.api.binance.util + +import org.springframework.security.core.context.SecurityContext +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken + +fun SecurityContext.jwtAuthentication(): JwtAuthenticationToken { + return authentication as JwtAuthenticationToken +} + +fun JwtAuthenticationToken.tokenValue(): String { + return this.token.tokenValue +} \ No newline at end of file From 5407bb1afa9316c02fb764d3f60ea9fb6111740e Mon Sep 17 00:00:00 2001 From: maryarm Date: Tue, 24 Aug 2021 14:15:08 +0430 Subject: [PATCH 29/59] Fix #37-Add Filter To fix FormParam problem in WebFlux --- Api/api-app/pom.xml | 16 +++- Api/api-ports/api-binance-rest/pom.xml | 10 ++- .../port/api/binance/config/RestConfig.kt | 7 ++ Utility/interceptors/.gitignore | 33 +++++++ Utility/interceptors/pom.xml | 90 +++++++++++++++++++ .../interceptor/FormDataWorkaroundFilter.java | 45 ++++++++++ .../FormDataServerHttpRequestDecorator.java | 19 ++++ .../FormDataServerWebExchangeDecorator.java | 25 ++++++ Utility/pom.xml | 1 + 9 files changed, 242 insertions(+), 4 deletions(-) create mode 100644 Utility/interceptors/.gitignore create mode 100644 Utility/interceptors/pom.xml create mode 100644 Utility/interceptors/src/main/kotlin/co/nilin/opex/utility/interceptor/FormDataWorkaroundFilter.java create mode 100644 Utility/interceptors/src/main/kotlin/co/nilin/opex/utility/interceptor/decorator/FormDataServerHttpRequestDecorator.java create mode 100644 Utility/interceptors/src/main/kotlin/co/nilin/opex/utility/interceptor/decorator/FormDataServerWebExchangeDecorator.java diff --git a/Api/api-app/pom.xml b/Api/api-app/pom.xml index 691aa417f..218068891 100644 --- a/Api/api-app/pom.xml +++ b/Api/api-app/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex api-app - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT api-app Api app Opex @@ -20,6 +20,7 @@ ${version} ${version} ${version} + ${version} @@ -39,6 +40,17 @@ org.springframework.boot spring-boot-starter + + co.nilin.opex + error-handler + ${utility.version} + + + co.nilin.opex + interceptors + ${utility.version} + provided + co.nilin.opex accountant-core @@ -49,7 +61,7 @@ api-core ${api.version} - + co.nilin.opex api-eventlistener-kafka ${api.version} diff --git a/Api/api-ports/api-binance-rest/pom.xml b/Api/api-ports/api-binance-rest/pom.xml index 049c12a74..2b267845d 100644 --- a/Api/api-ports/api-binance-rest/pom.xml +++ b/Api/api-ports/api-binance-rest/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex api-binance-rest - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT api-binance-rest Api Binance Rest @@ -20,6 +20,7 @@ ${version} ${version} 2020.0.2 + ${version} @@ -35,6 +36,12 @@ ${api.version} provided + + co.nilin.opex + interceptors + ${utility.version} + provided + org.springframework.boot spring-boot-starter-webflux @@ -47,7 +54,6 @@ org.springframework.boot spring-boot-starter-actuator - org.springframework.boot spring-boot-starter-data-r2dbc diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/RestConfig.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/RestConfig.kt index cb717099f..4c6a23140 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/RestConfig.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/RestConfig.kt @@ -1,9 +1,11 @@ package co.nilin.opex.port.api.binance.config +import co.nilin.opex.utility.interceptor.FormDataWorkaroundFilter import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.format.Formatter import java.util.* +import org.springframework.web.server.WebFilter @Configuration class RestConfig { @@ -19,4 +21,9 @@ class RestConfig { } } } + + @Bean + fun formDataWebFilter(): WebFilter { + return FormDataWorkaroundFilter() + } } \ No newline at end of file diff --git a/Utility/interceptors/.gitignore b/Utility/interceptors/.gitignore new file mode 100644 index 000000000..549e00a2a --- /dev/null +++ b/Utility/interceptors/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/Utility/interceptors/pom.xml b/Utility/interceptors/pom.xml new file mode 100644 index 000000000..7783aa213 --- /dev/null +++ b/Utility/interceptors/pom.xml @@ -0,0 +1,90 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.opex + interceptors + 1.0-SNAPSHOT + interceptors + REST interceptors + + + 1.8 + 1.4.31 + + + + + org.springframework.boot + spring-boot-starter-webflux + + + com.fasterxml.jackson.module + jackson-module-kotlin + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + io.projectreactor.netty + reactor-netty + 0.9.12.RELEASE + + + org.springframework.boot + spring-boot-starter-test + test + + + io.projectreactor + reactor-test + test + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + diff --git a/Utility/interceptors/src/main/kotlin/co/nilin/opex/utility/interceptor/FormDataWorkaroundFilter.java b/Utility/interceptors/src/main/kotlin/co/nilin/opex/utility/interceptor/FormDataWorkaroundFilter.java new file mode 100644 index 000000000..17dccc05f --- /dev/null +++ b/Utility/interceptors/src/main/kotlin/co/nilin/opex/utility/interceptor/FormDataWorkaroundFilter.java @@ -0,0 +1,45 @@ +package co.nilin.opex.utility.interceptor; + +import co.nilin.opex.utility.interceptor.decorator.*; +import org.springframework.http.codec.multipart.*; +import org.springframework.http.server.reactive.*; +import org.springframework.util.*; +import org.springframework.web.server.*; +import reactor.core.publisher.*; + +import java.util.*; + + +public class FormDataWorkaroundFilter implements WebFilter { + + @Override + public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { + final ServerHttpRequest request = exchange.getRequest(); + + final MultiValueMap queryParams = new LinkedMultiValueMap<>(); + + //add all content from form data to query params + exchange.getFormData().subscribe(queryParams::putAll); + + exchange.getMultipartData().subscribe(map -> { + map.forEach((key, value) -> { + List list = value; + list.forEach(item -> { + //add each form field parts to query params + if (item instanceof FormFieldPart) { + final FormFieldPart formFieldPart = (FormFieldPart) item; + queryParams.add(key, formFieldPart.value()); + } + }); + + }); + }); + + //add original query params to win identical name war + queryParams.putAll(request.getQueryParams()); + + return chain.filter(new FormDataServerWebExchangeDecorator(queryParams, exchange)); + } + + +} diff --git a/Utility/interceptors/src/main/kotlin/co/nilin/opex/utility/interceptor/decorator/FormDataServerHttpRequestDecorator.java b/Utility/interceptors/src/main/kotlin/co/nilin/opex/utility/interceptor/decorator/FormDataServerHttpRequestDecorator.java new file mode 100644 index 000000000..670365200 --- /dev/null +++ b/Utility/interceptors/src/main/kotlin/co/nilin/opex/utility/interceptor/decorator/FormDataServerHttpRequestDecorator.java @@ -0,0 +1,19 @@ +package co.nilin.opex.utility.interceptor.decorator; + +import org.springframework.http.server.reactive.*; +import org.springframework.util.*; + +public class FormDataServerHttpRequestDecorator extends ServerHttpRequestDecorator { + + private MultiValueMap queryParams; + + FormDataServerHttpRequestDecorator(MultiValueMap queryParams, ServerHttpRequest delegate) { + super(delegate); + this.queryParams = queryParams; + } + + @Override + public MultiValueMap getQueryParams() { + return queryParams; + } +} \ No newline at end of file diff --git a/Utility/interceptors/src/main/kotlin/co/nilin/opex/utility/interceptor/decorator/FormDataServerWebExchangeDecorator.java b/Utility/interceptors/src/main/kotlin/co/nilin/opex/utility/interceptor/decorator/FormDataServerWebExchangeDecorator.java new file mode 100644 index 000000000..57679d517 --- /dev/null +++ b/Utility/interceptors/src/main/kotlin/co/nilin/opex/utility/interceptor/decorator/FormDataServerWebExchangeDecorator.java @@ -0,0 +1,25 @@ +package co.nilin.opex.utility.interceptor.decorator; + +import org.slf4j.*; +import org.springframework.http.server.reactive.*; +import org.springframework.util.*; +import org.springframework.web.server.*; + +public class FormDataServerWebExchangeDecorator extends ServerWebExchangeDecorator { + + private Logger log = LoggerFactory.getLogger( FormDataServerWebExchangeDecorator.class); + + private FormDataServerHttpRequestDecorator requestDecorator; + + + public FormDataServerWebExchangeDecorator(MultiValueMap queryParams, ServerWebExchange delegate) { + super(delegate); + requestDecorator = new FormDataServerHttpRequestDecorator(queryParams, delegate.getRequest()); + } + + @Override + public ServerHttpRequest getRequest() { + return requestDecorator; + } + +} \ No newline at end of file diff --git a/Utility/pom.xml b/Utility/pom.xml index ae7327669..1b8c0a40d 100644 --- a/Utility/pom.xml +++ b/Utility/pom.xml @@ -12,5 +12,6 @@ error-handler + interceptors \ No newline at end of file From b62c1e5372c9073ff6e5403a2f30cc458c0b5558 Mon Sep 17 00:00:00 2001 From: maryarm Date: Tue, 24 Aug 2021 14:51:59 +0430 Subject: [PATCH 30/59] Fix #38-Add logs for received Rest calls and WebClients --- Accountant/accountant-app/pom.xml | 17 ++-- .../src/main/resources/application.yml | 4 + Accountant/accountant-core/pom.xml | 2 +- .../accountant-eventlistener-kafka/pom.xml | 2 +- .../accountant-persister-postgres/pom.xml | 2 +- .../accountant-submitter-kafka/pom.xml | 2 +- .../accountant-wallet-proxy/pom.xml | 11 ++- .../wallet/config/WebClientConfig.kt | 13 +++ Accountant/pom.xml | 2 +- Api/api-app/pom.xml | 8 +- .../src/main/resources/application.yml | 4 + Api/api-core/pom.xml | 8 +- Api/api-ports/api-binance-rest/pom.xml | 6 -- .../api/binance/config/WebClientConfig.kt | 13 +++ Api/api-ports/api-eventlistener-kafka/pom.xml | 2 +- Api/api-ports/api-persister-postgres/pom.xml | 9 +- Api/pom.xml | 2 +- BlockchainGateway/bc-gateway-app/pom.xml | 2 +- BlockchainGateway/bc-gateway-core/pom.xml | 2 +- .../bc-persister-postgres/pom.xml | 2 +- BlockchainGateway/pom.xml | 2 +- EventLog/eventlog-app/pom.xml | 2 +- EventLog/eventlog-core/pom.xml | 2 +- .../eventlog-eventlistener-kafka/pom.xml | 2 +- .../eventlog-persister-postgres/pom.xml | 2 +- EventLog/pom.xml | 2 +- MatchingEngine/matching-app/pom.xml | 2 +- MatchingEngine/matching-core/pom.xml | 2 +- .../matching-eventlistener-kafka/pom.xml | 2 +- .../matching-snapshots-redis/pom.xml | 2 +- .../matching-submitter-kafka/pom.xml | 2 +- MatchingEngine/pom.xml | 2 +- MatchingGateway/gateway-app/pom.xml | 12 ++- .../nilin/opex/app/config/WebClientConfig.kt | 13 +++ .../src/main/resources/application.yml | 4 + .../order-submitter-kafka/pom.xml | 2 +- MatchingGateway/pom.xml | 2 +- UserManagement/keycloak-gateway/pom.xml | 2 +- UserManagement/pom.xml | 2 +- Utility/logging-handler/.gitignore | 33 +++++++ Utility/logging-handler/pom.xml | 90 +++++++++++++++++++ .../utility/log/interceptor/LogUtils.java | 58 ++++++++++++ .../log/interceptor/RequestFilter.java | 35 ++++++++ .../log/interceptor/WebClientInterceptor.kt | 49 ++++++++++ ...yloadBufferServerHttpRequestDecorator.java | 55 ++++++++++++ ...loadBufferServerHttpResponseDecorator.java | 58 ++++++++++++ .../PayloadServerWebExchangeDecorator.java | 33 +++++++ Wallet/pom.xml | 2 +- Wallet/wallet-app/pom.xml | 20 +++-- .../src/main/resources/application.yml | 2 + Wallet/wallet-core/pom.xml | 2 +- .../wallet-eventlistener-kafka/pom.xml | 2 +- .../wallet-persister-postgres/pom.xml | 2 +- 53 files changed, 547 insertions(+), 68 deletions(-) create mode 100644 Utility/logging-handler/.gitignore create mode 100644 Utility/logging-handler/pom.xml create mode 100644 Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/LogUtils.java create mode 100644 Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/RequestFilter.java create mode 100644 Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/WebClientInterceptor.kt create mode 100644 Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/decorator/PayloadBufferServerHttpRequestDecorator.java create mode 100644 Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/decorator/PayloadBufferServerHttpResponseDecorator.java create mode 100644 Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/decorator/PayloadServerWebExchangeDecorator.java diff --git a/Accountant/accountant-app/pom.xml b/Accountant/accountant-app/pom.xml index acee7567f..ade675037 100644 --- a/Accountant/accountant-app/pom.xml +++ b/Accountant/accountant-app/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex accountant-app - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT accountant-app Accountant app Opex @@ -18,13 +18,10 @@ 1.8 1.4.31 ${version} + ${version} - - org.springframework.boot - spring-boot-starter - org.jetbrains.kotlin kotlin-reflect @@ -66,6 +63,16 @@ accountant-wallet-proxy ${accountant.version} + + co.nilin.opex + error-handler + ${utility.version} + + + co.nilin.opex + logging-handler + ${utility.version} + org.springframework.boot diff --git a/Accountant/accountant-app/src/main/resources/application.yml b/Accountant/accountant-app/src/main/resources/application.yml index a0ba514c7..aa238fa2f 100644 --- a/Accountant/accountant-app/src/main/resources/application.yml +++ b/Accountant/accountant-app/src/main/resources/application.yml @@ -1,4 +1,8 @@ server.port: 8089 +logging: + level: + co.nilin: DEBUG + reactor.netty.http.client: DEBUG spring: application: name: opex-accountant diff --git a/Accountant/accountant-core/pom.xml b/Accountant/accountant-core/pom.xml index 9dbe36835..3f7cc6f20 100644 --- a/Accountant/accountant-core/pom.xml +++ b/Accountant/accountant-core/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex accountant-core - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT accountant-core Accountant logic of Opex diff --git a/Accountant/accountant-ports/accountant-eventlistener-kafka/pom.xml b/Accountant/accountant-ports/accountant-eventlistener-kafka/pom.xml index 09689d7ff..e85bab28d 100644 --- a/Accountant/accountant-ports/accountant-eventlistener-kafka/pom.xml +++ b/Accountant/accountant-ports/accountant-eventlistener-kafka/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex accountant-eventlistener-kafka - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT accountant-eventlistener-kafka Accountant kafka listener of Opex diff --git a/Accountant/accountant-ports/accountant-persister-postgres/pom.xml b/Accountant/accountant-ports/accountant-persister-postgres/pom.xml index 03b0e336a..db635b291 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/pom.xml +++ b/Accountant/accountant-ports/accountant-persister-postgres/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex accountant-persister-postgres - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT accountant-persister-postgres Persist items of Opex accountant on Postgres diff --git a/Accountant/accountant-ports/accountant-submitter-kafka/pom.xml b/Accountant/accountant-ports/accountant-submitter-kafka/pom.xml index 5d24cf377..f7d3e70ab 100644 --- a/Accountant/accountant-ports/accountant-submitter-kafka/pom.xml +++ b/Accountant/accountant-ports/accountant-submitter-kafka/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex accountant-submitter-kafka - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT accountant-temp-submitter-kafka Accountant kafka event submitter of Opex diff --git a/Accountant/accountant-ports/accountant-wallet-proxy/pom.xml b/Accountant/accountant-ports/accountant-wallet-proxy/pom.xml index 8164d4c94..d536d061c 100644 --- a/Accountant/accountant-ports/accountant-wallet-proxy/pom.xml +++ b/Accountant/accountant-ports/accountant-wallet-proxy/pom.xml @@ -10,16 +10,17 @@ co.nilin.opex accountant-wallet-proxy - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT accountant-wallet-proxy Opex wallet proxy 1.8 1.4.31 + 2020.0.2 ${version} ${version} - 2020.0.2 + ${version} @@ -35,6 +36,12 @@ ${accountant.version} provided + + co.nilin.opex + logging-handler + ${utility.version} + provided + org.springframework.boot spring-boot-starter-webflux diff --git a/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/opex/port/accountant/wallet/config/WebClientConfig.kt b/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/opex/port/accountant/wallet/config/WebClientConfig.kt index 857143a6b..ce2bca7fd 100644 --- a/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/opex/port/accountant/wallet/config/WebClientConfig.kt +++ b/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/opex/port/accountant/wallet/config/WebClientConfig.kt @@ -1,19 +1,32 @@ package co.nilin.opex.port.accountant.wallet.config +import co.nilin.opex.utility.log.interceptor.CustomLogger import org.springframework.cloud.client.ServiceInstance import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer import org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerExchangeFilterFunction import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration +import org.springframework.http.client.reactive.ReactorClientHttpConnector import org.springframework.web.reactive.function.client.WebClient +import reactor.netty.http.client.HttpClient @Configuration class WebClientConfig { @Bean fun webClient(loadBalancerFactory: ReactiveLoadBalancer.Factory): WebClient { + val logger = CustomLogger(HttpClient::class.java) return WebClient.builder() + .clientConnector( + ReactorClientHttpConnector( + HttpClient + .create() + .doOnRequest { request, connection -> + connection.addHandlerFirst(logger) + } + ) + ) .filter( ReactorLoadBalancerExchangeFilterFunction( loadBalancerFactory, LoadBalancerProperties(), emptyList() diff --git a/Accountant/pom.xml b/Accountant/pom.xml index 4d974760c..436f574ac 100644 --- a/Accountant/pom.xml +++ b/Accountant/pom.xml @@ -4,7 +4,7 @@ 4.0.0 co.nilin.opex accountant-root - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT accountant-root pom Accountant root of Opex diff --git a/Api/api-app/pom.xml b/Api/api-app/pom.xml index 218068891..d92bfdf3d 100644 --- a/Api/api-app/pom.xml +++ b/Api/api-app/pom.xml @@ -42,15 +42,9 @@ co.nilin.opex - error-handler + logging-handler ${utility.version} - - co.nilin.opex - interceptors - ${utility.version} - provided - co.nilin.opex accountant-core diff --git a/Api/api-app/src/main/resources/application.yml b/Api/api-app/src/main/resources/application.yml index 941180d9b..07f248f0a 100644 --- a/Api/api-app/src/main/resources/application.yml +++ b/Api/api-app/src/main/resources/application.yml @@ -1,4 +1,8 @@ server.port: 8094 +logging: + level: + co.nilin: DEBUG + reactor.netty.http.client: DEBUG spring: application: name: opex-api diff --git a/Api/api-core/pom.xml b/Api/api-core/pom.xml index 40ff51b14..b8ded6631 100644 --- a/Api/api-core/pom.xml +++ b/Api/api-core/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex api-core - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT api-core Api logic of Opex @@ -57,12 +57,6 @@ ${accountant.version} provided - - co.nilin.opex - error-handler - 1.0-SNAPSHOT - - org.springframework.boot spring-boot-starter-test diff --git a/Api/api-ports/api-binance-rest/pom.xml b/Api/api-ports/api-binance-rest/pom.xml index 2b267845d..60f9f72ee 100644 --- a/Api/api-ports/api-binance-rest/pom.xml +++ b/Api/api-ports/api-binance-rest/pom.xml @@ -36,12 +36,6 @@ ${api.version} provided - - co.nilin.opex - interceptors - ${utility.version} - provided - org.springframework.boot spring-boot-starter-webflux diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/WebClientConfig.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/WebClientConfig.kt index 6f230375d..84aa19305 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/WebClientConfig.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/WebClientConfig.kt @@ -1,19 +1,32 @@ package co.nilin.opex.port.api.binance.config +import co.nilin.opex.utility.log.interceptor.CustomLogger import org.springframework.cloud.client.ServiceInstance import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer import org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerExchangeFilterFunction import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration +import org.springframework.http.client.reactive.ReactorClientHttpConnector import org.springframework.web.reactive.function.client.WebClient +import reactor.netty.http.client.HttpClient @Configuration class WebClientConfig { @Bean fun webClient(loadBalancerFactory: ReactiveLoadBalancer.Factory): WebClient { + val logger = CustomLogger(HttpClient::class.java) return WebClient.builder() + .clientConnector( + ReactorClientHttpConnector( + HttpClient + .create() + .doOnRequest { request, connection -> + connection.addHandlerFirst(logger) + } + ) + ) .filter( ReactorLoadBalancerExchangeFilterFunction( loadBalancerFactory, LoadBalancerProperties(), emptyList() diff --git a/Api/api-ports/api-eventlistener-kafka/pom.xml b/Api/api-ports/api-eventlistener-kafka/pom.xml index 3ece6c8c9..02f69f254 100644 --- a/Api/api-ports/api-eventlistener-kafka/pom.xml +++ b/Api/api-ports/api-eventlistener-kafka/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex api-eventlistener-kafka - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT api-eventlistener-kafka Api kafka listener of Opex diff --git a/Api/api-ports/api-persister-postgres/pom.xml b/Api/api-ports/api-persister-postgres/pom.xml index 413755120..3beabdc05 100644 --- a/Api/api-ports/api-persister-postgres/pom.xml +++ b/Api/api-ports/api-persister-postgres/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex api-persister-postgres - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT api-persister-postgres Persist items of Opex api on Postgres @@ -20,6 +20,7 @@ ${version} ${version} ${version} + ${version} @@ -41,6 +42,12 @@ ${accountant.version} provided + + co.nilin.opex + error-handler + ${utility.version} + provided + org.springframework.boot spring-boot-starter-data-r2dbc diff --git a/Api/pom.xml b/Api/pom.xml index 6544168ba..8e670c159 100644 --- a/Api/pom.xml +++ b/Api/pom.xml @@ -4,7 +4,7 @@ 4.0.0 co.nilin.opex api-root - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT api-root pom Api root of Opex diff --git a/BlockchainGateway/bc-gateway-app/pom.xml b/BlockchainGateway/bc-gateway-app/pom.xml index ea7ba8495..fcd1200f7 100644 --- a/BlockchainGateway/bc-gateway-app/pom.xml +++ b/BlockchainGateway/bc-gateway-app/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex.external bc-gateway-app - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT bc-gateway-app Blockchain gateway app of Opex diff --git a/BlockchainGateway/bc-gateway-core/pom.xml b/BlockchainGateway/bc-gateway-core/pom.xml index 7c42fe00e..bf2a407dd 100644 --- a/BlockchainGateway/bc-gateway-core/pom.xml +++ b/BlockchainGateway/bc-gateway-core/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex.external bc-gateway-core - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT bc-gateway-core Blockchain gateway core of Opex diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/pom.xml b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/pom.xml index 2401a6b65..321effaa2 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/pom.xml +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex.external bc-gateway-persister-postgres - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT bc-gateway-persister-postgres Persist items of Opex blockchain gateway on Postgres diff --git a/BlockchainGateway/pom.xml b/BlockchainGateway/pom.xml index ec4fbb8c8..d2090befe 100644 --- a/BlockchainGateway/pom.xml +++ b/BlockchainGateway/pom.xml @@ -4,7 +4,7 @@ 4.0.0 co.nilin.opex.external bc-gateway - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT bc-gateway pom Blockchain gateway root of opex diff --git a/EventLog/eventlog-app/pom.xml b/EventLog/eventlog-app/pom.xml index e42c46312..fe3074461 100644 --- a/EventLog/eventlog-app/pom.xml +++ b/EventLog/eventlog-app/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex eventlog-app - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT eventlog-app Event log running app Opex diff --git a/EventLog/eventlog-core/pom.xml b/EventLog/eventlog-core/pom.xml index 4c208e517..f916ddc41 100644 --- a/EventLog/eventlog-core/pom.xml +++ b/EventLog/eventlog-core/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex eventlog-core - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT eventlog-core Event log of Opex diff --git a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/pom.xml b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/pom.xml index b669e3130..38abd4d43 100644 --- a/EventLog/eventlog-ports/eventlog-eventlistener-kafka/pom.xml +++ b/EventLog/eventlog-ports/eventlog-eventlistener-kafka/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex eventlog-eventlistener-kafka - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT eventlog-eventlistener-kafka Matching engine kafka trade handler of Opex diff --git a/EventLog/eventlog-ports/eventlog-persister-postgres/pom.xml b/EventLog/eventlog-ports/eventlog-persister-postgres/pom.xml index 2fb2c4b6d..3c2fcc5e3 100644 --- a/EventLog/eventlog-ports/eventlog-persister-postgres/pom.xml +++ b/EventLog/eventlog-ports/eventlog-persister-postgres/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex eventlog-persister-postgres - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT eventlog-persister-postgres Persist items of Opex on Postgres diff --git a/EventLog/pom.xml b/EventLog/pom.xml index 6678be074..25d17a0d6 100644 --- a/EventLog/pom.xml +++ b/EventLog/pom.xml @@ -4,7 +4,7 @@ 4.0.0 co.nilin.opex eventlog - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT eventlog pom Event log root of Opex diff --git a/MatchingEngine/matching-app/pom.xml b/MatchingEngine/matching-app/pom.xml index d8b64d033..1f93bef15 100644 --- a/MatchingEngine/matching-app/pom.xml +++ b/MatchingEngine/matching-app/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex matching-app - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT matching-app Matching engine running app Opex diff --git a/MatchingEngine/matching-core/pom.xml b/MatchingEngine/matching-core/pom.xml index c52250d16..48b09607a 100644 --- a/MatchingEngine/matching-core/pom.xml +++ b/MatchingEngine/matching-core/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex matching-core - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT matching-core Matching engine of Opex diff --git a/MatchingEngine/matching-ports/matching-eventlistener-kafka/pom.xml b/MatchingEngine/matching-ports/matching-eventlistener-kafka/pom.xml index d3ec40a0a..4f0975361 100644 --- a/MatchingEngine/matching-ports/matching-eventlistener-kafka/pom.xml +++ b/MatchingEngine/matching-ports/matching-eventlistener-kafka/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex matching-eventlistener-kafka - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT matching-eventlistener-kafka Matching engine kafka order submitter of Opex diff --git a/MatchingEngine/matching-ports/matching-snapshots-redis/pom.xml b/MatchingEngine/matching-ports/matching-snapshots-redis/pom.xml index 0a577b13e..5d1854fe3 100644 --- a/MatchingEngine/matching-ports/matching-snapshots-redis/pom.xml +++ b/MatchingEngine/matching-ports/matching-snapshots-redis/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex matching-snapshots-redis - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT matching-snapshots-redis Persist Matching engine snapshot of Opex on Redis diff --git a/MatchingEngine/matching-ports/matching-submitter-kafka/pom.xml b/MatchingEngine/matching-ports/matching-submitter-kafka/pom.xml index 1642e2403..9313485aa 100644 --- a/MatchingEngine/matching-ports/matching-submitter-kafka/pom.xml +++ b/MatchingEngine/matching-ports/matching-submitter-kafka/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex matching-submitter-kafka - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT matching-submitter-kafka Matching engine kafka order submitter of Opex diff --git a/MatchingEngine/pom.xml b/MatchingEngine/pom.xml index e6838d569..bd686f926 100644 --- a/MatchingEngine/pom.xml +++ b/MatchingEngine/pom.xml @@ -4,7 +4,7 @@ 4.0.0 co.nilin.opex matching-engine - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT matching-engine pom Matching Engine root of Opex diff --git a/MatchingGateway/gateway-app/pom.xml b/MatchingGateway/gateway-app/pom.xml index 170c311f0..576baf60c 100644 --- a/MatchingGateway/gateway-app/pom.xml +++ b/MatchingGateway/gateway-app/pom.xml @@ -10,15 +10,16 @@ co.nilin.opex matching-gateway-app - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT matching-gateway-app Matching gateway running app Opex 1.8 1.4.31 - ${version} 2020.0.2 + ${version} + ${version} @@ -92,7 +93,12 @@ co.nilin.opex error-handler - 1.0-SNAPSHOT + ${utility.version} + + + co.nilin.opex + logging-handler + ${utility.version} diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/config/WebClientConfig.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/config/WebClientConfig.kt index b0c504071..ae61545dd 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/config/WebClientConfig.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/config/WebClientConfig.kt @@ -1,19 +1,32 @@ package co.nilin.opex.app.config +import co.nilin.opex.utility.log.interceptor.CustomLogger import org.springframework.cloud.client.ServiceInstance import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer import org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerExchangeFilterFunction import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration +import org.springframework.http.client.reactive.ReactorClientHttpConnector import org.springframework.web.reactive.function.client.WebClient +import reactor.netty.http.client.HttpClient @Configuration class WebClientConfig { @Bean fun webClient(loadBalancerFactory: ReactiveLoadBalancer.Factory): WebClient { + val logger = CustomLogger(HttpClient::class.java) return WebClient.builder() + .clientConnector( + ReactorClientHttpConnector( + HttpClient + .create() + .doOnRequest { request, connection -> + connection.addHandlerFirst(logger) + } + ) + ) .filter( ReactorLoadBalancerExchangeFilterFunction( loadBalancerFactory, LoadBalancerProperties(), emptyList() diff --git a/MatchingGateway/gateway-app/src/main/resources/application.yml b/MatchingGateway/gateway-app/src/main/resources/application.yml index b42b848c3..3ced8b234 100644 --- a/MatchingGateway/gateway-app/src/main/resources/application.yml +++ b/MatchingGateway/gateway-app/src/main/resources/application.yml @@ -1,4 +1,8 @@ server.port: 8093 +logging: + level: + co.nilin: DEBUG + reactor.netty.http.client: DEBUG spring: application: name: opex-gateway diff --git a/MatchingGateway/gateway-port/order-submitter-kafka/pom.xml b/MatchingGateway/gateway-port/order-submitter-kafka/pom.xml index fe578663d..0e915bdbb 100644 --- a/MatchingGateway/gateway-port/order-submitter-kafka/pom.xml +++ b/MatchingGateway/gateway-port/order-submitter-kafka/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex gateway-order-submitter-kafka - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT gateway-order-submitter-kafka Matching gateway kafka order submitter of Opex diff --git a/MatchingGateway/pom.xml b/MatchingGateway/pom.xml index 90493a8c8..c50dd1464 100644 --- a/MatchingGateway/pom.xml +++ b/MatchingGateway/pom.xml @@ -4,7 +4,7 @@ 4.0.0 co.nilin.opex matching-gateway-root - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT matching-gateway-root pom Matching Api Gateway root of Opex diff --git a/UserManagement/keycloak-gateway/pom.xml b/UserManagement/keycloak-gateway/pom.xml index 1d5c558d7..98a1ef20f 100644 --- a/UserManagement/keycloak-gateway/pom.xml +++ b/UserManagement/keycloak-gateway/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex keycloak-gateway - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT keycloak-gateway Keycloak gateway app Opex diff --git a/UserManagement/pom.xml b/UserManagement/pom.xml index 6f820e2db..8749a2ce1 100644 --- a/UserManagement/pom.xml +++ b/UserManagement/pom.xml @@ -4,7 +4,7 @@ 4.0.0 co.nilin.opex user-management-root - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT user-management-root pom User Management root of Opex diff --git a/Utility/logging-handler/.gitignore b/Utility/logging-handler/.gitignore new file mode 100644 index 000000000..549e00a2a --- /dev/null +++ b/Utility/logging-handler/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/Utility/logging-handler/pom.xml b/Utility/logging-handler/pom.xml new file mode 100644 index 000000000..3c6138820 --- /dev/null +++ b/Utility/logging-handler/pom.xml @@ -0,0 +1,90 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.opex + logging-handler + 1.0-SNAPSHOT + logging-handler + REST logging handler + + + 1.8 + 1.4.31 + + + + + org.springframework.boot + spring-boot-starter-webflux + + + com.fasterxml.jackson.module + jackson-module-kotlin + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + io.projectreactor.netty + reactor-netty + 0.9.12.RELEASE + + + org.springframework.boot + spring-boot-starter-test + test + + + io.projectreactor + reactor-test + test + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + diff --git a/Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/LogUtils.java b/Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/LogUtils.java new file mode 100644 index 000000000..9c5fa48e9 --- /dev/null +++ b/Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/LogUtils.java @@ -0,0 +1,58 @@ +package co.nilin.opex.utility.log.interceptor; + +import io.netty.buffer.UnpooledByteBufAllocator; +import org.slf4j.Logger; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.core.io.buffer.NettyDataBufferFactory; +import org.springframework.http.MediaType; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.util.Arrays; +import java.util.List; + +public class LogUtils { + + public static final List legalLogMediaTypes = Arrays.asList( + MediaType.TEXT_XML, + MediaType.APPLICATION_XML, + MediaType.APPLICATION_JSON, + MediaType.TEXT_PLAIN, + MediaType.TEXT_XML); + + public static T loggingRequest(Logger log, String tracing, T buffer) { + return logging(log, tracing,"request: ", buffer); + } + + public static T loggingResponse(Logger log, String tracing, T buffer) { + return logging(log, tracing,"response: ", buffer); + } + + private static T logging(Logger log, String tracing,String inOrOut, T buffer) { + InputStream dataBuffer = buffer.asInputStream(); + byte[] bytes = toByteArray(dataBuffer); + NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false)); + if (log.isDebugEnabled()) { + log.debug("{}-{}: {}", tracing, inOrOut, new String(bytes)); + } + DataBufferUtils.release(buffer); + return (T) nettyDataBufferFactory.wrap(bytes); + } + + private static byte[] toByteArray(InputStream inStream) { + ByteArrayOutputStream swapStream = new ByteArrayOutputStream(); + byte[] buff = new byte[100]; + int rc = 0; + byte[] in_b = new byte[]{}; + try { + while ((rc = inStream.read(buff, 0, 100)) > 0) { + swapStream.write(buff, 0, rc); + } + in_b = swapStream.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + } + return in_b; + } +} \ No newline at end of file diff --git a/Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/RequestFilter.java b/Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/RequestFilter.java new file mode 100644 index 000000000..83d68b9cc --- /dev/null +++ b/Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/RequestFilter.java @@ -0,0 +1,35 @@ +package co.nilin.opex.utility.log.interceptor; + +import co.nilin.opex.utility.log.interceptor.decorator.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilter; +import org.springframework.web.server.WebFilterChain; +import reactor.core.publisher.Mono; + +import java.util.UUID; + +@Component +@Order(2) +public class RequestFilter implements WebFilter { + + private Logger log = LoggerFactory.getLogger( RequestFilter.class); + + @Override + public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { + if ( exchange.getRequest().getPath().toString().startsWith("/actuator/health")) + return chain.filter(exchange); + long startTime = System.currentTimeMillis(); + String tracing = UUID.randomUUID().toString(); + return chain.filter(new PayloadServerWebExchangeDecorator(tracing, exchange)) + .doOnSuccess((done) -> success(tracing, startTime)); + } + + private void success(String tracing, long startTime) { + log.info("{}-Response Time:{} s", tracing, (System.currentTimeMillis() - startTime) / 1000.0); + } + +} \ No newline at end of file diff --git a/Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/WebClientInterceptor.kt b/Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/WebClientInterceptor.kt new file mode 100644 index 000000000..5dd922df3 --- /dev/null +++ b/Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/WebClientInterceptor.kt @@ -0,0 +1,49 @@ +package co.nilin.opex.utility.log.interceptor + +import io.netty.buffer.ByteBuf +import io.netty.buffer.ByteBufHolder +import io.netty.channel.ChannelHandlerContext +import io.netty.handler.logging.LoggingHandler +import io.netty.util.internal.PlatformDependent.allocateUninitializedArray +import java.nio.charset.Charset +import java.nio.charset.Charset.defaultCharset + + +class CustomLogger(clazz: Class<*>?) : LoggingHandler(clazz) { + + override fun format(ctx: ChannelHandlerContext?, event: String?, arg: Any?): String { + return when (arg) { + is ByteBuf -> { + val msg = arg + decode(msg, msg.readerIndex(), msg.readableBytes(), defaultCharset()) + } + is ByteBufHolder -> { + val msg = arg.content() + decode(msg, msg.readerIndex(), msg.readableBytes(), defaultCharset()) + } + else -> { + super.format(ctx, event, arg) + } + } + } + + private fun decode(src: ByteBuf, readerIndex: Int, len: Int, charset: Charset): String { + if (len != 0) { + val array: ByteArray + val offset: Int + if (src.hasArray()) { + array = src.array() + offset = src.arrayOffset() + readerIndex + } else { + array = allocateUninitializedArray(Math.max(len, 1024)) + offset = 0 + src.getBytes(readerIndex, array, 0, len) + } + return String(array, offset, len, charset) + } + return "" + } + + + +} \ No newline at end of file diff --git a/Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/decorator/PayloadBufferServerHttpRequestDecorator.java b/Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/decorator/PayloadBufferServerHttpRequestDecorator.java new file mode 100644 index 000000000..36cbd2287 --- /dev/null +++ b/Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/decorator/PayloadBufferServerHttpRequestDecorator.java @@ -0,0 +1,55 @@ +package co.nilin.opex.utility.log.interceptor.decorator; + +import co.nilin.opex.utility.log.interceptor.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpRequestDecorator; +import org.springframework.util.StringUtils; +import reactor.core.publisher.Flux; + + +import java.util.Optional; +import java.util.stream.Collectors; + +import static reactor.core.scheduler.Schedulers.single; + +public class PayloadBufferServerHttpRequestDecorator extends ServerHttpRequestDecorator { + + private Flux body; + private Logger log = LoggerFactory.getLogger(PayloadBufferServerHttpRequestDecorator.class); + + PayloadBufferServerHttpRequestDecorator(String tracing, ServerHttpRequest delegate) { + super(delegate); + final String path = delegate.getURI().getPath(); + final String query = delegate.getURI().getQuery(); + final String method = Optional.ofNullable(delegate.getMethod()).orElse(HttpMethod.GET).name(); + final String headers = delegate.getHeaders().entrySet() + .stream() + .map(entry -> " " + entry.getKey() + ": [" + String.join(";", entry.getValue()) + "]") + .collect(Collectors.joining("\n")); + final MediaType contentType = delegate.getHeaders().getContentType(); + if (log.isDebugEnabled()) { + log.debug("{}-" + + "HttpMethod : {}\n" + + "Uri : {}\n" + + "Headers : \n" + + "{}", tracing, method, path + (StringUtils.hasText(query) ? "?" + query : ""), headers); + } + Flux flux = super.getBody(); + if (LogUtils.legalLogMediaTypes.contains(contentType)) { + body = flux.publishOn(single()).map(dataBuffer -> LogUtils.loggingRequest(log, tracing, dataBuffer)); + } else { + body = flux; + } + } + + @Override + public Flux getBody() { + return body; + } + +} \ No newline at end of file diff --git a/Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/decorator/PayloadBufferServerHttpResponseDecorator.java b/Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/decorator/PayloadBufferServerHttpResponseDecorator.java new file mode 100644 index 000000000..90880f7b0 --- /dev/null +++ b/Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/decorator/PayloadBufferServerHttpResponseDecorator.java @@ -0,0 +1,58 @@ +package co.nilin.opex.utility.log.interceptor.decorator; + + +import co.nilin.opex.utility.log.interceptor.*; +import org.reactivestreams.*; +import org.slf4j.*; +import org.springframework.core.io.buffer.*; +import org.springframework.http.*; +import org.springframework.http.server.reactive.*; +import reactor.core.publisher.*; + +import java.util.stream.*; + +import static reactor.core.scheduler.Schedulers.*; + +public class PayloadBufferServerHttpResponseDecorator extends ServerHttpResponseDecorator { + private Logger log = LoggerFactory.getLogger(PayloadBufferServerHttpResponseDecorator.class); + + private String tracing; + + PayloadBufferServerHttpResponseDecorator(String tracing, ServerHttpResponse delegate) { + super(delegate); + this.tracing = tracing; + final String headers = delegate.getHeaders().entrySet() + .stream() + .map(entry -> " " + entry.getKey() + ": [" + String.join(";", entry.getValue()) + "]") + .collect(Collectors.joining("\n")); + if (log.isDebugEnabled()) { + log.debug("{}-" + + "Response Status : {} \n" + + "Response Headers : \n" + + "{}", tracing, delegate.getRawStatusCode(), headers); + } + } + + @Override + public Mono writeAndFlushWith(Publisher> body) { + return super.writeAndFlushWith(body); + } + + @SuppressWarnings("unchecked") + @Override + public Mono writeWith(Publisher body) { + final MediaType contentType = super.getHeaders().getContentType(); + if (LogUtils.legalLogMediaTypes.stream().anyMatch(mt -> mt.isCompatibleWith(contentType))) { + if (body instanceof Mono) { + final Mono monoBody = (Mono) body; + return super.writeWith(monoBody.publishOn(single()) + .map(dataBuffer -> LogUtils.loggingResponse(log, tracing, dataBuffer))); + } else if (body instanceof Flux) { + final Flux monoBody = (Flux) body; + return super.writeWith(monoBody.publishOn(single()) + .map(dataBuffer -> LogUtils.loggingResponse(log, tracing, dataBuffer))); + } + } + return super.writeWith(body); + } +} \ No newline at end of file diff --git a/Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/decorator/PayloadServerWebExchangeDecorator.java b/Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/decorator/PayloadServerWebExchangeDecorator.java new file mode 100644 index 000000000..95593ec04 --- /dev/null +++ b/Utility/logging-handler/src/main/kotlin/co/nilin/opex/utility/log/interceptor/decorator/PayloadServerWebExchangeDecorator.java @@ -0,0 +1,33 @@ +package co.nilin.opex.utility.log.interceptor.decorator; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.ServerWebExchangeDecorator; + +public class PayloadServerWebExchangeDecorator extends ServerWebExchangeDecorator { + + private Logger log = LoggerFactory.getLogger( PayloadServerWebExchangeDecorator.class); + + private PayloadBufferServerHttpRequestDecorator requestDecorator; + + private PayloadBufferServerHttpResponseDecorator responseDecorator; + + public PayloadServerWebExchangeDecorator(String tracing, ServerWebExchange delegate) { + super(delegate); + requestDecorator = new PayloadBufferServerHttpRequestDecorator(tracing, delegate.getRequest()); + responseDecorator = new PayloadBufferServerHttpResponseDecorator(tracing, delegate.getResponse()); + } + + @Override + public ServerHttpRequest getRequest() { + return requestDecorator; + } + + @Override + public ServerHttpResponse getResponse() { + return responseDecorator; + } +} \ No newline at end of file diff --git a/Wallet/pom.xml b/Wallet/pom.xml index b7d01cd20..cef5678c4 100644 --- a/Wallet/pom.xml +++ b/Wallet/pom.xml @@ -4,7 +4,7 @@ 4.0.0 co.nilin.opex wallets - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT wallets pom Wallet managment root of Opex diff --git a/Wallet/wallet-app/pom.xml b/Wallet/wallet-app/pom.xml index d75d4ee24..75143ea27 100644 --- a/Wallet/wallet-app/pom.xml +++ b/Wallet/wallet-app/pom.xml @@ -10,15 +10,16 @@ co.nilin.opex wallet-app - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT wallet-app Wallet managment app of Opex 1.8 1.4.31 - ${version} 2020.0.2 + ${version} + ${version} @@ -86,6 +87,16 @@ wallet-eventlistener-kafka ${wallet.version} + + co.nilin.opex + error-handler + ${utility.version} + + + co.nilin.opex + logging-handler + ${utility.version} + org.springframework.cloud spring-cloud-starter-consul-all @@ -112,11 +123,6 @@ springfox-boot-starter 3.0.0 - - co.nilin.opex - error-handler - 1.0-SNAPSHOT - diff --git a/Wallet/wallet-app/src/main/resources/application.yml b/Wallet/wallet-app/src/main/resources/application.yml index fad5e6431..1f82b108a 100644 --- a/Wallet/wallet-app/src/main/resources/application.yml +++ b/Wallet/wallet-app/src/main/resources/application.yml @@ -33,5 +33,7 @@ app: logging: level: org.apache.kafka: DEBUG + co.nilin: DEBUG + reactor.netty.http.client: DEBUG swagger.authUrl: https://api.opex.dev diff --git a/Wallet/wallet-core/pom.xml b/Wallet/wallet-core/pom.xml index e8e983127..366ecc638 100644 --- a/Wallet/wallet-core/pom.xml +++ b/Wallet/wallet-core/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex wallet-core - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT wallet-core Wallet managment of Opex diff --git a/Wallet/wallet-ports/wallet-eventlistener-kafka/pom.xml b/Wallet/wallet-ports/wallet-eventlistener-kafka/pom.xml index e33ae0781..7b8cba33b 100644 --- a/Wallet/wallet-ports/wallet-eventlistener-kafka/pom.xml +++ b/Wallet/wallet-ports/wallet-eventlistener-kafka/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex wallet-eventlistener-kafka - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT wallet-eventlistener-kafka Wallet kafka listener of Opex diff --git a/Wallet/wallet-ports/wallet-persister-postgres/pom.xml b/Wallet/wallet-ports/wallet-persister-postgres/pom.xml index 11e0682fa..51d4de352 100644 --- a/Wallet/wallet-ports/wallet-persister-postgres/pom.xml +++ b/Wallet/wallet-ports/wallet-persister-postgres/pom.xml @@ -10,7 +10,7 @@ co.nilin.opex wallet-persister-postgres - 0.0.1-SNAPSHOT + 1.0-SNAPSHOT wallet-persister-postgres Persist items of Opex wallet on Postgres From 2a10cb4a4ee74c7a83b56050c123a57b567b8712 Mon Sep 17 00:00:00 2001 From: maryarm Date: Tue, 24 Aug 2021 14:53:47 +0430 Subject: [PATCH 31/59] Fix #38-Modify poms for logging-handler --- Api/api-app/pom.xml | 11 +++++++++++ Api/api-ports/api-binance-rest/pom.xml | 18 ++++++++++++++++++ Utility/pom.xml | 1 + 3 files changed, 30 insertions(+) diff --git a/Api/api-app/pom.xml b/Api/api-app/pom.xml index d92bfdf3d..f430eb9c5 100644 --- a/Api/api-app/pom.xml +++ b/Api/api-app/pom.xml @@ -45,6 +45,17 @@ logging-handler ${utility.version} + + co.nilin.opex + error-handler + ${utility.version} + + + co.nilin.opex + interceptors + ${utility.version} + provided + co.nilin.opex accountant-core diff --git a/Api/api-ports/api-binance-rest/pom.xml b/Api/api-ports/api-binance-rest/pom.xml index 60f9f72ee..1484b63cc 100644 --- a/Api/api-ports/api-binance-rest/pom.xml +++ b/Api/api-ports/api-binance-rest/pom.xml @@ -36,6 +36,24 @@ ${api.version} provided + + co.nilin.opex + logging-handler + ${utility.version} + provided + + + co.nilin.opex + error-handler + ${utility.version} + provided + + + co.nilin.opex + interceptors + ${utility.version} + provided + org.springframework.boot spring-boot-starter-webflux diff --git a/Utility/pom.xml b/Utility/pom.xml index 1b8c0a40d..2a7f7fc15 100644 --- a/Utility/pom.xml +++ b/Utility/pom.xml @@ -12,6 +12,7 @@ error-handler + logging-handler interceptors \ No newline at end of file From e57eaf2d682fe81d34caac48c70bbd35c1475189 Mon Sep 17 00:00:00 2001 From: maryarm Date: Tue, 24 Aug 2021 14:57:14 +0430 Subject: [PATCH 32/59] Fix #40-For Throwable set response status to INTERNAL_SERVER_ERROR --- .../nilin/opex/utility/error/controller/ExceptionController.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/controller/ExceptionController.kt b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/controller/ExceptionController.kt index 0e91b3367..1d0ba0ca5 100644 --- a/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/controller/ExceptionController.kt +++ b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/controller/ExceptionController.kt @@ -15,6 +15,7 @@ import org.springframework.web.reactive.function.client.WebClientResponseExcepti import org.springframework.web.server.ServerWebInputException import java.nio.charset.StandardCharsets import java.util.* +import org.springframework.http.HttpStatus @RestControllerAdvice class ExceptionController( @@ -81,7 +82,7 @@ class ExceptionController( @ExceptionHandler(Throwable::class) fun handle(e: Throwable): ResponseEntity { logger.error("Generic error", e) - val opexException = OpexException(OpexError.InternalServerError) + val opexException = OpexException(status = HttpStatus.INTERNAL_SERVER_ERROR, error = OpexError.InternalServerError) val error = translator.translate(opexException) return response(error) } From 8905ca383b02d01daf7b5478b11c264076b49bbb Mon Sep 17 00:00:00 2001 From: maryarm Date: Tue, 24 Aug 2021 17:20:09 +0430 Subject: [PATCH 33/59] Fix #41: Set all required properties in json file --- .../src/main/resources/opex-realm.json | 3858 ++++++++++------- 1 file changed, 2264 insertions(+), 1594 deletions(-) diff --git a/UserManagement/keycloak-gateway/src/main/resources/opex-realm.json b/UserManagement/keycloak-gateway/src/main/resources/opex-realm.json index d85b18801..bccc3db87 100644 --- a/UserManagement/keycloak-gateway/src/main/resources/opex-realm.json +++ b/UserManagement/keycloak-gateway/src/main/resources/opex-realm.json @@ -1,1631 +1,2301 @@ { - "id" : "opex", - "realm" : "opex", - "notBefore" : 0, - "revokeRefreshToken" : false, - "refreshTokenMaxReuse" : 0, - "accessTokenLifespan" : 300, - "accessTokenLifespanForImplicitFlow" : 900, - "ssoSessionIdleTimeout" : 1800, - "ssoSessionMaxLifespan" : 36000, - "ssoSessionIdleTimeoutRememberMe" : 0, - "ssoSessionMaxLifespanRememberMe" : 0, - "offlineSessionIdleTimeout" : 2592000, - "offlineSessionMaxLifespanEnabled" : false, - "offlineSessionMaxLifespan" : 5184000, - "accessCodeLifespan" : 60, - "accessCodeLifespanUserAction" : 300, - "accessCodeLifespanLogin" : 1800, - "actionTokenGeneratedByAdminLifespan" : 43200, - "actionTokenGeneratedByUserLifespan" : 300, - "enabled" : true, - "sslRequired" : "none", - "registrationAllowed" : true, - "registrationEmailAsUsername" : false, - "rememberMe" : false, - "verifyEmail" : false, - "loginWithEmailAllowed" : true, - "duplicateEmailsAllowed" : false, - "resetPasswordAllowed" : true, - "editUsernameAllowed" : false, - "bruteForceProtected" : true, - "permanentLockout" : false, - "maxFailureWaitSeconds" : 900, - "minimumQuickLoginWaitSeconds" : 60, - "waitIncrementSeconds" : 60, - "quickLoginCheckMilliSeconds" : 1000, - "maxDeltaTimeSeconds" : 43200, - "failureFactor" : 30, - "roles" : { - "realm" : [ { - "id" : "3b6109f5-6e5a-4578-83c3-791ec3e2bf9e", - "name" : "offline_access", - "description" : "${role_offline-access}", - "composite" : false, - "clientRole" : false, - "containerId" : "opex", - "attributes" : { } - }, { - "id" : "0dd6a8c7-d669-4941-9ea1-521980e9c53f", - "name" : "uma_authorization", - "description" : "${role_uma_authorization}", - "composite" : false, - "clientRole" : false, - "containerId" : "opex", - "attributes" : { } - }, { - "id" : "ca962095-7f9b-49e2-a190-e391a0d4b704", - "name" : "user", - "composite" : false, - "clientRole" : false, - "containerId" : "opex", - "attributes" : { } - } ], - "client" : { - "newClient" : [ ], - "realm-management" : [ { - "id" : "5d00243f-ceec-4b0c-995e-d86d5b8a0ae6", - "name" : "view-clients", - "description" : "${role_view-clients}", - "composite" : true, - "composites" : { - "client" : { - "realm-management" : [ "query-clients" ] - } + "id": "opex", + "realm": "opex", + "notBefore": 0, + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 300, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "enabled": true, + "sslRequired": "none", + "registrationAllowed": true, + "registrationEmailAsUsername": false, + "rememberMe": false, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": true, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "roles": { + "realm": [ + { + "id": "3b6109f5-6e5a-4578-83c3-791ec3e2bf9e", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "opex", + "attributes": {} + }, + { + "id": "0dd6a8c7-d669-4941-9ea1-521980e9c53f", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "opex", + "attributes": {} + }, + { + "id": "ca962095-7f9b-49e2-a190-e391a0d4b704", + "name": "user", + "composite": false, + "clientRole": false, + "containerId": "opex", + "attributes": {} + } + ], + "client": { + "newClient": [], + "realm-management": [ + { + "id": "5d00243f-ceec-4b0c-995e-d86d5b8a0ae6", + "name": "view-clients", + "description": "${role_view-clients}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-clients" + ] + } + }, + "clientRole": true, + "containerId": "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes": {} + }, + { + "id": "941612de-bd85-47a5-8dfa-37c270dde28c", + "name": "view-authorization", + "description": "${role_view-authorization}", + "composite": false, + "clientRole": true, + "containerId": "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes": {} + }, + { + "id": "5ea9810d-63cc-4277-9b32-ba8a3d3c6091", + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "containerId": "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes": {} + }, + { + "id": "8b7b0dd8-350b-473e-b8cd-8acad34f1358", + "name": "query-clients", + "description": "${role_query-clients}", + "composite": false, + "clientRole": true, + "containerId": "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes": {} + }, + { + "id": "0f8e5ee8-b014-4b7c-9b69-50f46abcba5f", + "name": "query-groups", + "description": "${role_query-groups}", + "composite": false, + "clientRole": true, + "containerId": "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes": {} + }, + { + "id": "911b1489-9383-4734-b134-bf49bf992ce9", + "name": "manage-clients", + "description": "${role_manage-clients}", + "composite": false, + "clientRole": true, + "containerId": "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes": {} + }, + { + "id": "5d48274c-bd6b-4c26-ad54-f1a2254beac0", + "name": "view-realm", + "description": "${role_view-realm}", + "composite": false, + "clientRole": true, + "containerId": "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes": {} + }, + { + "id": "3ea43b64-316f-4693-8346-9ee78b24adaf", + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes": {} + }, + { + "id": "49735614-96ec-49b2-98fe-3af9bcd1a33a", + "name": "create-client", + "description": "${role_create-client}", + "composite": false, + "clientRole": true, + "containerId": "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes": {} + }, + { + "id": "e8f8c3cc-0ff1-4f72-a271-db6821a3cdb6", + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "containerId": "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes": {} + }, + { + "id": "387418b1-4f80-4b00-b9dd-805ca041f805", + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes": {} + }, + { + "id": "427c27d4-521a-464b-a0df-16d7f537e8d5", + "name": "realm-admin", + "description": "${role_realm-admin}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "view-clients", + "view-authorization", + "manage-realm", + "query-clients", + "query-groups", + "manage-clients", + "view-realm", + "manage-identity-providers", + "create-client", + "manage-users", + "view-identity-providers", + "query-users", + "query-realms", + "view-users", + "manage-authorization", + "impersonation", + "view-events", + "manage-events" + ] + } + }, + "clientRole": true, + "containerId": "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes": {} + }, + { + "id": "a574cf01-03e4-4573-ab9e-276d13a1ce8d", + "name": "query-users", + "description": "${role_query-users}", + "composite": false, + "clientRole": true, + "containerId": "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes": {} }, - "clientRole" : true, - "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", - "attributes" : { } - }, { - "id" : "941612de-bd85-47a5-8dfa-37c270dde28c", - "name" : "view-authorization", - "description" : "${role_view-authorization}", - "composite" : false, - "clientRole" : true, - "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", - "attributes" : { } - }, { - "id" : "5ea9810d-63cc-4277-9b32-ba8a3d3c6091", - "name" : "manage-realm", - "description" : "${role_manage-realm}", - "composite" : false, - "clientRole" : true, - "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", - "attributes" : { } - }, { - "id" : "8b7b0dd8-350b-473e-b8cd-8acad34f1358", - "name" : "query-clients", - "description" : "${role_query-clients}", - "composite" : false, - "clientRole" : true, - "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", - "attributes" : { } - }, { - "id" : "0f8e5ee8-b014-4b7c-9b69-50f46abcba5f", - "name" : "query-groups", - "description" : "${role_query-groups}", - "composite" : false, - "clientRole" : true, - "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", - "attributes" : { } - }, { - "id" : "911b1489-9383-4734-b134-bf49bf992ce9", - "name" : "manage-clients", - "description" : "${role_manage-clients}", - "composite" : false, - "clientRole" : true, - "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", - "attributes" : { } - }, { - "id" : "5d48274c-bd6b-4c26-ad54-f1a2254beac0", - "name" : "view-realm", - "description" : "${role_view-realm}", - "composite" : false, - "clientRole" : true, - "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", - "attributes" : { } - }, { - "id" : "3ea43b64-316f-4693-8346-9ee78b24adaf", - "name" : "manage-identity-providers", - "description" : "${role_manage-identity-providers}", - "composite" : false, - "clientRole" : true, - "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", - "attributes" : { } - }, { - "id" : "49735614-96ec-49b2-98fe-3af9bcd1a33a", - "name" : "create-client", - "description" : "${role_create-client}", - "composite" : false, - "clientRole" : true, - "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", - "attributes" : { } - }, { - "id" : "e8f8c3cc-0ff1-4f72-a271-db6821a3cdb6", - "name" : "manage-users", - "description" : "${role_manage-users}", - "composite" : false, - "clientRole" : true, - "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", - "attributes" : { } - }, { - "id" : "387418b1-4f80-4b00-b9dd-805ca041f805", - "name" : "view-identity-providers", - "description" : "${role_view-identity-providers}", - "composite" : false, - "clientRole" : true, - "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", - "attributes" : { } - }, { - "id" : "427c27d4-521a-464b-a0df-16d7f537e8d5", - "name" : "realm-admin", - "description" : "${role_realm-admin}", - "composite" : true, - "composites" : { - "client" : { - "realm-management" : [ "view-clients", "view-authorization", "query-groups", "manage-realm", "query-clients", "manage-clients", "view-realm", "manage-identity-providers", "create-client", "manage-users", "view-identity-providers", "query-users", "query-realms", "view-users", "impersonation", "manage-authorization", "manage-events", "view-events" ] + { + "id": "c3a253a8-a1b6-4d38-9677-f728f32482ad", + "name": "query-realms", + "description": "${role_query-realms}", + "composite": false, + "clientRole": true, + "containerId": "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes": {} + }, + { + "id": "f3cb93da-273e-419a-b2f4-93f09896abcf", + "name": "view-users", + "description": "${role_view-users}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-users", + "query-groups" + ] + } + }, + "clientRole": true, + "containerId": "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes": {} + }, + { + "id": "0332e99b-3dfc-4193-9e13-5728f8f3e6d6", + "name": "impersonation", + "description": "${role_impersonation}", + "composite": false, + "clientRole": true, + "containerId": "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes": {} + }, + { + "id": "6eedf2b7-50ef-4495-a89b-54aef751b7fa", + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "composite": false, + "clientRole": true, + "containerId": "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes": {} + }, + { + "id": "aac3def5-f193-4a6c-9065-1667a0746a8a", + "name": "manage-events", + "description": "${role_manage-events}", + "composite": false, + "clientRole": true, + "containerId": "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes": {} + }, + { + "id": "b690cb9c-0f4a-4be5-ade0-b40443d8149d", + "name": "view-events", + "description": "${role_view-events}", + "composite": false, + "clientRole": true, + "containerId": "6a4bfbd0-576d-4778-af56-56f876647355", + "attributes": {} + } + ], + "security-admin-console": [], + "admin-cli": [], + "account-console": [], + "broker": [ + { + "id": "397b5703-4c81-48fd-a24c-a7e8177ef657", + "name": "read-token", + "description": "${role_read-token}", + "composite": false, + "clientRole": true, + "containerId": "4b9609f0-48d1-4e71-9381-2ecec08616f9", + "attributes": {} + } + ], + "account": [ + { + "id": "8daa8096-d14e-4d1c-ad1f-83f822016aa1", + "name": "manage-account", + "description": "${role_manage-account}", + "composite": true, + "composites": { + "client": { + "account": [ + "manage-account-links" + ] + } + }, + "clientRole": true, + "containerId": "12eebf0b-a3eb-49f8-9ecf-173cf8a00145", + "attributes": {} + }, + { + "id": "cc86369d-55fc-47ed-9592-9e89116032c0", + "name": "view-consent", + "description": "${role_view-consent}", + "composite": false, + "clientRole": true, + "containerId": "12eebf0b-a3eb-49f8-9ecf-173cf8a00145", + "attributes": {} + }, + { + "id": "ca726012-91f9-4c58-bc7e-e435c9c244ab", + "name": "manage-consent", + "description": "${role_manage-consent}", + "composite": true, + "composites": { + "client": { + "account": [ + "view-consent" + ] + } + }, + "clientRole": true, + "containerId": "12eebf0b-a3eb-49f8-9ecf-173cf8a00145", + "attributes": {} + }, + { + "id": "f6839de1-e7fc-42bb-be60-578ea3d9361b", + "name": "view-applications", + "description": "${role_view-applications}", + "composite": false, + "clientRole": true, + "containerId": "12eebf0b-a3eb-49f8-9ecf-173cf8a00145", + "attributes": {} + }, + { + "id": "948269c7-a69c-4c82-a7f3-88868713dfd9", + "name": "manage-account-links", + "description": "${role_manage-account-links}", + "composite": false, + "clientRole": true, + "containerId": "12eebf0b-a3eb-49f8-9ecf-173cf8a00145", + "attributes": {} + }, + { + "id": "ee9cf17f-b9b1-4f4a-b521-83b5d1d7388b", + "name": "delete-account", + "description": "${role_delete-account}", + "composite": false, + "clientRole": true, + "containerId": "12eebf0b-a3eb-49f8-9ecf-173cf8a00145", + "attributes": {} + }, + { + "id": "aed18201-2433-4998-8fa3-0979b0b31c10", + "name": "view-profile", + "description": "${role_view-profile}", + "composite": false, + "clientRole": true, + "containerId": "12eebf0b-a3eb-49f8-9ecf-173cf8a00145", + "attributes": {} + } + ] + } + }, + "groups": [], + "defaultRoles": [ + "offline_access", + "uma_authorization" + ], + "requiredCredentials": [ + "password" + ], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpSupportedApplications": [ + "FreeOTP", + "Google Authenticator" + ], + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "users": [ + { + "id": "cb6af759-5e4f-42b3-86ea-b3754ce4d422", + "createdTimestamp": 1624136397065, + "username": "service-account-account-console", + "enabled": true, + "totp": false, + "emailVerified": false, + "serviceAccountClientId": "account-console", + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": [ + "offline_access", + "uma_authorization" + ], + "clientRoles": { + "realm-management": [ + "manage-users" + ], + "account": [ + "manage-account", + "view-profile" + ] + }, + "notBefore": 0, + "groups": [] + } + ], + "scopeMappings": [ + { + "client": "account-console", + "roles": [ + "offline_access", + "uma_authorization", + "user" + ] + }, + { + "clientScope": "offline_access", + "roles": [ + "offline_access" + ] + } + ], + "clientScopeMappings": { + "realm-management": [ + { + "client": "account-console", + "roles": [ + "impersonation", + "realm-admin", + "manage-users" + ] + } + ], + "account": [ + { + "client": "account-console", + "roles": [ + "manage-account" + ] + } + ] + }, + "clients": [ + { + "id": "12eebf0b-a3eb-49f8-9ecf-173cf8a00145", + "clientId": "account", + "name": "${client_account}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/opex/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "fae6f87e-5b66-435c-b5aa-fd42c7641604", + "defaultRoles": [ + "manage-account", + "view-profile" + ], + "redirectUris": [ + "/realms/opex/account/*", + "http://localhost:3000/*" + ], + "webOrigins": [ + "*" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.multivalued.roles": "false", + "saml.force.post.binding": "false", + "saml.encrypt": "false", + "saml.server.signature": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "backchannel.logout.session.required": "false", + "client_credentials.use_refresh_token": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "trust", + "role_list", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "ceabb7ca-b063-4755-90fb-8de2cc7e5e00", + "clientId": "account-console", + "name": "${client_account-console}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/opex/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "fae6f87e-5b66-435c-b5aa-fd42c7641604", + "redirectUris": [ + "http://localhost:3000/*", + "/realms/opex/account/*", + "https://opex.dev/*" + ], + "webOrigins": [ + "+" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": true, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.multivalued.roles": "false", + "saml.force.post.binding": "false", + "saml.encrypt": "false", + "saml.server.signature": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "backchannel.logout.session.required": "false", + "client_credentials.use_refresh_token": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "pkce.code.challenge.method": "S256", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "b83a852e-e3e3-46dc-9e76-3d175fc4a5d4", + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientHost", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientHost", + "jsonType.label": "String" } }, - "clientRole" : true, - "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", - "attributes" : { } - }, { - "id" : "a574cf01-03e4-4573-ab9e-276d13a1ce8d", - "name" : "query-users", - "description" : "${role_query-users}", - "composite" : false, - "clientRole" : true, - "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", - "attributes" : { } - }, { - "id" : "c3a253a8-a1b6-4d38-9677-f728f32482ad", - "name" : "query-realms", - "description" : "${role_query-realms}", - "composite" : false, - "clientRole" : true, - "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", - "attributes" : { } - }, { - "id" : "f3cb93da-273e-419a-b2f4-93f09896abcf", - "name" : "view-users", - "description" : "${role_view-users}", - "composite" : true, - "composites" : { - "client" : { - "realm-management" : [ "query-users", "query-groups" ] + { + "id": "6b335962-bc9b-4095-ad36-48163e443a6f", + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientId", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientId", + "jsonType.label": "String" } }, - "clientRole" : true, - "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", - "attributes" : { } - }, { - "id" : "6eedf2b7-50ef-4495-a89b-54aef751b7fa", - "name" : "manage-authorization", - "description" : "${role_manage-authorization}", - "composite" : false, - "clientRole" : true, - "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", - "attributes" : { } - }, { - "id" : "0332e99b-3dfc-4193-9e13-5728f8f3e6d6", - "name" : "impersonation", - "description" : "${role_impersonation}", - "composite" : false, - "clientRole" : true, - "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", - "attributes" : { } - }, { - "id" : "b690cb9c-0f4a-4be5-ade0-b40443d8149d", - "name" : "view-events", - "description" : "${role_view-events}", - "composite" : false, - "clientRole" : true, - "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", - "attributes" : { } - }, { - "id" : "aac3def5-f193-4a6c-9065-1667a0746a8a", - "name" : "manage-events", - "description" : "${role_manage-events}", - "composite" : false, - "clientRole" : true, - "containerId" : "6a4bfbd0-576d-4778-af56-56f876647355", - "attributes" : { } - } ], - "security-admin-console" : [ ], - "admin-cli" : [ ], - "broker" : [ { - "id" : "397b5703-4c81-48fd-a24c-a7e8177ef657", - "name" : "read-token", - "description" : "${role_read-token}", - "composite" : false, - "clientRole" : true, - "containerId" : "4b9609f0-48d1-4e71-9381-2ecec08616f9", - "attributes" : { } - } ], - "account" : [ { - "id" : "8daa8096-d14e-4d1c-ad1f-83f822016aa1", - "name" : "manage-account", - "description" : "${role_manage-account}", - "composite" : true, - "composites" : { - "client" : { - "account" : [ "manage-account-links" ] + { + "id": "4c6842a7-9120-49b4-92eb-6cd4a3ff742d", + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientAddress", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientAddress", + "jsonType.label": "String" } }, - "clientRole" : true, - "containerId" : "12eebf0b-a3eb-49f8-9ecf-173cf8a00145", - "attributes" : { } - }, { - "id" : "948269c7-a69c-4c82-a7f3-88868713dfd9", - "name" : "manage-account-links", - "description" : "${role_manage-account-links}", - "composite" : false, - "clientRole" : true, - "containerId" : "12eebf0b-a3eb-49f8-9ecf-173cf8a00145", - "attributes" : { } - }, { - "id" : "aed18201-2433-4998-8fa3-0979b0b31c10", - "name" : "view-profile", - "description" : "${role_view-profile}", - "composite" : false, - "clientRole" : true, - "containerId" : "12eebf0b-a3eb-49f8-9ecf-173cf8a00145", - "attributes" : { } - } ] + { + "id": "6551bb7e-54af-455e-8de6-0e5acb1b3527", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ], + "defaultClientScopes": [ + "trust", + "web-origins", + "role_list", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "13d76feb-d762-4409-bb84-7a75bc395a61", + "clientId": "admin-cli", + "name": "${client_admin-cli}", + "rootUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "redirectUris": [], + "webOrigins": [ + "*" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "access.token.lifespan": "3600", + "saml.multivalued.roles": "false", + "saml.force.post.binding": "false", + "saml.encrypt": "false", + "saml.server.signature": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "backchannel.logout.session.required": "false", + "client_credentials.use_refresh_token": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "trust", + "role_list", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "4b9609f0-48d1-4e71-9381-2ecec08616f9", + "clientId": "broker", + "name": "${client_broker}", + "rootUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "redirectUris": [ + "http://localhost:3000/*", + "https://opex.dev/*" + ], + "webOrigins": [ + "*" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.multivalued.roles": "false", + "saml.force.post.binding": "false", + "saml.encrypt": "false", + "saml.server.signature": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "backchannel.logout.session.required": "false", + "client_credentials.use_refresh_token": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "trust", + "role_list", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "b88ce206-63d6-43b6-87c9-ea09d8c02f32", + "clientId": "newClient", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "redirectUris": [ + "http://localhost:8082/new-client/login/oauth2/code/custom", + "http://localhost:3000/*", + "http://localhost:8089/auth/redirect/", + "https://opex.dev/*" + ], + "webOrigins": [ + "+" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": true, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "saml.server.signature": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "backchannel.logout.session.required": "false", + "client_credentials.use_refresh_token": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [ + "trust", + "role_list", + "profile" + ], + "optionalClientScopes": [ + "web-origins", + "address", + "read", + "phone", + "roles", + "offline_access", + "microprofile-jwt", + "write", + "email" + ] + }, + { + "id": "6a4bfbd0-576d-4778-af56-56f876647355", + "clientId": "realm-management", + "name": "${client_realm-management}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "role_list", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "8e358d2f-b085-4243-8e6e-c175431e5eeb", + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "rootUrl": "${authAdminUrl}", + "baseUrl": "/admin/opex/console/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "redirectUris": [ + "/admin/opex/console/*" + ], + "webOrigins": [ + "+" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.multivalued.roles": "false", + "saml.force.post.binding": "false", + "saml.encrypt": "false", + "saml.server.signature": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "backchannel.logout.session.required": "false", + "client_credentials.use_refresh_token": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "pkce.code.challenge.method": "S256", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "9cfca9ee-493d-4b5e-8170-2d364149de59", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "role_list", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] } - }, - "groups" : [ ], - "defaultRoles" : [ "uma_authorization", "offline_access" ], - "requiredCredentials" : [ "password" ], - "otpPolicyType" : "totp", - "otpPolicyAlgorithm" : "HmacSHA1", - "otpPolicyInitialCounter" : 0, - "otpPolicyDigits" : 6, - "otpPolicyLookAheadWindow" : 1, - "otpPolicyPeriod" : 30, - "otpSupportedApplications" : [ "FreeOTP", "Google Authenticator" ], - "webAuthnPolicyRpEntityName" : "keycloak", - "webAuthnPolicySignatureAlgorithms" : [ "ES256" ], - "webAuthnPolicyRpId" : "", - "webAuthnPolicyAttestationConveyancePreference" : "not specified", - "webAuthnPolicyAuthenticatorAttachment" : "not specified", - "webAuthnPolicyRequireResidentKey" : "not specified", - "webAuthnPolicyUserVerificationRequirement" : "not specified", - "webAuthnPolicyCreateTimeout" : 0, - "webAuthnPolicyAvoidSameAuthenticatorRegister" : false, - "webAuthnPolicyAcceptableAaguids" : [ ], - "users" : [ { - "id" : "a5461470-33eb-4b2d-82d4-b0484e96ad7f", - "createdTimestamp" : 1574174706812, - "username" : "john@test.com", - "enabled" : true, - "totp" : false, - "emailVerified" : false, - "credentials" : [ { - "id" : "024b62f1-3af0-49fc-a15f-e843b72e00df", - "type" : "password", - "createdDate" : 1574174719522, - "secretData" : "{\"value\":\"tfHk9estQfa27osLCY2QelpkL6Sm6rPS+iQ33ytPSD4p10Fk7ophI7ChERAnzDjVSmvdsZAttsg2Yr+8F4Gzfw==\",\"salt\":\"goh99WgI7T1ZuGdrKzNAeQ==\"}", - "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\"}" - } ], - "disableableCredentialTypes" : [ ], - "requiredActions" : [ ], - "realmRoles" : [ "offline_access", "uma_authorization" ], - "clientRoles" : { - "account" : [ "manage-account", "view-profile" ] + ], + "clientScopes": [ + { + "id": "77c7e29d-1a22-4419-bbfb-4a62bb033449", + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${addressScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "94e1879d-b49e-4178-96e0-bf8d7f32c160", + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] }, - "notBefore" : 0, - "groups" : [ ] - }, { - "id" : "22a4d9fe-194c-4c6e-841a-8a55b402459f", - "createdTimestamp" : 1580237252522, - "username" : "mike@other.com", - "enabled" : true, - "totp" : false, - "emailVerified" : false, - "credentials" : [ { - "id" : "73c8d1ca-d548-4e1a-a82d-2c1d0d3ee017", - "type" : "password", - "createdDate" : 1580237291489, - "secretData" : "{\"value\":\"fea4QiAzqmOctQGtJrKvoPRIUKLdQ5UKFIEPEQ7PYjTjxvucDZVk8yzXNfsDHvgpZ2rWiwsACgvsymdHnmru9w==\",\"salt\":\"nu5yJh98jz8cGLnLx36uvg==\"}", - "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\"}" - } ], - "disableableCredentialTypes" : [ ], - "requiredActions" : [ ], - "realmRoles" : [ "offline_access", "uma_authorization" ], - "clientRoles" : { - "account" : [ "manage-account", "view-profile" ] + { + "id": "b3526ac1-10e2-4344-8621-9c5a0853e97a", + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${emailScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "d30270dc-baa6-455a-8ff6-ddccf8a78d86", + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + }, + { + "id": "f5b1684d-e479-4134-8578-457fa64717da", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + } + ] }, - "notBefore" : 0, - "groups" : [ ] - } ], - "scopeMappings" : [ { - "clientScope" : "offline_access", - "roles" : [ "offline_access" ] - } ], - "clients" : [ { - "id" : "12eebf0b-a3eb-49f8-9ecf-173cf8a00145", - "clientId" : "account", - "name" : "${client_account}", - "rootUrl" : "${authBaseUrl}", - "baseUrl" : "/realms/opex/account/", - "surrogateAuthRequired" : false, - "enabled" : true, - "clientAuthenticatorType" : "client-secret", - "secret" : "**********", - "defaultRoles" : [ "manage-account", "view-profile" ], - "redirectUris" : [ "/realms/opex/account/*" ], - "webOrigins" : [ ], - "notBefore" : 0, - "bearerOnly" : false, - "consentRequired" : false, - "standardFlowEnabled" : true, - "implicitFlowEnabled" : false, - "directAccessGrantsEnabled" : false, - "serviceAccountsEnabled" : false, - "publicClient" : false, - "frontchannelLogout" : false, - "protocol" : "openid-connect", - "attributes" : { }, - "authenticationFlowBindingOverrides" : { }, - "fullScopeAllowed" : false, - "nodeReRegistrationTimeout" : 0, - "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email", "trust" ], - "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] - }, { - "id" : "13d76feb-d762-4409-bb84-7a75bc395a61", - "clientId" : "admin-cli", - "name" : "${client_admin-cli}", - "surrogateAuthRequired" : false, - "enabled" : true, - "clientAuthenticatorType" : "client-secret", - "secret" : "**********", - "redirectUris" : [ ], - "webOrigins" : [ ], - "notBefore" : 0, - "bearerOnly" : false, - "consentRequired" : false, - "standardFlowEnabled" : false, - "implicitFlowEnabled" : false, - "directAccessGrantsEnabled" : true, - "serviceAccountsEnabled" : false, - "publicClient" : true, - "frontchannelLogout" : false, - "protocol" : "openid-connect", - "attributes" : { }, - "authenticationFlowBindingOverrides" : { }, - "fullScopeAllowed" : false, - "nodeReRegistrationTimeout" : 0, - "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email", "trust" ], - "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] - }, { - "id" : "4b9609f0-48d1-4e71-9381-2ecec08616f9", - "clientId" : "broker", - "name" : "${client_broker}", - "surrogateAuthRequired" : false, - "enabled" : true, - "clientAuthenticatorType" : "client-secret", - "secret" : "**********", - "redirectUris" : [ ], - "webOrigins" : [ ], - "notBefore" : 0, - "bearerOnly" : false, - "consentRequired" : false, - "standardFlowEnabled" : true, - "implicitFlowEnabled" : false, - "directAccessGrantsEnabled" : false, - "serviceAccountsEnabled" : false, - "publicClient" : false, - "frontchannelLogout" : false, - "protocol" : "openid-connect", - "attributes" : { }, - "authenticationFlowBindingOverrides" : { }, - "fullScopeAllowed" : false, - "nodeReRegistrationTimeout" : 0, - "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email", "trust" ], - "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] - }, { - "id" : "b88ce206-63d6-43b6-87c9-ea09d8c02f32", - "clientId" : "newClient", - "surrogateAuthRequired" : false, - "enabled" : true, - "clientAuthenticatorType" : "client-secret", - "secret" : "newClientSecret", - "redirectUris" : [ "http://localhost:8082/new-client/login/oauth2/code/custom", - "http://localhost:8089/", - "http://localhost:8089/auth/redirect/"], - "webOrigins" : [ "+" ], - "notBefore" : 0, - "bearerOnly" : false, - "consentRequired" : false, - "standardFlowEnabled" : true, - "implicitFlowEnabled" : true, - "directAccessGrantsEnabled" : true, - "serviceAccountsEnabled" : false, - "publicClient" : false, - "frontchannelLogout" : false, - "protocol" : "openid-connect", - "attributes" : { - "saml.assertion.signature" : "false", - "saml.force.post.binding" : "false", - "saml.multivalued.roles" : "false", - "saml.encrypt" : "false", - "saml.server.signature" : "false", - "saml.server.signature.keyinfo.ext" : "false", - "exclude.session.state.from.auth.response" : "false", - "saml_force_name_id_format" : "false", - "saml.client.signature" : "false", - "tls.client.certificate.bound.access.tokens" : "false", - "saml.authnstatement" : "false", - "display.on.consent.screen" : "false", - "saml.onetimeuse.condition" : "false" + { + "id": "c658ae14-e96a-4745-b21b-2ed5c4c63f5f", + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "959521bc-5ffd-465b-95f2-5b0c20d1909c", + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + }, + { + "id": "07b8550c-b298-4cce-9ffb-900182575b76", + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "userinfo.token.claim": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + } + ] }, - "authenticationFlowBindingOverrides" : { }, - "fullScopeAllowed" : true, - "nodeReRegistrationTimeout" : -1, - "defaultClientScopes" : [ "role_list", "profile", "trust" ], - "optionalClientScopes" : [ "web-origins", "address", "read", "phone", "roles", "offline_access", "microprofile-jwt", "write", "email" ] - }, { - "id" : "6a4bfbd0-576d-4778-af56-56f876647355", - "clientId" : "realm-management", - "name" : "${client_realm-management}", - "surrogateAuthRequired" : false, - "enabled" : true, - "clientAuthenticatorType" : "client-secret", - "secret" : "**********", - "redirectUris" : [ ], - "webOrigins" : [ ], - "notBefore" : 0, - "bearerOnly" : true, - "consentRequired" : false, - "standardFlowEnabled" : true, - "implicitFlowEnabled" : false, - "directAccessGrantsEnabled" : false, - "serviceAccountsEnabled" : false, - "publicClient" : false, - "frontchannelLogout" : false, - "protocol" : "openid-connect", - "attributes" : { }, - "authenticationFlowBindingOverrides" : { }, - "fullScopeAllowed" : false, - "nodeReRegistrationTimeout" : 0, - "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email" ], - "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] - }, { - "id" : "8e358d2f-b085-4243-8e6e-c175431e5eeb", - "clientId" : "security-admin-console", - "name" : "${client_security-admin-console}", - "rootUrl" : "${authAdminUrl}", - "baseUrl" : "/admin/opex/console/", - "surrogateAuthRequired" : false, - "enabled" : true, - "clientAuthenticatorType" : "client-secret", - "secret" : "**********", - "redirectUris" : [ "/admin/opex/console/*" ], - "webOrigins" : [ "+" ], - "notBefore" : 0, - "bearerOnly" : false, - "consentRequired" : false, - "standardFlowEnabled" : true, - "implicitFlowEnabled" : false, - "directAccessGrantsEnabled" : false, - "serviceAccountsEnabled" : false, - "publicClient" : true, - "frontchannelLogout" : false, - "protocol" : "openid-connect", - "attributes" : { }, - "authenticationFlowBindingOverrides" : { }, - "fullScopeAllowed" : false, - "nodeReRegistrationTimeout" : 0, - "protocolMappers" : [ { - "id" : "9cfca9ee-493d-4b5e-8170-2d364149de59", - "name" : "locale", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "userinfo.token.claim" : "true", - "user.attribute" : "locale", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "locale", - "jsonType.label" : "String" + { + "id": "569b3d44-4ecd-4768-a58c-70ff38f4b4fe", + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" } - } ], - "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email" ], - "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] - } ], - "clientScopes" : [ { - "id" : "77c7e29d-1a22-4419-bbfb-4a62bb033449", - "name" : "address", - "description" : "OpenID Connect built-in scope: address", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "true", - "display.on.consent.screen" : "true", - "consent.screen.text" : "${addressScopeConsentText}" }, - "protocolMappers" : [ { - "id" : "94e1879d-b49e-4178-96e0-bf8d7f32c160", - "name" : "address", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-address-mapper", - "consentRequired" : false, - "config" : { - "user.attribute.formatted" : "formatted", - "user.attribute.country" : "country", - "user.attribute.postal_code" : "postal_code", - "userinfo.token.claim" : "true", - "user.attribute.street" : "street", - "id.token.claim" : "true", - "user.attribute.region" : "region", - "access.token.claim" : "true", - "user.attribute.locality" : "locality" - } - } ] - }, { - "id" : "b3526ac1-10e2-4344-8621-9c5a0853e97a", - "name" : "email", - "description" : "OpenID Connect built-in scope: email", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "true", - "display.on.consent.screen" : "true", - "consent.screen.text" : "${emailScopeConsentText}" + { + "id": "a3e7b19d-df6c-437e-9eea-06fec1becb2f", + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${phoneScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "72a070f7-4363-4c88-8153-6fd2d12b9b04", + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + }, + { + "id": "24b42c6d-a93c-4aa1-9a03-2a2b55954c13", + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + } + ] }, - "protocolMappers" : [ { - "id" : "d30270dc-baa6-455a-8ff6-ddccf8a78d86", - "name" : "email verified", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-property-mapper", - "consentRequired" : false, - "config" : { - "userinfo.token.claim" : "true", - "user.attribute" : "emailVerified", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "email_verified", - "jsonType.label" : "boolean" - } - }, { - "id" : "f5b1684d-e479-4134-8578-457fa64717da", - "name" : "email", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-property-mapper", - "consentRequired" : false, - "config" : { - "userinfo.token.claim" : "true", - "user.attribute" : "email", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "email", - "jsonType.label" : "String" - } - } ] - }, { - "id" : "c658ae14-e96a-4745-b21b-2ed5c4c63f5f", - "name" : "microprofile-jwt", - "description" : "Microprofile - JWT built-in scope", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "true", - "display.on.consent.screen" : "false" + { + "id": "ba8c9950-fd0b-4434-8be6-b58456d7b6d4", + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${profileScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "0a9ddd71-309c-40f0-8ea6-a0791070c6ed", + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "id": "fbf53bbd-1ad0-4bf8-8030-50f81696d8ee", + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "id": "423be2cd-42c0-462e-9030-18f9b28ff2d3", + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "id": "53eb9006-4b81-474a-8b60-80f775d54b63", + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "id": "4d8bc82a-eaeb-499e-8eb2-0f1dcbe91699", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "id": "d3b25485-4042-419d-afff-cfd63a76e229", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "422cfa5a-f2f4-4f36-82df-91b47ae1ea50", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "3f2863c1-d98d-45b5-b08f-af9c4d9c10f8", + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "id": "c98c063d-eee4-41a0-9130-595afd709d1f", + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + }, + { + "id": "8dbed80a-d672-4185-8dda-4bba2a56ec83", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "5e5c690c-93cf-489d-a054-b109eab8911b", + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "String" + } + }, + { + "id": "3b985202-af8a-42f1-ac5f-0966a404f5d7", + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + }, + { + "id": "6eafd1b3-7121-4919-ad1e-039fa58acc32", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "73cba925-8c31-443f-9601-b1514e6396c1", + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + } + ] }, - "protocolMappers" : [ { - "id" : "959521bc-5ffd-465b-95f2-5b0c20d1909c", - "name" : "upn", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-property-mapper", - "consentRequired" : false, - "config" : { - "userinfo.token.claim" : "true", - "user.attribute" : "username", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "upn", - "jsonType.label" : "String" - } - }, { - "id" : "07b8550c-b298-4cce-9ffb-900182575b76", - "name" : "groups", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-realm-role-mapper", - "consentRequired" : false, - "config" : { - "multivalued" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "foo", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "groups", - "jsonType.label" : "String" + { + "id": "c1a2eb23-25c6-4be7-a791-bbdca99c83f7", + "name": "read", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true" } - } ] - }, { - "id" : "569b3d44-4ecd-4768-a58c-70ff38f4b4fe", - "name" : "offline_access", - "description" : "OpenID Connect built-in scope: offline_access", - "protocol" : "openid-connect", - "attributes" : { - "consent.screen.text" : "${offlineAccessScopeConsentText}", - "display.on.consent.screen" : "true" - } - }, { - "id" : "a3e7b19d-df6c-437e-9eea-06fec1becb2f", - "name" : "phone", - "description" : "OpenID Connect built-in scope: phone", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "true", - "display.on.consent.screen" : "true", - "consent.screen.text" : "${phoneScopeConsentText}" }, - "protocolMappers" : [ { - "id" : "72a070f7-4363-4c88-8153-6fd2d12b9b04", - "name" : "phone number verified", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "userinfo.token.claim" : "true", - "user.attribute" : "phoneNumberVerified", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "phone_number_verified", - "jsonType.label" : "boolean" - } - }, { - "id" : "24b42c6d-a93c-4aa1-9a03-2a2b55954c13", - "name" : "phone number", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "userinfo.token.claim" : "true", - "user.attribute" : "phoneNumber", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "phone_number", - "jsonType.label" : "String" - } - } ] - }, { - "id" : "ba8c9950-fd0b-4434-8be6-b58456d7b6d4", - "name" : "profile", - "description" : "OpenID Connect built-in scope: profile", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "true", - "display.on.consent.screen" : "true", - "consent.screen.text" : "${profileScopeConsentText}" + { + "id": "18e141bf-dabe-4858-879c-dbc439cdead4", + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "10cbe37f-0198-4d65-bc8a-bfe5ad8145d1", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] }, - "protocolMappers" : [ { - "id" : "0a9ddd71-309c-40f0-8ea6-a0791070c6ed", - "name" : "profile", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "userinfo.token.claim" : "true", - "user.attribute" : "profile", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "profile", - "jsonType.label" : "String" - } - }, { - "id" : "fbf53bbd-1ad0-4bf8-8030-50f81696d8ee", - "name" : "nickname", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "userinfo.token.claim" : "true", - "user.attribute" : "nickname", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "nickname", - "jsonType.label" : "String" - } - }, { - "id" : "423be2cd-42c0-462e-9030-18f9b28ff2d3", - "name" : "gender", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "userinfo.token.claim" : "true", - "user.attribute" : "gender", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "gender", - "jsonType.label" : "String" - } - }, { - "id" : "53eb9006-4b81-474a-8b60-80f775d54b63", - "name" : "picture", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "userinfo.token.claim" : "true", - "user.attribute" : "picture", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "picture", - "jsonType.label" : "String" - } - }, { - "id" : "4d8bc82a-eaeb-499e-8eb2-0f1dcbe91699", - "name" : "locale", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "userinfo.token.claim" : "true", - "user.attribute" : "locale", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "locale", - "jsonType.label" : "String" - } - }, { - "id" : "d3b25485-4042-419d-afff-cfd63a76e229", - "name" : "full name", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-full-name-mapper", - "consentRequired" : false, - "config" : { - "id.token.claim" : "true", - "access.token.claim" : "true", - "userinfo.token.claim" : "true" - } - }, { - "id" : "422cfa5a-f2f4-4f36-82df-91b47ae1ea50", - "name" : "given name", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-property-mapper", - "consentRequired" : false, - "config" : { - "userinfo.token.claim" : "true", - "user.attribute" : "firstName", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "given_name", - "jsonType.label" : "String" - } - }, { - "id" : "3f2863c1-d98d-45b5-b08f-af9c4d9c10f8", - "name" : "website", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "userinfo.token.claim" : "true", - "user.attribute" : "website", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "website", - "jsonType.label" : "String" - } - }, { - "id" : "c98c063d-eee4-41a0-9130-595afd709d1f", - "name" : "middle name", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "userinfo.token.claim" : "true", - "user.attribute" : "middleName", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "middle_name", - "jsonType.label" : "String" - } - }, { - "id" : "8dbed80a-d672-4185-8dda-4bba2a56ec83", - "name" : "family name", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-property-mapper", - "consentRequired" : false, - "config" : { - "userinfo.token.claim" : "true", - "user.attribute" : "lastName", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "family_name", - "jsonType.label" : "String" - } - }, { - "id" : "5e5c690c-93cf-489d-a054-b109eab8911b", - "name" : "updated at", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "userinfo.token.claim" : "true", - "user.attribute" : "updatedAt", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "updated_at", - "jsonType.label" : "String" - } - }, { - "id" : "3b985202-af8a-42f1-ac5f-0966a404f5d7", - "name" : "birthdate", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "userinfo.token.claim" : "true", - "user.attribute" : "birthdate", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "birthdate", - "jsonType.label" : "String" - } - }, { - "id" : "6eafd1b3-7121-4919-ad1e-039fa58acc32", - "name" : "username", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-property-mapper", - "consentRequired" : false, - "config" : { - "userinfo.token.claim" : "true", - "user.attribute" : "username", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "preferred_username", - "jsonType.label" : "String" - } - }, { - "id" : "73cba925-8c31-443f-9601-b1514e6396c1", - "name" : "zoneinfo", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "userinfo.token.claim" : "true", - "user.attribute" : "zoneinfo", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "zoneinfo", - "jsonType.label" : "String" - } - } ] - }, { - "id" : "c1a2eb23-25c6-4be7-a791-bbdca99c83f7", - "name" : "read", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "true", - "display.on.consent.screen" : "true" - } - }, { - "id" : "18e141bf-dabe-4858-879c-dbc439cdead4", - "name" : "role_list", - "description" : "SAML role list", - "protocol" : "saml", - "attributes" : { - "consent.screen.text" : "${samlRoleListScopeConsentText}", - "display.on.consent.screen" : "true" + { + "id": "111ed87a-5fd3-4cee-96df-8dbfb88cfdc0", + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "true", + "consent.screen.text": "${rolesScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "24924d8d-6071-4a93-b40f-326176cb335e", + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "id": "2f6a9bdf-3758-484c-996d-e4f93555559f", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + }, + { + "id": "804d4798-d9a3-4fd3-8b28-d12142e8cb3d", + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String", + "multivalued": "true" + } + } + ] }, - "protocolMappers" : [ { - "id" : "10cbe37f-0198-4d65-bc8a-bfe5ad8145d1", - "name" : "role list", - "protocol" : "saml", - "protocolMapper" : "saml-role-list-mapper", - "consentRequired" : false, - "config" : { - "single" : "false", - "attribute.nameformat" : "Basic", - "attribute.name" : "Role" - } - } ] - }, { - "id" : "111ed87a-5fd3-4cee-96df-8dbfb88cfdc0", - "name" : "roles", - "description" : "OpenID Connect scope for add user roles to the access token", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "false", - "display.on.consent.screen" : "true", - "consent.screen.text" : "${rolesScopeConsentText}" + { + "id": "51d49314-b511-43e0-9258-bfb873758a78", + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false", + "consent.screen.text": "" + }, + "protocolMappers": [ + { + "id": "2b384cd0-9e85-4a87-8eeb-2b480b0587b7", + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": {} + } + ] }, - "protocolMappers" : [ { - "id" : "24924d8d-6071-4a93-b40f-326176cb335e", - "name" : "realm roles", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-realm-role-mapper", - "consentRequired" : false, - "config" : { - "user.attribute" : "foo", - "access.token.claim" : "true", - "claim.name" : "realm_access.roles", - "jsonType.label" : "String", - "multivalued" : "true" - } - }, { - "id" : "2f6a9bdf-3758-484c-996d-e4f93555559f", - "name" : "audience resolve", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-audience-resolve-mapper", - "consentRequired" : false, - "config" : { } - }, { - "id" : "804d4798-d9a3-4fd3-8b28-d12142e8cb3d", - "name" : "client roles", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-client-role-mapper", - "consentRequired" : false, - "config" : { - "user.attribute" : "foo", - "access.token.claim" : "true", - "claim.name" : "resource_access.${client_id}.roles", - "jsonType.label" : "String", - "multivalued" : "true" + { + "id": "c3e253fb-7361-47cf-9d4a-86245686fdf1", + "name": "write", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true" } - } ] - }, { - "id" : "51d49314-b511-43e0-9258-bfb873758a78", - "name" : "web-origins", - "description" : "OpenID Connect scope for add allowed web origins to the access token", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "false", - "display.on.consent.screen" : "false", - "consent.screen.text" : "" }, - "protocolMappers" : [ { - "id" : "2b384cd0-9e85-4a87-8eeb-2b480b0587b7", - "name" : "allowed web origins", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-allowed-origins-mapper", - "consentRequired" : false, - "config" : { } - } ] - }, { - "id" : "c3e253fb-7361-47cf-9d4a-86245686fdf1", - "name" : "write", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "true", - "display.on.consent.screen" : "true" - } - }, { - "id" : "d4e253fb-7361-47cf-9d4a-86245686fdf2", - "name" : "trust", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "true", - "display.on.consent.screen" : "true" + "id": "d4e253fb-7361-47cf-9d4a-86245686fdf2", + "name": "trust", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true" } - }], - "defaultDefaultClientScopes" : [ "roles", "role_list", "web-origins", "email", "profile", "trust" ], - "defaultOptionalClientScopes" : [ "offline_access", "address", "phone", "microprofile-jwt" ], - "browserSecurityHeaders" : { - "contentSecurityPolicyReportOnly" : "", - "xContentTypeOptions" : "nosniff", - "xRobotsTag" : "none", - "xFrameOptions" : "SAMEORIGIN", - "contentSecurityPolicy" : "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", - "xXSSProtection" : "1; mode=block", - "strictTransportSecurity" : "max-age=31536000; includeSubDomains" + } + ], + "defaultDefaultClientScopes": [ + "roles", + "role_list", + "web-origins", + "email", + "profile", + "trust" + ], + "defaultOptionalClientScopes": [ + "offline_access", + "address", + "phone", + "microprofile-jwt" + ], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection": "1; mode=block", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" }, - "smtpServer" : { + "smtpServer": { + "password": "642467973026C6F093FB1E39C4BFC0D15042", + "auth": "true", + "port": "2525", "host": "smtp.elasticemail.com", - "port": 2525, "from": "for.demo.purpose.only@opex.dev", - "auth": true, - "user": "for.demo.purpose.only@opex.dev", - "password": "642467973026C6F093FB1E39C4BFC0D15042" + "user": "for.demo.purpose.only@opex.dev" }, - "eventsEnabled" : false, - "eventsListeners" : [ "jboss-logging", "pl_event_listener" ], - "enabledEventTypes" : [ ], - "adminEventsEnabled" : false, - "adminEventsDetailsEnabled" : false, - "components" : { - "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" : [ { - "id" : "84305f42-4b6d-4b0a-ac7c-53e406e3ac63", - "name" : "Allowed Client Scopes", - "providerId" : "allowed-client-templates", - "subType" : "authenticated", - "subComponents" : { }, - "config" : { - "allow-default-scopes" : [ "true" ] + "eventsEnabled": false, + "eventsListeners": [ + "jboss-logging", + "pl_event_listener" + ], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "identityProviders": [], + "identityProviderMappers": [], + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "id": "84305f42-4b6d-4b0a-ac7c-53e406e3ac63", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "c7c38a95-744f-4558-a403-9cf692fe1944", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "365b2899-befe-4417-b89b-562650ec4446", + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "81c32244-7921-43e9-9356-a3469259b78c", + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": [ + "true" + ], + "trusted-hosts": [ + "https://opex.dev/bdemo/login" + ], + "client-uris-must-match": [ + "true" + ] + } + }, + { + "id": "d09b2147-afea-4f7f-a49c-0aec7eee10de", + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": [ + "200" + ] + } + }, + { + "id": "41ffde1b-72a2-416f-87a7-94989e940dc0", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-address-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-usermodel-property-mapper", + "saml-role-list-mapper", + "saml-user-property-mapper", + "oidc-usermodel-attribute-mapper", + "oidc-full-name-mapper", + "saml-user-attribute-mapper" + ] + } + }, + { + "id": "76075388-2782-4656-a986-313493239a9f", + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "3caaf57a-9cd7-48c1-b709-b40b887414f7", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "saml-user-property-mapper", + "saml-role-list-mapper", + "oidc-usermodel-attribute-mapper", + "saml-user-attribute-mapper", + "oidc-usermodel-property-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-address-mapper", + "oidc-full-name-mapper" + ] + } } - }, { - "id" : "c7c38a95-744f-4558-a403-9cf692fe1944", - "name" : "Allowed Client Scopes", - "providerId" : "allowed-client-templates", - "subType" : "anonymous", - "subComponents" : { }, - "config" : { - "allow-default-scopes" : [ "true" ] + ], + "org.keycloak.keys.KeyProvider": [ + { + "id": "d67a940a-52e4-44a5-9f69-6ffdd67a188f", + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "id": "48d40de3-6234-42e8-9449-f68f56abb54b", + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "id": "52ea1c5d-2a30-459f-b66a-249f298b32f8", + "name": "hmac-generated", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "HS256" + ] + } } - }, { - "id" : "365b2899-befe-4417-b89b-562650ec4446", - "name" : "Full Scope Disabled", - "providerId" : "scope", - "subType" : "anonymous", - "subComponents" : { }, - "config" : { } - }, { - "id" : "81c32244-7921-43e9-9356-a3469259b78c", - "name" : "Trusted Hosts", - "providerId" : "trusted-hosts", - "subType" : "anonymous", - "subComponents" : { }, - "config" : { - "host-sending-registration-request-must-match" : [ "true" ], - "client-uris-must-match" : [ "true" ] - } - }, { - "id" : "d09b2147-afea-4f7f-a49c-0aec7eee10de", - "name" : "Max Clients Limit", - "providerId" : "max-clients", - "subType" : "anonymous", - "subComponents" : { }, - "config" : { - "max-clients" : [ "200" ] - } - }, { - "id" : "41ffde1b-72a2-416f-87a7-94989e940dc0", - "name" : "Allowed Protocol Mapper Types", - "providerId" : "allowed-protocol-mappers", - "subType" : "authenticated", - "subComponents" : { }, - "config" : { - "allowed-protocol-mapper-types" : [ "saml-user-attribute-mapper", "oidc-usermodel-property-mapper", "saml-role-list-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-user-property-mapper", "oidc-address-mapper", "oidc-usermodel-attribute-mapper", "oidc-full-name-mapper" ] - } - }, { - "id" : "76075388-2782-4656-a986-313493239a9f", - "name" : "Consent Required", - "providerId" : "consent-required", - "subType" : "anonymous", - "subComponents" : { }, - "config" : { } - }, { - "id" : "3caaf57a-9cd7-48c1-b709-b40b887414f7", - "name" : "Allowed Protocol Mapper Types", - "providerId" : "allowed-protocol-mappers", - "subType" : "anonymous", - "subComponents" : { }, - "config" : { - "allowed-protocol-mapper-types" : [ "oidc-usermodel-property-mapper", "saml-user-property-mapper", "oidc-address-mapper", "saml-role-list-mapper", "oidc-usermodel-attribute-mapper", "saml-user-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-full-name-mapper" ] - } - } ], - "org.keycloak.keys.KeyProvider" : [ { - "id" : "d67a940a-52e4-44a5-9f69-6ffdd67a188f", - "name" : "rsa-generated", - "providerId" : "rsa-generated", - "subComponents" : { }, - "config" : { - "privateKey" : [ "MIIEpQIBAAKCAQEAsw+lmV2HbpgXllKS+ccyCerlWDir32Y3yFvXF3CbzYRKVg/4FddA8itMCPpPjVOAo26bi/0WImHHYPyGxSH3QQIQaSdS5AFUNarpFc9M/W4Bu7k5h8sKb+vzZA3hMLFyvmQ2JtY8Zuqi1BBB33OAlkOHprJFTrrt1xa4a6KJjKXlDp5o5hOFgvZbsW+k/fBU0CLCbL3PlXQLhqHgEncpL9L9FF/xDbPIGftxjEWcBPFG/SuyygT35xRFMxB7ifR+wJZwVnoC4RhbhgJdL2EiUVT7wq5MTsxr2j4nAAJYatK/7OTecs6y/7bGNAGEoqh4Q3X3D95YFex0scmHgY1hWwIDAQABAoIBAQCAesmnsaRrhlXmVnl/H56X3yOQmZk5Qm7kJIHBH9urKzDrb6niggDnwolcJ77M+Q1kNwkE+SPYp7AXtxo8eJKg4opD6SNg1lNF7swwCf6EGVrFB7Jv7RAgHn0VXaAnACwE0ILKew38Cn+rCb86RfYN24aJ5YA1bYsaugK5uHZZo7kLnv5PUwOwu4PqhAKLPyQt2P1gN3rzf0TGmL5Ylqoxx6X6QBKTwOqAzsWPshMxOB+GzIDDegvzQ6UIV/FzsxIZFLVAPtAWxa77TgT8LeBxDz/UF3H5cWEP5LJ3108ZUOa/EyPiIAaMsLtgVycf7gtsOEsQ4q/OqqJrL4NdNUaBAoGBAPVeguDaUwZIMOYGH2sjYaShaddMFsR2u93dj/GI/T8A4/iXz2yxfHl0LEmxQpZkzyVFpYkYO54w59zLdhzpFOkuGOMT+1j4FnMEjSnzQhZzLVxwuQ9cX0VhYRf+gpwhUOrmbPZB3tYWIiDGm823c6J4QCi0kG5R7pPb/nowpIulAoGBALrRr6RdL/lqrt/REtVW2KSdL6r38sN12vmgjBzIQFLWQFSzDxjaPLeZY87uIhpAWAuZJW+NZ7WfF85WuKDwSbuoZ1ln1yj3FkJkYZmdOid8VpEam2Z9sCncvbAq/pvhgSKMYZeXQ2+dPM0N1HiDb9SEuPIbS3hvcYwNAcLyWqj/AoGBAOfAmMVf8MMiNG1OoyZCiNtCSgG8MFToAJGRz39G8EstwCTw3k2/Zd4hSCNidY4vMSf3HF7csJK9hoIY+jpcPA/yJjd0jBaAXFPOnLZeuLEToGiLX3+Os72IOHi9PwfQv+jeM1R06tAyn5FthYNMHr/57D+GLFTGthyZ0UX/46qxAoGAJ7ENRDqYSsGjzeG5wqHk/XR4ADcV2PldQNQfcK4LHI5wtI4mkv0rEUcBsaFelX0+N5ieH4lHk4rtn+VE7MygncI10wUA7a8xh4GUSvLgvCrqqYGhqrDhkMNZeehol+3dZd21jmOQ7FHX7SkXD1O9msVoFeg+rKPg2ASbbzPWlzkCgYEAgVQKaTCgh3E2z3E9OhzeMIq7qyJuSKsP5FHsTT17U46/rPk+XBhLB/VBkY0Hn29NW4CWTmvEaJrJiejKJaPap8DBjMpJ7KnMidEeHaRykCuPmi9VUT2Q+FE5lQaPi8fYIlHI7BsIY+wTVlKRezkKpuFezocyP5YR34XDkM0BhRI=" ], - "certificate" : [ "MIICnzCCAYcCBgFuhB77/jANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhiYWVsZHVuZzAeFw0xOTExMTkxNDQyMzNaFw0yOTExMTkxNDQ0MTNaMBMxETAPBgNVBAMMCGJhZWxkdW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsw+lmV2HbpgXllKS+ccyCerlWDir32Y3yFvXF3CbzYRKVg/4FddA8itMCPpPjVOAo26bi/0WImHHYPyGxSH3QQIQaSdS5AFUNarpFc9M/W4Bu7k5h8sKb+vzZA3hMLFyvmQ2JtY8Zuqi1BBB33OAlkOHprJFTrrt1xa4a6KJjKXlDp5o5hOFgvZbsW+k/fBU0CLCbL3PlXQLhqHgEncpL9L9FF/xDbPIGftxjEWcBPFG/SuyygT35xRFMxB7ifR+wJZwVnoC4RhbhgJdL2EiUVT7wq5MTsxr2j4nAAJYatK/7OTecs6y/7bGNAGEoqh4Q3X3D95YFex0scmHgY1hWwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCLf01IsqWCKF2vw5y/NPCgKFEP4vtlXvr1XusMr9sAMSgbAVKLm5wZg4Ue8Q7iUq1LJzZ7EOeaXfIHxk33vjf0RlJQAaVn2K/kZJ4rFXuU6g+mpoRgiFsCPNexXXMdG294osyJ60g5OWCt5KrdocFjkKbF9EYm2CBj20G5MxCf+VDGDIcNvgzp478CZODiFVb6JgQqRrE3CGzQKdKiHG9cbXAledMJuWw3h0dzAqM46nl7GRkNK2cmVJO+cDD7xMrD83Q84PoG1ixMsQ/Tv5CqfREC4Dp9/j4XaoEOJ5S7oBaiRqTMzxk/lPjmbUrdyLM0Kokrrwc2yF74m9NuwHh2" ], - "priority" : [ "100" ] - } - }, { - "id" : "48d40de3-6234-42e8-9449-f68f56abb54b", - "name" : "aes-generated", - "providerId" : "aes-generated", - "subComponents" : { }, - "config" : { - "kid" : [ "a1643e07-7de0-4265-a42b-21152b16f2a5" ], - "secret" : [ "KH8ObwyJF_YZ6pJ5v9YPuA" ], - "priority" : [ "100" ] + ] + }, + "internationalizationEnabled": false, + "supportedLocales": [], + "authenticationFlows": [ + { + "id": "1994872d-58ab-40e9-9f3c-bd94d498fde9", + "alias": "Handle Existing Account", + "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "REQUIRED", + "priority": 20, + "flowAlias": "Handle Existing Account - Alternatives - 0", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "2d9ee8ef-c756-41b6-a504-2b500650a966", + "alias": "Handle Existing Account - Alternatives - 0", + "description": "Subflow of Handle Existing Account with alternative executions", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-email-verification", + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "ALTERNATIVE", + "priority": 20, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "424ef9c8-da39-4813-9093-964f16f8eed9", + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "CONDITIONAL", + "priority": 20, + "flowAlias": "Verify Existing Account by Re-authentication - auth-otp-form - Conditional", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "5321e985-f444-4772-8a7d-44eeab19d8fa", + "alias": "Verify Existing Account by Re-authentication - auth-otp-form - Conditional", + "description": "Flow to determine if the auth-otp-form authenticator should be used or not.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-otp-form", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "06842669-a459-456d-b9fb-11d9236c3377", + "alias": "browser", + "description": "browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-spnego", + "requirement": "DISABLED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "identity-provider-redirector", + "requirement": "ALTERNATIVE", + "priority": 25, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "ALTERNATIVE", + "priority": 30, + "flowAlias": "forms", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "e869296d-430f-4721-a5cd-2d5fe66294e0", + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-jwt", + "requirement": "ALTERNATIVE", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-secret-jwt", + "requirement": "ALTERNATIVE", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-x509", + "requirement": "ALTERNATIVE", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "0e7775f8-7b6d-41a2-9f55-72416864fd87", + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "direct-grant-validate-password", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "CONDITIONAL", + "priority": 30, + "flowAlias": "direct grant - direct-grant-validate-otp - Conditional", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "5fa95cf1-9d79-4066-93bb-86da3c4a1fa5", + "alias": "direct grant - direct-grant-validate-otp - Conditional", + "description": "Flow to determine if the direct-grant-validate-otp authenticator should be used or not.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "direct-grant-validate-otp", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "89c5f0a2-310b-4388-b9fc-be14f35cb36a", + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "ba4ec48d-de84-4847-9c53-cb16a9114cf8", + "alias": "first broker login", + "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "REQUIRED", + "priority": 20, + "flowAlias": "first broker login - Alternatives - 0", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "fdccc5f5-2ef8-4e94-acd2-34ed9b12c3d1", + "alias": "first broker login - Alternatives - 0", + "description": "Subflow of first broker login with alternative executions", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "ALTERNATIVE", + "priority": 20, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "ceef545f-aade-4066-9aa2-13d21abda764", + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "CONDITIONAL", + "priority": 20, + "flowAlias": "forms - auth-otp-form - Conditional", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "031b60ca-098a-4301-acdc-5e91e3c00de6", + "alias": "forms - auth-otp-form - Conditional", + "description": "Flow to determine if the auth-otp-form authenticator should be used or not.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-otp-form", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "303db066-ce61-40e1-99c5-d8dc28903585", + "alias": "http challenge", + "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "no-cookie-redirect", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "basic-auth", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "basic-auth-otp", + "requirement": "DISABLED", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-spnego", + "requirement": "DISABLED", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "52600ab5-b57f-4ad6-85ea-397afe32cbda", + "alias": "registration", + "description": "registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "requirement": "REQUIRED", + "priority": 10, + "flowAlias": "registration form", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "895289c7-ab6f-4388-9088-1ccb21725996", + "alias": "registration form", + "description": "registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-profile-action", + "requirement": "REQUIRED", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-password-action", + "requirement": "REQUIRED", + "priority": 50, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-recaptcha-action", + "requirement": "DISABLED", + "priority": 60, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "dcc7bdcc-548c-4b40-af94-b3cad69a3b87", + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-credential-email", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-password", + "requirement": "REQUIRED", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "CONDITIONAL", + "priority": 40, + "flowAlias": "reset credentials - reset-otp - Conditional", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "96dff9e4-56be-4c62-a146-2507d00b88c9", + "alias": "reset credentials - reset-otp - Conditional", + "description": "Flow to determine if the reset-otp authenticator should be used or not.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-otp", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "fc10429a-01a5-4e8a-bad3-7b4794ee7688", + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + } + ], + "authenticatorConfig": [ + { + "id": "cda25df3-6d3b-49f9-bb52-b24ab4d1ee57", + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" } - }, { - "id" : "52ea1c5d-2a30-459f-b66a-249f298b32f8", - "name" : "hmac-generated", - "providerId" : "hmac-generated", - "subComponents" : { }, - "config" : { - "kid" : [ "0d90cbd7-7164-42cc-88e2-d215e79ae8ea" ], - "secret" : [ "AFOsLJGAelu021azV_tqcT4qhune3A0xfvGZiqfdTCBcC2UAAErphg1Q3FC4eET83w8uDyi3CMtD1v-6lXqhqw" ], - "priority" : [ "100" ], - "algorithm" : [ "HS256" ] + }, + { + "id": "fbf2034e-a602-44d7-8c11-8d27450f930b", + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" } - } ] - }, - "internationalizationEnabled" : false, - "supportedLocales" : [ ], - "authenticationFlows" : [ { - "id" : "1dca477c-12fa-47df-a6e2-65fa08ed9565", - "alias" : "Handle Existing Account", - "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "idp-confirm-link", - "requirement" : "REQUIRED", - "priority" : 10, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "authenticator" : "idp-email-verification", - "requirement" : "ALTERNATIVE", - "priority" : 20, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "requirement" : "ALTERNATIVE", - "priority" : 30, - "flowAlias" : "Verify Existing Account by Re-authentication", - "userSetupAllowed" : false, - "autheticatorFlow" : true - } ] - }, { - "id" : "a216230b-1eb5-4066-aa48-8556e4a50f72", - "alias" : "Verify Existing Account by Re-authentication", - "description" : "Reauthentication of existing account", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "idp-username-password-form", - "requirement" : "REQUIRED", - "priority" : 10, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "requirement" : "CONDITIONAL", - "priority" : 20, - "flowAlias" : "Verify Existing Account by Re-authentication - auth-otp-form - Conditional", - "userSetupAllowed" : false, - "autheticatorFlow" : true - } ] - }, { - "id" : "c8e26747-1aca-4d11-8ce6-8a99a334aad5", - "alias" : "Verify Existing Account by Re-authentication - auth-otp-form - Conditional", - "description" : "Flow to determine if the auth-otp-form authenticator should be used or not.", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "conditional-user-configured", - "requirement" : "REQUIRED", - "priority" : 10, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "authenticator" : "auth-otp-form", - "requirement" : "REQUIRED", - "priority" : 20, - "userSetupAllowed" : false, - "autheticatorFlow" : false - } ] - }, { - "id" : "5823f307-af3c-4b0f-83f6-a7231a57789a", - "alias" : "browser", - "description" : "browser based authentication", - "providerId" : "basic-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "auth-cookie", - "requirement" : "ALTERNATIVE", - "priority" : 10, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "authenticator" : "auth-spnego", - "requirement" : "DISABLED", - "priority" : 20, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "authenticator" : "identity-provider-redirector", - "requirement" : "ALTERNATIVE", - "priority" : 25, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "requirement" : "ALTERNATIVE", - "priority" : 30, - "flowAlias" : "forms", - "userSetupAllowed" : false, - "autheticatorFlow" : true - } ] - }, { - "id" : "c8b61763-3346-42a7-b9c1-e8d33d2d883e", - "alias" : "clients", - "description" : "Base authentication for clients", - "providerId" : "client-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "client-secret", - "requirement" : "ALTERNATIVE", - "priority" : 10, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "authenticator" : "client-jwt", - "requirement" : "ALTERNATIVE", - "priority" : 20, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "authenticator" : "client-secret-jwt", - "requirement" : "ALTERNATIVE", - "priority" : 30, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "authenticator" : "client-x509", - "requirement" : "ALTERNATIVE", - "priority" : 40, - "userSetupAllowed" : false, - "autheticatorFlow" : false - } ] - }, { - "id" : "cb9be161-629e-4402-b6eb-d102b5a6f03b", - "alias" : "direct grant", - "description" : "OpenID Connect Resource Owner Grant", - "providerId" : "basic-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "direct-grant-validate-username", - "requirement" : "REQUIRED", - "priority" : 10, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "authenticator" : "direct-grant-validate-password", - "requirement" : "REQUIRED", - "priority" : 20, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "requirement" : "CONDITIONAL", - "priority" : 30, - "flowAlias" : "direct grant - direct-grant-validate-otp - Conditional", - "userSetupAllowed" : false, - "autheticatorFlow" : true - } ] - }, { - "id" : "5dd842c9-1322-450f-aa65-c868cdca213e", - "alias" : "direct grant - direct-grant-validate-otp - Conditional", - "description" : "Flow to determine if the direct-grant-validate-otp authenticator should be used or not.", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "conditional-user-configured", - "requirement" : "REQUIRED", - "priority" : 10, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "authenticator" : "direct-grant-validate-otp", - "requirement" : "REQUIRED", - "priority" : 20, - "userSetupAllowed" : false, - "autheticatorFlow" : false - } ] - }, { - "id" : "9a38ae9d-c67c-43aa-a86a-44fce0394aec", - "alias" : "docker auth", - "description" : "Used by Docker clients to authenticate against the IDP", - "providerId" : "basic-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "docker-http-basic-authenticator", - "requirement" : "REQUIRED", - "priority" : 10, - "userSetupAllowed" : false, - "autheticatorFlow" : false - } ] - }, { - "id" : "77a8fa1c-689e-421c-b6de-d8d78a8a19bb", - "alias" : "first broker login", - "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", - "providerId" : "basic-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticatorConfig" : "review profile config", - "authenticator" : "idp-review-profile", - "requirement" : "REQUIRED", - "priority" : 10, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "authenticatorConfig" : "create unique user config", - "authenticator" : "idp-create-user-if-unique", - "requirement" : "ALTERNATIVE", - "priority" : 20, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "requirement" : "ALTERNATIVE", - "priority" : 30, - "flowAlias" : "Handle Existing Account", - "userSetupAllowed" : false, - "autheticatorFlow" : true - } ] - }, { - "id" : "6c3afd8e-1bca-45a8-bb82-f779b2cd62f7", - "alias" : "forms", - "description" : "Username, password, otp and other auth forms.", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "auth-username-password-form", - "requirement" : "REQUIRED", - "priority" : 10, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "requirement" : "CONDITIONAL", - "priority" : 20, - "flowAlias" : "forms - auth-otp-form - Conditional", - "userSetupAllowed" : false, - "autheticatorFlow" : true - } ] - }, { - "id" : "8dca43f9-2dda-4ed9-9b07-ac731c610ec3", - "alias" : "forms - auth-otp-form - Conditional", - "description" : "Flow to determine if the auth-otp-form authenticator should be used or not.", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "conditional-user-configured", - "requirement" : "REQUIRED", - "priority" : 10, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "authenticator" : "auth-otp-form", - "requirement" : "REQUIRED", - "priority" : 20, - "userSetupAllowed" : false, - "autheticatorFlow" : false - } ] - }, { - "id" : "bd7f98ba-7a67-48ba-af12-e667e13df447", - "alias" : "http challenge", - "description" : "An authentication flow based on challenge-response HTTP Authentication Schemes", - "providerId" : "basic-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "no-cookie-redirect", - "requirement" : "REQUIRED", - "priority" : 10, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "authenticator" : "basic-auth", - "requirement" : "REQUIRED", - "priority" : 20, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "authenticator" : "basic-auth-otp", - "requirement" : "DISABLED", - "priority" : 30, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "authenticator" : "auth-spnego", - "requirement" : "DISABLED", - "priority" : 40, - "userSetupAllowed" : false, - "autheticatorFlow" : false - } ] - }, { - "id" : "f36eff20-a2a9-4652-bb0c-9b4bd3c100a8", - "alias" : "registration", - "description" : "registration flow", - "providerId" : "basic-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "registration-page-form", - "requirement" : "REQUIRED", - "priority" : 10, - "flowAlias" : "registration form", - "userSetupAllowed" : false, - "autheticatorFlow" : true - } ] - }, { - "id" : "d1536e05-0a6a-4c07-8d48-6124fd2fe835", - "alias" : "registration form", - "description" : "registration form", - "providerId" : "form-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "registration-user-creation", - "requirement" : "REQUIRED", - "priority" : 20, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "authenticator" : "registration-profile-action", - "requirement" : "REQUIRED", - "priority" : 40, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "authenticator" : "registration-password-action", - "requirement" : "REQUIRED", - "priority" : 50, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "authenticator" : "registration-recaptcha-action", - "requirement" : "DISABLED", - "priority" : 60, - "userSetupAllowed" : false, - "autheticatorFlow" : false - } ] - }, { - "id" : "24504d0d-93e5-4476-b203-ba997abbd689", - "alias" : "reset credentials", - "description" : "Reset credentials for a user if they forgot their password or something", - "providerId" : "basic-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "reset-credentials-choose-user", - "requirement" : "REQUIRED", - "priority" : 10, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "authenticator" : "reset-credential-email", - "requirement" : "REQUIRED", - "priority" : 20, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "authenticator" : "reset-password", - "requirement" : "REQUIRED", - "priority" : 30, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "requirement" : "CONDITIONAL", - "priority" : 40, - "flowAlias" : "reset credentials - reset-otp - Conditional", - "userSetupAllowed" : false, - "autheticatorFlow" : true - } ] - }, { - "id" : "91ff889c-e767-4988-beff-2e3437f870aa", - "alias" : "reset credentials - reset-otp - Conditional", - "description" : "Flow to determine if the reset-otp authenticator should be used or not.", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "conditional-user-configured", - "requirement" : "REQUIRED", - "priority" : 10, - "userSetupAllowed" : false, - "autheticatorFlow" : false - }, { - "authenticator" : "reset-otp", - "requirement" : "REQUIRED", - "priority" : 20, - "userSetupAllowed" : false, - "autheticatorFlow" : false - } ] - }, { - "id" : "2c6aa578-0d3a-4ad4-aa3c-f0ede99afc44", - "alias" : "saml ecp", - "description" : "SAML ECP Profile Authentication Flow", - "providerId" : "basic-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "http-basic-authenticator", - "requirement" : "REQUIRED", - "priority" : 10, - "userSetupAllowed" : false, - "autheticatorFlow" : false - } ] - } ], - "authenticatorConfig" : [ { - "id" : "f235f13d-e935-45b3-b87b-0d6faf1eebc1", - "alias" : "create unique user config", - "config" : { - "require.password.update.after.registration" : "false" } - }, { - "id" : "edfa789a-564c-41bd-b145-0c8493a70593", - "alias" : "review profile config", - "config" : { - "update.profile.on.first.login" : "missing" + ], + "requiredActions": [ + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "priority": 10, + "config": {} + }, + { + "alias": "terms_and_conditions", + "name": "Terms and Conditions", + "providerId": "terms_and_conditions", + "enabled": false, + "defaultAction": false, + "priority": 20, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "priority": 30, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 40, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "priority": 50, + "config": {} + }, + { + "alias": "delete_account", + "name": "Delete Account", + "providerId": "delete_account", + "enabled": false, + "defaultAction": false, + "priority": 60, + "config": {} + }, + { + "alias": "update_user_locale", + "name": "Update User Locale", + "providerId": "update_user_locale", + "enabled": true, + "defaultAction": false, + "priority": 1000, + "config": {} } - } ], - "requiredActions" : [ { - "alias" : "CONFIGURE_TOTP", - "name" : "Configure OTP", - "providerId" : "CONFIGURE_TOTP", - "enabled" : true, - "defaultAction" : false, - "priority" : 10, - "config" : { } - }, { - "alias" : "terms_and_conditions", - "name" : "Terms and Conditions", - "providerId" : "terms_and_conditions", - "enabled" : false, - "defaultAction" : false, - "priority" : 20, - "config" : { } - }, { - "alias" : "UPDATE_PASSWORD", - "name" : "Update Password", - "providerId" : "UPDATE_PASSWORD", - "enabled" : true, - "defaultAction" : false, - "priority" : 30, - "config" : { } - }, { - "alias" : "UPDATE_PROFILE", - "name" : "Update Profile", - "providerId" : "UPDATE_PROFILE", - "enabled" : true, - "defaultAction" : false, - "priority" : 40, - "config" : { } - }, { - "alias" : "VERIFY_EMAIL", - "name" : "Verify Email", - "providerId" : "VERIFY_EMAIL", - "enabled" : true, - "defaultAction" : false, - "priority" : 50, - "config" : { } - } ], - "browserFlow" : "browser", - "registrationFlow" : "registration", - "directGrantFlow" : "direct grant", - "resetCredentialsFlow" : "reset credentials", - "clientAuthenticationFlow" : "clients", - "dockerAuthenticationFlow" : "docker auth", - "attributes" : { - "webAuthnPolicyAuthenticatorAttachment" : "not specified", - "_browser_header.xRobotsTag" : "none", - "webAuthnPolicyRpEntityName" : "keycloak", - "failureFactor" : "30", - "actionTokenGeneratedByUserLifespan" : "300", - "maxDeltaTimeSeconds" : "43200", - "webAuthnPolicySignatureAlgorithms" : "ES256", - "offlineSessionMaxLifespan" : "5184000", - "_browser_header.contentSecurityPolicyReportOnly" : "", - "bruteForceProtected" : "false", - "_browser_header.contentSecurityPolicy" : "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", - "_browser_header.xXSSProtection" : "1; mode=block", - "_browser_header.xFrameOptions" : "SAMEORIGIN", - "_browser_header.strictTransportSecurity" : "max-age=31536000; includeSubDomains", - "webAuthnPolicyUserVerificationRequirement" : "not specified", - "permanentLockout" : "false", - "quickLoginCheckMilliSeconds" : "1000", - "webAuthnPolicyCreateTimeout" : "0", - "webAuthnPolicyRequireResidentKey" : "not specified", - "webAuthnPolicyRpId" : "", - "webAuthnPolicyAttestationConveyancePreference" : "not specified", - "maxFailureWaitSeconds" : "900", - "minimumQuickLoginWaitSeconds" : "60", - "webAuthnPolicyAvoidSameAuthenticatorRegister" : "false", - "_browser_header.xContentTypeOptions" : "nosniff", - "actionTokenGeneratedByAdminLifespan" : "43200", - "waitIncrementSeconds" : "60", - "offlineSessionMaxLifespanEnabled" : "false" + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "attributes": { + "clientOfflineSessionMaxLifespan": "0", + "clientSessionIdleTimeout": "0", + "clientSessionMaxLifespan": "0", + "clientOfflineSessionIdleTimeout": "0" }, - "keycloakVersion" : "8.0.0", - "userManagedAccessAllowed" : false + "keycloakVersion": "12.0.4", + "userManagedAccessAllowed": false } \ No newline at end of file From e6c0b50a29f34228e620323a9329864a9bc7f61e Mon Sep 17 00:00:00 2001 From: maryarm Date: Tue, 24 Aug 2021 18:33:38 +0430 Subject: [PATCH 34/59] Fix #42: Change "date" type from LocalDateTime to Long --- .../nilin/opex/port/accountant/wallet/proxy/WalletProxyImpl.kt | 2 +- .../kotlin/co/nilin/opex/wallet/core/inout/TransferResult.kt | 2 +- .../co/nilin/opex/wallet/core/service/TransferService.kt | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/opex/port/accountant/wallet/proxy/WalletProxyImpl.kt b/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/opex/port/accountant/wallet/proxy/WalletProxyImpl.kt index 65fe00059..480ee1f1d 100644 --- a/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/opex/port/accountant/wallet/proxy/WalletProxyImpl.kt +++ b/Accountant/accountant-ports/accountant-wallet-proxy/src/main/kotlin/co/nilin/opex/port/accountant/wallet/proxy/WalletProxyImpl.kt @@ -12,7 +12,7 @@ import java.time.LocalDateTime inline fun typeRef(): ParameterizedTypeReference = object : ParameterizedTypeReference() {} data class TransferResult( - val date: LocalDateTime, + val date: Long, val sourceBalanceBeforeAction: Amount, val sourceBalanceAfterAction: Amount, val amount: Amount diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/inout/TransferResult.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/inout/TransferResult.kt index 8ebf1ae9c..32988898d 100644 --- a/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/inout/TransferResult.kt +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/inout/TransferResult.kt @@ -3,4 +3,4 @@ package co.nilin.opex.wallet.core.inout import co.nilin.opex.wallet.core.model.Amount import java.time.LocalDateTime -data class TransferResult(val date: LocalDateTime, val sourceBalanceBeforeAction: Amount, val sourceBalanceAfterAction: Amount, val amount: Amount) \ No newline at end of file +data class TransferResult(val date: Long, val sourceBalanceBeforeAction: Amount, val sourceBalanceAfterAction: Amount, val amount: Amount) \ No newline at end of file diff --git a/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/service/TransferService.kt b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/service/TransferService.kt index adb3fcde1..05ffec62b 100644 --- a/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/service/TransferService.kt +++ b/Wallet/wallet-core/src/main/kotlin/co/nilin/opex/wallet/core/service/TransferService.kt @@ -15,6 +15,7 @@ import co.nilin.opex.wallet.core.spi.WalletManager import co.nilin.opex.wallet.core.spi.WalletOwnerManager import org.springframework.stereotype.Service import java.time.LocalDateTime +import java.util.Date @Service class TransferService( @@ -69,6 +70,6 @@ class TransferService( //post transfer hook(dispatch post transfer event) //notify balance change - return TransferResult(LocalDateTime.now(), srcWalletBalance, srcWallet.balance(), balance) + return TransferResult(Date().time, srcWalletBalance, srcWallet.balance(), balance) } } \ No newline at end of file From 68417008cf50bce60572e69bc48ef5d2891865ef Mon Sep 17 00:00:00 2001 From: Peyman Date: Wed, 25 Aug 2021 13:18:31 +0430 Subject: [PATCH 35/59] close #33: Order book API implemented --- .../opex/api/core/spi/MarketQueryHandler.kt | 11 ++++ .../binance/controller/MarketController.kt | 60 ++++++++++++++++- .../api/binance/data/OrderBookResponse.kt | 9 +++ .../opex/port/api/binance/data/RecentTrade.kt | 11 ++++ .../port/api/postgres/dao/OrderRepository.kt | 27 ++++++++ .../postgres/impl/MarketQueryHandlerImpl.kt | 65 +++++++++++++++++++ .../opex/utility/error/data/OpexError.kt | 5 +- 7 files changed, 186 insertions(+), 2 deletions(-) create mode 100644 Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/OrderBookResponse.kt create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/RecentTrade.kt create mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt new file mode 100644 index 000000000..7859eac80 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt @@ -0,0 +1,11 @@ +package co.nilin.opex.api.core.spi + +import co.nilin.opex.api.core.inout.QueryOrderResponse + +interface MarketQueryHandler { + + suspend fun openBidOrders(symbol: String, limit: Int): List + + suspend fun openAskOrders(symbol: String, limit: Int): List + +} \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt index 8afe2c0d3..362a004b1 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt @@ -1,8 +1,66 @@ package co.nilin.opex.port.api.binance.controller +import co.nilin.opex.api.core.spi.MarketQueryHandler +import co.nilin.opex.api.core.spi.SymbolMapper +import co.nilin.opex.port.api.binance.data.OrderBookResponse +import co.nilin.opex.utility.error.data.OpexError +import co.nilin.opex.utility.error.data.OpexException +import co.nilin.opex.utility.error.data.throwError +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController +import java.math.BigDecimal +import kotlin.collections.ArrayList @RestController -class MarketController { +class MarketController( + private val marketQueryMarket: MarketQueryHandler, + private val symbolMapper: SymbolMapper, +) { + + private val orderBookValidLimits = arrayListOf(5, 10, 20, 50, 100, 500, 1000, 5000) + private val defaultOrderBookLimit = 100 + + // Limit - Weight + // 5, 10, 20, 50, 100 - 1 + // 500 - 5 + // 1000 - 10 + // 5000 - 50 + @GetMapping("/v3/depth") + suspend fun orderBook( + @RequestParam("symbol") + symbol: String, + @RequestParam("limit", required = false) + limit: Int? // Default 100; max 5000. Valid limits:[5, 10, 20, 50, 100, 500, 1000, 5000] + ): OrderBookResponse { + val validLimit = limit ?: 100 + val localSymbol = symbolMapper.unmap(symbol) ?: throw OpexException(OpexError.SymbolNotFound) + if (!orderBookValidLimits.contains(validLimit)) + throwError(OpexError.InvalidLimitForOrderBook) + + val mappedBidOrders = ArrayList>() + val mappedAskOrders = ArrayList>() + + val bidOrders = marketQueryMarket.openBidOrders(localSymbol, validLimit) + val askOrders = marketQueryMarket.openAskOrders(localSymbol, validLimit) + + bidOrders.forEach { + val mapped = arrayListOf().apply { + add(it.price) + add(it.origQty) + } + mappedBidOrders.add(mapped) + } + + askOrders.forEach { + val mapped = arrayListOf().apply { + add(it.price) + add(it.origQty) + } + mappedAskOrders.add(mapped) + } + + return OrderBookResponse(-1, mappedBidOrders, mappedAskOrders) + } } \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/OrderBookResponse.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/OrderBookResponse.kt new file mode 100644 index 000000000..dbe8f96c5 --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/OrderBookResponse.kt @@ -0,0 +1,9 @@ +package co.nilin.opex.port.api.binance.data + +import java.math.BigDecimal + +data class OrderBookResponse( + val lastUpdateId: Long, + val bids: List>, // Inner list -> [0]: PRICE, [1]: QTY + val asks: List> // Inner list -> [0]: PRICE, [1]: QTY +) \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/RecentTrade.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/RecentTrade.kt new file mode 100644 index 000000000..5ef14ddec --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/RecentTrade.kt @@ -0,0 +1,11 @@ +package co.nilin.opex.port.api.binance.data + +data class RecentTrade( + val id: Long, + val price: Float, + val qty: Float, + val quoteQty: Float, + val time: Long, + val isBuyerMaker: Boolean, + val isBestMatch: Boolean +) \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt index 54a10c23d..6623514dd 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt @@ -1,16 +1,19 @@ package co.nilin.opex.port.api.postgres.dao +import co.nilin.opex.matching.core.model.OrderDirection import co.nilin.opex.port.api.postgres.model.OrderModel import kotlinx.coroutines.flow.Flow import org.springframework.data.r2dbc.repository.Query import org.springframework.data.repository.query.Param import org.springframework.data.repository.reactive.ReactiveCrudRepository import org.springframework.stereotype.Repository +import reactor.core.publisher.Flux import reactor.core.publisher.Mono import java.util.* @Repository interface OrderRepository : ReactiveCrudRepository { + @Query("select * from orders where ouid = :ouid") fun findByOuid(@Param("ouid") ouid: String): Mono @@ -53,4 +56,28 @@ interface OrderRepository : ReactiveCrudRepository { @Param("endTime") endTime: Date? ): Flow + + @Query("select * from orders where symbol = :symbol and side = :direction and status in (:statuses) order by price asc limit :limit") + fun findBySymbolAndDirectionAndStatusSortAscendingByPrice( + @Param("symbol") + symbol: String, + @Param("direction") + direction: OrderDirection, + @Param("limit") + limit: Int, + @Param("statuses") + status: Collection + ): Flux + + @Query("select * from orders where symbol = :symbol and side = :direction and status in (:statuses) order by price desc limit :limit") + fun findBySymbolAndDirectionAndStatusSortDescendingByPrice( + @Param("symbol") + symbol: String, + @Param("direction") + direction: OrderDirection, + @Param("limit") + limit: Int, + @Param("statuses") + status: Collection + ): Flux } \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt new file mode 100644 index 000000000..263bf6e92 --- /dev/null +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt @@ -0,0 +1,65 @@ +package co.nilin.opex.port.api.postgres.impl + +import co.nilin.opex.api.core.inout.OrderStatus +import co.nilin.opex.api.core.inout.QueryOrderResponse +import co.nilin.opex.api.core.spi.MarketQueryHandler +import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.port.api.postgres.dao.OrderRepository +import co.nilin.opex.port.api.postgres.dao.TradeRepository +import co.nilin.opex.port.api.postgres.model.OrderModel +import co.nilin.opex.port.api.postgres.util.* +import kotlinx.coroutines.reactive.awaitFirstOrElse +import org.springframework.stereotype.Component +import java.math.BigDecimal +import java.time.ZoneId +import java.util.* + +@Component +class MarketQueryHandlerImpl( + private val orderRepository: OrderRepository, + private val tradeRepository: TradeRepository, +) : MarketQueryHandler { + + override suspend fun openBidOrders(symbol: String, limit: Int): List { + return orderRepository.findBySymbolAndDirectionAndStatusSortDescendingByPrice( + symbol, + OrderDirection.BID, + limit, + listOf(OrderStatus.NEW.ordinal, OrderStatus.PARTIALLY_FILLED.ordinal) + ).collectList() + .awaitFirstOrElse { emptyList() } + .map { it.asQueryOrderResponse() } + } + + override suspend fun openAskOrders(symbol: String, limit: Int): List { + return orderRepository.findBySymbolAndDirectionAndStatusSortAscendingByPrice( + symbol, + OrderDirection.ASK, + limit, + listOf(OrderStatus.NEW.ordinal, OrderStatus.PARTIALLY_FILLED.ordinal) + ).collectList() + .awaitFirstOrElse { emptyList() } + .map { it.asQueryOrderResponse() } + } + + private fun OrderModel.asQueryOrderResponse() = QueryOrderResponse( + symbol, + orderId ?: -1, + -1, + clientOrderId ?: "", + BigDecimal(price!!), + BigDecimal(quantity!!), + BigDecimal(executedQuantity!!), + BigDecimal(accumulativeQuoteQty ?: 0.0), + status!!.toOrderStatus(), + constraint!!.toTimeInForce(), + type!!.toApiOrderType(), + direction!!.toOrderSide(), + null, + null, + Date.from(createDate!!.atZone(ZoneId.systemDefault()).toInstant()), + Date.from(updateDate.atZone(ZoneId.systemDefault()).toInstant()), + status.toOrderStatus().isWorking(), + quoteQuantity!!.toBigDecimal() + ) +} \ No newline at end of file diff --git a/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/OpexError.kt b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/OpexError.kt index 498f29d2f..2c5e0bc5e 100644 --- a/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/OpexError.kt +++ b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/OpexError.kt @@ -28,7 +28,10 @@ enum class OpexError(val code: Int, val message: String?, val status: HttpStatus WalletOwnerNotFound(6001, null, HttpStatus.NOT_FOUND), // code 7000: api - OrderNotFound(7001, "No order found", HttpStatus.NOT_FOUND); + OrderNotFound(7001, "No order found", HttpStatus.NOT_FOUND), + SymbolNotFound(7002, "No symbol found", HttpStatus.NOT_FOUND), + InvalidLimitForOrderBook(7003, "Valid limits: [5, 10, 20, 50, 100, 500, 1000, 5000]", HttpStatus.BAD_REQUEST), + InvalidLimitForRecentTrades(7004, "Valid limits: 1 min - 1000 max", HttpStatus.BAD_REQUEST); companion object { fun findByCode(code: Int?): OpexError? { From bff4ae4bcb994e2ec9c5b81436a961936663a02d Mon Sep 17 00:00:00 2001 From: Peyman Date: Wed, 25 Aug 2021 16:29:49 +0430 Subject: [PATCH 36/59] close #34: Trade log API implemented --- .../opex/api/core/spi/MarketQueryHandler.kt | 5 ++ .../binance/controller/MarketController.kt | 38 +++++++++++-- ...{RecentTrade.kt => RecentTradeResponse.kt} | 10 ++-- .../port/api/postgres/dao/TradeRepository.kt | 8 +++ .../postgres/impl/MarketQueryHandlerImpl.kt | 55 +++++++++++++++++++ 5 files changed, 108 insertions(+), 8 deletions(-) rename Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/{RecentTrade.kt => RecentTradeResponse.kt} (50%) diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt index 7859eac80..a01472f21 100644 --- a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt @@ -1,6 +1,9 @@ package co.nilin.opex.api.core.spi import co.nilin.opex.api.core.inout.QueryOrderResponse +import co.nilin.opex.api.core.inout.TradeResponse +import kotlinx.coroutines.flow.Flow +import java.security.Principal interface MarketQueryHandler { @@ -8,4 +11,6 @@ interface MarketQueryHandler { suspend fun openAskOrders(symbol: String, limit: Int): List + suspend fun recentTrades(principal: Principal, symbol: String, limit: Int): Flow + } \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt index 362a004b1..dd0a4a346 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt @@ -3,23 +3,26 @@ package co.nilin.opex.port.api.binance.controller import co.nilin.opex.api.core.spi.MarketQueryHandler import co.nilin.opex.api.core.spi.SymbolMapper import co.nilin.opex.port.api.binance.data.OrderBookResponse +import co.nilin.opex.port.api.binance.data.RecentTradeResponse import co.nilin.opex.utility.error.data.OpexError import co.nilin.opex.utility.error.data.OpexException import co.nilin.opex.utility.error.data.throwError +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController import java.math.BigDecimal +import java.security.Principal import kotlin.collections.ArrayList @RestController class MarketController( - private val marketQueryMarket: MarketQueryHandler, + private val marketQueryHandler: MarketQueryHandler, private val symbolMapper: SymbolMapper, ) { private val orderBookValidLimits = arrayListOf(5, 10, 20, 50, 100, 500, 1000, 5000) - private val defaultOrderBookLimit = 100 // Limit - Weight // 5, 10, 20, 50, 100 - 1 @@ -41,8 +44,8 @@ class MarketController( val mappedBidOrders = ArrayList>() val mappedAskOrders = ArrayList>() - val bidOrders = marketQueryMarket.openBidOrders(localSymbol, validLimit) - val askOrders = marketQueryMarket.openAskOrders(localSymbol, validLimit) + val bidOrders = marketQueryHandler.openBidOrders(localSymbol, validLimit) + val askOrders = marketQueryHandler.openAskOrders(localSymbol, validLimit) bidOrders.forEach { val mapped = arrayListOf().apply { @@ -63,4 +66,31 @@ class MarketController( return OrderBookResponse(-1, mappedBidOrders, mappedAskOrders) } + @GetMapping("/v3/trades") + suspend fun recentTrades( + principal: Principal, + @RequestParam("symbol") + symbol: String, + @RequestParam("limit", required = false) + limit: Int? // Default 500; max 1000. + ): Flow { + val validLimit = limit ?: 500 + val localSymbol = symbolMapper.unmap(symbol) ?: throw OpexException(OpexError.SymbolNotFound) + if (validLimit !in 1..1000) + throwError(OpexError.InvalidLimitForRecentTrades) + + return marketQueryHandler.recentTrades(principal, localSymbol, validLimit) + .map { + RecentTradeResponse( + it.id, + it.price, + it.qty, + it.quoteQty, + it.time.time, + it.isMaker && it.isBuyer, + it.isBestMatch + ) + } + } + } \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/RecentTrade.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/RecentTradeResponse.kt similarity index 50% rename from Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/RecentTrade.kt rename to Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/RecentTradeResponse.kt index 5ef14ddec..2a6c76513 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/RecentTrade.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/RecentTradeResponse.kt @@ -1,10 +1,12 @@ package co.nilin.opex.port.api.binance.data -data class RecentTrade( +import java.math.BigDecimal + +data class RecentTradeResponse( val id: Long, - val price: Float, - val qty: Float, - val quoteQty: Float, + val price: BigDecimal, + val qty: BigDecimal, + val quoteQty: BigDecimal, val time: Long, val isBuyerMaker: Boolean, val isBestMatch: Boolean diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/TradeRepository.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/TradeRepository.kt index 58d9ab037..7282b558f 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/TradeRepository.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/TradeRepository.kt @@ -32,4 +32,12 @@ interface TradeRepository : ReactiveCrudRepository { @Param("endTime") endTime: Date? ): Flow + + @Query("select * from trades where symbol = :symbol order by create_date desc limit :limit") + fun findBySymbolSortDescendingByCreateDate( + @Param("symbol") + symbol: String, + @Param("limit") + limit:Int + ): Flow } \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt index 263bf6e92..6645c2ed6 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt @@ -2,15 +2,20 @@ package co.nilin.opex.port.api.postgres.impl import co.nilin.opex.api.core.inout.OrderStatus import co.nilin.opex.api.core.inout.QueryOrderResponse +import co.nilin.opex.api.core.inout.TradeResponse import co.nilin.opex.api.core.spi.MarketQueryHandler import co.nilin.opex.matching.core.model.OrderDirection import co.nilin.opex.port.api.postgres.dao.OrderRepository import co.nilin.opex.port.api.postgres.dao.TradeRepository import co.nilin.opex.port.api.postgres.model.OrderModel import co.nilin.opex.port.api.postgres.util.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.reactive.awaitFirst import kotlinx.coroutines.reactive.awaitFirstOrElse import org.springframework.stereotype.Component import java.math.BigDecimal +import java.security.Principal import java.time.ZoneId import java.util.* @@ -42,6 +47,56 @@ class MarketQueryHandlerImpl( .map { it.asQueryOrderResponse() } } + override suspend fun recentTrades(principal: Principal, symbol: String, limit: Int): Flow { + return tradeRepository.findBySymbolSortDescendingByCreateDate(symbol, limit) + .map { + val takerOrder = orderRepository.findByOuid(it.takerOuid).awaitFirst() + val makerOrder = orderRepository.findByOuid(it.makerOuid).awaitFirst() + val isMakerBuyer = makerOrder.direction == OrderDirection.ASK + TradeResponse( + it.symbol, + it.tradeId, + if (it.takerUuid == principal.name) { + takerOrder.orderId!! + } else { + makerOrder.orderId!! + }, + -1, + if (it.takerUuid == principal.name) { + it.takerPrice.toBigDecimal() + } else { + it.makerPrice.toBigDecimal() + }, + it.matchedQuantity.toBigDecimal(), + if (isMakerBuyer) { + makerOrder.quoteQuantity!!.toBigDecimal() + } else { + takerOrder.quoteQuantity!!.toBigDecimal() + }, + if (it.takerUuid == principal.name) { + it.takerCommision!!.toBigDecimal() + } else { + it.makerCommision!!.toBigDecimal() + }, + if (it.takerUuid == principal.name) { + it.takerCommisionAsset!! + } else { + it.makerCommisionAsset!! + }, + Date.from( + it.createDate.atZone(ZoneId.systemDefault()).toInstant() + ), + if (it.takerUuid == principal.name) { + OrderDirection.ASK == takerOrder.direction + } else { + OrderDirection.ASK == makerOrder.direction + }, + it.makerUuid == principal.name, + true + ) + } + } + private fun OrderModel.asQueryOrderResponse() = QueryOrderResponse( symbol, orderId ?: -1, From 2eca3651f21abec4b7f61289da7bbbfa5e126887 Mon Sep 17 00:00:00 2001 From: Peyman Date: Wed, 25 Aug 2021 16:31:37 +0430 Subject: [PATCH 37/59] close #39: Removed public.cert files --- Api/api-app/src/main/resources/public.cert | 3 --- .../bc-gateway-app/src/main/resources/public.cert | 3 --- MatchingGateway/gateway-app/src/main/resources/public.cert | 3 --- Wallet/wallet-app/src/main/resources/public.cert | 3 --- 4 files changed, 12 deletions(-) delete mode 100644 Api/api-app/src/main/resources/public.cert delete mode 100644 BlockchainGateway/bc-gateway-app/src/main/resources/public.cert delete mode 100644 MatchingGateway/gateway-app/src/main/resources/public.cert delete mode 100644 Wallet/wallet-app/src/main/resources/public.cert diff --git a/Api/api-app/src/main/resources/public.cert b/Api/api-app/src/main/resources/public.cert deleted file mode 100644 index 8589de065..000000000 --- a/Api/api-app/src/main/resources/public.cert +++ /dev/null @@ -1,3 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsw+lmV2HbpgXllKS+ccyCerlWDir32Y3yFvXF3CbzYRKVg/4FddA8itMCPpPjVOAo26bi/0WImHHYPyGxSH3QQIQaSdS5AFUNarpFc9M/W4Bu7k5h8sKb+vzZA3hMLFyvmQ2JtY8Zuqi1BBB33OAlkOHprJFTrrt1xa4a6KJjKXlDp5o5hOFgvZbsW+k/fBU0CLCbL3PlXQLhqHgEncpL9L9FF/xDbPIGftxjEWcBPFG/SuyygT35xRFMxB7ifR+wJZwVnoC4RhbhgJdL2EiUVT7wq5MTsxr2j4nAAJYatK/7OTecs6y/7bGNAGEoqh4Q3X3D95YFex0scmHgY1hWwIDAQAB ------END PUBLIC KEY----- \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-app/src/main/resources/public.cert b/BlockchainGateway/bc-gateway-app/src/main/resources/public.cert deleted file mode 100644 index 8589de065..000000000 --- a/BlockchainGateway/bc-gateway-app/src/main/resources/public.cert +++ /dev/null @@ -1,3 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsw+lmV2HbpgXllKS+ccyCerlWDir32Y3yFvXF3CbzYRKVg/4FddA8itMCPpPjVOAo26bi/0WImHHYPyGxSH3QQIQaSdS5AFUNarpFc9M/W4Bu7k5h8sKb+vzZA3hMLFyvmQ2JtY8Zuqi1BBB33OAlkOHprJFTrrt1xa4a6KJjKXlDp5o5hOFgvZbsW+k/fBU0CLCbL3PlXQLhqHgEncpL9L9FF/xDbPIGftxjEWcBPFG/SuyygT35xRFMxB7ifR+wJZwVnoC4RhbhgJdL2EiUVT7wq5MTsxr2j4nAAJYatK/7OTecs6y/7bGNAGEoqh4Q3X3D95YFex0scmHgY1hWwIDAQAB ------END PUBLIC KEY----- \ No newline at end of file diff --git a/MatchingGateway/gateway-app/src/main/resources/public.cert b/MatchingGateway/gateway-app/src/main/resources/public.cert deleted file mode 100644 index 8589de065..000000000 --- a/MatchingGateway/gateway-app/src/main/resources/public.cert +++ /dev/null @@ -1,3 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsw+lmV2HbpgXllKS+ccyCerlWDir32Y3yFvXF3CbzYRKVg/4FddA8itMCPpPjVOAo26bi/0WImHHYPyGxSH3QQIQaSdS5AFUNarpFc9M/W4Bu7k5h8sKb+vzZA3hMLFyvmQ2JtY8Zuqi1BBB33OAlkOHprJFTrrt1xa4a6KJjKXlDp5o5hOFgvZbsW+k/fBU0CLCbL3PlXQLhqHgEncpL9L9FF/xDbPIGftxjEWcBPFG/SuyygT35xRFMxB7ifR+wJZwVnoC4RhbhgJdL2EiUVT7wq5MTsxr2j4nAAJYatK/7OTecs6y/7bGNAGEoqh4Q3X3D95YFex0scmHgY1hWwIDAQAB ------END PUBLIC KEY----- \ No newline at end of file diff --git a/Wallet/wallet-app/src/main/resources/public.cert b/Wallet/wallet-app/src/main/resources/public.cert deleted file mode 100644 index 8589de065..000000000 --- a/Wallet/wallet-app/src/main/resources/public.cert +++ /dev/null @@ -1,3 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsw+lmV2HbpgXllKS+ccyCerlWDir32Y3yFvXF3CbzYRKVg/4FddA8itMCPpPjVOAo26bi/0WImHHYPyGxSH3QQIQaSdS5AFUNarpFc9M/W4Bu7k5h8sKb+vzZA3hMLFyvmQ2JtY8Zuqi1BBB33OAlkOHprJFTrrt1xa4a6KJjKXlDp5o5hOFgvZbsW+k/fBU0CLCbL3PlXQLhqHgEncpL9L9FF/xDbPIGftxjEWcBPFG/SuyygT35xRFMxB7ifR+wJZwVnoC4RhbhgJdL2EiUVT7wq5MTsxr2j4nAAJYatK/7OTecs6y/7bGNAGEoqh4Q3X3D95YFex0scmHgY1hWwIDAQAB ------END PUBLIC KEY----- \ No newline at end of file From 75ec741ac39e272ff9b5cfba97a06e76dc68df73 Mon Sep 17 00:00:00 2001 From: Peyman Date: Sat, 28 Aug 2021 15:56:59 +0430 Subject: [PATCH 38/59] close #44: Add CORS config for all endpoints --- .../co/nilin/opex/app/config/CorsConfig.kt | 20 +++++++++++++++++++ .../src/main/resources/application-docker.yml | 2 ++ .../binance/controller/AccountController.kt | 1 - 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 Api/api-app/src/main/kotlin/co/nilin/opex/app/config/CorsConfig.kt diff --git a/Api/api-app/src/main/kotlin/co/nilin/opex/app/config/CorsConfig.kt b/Api/api-app/src/main/kotlin/co/nilin/opex/app/config/CorsConfig.kt new file mode 100644 index 000000000..06a3078a4 --- /dev/null +++ b/Api/api-app/src/main/kotlin/co/nilin/opex/app/config/CorsConfig.kt @@ -0,0 +1,20 @@ +package co.nilin.opex.app.config + +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Configuration +import org.springframework.web.reactive.config.CorsRegistry +import org.springframework.web.reactive.config.WebFluxConfigurer + +@Configuration +class CorsConfig : WebFluxConfigurer { + + @Value("\${app.cors.allowed-hosts}") + private lateinit var hosts: Array + + override fun addCorsMappings(registry: CorsRegistry) { + registry.addMapping("/**") + .allowedOrigins(*hosts) + .allowedHeaders("*") + } + +} \ No newline at end of file diff --git a/Api/api-app/src/main/resources/application-docker.yml b/Api/api-app/src/main/resources/application-docker.yml index 0b62d8ebb..857c92425 100644 --- a/Api/api-app/src/main/resources/application-docker.yml +++ b/Api/api-app/src/main/resources/application-docker.yml @@ -13,6 +13,8 @@ spring: port: 8500 app: + cors: + allowed-hosts: https://opex.dev, http://localhost:3000 matching-gateway: url: lb://opex-gateway wallet: diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt index dd538ff2c..4682e3d32 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt @@ -28,7 +28,6 @@ import org.springframework.security.core.context.SecurityContext import org.springframework.web.bind.annotation.* @RestController -@CrossOrigin(origins = ["https://opex.dev", "http://localhost:3000"], allowedHeaders = ["*"]) class AccountController( val queryHandler: UserQueryHandler, val matchingGatewayProxy: MEGatewayProxy, From 02ef0fc8040d8244d712dc7b6636fce9fda540f2 Mon Sep 17 00:00:00 2001 From: Peyman Date: Sat, 28 Aug 2021 16:00:35 +0430 Subject: [PATCH 39/59] close #45: Fix BigDecimal problem for API --- .../api/postgres/impl/MarketQueryHandlerImpl.kt | 8 ++++---- .../api/postgres/impl/UserQueryHandlerImpl.kt | 17 +++++++---------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt index 6645c2ed6..bf7a37cab 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt @@ -102,10 +102,10 @@ class MarketQueryHandlerImpl( orderId ?: -1, -1, clientOrderId ?: "", - BigDecimal(price!!), - BigDecimal(quantity!!), - BigDecimal(executedQuantity!!), - BigDecimal(accumulativeQuoteQty ?: 0.0), + price!!.toBigDecimal(), + quantity!!.toBigDecimal(), + executedQuantity!!.toBigDecimal(), + (accumulativeQuoteQty ?: 0.0).toBigDecimal(), status!!.toOrderStatus(), constraint!!.toTimeInForce(), type!!.toApiOrderType(), diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt index 4dfafad98..5edb98389 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt @@ -118,21 +118,18 @@ class UserQueryHandlerImpl( order.orderId ?: -1, -1, order.clientOrderId ?: "", - BigDecimal(order.price!!), - BigDecimal(order.quantity!!), - BigDecimal(order.executedQuantity!!), - BigDecimal(order.accumulativeQuoteQty ?: 0.0), + order.price!!.toBigDecimal(), + order.quantity!!.toBigDecimal(), + order.executedQuantity!!.toBigDecimal(), + (order.accumulativeQuoteQty ?: 0.0).toBigDecimal(), order.status!!.toOrderStatus(), order.constraint!!.toTimeInForce(), order.type!!.toApiOrderType(), order.direction!!.toOrderSide(), null, null, - Date.from( - order.createDate!!.atZone(ZoneId.systemDefault()).toInstant() - ), - Date.from( - order.updateDate.atZone(ZoneId.systemDefault()).toInstant() - ), order.status.toOrderStatus().isWorking(), order.quoteQuantity!!.toBigDecimal() + Date.from(order.createDate!!.atZone(ZoneId.systemDefault()).toInstant()), + Date.from(order.updateDate.atZone(ZoneId.systemDefault()).toInstant()), + order.status.toOrderStatus().isWorking(), order.quoteQuantity!!.toBigDecimal() ) } \ No newline at end of file From dfa467d3a5c8bd0302926cffe45c10d42323aa65 Mon Sep 17 00:00:00 2001 From: metalicn20 Date: Sun, 29 Aug 2021 13:11:31 +0430 Subject: [PATCH 40/59] Close #23, Complete db config for bc-persister-postgres (#46) * Fix application class name * Fix and improve db models * Add table scripts * Add remained repository classes * Add uuid and (address, memo) UNIQUE constraints --- .../nilin/opex/bcgateway/app/BCGatewayApp.kt | 6 +- .../postgres/config/PostgresConfig.kt | 63 +++++++++++++++++-- .../postgres/dao/AddressTypeRepository.kt | 2 +- .../dao/AssignedAddressChainRepository.kt | 2 +- .../postgres/dao/AssignedAddressRepository.kt | 7 ++- .../postgres/dao/CachedAddressRepository.kt | 13 ++++ .../dao/ChainAddressTypeRepository.kt | 8 +++ .../postgres/dao/ChainEndpointRepository.kt | 8 +++ .../bcgateway/postgres/dao/ChainRepository.kt | 2 +- .../postgres/dao/ChainSyncRecordRepository.kt | 13 ++++ .../dao/ChainSyncScheduleRepository.kt | 13 ++++ .../postgres/model/AddressTypeModel.kt | 8 +-- .../postgres/model/CachedAddressModel.kt | 5 +- .../bcgateway/postgres/model/ChainModel.kt | 19 +++--- .../bcgateway/postgres/model/SyncModel.kt | 14 +++-- 15 files changed, 146 insertions(+), 37 deletions(-) create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/CachedAddressRepository.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainAddressTypeRepository.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainEndpointRepository.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncRecordRepository.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncScheduleRepository.kt diff --git a/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/BCGatewayApp.kt b/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/BCGatewayApp.kt index 3a906cb3a..3762d89a6 100644 --- a/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/BCGatewayApp.kt +++ b/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/BCGatewayApp.kt @@ -5,9 +5,9 @@ import org.springframework.boot.runApplication import org.springframework.context.annotation.ComponentScan @SpringBootApplication -@ComponentScan("co.nilin.mixchange") -class WalletApp +@ComponentScan("co.nilin.opex") +class BCGatewayApp fun main(args: Array) { - runApplication(*args) + runApplication(*args) } diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/config/PostgresConfig.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/config/PostgresConfig.kt index 66357545d..07ca7afa5 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/config/PostgresConfig.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/config/PostgresConfig.kt @@ -1,22 +1,73 @@ -package co.nilin.mixchange.port.order.kafka.config - +package co.nilin.opex.port.bcgateway.postgres.config import org.springframework.context.annotation.Configuration import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories import org.springframework.r2dbc.core.DatabaseClient - @Configuration -@EnableR2dbcRepositories(basePackages = ["co.nilin.mixchange"]) +@EnableR2dbcRepositories(basePackages = ["co.nilin.opex"]) class PostgresConfig(db: DatabaseClient) { init { val initDb = db.sql { """ + CREATE TABLE IF NOT EXISTS address_types ( + id SERIAL PRIMARY KEY, + address_type VARCHAR(72), + address_regex VARCHAR(72), + memo_regex VARCHAR(72) + ); + CREATE TABLE IF NOT EXISTS assigned_addresses ( + id SERIAL PRIMARY KEY, + uuid VARCHAR(72) UNIQUE, + address VARCHAR(72), + memo VARCHAR(72), + addr_type_id numeric, + UNIQUE (address, memo) + ); + CREATE TABLE IF NOT EXISTS assigned_address_chains ( + id SERIAL PRIMARY KEY, + assigned_address_id numeric, + chain VARCHAR(72) + ); + CREATE TABLE IF NOT EXISTS cached_addresses ( + id SERIAL PRIMARY KEY, + address VARCHAR(72), + memo VARCHAR(72), + address_type VARCHAR(72) + ); + CREATE TABLE IF NOT EXISTS chain ( + name VARCHAR(72) PRIMARY KEY + ); + CREATE TABLE IF NOT EXISTS chain_address_types ( + id SERIAL PRIMARY KEY, + chain_name VARCHAR(72), + addr_type_id numeric + ); + CREATE TABLE IF NOT EXISTS chain_endpoints ( + id SERIAL PRIMARY KEY, + chain_name VARCHAR(72), + endpoint_url VARCHAR(72), + endpoint_user VARCHAR(72), + endpoint_password VARCHAR(72) + ); + CREATE TABLE IF NOT EXISTS chain_sync_schedule ( + chain VARCHAR(72) PRIMARY KEY, + retry_time TIMESTAMP, + delay numeric + ); + CREATE TABLE IF NOT EXISTS chain_sync_record ( + chain VARCHAR(72) PRIMARY KEY, + time TIMESTAMP, + endpoint_url VARCHAR(72), + latest_block numeric, + success BOOLEAN, + error VARCHAR(100) + ); """ } initDb // initialize the database - .then() - .subscribe() // execute + .then() + .subscribe() // execute } } diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AddressTypeRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AddressTypeRepository.kt index 7c1f67d0a..657a2caf4 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AddressTypeRepository.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AddressTypeRepository.kt @@ -5,4 +5,4 @@ import org.springframework.data.repository.reactive.ReactiveCrudRepository import org.springframework.stereotype.Repository @Repository -interface AddressTypeRepository: ReactiveCrudRepository \ No newline at end of file +interface AddressTypeRepository : ReactiveCrudRepository \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AssignedAddressChainRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AssignedAddressChainRepository.kt index 65c973242..47b7d7524 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AssignedAddressChainRepository.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AssignedAddressChainRepository.kt @@ -9,7 +9,7 @@ import org.springframework.data.repository.reactive.ReactiveCrudRepository import org.springframework.stereotype.Repository @Repository -interface AssignedAddressChainRepository: ReactiveCrudRepository { +interface AssignedAddressChainRepository : ReactiveCrudRepository { @Query("select * from assigned_address_chains where assigned_address_id = :assignedAddress") fun findByAssignedAddress(@Param("assignedAddress") type: Long): Flow } \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AssignedAddressRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AssignedAddressRepository.kt index 1c0c1ba2e..96ca13fbf 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AssignedAddressRepository.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AssignedAddressRepository.kt @@ -8,8 +8,9 @@ import org.springframework.data.repository.reactive.ReactiveCrudRepository import org.springframework.stereotype.Repository @Repository -interface AssignedAddressRepository: ReactiveCrudRepository { +interface AssignedAddressRepository : ReactiveCrudRepository { @Query("select * from assigned_addresses where uuid = :uuid and addr_type_id in (:addressTypes)") - fun findByUuidAndAddressType(@Param("uuid") uuid: String - , @Param("addressTypes") types: List): Flow + fun findByUuidAndAddressType( + @Param("uuid") uuid: String, @Param("addressTypes") types: List + ): Flow } \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/CachedAddressRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/CachedAddressRepository.kt new file mode 100644 index 000000000..f3a30bdeb --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/CachedAddressRepository.kt @@ -0,0 +1,13 @@ +package co.nilin.opex.port.bcgateway.postgres.dao + +import co.nilin.opex.port.bcgateway.postgres.model.AssignedAddressChainModel +import co.nilin.opex.port.bcgateway.postgres.model.CachedAddressModel +import co.nilin.opex.port.bcgateway.postgres.model.ChainModel +import kotlinx.coroutines.flow.Flow +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository + +interface CachedAddressRepository : ReactiveCrudRepository { + +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainAddressTypeRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainAddressTypeRepository.kt new file mode 100644 index 000000000..5708cb225 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainAddressTypeRepository.kt @@ -0,0 +1,8 @@ +package co.nilin.opex.port.bcgateway.postgres.dao + +import co.nilin.opex.port.bcgateway.postgres.model.ChainAddressTypeModel +import org.springframework.data.repository.reactive.ReactiveCrudRepository + +interface ChainAddressTypeRepository : ReactiveCrudRepository { + +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainEndpointRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainEndpointRepository.kt new file mode 100644 index 000000000..9c43fe56c --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainEndpointRepository.kt @@ -0,0 +1,8 @@ +package co.nilin.opex.port.bcgateway.postgres.dao + +import co.nilin.opex.port.bcgateway.postgres.model.ChainEndpointModel +import org.springframework.data.repository.reactive.ReactiveCrudRepository + +interface ChainEndpointRepository : ReactiveCrudRepository { + +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainRepository.kt index cafa5bbf4..35de62148 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainRepository.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainRepository.kt @@ -7,6 +7,6 @@ import org.springframework.data.r2dbc.repository.Query import org.springframework.data.repository.query.Param import org.springframework.data.repository.reactive.ReactiveCrudRepository -interface ChainRepository: ReactiveCrudRepository { +interface ChainRepository : ReactiveCrudRepository { } \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncRecordRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncRecordRepository.kt new file mode 100644 index 000000000..94cb26e68 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncRecordRepository.kt @@ -0,0 +1,13 @@ +package co.nilin.opex.port.bcgateway.postgres.dao + +import co.nilin.opex.port.bcgateway.postgres.model.AssignedAddressChainModel +import co.nilin.opex.port.bcgateway.postgres.model.ChainModel +import co.nilin.opex.port.bcgateway.postgres.model.SyncRecord +import kotlinx.coroutines.flow.Flow +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository + +interface ChainSyncRecordRepository : ReactiveCrudRepository { + +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncScheduleRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncScheduleRepository.kt new file mode 100644 index 000000000..7455e08bd --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncScheduleRepository.kt @@ -0,0 +1,13 @@ +package co.nilin.opex.port.bcgateway.postgres.dao + +import co.nilin.opex.port.bcgateway.postgres.model.AssignedAddressChainModel +import co.nilin.opex.port.bcgateway.postgres.model.ChainModel +import co.nilin.opex.port.bcgateway.postgres.model.SyncScheduleModel +import kotlinx.coroutines.flow.Flow +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository + +interface ChainSyncScheduleRepository : ReactiveCrudRepository { + +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/AddressTypeModel.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/AddressTypeModel.kt index d2e0c5aa0..fc1995fe5 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/AddressTypeModel.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/AddressTypeModel.kt @@ -5,8 +5,8 @@ import org.springframework.data.relational.core.mapping.Table @Table("address_types") data class AddressTypeModel( - val id: Long? - , @Column("address_type") val type: String - , @Column("address_regex") val addressRegex: String - , @Column("memo_regex") val memoRegex: String + val id: Long?, + @Column("address_type") val type: String, + @Column("address_regex") val addressRegex: String, + @Column("memo_regex") val memoRegex: String ) \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/CachedAddressModel.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/CachedAddressModel.kt index 8f207c68d..fbb698b1b 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/CachedAddressModel.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/CachedAddressModel.kt @@ -4,7 +4,6 @@ import org.springframework.data.relational.core.mapping.Column import org.springframework.data.relational.core.mapping.Table @Table("cached_addresses") -data class CachedAddressModel(val address: String - , val memo: String? - , @Column("address_type") val type: Long +data class CachedAddressModel( + val id: Long?, val address: String, val memo: String?, @Column("address_type") val type: Long ) \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/ChainModel.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/ChainModel.kt index 5204e8d15..11c79f3e0 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/ChainModel.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/ChainModel.kt @@ -8,14 +8,15 @@ import org.springframework.data.relational.core.mapping.Table data class ChainModel(@Id val name: String) @Table("chain_address_types") -data class ChainAddressTypeModel(@Id val id: Long? -, @Column("chain_name") val chainName: String -, @Column("addr_type_id") val addressTypeId: Long) +data class ChainAddressTypeModel( + @Id val id: Long?, @Column("chain_name") val chainName: String, @Column("addr_type_id") val addressTypeId: Long +) @Table("chain_endpoints") -data class ChainEndpointModel(@Id val id: Long? - , @Column("chain_name") val chainName: String - , @Column("endpoint_url") val url: String - , @Column("endpoint_user") val user: String - , @Column("endpoint_password") val password: String - ) \ No newline at end of file +data class ChainEndpointModel( + @Id val id: Long?, + @Column("chain_name") val chainName: String, + @Column("endpoint_url") val url: String, + @Column("endpoint_user") val user: String, + @Column("endpoint_password") val password: String +) \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/SyncModel.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/SyncModel.kt index 3a4d53ec0..a131264ce 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/SyncModel.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/SyncModel.kt @@ -11,11 +11,13 @@ data class SyncScheduleModel( ) @Table("chain_sync_record") -data class SyncRecord(@Id @Column("chain") val chain: String -, val time: LocalDateTime -, @Column("endpoint_url") val endpointUrl: String -, @Column("latest_block") val latestBlock: Long? -, val success: Boolean -, val error: String?) +data class SyncRecord( + @Id @Column("chain") val chain: String, + val time: LocalDateTime, + @Column("endpoint_url") val endpointUrl: String, + @Column("latest_block") val latestBlock: Long?, + val success: Boolean, + val error: String? +) From a92721209b42d19f2c6db8133d106492db2a5979 Mon Sep 17 00:00:00 2001 From: Peyman Date: Sun, 29 Aug 2021 15:15:07 +0430 Subject: [PATCH 41/59] close #43: Removed authentication for market API and fixed isBuyerMaker field --- .../api/core/inout/MarketTradeResponse.kt | 15 +++++ .../opex/api/core/inout/TradeResponse.kt | 3 +- .../opex/api/core/spi/MarketQueryHandler.kt | 6 +- .../port/api/binance/config/SecurityConfig.kt | 2 + .../binance/controller/MarketController.kt | 5 +- .../port/api/postgres/dao/OrderRepository.kt | 3 + .../postgres/impl/MarketQueryHandlerImpl.kt | 56 ++++++------------- .../api/postgres/impl/UserQueryHandlerImpl.kt | 5 +- 8 files changed, 50 insertions(+), 45 deletions(-) create mode 100644 Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/MarketTradeResponse.kt diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/MarketTradeResponse.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/MarketTradeResponse.kt new file mode 100644 index 000000000..c8baafb1f --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/MarketTradeResponse.kt @@ -0,0 +1,15 @@ +package co.nilin.opex.api.core.inout + +import java.math.BigDecimal +import java.util.* + +data class MarketTradeResponse( + val symbol: String, + val id: Long, + val price: BigDecimal, + val qty: BigDecimal, + val quoteQty: BigDecimal, + val time: Date, + val isBestMatch: Boolean, + val isMakerBuyer: Boolean +) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/TradeResponse.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/TradeResponse.kt index 745407236..ffbb1d604 100644 --- a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/TradeResponse.kt +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/TradeResponse.kt @@ -16,5 +16,6 @@ data class TradeResponse( val time: Date, val isBuyer: Boolean, val isMaker: Boolean, - val isBestMatch: Boolean + val isBestMatch: Boolean, + val isMakerBuyer: Boolean ) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt index a01472f21..cd21d1283 100644 --- a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt @@ -1,7 +1,7 @@ package co.nilin.opex.api.core.spi +import co.nilin.opex.api.core.inout.MarketTradeResponse import co.nilin.opex.api.core.inout.QueryOrderResponse -import co.nilin.opex.api.core.inout.TradeResponse import kotlinx.coroutines.flow.Flow import java.security.Principal @@ -11,6 +11,8 @@ interface MarketQueryHandler { suspend fun openAskOrders(symbol: String, limit: Int): List - suspend fun recentTrades(principal: Principal, symbol: String, limit: Int): Flow + suspend fun lastOrder(symbol: String): QueryOrderResponse? + + suspend fun recentTrades(principal: Principal, symbol: String, limit: Int): Flow } \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt index 63cadfae7..69d71b71c 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt @@ -26,6 +26,8 @@ class SecurityConfig(private val webClient: WebClient) { .pathMatchers("/swagger-ui/**").permitAll() .pathMatchers("/swagger-resources/**").permitAll() .pathMatchers("/v2/api-docs").permitAll() + .pathMatchers("/v3/depth").permitAll() + .pathMatchers("/v3/trades").permitAll() .pathMatchers(HttpMethod.OPTIONS, "/**").permitAll() .pathMatchers("/**").hasAuthority("SCOPE_trust") .anyExchange().authenticated() diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt index dd0a4a346..993df536d 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt @@ -63,7 +63,8 @@ class MarketController( mappedAskOrders.add(mapped) } - return OrderBookResponse(-1, mappedBidOrders, mappedAskOrders) + val lastOrder = marketQueryHandler.lastOrder(localSymbol) + return OrderBookResponse(lastOrder?.orderId ?: -1, mappedBidOrders, mappedAskOrders) } @GetMapping("/v3/trades") @@ -87,7 +88,7 @@ class MarketController( it.qty, it.quoteQty, it.time.time, - it.isMaker && it.isBuyer, + it.isMakerBuyer, it.isBestMatch ) } diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt index 6623514dd..34a01ea9e 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt @@ -80,4 +80,7 @@ interface OrderRepository : ReactiveCrudRepository { @Param("statuses") status: Collection ): Flux + + @Query("select * from orders where symbol = :symbol order by create_date desc limit 1") + fun findLastOrderBySymbol(@Param("symbol") symbol: String): Mono } \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt index bf7a37cab..13315c863 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt @@ -1,5 +1,6 @@ package co.nilin.opex.port.api.postgres.impl +import co.nilin.opex.api.core.inout.MarketTradeResponse import co.nilin.opex.api.core.inout.OrderStatus import co.nilin.opex.api.core.inout.QueryOrderResponse import co.nilin.opex.api.core.inout.TradeResponse @@ -13,6 +14,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.reactive.awaitFirst import kotlinx.coroutines.reactive.awaitFirstOrElse +import kotlinx.coroutines.reactive.awaitFirstOrNull import org.springframework.stereotype.Component import java.math.BigDecimal import java.security.Principal @@ -47,52 +49,30 @@ class MarketQueryHandlerImpl( .map { it.asQueryOrderResponse() } } - override suspend fun recentTrades(principal: Principal, symbol: String, limit: Int): Flow { + override suspend fun lastOrder(symbol: String): QueryOrderResponse? { + return orderRepository.findLastOrderBySymbol(symbol) + .awaitFirstOrNull() + ?.asQueryOrderResponse() + } + + override suspend fun recentTrades(principal: Principal, symbol: String, limit: Int): Flow { return tradeRepository.findBySymbolSortDescendingByCreateDate(symbol, limit) .map { val takerOrder = orderRepository.findByOuid(it.takerOuid).awaitFirst() val makerOrder = orderRepository.findByOuid(it.makerOuid).awaitFirst() - val isMakerBuyer = makerOrder.direction == OrderDirection.ASK - TradeResponse( + val isMakerBuyer = makerOrder.direction == OrderDirection.BID + MarketTradeResponse( it.symbol, it.tradeId, - if (it.takerUuid == principal.name) { - takerOrder.orderId!! - } else { - makerOrder.orderId!! - }, - -1, - if (it.takerUuid == principal.name) { - it.takerPrice.toBigDecimal() - } else { - it.makerPrice.toBigDecimal() - }, + if (isMakerBuyer) it.makerPrice.toBigDecimal() else it.takerPrice.toBigDecimal(), it.matchedQuantity.toBigDecimal(), - if (isMakerBuyer) { + if (isMakerBuyer) makerOrder.quoteQuantity!!.toBigDecimal() - } else { - takerOrder.quoteQuantity!!.toBigDecimal() - }, - if (it.takerUuid == principal.name) { - it.takerCommision!!.toBigDecimal() - } else { - it.makerCommision!!.toBigDecimal() - }, - if (it.takerUuid == principal.name) { - it.takerCommisionAsset!! - } else { - it.makerCommisionAsset!! - }, - Date.from( - it.createDate.atZone(ZoneId.systemDefault()).toInstant() - ), - if (it.takerUuid == principal.name) { - OrderDirection.ASK == takerOrder.direction - } else { - OrderDirection.ASK == makerOrder.direction - }, - it.makerUuid == principal.name, - true + else + takerOrder.quoteQuantity!!.toBigDecimal(), + Date.from(it.createDate.atZone(ZoneId.systemDefault()).toInstant()), + true, + isMakerBuyer ) } } diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt index 5edb98389..8776e4a70 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt @@ -67,7 +67,7 @@ class UserQueryHandlerImpl( ).map { trade -> val takerOrder = orderRepository.findByOuid(trade.takerOuid).awaitFirst() val makerOrder = orderRepository.findByOuid(trade.makerOuid).awaitFirst() - val isMakerBuyer = makerOrder.direction == OrderDirection.ASK + val isMakerBuyer = makerOrder.direction == OrderDirection.BID TradeResponse( trade.symbol, trade.tradeId, @@ -107,7 +107,8 @@ class UserQueryHandlerImpl( OrderDirection.ASK == makerOrder.direction }, trade.makerUuid == principal.name, - true + true, + isMakerBuyer ) } } From 401641da88947d5f7765f5d294252100fcd89a10 Mon Sep 17 00:00:00 2001 From: Peyman Date: Mon, 30 Aug 2021 17:20:48 +0430 Subject: [PATCH 42/59] close #49: Add 'code' to api OrderStatus close #50: Group order book orders --- .../opex/api/core/inout/OrderBookResponse.kt | 8 ++++++ .../nilin/opex/api/core/inout/OrderEnums.kt | 16 ++++++------ .../opex/api/core/spi/MarketQueryHandler.kt | 5 ++-- .../binance/controller/MarketController.kt | 8 +++--- .../port/api/postgres/dao/OrderRepository.kt | 25 ++++++++++++++++--- .../postgres/impl/MarketQueryHandlerImpl.kt | 17 ++++++------- .../api/postgres/impl/UserQueryHandlerImpl.kt | 2 +- .../model/AggregatedOrderPriceModel.kt | 6 +++++ .../port/api/postgres/util/EnumExtensions.kt | 2 +- 9 files changed, 59 insertions(+), 30 deletions(-) create mode 100644 Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OrderBookResponse.kt create mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/AggregatedOrderPriceModel.kt diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OrderBookResponse.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OrderBookResponse.kt new file mode 100644 index 000000000..53e8f0c01 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OrderBookResponse.kt @@ -0,0 +1,8 @@ +package co.nilin.opex.api.core.inout + +import java.math.BigDecimal + +data class OrderBookResponse( + val price: BigDecimal?, + val quantity: BigDecimal? +) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OrderEnums.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OrderEnums.kt index 8c0423fa8..f3db22c13 100644 --- a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OrderEnums.kt +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OrderEnums.kt @@ -6,15 +6,15 @@ enum class TimeInForce { FOK, //Fill or Kill, An order will expire if the full order cannot be filled upon execution. } -enum class OrderStatus { +enum class OrderStatus(val code: Int) { - NEW, //The order has been accepted by the engine. - PARTIALLY_FILLED, //A part of the order has been filled. - FILLED, //The order has been completed. - CANCELED, //The order has been canceled by the user. - PENDING_CANCEL, //Currently unused - REJECTED, //The order was not accepted by the engine and not processed. - EXPIRED //The order was canceled according to the order type's rules (e.g. LIMIT FOK orders with no fill, LIMIT IOC or MARKET orders that partially fill) or by the exchange, (e.g. orders canceled during liquidation, orders canceled during maintenance) + NEW(1), //The order has been accepted by the engine. + PARTIALLY_FILLED(4), //A part of the order has been filled. + FILLED(5), //The order has been completed. + CANCELED(2), //The order has been canceled by the user. + PENDING_CANCEL(7), //Currently unused + REJECTED(3), //The order was not accepted by the engine and not processed. + EXPIRED(6) //The order was canceled according to the order type's rules (e.g. LIMIT FOK orders with no fill, LIMIT IOC or MARKET orders that partially fill) or by the exchange, (e.g. orders canceled during liquidation, orders canceled during maintenance) } enum class OrderType { diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt index cd21d1283..1cbd74161 100644 --- a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt @@ -1,15 +1,16 @@ package co.nilin.opex.api.core.spi import co.nilin.opex.api.core.inout.MarketTradeResponse +import co.nilin.opex.api.core.inout.OrderBookResponse import co.nilin.opex.api.core.inout.QueryOrderResponse import kotlinx.coroutines.flow.Flow import java.security.Principal interface MarketQueryHandler { - suspend fun openBidOrders(symbol: String, limit: Int): List + suspend fun openBidOrders(symbol: String, limit: Int): List - suspend fun openAskOrders(symbol: String, limit: Int): List + suspend fun openAskOrders(symbol: String, limit: Int): List suspend fun lastOrder(symbol: String): QueryOrderResponse? diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt index 993df536d..5bcfddeee 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt @@ -49,16 +49,16 @@ class MarketController( bidOrders.forEach { val mapped = arrayListOf().apply { - add(it.price) - add(it.origQty) + add(it.price ?: BigDecimal.ZERO) + add(it.quantity ?: BigDecimal.ZERO) } mappedBidOrders.add(mapped) } askOrders.forEach { val mapped = arrayListOf().apply { - add(it.price) - add(it.origQty) + add(it.price ?: BigDecimal.ZERO) + add(it.quantity ?: BigDecimal.ZERO) } mappedAskOrders.add(mapped) } diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt index 34a01ea9e..2a8ae9232 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt @@ -1,6 +1,7 @@ package co.nilin.opex.port.api.postgres.dao import co.nilin.opex.matching.core.model.OrderDirection +import co.nilin.opex.port.api.postgres.model.AggregatedOrderPriceModel import co.nilin.opex.port.api.postgres.model.OrderModel import kotlinx.coroutines.flow.Flow import org.springframework.data.r2dbc.repository.Query @@ -57,7 +58,15 @@ interface OrderRepository : ReactiveCrudRepository { endTime: Date? ): Flow - @Query("select * from orders where symbol = :symbol and side = :direction and status in (:statuses) order by price asc limit :limit") + @Query( + """ + select price, sum(quantity) as quantity from orders + where symbol = :symbol and side = :direction and status in (:statuses) + group by price + order by price asc + limit :limit + """ + ) fun findBySymbolAndDirectionAndStatusSortAscendingByPrice( @Param("symbol") symbol: String, @@ -67,9 +76,17 @@ interface OrderRepository : ReactiveCrudRepository { limit: Int, @Param("statuses") status: Collection - ): Flux + ): Flux - @Query("select * from orders where symbol = :symbol and side = :direction and status in (:statuses) order by price desc limit :limit") + @Query( + """ + select price, sum(quantity) as quantity from orders + where symbol = :symbol and side = :direction and status in (:statuses) + group by price + order by price desc + limit :limit + """ + ) fun findBySymbolAndDirectionAndStatusSortDescendingByPrice( @Param("symbol") symbol: String, @@ -79,7 +96,7 @@ interface OrderRepository : ReactiveCrudRepository { limit: Int, @Param("statuses") status: Collection - ): Flux + ): Flux @Query("select * from orders where symbol = :symbol order by create_date desc limit 1") fun findLastOrderBySymbol(@Param("symbol") symbol: String): Mono diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt index 13315c863..4dbad9a66 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt @@ -1,9 +1,6 @@ package co.nilin.opex.port.api.postgres.impl -import co.nilin.opex.api.core.inout.MarketTradeResponse -import co.nilin.opex.api.core.inout.OrderStatus -import co.nilin.opex.api.core.inout.QueryOrderResponse -import co.nilin.opex.api.core.inout.TradeResponse +import co.nilin.opex.api.core.inout.* import co.nilin.opex.api.core.spi.MarketQueryHandler import co.nilin.opex.matching.core.model.OrderDirection import co.nilin.opex.port.api.postgres.dao.OrderRepository @@ -27,26 +24,26 @@ class MarketQueryHandlerImpl( private val tradeRepository: TradeRepository, ) : MarketQueryHandler { - override suspend fun openBidOrders(symbol: String, limit: Int): List { + override suspend fun openBidOrders(symbol: String, limit: Int): List { return orderRepository.findBySymbolAndDirectionAndStatusSortDescendingByPrice( symbol, OrderDirection.BID, limit, - listOf(OrderStatus.NEW.ordinal, OrderStatus.PARTIALLY_FILLED.ordinal) + listOf(OrderStatus.NEW.code, OrderStatus.PARTIALLY_FILLED.code) ).collectList() .awaitFirstOrElse { emptyList() } - .map { it.asQueryOrderResponse() } + .map { OrderBookResponse(it.price?.toBigDecimal(), it.quantity?.toBigDecimal()) } } - override suspend fun openAskOrders(symbol: String, limit: Int): List { + override suspend fun openAskOrders(symbol: String, limit: Int): List { return orderRepository.findBySymbolAndDirectionAndStatusSortAscendingByPrice( symbol, OrderDirection.ASK, limit, - listOf(OrderStatus.NEW.ordinal, OrderStatus.PARTIALLY_FILLED.ordinal) + listOf(OrderStatus.NEW.code, OrderStatus.PARTIALLY_FILLED.code) ).collectList() .awaitFirstOrElse { emptyList() } - .map { it.asQueryOrderResponse() } + .map { OrderBookResponse(it.price?.toBigDecimal(), it.quantity?.toBigDecimal()) } } override suspend fun lastOrder(symbol: String): QueryOrderResponse? { diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt index 8776e4a70..2846d1f16 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt @@ -46,7 +46,7 @@ class UserQueryHandlerImpl( return orderRepository.findByUuidAndSymbolAndStatus( principal.name, symbol, - listOf(OrderStatus.NEW.ordinal, OrderStatus.PARTIALLY_FILLED.ordinal) + listOf(OrderStatus.NEW.code, OrderStatus.PARTIALLY_FILLED.code) ).filter { orderModel -> orderModel.constraint != null } .map { order -> orderToQueryResponse(order) } } diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/AggregatedOrderPriceModel.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/AggregatedOrderPriceModel.kt new file mode 100644 index 000000000..d9c72cb18 --- /dev/null +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/AggregatedOrderPriceModel.kt @@ -0,0 +1,6 @@ +package co.nilin.opex.port.api.postgres.model + +data class AggregatedOrderPriceModel( + val price: Double?, + val quantity: Double? +) \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/util/EnumExtensions.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/util/EnumExtensions.kt index fce203911..8c993b8db 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/util/EnumExtensions.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/util/EnumExtensions.kt @@ -35,7 +35,7 @@ fun OrderDirection.toOrderSide(): OrderSide { } fun OrderStatus.isWorking(): Boolean { - return listOf(OrderStatus.NEW, OrderStatus.PARTIALLY_FILLED).contains(this) + return listOf(OrderStatus.NEW, OrderStatus.PARTIALLY_FILLED).contains(this) } fun Int.toOrderStatus(): OrderStatus { From aa09093c109034d802566c18237634acfe8c29f0 Mon Sep 17 00:00:00 2001 From: Peyman Date: Tue, 31 Aug 2021 15:44:24 +0430 Subject: [PATCH 43/59] close #47: Ticker API implemented for 24h, 7d, 1 month duration --- .../core/inout}/AggregatedOrderPriceModel.kt | 2 +- .../api/core/inout/PriceChangeResponse.kt | 21 +++++ .../opex/api/core/spi/MarketQueryHandler.kt | 12 +-- .../port/api/binance/config/SecurityConfig.kt | 1 + .../binance/controller/MarketController.kt | 42 ++++++++- .../port/api/postgres/dao/OrderRepository.kt | 2 +- .../port/api/postgres/dao/TradeRepository.kt | 90 +++++++++++++++---- .../postgres/impl/MarketQueryHandlerImpl.kt | 45 +++++++++- .../api/postgres/model/TradeTickerData.kt | 33 +++++++ .../opex/utility/error/data/OpexError.kt | 3 +- 10 files changed, 224 insertions(+), 27 deletions(-) rename Api/{api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model => api-core/src/main/kotlin/co/nilin/opex/api/core/inout}/AggregatedOrderPriceModel.kt (66%) create mode 100644 Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/PriceChangeResponse.kt create mode 100644 Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/TradeTickerData.kt diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/AggregatedOrderPriceModel.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/AggregatedOrderPriceModel.kt similarity index 66% rename from Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/AggregatedOrderPriceModel.kt rename to Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/AggregatedOrderPriceModel.kt index d9c72cb18..4537ff914 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/AggregatedOrderPriceModel.kt +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/AggregatedOrderPriceModel.kt @@ -1,4 +1,4 @@ -package co.nilin.opex.port.api.postgres.model +package co.nilin.opex.api.core.inout data class AggregatedOrderPriceModel( val price: Double?, diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/PriceChangeResponse.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/PriceChangeResponse.kt new file mode 100644 index 000000000..666c38ded --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/PriceChangeResponse.kt @@ -0,0 +1,21 @@ +package co.nilin.opex.api.core.inout + +data class PriceChangeResponse( + val symbol: String, + val priceChange: Double = 0.0, + val priceChangePercent: Double = 0.0, + val weightedAvgPrice: Double = 0.0, + val lastPrice: Double = 0.0, + val lastQty: Double = 0.0, + val bidPrice: Double = 0.0, + val askPrice: Double = 0.0, + val openPrice: Double = 0.0, + val highPrice: Double = 0.0, + val lowPrice: Double = 0.0, + val volume: Double = 0.0, + val openTime: Long, + val closeTime: Long, + val firstId: Long = 0, + val lastId: Long = 0, + val count: Long = 0, +) diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt index 1cbd74161..daf2826a3 100644 --- a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt @@ -1,19 +1,21 @@ package co.nilin.opex.api.core.spi -import co.nilin.opex.api.core.inout.MarketTradeResponse -import co.nilin.opex.api.core.inout.OrderBookResponse -import co.nilin.opex.api.core.inout.QueryOrderResponse +import co.nilin.opex.api.core.inout.* import kotlinx.coroutines.flow.Flow -import java.security.Principal +import java.time.LocalDateTime interface MarketQueryHandler { + suspend fun getTradeTickerData(startFrom: LocalDateTime): List + + suspend fun getTradeTickerDataBySymbol(symbol: String, startFrom: LocalDateTime): PriceChangeResponse + suspend fun openBidOrders(symbol: String, limit: Int): List suspend fun openAskOrders(symbol: String, limit: Int): List suspend fun lastOrder(symbol: String): QueryOrderResponse? - suspend fun recentTrades(principal: Principal, symbol: String, limit: Int): Flow + suspend fun recentTrades(symbol: String, limit: Int): Flow } \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt index 69d71b71c..7c39a9c12 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt @@ -28,6 +28,7 @@ class SecurityConfig(private val webClient: WebClient) { .pathMatchers("/v2/api-docs").permitAll() .pathMatchers("/v3/depth").permitAll() .pathMatchers("/v3/trades").permitAll() + .pathMatchers("/v3/ticker/**").permitAll() .pathMatchers(HttpMethod.OPTIONS, "/**").permitAll() .pathMatchers("/**").hasAuthority("SCOPE_trust") .anyExchange().authenticated() diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt index 5bcfddeee..fddb8787a 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt @@ -3,6 +3,7 @@ package co.nilin.opex.port.api.binance.controller import co.nilin.opex.api.core.spi.MarketQueryHandler import co.nilin.opex.api.core.spi.SymbolMapper import co.nilin.opex.port.api.binance.data.OrderBookResponse +import co.nilin.opex.api.core.inout.PriceChangeResponse import co.nilin.opex.port.api.binance.data.RecentTradeResponse import co.nilin.opex.utility.error.data.OpexError import co.nilin.opex.utility.error.data.OpexException @@ -10,10 +11,16 @@ import co.nilin.opex.utility.error.data.throwError import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController import java.math.BigDecimal import java.security.Principal +import java.time.Instant +import java.time.LocalDateTime +import java.time.ZoneId +import java.util.* +import java.util.concurrent.TimeUnit import kotlin.collections.ArrayList @RestController @@ -23,6 +30,7 @@ class MarketController( ) { private val orderBookValidLimits = arrayListOf(5, 10, 20, 50, 100, 500, 1000, 5000) + private val validDurations = arrayListOf("24h", "7d", "1m") // Limit - Weight // 5, 10, 20, 50, 100 - 1 @@ -80,7 +88,7 @@ class MarketController( if (validLimit !in 1..1000) throwError(OpexError.InvalidLimitForRecentTrades) - return marketQueryHandler.recentTrades(principal, localSymbol, validLimit) + return marketQueryHandler.recentTrades(localSymbol, validLimit) .map { RecentTradeResponse( it.id, @@ -94,4 +102,36 @@ class MarketController( } } + @GetMapping("/v3/ticker/{duration}") + suspend fun priceChange( + @PathVariable("duration") + duration: String, + @RequestParam("symbol", required = false) + symbol: String?, + ): List { + val localSymbol = if (symbol.isNullOrEmpty()) + null + else + symbolMapper.unmap(symbol) ?: throw OpexException(OpexError.SymbolNotFound) + + if (!validDurations.contains(duration)) + throwError(OpexError.InvalidPriceChangeDuration) + + val now = Date().time + val before = when (duration) { + "24h" -> Date(now - TimeUnit.DAYS.toMillis(1)) + "7d" -> Date(now - TimeUnit.DAYS.toMillis(7)) + "1m" -> Date(now - TimeUnit.DAYS.toMillis(31)) + else -> Date(now - TimeUnit.DAYS.toMillis(1)) + } + + val instant = Instant.ofEpochMilli(before.time) + val startDate = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()) + + return if (symbol.isNullOrEmpty()) + marketQueryHandler.getTradeTickerData(startDate) + else + listOf(marketQueryHandler.getTradeTickerDataBySymbol(localSymbol!!, startDate)) + } + } \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt index 2a8ae9232..1c777bad3 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt @@ -1,7 +1,7 @@ package co.nilin.opex.port.api.postgres.dao import co.nilin.opex.matching.core.model.OrderDirection -import co.nilin.opex.port.api.postgres.model.AggregatedOrderPriceModel +import co.nilin.opex.api.core.inout.AggregatedOrderPriceModel import co.nilin.opex.port.api.postgres.model.OrderModel import kotlinx.coroutines.flow.Flow import org.springframework.data.r2dbc.repository.Query diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/TradeRepository.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/TradeRepository.kt index 7282b558f..db3965662 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/TradeRepository.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/TradeRepository.kt @@ -1,11 +1,15 @@ package co.nilin.opex.port.api.postgres.dao import co.nilin.opex.port.api.postgres.model.TradeModel +import co.nilin.opex.port.api.postgres.model.TradeTickerData import kotlinx.coroutines.flow.Flow import org.springframework.data.r2dbc.repository.Query import org.springframework.data.repository.query.Param import org.springframework.data.repository.reactive.ReactiveCrudRepository import org.springframework.stereotype.Repository +import reactor.core.publisher.Flux +import reactor.core.publisher.Mono +import java.time.LocalDateTime import java.util.* @Repository @@ -14,23 +18,24 @@ interface TradeRepository : ReactiveCrudRepository { @Query("select * from trades where :ouid in (taker_ouid, maker_ouid) ") fun findByOuid(@Param("ouid") ouid: String): Flow - @Query("select * from trades where :uuid in (taker_uuid, maker_uuid) " + - "and (:fromTrade is null or id > :fromTrade) " + - "and (:symbol is null or symbol = :symbol) " + - "and (:startTime is null or trade_date >= :startTime) " + - "and (:endTime is null or trade_date < :endTime)" + @Query( + "select * from trades where :uuid in (taker_uuid, maker_uuid) " + + "and (:fromTrade is null or id > :fromTrade) " + + "and (:symbol is null or symbol = :symbol) " + + "and (:startTime is null or trade_date >= :startTime) " + + "and (:endTime is null or trade_date < :endTime)" ) fun findByUuidAndSymbolAndTimeBetweenAndTradeIdGreaterThan( - @Param("uuid") - uuid: String, - @Param("symbol") - symbol: String?, - @Param("fromTrade") - fromTrade: Long?, - @Param("startTime") - startTime: Date?, - @Param("endTime") - endTime: Date? + @Param("uuid") + uuid: String, + @Param("symbol") + symbol: String?, + @Param("fromTrade") + fromTrade: Long?, + @Param("startTime") + startTime: Date?, + @Param("endTime") + endTime: Date? ): Flow @Query("select * from trades where symbol = :symbol order by create_date desc limit :limit") @@ -38,6 +43,59 @@ interface TradeRepository : ReactiveCrudRepository { @Param("symbol") symbol: String, @Param("limit") - limit:Int + limit: Int ): Flow + + @Query( + """ + select symbol, + (select taker_price from trades where create_date > :date and symbol=t.symbol order by create_date desc limit 1) - (select taker_price from trades where create_date > :date and symbol=t.symbol order by create_date asc limit 1) as price_change, + ((((select taker_price from trades where create_date > :date and symbol=t.symbol order by create_date desc limit 1) - (select taker_price from trades where create_date > :date and symbol=t.symbol order by create_date asc limit 1))/(select taker_price from trades where create_date > :date and symbol=t.symbol order by create_date asc limit 1))*100) as price_change_percent, + (sum(matched_quantity)/sum(taker_price)) as weighted_avg_price, + (select taker_price from trades where create_date > :date and symbol=t.symbol order by create_date asc limit 1) as last_price, + (select matched_quantity from trades where create_date > :date and symbol=t.symbol order by create_date asc limit 1) as last_qty, + (select price from orders where create_date > :date and symbol=t.symbol and (status=1 or status=4) and side='BID' order by create_date desc limit 1) as bid_price, + (select price from orders where create_date > :date and symbol=t.symbol and (status=1 or status=4) and side='ASK' order by create_date asc limit 1) as ask_price, + (select price from orders where create_date > :date and symbol=t.symbol and (status=1 or status=4) order by create_date desc limit 1) as open_price, + max(taker_price) as high_price, + min(taker_price) as low_price, + sum(matched_quantity) as volume, + (select id from trades where create_date > :date and symbol=t.symbol order by create_date asc limit 1) as first_id, + (select id from trades where create_date > :date and symbol=t.symbol order by create_date desc limit 1) as last_id, + count(id) as count + from trades as t + where create_date > :date + group by symbol + """ + ) + fun tradeTicker(@Param("date") createDate: LocalDateTime): Flux + + @Query( + """ + select symbol, + (select taker_price from trades where create_date > :date and symbol=:symbol order by create_date desc limit 1) - (select taker_price from trades where create_date > :date and symbol=:symbol order by create_date asc limit 1) as price_change, + ((((select taker_price from trades where create_date > :date and symbol=:symbol order by create_date desc limit 1) - (select taker_price from trades where create_date > :date and symbol=:symbol order by create_date asc limit 1))/(select taker_price from trades where create_date > :date and symbol=:symbol order by create_date asc limit 1))*100) as price_change_percent, + (sum(matched_quantity)/sum(taker_price)) as weighted_avg_price, + (select taker_price from trades where create_date > :date and symbol=:symbol order by create_date asc limit 1) as last_price, + (select matched_quantity from trades where create_date > :date and symbol=:symbol order by create_date asc limit 1) as last_qty, + (select price from orders where create_date > :date and symbol=t.symbol and (status=1 or status=4) and side='BID' order by create_date desc limit 1) as bid_price, + (select price from orders where create_date > :date and symbol=t.symbol and (status=1 or status=4) and side='ASK' order by create_date asc limit 1) as ask_price, + (select price from orders where create_date > :date and symbol=t.symbol and (status=1 or status=4) order by create_date desc limit 1) as open_price, + max(taker_price) as high_price, + min(taker_price) as low_price, + sum(matched_quantity) as volume, + (select id from trades where create_date > :date and symbol=:symbol order by create_date asc limit 1) as first_id, + (select id from trades where create_date > :date and symbol=:symbol order by create_date desc limit 1) as last_id, + count(id) as count + from trades as t + where create_date > :date and symbol = :symbol + group by symbol + """ + ) + fun tradeTickerBySymbol( + @Param("symbol") + symbol: String, + @Param("date") + createDate: LocalDateTime, + ): Mono } \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt index 4dbad9a66..ba0a966ed 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt @@ -6,6 +6,7 @@ import co.nilin.opex.matching.core.model.OrderDirection import co.nilin.opex.port.api.postgres.dao.OrderRepository import co.nilin.opex.port.api.postgres.dao.TradeRepository import co.nilin.opex.port.api.postgres.model.OrderModel +import co.nilin.opex.port.api.postgres.model.TradeTickerData import co.nilin.opex.port.api.postgres.util.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map @@ -13,9 +14,10 @@ import kotlinx.coroutines.reactive.awaitFirst import kotlinx.coroutines.reactive.awaitFirstOrElse import kotlinx.coroutines.reactive.awaitFirstOrNull import org.springframework.stereotype.Component +import java.time.LocalDateTime import java.math.BigDecimal -import java.security.Principal import java.time.ZoneId +import java.time.ZoneOffset import java.util.* @Component @@ -24,6 +26,25 @@ class MarketQueryHandlerImpl( private val tradeRepository: TradeRepository, ) : MarketQueryHandler { + override suspend fun getTradeTickerData(startFrom: LocalDateTime): List { + return tradeRepository.tradeTicker(startFrom) + .collectList() + .awaitFirstOrElse { emptyList() } + .map { it.asPriceChangeResponse(Date().time, startFrom.toInstant(ZoneOffset.UTC).toEpochMilli()) } + + } + + override suspend fun getTradeTickerDataBySymbol(symbol: String, startFrom: LocalDateTime): PriceChangeResponse { + return tradeRepository.tradeTickerBySymbol(symbol, startFrom) + .awaitFirstOrNull() + ?.asPriceChangeResponse(Date().time, startFrom.toInstant(ZoneOffset.UTC).toEpochMilli()) + ?: PriceChangeResponse( + symbol = symbol, + openTime = Date().time, + closeTime = startFrom.toInstant(ZoneOffset.UTC).toEpochMilli() + ) + } + override suspend fun openBidOrders(symbol: String, limit: Int): List { return orderRepository.findBySymbolAndDirectionAndStatusSortDescendingByPrice( symbol, @@ -52,7 +73,7 @@ class MarketQueryHandlerImpl( ?.asQueryOrderResponse() } - override suspend fun recentTrades(principal: Principal, symbol: String, limit: Int): Flow { + override suspend fun recentTrades(symbol: String, limit: Int): Flow { return tradeRepository.findBySymbolSortDescendingByCreateDate(symbol, limit) .map { val takerOrder = orderRepository.findByOuid(it.takerOuid).awaitFirst() @@ -94,4 +115,24 @@ class MarketQueryHandlerImpl( status.toOrderStatus().isWorking(), quoteQuantity!!.toBigDecimal() ) + + private fun TradeTickerData.asPriceChangeResponse(openTime: Long, closeTime: Long) = PriceChangeResponse( + symbol, + priceChange ?: 0.0, + priceChangePercent ?: 0.0, + weightedAvgPrice ?: 0.0, + lastPrice ?: 0.0, + lastQty ?: 0.0, + bidPrice ?: 0.0, + askPrice ?: 0.0, + openPrice ?: 0.0, + highPrice ?: 0.0, + lowPrice ?: 0.0, + volume ?: 0.0, + openTime, + closeTime, + firstId ?: -1, + lastId ?: -1, + count ?: 0 + ) } \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/TradeTickerData.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/TradeTickerData.kt new file mode 100644 index 000000000..c0e7bdd79 --- /dev/null +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/model/TradeTickerData.kt @@ -0,0 +1,33 @@ +package co.nilin.opex.port.api.postgres.model + +import org.springframework.data.relational.core.mapping.Column + +data class TradeTickerData( + val symbol: String, + @Column("price_change") + val priceChange: Double?, + @Column("price_change_percent") + val priceChangePercent: Double?, + @Column("weighted_avg_price") + val weightedAvgPrice: Double?, + @Column("last_price") + val lastPrice: Double?, + @Column("last_qty") + val lastQty: Double?, + @Column("bid_price") + val bidPrice: Double?, + @Column("ask_price") + val askPrice: Double?, + @Column("open_price") + val openPrice: Double?, + @Column("high_price") + val highPrice: Double?, + @Column("low_price") + val lowPrice: Double?, + val volume: Double?, + @Column("first_id") + val firstId: Long?, + @Column("last_id") + val lastId: Long?, + val count: Long?, +) diff --git a/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/OpexError.kt b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/OpexError.kt index 2c5e0bc5e..19b8db298 100644 --- a/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/OpexError.kt +++ b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/OpexError.kt @@ -31,7 +31,8 @@ enum class OpexError(val code: Int, val message: String?, val status: HttpStatus OrderNotFound(7001, "No order found", HttpStatus.NOT_FOUND), SymbolNotFound(7002, "No symbol found", HttpStatus.NOT_FOUND), InvalidLimitForOrderBook(7003, "Valid limits: [5, 10, 20, 50, 100, 500, 1000, 5000]", HttpStatus.BAD_REQUEST), - InvalidLimitForRecentTrades(7004, "Valid limits: 1 min - 1000 max", HttpStatus.BAD_REQUEST); + InvalidLimitForRecentTrades(7004, "Valid limits: 1 min - 1000 max", HttpStatus.BAD_REQUEST), + InvalidPriceChangeDuration(7005, "Valid durations: [24h, 7d, 1m]", HttpStatus.BAD_REQUEST); companion object { fun findByCode(code: Int?): OpexError? { From 564b171ad7a15060e87f6c07f2b551eb07666f30 Mon Sep 17 00:00:00 2001 From: Peyman Date: Wed, 1 Sep 2021 12:23:59 +0430 Subject: [PATCH 44/59] close #49: Fixed, but needs more testing --- .../port/api/postgres/impl/TradePersisterImpl.kt | 12 ++++++------ .../wallet/app/service/UserRegistrationService.kt | 10 ++++++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/TradePersisterImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/TradePersisterImpl.kt index 35e030d2e..776595716 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/TradePersisterImpl.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/TradePersisterImpl.kt @@ -73,10 +73,10 @@ class TradePersisterImpl(val tradeRepository: TradeRepository, val orderReposito trade.takerPrice.multiply( (trade.takerQuantity.minus(trade.takerRemainedQuantity)) ).toDouble(), - if (trade.takerRemainedQuantity == BigDecimal.ZERO) { - OrderStatus.PARTIALLY_FILLED.code - } else { + if (trade.takerRemainedQuantity.compareTo(BigDecimal.ZERO) == 0) { OrderStatus.FILLED.code + } else { + OrderStatus.PARTIALLY_FILLED.code }, existingOrder?.createDate, LocalDateTime.now() @@ -111,10 +111,10 @@ class TradePersisterImpl(val tradeRepository: TradeRepository, val orderReposito trade.makerPrice.multiply( (trade.makerQuantity.minus(trade.makerRemainedQuantity)) ).toDouble(), - if (trade.makerRemainedQuantity == BigDecimal.ZERO) { - OrderStatus.PARTIALLY_FILLED.code - } else { + if (trade.makerRemainedQuantity.compareTo(BigDecimal.ZERO) == 0) { OrderStatus.FILLED.code + } else { + OrderStatus.PARTIALLY_FILLED.code }, existingOrder?.createDate ?: LocalDateTime.now(), LocalDateTime.now() diff --git a/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/service/UserRegistrationService.kt b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/service/UserRegistrationService.kt index 60160335f..349ad7523 100644 --- a/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/service/UserRegistrationService.kt +++ b/Wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/service/UserRegistrationService.kt @@ -24,6 +24,16 @@ class UserRegistrationService( suspend fun registerNewUser(event: UserCreatedEvent): Wallet { val owner = walletOwnerManager.createWalletOwner(event.uuid, "${event.firstName} ${event.lastName}", "1") + + val btcSymbol = Symbol("btc") + //TODO REMOVE LATER + walletManager.createWallet( + owner, + Amount(btcSymbol, BigDecimal.ONE), + btcSymbol, + "main" + ) + return walletManager.createWallet( owner, Amount(symbol, amount), From e0b3994e329d1fdda5df14821780720d77b9dd82 Mon Sep 17 00:00:00 2001 From: Peyman Date: Wed, 1 Sep 2021 13:55:40 +0430 Subject: [PATCH 45/59] close #52: Fixed --- .../co/nilin/opex/port/api/postgres/dao/OrderRepository.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt index 1c777bad3..1c3824c93 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/OrderRepository.kt @@ -60,7 +60,7 @@ interface OrderRepository : ReactiveCrudRepository { @Query( """ - select price, sum(quantity) as quantity from orders + select price, (sum(quantity) - sum(executed_qty)) as quantity from orders where symbol = :symbol and side = :direction and status in (:statuses) group by price order by price asc @@ -80,7 +80,7 @@ interface OrderRepository : ReactiveCrudRepository { @Query( """ - select price, sum(quantity) as quantity from orders + select price, (sum(quantity) - sum(executed_qty)) as quantity from orders where symbol = :symbol and side = :direction and status in (:statuses) group by price order by price desc From 82fbbe330aadfe1e07a1b5706a302b7b49ebe784 Mon Sep 17 00:00:00 2001 From: Peyman Date: Sun, 5 Sep 2021 14:01:06 +0430 Subject: [PATCH 46/59] close #53: Fixed saving issues --- .../opex/accountant/core/inout/OrderStatus.kt | 45 +++++++++++--- .../api/postgres/impl/OrderPersisterImpl.kt | 58 +++++++++++++++---- 2 files changed, 83 insertions(+), 20 deletions(-) diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/inout/OrderStatus.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/inout/OrderStatus.kt index f7bf2f5fa..b554691b6 100644 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/inout/OrderStatus.kt +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/inout/OrderStatus.kt @@ -1,11 +1,40 @@ package co.nilin.opex.accountant.core.inout -enum class OrderStatus(val code: Int) { - REQUESTED(0), - NEW(1), //The order has been accepted by the engine. - PARTIALLY_FILLED(4), //A part of the order has been filled. - FILLED(5), //The order has been completed. - CANCELED(2), //The order has been canceled by the user. - REJECTED(3), //The order was not accepted by the engine and not processed. - EXPIRED(6) //The order was canceled according to the order type's rules (e.g. LIMIT FOK orders with no fill, LIMIT IOC or MARKET orders that partially fill) or by the exchange, (e.g. orders canceled during liquidation, orders canceled during maintenance) +enum class OrderStatus(val code: Int, private val orderOfAppearance: Int) { + + REQUESTED(0, 0), + NEW(1, 1), //The order has been accepted by the engine. + PARTIALLY_FILLED(4, 2), //A part of the order has been filled. + FILLED(5, 3), //The order has been completed. + CANCELED(2, 3), //The order has been canceled by the user. + REJECTED(3, 3), //The order was not accepted by the engine and not processed. + EXPIRED(6, 3); //The order was canceled according to the order type's rules (e.g. LIMIT FOK orders with no fill, LIMIT IOC or MARKET orders that partially fill) or by the exchange, (e.g. orders canceled during liquidation, orders canceled during maintenance) + + fun comesBefore(status: OrderStatus?): Boolean { + if (status == null) + return false + return orderOfAppearance < status.orderOfAppearance + } + + fun comesAfter(status: OrderStatus?): Boolean { + if (status == null) + return false + return orderOfAppearance > status.orderOfAppearance + } + + companion object { + fun fromCode(code: Int?): OrderStatus? { + if (code == null) + return null + return values().find { it.code == code } + } + } +} + +fun Int?.comesBefore(code: Int?): Boolean { + return OrderStatus.fromCode(this)?.comesBefore(OrderStatus.fromCode(code)) == true +} + +fun Int?.comesAfter(code: Int?): Boolean { + return OrderStatus.fromCode(this)?.comesAfter(OrderStatus.fromCode(code)) == true } \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/OrderPersisterImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/OrderPersisterImpl.kt index 0241c16fc..04d2d5241 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/OrderPersisterImpl.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/OrderPersisterImpl.kt @@ -1,7 +1,7 @@ package co.nilin.opex.port.api.postgres.impl -import co.nilin.opex.accountant.core.inout.OrderStatus import co.nilin.opex.accountant.core.inout.RichOrder +import co.nilin.opex.accountant.core.inout.comesAfter import co.nilin.opex.api.core.spi.OrderPersister import co.nilin.opex.port.api.postgres.dao.OrderRepository import co.nilin.opex.port.api.postgres.model.OrderModel @@ -12,17 +12,15 @@ import java.time.LocalDateTime @Component class OrderPersisterImpl(val orderRepository: OrderRepository) : OrderPersister { override suspend fun save(order: RichOrder) { + var alreadySaved = false val existingOrder = orderRepository .findByOuid(order.ouid) .awaitFirstOrNull() if (existingOrder == null || existingOrder.executedQuantity?.compareTo(order.executedQuantity.toDouble()) == -1 - || order.status == OrderStatus.REJECTED.code - || order.status == OrderStatus.CANCELED.code - || order.status == OrderStatus.EXPIRED.code - || order.status == OrderStatus.FILLED.code - || existingOrder.type == null - ) + || order.status.comesAfter(existingOrder.status) + ) { + alreadySaved = true orderRepository.save( OrderModel( existingOrder?.id, @@ -39,15 +37,51 @@ class OrderPersisterImpl(val orderRepository: OrderRepository) : OrderPersister order.direction, order.constraint, order.type, - order.price.toDouble(), - order.quantity.toDouble(), - order.quoteQuantity.toDouble(), - order.executedQuantity.toDouble(), - order.accumulativeQuoteQty.toDouble(), + existingOrder?.price ?: order.price.toDouble(), + existingOrder?.quantity ?: order.quantity.toDouble(), + existingOrder?.quoteQuantity ?: order.quoteQuantity.toDouble(), + existingOrder?.executedQuantity ?: order.executedQuantity.toDouble(), + existingOrder?.accumulativeQuoteQty ?: order.accumulativeQuoteQty.toDouble(), order.status, existingOrder?.createDate ?: LocalDateTime.now(), LocalDateTime.now() ) ).awaitFirstOrNull() + } + + existingOrder?.apply { + if ( + !alreadySaved && + (makerFee == null || takerFee == null || leftSideFraction == null + || rightSideFraction == null || constraint == null || type == null) + ) { + orderRepository.save( + OrderModel( + existingOrder.id, + existingOrder.ouid, + existingOrder.uuid, + null, + existingOrder.symbol, + existingOrder.orderId, + order.makerFee.toDouble(), + order.takerFee.toDouble(), + order.leftSideFraction.toDouble(), + order.rightSideFraction.toDouble(), + order.userLevel, + order.direction, + order.constraint, + order.type, + existingOrder.price, + existingOrder.quantity, + existingOrder.quoteQuantity, + existingOrder.executedQuantity, + existingOrder.accumulativeQuoteQty, + existingOrder.status, + existingOrder.createDate ?: LocalDateTime.now(), + existingOrder.updateDate + ) + ).awaitFirstOrNull() + } + } } } \ No newline at end of file From b55d0e30a11eb992306cd000d21147c21d224b01 Mon Sep 17 00:00:00 2001 From: Peyman Date: Sun, 5 Sep 2021 17:25:31 +0430 Subject: [PATCH 47/59] close #53: Part of previous commit --- .../api/postgres/impl/TradePersisterImpl.kt | 151 ++++++++++-------- .../matching/core/engine/SimpleOrderBook.kt | 22 +++ 2 files changed, 109 insertions(+), 64 deletions(-) diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/TradePersisterImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/TradePersisterImpl.kt index 776595716..1e0ce1a41 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/TradePersisterImpl.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/TradePersisterImpl.kt @@ -2,6 +2,7 @@ package co.nilin.opex.port.api.postgres.impl import co.nilin.opex.accountant.core.inout.OrderStatus import co.nilin.opex.accountant.core.inout.RichTrade +import co.nilin.opex.accountant.core.inout.comesBefore import co.nilin.opex.api.core.spi.TradePersister import co.nilin.opex.port.api.postgres.dao.OrderRepository import co.nilin.opex.port.api.postgres.dao.TradeRepository @@ -50,75 +51,97 @@ class TradePersisterImpl(val tradeRepository: TradeRepository, val orderReposito val existingOrder = orderRepository .findByOuid(trade.takerOuid) .awaitFirstOrNull() - orderRepository.save( - OrderModel( - existingOrder?.id, - trade.takerOuid, - trade.takerUuid, - null, - trade.pair, - trade.takerOrderId, - existingOrder?.makerFee, - existingOrder?.takerFee, - existingOrder?.leftSideFraction, - existingOrder?.rightSideFraction, - existingOrder?.userLevel, - trade.takerDirection, - existingOrder?.constraint, - existingOrder?.type, - trade.takerPrice.toDouble(), - trade.takerQuantity.toDouble(), - trade.takerQuoteQuantity.toDouble(), - (trade.takerQuantity.minus(trade.takerRemainedQuantity)).toDouble(), - trade.takerPrice.multiply( - (trade.takerQuantity.minus(trade.takerRemainedQuantity)) - ).toDouble(), - if (trade.takerRemainedQuantity.compareTo(BigDecimal.ZERO) == 0) { - OrderStatus.FILLED.code - } else { - OrderStatus.PARTIALLY_FILLED.code - }, - existingOrder?.createDate, - LocalDateTime.now() - ) - ).awaitFirstOrNull() + + val executedQuantity = (trade.takerQuantity.minus(trade.takerRemainedQuantity)).toDouble() + val status = if (trade.takerRemainedQuantity.compareTo(BigDecimal.ZERO) == 0) { + OrderStatus.FILLED.code + } else { + OrderStatus.PARTIALLY_FILLED.code + } + + if (existingOrder == null || existingOrder.status.comesBefore(status) || (existingOrder.executedQuantity + ?: 0.0) < executedQuantity + ) + orderRepository.save( + OrderModel( + existingOrder?.id, + trade.takerOuid, + trade.takerUuid, + null, + trade.pair, + trade.takerOrderId, + existingOrder?.makerFee, + existingOrder?.takerFee, + existingOrder?.leftSideFraction, + existingOrder?.rightSideFraction, + existingOrder?.userLevel, + trade.takerDirection, + existingOrder?.constraint, + existingOrder?.type, + trade.takerPrice.toDouble(), + trade.takerQuantity.toDouble(), + trade.takerQuoteQuantity.toDouble(), + (trade.takerQuantity.minus(trade.takerRemainedQuantity)).toDouble(), + trade.takerPrice.multiply( + (trade.takerQuantity.minus(trade.takerRemainedQuantity)) + ).toDouble(), + if (trade.takerRemainedQuantity.compareTo(BigDecimal.ZERO) == 0) { + OrderStatus.FILLED.code + } else { + OrderStatus.PARTIALLY_FILLED.code + }, + existingOrder?.createDate, + LocalDateTime.now() + ) + ).awaitFirstOrNull() } private suspend fun saveMakerOrder(trade: RichTrade) { val existingOrder = orderRepository .findByOuid(trade.makerOuid) .awaitFirstOrNull() - orderRepository.save( - OrderModel( - existingOrder?.id, - trade.makerOuid, - trade.makerUuid, - null, - trade.pair, - trade.makerOrderId, - existingOrder?.makerFee, - existingOrder?.takerFee, - existingOrder?.leftSideFraction, - existingOrder?.rightSideFraction, - existingOrder?.userLevel, - trade.makerDirection, - existingOrder?.constraint, - existingOrder?.type, - trade.makerPrice.toDouble(), - trade.makerQuantity.toDouble(), - trade.makerQuoteQuantity.toDouble(), - (trade.makerQuantity.minus(trade.makerRemainedQuantity)).toDouble(), - trade.makerPrice.multiply( - (trade.makerQuantity.minus(trade.makerRemainedQuantity)) - ).toDouble(), - if (trade.makerRemainedQuantity.compareTo(BigDecimal.ZERO) == 0) { - OrderStatus.FILLED.code - } else { - OrderStatus.PARTIALLY_FILLED.code - }, - existingOrder?.createDate ?: LocalDateTime.now(), - LocalDateTime.now() - ) - ).awaitFirstOrNull() + + val executedQuantity = (trade.makerQuantity.minus(trade.makerRemainedQuantity)).toDouble() + val status = if (trade.makerRemainedQuantity.compareTo(BigDecimal.ZERO) == 0) { + OrderStatus.FILLED.code + } else { + OrderStatus.PARTIALLY_FILLED.code + } + + if (existingOrder == null || existingOrder.status.comesBefore(status) || (existingOrder.executedQuantity + ?: 0.0) < executedQuantity + ) + orderRepository.save( + OrderModel( + existingOrder?.id, + trade.makerOuid, + trade.makerUuid, + null, + trade.pair, + trade.makerOrderId, + existingOrder?.makerFee, + existingOrder?.takerFee, + existingOrder?.leftSideFraction, + existingOrder?.rightSideFraction, + existingOrder?.userLevel, + trade.makerDirection, + existingOrder?.constraint, + existingOrder?.type, + trade.makerPrice.toDouble(), + trade.makerQuantity.toDouble(), + trade.makerQuoteQuantity.toDouble(), + (trade.makerQuantity.minus(trade.makerRemainedQuantity)).toDouble(), + trade.makerPrice.multiply( + (trade.makerQuantity.minus(trade.makerRemainedQuantity)) + ).toDouble(), + if (trade.makerRemainedQuantity.compareTo(BigDecimal.ZERO) == 0) { + OrderStatus.FILLED.code + } else { + OrderStatus.PARTIALLY_FILLED.code + }, + existingOrder?.createDate ?: LocalDateTime.now(), + LocalDateTime.now() + ) + ).awaitFirstOrNull() } } \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBook.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBook.kt index e5d2e8fb7..362fa9a4e 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBook.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBook.kt @@ -5,11 +5,14 @@ import co.nilin.opex.matching.core.eventh.events.* import co.nilin.opex.matching.core.inout.* import co.nilin.opex.matching.core.model.* import exchange.core2.collections.art.LongAdaptiveRadixTreeMap +import org.slf4j.LoggerFactory import java.util.* import java.util.concurrent.atomic.AtomicLong class SimpleOrderBook(val pair: Pair, var replayMode: Boolean) : OrderBook { + private val logger =LoggerFactory.getLogger(SimpleOrderBook::class.java) + val askOrders = LongAdaptiveRadixTreeMap() val bidOrders = LongAdaptiveRadixTreeMap() val orders = TreeMap() @@ -37,6 +40,14 @@ class SimpleOrderBook(val pair: Pair, var replayMode: Boolean) : OrderBook { data class Bucket(val price: Long, var totalQuantity: Long, var ordersCount: Long, var lastOrder: SimpleOrder) override fun handleNewOrderCommand(orderCommand: OrderCreateCommand): Order? { + logger.info("****************** new order received *******************") + logger.info("** order id: ${orderCommand.ouid}") + logger.info("** price: ${orderCommand.price}") + logger.info("** quantity: ${orderCommand.quantity}") + logger.info("** direction: ${orderCommand.direction}") + logger.info("*********************************************************") + println() + val order = when (orderCommand.matchConstraint) { MatchConstraint.GTC -> { if (orderCommand.orderType == OrderType.MARKET_ORDER) { @@ -116,6 +127,17 @@ class SimpleOrderBook(val pair: Pair, var replayMode: Boolean) : OrderBook { } } lastOrder = order + + logger.info("******************** command handled ********************") + logger.info("** ask orders size: ${askOrders.entriesList().size}") + logger.info("** bid orders size: ${bidOrders.entriesList().size}") + logger.info("** orders size: ${orders.size}") + logger.info("** bestAskOrder: ${bestAskOrder?.ouid}") + logger.info("** bestBidOrder: ${bestBidOrder?.ouid}") + logger.info("** lastOrder: ${lastOrder?.ouid}") + logger.info("********************************************************") + println() + return order } From 4dbe2245b91f18258cf662114275e35ab480b81f Mon Sep 17 00:00:00 2001 From: maryarm Date: Mon, 6 Sep 2021 22:56:38 +0200 Subject: [PATCH 48/59] Fixed #55: remove faulty setting of bestBidOrder in putInGTCQueue --- .../matching/core/engine/SimpleOrderBook.kt | 11 +++-- .../core/engine/SimpleOrderBookUnitTest.kt | 47 ++++++++++++++++--- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBook.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBook.kt index 362fa9a4e..1d9c802a7 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBook.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBook.kt @@ -295,13 +295,13 @@ class SimpleOrderBook(val pair: Pair, var replayMode: Boolean) : OrderBook { private fun putGtcInQueue(order: SimpleOrder): SimpleOrder { if (order.direction == OrderDirection.BID) { - return putGtcInQueue(order, bidOrders, { price, queue -> + return putGtcInQueue(order, bidOrders, bestBidOrder, { price, queue -> queue.getHigherValue(price) }) { newMakerOrder: SimpleOrder? -> bestBidOrder = newMakerOrder } } else { - return putGtcInQueue(order, askOrders, { price, queue -> + return putGtcInQueue(order, askOrders, bestAskOrder, { price, queue -> queue.getLowerValue(price) }) { newMakerOrder: SimpleOrder? -> bestAskOrder = newMakerOrder @@ -356,6 +356,7 @@ class SimpleOrderBook(val pair: Pair, var replayMode: Boolean) : OrderBook { fun putGtcInQueue(order: SimpleOrder, queue: LongAdaptiveRadixTreeMap, + bestOrder: SimpleOrder?, betterBucketSelector: (price: Long, queue: LongAdaptiveRadixTreeMap) -> Bucket?, setNewMarkerOrder: (SimpleOrder?) -> Unit ): SimpleOrder { @@ -396,9 +397,9 @@ class SimpleOrderBook(val pair: Pair, var replayMode: Boolean) : OrderBook { order.better = aboveBucketLastOrder order.worse = worseOrder } else { - if (bestBidOrder != null) - bestBidOrder!!.better = order - order.worse = bestBidOrder + if (bestOrder != null) + bestOrder.better = order + order.worse = bestOrder setNewMarkerOrder(order) } } diff --git a/MatchingEngine/matching-core/src/test/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBookUnitTest.kt b/MatchingEngine/matching-core/src/test/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBookUnitTest.kt index f442a0dd0..6cf55e36d 100644 --- a/MatchingEngine/matching-core/src/test/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBookUnitTest.kt +++ b/MatchingEngine/matching-core/src/test/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBookUnitTest.kt @@ -266,13 +266,48 @@ class SimpleOrderBookUnitTest { } @Test - fun givenOrderBookWithGtcAskOrder_whenGtcBidLimitOrderWithLessPriceCreated_then() { - //given + fun whenSample1SequenceOfOrdersOccurs_thenAllSuccess() { + val orderBook = SimpleOrderBook(ETH_BTC_PAIR, false) - orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, ETH_BTC_PAIR, 33333, 1000000, OrderDirection.ASK, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) - //when - orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, ETH_BTC_PAIR, 34482, 5000000, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) as SimpleOrderBook.SimpleOrder - //then + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, ETH_BTC_PAIR, 5000000, 10000, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) as SimpleOrderBook.SimpleOrder + Assertions.assertNotNull(orderBook.bestBidOrder) + Assertions.assertEquals(1, orderBook.bidOrders.entriesList().size) + Assertions.assertEquals(1, orderBook.orders.size) + + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, ETH_BTC_PAIR, 4900000, 20000, OrderDirection.ASK, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) as SimpleOrderBook.SimpleOrder + Assertions.assertNull(orderBook.bestBidOrder) + Assertions.assertNotNull(orderBook.bestAskOrder) + Assertions.assertEquals(0, orderBook.bidOrders.entriesList().size) + Assertions.assertEquals(1, orderBook.askOrders.entriesList().size) + Assertions.assertEquals(1, orderBook.orders.size) + + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, ETH_BTC_PAIR, 4800000, 10000, OrderDirection.ASK, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) as SimpleOrderBook.SimpleOrder + Assertions.assertNull(orderBook.bestBidOrder) + Assertions.assertNotNull(orderBook.bestAskOrder) + Assertions.assertEquals(0, orderBook.bidOrders.entriesList().size) + Assertions.assertEquals(2, orderBook.askOrders.entriesList().size) + Assertions.assertEquals(2, orderBook.orders.size) + + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, ETH_BTC_PAIR, 4850000, 20000, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) as SimpleOrderBook.SimpleOrder + Assertions.assertEquals(1, orderBook.bidOrders.entriesList().size) + Assertions.assertEquals(1, orderBook.askOrders.entriesList().size) + Assertions.assertEquals(2, orderBook.orders.size) + Assertions.assertNotNull(orderBook.bestBidOrder) + Assertions.assertNotNull(orderBook.bestAskOrder) + + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, ETH_BTC_PAIR, 4850100, 10000, OrderDirection.ASK, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) as SimpleOrderBook.SimpleOrder + Assertions.assertEquals(1, orderBook.bidOrders.entriesList().size) + Assertions.assertEquals(2, orderBook.askOrders.entriesList().size) + Assertions.assertEquals(3, orderBook.orders.size) + Assertions.assertNotNull(orderBook.bestBidOrder) + Assertions.assertNotNull(orderBook.bestAskOrder) + + orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, ETH_BTC_PAIR, 4849900, 10000, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) as SimpleOrderBook.SimpleOrder + Assertions.assertEquals(2, orderBook.bidOrders.entriesList().size) + Assertions.assertEquals(2, orderBook.askOrders.entriesList().size) + Assertions.assertEquals(4, orderBook.orders.size) + Assertions.assertNotNull(orderBook.bestBidOrder) + Assertions.assertNotNull(orderBook.bestAskOrder) } } \ No newline at end of file From 930e642658bad2ed816c65f8722418849fb96964 Mon Sep 17 00:00:00 2001 From: maryarm Date: Mon, 6 Sep 2021 23:10:25 +0200 Subject: [PATCH 49/59] Fix #56: used logger with param instead of println --- .../kotlin/co/nilin/opex/eventlog/app/config/AppConfig.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/EventLog/eventlog-app/src/main/kotlin/co/nilin/opex/eventlog/app/config/AppConfig.kt b/EventLog/eventlog-app/src/main/kotlin/co/nilin/opex/eventlog/app/config/AppConfig.kt index 19b2ffc19..82cb52f44 100644 --- a/EventLog/eventlog-app/src/main/kotlin/co/nilin/opex/eventlog/app/config/AppConfig.kt +++ b/EventLog/eventlog-app/src/main/kotlin/co/nilin/opex/eventlog/app/config/AppConfig.kt @@ -17,6 +17,7 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import java.util.concurrent.Executors +import org.slf4j.LoggerFactory @Configuration class AppConfig { @@ -84,11 +85,14 @@ class AppConfig { class EventlogTradeListener(val tradePersister: TradePersister) : TradeListener { + private val log = LoggerFactory.getLogger(EventlogTradeListener::class.java) + override fun id(): String { return "TradeListener" } override fun onTrade(tradeEvent: TradeEvent, partition: Int, offset: Long, timestamp: Long) { + log.debug("Receive TradeEvent {}", tradeEvent) runBlocking { tradePersister.saveTrade(tradeEvent) } @@ -99,11 +103,14 @@ class AppConfig { val orderPersister: OrderPersister, val eventPersister: EventPersister ) : EventListener { + private val log = LoggerFactory.getLogger(EventlogEventListener::class.java) + override fun id(): String { return "EventListener" } override fun onEvent(coreEvent: CoreEvent, partition: Int, offset: Long, timestamp: Long) { + log.debug("Receive CoreEvent {}", coreEvent) runBlocking { if (coreEvent is CreateOrderEvent) orderPersister.saveOrder(coreEvent) @@ -115,7 +122,6 @@ class AppConfig { orderPersister.cancelOrder(coreEvent) eventPersister.saveEvent(coreEvent) } - println("onEvent") } } } \ No newline at end of file From ec828660750e72cf60ed4a3f669532e494ce728e Mon Sep 17 00:00:00 2001 From: Ebrahim Hosseiny Fadaee Date: Sat, 11 Sep 2021 14:29:48 +0430 Subject: [PATCH 50/59] Close #25, Complete assign address service (#57) * Complete AssignAddressHandler implementation * Rename cached address to reserved address * Add unique constraint to reserved_addresses table * Fix unique constraint in assigned addresses * Improve sql initiate script * Fix persist method * Fix uuid unique constraint in assigned addresses * Add currency models * Add currency repositories * Fix currency implementation * Implement currency loader * Change currency table primary key to symbol * Add currency tables * Format Chain.kt * Fix symbol nullability in WalletSyncServiceImpl.kt * Add blockchain module to docker-compose * Fix blockchain module port configs * Fix bc-gateway docker config * Fix bc-gateway database name in docker config * Add missing @Repository attributes * Add bc-gateway-postgres dependency to bc-gateway-app * Update docker-compose config * Fix sql scripts * Fix blockchain gateway port * Pluralize db names * Fix chain model table names * Fix numeric types in database * Fix currency loader * Update chain repository * Add id to chain repository fetch method * Add chain model data to currency loader * Refactor currency loader * Apply DRY to currency loader * Fix null currency issue in currency loader * Handle not found currency --- BlockchainGateway/bc-gateway-app/pom.xml | 441 +++++++++--------- .../opex/bcgateway/app/config/AppConfig.kt | 4 +- .../src/main/resources/application-docker.yml | 2 +- .../src/main/resources/application.yml | 2 +- .../opex/bcgateway/core/api/InfoService.kt | 2 +- .../opex/bcgateway/core/model/Address.kt | 2 +- .../nilin/opex/bcgateway/core/model/Chain.kt | 15 +- .../core/service/AssignAddressServiceImpl.kt | 16 +- .../bcgateway/core/service/InfoServiceImpl.kt | 2 +- .../core/service/WalletSyncServiceImpl.kt | 4 +- .../core/spi/CachedAddressHandler.kt | 10 - .../opex/bcgateway/core/spi/CurrencyLoader.kt | 2 +- .../core/spi/ReservedAddressHandler.kt | 9 + .../AssignAddressServiceImplUnitTest.kt | 33 +- .../postgres/config/PostgresConfig.kt | 61 ++- .../postgres/dao/AssignedAddressRepository.kt | 6 + .../postgres/dao/CachedAddressRepository.kt | 13 - .../dao/ChainAddressTypeRepository.kt | 6 +- .../postgres/dao/ChainEndpointRepository.kt | 6 +- .../bcgateway/postgres/dao/ChainRepository.kt | 20 +- .../postgres/dao/ChainSyncRecordRepository.kt | 6 +- .../dao/ChainSyncScheduleRepository.kt | 6 +- .../dao/CurrencyImplementationRepository.kt | 24 + .../postgres/dao/CurrencyRepository.kt | 14 + .../postgres/dao/ReservedAddressRepository.kt | 8 + .../impl/AssignedAddressHandlerImpl.kt | 14 +- .../postgres/impl/CachedAddressHandlerImpl.kt | 15 - .../postgres/impl/CurrencyLoaderImpl.kt | 59 ++- .../impl/ReservedAddressHandlerImpl.kt | 17 + .../bcgateway/postgres/model/ChainModel.kt | 2 +- .../model/CurrencyImplementationModel.kt | 20 + .../bcgateway/postgres/model/CurrencyModel.kt | 12 + ...ddressModel.kt => ReservedAddressModel.kt} | 4 +- Deployment/docker-compose.yml | 45 +- 34 files changed, 550 insertions(+), 352 deletions(-) delete mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/CachedAddressHandler.kt create mode 100644 BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/ReservedAddressHandler.kt delete mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/CachedAddressRepository.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/CurrencyImplementationRepository.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/CurrencyRepository.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ReservedAddressRepository.kt delete mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/CachedAddressHandlerImpl.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/ReservedAddressHandlerImpl.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/CurrencyImplementationModel.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/CurrencyModel.kt rename BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/{CachedAddressModel.kt => ReservedAddressModel.kt} (81%) diff --git a/BlockchainGateway/bc-gateway-app/pom.xml b/BlockchainGateway/bc-gateway-app/pom.xml index fcd1200f7..71355c3df 100644 --- a/BlockchainGateway/bc-gateway-app/pom.xml +++ b/BlockchainGateway/bc-gateway-app/pom.xml @@ -1,227 +1,232 @@ - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.4.4 - - - co.nilin.opex.external - bc-gateway-app - 1.0-SNAPSHOT - bc-gateway-app - Blockchain gateway app of Opex + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + co.nilin.opex.external + bc-gateway-app + 1.0-SNAPSHOT + bc-gateway-app + Blockchain gateway app of Opex - - 1.8 - 1.4.31 - ${version} - ${version} - 2020.0.2 - + + 1.8 + 1.4.31 + ${version} + ${version} + 2020.0.2 + - - - org.springframework.boot - spring-boot-starter-webflux - - - com.fasterxml.jackson.module - jackson-module-kotlin - - - - org.jetbrains.kotlin - kotlin-stdlib - - - io.projectreactor.kotlin - reactor-kotlin-extensions - - - org.jetbrains.kotlin - kotlin-reflect - - - org.jetbrains.kotlin - kotlin-stdlib-jdk8 - - - org.jetbrains.kotlinx - kotlinx-coroutines-reactor - - - org.jetbrains.kotlinx - kotlinx-coroutines-core - - - org.springframework.boot - spring-boot-starter-test - test - - - org.junit.vintage - junit-vintage-engine - - - - - io.projectreactor - reactor-test - test - - - org.springframework.cloud - spring-cloud-starter-consul-all - - - org.springframework.boot - spring-boot-starter-actuator - - - org.springframework.boot - spring-boot-starter-security - - - org.springframework.boot - spring-boot-starter-oauth2-resource-server - - - org.bouncycastle - bcprov-jdk15on - 1.60 - - - co.nilin.opex.external - bc-gateway-core - ${bc-gateway.version} - - + + + org.springframework.boot + spring-boot-starter-webflux + + + com.fasterxml.jackson.module + jackson-module-kotlin + + + + org.jetbrains.kotlin + kotlin-stdlib + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + io.projectreactor + reactor-test + test + + + org.springframework.cloud + spring-cloud-starter-consul-all + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + + org.bouncycastle + bcprov-jdk15on + 1.60 + + + co.nilin.opex.external + bc-gateway-core + ${bc-gateway.version} + + + co.nilin.opex.external + bc-gateway-persister-postgres + ${bc-gateway.version} + + - - - - org.springframework.cloud - spring-cloud-dependencies - ${spring-cloud.version} - pom - import - - - + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + - - ${project.basedir}/src/main/kotlin - ${project.basedir}/src/test/kotlin - - - org.springframework.boot - spring-boot-maven-plugin - - - org.apache.maven.plugins - maven-surefire-plugin - 2.18 - - - ${skip.unit.tests} - - - **/*IntegrationTest.java - - - - - org.codehaus.mojo - build-helper-maven-plugin - - - add-test-source - generate-test-sources - - add-test-source - - - - src/test/java - - - - - compile - - add-source - - - - src/main/java - - - - - - - org.jetbrains.kotlin - kotlin-maven-plugin - ${kotlin.version} - - - compile - compile - - compile - - - - test-compile - test-compile - - test-compile - - - - - - -Xjsr305=strict - - - spring - - 1.8 - - - - org.jetbrains.kotlin - kotlin-maven-allopen - ${kotlin.version} - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - compile - compile - - compile - - - - testCompile - test-compile - - testCompile - - - - - - opex-bc-gateway - + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18 + + + ${skip.unit.tests} + + + **/*IntegrationTest.java + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-test-source + generate-test-sources + + add-test-source + + + + src/test/java + + + + + compile + + add-source + + + + src/main/java + + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + + -Xjsr305=strict + + + spring + + 1.8 + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + compile + compile + + compile + + + + testCompile + test-compile + + testCompile + + + + + + opex-bc-gateway + diff --git a/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/AppConfig.kt b/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/AppConfig.kt index fc5c07cff..58cc98ade 100644 --- a/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/AppConfig.kt +++ b/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/AppConfig.kt @@ -21,9 +21,9 @@ class AppConfig { fun assignAddressService( currencyLoader: CurrencyLoader, assignedAddressHandler: AssignedAddressHandler, - cachedAddressHandler: CachedAddressHandler + reservedAddressHandler: ReservedAddressHandler ): AssignAddressService { - return AssignAddressServiceImpl(currencyLoader, assignedAddressHandler, cachedAddressHandler) + return AssignAddressServiceImpl(currencyLoader, assignedAddressHandler, reservedAddressHandler) } @Bean diff --git a/BlockchainGateway/bc-gateway-app/src/main/resources/application-docker.yml b/BlockchainGateway/bc-gateway-app/src/main/resources/application-docker.yml index 37996be60..ffbf14176 100644 --- a/BlockchainGateway/bc-gateway-app/src/main/resources/application-docker.yml +++ b/BlockchainGateway/bc-gateway-app/src/main/resources/application-docker.yml @@ -1,4 +1,4 @@ -server.port: 8091 +server.port: 8095 spring: application: name: opex-bc-gateway diff --git a/BlockchainGateway/bc-gateway-app/src/main/resources/application.yml b/BlockchainGateway/bc-gateway-app/src/main/resources/application.yml index 2c544a1f5..c21510415 100644 --- a/BlockchainGateway/bc-gateway-app/src/main/resources/application.yml +++ b/BlockchainGateway/bc-gateway-app/src/main/resources/application.yml @@ -1,4 +1,4 @@ -server.port: 8091 +server.port: 8095 spring: application: name: opex-bc-gateway diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/api/InfoService.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/api/InfoService.kt index 6286ab830..838e2a7d8 100644 --- a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/api/InfoService.kt +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/api/InfoService.kt @@ -3,6 +3,6 @@ package co.nilin.opex.bcgateway.core.api import co.nilin.opex.bcgateway.core.model.CurrencyInfo interface InfoService { - suspend fun countCachedAddresses(): Long + suspend fun countReservedAddresses(): Long suspend fun getCurrencyInfo(): CurrencyInfo } \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Address.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Address.kt index 14572e8e9..ff7a125fe 100644 --- a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Address.kt +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Address.kt @@ -1,7 +1,7 @@ package co.nilin.opex.bcgateway.core.model data class AddressType(val id: Long, val type: String, val addressRegex: String, val memoRegex: String) -data class CachedAddress(val address: String, val memo: String?, val type: AddressType) +data class ReservedAddress(val address: String, val memo: String?, val type: AddressType) data class AssignedAddress(val uuid: String, val address: String, val memo: String?, val type: AddressType, val chains: MutableList ){ override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Chain.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Chain.kt index 73875ced3..c591687f8 100644 --- a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Chain.kt +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Chain.kt @@ -6,12 +6,13 @@ import java.time.LocalDateTime data class Endpoint(val url: String) data class Chain(val name: String, val addressTypes: List, val endpoints: List) data class ChainSyncSchedule(val chainName: String, val retryTime: LocalDateTime, val delay: Long) -data class ChainSyncRecord(val chainName: String - , val time: LocalDateTime - , val endpoint: Endpoint - , val latestBlock: Long? - , val success: Boolean - , val error: String? - , val records: List +data class ChainSyncRecord( + val chainName: String, + val time: LocalDateTime, + val endpoint: Endpoint, + val latestBlock: Long?, + val success: Boolean, + val error: String?, + val records: List ) diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/AssignAddressServiceImpl.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/AssignAddressServiceImpl.kt index 95dddaf43..ef4cb3a45 100644 --- a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/AssignAddressServiceImpl.kt +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/AssignAddressServiceImpl.kt @@ -6,14 +6,14 @@ import co.nilin.opex.bcgateway.core.model.AssignedAddress import co.nilin.opex.bcgateway.core.model.Chain import co.nilin.opex.bcgateway.core.model.Currency import co.nilin.opex.bcgateway.core.spi.AssignedAddressHandler -import co.nilin.opex.bcgateway.core.spi.CachedAddressHandler +import co.nilin.opex.bcgateway.core.spi.ReservedAddressHandler import co.nilin.opex.bcgateway.core.spi.CurrencyLoader import java.lang.RuntimeException class AssignAddressServiceImpl( val currencyLoader: CurrencyLoader, val assignedAddressHandler: AssignedAddressHandler, - val cachedAddressHandler: CachedAddressHandler + val reservedAddressHandler: ReservedAddressHandler ) : AssignAddressService { override suspend fun assignAddress(user: String, currency: Currency): List { @@ -42,19 +42,19 @@ class AssignAddressServiceImpl( } result.add(assigned) } else { - val cachedAddress = cachedAddressHandler.peekCachedAddress(addressType) - if (cachedAddress != null) { + val reservedAddress = reservedAddressHandler.peekReservedAddress(addressType) + if (reservedAddress != null) { val newAssigned = AssignedAddress( user, - cachedAddress.address, - cachedAddress.memo, + reservedAddress.address, + reservedAddress.memo, addressType, chainAddressTypeMap.get(addressType)!! ) - cachedAddressHandler.remove(cachedAddress) + reservedAddressHandler.remove(reservedAddress) result.add(newAssigned) } else - throw RuntimeException("No cached address available for $addressType") + throw RuntimeException("No reserved address available for $addressType") } } diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/InfoServiceImpl.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/InfoServiceImpl.kt index 5f4d53f49..e600f2369 100644 --- a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/InfoServiceImpl.kt +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/InfoServiceImpl.kt @@ -4,7 +4,7 @@ import co.nilin.opex.bcgateway.core.api.InfoService import co.nilin.opex.bcgateway.core.model.CurrencyInfo class InfoServiceImpl: InfoService { - override suspend fun countCachedAddresses(): Long { + override suspend fun countReservedAddresses(): Long { TODO() } diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/WalletSyncServiceImpl.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/WalletSyncServiceImpl.kt index 97d80774b..77228ea44 100644 --- a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/WalletSyncServiceImpl.kt +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/WalletSyncServiceImpl.kt @@ -26,9 +26,9 @@ class WalletSyncServiceImpl( deposits.map { deposit -> async(dispatcher) { val uuid = assignedAddressHandler.findUuid(deposit.depositor, deposit.depositorMemo) - if ( uuid != null ) { + if (uuid != null) { val symbol = currencyLoader.findSymbol(deposit.chain!!, deposit.tokenAddress) - walletProxy.transfer(uuid, symbol, deposit.amount) + if (symbol != null) walletProxy.transfer(uuid, symbol, deposit.amount) } } } diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/CachedAddressHandler.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/CachedAddressHandler.kt deleted file mode 100644 index c4271ed2b..000000000 --- a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/CachedAddressHandler.kt +++ /dev/null @@ -1,10 +0,0 @@ -package co.nilin.opex.bcgateway.core.spi - -import co.nilin.opex.bcgateway.core.model.AddressType -import co.nilin.opex.bcgateway.core.model.AssignedAddress -import co.nilin.opex.bcgateway.core.model.CachedAddress - -interface CachedAddressHandler { - suspend fun peekCachedAddress(addressType: AddressType): CachedAddress? - suspend fun remove(cacheAddress: CachedAddress) -} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/CurrencyLoader.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/CurrencyLoader.kt index 2b105b14f..32aa395f1 100644 --- a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/CurrencyLoader.kt +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/CurrencyLoader.kt @@ -5,6 +5,6 @@ import co.nilin.opex.bcgateway.core.model.CurrencyInfo interface CurrencyLoader { suspend fun fetchCurrencyInfo(symbol: String): CurrencyInfo - suspend fun findSymbol(chain: String, address: String?): String + suspend fun findSymbol(chain: String, address: String?): String? suspend fun findImplementationsWithTokenOnChain(chain: String): List } \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/ReservedAddressHandler.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/ReservedAddressHandler.kt new file mode 100644 index 000000000..67a3c837b --- /dev/null +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/ReservedAddressHandler.kt @@ -0,0 +1,9 @@ +package co.nilin.opex.bcgateway.core.spi + +import co.nilin.opex.bcgateway.core.model.AddressType +import co.nilin.opex.bcgateway.core.model.ReservedAddress + +interface ReservedAddressHandler { + suspend fun peekReservedAddress(addressType: AddressType): ReservedAddress? + suspend fun remove(reservedAddress: ReservedAddress) +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-core/src/test/kotlin/co/nilin/opex/bcgateway/core/service/AssignAddressServiceImplUnitTest.kt b/BlockchainGateway/bc-gateway-core/src/test/kotlin/co/nilin/opex/bcgateway/core/service/AssignAddressServiceImplUnitTest.kt index bd71823f0..be773908d 100644 --- a/BlockchainGateway/bc-gateway-core/src/test/kotlin/co/nilin/opex/bcgateway/core/service/AssignAddressServiceImplUnitTest.kt +++ b/BlockchainGateway/bc-gateway-core/src/test/kotlin/co/nilin/opex/bcgateway/core/service/AssignAddressServiceImplUnitTest.kt @@ -2,13 +2,13 @@ package co.nilin.opex.bcgateway.core.service import co.nilin.opex.bcgateway.core.model.AddressType import co.nilin.opex.bcgateway.core.model.AssignedAddress -import co.nilin.opex.bcgateway.core.model.CachedAddress +import co.nilin.opex.bcgateway.core.model.ReservedAddress import co.nilin.opex.bcgateway.core.model.Chain import co.nilin.opex.bcgateway.core.model.Currency import co.nilin.opex.bcgateway.core.model.CurrencyImplementation import co.nilin.opex.bcgateway.core.model.CurrencyInfo import co.nilin.opex.bcgateway.core.spi.AssignedAddressHandler -import co.nilin.opex.bcgateway.core.spi.CachedAddressHandler +import co.nilin.opex.bcgateway.core.spi.ReservedAddressHandler import co.nilin.opex.bcgateway.core.spi.CurrencyLoader import java.lang.RuntimeException import java.math.BigDecimal @@ -16,7 +16,6 @@ import java.util.UUID import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test -import org.junit.jupiter.api.function.Executable import org.mockito.Mock import org.mockito.Mockito import org.mockito.MockitoAnnotations @@ -29,7 +28,7 @@ class AssignAddressServiceImplUnitTest { lateinit var assignedAddressHandler: AssignedAddressHandler @Mock - lateinit var cachedAddressHandler: CachedAddressHandler + lateinit var reservedAddressHandler: ReservedAddressHandler val assignAddressServiceImpl: AssignAddressServiceImpl @@ -43,7 +42,7 @@ class AssignAddressServiceImplUnitTest { init { MockitoAnnotations.openMocks(this) assignAddressServiceImpl = AssignAddressServiceImpl( - currencyLoader, assignedAddressHandler, cachedAddressHandler + currencyLoader, assignedAddressHandler, reservedAddressHandler ) runBlocking { val eth = @@ -67,17 +66,17 @@ class AssignAddressServiceImplUnitTest { } @Test - fun givenCachedAddressAndUserWithNoAssignedAddress_whenAssignAddress_thenCachedAddressAssigned() { + fun givenReservedAddressAndUserWithNoAssignedAddress_whenAssignAddress_thenReservedAddressAssigned() { runBlocking { val user = UUID.randomUUID().toString() Mockito.`when`(assignedAddressHandler.fetchAssignedAddresses(user, listOf(ethAddressType, ethMemoAddressType))).thenReturn( emptyList() ) - Mockito.`when`(cachedAddressHandler.peekCachedAddress(ethAddressType)).thenReturn( - CachedAddress("0x1", null, ethAddressType) + Mockito.`when`(reservedAddressHandler.peekReservedAddress(ethAddressType)).thenReturn( + ReservedAddress("0x1", null, ethAddressType) ) - Mockito.`when`(cachedAddressHandler.peekCachedAddress(ethMemoAddressType)).thenReturn( - CachedAddress("0x2", "Memo", ethMemoAddressType) + Mockito.`when`(reservedAddressHandler.peekReservedAddress(ethMemoAddressType)).thenReturn( + ReservedAddress("0x2", "Memo", ethMemoAddressType) ) val assignedAddress = assignAddressServiceImpl.assignAddress(user, currency) Assertions.assertEquals( @@ -102,13 +101,13 @@ class AssignAddressServiceImplUnitTest { } @Test - fun givenNoCachedAddressAndUserWithNoAssignedAddress_whenAssignAddress_thenExcpetion() { + fun givenNoReservedAddressAndUserWithNoAssignedAddress_whenAssignAddress_thenExcpetion() { runBlocking { val user = UUID.randomUUID().toString() Mockito.`when`(assignedAddressHandler.fetchAssignedAddresses(user, listOf(ethAddressType, ethMemoAddressType))).thenReturn( emptyList() ) - Mockito.`when`(cachedAddressHandler.peekCachedAddress(ethAddressType)).thenReturn(null) + Mockito.`when`(reservedAddressHandler.peekReservedAddress(ethAddressType)).thenReturn(null) Assertions.assertThrows(RuntimeException::class.java) { runBlocking { @@ -119,7 +118,7 @@ class AssignAddressServiceImplUnitTest { } @Test - fun givenCachedAddressAndUserOneAssignedAddress_whenAssignAddress_thenCachedAddressAssigned() { + fun givenReservedAddressAndUserOneAssignedAddress_whenAssignAddress_thenReservedAddressAssigned() { runBlocking { val user = UUID.randomUUID().toString() Mockito.`when`(assignedAddressHandler.fetchAssignedAddresses(user, listOf(ethAddressType, ethMemoAddressType))).thenReturn( @@ -132,11 +131,11 @@ class AssignAddressServiceImplUnitTest { ) ) ) - Mockito.`when`(cachedAddressHandler.peekCachedAddress(ethAddressType)).thenReturn( - CachedAddress("0x1", null, ethAddressType) + Mockito.`when`(reservedAddressHandler.peekReservedAddress(ethAddressType)).thenReturn( + ReservedAddress("0x1", null, ethAddressType) ) - Mockito.`when`(cachedAddressHandler.peekCachedAddress(ethMemoAddressType)).thenReturn( - CachedAddress("0x2", "Memo", ethMemoAddressType) + Mockito.`when`(reservedAddressHandler.peekReservedAddress(ethMemoAddressType)).thenReturn( + ReservedAddress("0x2", "Memo", ethMemoAddressType) ) val assignedAddress = assignAddressServiceImpl.assignAddress(user, currency) Assertions.assertEquals( diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/config/PostgresConfig.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/config/PostgresConfig.kt index 07ca7afa5..0804207c9 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/config/PostgresConfig.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/config/PostgresConfig.kt @@ -13,57 +13,72 @@ class PostgresConfig(db: DatabaseClient) { """ CREATE TABLE IF NOT EXISTS address_types ( id SERIAL PRIMARY KEY, - address_type VARCHAR(72), - address_regex VARCHAR(72), - memo_regex VARCHAR(72) + address_type VARCHAR(72) NOT NULL, + address_regex VARCHAR(72) NOT NULL, + memo_regex VARCHAR(72) NOT NULL ); CREATE TABLE IF NOT EXISTS assigned_addresses ( id SERIAL PRIMARY KEY, - uuid VARCHAR(72) UNIQUE, - address VARCHAR(72), + uuid VARCHAR(72) NOT NULL, + address VARCHAR(72) NOT NULL, memo VARCHAR(72), - addr_type_id numeric, + addr_type_id INTEGER NOT NULL, UNIQUE (address, memo) ); CREATE TABLE IF NOT EXISTS assigned_address_chains ( id SERIAL PRIMARY KEY, - assigned_address_id numeric, - chain VARCHAR(72) + assigned_address_id INTEGER NOT NULL, + chain VARCHAR(72) NOT NULL ); - CREATE TABLE IF NOT EXISTS cached_addresses ( + CREATE TABLE IF NOT EXISTS reserved_addresses ( id SERIAL PRIMARY KEY, - address VARCHAR(72), + address VARCHAR(72) NOT NULL, memo VARCHAR(72), - address_type VARCHAR(72) + address_type VARCHAR(72) NOT NULL, + UNIQUE (address, memo) ); - CREATE TABLE IF NOT EXISTS chain ( + CREATE TABLE IF NOT EXISTS chains ( name VARCHAR(72) PRIMARY KEY ); CREATE TABLE IF NOT EXISTS chain_address_types ( id SERIAL PRIMARY KEY, - chain_name VARCHAR(72), - addr_type_id numeric + chain_name VARCHAR(72) NOT NULL REFERENCES chains (name), + addr_type_id INTEGER NOT NULL REFERENCES address_types (id) ); CREATE TABLE IF NOT EXISTS chain_endpoints ( id SERIAL PRIMARY KEY, - chain_name VARCHAR(72), - endpoint_url VARCHAR(72), + chain_name VARCHAR(72) NOT NULL, + endpoint_url VARCHAR(72) NOT NULL, endpoint_user VARCHAR(72), endpoint_password VARCHAR(72) ); - CREATE TABLE IF NOT EXISTS chain_sync_schedule ( + CREATE TABLE IF NOT EXISTS chain_sync_schedules ( chain VARCHAR(72) PRIMARY KEY, retry_time TIMESTAMP, - delay numeric + delay NUMERIC ); - CREATE TABLE IF NOT EXISTS chain_sync_record ( + CREATE TABLE IF NOT EXISTS chain_sync_records ( chain VARCHAR(72) PRIMARY KEY, - time TIMESTAMP, - endpoint_url VARCHAR(72), - latest_block numeric, - success BOOLEAN, + time TIMESTAMP NOT NULL, + endpoint_url VARCHAR(72) NOT NULL, + latest_block INTEGER NOT NULL, + success BOOLEAN NOT NULL, error VARCHAR(100) ); + CREATE TABLE IF NOT EXISTS currency ( + symbol VARCHAR(72) PRIMARY KEY, + name VARCHAR(72) NOT NULL + ); + CREATE TABLE IF NOT EXISTS currency_implementations ( + symbol VARCHAR(72) PRIMARY KEY, + chain VARCHAR(72) NOT NULL, + token BOOLEAN NOT NULL, + token_address VARCHAR(72), + token_name VARCHAR(72), + withdraw_enabled BOOLEAN NOT NULL, + withdraw_fee NUMERIC NOT NULL, + withdraw_min NUMERIC NOT NULL + ); """ } initDb // initialize the database diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AssignedAddressRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AssignedAddressRepository.kt index 96ca13fbf..e6e7e147c 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AssignedAddressRepository.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/AssignedAddressRepository.kt @@ -6,6 +6,7 @@ import org.springframework.data.r2dbc.repository.Query import org.springframework.data.repository.query.Param import org.springframework.data.repository.reactive.ReactiveCrudRepository import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono @Repository interface AssignedAddressRepository : ReactiveCrudRepository { @@ -13,4 +14,9 @@ interface AssignedAddressRepository : ReactiveCrudRepository ): Flow + + @Query("select * from assigned_addresses where address = :address and (:memo is null or memo = :memo)") + fun findByAddressAndMemo( + @Param("address") address: String, @Param("memo") memo: String? + ): Mono } \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/CachedAddressRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/CachedAddressRepository.kt deleted file mode 100644 index f3a30bdeb..000000000 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/CachedAddressRepository.kt +++ /dev/null @@ -1,13 +0,0 @@ -package co.nilin.opex.port.bcgateway.postgres.dao - -import co.nilin.opex.port.bcgateway.postgres.model.AssignedAddressChainModel -import co.nilin.opex.port.bcgateway.postgres.model.CachedAddressModel -import co.nilin.opex.port.bcgateway.postgres.model.ChainModel -import kotlinx.coroutines.flow.Flow -import org.springframework.data.r2dbc.repository.Query -import org.springframework.data.repository.query.Param -import org.springframework.data.repository.reactive.ReactiveCrudRepository - -interface CachedAddressRepository : ReactiveCrudRepository { - -} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainAddressTypeRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainAddressTypeRepository.kt index 5708cb225..2248a9474 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainAddressTypeRepository.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainAddressTypeRepository.kt @@ -2,7 +2,7 @@ package co.nilin.opex.port.bcgateway.postgres.dao import co.nilin.opex.port.bcgateway.postgres.model.ChainAddressTypeModel import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository -interface ChainAddressTypeRepository : ReactiveCrudRepository { - -} \ No newline at end of file +@Repository +interface ChainAddressTypeRepository : ReactiveCrudRepository \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainEndpointRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainEndpointRepository.kt index 9c43fe56c..88e5fdc26 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainEndpointRepository.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainEndpointRepository.kt @@ -2,7 +2,7 @@ package co.nilin.opex.port.bcgateway.postgres.dao import co.nilin.opex.port.bcgateway.postgres.model.ChainEndpointModel import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository -interface ChainEndpointRepository : ReactiveCrudRepository { - -} \ No newline at end of file +@Repository +interface ChainEndpointRepository : ReactiveCrudRepository \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainRepository.kt index 35de62148..9395b112a 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainRepository.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainRepository.kt @@ -1,12 +1,28 @@ package co.nilin.opex.port.bcgateway.postgres.dao -import co.nilin.opex.port.bcgateway.postgres.model.AssignedAddressChainModel +import co.nilin.opex.port.bcgateway.postgres.model.AddressTypeModel +import co.nilin.opex.port.bcgateway.postgres.model.ChainEndpointModel import co.nilin.opex.port.bcgateway.postgres.model.ChainModel import kotlinx.coroutines.flow.Flow import org.springframework.data.r2dbc.repository.Query -import org.springframework.data.repository.query.Param import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository +@Repository interface ChainRepository : ReactiveCrudRepository { + fun findByName(name: String): Flow + @Query( + """ + select address_types.id, chain_address_types.chain_name, address_types.address_type, address_types.address_regex, address_types.memo_regex + from chain_address_types + join address_types + on address_types.id = chain_address_types.addr_type_id + where chain_name = :name + """ + ) + fun findAddressTypesByName(name: String): Flow + + @Query("select * from chain_endpoints where chain_name = :name") + fun findEndpointsByName(name: String): Flow } \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncRecordRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncRecordRepository.kt index 94cb26e68..b3f4ee2b0 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncRecordRepository.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncRecordRepository.kt @@ -7,7 +7,7 @@ import kotlinx.coroutines.flow.Flow import org.springframework.data.r2dbc.repository.Query import org.springframework.data.repository.query.Param import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository -interface ChainSyncRecordRepository : ReactiveCrudRepository { - -} \ No newline at end of file +@Repository +interface ChainSyncRecordRepository : ReactiveCrudRepository \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncScheduleRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncScheduleRepository.kt index 7455e08bd..48a7a0252 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncScheduleRepository.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncScheduleRepository.kt @@ -7,7 +7,7 @@ import kotlinx.coroutines.flow.Flow import org.springframework.data.r2dbc.repository.Query import org.springframework.data.repository.query.Param import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository -interface ChainSyncScheduleRepository : ReactiveCrudRepository { - -} \ No newline at end of file +@Repository +interface ChainSyncScheduleRepository : ReactiveCrudRepository \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/CurrencyImplementationRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/CurrencyImplementationRepository.kt new file mode 100644 index 000000000..6d00f6a36 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/CurrencyImplementationRepository.kt @@ -0,0 +1,24 @@ +package co.nilin.opex.port.bcgateway.postgres.dao + +import co.nilin.opex.port.bcgateway.postgres.model.CurrencyImplementationModel +import kotlinx.coroutines.flow.Flow +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono + +@Repository +interface CurrencyImplementationRepository : ReactiveCrudRepository { + fun findBySymbol( + symbol: String + ): Flow + + fun findByChain( + chain: String + ): Flow + + fun findByChainAndTokenAddress( + chain: String, tokenAddress: String? + ): Mono +} diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/CurrencyRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/CurrencyRepository.kt new file mode 100644 index 000000000..f44e25b6b --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/CurrencyRepository.kt @@ -0,0 +1,14 @@ +package co.nilin.opex.port.bcgateway.postgres.dao + +import co.nilin.opex.port.bcgateway.postgres.model.CurrencyModel +import kotlinx.coroutines.flow.Flow +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono + +@Repository +interface CurrencyRepository : ReactiveCrudRepository { + fun findBySymbol(symbol: String): Mono +} diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ReservedAddressRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ReservedAddressRepository.kt new file mode 100644 index 000000000..a48a80366 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ReservedAddressRepository.kt @@ -0,0 +1,8 @@ +package co.nilin.opex.port.bcgateway.postgres.dao + +import co.nilin.opex.port.bcgateway.postgres.model.ReservedAddressModel +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository + +@Repository +interface ReservedAddressRepository : ReactiveCrudRepository \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/AssignedAddressHandlerImpl.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/AssignedAddressHandlerImpl.kt index 9c42d6695..1e7695999 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/AssignedAddressHandlerImpl.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/AssignedAddressHandlerImpl.kt @@ -7,9 +7,11 @@ import co.nilin.opex.bcgateway.core.spi.ChainLoader import co.nilin.opex.port.bcgateway.postgres.dao.AddressTypeRepository import co.nilin.opex.port.bcgateway.postgres.dao.AssignedAddressChainRepository import co.nilin.opex.port.bcgateway.postgres.dao.AssignedAddressRepository +import co.nilin.opex.port.bcgateway.postgres.model.AssignedAddressModel import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.toList import kotlinx.coroutines.reactive.awaitFirst +import kotlinx.coroutines.reactive.awaitFirstOrNull import org.springframework.stereotype.Service @Service @@ -42,10 +44,18 @@ class AssignedAddressHandlerImpl( } override suspend fun persist(assignedAddress: AssignedAddress) { - TODO("Not yet implemented") + assignedAddressRepository.save( + AssignedAddressModel( + null, + assignedAddress.uuid, + assignedAddress.address, + assignedAddress.memo, + assignedAddress.type.id + ) + ).awaitFirst() } override suspend fun findUuid(address: String, memo: String?): String? { - TODO("Not yet implemented") + return assignedAddressRepository.findByAddressAndMemo(address, memo).awaitFirstOrNull()?.uuid } } \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/CachedAddressHandlerImpl.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/CachedAddressHandlerImpl.kt deleted file mode 100644 index 52e868cfe..000000000 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/CachedAddressHandlerImpl.kt +++ /dev/null @@ -1,15 +0,0 @@ -package co.nilin.opex.port.bcgateway.postgres.impl - -import co.nilin.opex.bcgateway.core.model.AddressType -import co.nilin.opex.bcgateway.core.model.CachedAddress -import co.nilin.opex.bcgateway.core.spi.CachedAddressHandler - -class CachedAddressHandlerImpl: CachedAddressHandler { - override suspend fun peekCachedAddress(addressType: AddressType): CachedAddress? { - TODO("Not yet implemented") - } - - override suspend fun remove(cacheAddress: CachedAddress) { - TODO("Not yet implemented") - } -} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/CurrencyLoaderImpl.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/CurrencyLoaderImpl.kt index 0d519f77b..118329469 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/CurrencyLoaderImpl.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/CurrencyLoaderImpl.kt @@ -1,19 +1,62 @@ package co.nilin.opex.port.bcgateway.postgres.impl -import co.nilin.opex.bcgateway.core.model.CurrencyImplementation -import co.nilin.opex.bcgateway.core.model.CurrencyInfo +import co.nilin.opex.bcgateway.core.model.* import co.nilin.opex.bcgateway.core.spi.CurrencyLoader +import co.nilin.opex.port.bcgateway.postgres.dao.ChainRepository +import co.nilin.opex.port.bcgateway.postgres.dao.CurrencyImplementationRepository +import co.nilin.opex.port.bcgateway.postgres.dao.CurrencyRepository +import co.nilin.opex.port.bcgateway.postgres.model.CurrencyImplementationModel +import co.nilin.opex.port.bcgateway.postgres.model.CurrencyModel +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.reactive.awaitFirstOrNull +import kotlinx.coroutines.reactive.awaitSingle +import kotlinx.coroutines.reactive.awaitSingleOrNull +import org.springframework.stereotype.Component -class CurrencyLoaderImpl: CurrencyLoader { +@Component +class CurrencyLoaderImpl( + private val chainRepository: ChainRepository, + private val currencyRepository: CurrencyRepository, + private val currencyImplementationRepository: CurrencyImplementationRepository +) : CurrencyLoader { override suspend fun fetchCurrencyInfo(symbol: String): CurrencyInfo { - TODO("Not yet implemented") + val currencyDao = currencyRepository.findBySymbol(symbol).awaitSingleOrNull() + if (currencyDao === null) return CurrencyInfo(Currency("", symbol), emptyList()) + val currencyImplDao = currencyImplementationRepository.findBySymbol(symbol) + val currency = Currency(currencyDao.symbol, currencyDao.name) + val implementations = currencyImplDao.map { projectCurrencyImplementation(it, currencyDao) } + return CurrencyInfo(currency, implementations.toList()) } - override suspend fun findSymbol(chain: String, address: String?): String { - TODO("Not yet implemented") + override suspend fun findSymbol(chain: String, address: String?): String? { + return currencyImplementationRepository.findByChainAndTokenAddress(chain, address) + .awaitFirstOrNull()?.symbol } override suspend fun findImplementationsWithTokenOnChain(chain: String): List { - TODO("Not yet implemented") + return currencyImplementationRepository.findByChain(chain).map { projectCurrencyImplementation(it) }.toList() } -} \ No newline at end of file + + private suspend fun projectCurrencyImplementation( + implDao: CurrencyImplementationModel, + currencyDao: CurrencyModel? = null + ): CurrencyImplementation { + val addressTypesDao = chainRepository.findAddressTypesByName(implDao.chain) + val addressTypes = addressTypesDao.map { AddressType(it.id!!, it.type, it.addressRegex, it.memoRegex) } + val endpointsDao = chainRepository.findEndpointsByName(implDao.chain) + val endpoints = endpointsDao.map { Endpoint(it.url) } + val currencyDaoVal = currencyDao ?: currencyRepository.findBySymbol(implDao.symbol).awaitSingle() + val currency = Currency(currencyDaoVal.symbol, currencyDaoVal.name) + return CurrencyImplementation( + currency, + Chain(implDao.chain, addressTypes.toList(), endpoints.toList()), + implDao.token, + implDao.tokenAddress, + implDao.tokenName, + implDao.withdrawEnabled, + implDao.withdrawFee, + implDao.withdrawMin + ) + } +} diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/ReservedAddressHandlerImpl.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/ReservedAddressHandlerImpl.kt new file mode 100644 index 000000000..ed2e0e547 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/ReservedAddressHandlerImpl.kt @@ -0,0 +1,17 @@ +package co.nilin.opex.port.bcgateway.postgres.impl + +import co.nilin.opex.bcgateway.core.model.AddressType +import co.nilin.opex.bcgateway.core.model.ReservedAddress +import co.nilin.opex.bcgateway.core.spi.ReservedAddressHandler +import org.springframework.stereotype.Component + +@Component +class ReservedAddressHandlerImpl: ReservedAddressHandler { + override suspend fun peekReservedAddress(addressType: AddressType): ReservedAddress? { + TODO("Not yet implemented") + } + + override suspend fun remove(reservedAddress: ReservedAddress) { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/ChainModel.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/ChainModel.kt index 11c79f3e0..fb91ee878 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/ChainModel.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/ChainModel.kt @@ -4,7 +4,7 @@ import org.springframework.data.annotation.Id import org.springframework.data.relational.core.mapping.Column import org.springframework.data.relational.core.mapping.Table -@Table("chain") +@Table("chains") data class ChainModel(@Id val name: String) @Table("chain_address_types") diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/CurrencyImplementationModel.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/CurrencyImplementationModel.kt new file mode 100644 index 000000000..eeaaa6f59 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/CurrencyImplementationModel.kt @@ -0,0 +1,20 @@ +package co.nilin.opex.port.bcgateway.postgres.model + + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.math.BigDecimal + +@Table("currency_implementations") +class CurrencyImplementationModel( + @Id val id: Long?, + @Column("symbol") val symbol: String, + @Column("chain") val chain: String, + @Column("token") val token: Boolean, + @Column("token_address") val tokenAddress: String?, + @Column("token_name") val tokenName: String?, + @Column("withdraw_enabled") val withdrawEnabled: Boolean, + @Column("withdraw_fee") val withdrawFee: BigDecimal, + @Column("withdraw_min") val withdrawMin: BigDecimal, +) diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/CurrencyModel.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/CurrencyModel.kt new file mode 100644 index 000000000..354398e25 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/CurrencyModel.kt @@ -0,0 +1,12 @@ +package co.nilin.opex.port.bcgateway.postgres.model + + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table + +@Table("currency") +class CurrencyModel( + @Id @Column("symbol") val symbol: String, + @Column("name") val name: String +) diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/CachedAddressModel.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/ReservedAddressModel.kt similarity index 81% rename from BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/CachedAddressModel.kt rename to BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/ReservedAddressModel.kt index fbb698b1b..fb2d37358 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/CachedAddressModel.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/ReservedAddressModel.kt @@ -3,7 +3,7 @@ package co.nilin.opex.port.bcgateway.postgres.model import org.springframework.data.relational.core.mapping.Column import org.springframework.data.relational.core.mapping.Table -@Table("cached_addresses") -data class CachedAddressModel( +@Table("reserved_addresses") +data class ReservedAddressModel( val id: Long?, val address: String, val memo: String?, @Column("address_type") val type: Long ) \ No newline at end of file diff --git a/Deployment/docker-compose.yml b/Deployment/docker-compose.yml index 0dda34600..e10c538b7 100644 --- a/Deployment/docker-compose.yml +++ b/Deployment/docker-compose.yml @@ -45,16 +45,12 @@ services: condition: on-failure redis: image: "redis:alpine" - command: redis-server - ports: - "127.0.0.1:6379:6379" - volumes: - $PWD/runtime/redis-data:/var/lib/redis - $PWD/runtime/redis.conf:/usr/local/etc/redis/redis.conf - environment: - REDIS_REPLICATION_MODE=master networks: @@ -131,6 +127,21 @@ services: deploy: restart_policy: condition: on-failure + postgres-bc-gateway: + image: "postgres" + ports: + - 127.0.0.1:5438:5432 + environment: + - POSTGRES_USER=opex + - POSTGRES_PASSWORD=hiopex + - POSTGRES_DB=opex_bc_gateway + volumes: + - $PWD/runtime/bc-gateway-data:/var/lib/postgresql/data/ + networks: + - opex + deploy: + restart_policy: + condition: on-failure accountant: container_name: accountant build: @@ -310,6 +321,32 @@ services: - api networks: - opex + bc-gateway: + container_name: bc-gateway + build: + context: ../BlockchainGateway/bc-gateway-app + dockerfile: Dockerfile + ports: + - 127.0.0.1:8095:8095 + - 127.0.0.1:1051:1044 + environment: + - JAVA_OPTS=-Xmx256m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044 + - SPRING_PROFILES_ACTIVE=docker + - KAFKA_IP_PORT=kafka:9092 + - REDIS_HOST=redis + - CONSUL_HOST=consul + - DB_IP_PORT=postgres-bc-gateway + networks: + - opex + depends_on: + - zookeeper + - kafka + - redis + - consul + - postgres-bc-gateway + deploy: + restart_policy: + condition: on-failure networks: opex: driver: bridge \ No newline at end of file From 50e6312a368ffcb8588efc1e92413052dd358fc2 Mon Sep 17 00:00:00 2001 From: Peyman Date: Sat, 11 Sep 2021 14:29:50 +0430 Subject: [PATCH 51/59] close #54: Cancel order implemented --- .../core/service/OrderManagerImpl.kt | 4 +- .../opex/api/core/inout/CancelOrderRequest.kt | 3 + .../opex/api/core/inout/QueryOrderResponse.kt | 1 + .../nilin/opex/api/core/spi/MEGatewayProxy.kt | 3 + .../binance/controller/AccountController.kt | 67 +++++++++++++++++ .../api/binance/proxy/MEGatewayProxyImpl.kt | 15 ++++ .../postgres/impl/MarketQueryHandlerImpl.kt | 1 + .../api/postgres/impl/UserQueryHandlerImpl.kt | 1 + .../co/nilin/opex/app/config/AppConfig.kt | 61 +++++++++++++++- .../co/nilin/opex/app/config/AppSchedulers.kt | 1 + .../matching/core/engine/SimpleOrderBook.kt | 30 +++++--- .../order/kafka/config/OrderKafkaConfig.kt | 39 ++++++++-- .../kafka/consumer/EventKafkaListener.kt | 29 ++++++++ .../port/order/kafka/spi/EventListener.kt | 8 +++ .../opex/app/controller/OrderController.kt | 17 +++++ .../opex/app/inout/CancelOrderRequest.kt | 3 + .../co/nilin/opex/app/service/OrderService.kt | 71 +++++++++++-------- .../order/kafka/config/OrderKafkaConfig.kt | 16 +++-- .../order/kafka/service/EventSubmitter.kt | 28 ++++++++ 19 files changed, 344 insertions(+), 54 deletions(-) create mode 100644 Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/CancelOrderRequest.kt create mode 100644 MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/consumer/EventKafkaListener.kt create mode 100644 MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/spi/EventListener.kt create mode 100644 MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/inout/CancelOrderRequest.kt create mode 100644 MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/service/EventSubmitter.kt diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/service/OrderManagerImpl.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/service/OrderManagerImpl.kt index c1c4c0944..06638663f 100644 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/service/OrderManagerImpl.kt +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/service/OrderManagerImpl.kt @@ -131,13 +131,13 @@ open class OrderManagerImpl( order.origPrice.multiply( order.quantity.toBigDecimal().subtract(remainedQuantity) ), - (status ?: if (remainedQuantity.compareTo(BigDecimal.ZERO) == 0) { + status?.code ?: if (remainedQuantity.compareTo(BigDecimal.ZERO) == 0) { OrderStatus.FILLED.code } else if (remainedQuantity.compareTo(order.quantity.toBigDecimal()) == 0) { OrderStatus.NEW.code } else { OrderStatus.PARTIALLY_FILLED.code - }) as Int + } ) ) } diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/CancelOrderRequest.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/CancelOrderRequest.kt new file mode 100644 index 000000000..874e5ff99 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/CancelOrderRequest.kt @@ -0,0 +1,3 @@ +package co.nilin.opex.api.core.inout + +class CancelOrderRequest(val ouid: String, val uuid: String, val orderId: Long, val symbol: String) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/QueryOrderResponse.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/QueryOrderResponse.kt index 6edddbe15..72aa1a0d6 100644 --- a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/QueryOrderResponse.kt +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/QueryOrderResponse.kt @@ -5,6 +5,7 @@ import java.util.* data class QueryOrderResponse( val symbol: String, + val ouid: String, val orderId: Long, val orderListId: Long, //Unless part of an OCO, the value will always be -1. val clientOrderId: String, diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MEGatewayProxy.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MEGatewayProxy.kt index 2d268252d..7a427891d 100644 --- a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MEGatewayProxy.kt +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MEGatewayProxy.kt @@ -1,5 +1,6 @@ package co.nilin.opex.api.core.spi +import co.nilin.opex.api.core.inout.CancelOrderRequest import co.nilin.opex.api.core.inout.OrderSubmitResult import co.nilin.opex.matching.core.model.MatchConstraint import co.nilin.opex.matching.core.model.OrderDirection @@ -19,4 +20,6 @@ interface MEGatewayProxy { ) suspend fun createNewOrder(order: CreateOrderRequest, token: String?): OrderSubmitResult? + + suspend fun cancelOrder(request: CancelOrderRequest, token: String?): OrderSubmitResult? } \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt index 4682e3d32..afe9c8d43 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt @@ -15,6 +15,7 @@ import co.nilin.opex.port.api.binance.util.jwtAuthentication import co.nilin.opex.port.api.binance.util.tokenValue import co.nilin.opex.utility.error.data.OpexError import co.nilin.opex.utility.error.data.OpexException +import co.nilin.opex.utility.error.data.throwError import com.fasterxml.jackson.annotation.JsonInclude import io.swagger.annotations.* import java.math.BigDecimal @@ -60,6 +61,23 @@ class AccountController( val fills: List? ) + @JsonInclude(JsonInclude.Include.NON_NULL) + data class CancelOrderResponse( + val symbol: String, + val origClientOrderId: String?, + val orderId: Long?, + val orderListId: Long, //Unless OCO, value will be -1 + val clientOrderId: String?, + val price: BigDecimal?, + val origQty: BigDecimal?, + val executedQty: BigDecimal?, + val cummulativeQuoteQty: BigDecimal?, + val status: OrderStatus?, + val timeInForce: TimeInForce?, + val type: OrderType?, + val side: OrderSide? + ) + @JsonInclude(JsonInclude.Include.NON_NULL) data class QueryOrderResponse( val symbol: String, @@ -191,6 +209,55 @@ class AccountController( ) } + @DeleteMapping( + "/v3/order", + consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE], + produces = [MediaType.APPLICATION_JSON_VALUE] + ) + suspend fun cancelOrder( + principal: Principal, + @RequestParam(name = "symbol") + symbol: String, + @RequestParam(name = "orderId", required = false) + orderId: Long?, //Either orderId or origClientOrderId must be sent. + @RequestParam(name = "origClientOrderId", required = false) + origClientOrderId: String?, + @RequestParam(name = "newClientOrderId", required = false) + newClientOrderId: String?, + @ApiParam(value = "The value cannot be greater than 60000") + @RequestParam(name = "recvWindow", required = false) + recvWindow: Long?, //The value cannot be greater than 60000 + @RequestParam(name = "timestamp") + timestamp: Long, + @CurrentSecurityContext securityContext: SecurityContext + ): CancelOrderResponse { + val localSymbol = symbolMapper.unmap(symbol) ?: throw OpexException(OpexError.SymbolNotFound) + if (orderId == null && origClientOrderId == null) + throw OpexException(OpexError.BadRequest, message = "'orderId' or 'origClientOrderId' must be sent") + + val order = queryHandler.queryOrder(principal, QueryOrderRequest(localSymbol, orderId, origClientOrderId)) + ?: throw OpexException(OpexError.OrderNotFound) + + val request = CancelOrderRequest(order.ouid, principal.name, order.orderId, localSymbol) + matchingGatewayProxy.cancelOrder(request, securityContext.jwtAuthentication().tokenValue()) + + return CancelOrderResponse( + symbol, + origClientOrderId, + orderId, + -1, + null, + order.price, + order.origQty, + order.executedQty, + order.cummulativeQuoteQty, + order.status, + order.timeInForce, + order.type, + order.side + ) + } + /* Check an order's status. diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/proxy/MEGatewayProxyImpl.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/proxy/MEGatewayProxyImpl.kt index ff67200ff..4b1433cd7 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/proxy/MEGatewayProxyImpl.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/proxy/MEGatewayProxyImpl.kt @@ -1,5 +1,6 @@ package co.nilin.opex.port.api.binance.proxy +import co.nilin.opex.api.core.inout.CancelOrderRequest import co.nilin.opex.api.core.inout.OrderSubmitResult import co.nilin.opex.api.core.spi.MEGatewayProxy import co.nilin.opex.port.api.binance.util.LoggerDelegate @@ -37,4 +38,18 @@ class MEGatewayProxyImpl(private val client: WebClient) : MEGatewayProxy { .bodyToMono(typeRef()) .awaitSingleOrNull() } + + override suspend fun cancelOrder(request: CancelOrderRequest, token: String?): OrderSubmitResult? { + logger.info("calling matching-gateway order cancel") + return client.post() + .uri(URI.create("$baseUrl/order/cancel")) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer $token") + .body(Mono.just(request)) + .retrieve() + .onStatus({ t -> t.isError }, { it.createException() }) + .bodyToMono(typeRef()) + .awaitSingleOrNull() + } } \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt index ba0a966ed..7b940bc89 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt @@ -97,6 +97,7 @@ class MarketQueryHandlerImpl( private fun OrderModel.asQueryOrderResponse() = QueryOrderResponse( symbol, + ouid, orderId ?: -1, -1, clientOrderId ?: "", diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt index 2846d1f16..1b967dded 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/UserQueryHandlerImpl.kt @@ -116,6 +116,7 @@ class UserQueryHandlerImpl( private fun orderToQueryResponse(order: OrderModel) = QueryOrderResponse( order.symbol, + order.ouid, order.orderId ?: -1, -1, order.clientOrderId ?: "", diff --git a/MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/config/AppConfig.kt b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/config/AppConfig.kt index 81427366e..482f1c036 100644 --- a/MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/config/AppConfig.kt +++ b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/config/AppConfig.kt @@ -2,25 +2,31 @@ package co.nilin.opex.app.config import co.nilin.opex.app.bl.ExchangeEventHandler import co.nilin.opex.app.bl.OrderBooks +import co.nilin.opex.matching.core.eventh.events.* +import co.nilin.opex.matching.core.inout.OrderCancelCommand import co.nilin.opex.matching.core.inout.OrderCreateCommand +import co.nilin.opex.matching.core.inout.OrderEditCommand import co.nilin.opex.matching.core.model.PersistentOrderBook import co.nilin.opex.matching.core.spi.OrderBookPersister +import co.nilin.opex.port.order.kafka.consumer.EventKafkaListener import co.nilin.opex.port.order.kafka.consumer.OrderKafkaListener import co.nilin.opex.port.order.kafka.inout.OrderSubmitRequest +import co.nilin.opex.port.order.kafka.spi.EventListener import co.nilin.opex.port.order.kafka.spi.OrderSubmitRequestListener import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext +import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Value import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration -import kotlin.coroutines.EmptyCoroutineContext @Configuration class AppConfig { + @Value("\${spring.app.symbols}") private val symbols: String? = null @@ -69,12 +75,22 @@ class AppConfig { orderKafkaListener.addOrderListener(orderListener) } + @Bean + fun eventListener(orderBookPersister: OrderBookPersister): MatchingEngineEventListener { + return MatchingEngineEventListener(orderBookPersister) + } + + @Autowired + fun configureEventListener(eventKafkaListener: EventKafkaListener, eventListener: MatchingEngineEventListener) { + eventKafkaListener.addEventListener(eventListener) + } + @Autowired fun configureMatchingEngineListener(exchangeEventHandler: ExchangeEventHandler) { exchangeEventHandler.register() } - class OrderListener(val orderBookPersister: OrderBookPersister) : OrderSubmitRequestListener { + class OrderListener(private val orderBookPersister: OrderBookPersister) : OrderSubmitRequestListener { override fun id(): String { return "OrderListener" @@ -101,4 +117,45 @@ class AppConfig { } } + class MatchingEngineEventListener(private val orderBookPersister: OrderBookPersister) : EventListener { + + private val logger = LoggerFactory.getLogger(MatchingEngineEventListener::class.java) + + override fun id(): String { + return "EventListener" + } + + override fun onEvent(event: CoreEvent, partition: Int, offset: Long, timestamp: Long) { + logger.info("Received CoreEvent: ${event::class.java}") + + runBlocking(AppSchedulers.kafkaExecutor) { + val orderBook = OrderBooks.lookupOrderBook("${event.pair.leftSideName}_${event.pair.rightSideName}") + + when (event) { + is UpdatedOrderEvent -> orderBook.handleEditCommand( + OrderEditCommand( + event.ouid, + event.uuid, + event.orderId, + event.pair, + event.price, + event.quantity + ) + ) + + is CancelOrderEvent -> orderBook.handleCancelCommand( + OrderCancelCommand( + event.ouid, + event.uuid, + event.orderId, + event.pair + ) + ) + } + + orderBookPersister.storeLastState(orderBook.persistent()) + } + } + } + } \ No newline at end of file diff --git a/MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/config/AppSchedulers.kt b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/config/AppSchedulers.kt index 5162ee071..8aa5390f3 100644 --- a/MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/config/AppSchedulers.kt +++ b/MatchingEngine/matching-app/src/main/kotlin/co/nilin/opex/app/config/AppSchedulers.kt @@ -5,4 +5,5 @@ import java.util.concurrent.Executors object AppSchedulers { val generalExecutor = Executors.newFixedThreadPool(5).asCoroutineDispatcher() + val kafkaExecutor = Executors.newSingleThreadExecutor().asCoroutineDispatcher() } \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBook.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBook.kt index 1d9c802a7..dc38be548 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBook.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBook.kt @@ -127,21 +127,16 @@ class SimpleOrderBook(val pair: Pair, var replayMode: Boolean) : OrderBook { } } lastOrder = order - - logger.info("******************** command handled ********************") - logger.info("** ask orders size: ${askOrders.entriesList().size}") - logger.info("** bid orders size: ${bidOrders.entriesList().size}") - logger.info("** orders size: ${orders.size}") - logger.info("** bestAskOrder: ${bestAskOrder?.ouid}") - logger.info("** bestBidOrder: ${bestBidOrder?.ouid}") - logger.info("** lastOrder: ${lastOrder?.ouid}") - logger.info("********************************************************") - println() - + logCurrentState() return order } override fun handleCancelCommand(orderCommand: OrderCancelCommand) { + logger.info("********************* order cancel **********************") + logger.info("** order id: ${orderCommand.ouid}") + logger.info("*********************************************************") + println() + val order = orders.remove(orderCommand.orderId) if (order == null /*check for userid*/) { if (!replayMode) { @@ -166,6 +161,7 @@ class SimpleOrderBook(val pair: Pair, var replayMode: Boolean) : OrderBook { order.matchConstraint, order.orderType )) } + logCurrentState() } override fun handleEditCommand(orderCommand: OrderEditCommand): Order? { @@ -450,4 +446,16 @@ class SimpleOrderBook(val pair: Pair, var replayMode: Boolean) : OrderBook { }?.forEach { order -> putGtcInQueue(order) } orderCounter.set(persistentOrderBook.lastOrder?.id?:0) } + + private fun logCurrentState(){ + logger.info("******************** command handled ********************") + logger.info("** ask orders size: ${askOrders.entriesList().size}") + logger.info("** bid orders size: ${bidOrders.entriesList().size}") + logger.info("** orders size: ${orders.size}") + logger.info("** bestAskOrder: ${bestAskOrder?.ouid}") + logger.info("** bestBidOrder: ${bestBidOrder?.ouid}") + logger.info("** lastOrder: ${lastOrder?.ouid}") + logger.info("*********************************************************") + println() + } } \ No newline at end of file diff --git a/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/config/OrderKafkaConfig.kt b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/config/OrderKafkaConfig.kt index 4ca2c9563..b2ad1fae5 100644 --- a/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/config/OrderKafkaConfig.kt +++ b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/config/OrderKafkaConfig.kt @@ -1,5 +1,7 @@ package co.nilin.opex.port.order.kafka.config +import co.nilin.opex.matching.core.eventh.events.CoreEvent +import co.nilin.opex.port.order.kafka.consumer.EventKafkaListener import co.nilin.opex.port.order.kafka.consumer.OrderKafkaListener import co.nilin.opex.port.order.kafka.inout.OrderSubmitRequest import org.apache.kafka.clients.admin.NewTopic @@ -21,14 +23,17 @@ import org.springframework.kafka.core.DefaultKafkaProducerFactory import org.springframework.kafka.core.KafkaAdmin import org.springframework.kafka.core.KafkaTemplate import org.springframework.kafka.core.ProducerFactory +import org.springframework.kafka.listener.ConcurrentMessageListenerContainer import org.springframework.kafka.listener.ContainerProperties import org.springframework.kafka.listener.KafkaMessageListenerContainer import org.springframework.kafka.support.serializer.JsonDeserializer import org.springframework.kafka.support.serializer.JsonSerializer import java.util.* +import java.util.regex.Pattern @Configuration -class OrderKafkaConfig() { +class OrderKafkaConfig { + @Value("\${spring.kafka.bootstrap-servers}") private lateinit var bootstrapServers: String @@ -42,11 +47,11 @@ class OrderKafkaConfig() { private val applicationContext: GenericApplicationContext? = null @Autowired - fun createTopics(){ + fun createTopics() { symbols!!.split(",").map { s -> "orders_$s" } - .map { topic -> - applicationContext?.registerBean("topic_${topic}", NewTopic::class.java, topic, 1 ,1) - } + .map { topic -> + applicationContext?.registerBean("topic_${topic}", NewTopic::class.java, topic, 1, 1) + } } @Bean("orderProducerConfigs") @@ -84,14 +89,34 @@ class OrderKafkaConfig() { return DefaultKafkaConsumerFactory(consumerConfigs) } + @Bean("eventConsumerFactory") + fun eventConsumerFactory(@Qualifier("orderConsumerConfigs") consumerConfigs: Map): ConsumerFactory { + return DefaultKafkaConsumerFactory(consumerConfigs) + } @Autowired - fun configureListener(orderKafkaListener: OrderKafkaListener, @Qualifier("orderConsumerFactory") consumerFactory: ConsumerFactory, kafkaAdmin: KafkaAdmin) { + fun configureListener( + orderKafkaListener: OrderKafkaListener, + @Qualifier("orderConsumerFactory") consumerFactory: ConsumerFactory, + kafkaAdmin: KafkaAdmin + ) { val topics = symbols!!.split(",").map { s -> "orders_$s" }.toTypedArray() val containerProps = ContainerProperties(*topics) containerProps.messageListener = orderKafkaListener val container = KafkaMessageListenerContainer(consumerFactory, containerProps) - container.setBeanName("OrderKafkaListenerContainer") + container.beanName = "OrderKafkaListenerContainer" + container.start() + } + + @Autowired + fun configureEventListener( + eventListener: EventKafkaListener, + @Qualifier("eventConsumerFactory") consumerFactory: ConsumerFactory + ) { + val containerProps = ContainerProperties(Pattern.compile("events_.*")) + containerProps.messageListener = eventListener + val container = ConcurrentMessageListenerContainer(consumerFactory, containerProps) + container.beanName = "EventKafkaListenerContainer" container.start() } diff --git a/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/consumer/EventKafkaListener.kt b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/consumer/EventKafkaListener.kt new file mode 100644 index 000000000..e26a14466 --- /dev/null +++ b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/consumer/EventKafkaListener.kt @@ -0,0 +1,29 @@ +package co.nilin.opex.port.order.kafka.consumer + +import co.nilin.opex.matching.core.eventh.events.CoreEvent +import co.nilin.opex.port.order.kafka.spi.EventListener +import org.apache.kafka.clients.consumer.ConsumerRecord +import org.springframework.kafka.listener.MessageListener +import org.springframework.stereotype.Component + +@Component +class EventKafkaListener: MessageListener { + + val eventListeners = arrayListOf() + + override fun onMessage(data: ConsumerRecord) { + eventListeners.forEach{ + tl -> tl.onEvent(data.value(), data.partition(), data.offset(), data.timestamp()) + } + } + + fun addEventListener(tl: EventListener){ + eventListeners.add(tl) + } + + fun removeEventListener(tl: EventListener){ + eventListeners.removeIf { + item -> item.id() == tl.id() + } + } +} \ No newline at end of file diff --git a/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/spi/EventListener.kt b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/spi/EventListener.kt new file mode 100644 index 000000000..b0f4187fe --- /dev/null +++ b/MatchingEngine/matching-ports/matching-eventlistener-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/spi/EventListener.kt @@ -0,0 +1,8 @@ +package co.nilin.opex.port.order.kafka.spi + +import co.nilin.opex.matching.core.eventh.events.CoreEvent + +interface EventListener { + fun id(): String + fun onEvent(event: CoreEvent, partition: Int, offset: Long, timestamp: Long) +} \ No newline at end of file diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/controller/OrderController.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/controller/OrderController.kt index 4cfa7b290..9b243d2df 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/controller/OrderController.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/controller/OrderController.kt @@ -1,5 +1,6 @@ package co.nilin.opex.app.controller +import co.nilin.opex.app.inout.CancelOrderRequest import co.nilin.opex.app.inout.CreateOrderRequest import co.nilin.opex.app.service.OrderService import co.nilin.opex.port.order.kafka.inout.OrderSubmitResult @@ -32,4 +33,20 @@ class OrderController(val orderService: OrderService) { createOrderRequest.uuid = principal.name return orderService.submitNewOrder(createOrderRequest) } + + @PostMapping("/order/cancel") + @ApiResponse( + message = "OK", + code = 200, + examples = Example( + ExampleProperty( + value = "{ }", + mediaType = "application/json" + ) + ) + ) + suspend fun cancelOrder(principal: Principal, @RequestBody request: CancelOrderRequest): OrderSubmitResult { + request.uuid = principal.name + return orderService.cancelOrder(request) + } } \ No newline at end of file diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/inout/CancelOrderRequest.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/inout/CancelOrderRequest.kt new file mode 100644 index 000000000..e7a0fa1da --- /dev/null +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/inout/CancelOrderRequest.kt @@ -0,0 +1,3 @@ +package co.nilin.opex.app.inout + +class CancelOrderRequest(val ouid: String, var uuid: String, val orderId: Long, val symbol: String) \ No newline at end of file diff --git a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/service/OrderService.kt b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/service/OrderService.kt index 6fc179484..7b46616fe 100644 --- a/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/service/OrderService.kt +++ b/MatchingGateway/gateway-app/src/main/kotlin/co/nilin/opex/app/service/OrderService.kt @@ -1,12 +1,15 @@ package co.nilin.opex.app.service +import co.nilin.opex.app.inout.CancelOrderRequest import co.nilin.opex.app.inout.CreateOrderRequest import co.nilin.opex.app.spi.AccountantApiProxy import co.nilin.opex.app.spi.PairConfigLoader +import co.nilin.opex.matching.core.eventh.events.CancelOrderEvent import co.nilin.opex.matching.core.model.OrderDirection import co.nilin.opex.matching.core.model.Pair import co.nilin.opex.port.order.kafka.inout.OrderSubmitRequest import co.nilin.opex.port.order.kafka.inout.OrderSubmitResult +import co.nilin.opex.port.order.kafka.service.EventSubmitter import co.nilin.opex.port.order.kafka.service.OrderSubmitter import co.nilin.opex.utility.error.data.OpexError import co.nilin.opex.utility.error.data.throwError @@ -15,9 +18,10 @@ import java.util.* @Service class OrderService( - val accountantApiProxy: AccountantApiProxy, - val orderSubmitter: OrderSubmitter, - val pairConfigLoader: PairConfigLoader + val accountantApiProxy: AccountantApiProxy, + val orderSubmitter: OrderSubmitter, + val eventSubmitter: EventSubmitter, + val pairConfigLoader: PairConfigLoader ) { suspend fun submitNewOrder(createOrderRequest: CreateOrderRequest): OrderSubmitResult { val symbolSides = createOrderRequest.pair.split("_") @@ -26,38 +30,49 @@ class OrderService( else symbolSides[1] if (!accountantApiProxy.canCreateOrder( - createOrderRequest.uuid!!, - symbol, - if (createOrderRequest.direction == OrderDirection.ASK) - createOrderRequest.quantity - else - createOrderRequest.quantity.multiply(createOrderRequest.price) - ) + createOrderRequest.uuid!!, + symbol, + if (createOrderRequest.direction == OrderDirection.ASK) + createOrderRequest.quantity + else + createOrderRequest.quantity.multiply(createOrderRequest.price) + ) ) { throwError(OpexError.SubmitOrderForbiddenByAccountant) } val pairFeeConfig = pairConfigLoader.load( - createOrderRequest.pair, createOrderRequest.direction, "" + createOrderRequest.pair, createOrderRequest.direction, "" ) val orderSubmitRequest = OrderSubmitRequest( - UUID.randomUUID().toString(), - createOrderRequest.uuid!!//get from auth2 - , - null, - Pair(symbolSides[0], symbolSides[1]), - createOrderRequest.price.divide( - pairFeeConfig.pairConfig.rightSideFraction - .toBigDecimal() - ).longValueExact(), - createOrderRequest.quantity.divide( - pairFeeConfig.pairConfig.leftSideFraction - .toBigDecimal() - ) - .longValueExact(), - createOrderRequest.direction, - createOrderRequest.matchConstraint, - createOrderRequest.orderType + UUID.randomUUID().toString(), + createOrderRequest.uuid!! //get from auth2 + , + null, + Pair(symbolSides[0], symbolSides[1]), + createOrderRequest.price.divide( + pairFeeConfig.pairConfig.rightSideFraction + .toBigDecimal() + ).longValueExact(), + createOrderRequest.quantity.divide( + pairFeeConfig.pairConfig.leftSideFraction + .toBigDecimal() + ) + .longValueExact(), + createOrderRequest.direction, + createOrderRequest.matchConstraint, + createOrderRequest.orderType ) return orderSubmitter.submit(orderSubmitRequest) } + + suspend fun cancelOrder(request: CancelOrderRequest): OrderSubmitResult { + val symbols = request.symbol.split("_") + val event = CancelOrderEvent().apply { + ouid = request.ouid + uuid = request.uuid + orderId = request.orderId + pair = Pair(symbols[0], symbols[1]) + } + return eventSubmitter.submit(event) + } } \ No newline at end of file diff --git a/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/config/OrderKafkaConfig.kt b/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/config/OrderKafkaConfig.kt index e4900a5a5..540eb2c4d 100644 --- a/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/config/OrderKafkaConfig.kt +++ b/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/config/OrderKafkaConfig.kt @@ -1,7 +1,6 @@ package co.nilin.opex.port.order.kafka.config - - +import co.nilin.opex.matching.core.eventh.events.CoreEvent import co.nilin.opex.port.order.kafka.inout.OrderSubmitRequest import org.apache.kafka.clients.producer.ProducerConfig import org.apache.kafka.common.serialization.StringSerializer @@ -15,9 +14,9 @@ import org.springframework.kafka.core.ProducerFactory import org.springframework.kafka.support.serializer.JsonSerializer import java.util.* - @Configuration -class OrderKafkaConfig() { +class OrderKafkaConfig { + @Value("\${spring.kafka.bootstrap-servers}") private lateinit var bootstrapServers: String @@ -40,5 +39,14 @@ class OrderKafkaConfig() { return KafkaTemplate(producerFactory) } + @Bean("gatewayEventProducerFactory") + fun eventProducerFactory(@Qualifier("orderProducerConfigs") producerConfigs: Map): ProducerFactory { + return DefaultKafkaProducerFactory(producerConfigs) + } + + @Bean("gatewayEventKafkaTemplate") + fun eventKafkaTemplate(@Qualifier("gatewayEventProducerFactory") producerFactory: ProducerFactory): KafkaTemplate { + return KafkaTemplate(producerFactory) + } } \ No newline at end of file diff --git a/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/service/EventSubmitter.kt b/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/service/EventSubmitter.kt new file mode 100644 index 000000000..5a47b6311 --- /dev/null +++ b/MatchingGateway/gateway-port/order-submitter-kafka/src/main/kotlin/co/nilin/opex/port/order/kafka/service/EventSubmitter.kt @@ -0,0 +1,28 @@ +package co.nilin.opex.port.order.kafka.service + +import co.nilin.opex.matching.core.eventh.events.CoreEvent +import co.nilin.opex.port.order.kafka.inout.OrderSubmitResult +import org.slf4j.LoggerFactory +import org.springframework.kafka.core.KafkaTemplate +import org.springframework.stereotype.Component +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine + +@Component +class EventSubmitter(val kafkaTemplate: KafkaTemplate) { + + private val logger = LoggerFactory.getLogger(EventSubmitter::class.java) + + suspend fun submit(event: CoreEvent): OrderSubmitResult = suspendCoroutine { + logger.info("Submit event for pair ${event.pair} = ${event::class.java}") + val sendFuture = kafkaTemplate.send("events_${event.pair.leftSideName}_${event.pair.rightSideName}", event) + + sendFuture.addCallback({ sendResult -> + it.resume(OrderSubmitResult(sendResult?.recordMetadata?.offset())) + }, { exception -> + it.resumeWithException(exception) + }) + } + +} \ No newline at end of file From 47978c4d1f46d347425ced5ec277fb394cbf75c6 Mon Sep 17 00:00:00 2001 From: Peyman Date: Sun, 12 Sep 2021 16:01:09 +0430 Subject: [PATCH 52/59] Regarding issue #54: Changed cancel order logic in order to use ouid instead of order_id --- .../binance/controller/AccountController.kt | 27 ++++++++--------- .../port/api/binance/util/EnumExtensions.kt | 7 +++++ .../matching/core/engine/SimpleOrderBook.kt | 12 +++++--- .../core/engine/SimpleOrderBookUnitTest.kt | 29 ++++++++++++------- .../opex/utility/error/data/OpexError.kt | 3 +- 5 files changed, 49 insertions(+), 29 deletions(-) diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt index afe9c8d43..5bafcbdff 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt @@ -6,16 +6,9 @@ import co.nilin.opex.api.core.spi.SymbolMapper import co.nilin.opex.api.core.spi.UserQueryHandler import co.nilin.opex.api.core.spi.WalletProxy import co.nilin.opex.port.api.binance.data.AccountInfoResponse -import co.nilin.opex.port.api.binance.util.BalanceParser -import co.nilin.opex.port.api.binance.util.LoggerDelegate -import co.nilin.opex.port.api.binance.util.asMatchConstraint -import co.nilin.opex.port.api.binance.util.asMatchingOrderType -import co.nilin.opex.port.api.binance.util.asOrderDirection -import co.nilin.opex.port.api.binance.util.jwtAuthentication -import co.nilin.opex.port.api.binance.util.tokenValue +import co.nilin.opex.port.api.binance.util.* import co.nilin.opex.utility.error.data.OpexError import co.nilin.opex.utility.error.data.OpexException -import co.nilin.opex.utility.error.data.throwError import com.fasterxml.jackson.annotation.JsonInclude import io.swagger.annotations.* import java.math.BigDecimal @@ -238,10 +231,7 @@ class AccountController( val order = queryHandler.queryOrder(principal, QueryOrderRequest(localSymbol, orderId, origClientOrderId)) ?: throw OpexException(OpexError.OrderNotFound) - val request = CancelOrderRequest(order.ouid, principal.name, order.orderId, localSymbol) - matchingGatewayProxy.cancelOrder(request, securityContext.jwtAuthentication().tokenValue()) - - return CancelOrderResponse( + val response = CancelOrderResponse( symbol, origClientOrderId, orderId, @@ -251,11 +241,22 @@ class AccountController( order.origQty, order.executedQty, order.cummulativeQuoteQty, - order.status, + OrderStatus.CANCELED, order.timeInForce, order.type, order.side ) + + if (order.status == OrderStatus.CANCELED) + return response + + if (order.status.equalsAny(OrderStatus.REJECTED, OrderStatus.EXPIRED, OrderStatus.FILLED)) + throw OpexException(OpexError.CancelOrderNotAllowed) + + + val request = CancelOrderRequest(order.ouid, principal.name, order.orderId, localSymbol) + matchingGatewayProxy.cancelOrder(request, securityContext.jwtAuthentication().tokenValue()) + return response } /* diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/util/EnumExtensions.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/util/EnumExtensions.kt index 6ae29084e..9f1c1974f 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/util/EnumExtensions.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/util/EnumExtensions.kt @@ -26,4 +26,11 @@ fun co.nilin.opex.api.core.inout.OrderType.asMatchingOrderType(): OrderType { co.nilin.opex.api.core.inout.OrderType.MARKET -> OrderType.MARKET_ORDER else -> OrderType.LIMIT_ORDER } +} + +fun > R.equalsAny(vararg equals: R): Boolean { + for (e in equals) + if (this == e) + return true + return false } \ No newline at end of file diff --git a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBook.kt b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBook.kt index dc38be548..e615a60ec 100644 --- a/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBook.kt +++ b/MatchingEngine/matching-core/src/main/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBook.kt @@ -137,13 +137,17 @@ class SimpleOrderBook(val pair: Pair, var replayMode: Boolean) : OrderBook { logger.info("*********************************************************") println() - val order = orders.remove(orderCommand.orderId) + val simpleOrder = orders.entries.find { it.value.ouid == orderCommand.ouid } + val order = simpleOrder?.value if (order == null /*check for userid*/) { if (!replayMode) { EventDispatcher.emit(RejectOrderEvent(orderCommand.ouid, orderCommand.uuid,orderCommand.orderId, orderCommand.pair, RequestedOperation.CANCEL_ORDER, RejectReason.ORDER_NOT_FOUND)) } return + } else { + orders.remove(simpleOrder.key) } + if (order.direction == OrderDirection.BID) { handleCancelOrder(order, bidOrders, bestBidOrder) { newBestOrder: SimpleOrder? -> bestBidOrder = newBestOrder @@ -448,9 +452,9 @@ class SimpleOrderBook(val pair: Pair, var replayMode: Boolean) : OrderBook { } private fun logCurrentState(){ - logger.info("******************** command handled ********************") - logger.info("** ask orders size: ${askOrders.entriesList().size}") - logger.info("** bid orders size: ${bidOrders.entriesList().size}") + logger.info("******************** ${pair.leftSideName}-${pair.rightSideName} ********************") + logger.info("** askOrders size: ${askOrders.entriesList().size}") + logger.info("** bidOrders size: ${bidOrders.entriesList().size}") logger.info("** orders size: ${orders.size}") logger.info("** bestAskOrder: ${bestAskOrder?.ouid}") logger.info("** bestBidOrder: ${bestBidOrder?.ouid}") diff --git a/MatchingEngine/matching-core/src/test/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBookUnitTest.kt b/MatchingEngine/matching-core/src/test/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBookUnitTest.kt index 6cf55e36d..02130a642 100644 --- a/MatchingEngine/matching-core/src/test/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBookUnitTest.kt +++ b/MatchingEngine/matching-core/src/test/kotlin/co/nilin/opex/matching/core/engine/SimpleOrderBookUnitTest.kt @@ -7,8 +7,6 @@ import co.nilin.opex.matching.core.model.MatchConstraint import co.nilin.opex.matching.core.model.OrderDirection import co.nilin.opex.matching.core.model.OrderType import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.newSingleThreadContext -import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test import java.util.* @@ -133,10 +131,13 @@ class SimpleOrderBookUnitTest { fun givenOrderBook_whenCancelBestBidOrder_thenBestBidOrderChange(){ //given val orderBook = SimpleOrderBook(pair, false) - val firstOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 2, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) - val lastOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 1, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + val firstOrderId = UUID.randomUUID().toString() + val secondOrderId = UUID.randomUUID().toString() + + val firstOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(firstOrderId, uuid, pair, 2, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + val lastOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(secondOrderId, uuid, pair, 1, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) //when - orderBook.handleCancelCommand(OrderCancelCommand(UUID.randomUUID().toString(), uuid, firstOrder!!.id()!!, pair)) + orderBook.handleCancelCommand(OrderCancelCommand(firstOrderId, uuid, firstOrder!!.id()!!, pair)) //then Assertions.assertEquals(orderBook.bestBidOrder, lastOrder) Assertions.assertEquals(orderBook.bidOrders.entriesList().size, 1) @@ -146,11 +147,14 @@ class SimpleOrderBookUnitTest { fun givenOrderBookWithMoreBids_whenCancelBestBidOrder_thenBestBidOrderChange(){ //given val orderBook = SimpleOrderBook(pair, false) - val firstOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 2, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) - val secondOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 2, 3, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + val firstOrderId = UUID.randomUUID().toString() + val secondOrderId = UUID.randomUUID().toString() + + val firstOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(firstOrderId, uuid, pair, 2, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + val secondOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(secondOrderId, uuid, pair, 2, 3, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 1, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) //when - orderBook.handleCancelCommand(OrderCancelCommand(UUID.randomUUID().toString(), uuid, firstOrder!!.id()!!, pair)) + orderBook.handleCancelCommand(OrderCancelCommand(firstOrderId, uuid, firstOrder!!.id()!!, pair)) //then Assertions.assertEquals(orderBook.bestBidOrder, secondOrder) Assertions.assertEquals(orderBook.bidOrders.entriesList().size, 2) @@ -160,11 +164,14 @@ class SimpleOrderBookUnitTest { fun givenOrderBookWithMoreBids_whenCancelABidOrder_thenBestBidOrderNotChange(){ //given val orderBook = SimpleOrderBook(pair, false) - val firstOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 2, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) - val secondOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 2, 3, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + val firstOrderId = UUID.randomUUID().toString() + val secondOrderId = UUID.randomUUID().toString() + + val firstOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(firstOrderId, uuid, pair, 2, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) + val secondOrder = orderBook.handleNewOrderCommand(OrderCreateCommand(secondOrderId, uuid, pair, 2, 3, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) orderBook.handleNewOrderCommand(OrderCreateCommand(UUID.randomUUID().toString(), uuid, pair, 1, 1, OrderDirection.BID, MatchConstraint.GTC, OrderType.LIMIT_ORDER)) //when - orderBook.handleCancelCommand(OrderCancelCommand(UUID.randomUUID().toString(), uuid, secondOrder!!.id()!!, pair)) + orderBook.handleCancelCommand(OrderCancelCommand(secondOrderId, uuid, secondOrder!!.id()!!, pair)) //then Assertions.assertEquals(orderBook.bestBidOrder, firstOrder) Assertions.assertEquals(orderBook.bidOrders.entriesList().size, 2) diff --git a/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/OpexError.kt b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/OpexError.kt index 19b8db298..d03fdfe8c 100644 --- a/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/OpexError.kt +++ b/Utility/error-handler/src/main/kotlin/co/nilin/opex/utility/error/data/OpexError.kt @@ -32,7 +32,8 @@ enum class OpexError(val code: Int, val message: String?, val status: HttpStatus SymbolNotFound(7002, "No symbol found", HttpStatus.NOT_FOUND), InvalidLimitForOrderBook(7003, "Valid limits: [5, 10, 20, 50, 100, 500, 1000, 5000]", HttpStatus.BAD_REQUEST), InvalidLimitForRecentTrades(7004, "Valid limits: 1 min - 1000 max", HttpStatus.BAD_REQUEST), - InvalidPriceChangeDuration(7005, "Valid durations: [24h, 7d, 1m]", HttpStatus.BAD_REQUEST); + InvalidPriceChangeDuration(7005, "Valid durations: [24h, 7d, 1m]", HttpStatus.BAD_REQUEST), + CancelOrderNotAllowed(7006, "Canceling this order is not allowed", HttpStatus.FORBIDDEN); companion object { fun findByCode(code: Int?): OpexError? { From 49b6b855b37c8ebfbb20ee945238585ec4111edf Mon Sep 17 00:00:00 2001 From: Peyman Date: Tue, 14 Sep 2021 13:06:16 +0430 Subject: [PATCH 53/59] Fix #60: Allow cors for all methods --- .../src/main/kotlin/co/nilin/opex/app/config/CorsConfig.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/Api/api-app/src/main/kotlin/co/nilin/opex/app/config/CorsConfig.kt b/Api/api-app/src/main/kotlin/co/nilin/opex/app/config/CorsConfig.kt index 06a3078a4..e6e7a3480 100644 --- a/Api/api-app/src/main/kotlin/co/nilin/opex/app/config/CorsConfig.kt +++ b/Api/api-app/src/main/kotlin/co/nilin/opex/app/config/CorsConfig.kt @@ -15,6 +15,7 @@ class CorsConfig : WebFluxConfigurer { registry.addMapping("/**") .allowedOrigins(*hosts) .allowedHeaders("*") + .allowedMethods("*") } } \ No newline at end of file From 9c5245103e978cffc8dcd1cac44e7d28b7432ded Mon Sep 17 00:00:00 2001 From: Peyman Date: Tue, 14 Sep 2021 14:10:31 +0430 Subject: [PATCH 54/59] Close #59: Converted symbols --- .../binance/controller/AccountController.kt | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt index 5bafcbdff..080e82456 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/AccountController.kt @@ -171,8 +171,8 @@ class AccountController( timestamp: Long, @CurrentSecurityContext securityContext: SecurityContext ): NewOrderResponse { + val internalSymbol = symbolMapper.unmap(symbol) ?: throw OpexException(OpexError.SymbolNotFound) - val internalSymbol = symbolMapper.unmap(symbol)!! val request = MEGatewayProxy.CreateOrderRequest( securityContext.jwtAuthentication().name, internalSymbol, @@ -294,11 +294,13 @@ class AccountController( @RequestParam(name = "timestamp") timestamp: Long ): QueryOrderResponse { - val internalSymbol = symbolMapper.unmap(symbol)!! + val internalSymbol = symbolMapper.unmap(symbol) ?: throw OpexException(OpexError.SymbolNotFound) + val response = queryHandler.queryOrder(principal, QueryOrderRequest(internalSymbol, orderId, origClientOrderId)) ?: throw OpexException(OpexError.OrderNotFound) + return QueryOrderResponse( - symbolMapper.map(response.symbol)!!, + symbol, response.orderId, response.orderListId, response.clientOrderId, @@ -352,11 +354,12 @@ class AccountController( @RequestParam(name = "timestamp") timestamp: Long ): Flow { - val internalSymbol = symbolMapper.unmap(symbol) + val internalSymbol = symbolMapper.unmap(symbol) ?: throw OpexException(OpexError.SymbolNotFound) + return queryHandler.openOrders(principal, internalSymbol) .map { response -> QueryOrderResponse( - symbolMapper.map(response.symbol)!!, + symbol ?: "", response.orderId, response.orderListId, response.clientOrderId, @@ -415,11 +418,12 @@ class AccountController( @RequestParam(name = "timestamp") timestamp: Long ): Flow { - val internalSymbol = symbolMapper.unmap(symbol) + val internalSymbol = symbolMapper.unmap(symbol) ?: throw OpexException(OpexError.SymbolNotFound) + return queryHandler.allOrders(principal, AllOrderRequest(internalSymbol, startTime, endTime, limit)) .map { response -> QueryOrderResponse( - symbolMapper.map(response.symbol)!!, + symbol ?: "", response.orderId, response.orderListId, response.clientOrderId, @@ -482,14 +486,24 @@ class AccountController( @RequestParam(name = "timestamp") timestamp: Long ): Flow { - val internalSymbol = symbolMapper.unmap(symbol) + val internalSymbol = symbolMapper.unmap(symbol) ?: throw OpexException(OpexError.SymbolNotFound) + return queryHandler.allTrades(principal, TradeRequest(internalSymbol, fromId, startTime, endTime, limit)) .map { response -> TradeResponse( - symbolMapper.map(response.symbol)!!, response.id, - response.orderId, -1, response.price, response.qty, response.quoteQty, - response.commission, response.commissionAsset, response.time, response.isBuyer, - response.isMaker, response.isBestMatch + symbol ?: "", + response.id, + response.orderId, + -1, + response.price, + response.qty, + response.quoteQty, + response.commission, + response.commissionAsset, + response.time, + response.isBuyer, + response.isMaker, + response.isBestMatch ) } } From 71bd99e2c2c470a10ed8de04608f388982b8eb4e Mon Sep 17 00:00:00 2001 From: Ebrahim Hosseiny Fadaee Date: Tue, 14 Sep 2021 19:05:02 +0430 Subject: [PATCH 55/59] Close #26, Implement chain sync (#58) * Add SyncRecordHandler, WalletSyncRecordHandler raw implementations * Fix some optional columns in postgres module * Implement SyncRecordHandlerImpl.loadLastSuccessRecord() without records list * Add deposits table and repository * Add records to sync record handler * Add token flag to deposit * Implement SyncRecordHandlerImpl.saveSyncRecord() * Add transactional saving ability to sync records * Organize sync record imports * Implement saveReadyToSyncTransfers * Fix SyncModel table names --- .../opex/bcgateway/core/model/Deposit.kt | 9 ++- .../opex/bcgateway/core/model/WalletSync.kt | 5 +- .../postgres/config/PostgresConfig.kt | 15 +++- .../postgres/dao/ChainSyncRecordRepository.kt | 12 ++-- .../postgres/dao/DepositRepository.kt | 11 +++ .../postgres/impl/SyncRecordHandlerImpl.kt | 68 +++++++++++++++++++ .../impl/WalletSyncRecordHandlerImpl.kt | 34 ++++++++++ .../bcgateway/postgres/model/DepositModel.kt | 17 +++++ .../bcgateway/postgres/model/SyncModel.kt | 6 +- 9 files changed, 159 insertions(+), 18 deletions(-) create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/DepositRepository.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/SyncRecordHandlerImpl.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/WalletSyncRecordHandlerImpl.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/DepositModel.kt diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Deposit.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Deposit.kt index 579803a87..9cdaee869 100644 --- a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Deposit.kt +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Deposit.kt @@ -2,4 +2,11 @@ package co.nilin.opex.bcgateway.core.model import java.math.BigDecimal -data class Deposit(val depositor: String, val depositorMemo: String?, val amount: BigDecimal, val chain: String?, val tokenAddress: String?) +data class Deposit( + val depositor: String, + val depositorMemo: String?, + val amount: BigDecimal, + val chain: String?, + val token: Boolean, + val tokenAddress: String? +) diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/WalletSync.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/WalletSync.kt index 3f4ec00fa..81fc4115d 100644 --- a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/WalletSync.kt +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/WalletSync.kt @@ -4,8 +4,5 @@ import java.time.LocalDateTime data class WalletSyncSchedule(val retryTime: LocalDateTime, val delay: Long, val batchSize: Long?) data class WalletSyncRecord( - val time: LocalDateTime - , val success: Boolean - , val error: String? - , val deposit: Deposit + val time: LocalDateTime, val success: Boolean, val error: String?, val deposit: Deposit ) \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/config/PostgresConfig.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/config/PostgresConfig.kt index 0804207c9..14c3890df 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/config/PostgresConfig.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/config/PostgresConfig.kt @@ -54,14 +54,14 @@ class PostgresConfig(db: DatabaseClient) { ); CREATE TABLE IF NOT EXISTS chain_sync_schedules ( chain VARCHAR(72) PRIMARY KEY, - retry_time TIMESTAMP, - delay NUMERIC + retry_time TIMESTAMP NOT NULL, + delay NUMERIC NOT NULL ); CREATE TABLE IF NOT EXISTS chain_sync_records ( chain VARCHAR(72) PRIMARY KEY, time TIMESTAMP NOT NULL, endpoint_url VARCHAR(72) NOT NULL, - latest_block INTEGER NOT NULL, + latest_block INTEGER, success BOOLEAN NOT NULL, error VARCHAR(100) ); @@ -79,6 +79,15 @@ class PostgresConfig(db: DatabaseClient) { withdraw_fee NUMERIC NOT NULL, withdraw_min NUMERIC NOT NULL ); + CREATE TABLE IF NOT EXISTS deposits ( + id SERIAL PRIMARY KEY, + chain VARCHAR(72), + token BOOLEAN NOT NULL, + token_address VARCHAR(72), + amount NUMERIC NOT NULL, + depositor VARCHAR(72) NOT NULL, + depositorMemo VARCHAR(72) + ); """ } initDb // initialize the database diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncRecordRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncRecordRepository.kt index b3f4ee2b0..b0fa51f46 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncRecordRepository.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncRecordRepository.kt @@ -1,13 +1,11 @@ package co.nilin.opex.port.bcgateway.postgres.dao -import co.nilin.opex.port.bcgateway.postgres.model.AssignedAddressChainModel -import co.nilin.opex.port.bcgateway.postgres.model.ChainModel -import co.nilin.opex.port.bcgateway.postgres.model.SyncRecord -import kotlinx.coroutines.flow.Flow -import org.springframework.data.r2dbc.repository.Query -import org.springframework.data.repository.query.Param +import co.nilin.opex.port.bcgateway.postgres.model.SyncRecordModel import org.springframework.data.repository.reactive.ReactiveCrudRepository import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono @Repository -interface ChainSyncRecordRepository : ReactiveCrudRepository \ No newline at end of file +interface ChainSyncRecordRepository : ReactiveCrudRepository { + fun findByChain(chain: String): Mono +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/DepositRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/DepositRepository.kt new file mode 100644 index 000000000..f5e21069b --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/DepositRepository.kt @@ -0,0 +1,11 @@ +package co.nilin.opex.port.bcgateway.postgres.dao + +import co.nilin.opex.port.bcgateway.postgres.model.DepositModel +import kotlinx.coroutines.flow.Flow +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository + +@Repository +interface DepositRepository : ReactiveCrudRepository { + fun findByChain(chain: String): Flow +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/SyncRecordHandlerImpl.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/SyncRecordHandlerImpl.kt new file mode 100644 index 000000000..319b8e4c0 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/SyncRecordHandlerImpl.kt @@ -0,0 +1,68 @@ +package co.nilin.opex.port.bcgateway.postgres.impl + +import co.nilin.opex.bcgateway.core.model.ChainSyncRecord +import co.nilin.opex.bcgateway.core.model.Deposit +import co.nilin.opex.bcgateway.core.model.Endpoint +import co.nilin.opex.bcgateway.core.spi.SyncRecordHandler +import co.nilin.opex.port.bcgateway.postgres.dao.ChainSyncRecordRepository +import co.nilin.opex.port.bcgateway.postgres.dao.DepositRepository +import co.nilin.opex.port.bcgateway.postgres.model.DepositModel +import co.nilin.opex.port.bcgateway.postgres.model.SyncRecordModel +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.reactive.awaitFirst +import kotlinx.coroutines.reactive.awaitSingleOrNull +import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional + +@Component +class SyncRecordHandlerImpl( + private val chainSyncRecordRepository: ChainSyncRecordRepository, + private val depositRepository: DepositRepository +) : SyncRecordHandler { + override suspend fun loadLastSuccessRecord(chainName: String): ChainSyncRecord? { + val chainSyncRecordDao = chainSyncRecordRepository.findByChain(chainName).awaitSingleOrNull() + return if (chainSyncRecordDao !== null) { + val deposits = depositRepository.findByChain(chainName).map { + Deposit(it.depositor, it.depositorMemo, it.amount, it.chain, it.token, it.tokenAddress) + } + ChainSyncRecord( + chainSyncRecordDao.chain, + chainSyncRecordDao.time, + Endpoint(chainSyncRecordDao.endpointUrl), + chainSyncRecordDao.latestBlock, + chainSyncRecordDao.success, + chainSyncRecordDao.error, + deposits.toList() + ) + } else { + null + } + } + + @Transactional + override suspend fun saveSyncRecord(syncRecord: ChainSyncRecord) { + val chainSyncRecordDao = + SyncRecordModel( + syncRecord.chainName, + syncRecord.time, + syncRecord.endpoint.url, + syncRecord.latestBlock, + syncRecord.success, + syncRecord.error + ) + chainSyncRecordRepository.save(chainSyncRecordDao).awaitFirst() + val depositsDao = syncRecord.records.map { + DepositModel( + null, + it.depositor, + it.depositorMemo, + it.amount, + it.chain, + it.token, + it.tokenAddress + ) + } + depositRepository.saveAll(depositsDao).awaitFirst() + } +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/WalletSyncRecordHandlerImpl.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/WalletSyncRecordHandlerImpl.kt new file mode 100644 index 000000000..3addb0f6f --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/WalletSyncRecordHandlerImpl.kt @@ -0,0 +1,34 @@ +package co.nilin.opex.port.bcgateway.postgres.impl + +import co.nilin.opex.bcgateway.core.model.Deposit +import co.nilin.opex.bcgateway.core.spi.WalletSyncRecordHandler +import co.nilin.opex.port.bcgateway.postgres.dao.DepositRepository +import co.nilin.opex.port.bcgateway.postgres.model.DepositModel +import kotlinx.coroutines.reactive.awaitFirst +import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional + +@Component +class WalletSyncRecordHandlerImpl( + private val depositRepository: DepositRepository +) : WalletSyncRecordHandler { + @Transactional + override suspend fun saveReadyToSyncTransfers(chainName: String, deposits: List) { + val depositsDao = deposits.map { + DepositModel( + null, + it.depositor, + it.depositorMemo, + it.amount, + it.chain, + it.token, + it.tokenAddress + ) + } + depositRepository.saveAll(depositsDao).awaitFirst() + } + + override suspend fun findReadyToSyncTransfers(count: Long?): List { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/DepositModel.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/DepositModel.kt new file mode 100644 index 000000000..d79321a48 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/DepositModel.kt @@ -0,0 +1,17 @@ +package co.nilin.opex.port.bcgateway.postgres.model + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.math.BigDecimal + +@Table("deposits") +data class DepositModel( + @Id val id: Long?, + val depositor: String, + val depositorMemo: String?, + val amount: BigDecimal, + val chain: String?, + val token: Boolean, + val tokenAddress: String? +) \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/SyncModel.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/SyncModel.kt index a131264ce..d7093e80a 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/SyncModel.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/SyncModel.kt @@ -5,13 +5,13 @@ import org.springframework.data.relational.core.mapping.Column import org.springframework.data.relational.core.mapping.Table import java.time.LocalDateTime -@Table("chain_sync_schedule") +@Table("chain_sync_schedules") data class SyncScheduleModel( @Id @Column("chain") val chain: String, @Column("retry_time") val retryTime: LocalDateTime, val delay: Long ) -@Table("chain_sync_record") -data class SyncRecord( +@Table("chain_sync_records") +data class SyncRecordModel( @Id @Column("chain") val chain: String, val time: LocalDateTime, @Column("endpoint_url") val endpointUrl: String, From 0110c8d65aaf7e611a6a2d586d8a3262d454e3f5 Mon Sep 17 00:00:00 2001 From: Ebrahim Hosseiny Fadaee Date: Wed, 15 Sep 2021 10:51:58 +0430 Subject: [PATCH 56/59] Close #28, Implement wallet sync (#63) * Add SyncRecordHandler, WalletSyncRecordHandler raw implementations * Fix some optional columns in postgres module * Implement SyncRecordHandlerImpl.loadLastSuccessRecord() without records list * Add deposits table and repository * Add records to sync record handler * Add token flag to deposit * Implement SyncRecordHandlerImpl.saveSyncRecord() * Add transactional saving ability to sync records * Organize sync record imports * Implement saveReadyToSyncTransfers * Fix SyncModel table names * Add wallet sync schedule table and repository * Improve column types * Update wallet sync record * Update sync record names * Separate chain sync and wallet sync deposits * Make chain a required property in deposit * Fix wallet sync record model * Add wallet sync repositories * Add single row constraint to wallet sync schedules * Implement wallet sync scheduler handler * Implement deposit sync state * Remove wallet sync deposit model * Remove chain sync scheduler handler --- .../opex/bcgateway/app/config/AppConfig.kt | 10 +++--- .../nilin/opex/bcgateway/core/model/Chain.kt | 2 -- .../opex/bcgateway/core/model/Deposit.kt | 3 +- .../opex/bcgateway/core/model/WalletSync.kt | 2 +- .../core/service/ChainSyncServiceImpl.kt | 14 ++++---- .../core/service/WalletSyncServiceImpl.kt | 11 ++++++- ...rdHandler.kt => ChainSyncRecordHandler.kt} | 4 +-- ...andler.kt => ChainSyncSchedulerHandler.kt} | 4 +-- .../core/spi/WalletSyncRecordHandler.kt | 4 ++- .../core/service/ChainSyncServiceImplTest.kt | 31 +++++++++-------- .../postgres/config/PostgresConfig.kt | 33 +++++++++++++------ .../postgres/dao/ChainSyncRecordRepository.kt | 8 ++--- .../dao/ChainSyncScheduleRepository.kt | 13 +++++--- .../postgres/dao/DepositRepository.kt | 15 ++++++++- .../dao/WalletSyncRecordRepository.kt | 8 +++++ .../dao/WalletSyncScheduleRepository.kt | 14 ++++++++ ...rImpl.kt => ChainSyncRecordHandlerImpl.kt} | 29 +++++----------- .../impl/ChainSyncSchedulerHandlerImpl.kt | 29 ++++++++++++++++ .../postgres/impl/SyncSchedulerHandlerImpl.kt | 17 ---------- .../impl/WalletSyncRecordHandlerImpl.kt | 26 +++++++++++++-- .../impl/WalletSyncSchedulerHandlerImpl.kt | 15 ++++++--- .../model/{SyncModel.kt => ChainSyncModel.kt} | 6 ++-- .../bcgateway/postgres/model/DepositModel.kt | 6 ++-- .../postgres/model/WalletSyncModel.kt | 19 +++++++++++ 24 files changed, 214 insertions(+), 109 deletions(-) rename BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/{SyncRecordHandler.kt => ChainSyncRecordHandler.kt} (86%) rename BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/{SyncSchedulerHandler.kt => ChainSyncSchedulerHandler.kt} (88%) create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/WalletSyncRecordRepository.kt create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/WalletSyncScheduleRepository.kt rename BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/{SyncRecordHandlerImpl.kt => ChainSyncRecordHandlerImpl.kt} (69%) create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/ChainSyncSchedulerHandlerImpl.kt delete mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/SyncSchedulerHandlerImpl.kt rename BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/{SyncModel.kt => ChainSyncModel.kt} (90%) create mode 100644 BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/WalletSyncModel.kt diff --git a/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/AppConfig.kt b/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/AppConfig.kt index 58cc98ade..831ed2b07 100644 --- a/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/AppConfig.kt +++ b/BlockchainGateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/AppConfig.kt @@ -28,17 +28,17 @@ class AppConfig { @Bean fun chainSyncService( - syncSchedulerHandler: SyncSchedulerHandler, + chainSyncSchedulerHandler: ChainSyncSchedulerHandler, chainEndpointProxyFinder: ChainEndpointProxyFinder, - syncRecordHandler: SyncRecordHandler, + chainSyncRecordHandler: ChainSyncRecordHandler, walletSyncRecordHandler: WalletSyncRecordHandler, currencyLoader: CurrencyLoader, operator: TransactionalOperator ): ChainSyncService { return ChainSyncServiceImpl( - syncSchedulerHandler, + chainSyncSchedulerHandler, chainEndpointProxyFinder, - syncRecordHandler, + chainSyncRecordHandler, walletSyncRecordHandler, currencyLoader, operator, @@ -61,4 +61,4 @@ class AppConfig { fun infoService(): InfoService { return InfoServiceImpl() } -} \ No newline at end of file +} diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Chain.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Chain.kt index c591687f8..d88430445 100644 --- a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Chain.kt +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Chain.kt @@ -1,6 +1,5 @@ package co.nilin.opex.bcgateway.core.model -import java.math.BigDecimal import java.time.LocalDateTime data class Endpoint(val url: String) @@ -15,4 +14,3 @@ data class ChainSyncRecord( val error: String?, val records: List ) - diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Deposit.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Deposit.kt index 9cdaee869..d355d6df3 100644 --- a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Deposit.kt +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/Deposit.kt @@ -3,10 +3,11 @@ package co.nilin.opex.bcgateway.core.model import java.math.BigDecimal data class Deposit( + val id: Long?, val depositor: String, val depositorMemo: String?, val amount: BigDecimal, - val chain: String?, + val chain: String, val token: Boolean, val tokenAddress: String? ) diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/WalletSync.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/WalletSync.kt index 81fc4115d..439e5e053 100644 --- a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/WalletSync.kt +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/model/WalletSync.kt @@ -5,4 +5,4 @@ import java.time.LocalDateTime data class WalletSyncSchedule(val retryTime: LocalDateTime, val delay: Long, val batchSize: Long?) data class WalletSyncRecord( val time: LocalDateTime, val success: Boolean, val error: String?, val deposit: Deposit -) \ No newline at end of file +) diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/ChainSyncServiceImpl.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/ChainSyncServiceImpl.kt index 9bfb97219..f8f8f4144 100644 --- a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/ChainSyncServiceImpl.kt +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/ChainSyncServiceImpl.kt @@ -10,9 +10,9 @@ import java.time.temporal.ChronoUnit import kotlin.coroutines.coroutineContext open class ChainSyncServiceImpl( - private val syncSchedulerHandler: SyncSchedulerHandler, + private val chainSyncSchedulerHandler: ChainSyncSchedulerHandler, private val chainEndpointProxyFinder: ChainEndpointProxyFinder, - private val syncRecordHandler: SyncRecordHandler, + private val chainSyncRecordHandler: ChainSyncRecordHandler, private val walletSyncRecordHandler: WalletSyncRecordHandler, private val currencyLoader: CurrencyLoader, private val operator: TransactionalOperator, @@ -21,11 +21,11 @@ open class ChainSyncServiceImpl( override suspend fun startSyncWithChain() { withContext(coroutineContext) { - val schedules = syncSchedulerHandler.fetchActiveSchedules(currentTime()) + val schedules = chainSyncSchedulerHandler.fetchActiveSchedules(currentTime()) schedules.map { syncSchedule -> async(dispatcher) { val syncHandler = chainEndpointProxyFinder.findChainEndpointProxy(syncSchedule.chainName) - val lastSync = syncRecordHandler.loadLastSuccessRecord(syncSchedule.chainName) + val lastSync = chainSyncRecordHandler.loadLastSuccessRecord(syncSchedule.chainName) val tokens = currencyLoader.findImplementationsWithTokenOnChain(syncSchedule.chainName) .map { impl -> impl.tokenAddress!! } .toList() @@ -37,9 +37,9 @@ open class ChainSyncServiceImpl( ) operator.executeAndAwait { walletSyncRecordHandler.saveReadyToSyncTransfers(syncResult.chainName, syncResult.records) - syncRecordHandler.saveSyncRecord(syncResult) + chainSyncRecordHandler.saveSyncRecord(syncResult) if (syncResult.success) { - syncSchedulerHandler.prepareScheduleForNextTry( + chainSyncSchedulerHandler.prepareScheduleForNextTry( syncSchedule, currentTime().plus(syncSchedule.delay, ChronoUnit.SECONDS) ) @@ -51,4 +51,4 @@ open class ChainSyncServiceImpl( } protected open fun currentTime() = LocalDateTime.now() -} \ No newline at end of file +} diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/WalletSyncServiceImpl.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/WalletSyncServiceImpl.kt index 77228ea44..977202000 100644 --- a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/WalletSyncServiceImpl.kt +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/service/WalletSyncServiceImpl.kt @@ -1,6 +1,7 @@ package co.nilin.opex.bcgateway.core.service import co.nilin.opex.bcgateway.core.api.WalletSyncService +import co.nilin.opex.bcgateway.core.model.WalletSyncRecord import co.nilin.opex.bcgateway.core.spi.* import kotlinx.coroutines.ExecutorCoroutineDispatcher import kotlinx.coroutines.async @@ -27,8 +28,16 @@ class WalletSyncServiceImpl( async(dispatcher) { val uuid = assignedAddressHandler.findUuid(deposit.depositor, deposit.depositorMemo) if (uuid != null) { - val symbol = currencyLoader.findSymbol(deposit.chain!!, deposit.tokenAddress) + val symbol = currencyLoader.findSymbol(deposit.chain, deposit.tokenAddress) if (symbol != null) walletProxy.transfer(uuid, symbol, deposit.amount) + walletSyncRecordHandler.saveWalletSyncRecord( + WalletSyncRecord( + LocalDateTime.now(), + true, + null, + deposit + ) + ) } } } diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/SyncRecordHandler.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/ChainSyncRecordHandler.kt similarity index 86% rename from BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/SyncRecordHandler.kt rename to BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/ChainSyncRecordHandler.kt index d7803c28a..e8cc7106a 100644 --- a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/SyncRecordHandler.kt +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/ChainSyncRecordHandler.kt @@ -2,7 +2,7 @@ package co.nilin.opex.bcgateway.core.spi import co.nilin.opex.bcgateway.core.model.ChainSyncRecord -interface SyncRecordHandler { +interface ChainSyncRecordHandler { suspend fun loadLastSuccessRecord(chainName: String): ChainSyncRecord? suspend fun saveSyncRecord(syncRecord: ChainSyncRecord) -} \ No newline at end of file +} diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/SyncSchedulerHandler.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/ChainSyncSchedulerHandler.kt similarity index 88% rename from BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/SyncSchedulerHandler.kt rename to BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/ChainSyncSchedulerHandler.kt index be763513e..a04e498af 100644 --- a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/SyncSchedulerHandler.kt +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/ChainSyncSchedulerHandler.kt @@ -3,7 +3,7 @@ package co.nilin.opex.bcgateway.core.spi import co.nilin.opex.bcgateway.core.model.ChainSyncSchedule import java.time.LocalDateTime -interface SyncSchedulerHandler { +interface ChainSyncSchedulerHandler { suspend fun fetchActiveSchedules(time: LocalDateTime): List suspend fun prepareScheduleForNextTry(syncSchedule: ChainSyncSchedule, time: LocalDateTime) -} \ No newline at end of file +} diff --git a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/WalletSyncRecordHandler.kt b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/WalletSyncRecordHandler.kt index 18437efe9..90979ab28 100644 --- a/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/WalletSyncRecordHandler.kt +++ b/BlockchainGateway/bc-gateway-core/src/main/kotlin/co/nilin/opex/bcgateway/core/spi/WalletSyncRecordHandler.kt @@ -1,8 +1,10 @@ package co.nilin.opex.bcgateway.core.spi import co.nilin.opex.bcgateway.core.model.Deposit +import co.nilin.opex.bcgateway.core.model.WalletSyncRecord interface WalletSyncRecordHandler { suspend fun saveReadyToSyncTransfers(chainName: String, deposits: List) + suspend fun saveWalletSyncRecord(syncRecord: WalletSyncRecord) suspend fun findReadyToSyncTransfers(count: Long?): List -} \ No newline at end of file +} diff --git a/BlockchainGateway/bc-gateway-core/src/test/kotlin/co/nilin/opex/bcgateway/core/service/ChainSyncServiceImplTest.kt b/BlockchainGateway/bc-gateway-core/src/test/kotlin/co/nilin/opex/bcgateway/core/service/ChainSyncServiceImplTest.kt index 75115b8f3..f9997357e 100644 --- a/BlockchainGateway/bc-gateway-core/src/test/kotlin/co/nilin/opex/bcgateway/core/service/ChainSyncServiceImplTest.kt +++ b/BlockchainGateway/bc-gateway-core/src/test/kotlin/co/nilin/opex/bcgateway/core/service/ChainSyncServiceImplTest.kt @@ -6,8 +6,8 @@ import co.nilin.opex.bcgateway.core.model.Endpoint import co.nilin.opex.bcgateway.core.spi.ChainEndpointProxy import co.nilin.opex.bcgateway.core.spi.ChainEndpointProxyFinder import co.nilin.opex.bcgateway.core.spi.CurrencyLoader -import co.nilin.opex.bcgateway.core.spi.SyncRecordHandler -import co.nilin.opex.bcgateway.core.spi.SyncSchedulerHandler +import co.nilin.opex.bcgateway.core.spi.ChainSyncRecordHandler +import co.nilin.opex.bcgateway.core.spi.ChainSyncSchedulerHandler import co.nilin.opex.bcgateway.core.spi.WalletSyncRecordHandler import co.nilin.opex.bcgateway.test.OPERATOR import java.time.LocalDateTime @@ -23,7 +23,6 @@ import org.mockito.kotlin.any import org.mockito.kotlin.mock import org.mockito.kotlin.times import org.mockito.kotlin.verify -import org.mockito.kotlin.verifyNoMoreInteractions import org.mockito.kotlin.verifyZeroInteractions internal class ChainSyncServiceImplTest { @@ -34,13 +33,13 @@ internal class ChainSyncServiceImplTest { val syncService: ChainSyncServiceImpl @Mock - lateinit var syncSchedulerHandler: SyncSchedulerHandler + lateinit var chainSyncSchedulerHandler: ChainSyncSchedulerHandler @Mock lateinit var chainEndpointProxyFinder: ChainEndpointProxyFinder @Mock - lateinit var syncRecordHandler: SyncRecordHandler + lateinit var chainSyncRecordHandler: ChainSyncRecordHandler @Mock lateinit var walletSyncRecordHandler: WalletSyncRecordHandler @@ -59,9 +58,9 @@ internal class ChainSyncServiceImplTest { } syncService = object : ChainSyncServiceImpl( - syncSchedulerHandler, + chainSyncSchedulerHandler, chainEndpointProxyFinder, - syncRecordHandler, + chainSyncRecordHandler, walletSyncRecordHandler, currencyLoader, OPERATOR, @@ -75,7 +74,7 @@ internal class ChainSyncServiceImplTest { fun givenNoActiveSchedules_whenStartSync_thenNoOp() { runBlocking { //given - Mockito.`when`(syncSchedulerHandler.fetchActiveSchedules(any())).thenReturn(emptyList()) + Mockito.`when`(chainSyncSchedulerHandler.fetchActiveSchedules(any())).thenReturn(emptyList()) //when syncService.startSyncWithChain() @@ -83,7 +82,7 @@ internal class ChainSyncServiceImplTest { //then verifyZeroInteractions( chainEndpointProxyFinder, - syncRecordHandler, + chainSyncRecordHandler, walletSyncRecordHandler, currencyLoader ) @@ -96,7 +95,7 @@ internal class ChainSyncServiceImplTest { //given val delay = 100L val syncSchedule = ChainSyncSchedule(ethChain, time, delay) - Mockito.`when`(syncSchedulerHandler.fetchActiveSchedules(any())) + Mockito.`when`(chainSyncSchedulerHandler.fetchActiveSchedules(any())) .thenReturn(listOf(syncSchedule)) Mockito.`when`(endpointProxy.syncTransfers(any())).thenReturn( ChainSyncRecord( @@ -108,9 +107,9 @@ internal class ChainSyncServiceImplTest { syncService.startSyncWithChain() //then - verify(syncRecordHandler).saveSyncRecord(any()) + verify(chainSyncRecordHandler).saveSyncRecord(any()) verify(walletSyncRecordHandler).saveReadyToSyncTransfers(any(), any()) - verify(syncSchedulerHandler).prepareScheduleForNextTry(syncSchedule, time.plus(delay, ChronoUnit.SECONDS)) + verify(chainSyncSchedulerHandler).prepareScheduleForNextTry(syncSchedule, time.plus(delay, ChronoUnit.SECONDS)) } } @@ -120,7 +119,7 @@ internal class ChainSyncServiceImplTest { //given val delay = 100L val syncSchedule = ChainSyncSchedule(ethChain, time, delay) - Mockito.`when`(syncSchedulerHandler.fetchActiveSchedules(any())) + Mockito.`when`(chainSyncSchedulerHandler.fetchActiveSchedules(any())) .thenReturn(listOf(syncSchedule)) Mockito.`when`(endpointProxy.syncTransfers(any())).thenReturn( ChainSyncRecord( @@ -132,11 +131,11 @@ internal class ChainSyncServiceImplTest { syncService.startSyncWithChain() //then - verify(syncRecordHandler).saveSyncRecord(any()) + verify(chainSyncRecordHandler).saveSyncRecord(any()) verify(walletSyncRecordHandler).saveReadyToSyncTransfers(any(), any()) - verify(syncSchedulerHandler, times(0)).prepareScheduleForNextTry(any(), any()) + verify(chainSyncSchedulerHandler, times(0)).prepareScheduleForNextTry(any(), any()) } } -} \ No newline at end of file +} diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/config/PostgresConfig.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/config/PostgresConfig.kt index 14c3890df..df4ca430b 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/config/PostgresConfig.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/config/PostgresConfig.kt @@ -55,7 +55,7 @@ class PostgresConfig(db: DatabaseClient) { CREATE TABLE IF NOT EXISTS chain_sync_schedules ( chain VARCHAR(72) PRIMARY KEY, retry_time TIMESTAMP NOT NULL, - delay NUMERIC NOT NULL + delay INTEGER NOT NULL ); CREATE TABLE IF NOT EXISTS chain_sync_records ( chain VARCHAR(72) PRIMARY KEY, @@ -65,6 +65,28 @@ class PostgresConfig(db: DatabaseClient) { success BOOLEAN NOT NULL, error VARCHAR(100) ); + CREATE TABLE IF NOT EXISTS wallet_sync_schedules ( + id INTEGER PRIMARY KEY DEFAULT(1) CHECK(id = 1), + retry_time TIMESTAMP NOT NULL, + delay INTEGER NOT NULL, + batch_size INTEGER + ); + CREATE TABLE IF NOT EXISTS wallet_sync_records ( + id SERIAL PRIMARY KEY, + time TIMESTAMP NOT NULL, + success BOOLEAN NOT NULL, + error VARCHAR(100) + ); + CREATE TABLE IF NOT EXISTS deposits ( + id SERIAL PRIMARY KEY, + wallet_sync_record INTEGER NOT NULL, + chain VARCHAR(72) NOT NULL, + token BOOLEAN NOT NULL, + token_address VARCHAR(72), + amount NUMERIC NOT NULL, + depositor VARCHAR(72) NOT NULL, + depositor_memo VARCHAR(72) + ); CREATE TABLE IF NOT EXISTS currency ( symbol VARCHAR(72) PRIMARY KEY, name VARCHAR(72) NOT NULL @@ -79,15 +101,6 @@ class PostgresConfig(db: DatabaseClient) { withdraw_fee NUMERIC NOT NULL, withdraw_min NUMERIC NOT NULL ); - CREATE TABLE IF NOT EXISTS deposits ( - id SERIAL PRIMARY KEY, - chain VARCHAR(72), - token BOOLEAN NOT NULL, - token_address VARCHAR(72), - amount NUMERIC NOT NULL, - depositor VARCHAR(72) NOT NULL, - depositorMemo VARCHAR(72) - ); """ } initDb // initialize the database diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncRecordRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncRecordRepository.kt index b0fa51f46..20044463e 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncRecordRepository.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncRecordRepository.kt @@ -1,11 +1,11 @@ package co.nilin.opex.port.bcgateway.postgres.dao -import co.nilin.opex.port.bcgateway.postgres.model.SyncRecordModel +import co.nilin.opex.port.bcgateway.postgres.model.ChainSyncRecordModel import org.springframework.data.repository.reactive.ReactiveCrudRepository import org.springframework.stereotype.Repository import reactor.core.publisher.Mono @Repository -interface ChainSyncRecordRepository : ReactiveCrudRepository { - fun findByChain(chain: String): Mono -} \ No newline at end of file +interface ChainSyncRecordRepository : ReactiveCrudRepository { + fun findByChain(chain: String): Mono +} diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncScheduleRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncScheduleRepository.kt index 48a7a0252..c4395e8d5 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncScheduleRepository.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/ChainSyncScheduleRepository.kt @@ -1,13 +1,16 @@ package co.nilin.opex.port.bcgateway.postgres.dao -import co.nilin.opex.port.bcgateway.postgres.model.AssignedAddressChainModel -import co.nilin.opex.port.bcgateway.postgres.model.ChainModel -import co.nilin.opex.port.bcgateway.postgres.model.SyncScheduleModel +import co.nilin.opex.port.bcgateway.postgres.model.ChainSyncScheduleModel +import co.nilin.opex.port.bcgateway.postgres.model.WalletSyncScheduleModel import kotlinx.coroutines.flow.Flow import org.springframework.data.r2dbc.repository.Query -import org.springframework.data.repository.query.Param import org.springframework.data.repository.reactive.ReactiveCrudRepository import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono +import java.time.LocalDateTime @Repository -interface ChainSyncScheduleRepository : ReactiveCrudRepository \ No newline at end of file +interface ChainSyncScheduleRepository : ReactiveCrudRepository { + @Query("select * from chain_sync_schedules where retry_time <= :time") + fun findActiveSchedule(time: LocalDateTime): Flow +} diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/DepositRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/DepositRepository.kt index f5e21069b..848d4d364 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/DepositRepository.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/DepositRepository.kt @@ -2,10 +2,23 @@ package co.nilin.opex.port.bcgateway.postgres.dao import co.nilin.opex.port.bcgateway.postgres.model.DepositModel import kotlinx.coroutines.flow.Flow +import org.springframework.data.r2dbc.repository.Modifying +import org.springframework.data.r2dbc.repository.Query import org.springframework.data.repository.reactive.ReactiveCrudRepository import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono @Repository interface DepositRepository : ReactiveCrudRepository { fun findByChain(chain: String): Flow -} \ No newline at end of file + + @Query("select * from deposits where chain = :chain and wallet_sync_record is null") + fun findByChainWhereNotSynced(chain: String): Flow + + @Query("select * from deposits where wallet_record_id is null limit :count") + fun findLimited(count: Long?): Flow + + @Modifying + @Query("update deposits set wallet_sync_record = :walletSyncRecord where id = :id") + fun updateWalletSyncRecord(id: Long, walletSyncRecord: Long): Mono +} diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/WalletSyncRecordRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/WalletSyncRecordRepository.kt new file mode 100644 index 000000000..cab986087 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/WalletSyncRecordRepository.kt @@ -0,0 +1,8 @@ +package co.nilin.opex.port.bcgateway.postgres.dao + +import co.nilin.opex.port.bcgateway.postgres.model.WalletSyncRecordModel +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository + +@Repository +interface WalletSyncRecordRepository : ReactiveCrudRepository diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/WalletSyncScheduleRepository.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/WalletSyncScheduleRepository.kt new file mode 100644 index 000000000..176ff6a23 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/dao/WalletSyncScheduleRepository.kt @@ -0,0 +1,14 @@ +package co.nilin.opex.port.bcgateway.postgres.dao + +import co.nilin.opex.port.bcgateway.postgres.model.WalletSyncScheduleModel +import org.springframework.data.r2dbc.repository.Query +import org.springframework.data.repository.reactive.ReactiveCrudRepository +import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono +import java.time.LocalDateTime + +@Repository +interface WalletSyncScheduleRepository : ReactiveCrudRepository { + @Query("select * from wallet_sync_schedules where retry_time <= :time") + fun findActiveSchedule(time: LocalDateTime): Mono +} diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/SyncRecordHandlerImpl.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/ChainSyncRecordHandlerImpl.kt similarity index 69% rename from BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/SyncRecordHandlerImpl.kt rename to BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/ChainSyncRecordHandlerImpl.kt index 319b8e4c0..2a2cf7799 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/SyncRecordHandlerImpl.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/ChainSyncRecordHandlerImpl.kt @@ -3,11 +3,10 @@ package co.nilin.opex.port.bcgateway.postgres.impl import co.nilin.opex.bcgateway.core.model.ChainSyncRecord import co.nilin.opex.bcgateway.core.model.Deposit import co.nilin.opex.bcgateway.core.model.Endpoint -import co.nilin.opex.bcgateway.core.spi.SyncRecordHandler +import co.nilin.opex.bcgateway.core.spi.ChainSyncRecordHandler import co.nilin.opex.port.bcgateway.postgres.dao.ChainSyncRecordRepository import co.nilin.opex.port.bcgateway.postgres.dao.DepositRepository -import co.nilin.opex.port.bcgateway.postgres.model.DepositModel -import co.nilin.opex.port.bcgateway.postgres.model.SyncRecordModel +import co.nilin.opex.port.bcgateway.postgres.model.ChainSyncRecordModel import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.toList import kotlinx.coroutines.reactive.awaitFirst @@ -16,15 +15,15 @@ import org.springframework.stereotype.Component import org.springframework.transaction.annotation.Transactional @Component -class SyncRecordHandlerImpl( +class ChainSyncRecordHandlerImpl( private val chainSyncRecordRepository: ChainSyncRecordRepository, private val depositRepository: DepositRepository -) : SyncRecordHandler { +) : ChainSyncRecordHandler { override suspend fun loadLastSuccessRecord(chainName: String): ChainSyncRecord? { val chainSyncRecordDao = chainSyncRecordRepository.findByChain(chainName).awaitSingleOrNull() return if (chainSyncRecordDao !== null) { - val deposits = depositRepository.findByChain(chainName).map { - Deposit(it.depositor, it.depositorMemo, it.amount, it.chain, it.token, it.tokenAddress) + val deposits = depositRepository.findByChainWhereNotSynced(chainName).map { + Deposit(it.id, it.depositor, it.depositorMemo, it.amount, it.chain, it.token, it.tokenAddress) } ChainSyncRecord( chainSyncRecordDao.chain, @@ -43,7 +42,7 @@ class SyncRecordHandlerImpl( @Transactional override suspend fun saveSyncRecord(syncRecord: ChainSyncRecord) { val chainSyncRecordDao = - SyncRecordModel( + ChainSyncRecordModel( syncRecord.chainName, syncRecord.time, syncRecord.endpoint.url, @@ -52,17 +51,5 @@ class SyncRecordHandlerImpl( syncRecord.error ) chainSyncRecordRepository.save(chainSyncRecordDao).awaitFirst() - val depositsDao = syncRecord.records.map { - DepositModel( - null, - it.depositor, - it.depositorMemo, - it.amount, - it.chain, - it.token, - it.tokenAddress - ) - } - depositRepository.saveAll(depositsDao).awaitFirst() } -} \ No newline at end of file +} diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/ChainSyncSchedulerHandlerImpl.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/ChainSyncSchedulerHandlerImpl.kt new file mode 100644 index 000000000..f33961d16 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/ChainSyncSchedulerHandlerImpl.kt @@ -0,0 +1,29 @@ +package co.nilin.opex.port.bcgateway.postgres.impl + +import co.nilin.opex.bcgateway.core.model.ChainSyncSchedule +import co.nilin.opex.bcgateway.core.model.WalletSyncSchedule +import co.nilin.opex.bcgateway.core.spi.ChainSyncSchedulerHandler +import co.nilin.opex.port.bcgateway.postgres.dao.ChainSyncScheduleRepository +import co.nilin.opex.port.bcgateway.postgres.model.ChainSyncScheduleModel +import co.nilin.opex.port.bcgateway.postgres.model.WalletSyncScheduleModel +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.reactive.awaitFirst +import kotlinx.coroutines.reactive.awaitSingleOrNull +import org.springframework.stereotype.Component +import java.time.LocalDateTime + +@Component +class ChainSyncSchedulerHandlerImpl(private val chainSyncScheduleRepository: ChainSyncScheduleRepository) : + ChainSyncSchedulerHandler { + override suspend fun fetchActiveSchedules(time: LocalDateTime): List { + return chainSyncScheduleRepository.findActiveSchedule(time).map { + ChainSyncSchedule(it.chain, it.retryTime, it.delay) + }.toList() + } + + override suspend fun prepareScheduleForNextTry(syncSchedule: ChainSyncSchedule, time: LocalDateTime) { + val dao = ChainSyncScheduleModel(syncSchedule.chainName, time, syncSchedule.delay) + chainSyncScheduleRepository.save(dao).awaitFirst() + } +} diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/SyncSchedulerHandlerImpl.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/SyncSchedulerHandlerImpl.kt deleted file mode 100644 index 89c6e399b..000000000 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/SyncSchedulerHandlerImpl.kt +++ /dev/null @@ -1,17 +0,0 @@ -package co.nilin.opex.port.bcgateway.postgres.impl - -import co.nilin.opex.bcgateway.core.model.ChainSyncSchedule -import co.nilin.opex.bcgateway.core.spi.SyncSchedulerHandler -import org.springframework.stereotype.Component -import java.time.LocalDateTime - -@Component -class SyncSchedulerHandlerImpl: SyncSchedulerHandler { - override suspend fun fetchActiveSchedules(time: LocalDateTime): List { - TODO("Not yet implemented") - } - - override suspend fun prepareScheduleForNextTry(syncSchedule: ChainSyncSchedule, time: LocalDateTime) { - TODO("Not yet implemented") - } -} \ No newline at end of file diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/WalletSyncRecordHandlerImpl.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/WalletSyncRecordHandlerImpl.kt index 3addb0f6f..c5bd769b8 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/WalletSyncRecordHandlerImpl.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/WalletSyncRecordHandlerImpl.kt @@ -1,21 +1,28 @@ package co.nilin.opex.port.bcgateway.postgres.impl import co.nilin.opex.bcgateway.core.model.Deposit +import co.nilin.opex.bcgateway.core.model.WalletSyncRecord import co.nilin.opex.bcgateway.core.spi.WalletSyncRecordHandler import co.nilin.opex.port.bcgateway.postgres.dao.DepositRepository +import co.nilin.opex.port.bcgateway.postgres.dao.WalletSyncRecordRepository import co.nilin.opex.port.bcgateway.postgres.model.DepositModel +import co.nilin.opex.port.bcgateway.postgres.model.WalletSyncRecordModel +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.toList import kotlinx.coroutines.reactive.awaitFirst import org.springframework.stereotype.Component import org.springframework.transaction.annotation.Transactional @Component class WalletSyncRecordHandlerImpl( + private val walletSyncRecordRepository: WalletSyncRecordRepository, private val depositRepository: DepositRepository ) : WalletSyncRecordHandler { @Transactional override suspend fun saveReadyToSyncTransfers(chainName: String, deposits: List) { val depositsDao = deposits.map { DepositModel( + null, null, it.depositor, it.depositorMemo, @@ -28,7 +35,22 @@ class WalletSyncRecordHandlerImpl( depositRepository.saveAll(depositsDao).awaitFirst() } + @Transactional + override suspend fun saveWalletSyncRecord(syncRecord: WalletSyncRecord) { + val dao = walletSyncRecordRepository.save( + WalletSyncRecordModel( + null, + syncRecord.time, + syncRecord.success, + syncRecord.error + ) + ).awaitFirst() + depositRepository.updateWalletSyncRecord(syncRecord.deposit.id!!, dao.id!!).awaitFirst() + } + override suspend fun findReadyToSyncTransfers(count: Long?): List { - TODO("Not yet implemented") + return depositRepository.findLimited(count).map { + Deposit(it.id, it.depositor, it.depositorMemo, it.amount, it.chain, it.token, it.tokenAddress) + }.toList() } -} \ No newline at end of file +} diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/WalletSyncSchedulerHandlerImpl.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/WalletSyncSchedulerHandlerImpl.kt index d4ca29623..ec3fa6206 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/WalletSyncSchedulerHandlerImpl.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/impl/WalletSyncSchedulerHandlerImpl.kt @@ -2,16 +2,23 @@ package co.nilin.opex.port.bcgateway.postgres.impl import co.nilin.opex.bcgateway.core.model.WalletSyncSchedule import co.nilin.opex.bcgateway.core.spi.WalletSyncSchedulerHandler +import co.nilin.opex.port.bcgateway.postgres.dao.WalletSyncScheduleRepository +import co.nilin.opex.port.bcgateway.postgres.model.WalletSyncScheduleModel +import kotlinx.coroutines.reactive.awaitFirst +import kotlinx.coroutines.reactive.awaitSingleOrNull import org.springframework.stereotype.Component import java.time.LocalDateTime @Component -class WalletSyncSchedulerHandlerImpl: WalletSyncSchedulerHandler { +class WalletSyncSchedulerHandlerImpl(private val walletSyncScheduleRepository: WalletSyncScheduleRepository) : + WalletSyncSchedulerHandler { override suspend fun fetchActiveSchedule(time: LocalDateTime): WalletSyncSchedule? { - TODO("Not yet implemented") + val dao = walletSyncScheduleRepository.findActiveSchedule(time).awaitSingleOrNull() + return if (dao !== null) WalletSyncSchedule(dao.retryTime, dao.delay, dao.batchSize) else null } override suspend fun prepareScheduleForNextTry(syncSchedule: WalletSyncSchedule, time: LocalDateTime) { - TODO("Not yet implemented") + val dao = WalletSyncScheduleModel(1, time, syncSchedule.delay, syncSchedule.batchSize) + walletSyncScheduleRepository.save(dao).awaitFirst() } -} \ No newline at end of file +} diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/SyncModel.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/ChainSyncModel.kt similarity index 90% rename from BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/SyncModel.kt rename to BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/ChainSyncModel.kt index d7093e80a..c77b3da05 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/SyncModel.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/ChainSyncModel.kt @@ -6,12 +6,12 @@ import org.springframework.data.relational.core.mapping.Table import java.time.LocalDateTime @Table("chain_sync_schedules") -data class SyncScheduleModel( +data class ChainSyncScheduleModel( @Id @Column("chain") val chain: String, @Column("retry_time") val retryTime: LocalDateTime, val delay: Long ) @Table("chain_sync_records") -data class SyncRecordModel( +data class ChainSyncRecordModel( @Id @Column("chain") val chain: String, val time: LocalDateTime, @Column("endpoint_url") val endpointUrl: String, @@ -19,5 +19,3 @@ data class SyncRecordModel( val success: Boolean, val error: String? ) - - diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/DepositModel.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/DepositModel.kt index d79321a48..617eec478 100644 --- a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/DepositModel.kt +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/DepositModel.kt @@ -1,17 +1,17 @@ package co.nilin.opex.port.bcgateway.postgres.model import org.springframework.data.annotation.Id -import org.springframework.data.relational.core.mapping.Column import org.springframework.data.relational.core.mapping.Table import java.math.BigDecimal @Table("deposits") data class DepositModel( @Id val id: Long?, + val walletSyncRecord: Long?, val depositor: String, val depositorMemo: String?, val amount: BigDecimal, - val chain: String?, + val chain: String, val token: Boolean, val tokenAddress: String? -) \ No newline at end of file +) diff --git a/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/WalletSyncModel.kt b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/WalletSyncModel.kt new file mode 100644 index 000000000..3947af2b7 --- /dev/null +++ b/BlockchainGateway/bc-gateway-ports/bc-persister-postgres/src/main/kotlin/co/nilin/opex/port/bcgateway/postgres/model/WalletSyncModel.kt @@ -0,0 +1,19 @@ +package co.nilin.opex.port.bcgateway.postgres.model + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Column +import org.springframework.data.relational.core.mapping.Table +import java.time.LocalDateTime + +@Table("wallet_sync_schedules") +data class WalletSyncScheduleModel( + @Id val id: Long?, val retryTime: LocalDateTime, val delay: Long, val batchSize: Long? +) + +@Table("wallet_sync_records") +data class WalletSyncRecordModel( + @Id val id: Long?, + val time: LocalDateTime, + val success: Boolean, + val error: String? +) From 593e399583b6911e1ec736e6d21e96bf94404f0a Mon Sep 17 00:00:00 2001 From: Peyman Date: Wed, 15 Sep 2021 17:42:07 +0430 Subject: [PATCH 57/59] Close #61: Price ticker implemented --- .../api/core/inout/PriceTickerResponse.kt | 6 ++++ .../opex/api/core/spi/MarketQueryHandler.kt | 2 ++ .../binance/controller/MarketController.kt | 15 ++++++++- .../port/api/postgres/dao/TradeRepository.kt | 18 ++++++++--- .../postgres/impl/MarketQueryHandlerImpl.kt | 32 ++++++++++++++++++- 5 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/PriceTickerResponse.kt diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/PriceTickerResponse.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/PriceTickerResponse.kt new file mode 100644 index 000000000..d370637ef --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/PriceTickerResponse.kt @@ -0,0 +1,6 @@ +package co.nilin.opex.api.core.inout + +data class PriceTickerResponse( + val symbol: String?, + val price: String? +) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt index daf2826a3..c14938dc4 100644 --- a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/MarketQueryHandler.kt @@ -18,4 +18,6 @@ interface MarketQueryHandler { suspend fun recentTrades(symbol: String, limit: Int): Flow + suspend fun lastPrice(symbol: String?): List + } \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt index fddb8787a..7bb39a47c 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt @@ -4,6 +4,7 @@ import co.nilin.opex.api.core.spi.MarketQueryHandler import co.nilin.opex.api.core.spi.SymbolMapper import co.nilin.opex.port.api.binance.data.OrderBookResponse import co.nilin.opex.api.core.inout.PriceChangeResponse +import co.nilin.opex.api.core.inout.PriceTickerResponse import co.nilin.opex.port.api.binance.data.RecentTradeResponse import co.nilin.opex.utility.error.data.OpexError import co.nilin.opex.utility.error.data.OpexException @@ -102,7 +103,7 @@ class MarketController( } } - @GetMapping("/v3/ticker/{duration}") + @GetMapping("/v3/ticker/{duration:24h|7d|1m}") suspend fun priceChange( @PathVariable("duration") duration: String, @@ -134,4 +135,16 @@ class MarketController( listOf(marketQueryHandler.getTradeTickerDataBySymbol(localSymbol!!, startDate)) } + // Weight + // 1 for a single symbol + // 2 when the symbol parameter is omitted + @GetMapping("/v3/ticker/price") + suspend fun priceTicker(@RequestParam("symbol", required = false) symbol: String?): List { + val localSymbol = if (symbol == null) + null + else + symbolMapper.unmap(symbol) ?: throw OpexException(OpexError.SymbolNotFound) + return marketQueryHandler.lastPrice(localSymbol) + } + } \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/TradeRepository.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/TradeRepository.kt index db3965662..414f18091 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/TradeRepository.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/dao/TradeRepository.kt @@ -19,11 +19,13 @@ interface TradeRepository : ReactiveCrudRepository { fun findByOuid(@Param("ouid") ouid: String): Flow @Query( - "select * from trades where :uuid in (taker_uuid, maker_uuid) " + - "and (:fromTrade is null or id > :fromTrade) " + - "and (:symbol is null or symbol = :symbol) " + - "and (:startTime is null or trade_date >= :startTime) " + - "and (:endTime is null or trade_date < :endTime)" + """ + select * from trades where :uuid in (taker_uuid, maker_uuid) + and (:fromTrade is null or id > :fromTrade) + and (:symbol is null or symbol = :symbol) + and (:startTime is null or trade_date >= :startTime) + and (:endTime is null or trade_date < :endTime) + """ ) fun findByUuidAndSymbolAndTimeBetweenAndTradeIdGreaterThan( @Param("uuid") @@ -98,4 +100,10 @@ interface TradeRepository : ReactiveCrudRepository { @Param("date") createDate: LocalDateTime, ): Mono + + @Query("select * from trades where create_date in (select max(create_date) from trades group by symbol) and symbol = :symbol") + fun findBySymbolGroupBySymbol(@Param("symbol") symbol: String): Flux + + @Query("select * from trades where create_date in (select max(create_date) from trades group by symbol)") + fun findAllGroupBySymbol(): Flux } \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt index 7b940bc89..6d9f508b2 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/MarketQueryHandlerImpl.kt @@ -2,6 +2,7 @@ package co.nilin.opex.port.api.postgres.impl import co.nilin.opex.api.core.inout.* import co.nilin.opex.api.core.spi.MarketQueryHandler +import co.nilin.opex.api.core.spi.SymbolMapper import co.nilin.opex.matching.core.model.OrderDirection import co.nilin.opex.port.api.postgres.dao.OrderRepository import co.nilin.opex.port.api.postgres.dao.TradeRepository @@ -14,16 +15,19 @@ import kotlinx.coroutines.reactive.awaitFirst import kotlinx.coroutines.reactive.awaitFirstOrElse import kotlinx.coroutines.reactive.awaitFirstOrNull import org.springframework.stereotype.Component +import java.lang.Exception import java.time.LocalDateTime -import java.math.BigDecimal import java.time.ZoneId import java.time.ZoneOffset import java.util.* +import kotlin.math.max +import kotlin.math.min @Component class MarketQueryHandlerImpl( private val orderRepository: OrderRepository, private val tradeRepository: TradeRepository, + private val symbolMapper: SymbolMapper, ) : MarketQueryHandler { override suspend fun getTradeTickerData(startFrom: LocalDateTime): List { @@ -95,6 +99,32 @@ class MarketQueryHandlerImpl( } } + override suspend fun lastPrice(symbol: String?): List { + val list = if (symbol.isNullOrEmpty()) + tradeRepository.findAllGroupBySymbol() + else + tradeRepository.findBySymbolGroupBySymbol(symbol) + return list.collectList() + .awaitFirstOrElse { emptyList() } + .map { + val makerOrder = orderRepository.findByOuid(it.makerOuid).awaitFirst() + val apiSymbol = try { + symbolMapper.map(it.symbol) + } catch (e: Exception) { + it.symbol + } + val isMakerBuyer = makerOrder.direction == OrderDirection.BID + PriceTickerResponse( + apiSymbol, + if (isMakerBuyer) + min(it.takerPrice, it.makerPrice).toString() + else + max(it.takerPrice, it.makerPrice).toString() + ) + } + + } + private fun OrderModel.asQueryOrderResponse() = QueryOrderResponse( symbol, ouid, From fdadc1b6223cf33c81f9eb8551c4ccb3022be5dd Mon Sep 17 00:00:00 2001 From: Peyman Date: Sat, 18 Sep 2021 17:03:43 +0430 Subject: [PATCH 58/59] Close #62: Exchange info implemented --- .../app/controller/AccountantController.kt | 6 ++++ .../accountant/core/spi/PairConfigLoader.kt | 4 +++ .../postgres/impl/PairConfigLoaderImpl.kt | 15 ++++++++ .../src/main/resources/application-docker.yml | 2 ++ .../src/main/resources/application.yml | 2 ++ .../nilin/opex/api/core/inout/OrderEnums.kt | 6 +++- .../opex/api/core/inout/PairInfoResponse.kt | 9 +++++ .../co/nilin/opex/api/core/inout/RateLimit.kt | 10 ++++++ .../opex/api/core/inout/RateLimitType.kt | 9 +++++ .../opex/api/core/spi/AccountantProxy.kt | 4 +++ .../nilin/opex/api/core/spi/SymbolMapper.kt | 4 +++ .../port/api/binance/config/SecurityConfig.kt | 1 + .../binance/controller/MarketController.kt | 26 ++++++++++++++ .../api/binance/data/ExchangeInfoResponse.kt | 13 +++++++ .../api/binance/data/ExchangeInfoSymbol.kt | 19 ++++++++++ .../api/binance/data/RateLimitResponse.kt | 10 ++++++ .../api/binance/proxy/AccountantProxyImpl.kt | 35 +++++++++++++++++++ .../api/postgres/impl/SymbolMapperImpl.kt | 11 ++++++ 18 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/PairInfoResponse.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/RateLimit.kt create mode 100644 Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/RateLimitType.kt create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/ExchangeInfoResponse.kt create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/ExchangeInfoSymbol.kt create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/RateLimitResponse.kt create mode 100644 Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/proxy/AccountantProxyImpl.kt diff --git a/Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/controller/AccountantController.kt b/Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/controller/AccountantController.kt index 0acdd99f2..7cb4ce65b 100644 --- a/Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/controller/AccountantController.kt +++ b/Accountant/accountant-app/src/main/kotlin/co/nilin/opex/app/controller/AccountantController.kt @@ -1,5 +1,6 @@ package co.nilin.opex.app.controller +import co.nilin.opex.accountant.core.model.PairConfig import co.nilin.opex.accountant.core.model.PairFeeConfig import co.nilin.opex.accountant.core.spi.FinancialActionLoader import co.nilin.opex.accountant.core.spi.PairConfigLoader @@ -43,4 +44,9 @@ class AccountantController( return pairConfigLoader.load(pair, direction, level ?: "") } + @GetMapping("/config/all") + suspend fun fetchPairConfigs():List{ + return pairConfigLoader.loadPairConfigs() + } + } \ No newline at end of file diff --git a/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/PairConfigLoader.kt b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/PairConfigLoader.kt index d2fade236..22a9c2518 100644 --- a/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/PairConfigLoader.kt +++ b/Accountant/accountant-core/src/main/kotlin/co/nilin/opex/accountant/core/spi/PairConfigLoader.kt @@ -1,8 +1,12 @@ package co.nilin.opex.accountant.core.spi +import co.nilin.opex.accountant.core.model.PairConfig import co.nilin.opex.accountant.core.model.PairFeeConfig import co.nilin.opex.matching.core.model.OrderDirection interface PairConfigLoader { + + suspend fun loadPairConfigs(): List + suspend fun load(pair: String, direction: OrderDirection, userLevel: String): PairFeeConfig } \ No newline at end of file diff --git a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/PairConfigLoaderImpl.kt b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/PairConfigLoaderImpl.kt index c600b0267..5c499242c 100644 --- a/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/PairConfigLoaderImpl.kt +++ b/Accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/port/accountant/postgres/impl/PairConfigLoaderImpl.kt @@ -18,6 +18,21 @@ class PairConfigLoaderImpl( val pairConfigRepository: PairConfigRepository, val pairFeeConfigRepository: PairFeeConfigRepository ) : PairConfigLoader { + override suspend fun loadPairConfigs(): List { + return pairConfigRepository.findAll() + .collectList() + .awaitFirstOrElse { emptyList() } + .map { + PairConfig( + it.pair, + it.leftSideWalletSymbol, + it.rightSideWalletSymbol, + it.leftSideFraction, + it.rightSideFraction + ) + } + } + override suspend fun load(pair: String, direction: OrderDirection, userLevel: String): PairFeeConfig { val pairConfig = pairConfigRepository .findById(pair).awaitFirstOrElse { diff --git a/Api/api-app/src/main/resources/application-docker.yml b/Api/api-app/src/main/resources/application-docker.yml index 857c92425..2c3e93788 100644 --- a/Api/api-app/src/main/resources/application-docker.yml +++ b/Api/api-app/src/main/resources/application-docker.yml @@ -15,6 +15,8 @@ spring: app: cors: allowed-hosts: https://opex.dev, http://localhost:3000 + accountant: + url: lb://opex-accountant matching-gateway: url: lb://opex-gateway wallet: diff --git a/Api/api-app/src/main/resources/application.yml b/Api/api-app/src/main/resources/application.yml index 07f248f0a..c1517ab0d 100644 --- a/Api/api-app/src/main/resources/application.yml +++ b/Api/api-app/src/main/resources/application.yml @@ -32,6 +32,8 @@ spring: prefer-ip-address: true app: + accountant: + url: lb://opex-accountant matching-gateway: url: lb://opex-gateway wallet: diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OrderEnums.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OrderEnums.kt index f3db22c13..70c43fec1 100644 --- a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OrderEnums.kt +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/OrderEnums.kt @@ -24,7 +24,11 @@ enum class OrderType { STOP_LOSS_LIMIT, // timeInForce, quantity, price, stopPrice TAKE_PROFIT, // quantity, stopPrice TAKE_PROFIT_LIMIT, // timeInForce, quantity, price, stopPrice - LIMIT_MAKER, // quantity, price + LIMIT_MAKER; // quantity, price + + companion object { + fun activeTypes() = listOf(LIMIT, MARKET) + } } enum class OrderSide { diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/PairInfoResponse.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/PairInfoResponse.kt new file mode 100644 index 000000000..8d4c8ab2c --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/PairInfoResponse.kt @@ -0,0 +1,9 @@ +package co.nilin.opex.api.core.inout + +data class PairInfoResponse( + val pair: String, + val leftSideWalletSymbol: String, + val rightSideWalletSymbol: String, + val leftSideFraction: Double, + val rightSideFraction: Double +) \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/RateLimit.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/RateLimit.kt new file mode 100644 index 000000000..7de6e91c3 --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/RateLimit.kt @@ -0,0 +1,10 @@ +package co.nilin.opex.api.core.inout + +enum class RateLimit(val rateLimitType: RateLimitType, val interval: String, val intervalNum: Int, val limit: Int) { + + REQUEST_WEIGHT(RateLimitType.REQUEST_WEIGHT, "MINUTE", 1, 1200), + ORDERS_SECOND(RateLimitType.ORDERS, "SECOND", 10, 50), + ORDERS_DAY(RateLimitType.ORDERS, "DAY", 1, 16000), + RAW_REQUESTS(RateLimitType.RAW_REQUESTS, "MINUTE", 5, 6100) + +} \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/RateLimitType.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/RateLimitType.kt new file mode 100644 index 000000000..63231003a --- /dev/null +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/inout/RateLimitType.kt @@ -0,0 +1,9 @@ +package co.nilin.opex.api.core.inout + +enum class RateLimitType { + + REQUEST_WEIGHT, + ORDERS, + RAW_REQUESTS + +} \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/AccountantProxy.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/AccountantProxy.kt index 8bf00731e..16b831307 100644 --- a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/AccountantProxy.kt +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/AccountantProxy.kt @@ -1,5 +1,9 @@ package co.nilin.opex.api.core.spi +import co.nilin.opex.api.core.inout.PairInfoResponse + interface AccountantProxy { + suspend fun getPairConfigs(): List + } \ No newline at end of file diff --git a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/SymbolMapper.kt b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/SymbolMapper.kt index e1117590f..6e36e717c 100644 --- a/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/SymbolMapper.kt +++ b/Api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/SymbolMapper.kt @@ -1,6 +1,10 @@ package co.nilin.opex.api.core.spi interface SymbolMapper { + suspend fun map(symbol: String?): String? + suspend fun unmap(value: String?): String? + + suspend fun getKeyValues(): Map } \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt index 7c39a9c12..9d7bec883 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/config/SecurityConfig.kt @@ -29,6 +29,7 @@ class SecurityConfig(private val webClient: WebClient) { .pathMatchers("/v3/depth").permitAll() .pathMatchers("/v3/trades").permitAll() .pathMatchers("/v3/ticker/**").permitAll() + .pathMatchers("/v3/exchangeInfo").permitAll() .pathMatchers(HttpMethod.OPTIONS, "/**").permitAll() .pathMatchers("/**").hasAuthority("SCOPE_trust") .anyExchange().authenticated() diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt index 7bb39a47c..b0688a4ce 100644 --- a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/controller/MarketController.kt @@ -5,6 +5,9 @@ import co.nilin.opex.api.core.spi.SymbolMapper import co.nilin.opex.port.api.binance.data.OrderBookResponse import co.nilin.opex.api.core.inout.PriceChangeResponse import co.nilin.opex.api.core.inout.PriceTickerResponse +import co.nilin.opex.api.core.spi.AccountantProxy +import co.nilin.opex.port.api.binance.data.ExchangeInfoResponse +import co.nilin.opex.port.api.binance.data.ExchangeInfoSymbol import co.nilin.opex.port.api.binance.data.RecentTradeResponse import co.nilin.opex.utility.error.data.OpexError import co.nilin.opex.utility.error.data.OpexException @@ -26,6 +29,7 @@ import kotlin.collections.ArrayList @RestController class MarketController( + private val accountantProxy: AccountantProxy, private val marketQueryHandler: MarketQueryHandler, private val symbolMapper: SymbolMapper, ) { @@ -147,4 +151,26 @@ class MarketController( return marketQueryHandler.lastPrice(localSymbol) } + @GetMapping("/v3/exchangeInfo") + suspend fun pairInfo( + @RequestParam("symbol", required = false) + symbol: String?, + @RequestParam("symbols", required = false) + symbols: String? + ): ExchangeInfoResponse { + val symbolsMap = symbolMapper.getKeyValues() + val pairConfigs = accountantProxy.getPairConfigs() + .map { + ExchangeInfoSymbol( + symbolsMap[it.pair] ?: it.pair, + "TRADING", + it.leftSideWalletSymbol.toUpperCase(), + BigDecimal.valueOf(it.leftSideFraction).scale(), + it.rightSideWalletSymbol.toUpperCase(), + BigDecimal.valueOf(it.rightSideFraction).scale() + ) + } + return ExchangeInfoResponse(symbols = pairConfigs) + } + } \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/ExchangeInfoResponse.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/ExchangeInfoResponse.kt new file mode 100644 index 000000000..9a5cd229f --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/ExchangeInfoResponse.kt @@ -0,0 +1,13 @@ +package co.nilin.opex.port.api.binance.data + +import co.nilin.opex.api.core.inout.RateLimit +import java.util.* + +data class ExchangeInfoResponse( + val timezone: String = TimeZone.getDefault().id, + val serverTime: Long = Date().time, + val rateLimits: List = RateLimit.values() + .map { RateLimitResponse(it.rateLimitType, it.interval, it.intervalNum, it.limit) }, + val exchangeFilters: List = emptyList(), + val symbols: List +) \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/ExchangeInfoSymbol.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/ExchangeInfoSymbol.kt new file mode 100644 index 000000000..4679e05ea --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/ExchangeInfoSymbol.kt @@ -0,0 +1,19 @@ +package co.nilin.opex.port.api.binance.data + +import co.nilin.opex.api.core.inout.OrderType + +data class ExchangeInfoSymbol( + val symbol: String, + val status: String, + val baseAsset: String, + val baseAssetPrecision: Int, + val quoteAsset: String, + val quoteAssetPrecision: Int, + val orderTypes: List = OrderType.activeTypes(), + val icebergAllowed: Boolean = false, + val ocoAllowed: Boolean = false, + val isSpotTradingAllowed: Boolean = false, + val isMarginTradingAllowed: Boolean = false, + val filters: List = emptyList(), + val permissions: List = listOf("SPOT") +) \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/RateLimitResponse.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/RateLimitResponse.kt new file mode 100644 index 000000000..f102aeac6 --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/data/RateLimitResponse.kt @@ -0,0 +1,10 @@ +package co.nilin.opex.port.api.binance.data + +import co.nilin.opex.api.core.inout.RateLimitType + +data class RateLimitResponse( + val rateLimitType: RateLimitType, + val interval: String, + val intervalNum: Int, + val limit: Int +) \ No newline at end of file diff --git a/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/proxy/AccountantProxyImpl.kt b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/proxy/AccountantProxyImpl.kt new file mode 100644 index 000000000..a457a1b75 --- /dev/null +++ b/Api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/port/api/binance/proxy/AccountantProxyImpl.kt @@ -0,0 +1,35 @@ +package co.nilin.opex.port.api.binance.proxy + +import co.nilin.opex.api.core.inout.PairInfoResponse +import co.nilin.opex.api.core.spi.AccountantProxy +import co.nilin.opex.port.api.binance.util.LoggerDelegate +import kotlinx.coroutines.reactive.awaitSingle +import org.springframework.beans.factory.annotation.Value +import org.springframework.core.ParameterizedTypeReference +import org.springframework.http.MediaType +import org.springframework.stereotype.Component +import org.springframework.web.reactive.function.client.WebClient + +private inline fun typeRef(): ParameterizedTypeReference = + object : ParameterizedTypeReference() {} + +@Component +class AccountantProxyImpl(private val webClient: WebClient) : AccountantProxy { + + private val logger by LoggerDelegate() + + @Value("\${app.accountant.url}") + private lateinit var baseUrl: String + + override suspend fun getPairConfigs(): List { + logger.info("fetching pair configs") + return webClient.get() + .uri("$baseUrl/config/all") + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .onStatus({ t -> t.isError }, { it.createException() }) + .bodyToFlux(typeRef()) + .collectList() + .awaitSingle() + } +} \ No newline at end of file diff --git a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/SymbolMapperImpl.kt b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/SymbolMapperImpl.kt index e252214a4..5f6795eb4 100644 --- a/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/SymbolMapperImpl.kt +++ b/Api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/port/api/postgres/impl/SymbolMapperImpl.kt @@ -2,11 +2,13 @@ package co.nilin.opex.port.api.postgres.impl import co.nilin.opex.api.core.spi.SymbolMapper import co.nilin.opex.port.api.postgres.dao.SymbolMapRepository +import kotlinx.coroutines.reactive.awaitFirstOrElse import kotlinx.coroutines.reactive.awaitFirstOrNull import org.springframework.stereotype.Component @Component class SymbolMapperImpl(val symbolMapRepository: SymbolMapRepository) : SymbolMapper { + override suspend fun map(symbol: String?): String? { if (symbol == null) return null return symbolMapRepository.findBySymbol(symbol).awaitFirstOrNull()?.value @@ -16,4 +18,13 @@ class SymbolMapperImpl(val symbolMapRepository: SymbolMapRepository) : SymbolMap if (value == null) return null return symbolMapRepository.findByValue(value).awaitFirstOrNull()?.symbol } + + override suspend fun getKeyValues(): Map { + val map = HashMap() + symbolMapRepository.findAll() + .collectList() + .awaitFirstOrElse { emptyList() } + .forEach { map[it.symbol] = it.value } + return map + } } \ No newline at end of file From 124916bebf369cd7e6f1419bb795de937167f18f Mon Sep 17 00:00:00 2001 From: maryarm Date: Sat, 18 Sep 2021 22:56:54 +0200 Subject: [PATCH 59/59] Close #66: Add Jenkinsfile.deploy.groovy --- Jenkins/Jenkinsfile.deploy.groovy | 57 +++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Jenkins/Jenkinsfile.deploy.groovy diff --git a/Jenkins/Jenkinsfile.deploy.groovy b/Jenkins/Jenkinsfile.deploy.groovy new file mode 100644 index 000000000..628c665cd --- /dev/null +++ b/Jenkins/Jenkinsfile.deploy.groovy @@ -0,0 +1,57 @@ +pipeline { + agent any + + stages { + stage('Build') { + steps { + withMaven( + maven: 'maven-latest' + ) { + dir("Utility") { + sh 'mvn -B clean install' + } + + dir("MatchingEngine") { + sh 'mvn -B clean install' + } + + dir("MatchingGateway") { + sh 'mvn -B clean install' + } + + dir("Accountant") { + sh 'mvn -B clean install' + } + + dir("EventLog") { + sh 'mvn -B clean install' + } + + dir("UserManagement") { + sh 'mvn -B clean install' + } + + dir("Wallet") { + sh 'mvn -B clean install' + } + + dir("Api") { + sh 'mvn -B clean install' + } + } + + } + } + stage('Deliver') { + steps { + dir("Deployment") { + sh 'docker-compose build' + } + + dir("Deployment") { + sh 'docker-compose up -d' + } + } + } + } +}