├── .gitignore ├── Dockerfile_mtp-build ├── Dockerfile_mtp-data ├── README.md ├── build.gradle ├── doc ├── draw.io │ ├── png │ │ ├── application components.png │ │ ├── consumption - sequence.png │ │ ├── deployment components.png │ │ ├── fontend - sequence2.png │ │ ├── frontend - sequence.png │ │ └── producer sequece.png │ └── xml │ │ ├── application components.xml │ │ ├── consumption - sequence.xml │ │ ├── deployment components.xml │ │ ├── fontend - sequence2.xml │ │ ├── frontend - sequence.xml │ │ └── producer sequece.xml └── screenshot │ ├── transaction-detail.jpg │ ├── transactions-by-country.jpg │ ├── transactions-by-status.jpg │ └── transactions.jpg ├── docker-compose ├── .dockerignore ├── Dockerfile_mtp-module ├── build-images.sh ├── conf │ ├── spring-application_mtp-client.yml │ ├── spring-application_mtp-consumption.yml │ ├── spring-application_mtp-frontend.yml │ └── spring-application_mtp-processor.yml ├── docker-compose.yml └── start-compose.sh ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── mtp-client ├── build.gradle └── src │ └── main │ ├── conf │ └── spring-application.yml.template │ ├── java │ └── com │ │ └── vedri │ │ └── mtp │ │ └── consumption │ │ └── http │ │ └── akka │ │ ├── ConfigurableTransactionHttpClient.java │ │ └── TransactionHttpClient.java │ └── resources │ ├── application.conf │ ├── application.yml │ └── logback.xml ├── mtp-consumption ├── build.gradle └── src │ ├── main │ ├── conf │ │ └── spring-application.yml.template │ ├── java │ │ └── com │ │ │ └── vedri │ │ │ └── mtp │ │ │ └── consumption │ │ │ ├── ConsumptionApplication.java │ │ │ ├── ConsumptionConfig.java │ │ │ ├── ConsumptionProperties.java │ │ │ ├── MtpConsumptionConstants.java │ │ │ ├── http │ │ │ ├── AbstractHttpServer.java │ │ │ ├── ConsumptionAkkaConfiguration.java │ │ │ ├── ConsumptionRootActor.java │ │ │ ├── HttpServer.java │ │ │ └── akka │ │ │ │ ├── AkkaHttpServer.java │ │ │ │ └── MtpHttpApp.java │ │ │ ├── support │ │ │ └── kafka │ │ │ │ ├── KafkaMessageEnvelope.java │ │ │ │ └── KafkaProducerActor.java │ │ │ └── transaction │ │ │ ├── KafkaTransactionManager.java │ │ │ ├── TransactionManager.java │ │ │ ├── TransactionValidator.java │ │ │ └── ValidationFailedException.java │ ├── resources │ │ ├── application.conf │ │ ├── application.yml │ │ └── logback.xml │ └── scala │ │ └── akka │ │ └── http │ │ └── javadsl │ │ └── marshallers │ │ └── jackson │ │ └── CustomJackson.scala │ └── test │ └── java │ └── com │ └── vedri │ └── mtp │ └── consumption │ ├── ConsumptionTestConfig.java │ ├── http │ └── akka │ │ └── AkkaHttpServerTest.java │ ├── support │ └── cassandra │ │ ├── EmbeddedCassandra.java │ │ ├── EmbeddedCassandraConfiguration.java │ │ └── EmbeddedCassandraSupport.java │ └── transaction │ └── KafkaTransactionManagerTest.java ├── mtp-core-kafka ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── vedri │ └── mtp │ └── core │ └── support │ ├── kyro │ ├── KryoConfiguration.java │ ├── KryoDecoder.java │ ├── KryoEncoder.java │ ├── KryoThreadLocal.java │ └── ThreadLocalKryoDecoder.java │ └── serializer │ ├── TransactionJacksonDecoder.java │ ├── TransactionJacksonEncoder.java │ ├── TransactionKryoDecoder.java │ └── TransactionKryoEncoder.java ├── mtp-core-spark ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── vedri │ └── mtp │ └── processor │ └── support │ └── spark │ ├── CoreSparkProperties.java │ ├── SparkConfiguration.java │ └── SparkKryoRegistrator.java ├── mtp-core ├── build.gradle └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── vedri │ │ │ └── mtp │ │ │ └── core │ │ │ ├── CoreConfig.java │ │ │ ├── CoreProperties.java │ │ │ ├── rate │ │ │ └── cf │ │ │ │ ├── CfRate.java │ │ │ │ └── CfRateCalculator.java │ │ │ └── support │ │ │ ├── akka │ │ │ ├── AkkaConfiguration.java │ │ │ ├── AkkaMessage.java │ │ │ ├── AkkaTask.java │ │ │ ├── ClusterAkkaConfiguration.java │ │ │ ├── ClusterAwareHandler.java │ │ │ ├── ClusterAwareWatcher.java │ │ │ ├── LifecycleMessage.java │ │ │ ├── SpringActorProducer.java │ │ │ └── SpringExtension.java │ │ │ ├── cassandra │ │ │ └── CassandraConfiguration.java │ │ │ ├── http │ │ │ ├── AkkaHttpClient1.java │ │ │ ├── AkkaHttpClient2.java │ │ │ └── HttpComponentsHttpClient.java │ │ │ ├── json │ │ │ ├── CustomDateDateTimeDeserializer.java │ │ │ ├── JacksonConfiguration.java │ │ │ └── TransactionJacksonConfiguration.java │ │ │ ├── scala │ │ │ └── ScalaConverts.java │ │ │ └── spring │ │ │ └── AbstractApplication.java │ └── resources │ │ ├── application.conf │ │ ├── application.yml │ │ └── logback.xml │ └── test │ └── java │ └── com │ └── vedri │ └── mtp │ └── core │ └── rate │ └── cf │ └── CfRateCalculatorTest.java ├── mtp-frontend ├── .bowerrc ├── Gruntfile.js ├── bower.json ├── build.gradle ├── package.json └── src │ ├── main │ ├── conf │ │ └── spring-application.yml.template │ ├── java │ │ └── com │ │ │ └── vedri │ │ │ └── mtp │ │ │ └── frontend │ │ │ ├── FrontendApplication.java │ │ │ ├── FrontendConfig.java │ │ │ ├── FrontendProperties.java │ │ │ ├── MtpFrontendConstants.java │ │ │ ├── config │ │ │ ├── CacheConfiguration.java │ │ │ ├── FrontendAkkaConfiguration.java │ │ │ ├── FrontendRootActor.java │ │ │ ├── JacksonConfiguration.java │ │ │ ├── LocaleConfiguration.java │ │ │ ├── LoggingAspectConfiguration.java │ │ │ ├── MetricsConfiguration.java │ │ │ ├── OAuth2ServerConfiguration.java │ │ │ ├── SecurityConfiguration.java │ │ │ ├── WebConfigurer.java │ │ │ ├── WebsocketBrokerConfiguration.java │ │ │ ├── WebsocketConfiguration.java │ │ │ └── WebsocketSecurityConfiguration.java │ │ │ ├── support │ │ │ ├── aop │ │ │ │ └── logging │ │ │ │ │ └── LoggingAspect.java │ │ │ ├── apidoc │ │ │ │ └── SwaggerConfiguration.java │ │ │ ├── datetime │ │ │ │ ├── JSR310DateConverters.java │ │ │ │ ├── JSR310DateTimeSerializer.java │ │ │ │ └── JSR310LocalDateDeserializer.java │ │ │ ├── locale │ │ │ │ └── AngularCookieLocaleResolver.java │ │ │ ├── metrics │ │ │ │ ├── CassandraHealthIndicator.java │ │ │ │ └── MtpHealthIndicatorConfiguration.java │ │ │ ├── random │ │ │ │ └── RandomUtil.java │ │ │ ├── stomp │ │ │ │ ├── CallbackSubscriptionRegistry.java │ │ │ │ ├── DefaultSubscriptionRegistry.java │ │ │ │ ├── DeleteDestinationEvent.java │ │ │ │ ├── DestinationListener.java │ │ │ │ ├── NewDestinationEvent.java │ │ │ │ └── StompDestinationEvent.java │ │ │ └── web │ │ │ │ ├── HeaderUtil.java │ │ │ │ └── PaginationUtil.java │ │ │ ├── transaction │ │ │ └── aggregation │ │ │ │ ├── TransactionAggregationConfiguration.java │ │ │ │ ├── dao │ │ │ │ ├── SparkAggregationByCurrencyDao.java │ │ │ │ ├── SparkAggregationByOriginatingCountryDao.java │ │ │ │ ├── SparkAggregationByStatusDao.java │ │ │ │ └── SparkAggregationByUserDao.java │ │ │ │ └── subscription │ │ │ │ ├── AbstractPeriodicTopicActor.java │ │ │ │ ├── AggregationByCurrencyActor.java │ │ │ │ ├── AggregationByOriginatingCountryActor.java │ │ │ │ ├── AggregationByStatusActor.java │ │ │ │ ├── AggregationByUserActor.java │ │ │ │ ├── PeriodicTick.java │ │ │ │ ├── PtByAllCurrenciesActor.java │ │ │ │ ├── PtByAllOriginatingCountriesActor.java │ │ │ │ ├── PtByCurrencyActor.java │ │ │ │ ├── PtByOriginatingCountryActor.java │ │ │ │ ├── PtByUserActor.java │ │ │ │ ├── RtByAllCurrenciesActor.java │ │ │ │ ├── RtByAllOriginatingCountriesActor.java │ │ │ │ ├── RtByAllStatusesActor.java │ │ │ │ ├── RtByCurrencyActor.java │ │ │ │ ├── RtByOriginatingCountryActor.java │ │ │ │ ├── RtByStatusActor.java │ │ │ │ ├── RtByUserActor.java │ │ │ │ ├── TopicActorInfo.java │ │ │ │ ├── TopicActorSubscriptionInfo.java │ │ │ │ ├── WebsocketPeriodicActor.java │ │ │ │ └── WebsocketSessionRootActor.java │ │ │ ├── user │ │ │ ├── DefaultUserManager.java │ │ │ ├── UserManager.java │ │ │ └── dao │ │ │ │ ├── CassandraUserDao.java │ │ │ │ └── UserDao.java │ │ │ └── web │ │ │ ├── ApplicationWebXml.java │ │ │ ├── filter │ │ │ ├── CachingHttpHeadersFilter.java │ │ │ └── StaticResourcesProductionFilter.java │ │ │ ├── rest │ │ │ ├── admin │ │ │ │ ├── AccountResource.java │ │ │ │ ├── KeyAndPasswordDTO.java │ │ │ │ ├── LoggerDTO.java │ │ │ │ ├── LogsResource.java │ │ │ │ ├── ManagedUserDTO.java │ │ │ │ ├── UserDTO.java │ │ │ │ └── UserResource.java │ │ │ ├── errors │ │ │ │ ├── CustomParameterizedException.java │ │ │ │ ├── ErrorConstants.java │ │ │ │ ├── ErrorDTO.java │ │ │ │ ├── ExceptionTranslator.java │ │ │ │ ├── FieldErrorDTO.java │ │ │ │ └── ParameterizedErrorDTO.java │ │ │ ├── rate │ │ │ │ └── RateCalculatorResource.java │ │ │ └── transaction │ │ │ │ ├── CountryResource.java │ │ │ │ ├── CurrencyResource.java │ │ │ │ └── TransactionResource.java │ │ │ ├── security │ │ │ ├── AjaxLogoutSuccessHandler.java │ │ │ ├── AuthoritiesConstants.java │ │ │ ├── CustomUserDetails.java │ │ │ ├── Http401UnauthorizedEntryPoint.java │ │ │ ├── SecurityUtils.java │ │ │ ├── UserDetailsService.java │ │ │ └── UserNotActivatedException.java │ │ │ └── websocket │ │ │ ├── admin │ │ │ ├── ActivityDTO.java │ │ │ └── ActivityService.java │ │ │ └── transaction │ │ │ └── WebsocketSender.java │ ├── resources │ │ ├── application.conf │ │ ├── application.yml │ │ ├── i18n │ │ │ └── messages_en.properties │ │ └── logback.xml │ └── webapp │ │ ├── .htaccess │ │ ├── 404.html │ │ ├── apple-touch-icon.png │ │ ├── assets │ │ ├── images │ │ │ └── development_ribbon.png │ │ └── styles │ │ │ ├── documentation.css │ │ │ └── main.css │ │ ├── favicon.ico │ │ ├── i18n │ │ └── en │ │ │ ├── activate.json │ │ │ ├── audits.json │ │ │ ├── configuration.json │ │ │ ├── error.json │ │ │ ├── global.json │ │ │ ├── health.json │ │ │ ├── login.json │ │ │ ├── logs.json │ │ │ ├── main.json │ │ │ ├── metrics.json │ │ │ ├── password.json │ │ │ ├── register.json │ │ │ ├── reset.json │ │ │ ├── sessions.json │ │ │ ├── settings.json │ │ │ ├── tracker.json │ │ │ ├── transaction.json │ │ │ ├── transactions-by-country.json │ │ │ ├── transactions-by-currency.json │ │ │ ├── transactions-by-status.json │ │ │ └── user.management.json │ │ ├── index.html │ │ ├── maps │ │ ├── data │ │ │ ├── countries.geo.json │ │ │ └── countries.json │ │ ├── images │ │ │ └── flags32.png │ │ └── styles │ │ │ └── flags32.css │ │ ├── robots.txt │ │ ├── scripts │ │ ├── app │ │ │ ├── account │ │ │ │ ├── account.js │ │ │ │ ├── activate │ │ │ │ │ ├── activate.controller.js │ │ │ │ │ ├── activate.html │ │ │ │ │ └── activate.js │ │ │ │ ├── login │ │ │ │ │ ├── login.controller.js │ │ │ │ │ ├── login.html │ │ │ │ │ └── login.js │ │ │ │ ├── password │ │ │ │ │ ├── password.controller.js │ │ │ │ │ ├── password.directive.js │ │ │ │ │ ├── password.html │ │ │ │ │ └── password.js │ │ │ │ ├── register │ │ │ │ │ ├── register.controller.js │ │ │ │ │ ├── register.html │ │ │ │ │ └── register.js │ │ │ │ ├── reset │ │ │ │ │ ├── finish │ │ │ │ │ │ ├── reset.finish.controller.js │ │ │ │ │ │ ├── reset.finish.html │ │ │ │ │ │ └── reset.finish.js │ │ │ │ │ └── request │ │ │ │ │ │ ├── reset.request.controller.js │ │ │ │ │ │ ├── reset.request.html │ │ │ │ │ │ └── reset.request.js │ │ │ │ └── settings │ │ │ │ │ ├── settings.controller.js │ │ │ │ │ ├── settings.html │ │ │ │ │ └── settings.js │ │ │ ├── admin │ │ │ │ ├── admin.js │ │ │ │ ├── audits │ │ │ │ │ ├── audits.controller.js │ │ │ │ │ ├── audits.html │ │ │ │ │ └── audits.js │ │ │ │ ├── configuration │ │ │ │ │ ├── configuration.controller.js │ │ │ │ │ ├── configuration.html │ │ │ │ │ └── configuration.js │ │ │ │ ├── docs │ │ │ │ │ ├── docs.html │ │ │ │ │ └── docs.js │ │ │ │ ├── health │ │ │ │ │ ├── health.controller.js │ │ │ │ │ ├── health.html │ │ │ │ │ ├── health.js │ │ │ │ │ ├── health.modal.controller.js │ │ │ │ │ └── health.modal.html │ │ │ │ ├── logs │ │ │ │ │ ├── logs.controller.js │ │ │ │ │ ├── logs.html │ │ │ │ │ └── logs.js │ │ │ │ ├── metrics │ │ │ │ │ ├── metrics.controller.js │ │ │ │ │ ├── metrics.html │ │ │ │ │ ├── metrics.js │ │ │ │ │ ├── metrics.modal.controller.js │ │ │ │ │ └── metrics.modal.html │ │ │ │ ├── tracker │ │ │ │ │ ├── tracker.controller.js │ │ │ │ │ ├── tracker.html │ │ │ │ │ └── tracker.js │ │ │ │ └── user-management │ │ │ │ │ ├── user-management-detail.controller.js │ │ │ │ │ ├── user-management-detail.html │ │ │ │ │ ├── user-management.controller.js │ │ │ │ │ ├── user-management.html │ │ │ │ │ └── user-management.js │ │ │ ├── app.constants.js │ │ │ ├── app.js │ │ │ ├── entities │ │ │ │ ├── entity.js │ │ │ │ ├── transaction │ │ │ │ │ ├── transaction-detail.html │ │ │ │ │ ├── transaction-detail.js │ │ │ │ │ ├── transaction.js │ │ │ │ │ ├── transactions.html │ │ │ │ │ └── transactions.js │ │ │ │ ├── transactions-by-country │ │ │ │ │ ├── transactions-by-country.controller.js │ │ │ │ │ ├── transactions-by-country.html │ │ │ │ │ └── transactions-by-country.js │ │ │ │ ├── transactions-by-currency │ │ │ │ │ ├── transactions-by-currency.controller.js │ │ │ │ │ ├── transactions-by-currency.html │ │ │ │ │ └── transactions-by-currency.js │ │ │ │ └── transactions-by-status │ │ │ │ │ ├── transactions-by-status.controller.js │ │ │ │ │ ├── transactions-by-status.html │ │ │ │ │ └── transactions-by-status.js │ │ │ ├── error │ │ │ │ ├── accessdenied.html │ │ │ │ ├── error.html │ │ │ │ └── error.js │ │ │ └── main │ │ │ │ ├── main.controller.js │ │ │ │ ├── main.html │ │ │ │ └── main.js │ │ └── components │ │ │ ├── admin │ │ │ ├── audits.service.js │ │ │ ├── configuration.service.js │ │ │ ├── logs.service.js │ │ │ └── monitoring.service.js │ │ │ ├── alert │ │ │ ├── alert.directive.js │ │ │ └── alert.service.js │ │ │ ├── auth │ │ │ ├── auth.service.js │ │ │ ├── authority.directive.js │ │ │ ├── principal.service.js │ │ │ ├── provider │ │ │ │ └── auth.oauth2.service.js │ │ │ └── services │ │ │ │ ├── account.service.js │ │ │ │ ├── activate.service.js │ │ │ │ ├── password.service.js │ │ │ │ └── register.service.js │ │ │ ├── constants │ │ │ ├── general.constants.js │ │ │ └── validation-status.constant.js │ │ │ ├── entities │ │ │ ├── aggregation.service.js │ │ │ ├── country.service.js │ │ │ ├── currency.service.js │ │ │ └── transaction.service.js │ │ │ ├── form │ │ │ ├── form.directive.js │ │ │ ├── maxbytes.directive.js │ │ │ ├── minbytes.directive.js │ │ │ ├── pager.directive.js │ │ │ ├── pager.html │ │ │ ├── pagination.directive.js │ │ │ └── pagination.html │ │ │ ├── interceptor │ │ │ ├── auth.interceptor.js │ │ │ ├── errorhandler.interceptor.js │ │ │ └── notification.interceptor.js │ │ │ ├── language │ │ │ ├── language.controller.js │ │ │ └── language.service.js │ │ │ ├── navbar │ │ │ ├── navbar.controller.js │ │ │ ├── navbar.directive.js │ │ │ └── navbar.html │ │ │ ├── tracker │ │ │ └── tracker.service.js │ │ │ ├── user │ │ │ └── user.service.js │ │ │ ├── util │ │ │ ├── base64.service.js │ │ │ ├── capitalize.filter.js │ │ │ ├── datetime.filter.js │ │ │ ├── dateutil.service.js │ │ │ ├── parse-links.service.js │ │ │ ├── remember.service.js │ │ │ └── truncate.filter.js │ │ │ └── websocket │ │ │ └── websocket.service.js │ │ └── swagger-ui │ │ ├── images │ │ └── throbber.gif │ │ └── index.html │ └── test │ └── java │ └── com │ └── vedri │ └── mtp │ └── frontend │ └── web │ ├── rest │ └── admin │ │ ├── AccountResourceTest.java │ │ └── UserResourceTest.java │ └── security │ └── SecurityUtilsTest.java ├── mtp-model ├── build.gradle └── src │ ├── main │ ├── cql │ │ ├── create_data.cql │ │ ├── create_keyspace.cql │ │ ├── create_keyspace_prod.cql │ │ └── create_tables.cql │ ├── java │ │ └── com │ │ │ └── vedri │ │ │ └── mtp │ │ │ └── core │ │ │ ├── MtpConstants.java │ │ │ ├── country │ │ │ ├── CachingCountryManager.java │ │ │ ├── Country.java │ │ │ ├── CountryManager.java │ │ │ └── dao │ │ │ │ ├── CassandraCountryDao.java │ │ │ │ └── CountryDao.java │ │ │ ├── currency │ │ │ ├── CacheCurrencyManager.java │ │ │ ├── Currency.java │ │ │ └── CurrencyManager.java │ │ │ ├── rate │ │ │ ├── CachingRateCalculator.java │ │ │ ├── NoRateException.java │ │ │ ├── Rate.java │ │ │ ├── RateCalculator.java │ │ │ └── dao │ │ │ │ ├── CassandraRateDao.java │ │ │ │ └── RateDao.java │ │ │ ├── support │ │ │ └── cassandra │ │ │ │ ├── CassandraPartitionFetcher.java │ │ │ │ ├── CassandraPartitionForHourUtils.java │ │ │ │ ├── CassandraPartitionForMinuteUtils.java │ │ │ │ ├── CassandraUtils.java │ │ │ │ └── ColumnUtils.java │ │ │ ├── transaction │ │ │ ├── TableName.java │ │ │ ├── Transaction.java │ │ │ ├── aggregation │ │ │ │ ├── TimeAggregation.java │ │ │ │ ├── TransactionAggregationByCountry.java │ │ │ │ ├── TransactionAggregationByCurrency.java │ │ │ │ ├── TransactionAggregationByStatus.java │ │ │ │ ├── TransactionAggregationByUser.java │ │ │ │ ├── TransactionValidationStatus.java │ │ │ │ ├── YearToHourTime.java │ │ │ │ └── dao │ │ │ │ │ └── YearToHourTimeUtil.java │ │ │ └── dao │ │ │ │ ├── CassandraTransactionDao.java │ │ │ │ └── TransactionDao.java │ │ │ └── user │ │ │ └── User.java │ └── spark │ │ └── countries │ │ ├── countries.json │ │ ├── countries.json.txt │ │ └── import_countries.script.scala │ └── test │ └── java │ └── com │ └── vedri │ └── mtp │ └── core │ ├── country │ └── CachingCountryManagerTest.java │ ├── currency │ └── CacheCurrencyManagerTest.java │ └── rate │ └── CachingRateCalculatorTest.java ├── mtp-processor ├── build.gradle └── src │ └── main │ ├── conf │ └── spring-application.yml.template │ ├── java │ └── com │ │ └── vedri │ │ └── mtp │ │ └── processor │ │ ├── MtpProcessorConstants.java │ │ ├── ProcessorApplication.java │ │ ├── ProcessorConfig.java │ │ ├── ProcessorProperties.java │ │ ├── streaming │ │ ├── KafkaStreamingActor.java │ │ ├── ProcessorAkkaConfiguration.java │ │ ├── ProcessorRootActor.java │ │ └── handler │ │ │ ├── CreateStreamBuilder.java │ │ │ ├── CreateTransactionBuilder.java │ │ │ ├── DayTransactionAggregationByCountryBuilderTemplate.java │ │ │ ├── DayTransactionAggregationByCurrencyBuilderTemplate.java │ │ │ ├── FilterOkTransactionStatusBuilder.java │ │ │ ├── NoIO.java │ │ │ ├── PlacedDayTransactionAggregationByCountryBuilder.java │ │ │ ├── PlacedDayTransactionAggregationByCurrencyBuilder.java │ │ │ ├── PlacedTimeTransactionAggregationByCountryBuilder.java │ │ │ ├── PlacedTimeTransactionAggregationByCurrencyBuilder.java │ │ │ ├── PlacedTimeTransactionAggregationByUserBuilder.java │ │ │ ├── ReceivedDayTransactionAggregationByCountryBuilder.java │ │ │ ├── ReceivedDayTransactionAggregationByCurrencyBuilder.java │ │ │ ├── ReceivedTimeTransactionAggregationByCountryBuilder.java │ │ │ ├── ReceivedTimeTransactionAggregationByCurrencyBuilder.java │ │ │ ├── ReceivedTimeTransactionAggregationByStatusBuilder.java │ │ │ ├── ReceivedTimeTransactionAggregationByUserBuilder.java │ │ │ ├── StoreTransactionStatusBuilder.java │ │ │ ├── StreamBuilder.java │ │ │ ├── TimeTransactionAggregationByCountryBuilderTemplate.java │ │ │ ├── TimeTransactionAggregationByCurrencyBuilderTemplate.java │ │ │ ├── TimeTransactionAggregationByUserBuilderTemplate.java │ │ │ └── ValidateTransactionBuilder.java │ │ ├── support │ │ └── kafka │ │ │ ├── EmbeddedKafka.java │ │ │ └── KafkaConfiguration.java │ │ └── transaction │ │ └── TransactionValidator.java │ └── resources │ ├── application.conf │ ├── application.yml │ └── logback.xml └── settings.gradle /Dockerfile_mtp-build: -------------------------------------------------------------------------------- 1 | 2 | FROM frekele/java:jdk8 3 | 4 | ENV LOCAL_SRC_HOME . 5 | ENV SRC_HOME /code/mtp 6 | 7 | RUN apt-get update 8 | 9 | RUN apt-get -y install git 10 | 11 | ENV NODE_VERSION 0.12.0 12 | ENV NODE_DIR /opt/nodejs 13 | 14 | RUN mkdir ${NODE_DIR} && \ 15 | curl -L https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz | tar xvzf - -C ${NODE_DIR} --strip-components=1 16 | 17 | ENV PATH $PATH:${NODE_DIR}/bin 18 | 19 | RUN ["npm", "cache", "clean", "-f"] 20 | RUN ["npm", "install", "bower", "-g"] 21 | RUN ["npm", "install", "grunt-cli", "-g"] 22 | 23 | WORKDIR $SRC_HOME 24 | 25 | ADD $LOCAL_SRC_HOME $SRC_HOME 26 | 27 | WORKDIR $SRC_HOME/mtp-frontend 28 | 29 | RUN ["npm", "--no-optional", "install"] 30 | 31 | RUN ["bower", "--allow-root", "install"] 32 | 33 | RUN ["grunt", "build"] 34 | 35 | WORKDIR $SRC_HOME 36 | RUN ["./gradlew", "clean", "distZip"] 37 | 38 | 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MTP (My Test Project) 2 | 3 | ### [Wiki](https://github.com/vpugar/mtp/wiki) 4 | -------------------------------------------------------------------------------- /doc/draw.io/png/application components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpugar/mtp/f9f4f811d422357d0dc19df016c5835017a3ee2f/doc/draw.io/png/application components.png -------------------------------------------------------------------------------- /doc/draw.io/png/consumption - sequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpugar/mtp/f9f4f811d422357d0dc19df016c5835017a3ee2f/doc/draw.io/png/consumption - sequence.png -------------------------------------------------------------------------------- /doc/draw.io/png/deployment components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpugar/mtp/f9f4f811d422357d0dc19df016c5835017a3ee2f/doc/draw.io/png/deployment components.png -------------------------------------------------------------------------------- /doc/draw.io/png/fontend - sequence2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpugar/mtp/f9f4f811d422357d0dc19df016c5835017a3ee2f/doc/draw.io/png/fontend - sequence2.png -------------------------------------------------------------------------------- /doc/draw.io/png/frontend - sequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpugar/mtp/f9f4f811d422357d0dc19df016c5835017a3ee2f/doc/draw.io/png/frontend - sequence.png -------------------------------------------------------------------------------- /doc/draw.io/png/producer sequece.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpugar/mtp/f9f4f811d422357d0dc19df016c5835017a3ee2f/doc/draw.io/png/producer sequece.png -------------------------------------------------------------------------------- /doc/screenshot/transaction-detail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpugar/mtp/f9f4f811d422357d0dc19df016c5835017a3ee2f/doc/screenshot/transaction-detail.jpg -------------------------------------------------------------------------------- /doc/screenshot/transactions-by-country.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpugar/mtp/f9f4f811d422357d0dc19df016c5835017a3ee2f/doc/screenshot/transactions-by-country.jpg -------------------------------------------------------------------------------- /doc/screenshot/transactions-by-status.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpugar/mtp/f9f4f811d422357d0dc19df016c5835017a3ee2f/doc/screenshot/transactions-by-status.jpg -------------------------------------------------------------------------------- /doc/screenshot/transactions.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpugar/mtp/f9f4f811d422357d0dc19df016c5835017a3ee2f/doc/screenshot/transactions.jpg -------------------------------------------------------------------------------- /docker-compose/.dockerignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpugar/mtp/f9f4f811d422357d0dc19df016c5835017a3ee2f/docker-compose/.dockerignore -------------------------------------------------------------------------------- /docker-compose/Dockerfile_mtp-module: -------------------------------------------------------------------------------- 1 | 2 | FROM mtp-build:0.5 3 | 4 | ARG MTP_MODULE= 5 | 6 | ENV MTP_MODULE_ENV $MTP_MODULE 7 | ENV SRC_HOME /code/mtp 8 | ENV MTP_VERSION 0.5.0 9 | ENV WORK_HOME /opt/mtp 10 | 11 | 12 | WORKDIR /usr/local/bin 13 | ENV DOCKERIZE_VERSION v0.3.0 14 | RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ 15 | && tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ 16 | && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz 17 | 18 | 19 | WORKDIR $WORK_HOME 20 | 21 | RUN unzip $SRC_HOME/$MTP_MODULE/build/distributions/$MTP_MODULE-$MTP_VERSION.zip 22 | 23 | WORKDIR $WORK_HOME/$MTP_MODULE-$MTP_VERSION 24 | 25 | COPY conf/spring-application_$MTP_MODULE.yml conf/spring-application.yml 26 | 27 | CMD dockerize -wait tcp://mtp-data:9042 -timeout 60s $WORK_HOME/$MTP_MODULE_ENV-$MTP_VERSION/bin/$MTP_MODULE_ENV --spring.profiles.active=prod 28 | -------------------------------------------------------------------------------- /docker-compose/build-images.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CURRENT_PATH=`pwd -P` 4 | SCRIPT=`realpath -s $0` 5 | SCRIPTPATH=`dirname $SCRIPT` 6 | 7 | cd $SCRIPTPATH 8 | 9 | echo "Working in $SCRIPTPATH" 10 | 11 | echo "Building mtp-build" 12 | #docker build --force-rm -f ../Dockerfile_mtp-build -t mtp-build:0.5 .. 13 | 14 | echo "Building mtp-data - 1st part" 15 | docker build --force-rm -f ../Dockerfile_mtp-data -t mtp-data:0.5 .. 16 | ID="$(docker run -v mtp-data:/var/lib/cassandra/data -d --name mtp-data mtp-data:0.5)" 17 | #ID="$(docker ps -f name=mtp-data -q | head -1)" 18 | echo "Started casandra mtp-data with ID $ID" 19 | 20 | echo "Building mtp-data - 2nd part - importing data" 21 | docker exec $ID dockerize -wait tcp://localhost:9042 -timeout 60s cqlsh -f /code/mtp/cql/create_keyspace.cql 22 | docker exec $ID cqlsh -f /code/mtp/cql/create_tables.cql 23 | docker exec $ID cqlsh -f /code/mtp/cql/create_data.cql 24 | docker exec $ID sh -c "cd /code/mtp/countries; /opt/spark/spark-1.5.2-bin-hadoop2.6/bin/spark-shell --packages com.datastax.spark:spark-cassandra-connector_2.10:1.5.0-M2 --conf spark.cassandra.connection.host=localhost < import_countries.script.scala" 25 | docker commit $ID mtp-data:0.5 26 | docker stop $ID 27 | 28 | echo "Successfully finished build. Start all with 'start-compose.sh' or 'docker-compose up --build -d'" 29 | 30 | cd $CURRENT_PATH 31 | -------------------------------------------------------------------------------- /docker-compose/conf/spring-application_mtp-client.yml: -------------------------------------------------------------------------------- 1 | mtp: 2 | core: 3 | cassandra: 4 | hosts: mtp-data 5 | port: 9042 6 | keyspace: mtp -------------------------------------------------------------------------------- /docker-compose/conf/spring-application_mtp-consumption.yml: -------------------------------------------------------------------------------- 1 | mtp: 2 | consumption: 3 | cassandra: 4 | hosts: mtp-data 5 | kafkaClient: 6 | bootstrapServers: mtp-processor:9092 7 | httpServer: 8 | bindHost: 0.0.0.0 9 | publicProtocol: http 10 | publicHost: mtp-consumption 11 | publicPort: 9090 -------------------------------------------------------------------------------- /docker-compose/conf/spring-application_mtp-frontend.yml: -------------------------------------------------------------------------------- 1 | mtp: 2 | frontend: 3 | cassandra: 4 | hosts: mtp-data 5 | spark: 6 | master: local[10] 7 | cassandraHosts: mtp-data 8 | checkpointDir: /tmp/spark-frontend 9 | 10 | security: 11 | authentication: 12 | oauth: 13 | secret: mySecretOAuthSecret 14 | tokenValidityInSeconds: 1800 15 | rememberme: 16 | # security key (this key should be unique for your application, and kept secret) 17 | key: ec453265e6cfb1227a45d95d969ba35b002a5ef3 18 | server: 19 | address: mtp-frontend 20 | port: 8080 21 | #use-forward-headers: true 22 | #proxyName: localhost 23 | #proxyPort: 80 24 | #useIPVHosts=true 25 | #tomcat: -------------------------------------------------------------------------------- /docker-compose/conf/spring-application_mtp-processor.yml: -------------------------------------------------------------------------------- 1 | mtp: 2 | processor: 3 | cassandra: 4 | hosts: mtp-data 5 | spark: 6 | master: local[10] 7 | cassandraHosts: mtp-data 8 | batchInterval: 10000 9 | checkpointDir: /tmp 10 | kafkaServer: 11 | host: mtp-processor 12 | advertisedHost: mtp-processor 13 | zookeeper: 14 | connect: mtp-processor:2181 15 | cfRate: 16 | serviceUrl: http://mtp-frontend:8080/test/rates 17 | -------------------------------------------------------------------------------- /docker-compose/start-compose.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CURRENT_PATH=`pwd -P` 4 | SCRIPT=`realpath -s $0` 5 | SCRIPTPATH=`dirname $SCRIPT` 6 | 7 | cd $SCRIPTPATH 8 | 9 | echo "Working in $SCRIPTPATH" 10 | 11 | docker-compose up --build 12 | #docker-compose up --build -d 13 | 14 | cd $CURRENT_PATH 15 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | group=vedri 2 | rootProject.name=mtp 3 | bareVersion=0.5.0 4 | 5 | akkaHttpVersion=1.0 6 | akkaVersion=2.3.12 7 | cassandraDriverVersion=2.2.0-rc3 8 | cassandraVersion=2.1.9.2 9 | commonsLangVersion=3.4 10 | commonsIoVersion=2.4 11 | dropwizardMetricsVersion=3.1.2 12 | jacksonVersion=2.6.4 13 | jacksonScalaVersion=2.6.3 14 | jacksonVersionWithJSR310=2.6.4 15 | validationApiVersion=1.1.0.Final 16 | jcommanderVersion=1.48 17 | jodaTimeVersion=2.8.2 18 | kafkaVersion=0.8.2.2 19 | kryoVersion=3.0.3 20 | lombokVersion=1.16.6 21 | geronimoJavamailMailVersion=1.8.4 22 | guavaVersion=16.0.1 23 | hibernateValidatorVersion=5.2.2.Final 24 | httpclientVersion=4.5.1 25 | metricsSparkReporterVersion=1.2 26 | metricsSpringVersion=3.1.2 27 | mockitoVersion=1.10.19 28 | scalaMajorVersion=2.11 29 | scalaVersion=2.11.7 30 | slf4jVersion=1.7.12 31 | sparkCassandraConnectorVersion=1.5.0-M2 32 | sparkVersion=1.5.1 33 | springBootVersion=1.3.0.RC1 34 | springfoxVersion=2.0.3 35 | springSecurityVersion=4.0.2.RELEASE 36 | springSecurityOauth2Version=2.0.7.RELEASE 37 | springockitoVersion=1.0.9 38 | springVersion=4.2.2.RELEASE 39 | testNgVersion=6.9.4 40 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpugar/mtp/f9f4f811d422357d0dc19df016c5835017a3ee2f/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Sep 24 14:11:00 CEST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip 7 | -------------------------------------------------------------------------------- /mtp-client/src/main/conf/spring-application.yml.template: -------------------------------------------------------------------------------- 1 | mtp: 2 | core: 3 | cassandra: 4 | hosts: localhost 5 | port: 9042 6 | keyspace: mtp 7 | -------------------------------------------------------------------------------- /mtp-client/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | mtp: 2 | core: 3 | cluster.nodeName: nodeClient0 4 | akka: 5 | akkaSystemName: MtpClient 6 | logConfiguration: false 7 | logClusterMetrics: false 8 | cassandra: 9 | hosts: localhost 10 | port: 9042 11 | keyspace: mtp 12 | -------------------------------------------------------------------------------- /mtp-consumption/src/main/conf/spring-application.yml.template: -------------------------------------------------------------------------------- 1 | mtp: 2 | consumption: 3 | cassandra: 4 | hosts: localhost 5 | kafkaClient: 6 | bootstrapServers: localhost:9092 7 | httpServer: 8 | bindHost: localhost 9 | publicProtocol: http 10 | publicHost: localhost 11 | publicPort: 9090 -------------------------------------------------------------------------------- /mtp-consumption/src/main/java/com/vedri/mtp/consumption/ConsumptionApplication.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.consumption; 2 | 3 | import com.vedri.mtp.core.support.cassandra.CassandraConfiguration; 4 | import com.vedri.mtp.core.support.json.JacksonConfiguration; 5 | import com.vedri.mtp.core.support.kyro.KryoConfiguration; 6 | import org.springframework.context.ApplicationContext; 7 | 8 | import com.vedri.mtp.core.CoreConfig; 9 | import com.vedri.mtp.core.support.spring.AbstractApplication; 10 | 11 | public class ConsumptionApplication extends AbstractApplication { 12 | 13 | @Override 14 | protected Class[] getConfigs() { 15 | return new Class[] { 16 | CoreConfig.class, ConsumptionConfig.class, JacksonConfiguration.class, CassandraConfiguration.class, 17 | KryoConfiguration.class 18 | }; 19 | } 20 | 21 | @Override 22 | protected void doStart(ApplicationContext context) throws Exception { 23 | 24 | } 25 | 26 | public static void main(String[] args) throws Exception { 27 | new ConsumptionApplication().startApplication(args); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /mtp-consumption/src/main/java/com/vedri/mtp/consumption/ConsumptionConfig.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.consumption; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.ComponentScan; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | import com.vedri.mtp.core.CoreProperties; 10 | 11 | @ComponentScan(basePackages = { 12 | "com.vedri.mtp.core.support.serializer", 13 | "com.vedri.mtp.core.transaction", 14 | "com.vedri.mtp.core.support.json", 15 | "com.vedri.mtp.consumption.http", 16 | "com.vedri.mtp.consumption.support.kafka", 17 | "com.vedri.mtp.consumption.transaction" 18 | }) 19 | @Configuration 20 | @EnableConfigurationProperties({ ConsumptionProperties.class }) 21 | public class ConsumptionConfig { 22 | 23 | @Autowired 24 | private ConsumptionProperties consumptionProperties; 25 | 26 | @Bean 27 | public CoreProperties.Cluster clusterProperties() { 28 | return consumptionProperties.getCluster(); 29 | } 30 | 31 | @Bean 32 | public CoreProperties.Cassandra cassandraProperties() { 33 | return consumptionProperties.getCassandra(); 34 | } 35 | 36 | @Bean 37 | public CoreProperties.Akka akkaProperties() { 38 | return consumptionProperties.getAkka(); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /mtp-consumption/src/main/java/com/vedri/mtp/consumption/MtpConsumptionConstants.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.consumption; 2 | 3 | public interface MtpConsumptionConstants { 4 | 5 | // configs 6 | String CONFIG_PREFIX = "mtp.consumption"; 7 | 8 | String HTTP_HEADER_TIME_OFFSET = "Time-Offset"; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /mtp-consumption/src/main/java/com/vedri/mtp/consumption/http/AbstractHttpServer.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.consumption.http; 2 | 3 | import com.vedri.mtp.consumption.ConsumptionProperties; 4 | import lombok.Getter; 5 | import lombok.extern.slf4j.Slf4j; 6 | 7 | import org.springframework.beans.factory.annotation.Value; 8 | 9 | import akka.actor.ActorRef; 10 | 11 | import com.vedri.mtp.consumption.MtpConsumptionConstants; 12 | 13 | @Getter 14 | @Slf4j 15 | public abstract class AbstractHttpServer implements HttpServer { 16 | 17 | protected final ConsumptionProperties.HttpServer httpServer; 18 | 19 | protected AbstractHttpServer(ConsumptionProperties.HttpServer httpServer) { 20 | this.httpServer = httpServer; 21 | } 22 | 23 | @Override 24 | public void start() throws Exception { 25 | log.info("Starting http server"); 26 | doStart(); 27 | } 28 | 29 | @Override 30 | public void stop() throws Exception { 31 | log.info("Stopping http server"); 32 | doStop(); 33 | } 34 | 35 | protected abstract void doStart() throws Exception; 36 | 37 | protected abstract void doStop() throws Exception; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /mtp-consumption/src/main/java/com/vedri/mtp/consumption/http/HttpServer.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.consumption.http; 2 | 3 | public interface HttpServer { 4 | 5 | void start() throws Exception; 6 | 7 | void stop() throws Exception; 8 | 9 | } 10 | -------------------------------------------------------------------------------- /mtp-consumption/src/main/java/com/vedri/mtp/consumption/support/kafka/KafkaMessageEnvelope.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.consumption.support.kafka; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.ToString; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | @ToString 10 | public class KafkaMessageEnvelope { 11 | private String topic; 12 | private K key; 13 | private V message; 14 | } 15 | -------------------------------------------------------------------------------- /mtp-consumption/src/main/java/com/vedri/mtp/consumption/transaction/TransactionManager.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.consumption.transaction; 2 | 3 | import akka.actor.ActorRef; 4 | import com.vedri.mtp.core.transaction.Transaction; 5 | 6 | import java.util.UUID; 7 | 8 | public interface TransactionManager { 9 | 10 | void start(final ActorRef consumerActorRef); 11 | 12 | Transaction addTransaction(final Transaction transaction) throws ValidationFailedException; 13 | 14 | Transaction getTransaction(final String transactionId); 15 | 16 | Transaction getTransaction(final UUID transactionId); 17 | } 18 | -------------------------------------------------------------------------------- /mtp-consumption/src/main/java/com/vedri/mtp/consumption/transaction/ValidationFailedException.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.consumption.transaction; 2 | 3 | 4 | public class ValidationFailedException extends RuntimeException { 5 | 6 | public ValidationFailedException(String message) { 7 | super(message); 8 | } 9 | 10 | public ValidationFailedException(String message, Exception e) { 11 | super(message, e); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /mtp-consumption/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | mtp: 2 | consumption: 3 | cluster: 4 | appName: MtpConsumption 5 | nodeName: nodeConsumption0 6 | akka: 7 | akkaSystemName: MtpConsumption 8 | logConfiguration: false 9 | logClusterMetrics: false 10 | cassandra: 11 | hosts: localhost 12 | port: 9042 13 | keyspace: mtp 14 | balancingPool.instances: 5 15 | kafkaClient: 16 | bootstrapServers: localhost:9092 17 | acks: 1 18 | batchSize: 100 19 | topicName: mtp.transaction 20 | reconnectBackoffMs: 100 21 | httpServer: 22 | bindHost: localhost 23 | bindPort: 9090 24 | publicProtocol: http 25 | publicHost: localhost 26 | publicPort: 9090 27 | 28 | -------------------------------------------------------------------------------- /mtp-consumption/src/test/java/com/vedri/mtp/consumption/ConsumptionTestConfig.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.consumption; 2 | 3 | import com.vedri.mtp.core.CoreProperties; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | @Configuration 10 | @EnableConfigurationProperties({ ConsumptionProperties.class }) 11 | public class ConsumptionTestConfig { 12 | 13 | @Autowired 14 | private ConsumptionProperties consumptionProperties; 15 | 16 | @Bean 17 | public CoreProperties.Akka akkaProperties() { 18 | return consumptionProperties.getAkka(); 19 | } 20 | 21 | @Bean 22 | public CoreProperties.Cassandra cassandraProperties() { 23 | return consumptionProperties.getCassandra(); 24 | } 25 | 26 | @Bean 27 | public CoreProperties.Cluster clusterProperties() { 28 | return consumptionProperties.getCluster(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /mtp-consumption/src/test/java/com/vedri/mtp/consumption/support/cassandra/EmbeddedCassandra.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.consumption.support.cassandra; 2 | 3 | import org.cassandraunit.utils.EmbeddedCassandraServerHelper; 4 | import org.springframework.stereotype.Component; 5 | 6 | import javax.annotation.PostConstruct; 7 | import javax.annotation.PreDestroy; 8 | 9 | @Component 10 | public class EmbeddedCassandra { 11 | 12 | private String cassandraYamlFile = EmbeddedCassandraServerHelper.DEFAULT_CASSANDRA_YML_FILE; 13 | private String tmpDir = "build/embeddedCassandra"; 14 | 15 | @PostConstruct 16 | public void start() throws Exception { 17 | EmbeddedCassandraServerHelper.startEmbeddedCassandra(cassandraYamlFile, tmpDir, 60000); 18 | } 19 | 20 | @PreDestroy 21 | public void stop() { 22 | EmbeddedCassandraServerHelper.cleanEmbeddedCassandra(); 23 | } 24 | 25 | public void setCassandraYamlFile(String cassandraYamlFile) { 26 | this.cassandraYamlFile = cassandraYamlFile; 27 | } 28 | 29 | public void setTmpDir(String tmpDir) { 30 | this.tmpDir = tmpDir; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /mtp-consumption/src/test/java/com/vedri/mtp/consumption/support/cassandra/EmbeddedCassandraConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.consumption.support.cassandra; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.context.annotation.ComponentScan; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | import com.vedri.mtp.core.support.cassandra.CassandraConfiguration; 8 | 9 | @Configuration 10 | @ComponentScan(basePackages = { "com.vedri.mtp.consumption.support.cassandra" }) 11 | public class EmbeddedCassandraConfiguration extends CassandraConfiguration { 12 | 13 | @Autowired 14 | private EmbeddedCassandra embeddedCassandra; 15 | 16 | @Autowired 17 | private EmbeddedCassandraSupport embeddedCassandraSupport; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /mtp-core-kafka/build.gradle: -------------------------------------------------------------------------------- 1 | jar { 2 | manifest { 3 | attributes 'Implementation-Title': 'MTP-Core-Kafka', 'Implementation-Version': rootProject.version, 'Build-Date': buildDate 4 | } 5 | } 6 | 7 | dependencies { 8 | 9 | compile( 10 | ["joda-time:joda-time:${jodaTimeVersion}"], 11 | ["com.esotericsoftware:kryo:${kryoVersion}"], 12 | ["com.esotericsoftware:kryo-shaded:${kryoVersion}"], 13 | ["de.javakaffee:kryo-serializers:0.37"], 14 | ["org.apache.kafka:kafka_${scalaMajorVersion}:${kafkaVersion}"], 15 | ["org.scala-lang:scala-library:${scalaVersion}"], 16 | ["org.springframework.boot:spring-boot-starter:${springBootVersion}"] 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /mtp-core-kafka/src/main/java/com/vedri/mtp/core/support/kyro/KryoConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.support.kyro; 2 | 3 | import org.joda.time.DateTime; 4 | import org.joda.time.LocalDate; 5 | import org.joda.time.LocalDateTime; 6 | import org.springframework.beans.factory.config.ConfigurableBeanFactory; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.context.annotation.Scope; 10 | 11 | import com.esotericsoftware.kryo.Kryo; 12 | 13 | import de.javakaffee.kryoserializers.jodatime.JodaDateTimeSerializer; 14 | import de.javakaffee.kryoserializers.jodatime.JodaLocalDateSerializer; 15 | import de.javakaffee.kryoserializers.jodatime.JodaLocalDateTimeSerializer; 16 | 17 | @Configuration 18 | public class KryoConfiguration { 19 | 20 | @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) 21 | @Bean 22 | public Kryo kryo() { 23 | final Kryo kryo = new Kryo(); 24 | kryo.register(DateTime.class, new JodaDateTimeSerializer()); 25 | kryo.register(LocalDate.class, new JodaLocalDateSerializer()); 26 | kryo.register(LocalDateTime.class, new JodaLocalDateTimeSerializer()); 27 | return kryo; 28 | } 29 | 30 | @Bean 31 | public KryoThreadLocal kryoThreadLocal() { 32 | return new KryoThreadLocal(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /mtp-core-kafka/src/main/java/com/vedri/mtp/core/support/kyro/KryoDecoder.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.support.kyro; 2 | 3 | import java.lang.reflect.ParameterizedType; 4 | import java.lang.reflect.Type; 5 | 6 | import com.esotericsoftware.kryo.Kryo; 7 | import com.esotericsoftware.kryo.io.Input; 8 | import lombok.AccessLevel; 9 | import lombok.Getter; 10 | 11 | /** 12 | * From example: https://github.com/hisunwei/kafka-kryo-codec/blob/master/src/main/java/kafka/kryo/KryoDecoder.java 13 | */ 14 | @Getter(AccessLevel.PACKAGE) 15 | public class KryoDecoder implements kafka.serializer.Decoder { 16 | 17 | private final Kryo kryo; 18 | private final Class clazz; 19 | 20 | @SuppressWarnings("unchecked") 21 | public KryoDecoder(final Kryo kyro) { 22 | this.kryo = kyro; 23 | Type type = getClass().getGenericSuperclass(); 24 | Type[] trueType = ((ParameterizedType) type).getActualTypeArguments(); 25 | this.clazz = (Class) trueType[0]; 26 | kyro.register(clazz); 27 | } 28 | 29 | @Override 30 | @SuppressWarnings("unchecked") 31 | public T fromBytes(byte[] buffer) { 32 | return fromBytes(buffer, kryo, clazz); 33 | } 34 | 35 | public static T fromBytes(byte[] buffer, Kryo kryo, Class clazz) { 36 | try (Input input = new Input(buffer)) { 37 | return kryo.readObject(input, clazz); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /mtp-core-kafka/src/main/java/com/vedri/mtp/core/support/kyro/KryoEncoder.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.support.kyro; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.lang.reflect.ParameterizedType; 5 | import java.lang.reflect.Type; 6 | 7 | import com.esotericsoftware.kryo.Kryo; 8 | import com.esotericsoftware.kryo.io.Output; 9 | 10 | /** 11 | * From example: https://github.com/hisunwei/kafka-kryo-codec/blob/master/src/main/java/kafka/kryo/KryoEncoder.java 12 | */ 13 | public class KryoEncoder implements kafka.serializer.Encoder { 14 | 15 | private final Kryo kyro; 16 | 17 | @SuppressWarnings("unchecked") 18 | public KryoEncoder(final Kryo kyro) { 19 | this.kyro = kyro; 20 | Type type = getClass().getGenericSuperclass(); 21 | Type[] trueType = ((ParameterizedType) type).getActualTypeArguments(); 22 | kyro.register((Class) trueType[0]); 23 | } 24 | 25 | @Override 26 | public byte[] toBytes(T object) { 27 | try (Output out = new Output(new ByteArrayOutputStream())) { 28 | kyro.writeObject(out, object); 29 | return out.getBuffer(); 30 | } 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /mtp-core-kafka/src/main/java/com/vedri/mtp/core/support/kyro/KryoThreadLocal.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.support.kyro; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | 7 | import com.esotericsoftware.kryo.Kryo; 8 | 9 | public class KryoThreadLocal extends ThreadLocal implements ApplicationContextAware { 10 | 11 | private ApplicationContext applicationContext; 12 | 13 | @Override 14 | protected Kryo initialValue() { 15 | return applicationContext.getBean(Kryo.class); 16 | } 17 | 18 | @Override 19 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 20 | this.applicationContext = applicationContext; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /mtp-core-kafka/src/main/java/com/vedri/mtp/core/support/kyro/ThreadLocalKryoDecoder.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.support.kyro; 2 | 3 | public class ThreadLocalKryoDecoder implements kafka.serializer.Decoder { 4 | 5 | private final KryoThreadLocal kryoThreadLocal; 6 | private final KryoDecoder kryoDecoder; 7 | 8 | public ThreadLocalKryoDecoder(KryoThreadLocal kryoThreadLocal, KryoDecoder kryoDecoder) { 9 | this.kryoThreadLocal = kryoThreadLocal; 10 | this.kryoDecoder = kryoDecoder; 11 | } 12 | 13 | @Override 14 | public T fromBytes(byte[] bytes) { 15 | return KryoDecoder.fromBytes(bytes, kryoThreadLocal.get(), kryoDecoder.getClazz()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /mtp-core-kafka/src/main/java/com/vedri/mtp/core/support/serializer/TransactionJacksonDecoder.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.support.serializer; 2 | 3 | import java.io.IOException; 4 | 5 | import com.vedri.mtp.core.transaction.Transaction; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.beans.factory.annotation.Qualifier; 8 | 9 | import com.fasterxml.jackson.databind.ObjectMapper; 10 | import org.springframework.stereotype.Component; 11 | 12 | @Component 13 | public class TransactionJacksonDecoder implements kafka.serializer.Decoder { 14 | 15 | private final ObjectMapper objectMapper; 16 | 17 | @Autowired 18 | public TransactionJacksonDecoder(@Qualifier("objectMapper") ObjectMapper objectMapper) { 19 | this.objectMapper = objectMapper; 20 | } 21 | 22 | @Override 23 | public Transaction fromBytes(byte[] bytes) { 24 | try { 25 | return objectMapper.readValue(bytes, Transaction.class); 26 | } 27 | catch (IOException e) { 28 | throw new IllegalArgumentException(e.getMessage()); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /mtp-core-kafka/src/main/java/com/vedri/mtp/core/support/serializer/TransactionJacksonEncoder.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.support.serializer; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.beans.factory.annotation.Qualifier; 5 | import org.springframework.stereotype.Component; 6 | 7 | import com.fasterxml.jackson.core.JsonProcessingException; 8 | import com.fasterxml.jackson.databind.ObjectMapper; 9 | import com.vedri.mtp.core.transaction.Transaction; 10 | 11 | @Component 12 | public class TransactionJacksonEncoder implements kafka.serializer.Encoder { 13 | 14 | private final ObjectMapper objectMapper; 15 | 16 | @Autowired 17 | public TransactionJacksonEncoder(@Qualifier("objectMapper") ObjectMapper objectMapper) { 18 | this.objectMapper = objectMapper; 19 | } 20 | 21 | @Override 22 | public byte[] toBytes(Transaction transaction) { 23 | try { 24 | return objectMapper.writeValueAsBytes(transaction); 25 | } 26 | catch (JsonProcessingException e) { 27 | throw new IllegalArgumentException(e.getMessage()); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /mtp-core-kafka/src/main/java/com/vedri/mtp/core/support/serializer/TransactionKryoDecoder.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.support.serializer; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Component; 5 | 6 | import com.esotericsoftware.kryo.Kryo; 7 | import com.vedri.mtp.core.support.kyro.KryoDecoder; 8 | import com.vedri.mtp.core.support.kyro.KryoThreadLocal; 9 | import com.vedri.mtp.core.support.kyro.ThreadLocalKryoDecoder; 10 | import com.vedri.mtp.core.transaction.Transaction; 11 | 12 | @Component 13 | public class TransactionKryoDecoder extends ThreadLocalKryoDecoder { 14 | 15 | @Component 16 | static class InternalTransactionKryoDecoder extends KryoDecoder { 17 | 18 | @Autowired 19 | public InternalTransactionKryoDecoder(Kryo kryo) { 20 | super(kryo); 21 | } 22 | }; 23 | 24 | @Autowired 25 | public TransactionKryoDecoder(KryoThreadLocal kryoThreadLocal, 26 | InternalTransactionKryoDecoder internalTransactionKryoDecoder) { 27 | super(kryoThreadLocal, internalTransactionKryoDecoder); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /mtp-core-kafka/src/main/java/com/vedri/mtp/core/support/serializer/TransactionKryoEncoder.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.support.serializer; 2 | 3 | import com.vedri.mtp.core.support.kyro.KryoEncoder; 4 | import com.vedri.mtp.core.transaction.Transaction; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Component; 7 | 8 | import com.esotericsoftware.kryo.Kryo; 9 | 10 | @Component 11 | public class TransactionKryoEncoder extends KryoEncoder { 12 | 13 | @Autowired 14 | public TransactionKryoEncoder(Kryo kyro) { 15 | super(kyro); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /mtp-core-spark/build.gradle: -------------------------------------------------------------------------------- 1 | jar { 2 | manifest { 3 | attributes 'Implementation-Title': 'MTP-Core-Spark', 'Implementation-Version': rootProject.version, 'Build-Date': buildDate 4 | } 5 | } 6 | 7 | dependencies { 8 | compile( 9 | ["com.esotericsoftware:kryo:${kryoVersion}"], 10 | ["com.esotericsoftware:kryo-shaded:${kryoVersion}"], 11 | ["de.javakaffee:kryo-serializers:0.37"], 12 | ["joda-time:joda-time:${jodaTimeVersion}"], 13 | ["org.projectlombok:lombok:${lombokVersion}"], 14 | ["org.springframework.boot:spring-boot-starter:${springBootVersion}"], 15 | ["com.fasterxml.jackson.module:jackson-module-scala_${scalaMajorVersion}:${jacksonScalaVersion}"] 16 | ) 17 | compile("org.apache.spark:spark-core_${scalaMajorVersion}:${sparkVersion}") { 18 | exclude group: 'com.esotericsoftware.kryo' 19 | } 20 | compile("org.apache.spark:spark-catalyst_${scalaMajorVersion}:${sparkVersion}") { 21 | exclude group: 'com.esotericsoftware.kryo' 22 | } 23 | compile("org.apache.spark:spark-hive_${scalaMajorVersion}:${sparkVersion}") { 24 | exclude group: 'com.esotericsoftware.kryo' 25 | } 26 | compile("org.apache.spark:spark-sql_${scalaMajorVersion}:${sparkVersion}") { 27 | exclude group: 'com.esotericsoftware.kryo' 28 | } 29 | compile("org.apache.spark:spark-streaming_${scalaMajorVersion}:${sparkVersion}") { 30 | exclude group: 'com.esotericsoftware.kryo' 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /mtp-core-spark/src/main/java/com/vedri/mtp/processor/support/spark/CoreSparkProperties.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.processor.support.spark; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | 7 | @ConfigurationProperties(prefix = "mtp.core.spark", ignoreUnknownFields = false) 8 | @Getter 9 | public class CoreSparkProperties { 10 | 11 | private Spark spark; 12 | 13 | @Getter 14 | @Setter 15 | public static class Spark { 16 | private String master; 17 | private String cassandraHosts; 18 | private String cleanerTtl; 19 | private long batchInterval; 20 | private String checkpointDir; 21 | private Kryoserializer kryoserializer = new Kryoserializer(); 22 | 23 | @Getter 24 | @Setter 25 | public static class Kryoserializer { 26 | private int buffer = 24; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /mtp-core-spark/src/main/java/com/vedri/mtp/processor/support/spark/SparkKryoRegistrator.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.processor.support.spark; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import de.javakaffee.kryoserializers.jodatime.JodaDateTimeSerializer; 5 | import de.javakaffee.kryoserializers.jodatime.JodaLocalDateSerializer; 6 | import de.javakaffee.kryoserializers.jodatime.JodaLocalDateTimeSerializer; 7 | import org.apache.spark.serializer.KryoRegistrator; 8 | import org.joda.time.DateTime; 9 | import org.joda.time.LocalDate; 10 | import org.joda.time.LocalDateTime; 11 | 12 | public class SparkKryoRegistrator implements KryoRegistrator { 13 | @Override 14 | public void registerClasses(Kryo kryo) { 15 | kryo.register( DateTime.class, new JodaDateTimeSerializer() ); 16 | kryo.register( LocalDate.class, new JodaLocalDateSerializer() ); 17 | kryo.register( LocalDateTime.class, new JodaLocalDateTimeSerializer() ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /mtp-core/src/main/java/com/vedri/mtp/core/CoreConfig.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core; 2 | 3 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.ComponentScan; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; 8 | 9 | @ComponentScan(basePackages = "com.vedri.mtp.core.cluster") 10 | @Configuration 11 | @EnableConfigurationProperties({ CoreProperties.class }) 12 | public class CoreConfig { 13 | 14 | @Bean 15 | public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() { 16 | return new PropertySourcesPlaceholderConfigurer(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /mtp-core/src/main/java/com/vedri/mtp/core/CoreProperties.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | 8 | @ConfigurationProperties(prefix = "mtp.core", ignoreUnknownFields = false) 9 | @Getter 10 | public class CoreProperties { 11 | 12 | private final Cluster cluster = new Cluster(); 13 | private final Akka akka = new Akka(); 14 | private final Cassandra cassandra = new Cassandra(); 15 | private final CfRate cfRate = new CfRate(); 16 | 17 | @Getter 18 | @Setter 19 | public static class Cluster { 20 | private String appName; 21 | private String nodeName; 22 | } 23 | 24 | @Getter 25 | @Setter 26 | public static class Akka { 27 | private String akkaSystemName; 28 | private boolean logConfiguration; 29 | private boolean logClusterMetrics; 30 | } 31 | 32 | @Getter 33 | @Setter 34 | public static class Cassandra { 35 | private String[] hosts; 36 | private int port; 37 | private String keyspace; 38 | } 39 | 40 | @Getter 41 | @Setter 42 | public static class CfRate { 43 | private String serviceUrl; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /mtp-core/src/main/java/com/vedri/mtp/core/rate/cf/CfRate.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.rate.cf; 2 | 3 | import java.math.BigDecimal; 4 | 5 | import com.fasterxml.jackson.annotation.JsonRootName; 6 | import lombok.*; 7 | 8 | import org.joda.time.LocalDate; 9 | 10 | import com.vedri.mtp.core.rate.Rate; 11 | 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | @Setter 15 | @Getter 16 | @ToString 17 | public class CfRate { 18 | 19 | private String currencyFrom; 20 | private String currencyTo; 21 | private String cfTransferFee; 22 | private String bankTransferFee; 23 | private String youExchangeCf; 24 | private String youExchangeBank; 25 | private String youSave; 26 | private String cfRate; 27 | private String bankRate; 28 | private String cfBuyAmount; 29 | private String bankBuyAmount; 30 | 31 | public Rate toRate() { 32 | final Rate rate = new Rate(); 33 | rate.setKey(new Rate.Key(currencyFrom, currencyTo, LocalDate.now())); 34 | rate.setCfRate(new BigDecimal(cfRate)); 35 | rate.setBankRate(new BigDecimal(bankRate)); 36 | 37 | return rate; 38 | } 39 | 40 | public static CfRate fromRate(Rate rate) { 41 | final CfRate cfRate = new CfRate(); 42 | cfRate.setCurrencyFrom(rate.getKey().getCurrencyFrom()); 43 | cfRate.setCurrencyTo(rate.getKey().getCurrencyTo()); 44 | cfRate.setCfRate(rate.getCfRate().toString()); 45 | cfRate.setBankRate(rate.getBankRate().toString()); 46 | return cfRate; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /mtp-core/src/main/java/com/vedri/mtp/core/support/akka/AkkaMessage.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.support.akka; 2 | 3 | import java.io.Serializable; 4 | 5 | public interface AkkaMessage extends Serializable { 6 | } 7 | -------------------------------------------------------------------------------- /mtp-core/src/main/java/com/vedri/mtp/core/support/akka/AkkaTask.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.support.akka; 2 | 3 | public interface AkkaTask extends AkkaMessage { 4 | 5 | class QueryTask implements AkkaTask { 6 | 7 | } 8 | 9 | class GracefulShutdown implements AkkaTask { 10 | 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /mtp-core/src/main/java/com/vedri/mtp/core/support/akka/LifecycleMessage.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.support.akka; 2 | 3 | public interface LifecycleMessage extends AkkaMessage { 4 | 5 | class OutputStreamInitialized implements LifecycleMessage { 6 | } 7 | 8 | class NodeInitialized implements LifecycleMessage { 9 | } 10 | 11 | class Start implements LifecycleMessage { 12 | } 13 | 14 | class DataFeedStarted implements LifecycleMessage { 15 | } 16 | 17 | class Shutdown implements LifecycleMessage { 18 | } 19 | 20 | class TaskCompleted implements LifecycleMessage { 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /mtp-core/src/main/java/com/vedri/mtp/core/support/akka/SpringActorProducer.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.support.akka; 2 | 3 | import org.springframework.context.ApplicationContext; 4 | 5 | import akka.actor.Actor; 6 | import akka.actor.IndirectActorProducer; 7 | 8 | /** 9 | * An actor producer that lets Spring create the Actor instances. 10 | */ 11 | public class SpringActorProducer implements IndirectActorProducer { 12 | 13 | final ApplicationContext applicationContext; 14 | 15 | final String actorBeanName; 16 | 17 | public SpringActorProducer(ApplicationContext applicationContext, String actorBeanName) { 18 | this.applicationContext = applicationContext; 19 | this.actorBeanName = actorBeanName; 20 | } 21 | 22 | @Override 23 | public Actor produce() { 24 | return (Actor) applicationContext.getBean(actorBeanName); 25 | } 26 | 27 | @Override 28 | public Class actorClass() { 29 | return (Class) applicationContext.getType(actorBeanName); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /mtp-core/src/main/java/com/vedri/mtp/core/support/json/JacksonConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.support.json; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | import com.fasterxml.jackson.databind.MapperFeature; 7 | import com.fasterxml.jackson.databind.ObjectMapper; 8 | import com.fasterxml.jackson.datatype.joda.JodaModule; 9 | 10 | @Configuration 11 | public class JacksonConfiguration { 12 | 13 | @Bean 14 | public JodaModule jodaModule() { 15 | final JodaModule jodaModule = new JodaModule(); 16 | return jodaModule; 17 | } 18 | 19 | @Bean 20 | public ObjectMapper objectMapper() { 21 | final ObjectMapper objectMapper = new ObjectMapper().enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY); 22 | objectMapper.registerModule(jodaModule()); 23 | return objectMapper; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /mtp-core/src/main/java/com/vedri/mtp/core/support/json/TransactionJacksonConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.support.json; 2 | 3 | import org.joda.time.DateTime; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | import com.fasterxml.jackson.databind.MapperFeature; 8 | import com.fasterxml.jackson.databind.ObjectMapper; 9 | import com.fasterxml.jackson.datatype.joda.JodaModule; 10 | import com.vedri.mtp.core.MtpConstants; 11 | 12 | @Configuration 13 | public class TransactionJacksonConfiguration { 14 | 15 | @Bean 16 | public JodaModule transactionJodaModule() { 17 | final JodaModule jodaModule = new JodaModule(); 18 | jodaModule.addDeserializer(DateTime.class, 19 | new CustomDateDateTimeDeserializer(MtpConstants.TRANSACTION_DATE_FORMAT)); 20 | return jodaModule; 21 | } 22 | 23 | @Bean 24 | public ObjectMapper transactionObjectMapper() { 25 | final ObjectMapper objectMapper = new ObjectMapper().enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY); 26 | objectMapper.registerModule(transactionJodaModule()); 27 | return objectMapper; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /mtp-core/src/main/java/com/vedri/mtp/core/support/scala/ScalaConverts.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.support.scala; 2 | 3 | //import java.util.Map; 4 | // 5 | //import scala.Predef$; 6 | //import scala.Tuple2; 7 | //import scala.collection.JavaConverters$; 8 | import scala.reflect.ClassTag; 9 | 10 | public class ScalaConverts { 11 | 12 | // public static scala.collection.immutable.Map mapToImmutableMap(Map map) { 13 | // return JavaConverters$.MODULE$.mapAsScalaMapConverter(map).asScala().toMap( 14 | // Predef$.MODULE$.> conforms()); 15 | // } 16 | 17 | public static ClassTag toClassTag(Class clazz) { 18 | return scala.reflect.ClassTag$.MODULE$.apply(clazz); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /mtp-core/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | 2 | akka { 3 | loglevel = "DEBUG" 4 | loggers = ["akka.event.slf4j.Slf4jLogger"] 5 | logging-filter = "akka.event.slf4j.Slf4jLoggingFilter" 6 | logger-startup-timeout = 60s 7 | log-dead-letters = off 8 | log-dead-letters-during-shutdown = off 9 | 10 | remote { 11 | log-remote-lifecycle-events = off 12 | netty.tcp { 13 | port = 2550 14 | hostname = "127.0.0.1" 15 | } 16 | } 17 | 18 | actor { 19 | provider = "akka.cluster.ClusterActorRefProvider" 20 | 21 | default-dispatcher { 22 | # Throughput for default Dispatcher, set to 1 for as fair as possible 23 | throughput = 10 24 | } 25 | } 26 | 27 | cluster { 28 | log-info = on 29 | seed-nodes = [] 30 | roles = ["analytics"] 31 | gossip-interval = 5s 32 | publish-stats-interval = 10s 33 | auto-down-unreachable-after = 10s 34 | #metrics.enabled=off TODO new metrics ext 35 | metrics.gossip-interval = 10s 36 | metrics.collect-interval = 10s 37 | } 38 | } 39 | 40 | nodeCore0 { 41 | akka { 42 | remote { 43 | netty.tcp { 44 | port = 9160 45 | } 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /mtp-core/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | 2 | mtp: 3 | core: 4 | cluster: 5 | nodeName: nodeCore0 -------------------------------------------------------------------------------- /mtp-frontend/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "src/main/webapp/bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /mtp-frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mtp", 3 | "version": "0.5.0", 4 | "description": "Description for mtp", 5 | "private": true, 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "browser-sync": "2.9.6", 9 | "event-stream": "3.3.1", 10 | "generator-jhipster": "2.23.0", 11 | "grunt": "0.4.5", 12 | "grunt-angular-templates": "0.5.7", 13 | "grunt-autoprefixer": "2.2.0", 14 | "grunt-browser-sync": "2.1.3", 15 | "grunt-build-control": "0.3.0", 16 | "grunt-contrib-clean": "0.6.0", 17 | "grunt-contrib-concat": "0.5.1", 18 | "grunt-contrib-copy": "0.8.0", 19 | "grunt-contrib-cssmin": "0.11.0", 20 | "grunt-contrib-htmlmin": "0.4.0", 21 | "grunt-contrib-imagemin": "^1.0.0", 22 | "grunt-contrib-jshint": "0.11.0", 23 | "grunt-contrib-uglify": "0.8.0", 24 | "grunt-contrib-watch": "0.6.1", 25 | "grunt-karma": "0.11.0", 26 | "grunt-modernizr": "0.6.0", 27 | "grunt-ng-annotate": "0.10.0", 28 | "grunt-ng-constant": "1.1.0", 29 | "grunt-rev": "0.1.0", 30 | "grunt-svgmin": "2.0.1", 31 | "grunt-text-replace": "0.4.0", 32 | "grunt-usemin": "3.0.0", 33 | "grunt-wiredep": "2.0.0", 34 | "jasmine-core": "2.1.0", 35 | "jshint-stylish": "1.0.1", 36 | "load-grunt-tasks": "3.1.0", 37 | "lodash": "3.3.1", 38 | "requirejs": "2.1", 39 | "time-grunt": "1.1.0", 40 | "wiredep": "2.2.2", 41 | "yo": "1.3.0", 42 | "zeparser": "0.0.7" 43 | }, 44 | "engines": { 45 | "node": "0.12.0" 46 | }, 47 | "optionalDependencies": { 48 | "fsevents": "*" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/conf/spring-application.yml.template: -------------------------------------------------------------------------------- 1 | mtp: 2 | frontend: 3 | cassandra: 4 | hosts: localhost 5 | spark: 6 | master: local[10] 7 | cassandraHosts: localhost 8 | checkpointDir: /tmp/spark-frontend 9 | security: 10 | authentication: 11 | oauth: 12 | secret: mySecretOAuthSecret 13 | tokenValidityInSeconds: 1800 14 | rememberme: 15 | # security key (this key should be unique for your application, and kept secret) 16 | key: 5b802eb2cabc806b60315220f1aac2948a834541 17 | server: 18 | address: localhost 19 | port: 8080 20 | use-forward-headers: true 21 | proxyName: localhost 22 | proxyPort: 80 23 | #useIPVHosts=true 24 | #tomcat: 25 | # internal-proxies=192\\.168\\.\\d{1,3}\\.\\d{1,3} 26 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/FrontendApplication.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend; 2 | 3 | import java.net.InetAddress; 4 | 5 | import lombok.extern.slf4j.Slf4j; 6 | 7 | import org.springframework.context.ApplicationContext; 8 | import org.springframework.core.env.Environment; 9 | 10 | import com.vedri.mtp.core.CoreConfig; 11 | import com.vedri.mtp.core.support.cassandra.CassandraConfiguration; 12 | import com.vedri.mtp.core.support.spring.AbstractApplication; 13 | import com.vedri.mtp.processor.support.spark.SparkConfiguration; 14 | 15 | @Slf4j 16 | public class FrontendApplication extends AbstractApplication { 17 | 18 | @Override 19 | protected Class[] getConfigs() { 20 | return new Class[] { 21 | CoreConfig.class, FrontendConfig.class, CassandraConfiguration.class, SparkConfiguration.class 22 | }; 23 | } 24 | 25 | @Override 26 | protected void doStart(ApplicationContext context) throws Exception { 27 | log.info("Starting web application"); 28 | final Environment env = context.getEnvironment(); 29 | 30 | log.info("Access URLs:\n" + 31 | "----------------------------------------------------------\n" + 32 | "\tLocal: \t\thttp://127.0.0.1:{}\n" + 33 | "\tExternal: \thttp://{}:{}\n" + 34 | "----------------------------------------------------------", 35 | env.getProperty("server.port"), 36 | InetAddress.getLocalHost().getHostAddress(), 37 | env.getProperty("server.port")); 38 | } 39 | 40 | public static void main(String[] args) throws Exception { 41 | new FrontendApplication().startApplication(args); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/MtpFrontendConstants.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend; 2 | 3 | public interface MtpFrontendConstants { 4 | 5 | String NAME = "MtpFrontend"; 6 | String CONFIG_PREFIX = "mtp.frontend"; 7 | 8 | String TOPIC_DESTINATION_PREFIX = "/topic"; 9 | String WEBSOCKET_PREFIX = "/websocket"; 10 | 11 | static String wrapWebsocketPath(String name) { 12 | return WEBSOCKET_PREFIX + "/" + name; 13 | } 14 | 15 | static String wrapTopicDestinationPath(String name) { 16 | return TOPIC_DESTINATION_PREFIX + "/" + name; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/config/CacheConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.config; 2 | 3 | import javax.annotation.PreDestroy; 4 | 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.boot.autoconfigure.AutoConfigureAfter; 9 | import org.springframework.cache.CacheManager; 10 | import org.springframework.cache.annotation.EnableCaching; 11 | import org.springframework.cache.support.NoOpCacheManager; 12 | import org.springframework.context.annotation.Bean; 13 | import org.springframework.context.annotation.Configuration; 14 | import org.springframework.context.annotation.Profile; 15 | 16 | @Slf4j 17 | @Configuration 18 | @EnableCaching 19 | @AutoConfigureAfter(value = { MetricsConfiguration.class }) 20 | public class CacheConfiguration { 21 | 22 | private CacheManager cacheManager; 23 | 24 | @PreDestroy 25 | public void destroy() { 26 | log.info("Closing Cache Manager"); 27 | } 28 | 29 | @Bean 30 | public CacheManager cacheManager() { 31 | log.debug("No cache"); 32 | cacheManager = new NoOpCacheManager(); 33 | return cacheManager; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/config/FrontendAkkaConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | 5 | import akka.actor.ActorRef; 6 | import akka.actor.ActorSystem; 7 | 8 | import com.vedri.mtp.core.support.akka.ClusterAkkaConfiguration; 9 | import com.vedri.mtp.core.support.akka.SpringExtension; 10 | 11 | @Configuration 12 | public class FrontendAkkaConfiguration extends ClusterAkkaConfiguration { 13 | 14 | @Override 15 | protected void doCreateClusteredRootActors(ActorSystem system, SpringExtension.SpringExt springExt) { 16 | super.doCreateClusteredRootActors(system, springExt); 17 | 18 | ActorRef clusterRootActor = system.actorOf(springExt.props(FrontendRootActor.NAME), 19 | FrontendRootActor.NAME); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/config/JacksonConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.config; 2 | 3 | import java.time.*; 4 | 5 | import com.fasterxml.jackson.datatype.joda.JodaModule; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; 9 | 10 | import com.vedri.mtp.frontend.support.datetime.JSR310DateTimeSerializer; 11 | import com.vedri.mtp.frontend.support.datetime.JSR310LocalDateDeserializer; 12 | 13 | @Configuration 14 | public class JacksonConfiguration { 15 | 16 | @Bean 17 | Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() { 18 | JodaModule module = new JodaModule(); 19 | module.addSerializer(OffsetDateTime.class, JSR310DateTimeSerializer.INSTANCE); 20 | module.addSerializer(ZonedDateTime.class, JSR310DateTimeSerializer.INSTANCE); 21 | module.addSerializer(LocalDateTime.class, JSR310DateTimeSerializer.INSTANCE); 22 | module.addSerializer(Instant.class, JSR310DateTimeSerializer.INSTANCE); 23 | module.addDeserializer(LocalDate.class, JSR310LocalDateDeserializer.INSTANCE); 24 | return new Jackson2ObjectMapperBuilder() 25 | .findModulesViaServiceLoader(true) 26 | .modulesToInstall(module); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/config/LoggingAspectConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.context.annotation.EnableAspectJAutoProxy; 6 | import org.springframework.context.annotation.Profile; 7 | import org.springframework.core.env.Environment; 8 | 9 | import com.vedri.mtp.core.MtpConstants; 10 | import com.vedri.mtp.frontend.support.aop.logging.LoggingAspect; 11 | 12 | @Configuration 13 | @EnableAspectJAutoProxy 14 | public class LoggingAspectConfiguration { 15 | 16 | @Bean 17 | @Profile(MtpConstants.SPRING_PROFILE_DEVELOPMENT) 18 | public LoggingAspect loggingAspect(Environment env) { 19 | return new LoggingAspect(env); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/config/WebsocketBrokerConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.config; 2 | 3 | import javax.annotation.PostConstruct; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.messaging.simp.broker.AbstractBrokerMessageHandler; 9 | import org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler; 10 | 11 | import com.vedri.mtp.frontend.support.stomp.CallbackSubscriptionRegistry; 12 | 13 | @Configuration 14 | class WebsocketBrokerConfiguration { 15 | 16 | @Autowired 17 | private AbstractBrokerMessageHandler simpleBrokerMessageHandler; 18 | 19 | @Bean 20 | public CallbackSubscriptionRegistry callbackSubscriptionRegistry() { 21 | final CallbackSubscriptionRegistry callbackSubscriptionRegistry = new CallbackSubscriptionRegistry(); 22 | return callbackSubscriptionRegistry; 23 | } 24 | 25 | @PostConstruct 26 | public void init() { 27 | if (simpleBrokerMessageHandler instanceof SimpleBrokerMessageHandler) { 28 | ((SimpleBrokerMessageHandler) simpleBrokerMessageHandler) 29 | .setSubscriptionRegistry(callbackSubscriptionRegistry()); 30 | } 31 | else { 32 | throw new IllegalStateException("Only SimpleBrokerMessageHandler is supported"); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/support/datetime/JSR310DateTimeSerializer.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.support.datetime; 2 | 3 | import java.io.IOException; 4 | import java.time.ZoneId; 5 | import java.time.format.DateTimeFormatter; 6 | import java.time.temporal.TemporalAccessor; 7 | 8 | import com.fasterxml.jackson.core.JsonGenerator; 9 | import com.fasterxml.jackson.databind.JsonSerializer; 10 | import com.fasterxml.jackson.databind.SerializerProvider; 11 | 12 | public final class JSR310DateTimeSerializer extends JsonSerializer { 13 | 14 | private static final DateTimeFormatter ISOFormatter = 15 | DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(ZoneId.of("Z")); 16 | 17 | public static final JSR310DateTimeSerializer INSTANCE = new JSR310DateTimeSerializer(); 18 | 19 | private JSR310DateTimeSerializer() {} 20 | 21 | @Override 22 | public void serialize(TemporalAccessor value, JsonGenerator generator, SerializerProvider serializerProvider) throws IOException { 23 | generator.writeString(ISOFormatter.format(value)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/support/metrics/MtpHealthIndicatorConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.support.metrics; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.actuate.health.HealthIndicator; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | import com.datastax.driver.core.Session; 9 | 10 | @Configuration 11 | public class MtpHealthIndicatorConfiguration { 12 | 13 | @Autowired 14 | private Session session; 15 | 16 | @Bean 17 | public HealthIndicator cassandraHealthIndicator() { 18 | return new CassandraHealthIndicator(session); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/support/random/RandomUtil.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.support.random; 2 | 3 | import org.apache.commons.lang3.RandomStringUtils; 4 | 5 | /** 6 | * Utility class for generating random Strings. 7 | */ 8 | public final class RandomUtil { 9 | 10 | private static final int DEF_COUNT = 20; 11 | 12 | private RandomUtil() { 13 | } 14 | 15 | /** 16 | * Generates a password. 17 | * 18 | * @return the generated password 19 | */ 20 | public static String generatePassword() { 21 | return RandomStringUtils.randomAlphanumeric(DEF_COUNT); 22 | } 23 | 24 | /** 25 | * Generates an activation key. 26 | * 27 | * @return the generated activation key 28 | */ 29 | public static String generateActivationKey() { 30 | return RandomStringUtils.randomNumeric(DEF_COUNT); 31 | } 32 | 33 | /** 34 | * Generates a reset key. 35 | * 36 | * @return the generated reset key 37 | */ 38 | public static String generateResetKey() { 39 | return RandomStringUtils.randomNumeric(DEF_COUNT); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/support/stomp/DeleteDestinationEvent.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.support.stomp; 2 | 3 | import lombok.ToString; 4 | 5 | @ToString(callSuper = true) 6 | public class DeleteDestinationEvent extends StompDestinationEvent { 7 | 8 | public DeleteDestinationEvent(String destination) { 9 | super(destination, null); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/support/stomp/DestinationListener.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.support.stomp; 2 | 3 | public interface DestinationListener { 4 | 5 | void onEvent(NewDestinationEvent newDestinationEvent); 6 | 7 | void onEvent(DeleteDestinationEvent deleteDestinationEvent); 8 | } 9 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/support/stomp/NewDestinationEvent.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.support.stomp; 2 | 3 | 4 | import lombok.ToString; 5 | 6 | @ToString(callSuper = true) 7 | public class NewDestinationEvent extends StompDestinationEvent { 8 | 9 | public NewDestinationEvent(final String destination, final String user) { 10 | super(destination, user); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/support/stomp/StompDestinationEvent.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.support.stomp; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.ToString; 6 | 7 | @AllArgsConstructor 8 | @Getter 9 | @ToString 10 | public class StompDestinationEvent { 11 | 12 | private final String destination; 13 | private final String user; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/support/web/HeaderUtil.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.support.web; 2 | 3 | import org.springframework.http.HttpHeaders; 4 | 5 | /** 6 | * Utility class for http header creation. 7 | * 8 | */ 9 | public class HeaderUtil { 10 | 11 | public static HttpHeaders createAlert(String message, String param) { 12 | HttpHeaders headers = new HttpHeaders(); 13 | headers.add("X-mtp-alert", message); 14 | headers.add("X-mtp-params", param); 15 | return headers; 16 | } 17 | 18 | public static HttpHeaders createEntityCreationAlert(String entityName, String param) { 19 | return createAlert("mtpApp." + entityName + ".created", param); 20 | } 21 | 22 | public static HttpHeaders createEntityUpdateAlert(String entityName, String param) { 23 | return createAlert("mtpApp." + entityName + ".updated", param); 24 | } 25 | 26 | public static HttpHeaders createEntityDeletionAlert(String entityName, String param) { 27 | return createAlert("mtpApp." + entityName + ".deleted", param); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/transaction/aggregation/subscription/AggregationByStatusActor.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.transaction.aggregation.subscription; 2 | 3 | import akka.actor.ActorRef; 4 | import org.joda.time.DateTime; 5 | 6 | import com.vedri.mtp.core.transaction.aggregation.TransactionAggregationByStatus; 7 | import com.vedri.mtp.core.transaction.aggregation.TransactionValidationStatus; 8 | import com.vedri.mtp.core.transaction.aggregation.YearToHourTime; 9 | import com.vedri.mtp.frontend.transaction.aggregation.dao.SparkAggregationByStatusDao; 10 | import com.vedri.mtp.frontend.web.websocket.transaction.WebsocketSender; 11 | 12 | public abstract class AggregationByStatusActor extends AbstractPeriodicTopicActor { 13 | 14 | private final SparkAggregationByStatusDao sparkAggregationByStatusDao; 15 | 16 | public AggregationByStatusActor(WebsocketSender websocketSender, 17 | SparkAggregationByStatusDao sparkAggregationByStatusDao) { 18 | super(TransactionAggregationByStatus.class, websocketSender); 19 | this.sparkAggregationByStatusDao = sparkAggregationByStatusDao; 20 | } 21 | 22 | protected void loadByStatus(ActorRef requester, TransactionValidationStatus status) { 23 | sparkAggregationByStatusDao.load(status, new YearToHourTime(new DateTime()), requester); 24 | } 25 | 26 | protected void loadByAllStatuses(ActorRef requester) { 27 | sparkAggregationByStatusDao.loadAll(new YearToHourTime(new DateTime()), requester); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/transaction/aggregation/subscription/PeriodicTick.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.transaction.aggregation.subscription; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Getter 8 | @AllArgsConstructor 9 | @NoArgsConstructor 10 | public class PeriodicTick { 11 | 12 | private boolean returnToSender = false; 13 | } 14 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/transaction/aggregation/subscription/PtByUserActor.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.transaction.aggregation.subscription; 2 | 3 | import com.vedri.mtp.frontend.MtpFrontendConstants; 4 | import com.vedri.mtp.frontend.transaction.aggregation.dao.SparkAggregationByUserDao; 5 | import com.vedri.mtp.frontend.web.websocket.transaction.WebsocketSender; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.beans.factory.config.ConfigurableBeanFactory; 8 | import org.springframework.context.annotation.Scope; 9 | import org.springframework.stereotype.Service; 10 | 11 | @Service 12 | @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) 13 | public class PtByUserActor extends AggregationByUserActor { 14 | 15 | public static final String NAME = "ptByUserActor"; 16 | 17 | @Autowired 18 | public PtByUserActor(WebsocketSender websocketSender, SparkAggregationByUserDao sparkAggregationByUserDao) { 19 | super(websocketSender, sparkAggregationByUserDao); 20 | } 21 | 22 | @Override 23 | protected String getName() { 24 | return MtpFrontendConstants.wrapTopicDestinationPath(NAME + "/" + subscriptionInfo.getFilter()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/transaction/aggregation/subscription/RtByUserActor.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.transaction.aggregation.subscription; 2 | 3 | import com.vedri.mtp.frontend.MtpFrontendConstants; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.beans.factory.config.ConfigurableBeanFactory; 6 | import org.springframework.context.annotation.Scope; 7 | import org.springframework.stereotype.Service; 8 | 9 | import com.vedri.mtp.frontend.transaction.aggregation.dao.SparkAggregationByUserDao; 10 | import com.vedri.mtp.frontend.web.websocket.transaction.WebsocketSender; 11 | 12 | @Service 13 | @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) 14 | public class RtByUserActor extends AggregationByUserActor { 15 | 16 | public static final String NAME = "rtByUserActor"; 17 | 18 | @Autowired 19 | public RtByUserActor(WebsocketSender websocketSender, SparkAggregationByUserDao sparkAggregationByUserDao) { 20 | super(websocketSender, sparkAggregationByUserDao); 21 | } 22 | 23 | @Override 24 | protected String getName() { 25 | return MtpFrontendConstants.wrapTopicDestinationPath(NAME + "/" + subscriptionInfo.getFilter()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/transaction/aggregation/subscription/TopicActorInfo.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.transaction.aggregation.subscription; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.ToString; 6 | import akka.actor.AbstractActor; 7 | 8 | @AllArgsConstructor 9 | @Getter 10 | @ToString 11 | public class TopicActorInfo { 12 | 13 | private final String topicDestinationPath; 14 | private final String name; 15 | private final String allName; 16 | 17 | public boolean supportsDestination(String destination) { 18 | return destination.startsWith(topicDestinationPath); 19 | } 20 | 21 | public String destinationSuffix(String destination) { 22 | return destination.replace(topicDestinationPath + "/", ""); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/transaction/aggregation/subscription/TopicActorSubscriptionInfo.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.transaction.aggregation.subscription; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | @AllArgsConstructor 7 | @Getter 8 | public class TopicActorSubscriptionInfo { 9 | private F filter; 10 | } 11 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/user/UserManager.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.user; 2 | 3 | import java.util.Optional; 4 | 5 | import com.vedri.mtp.core.user.User; 6 | 7 | public interface UserManager { 8 | 9 | Optional activateRegistration(String key); 10 | 11 | Optional completePasswordReset(String newPassword, String key); 12 | 13 | Optional requestPasswordReset(String mail); 14 | 15 | User createUserInformation(String login, String password, String firstName, String lastName, String email, 16 | String langKey); 17 | 18 | void updateUserInformation(String firstName, String lastName, String email, String langKey); 19 | 20 | void changePassword(String password); 21 | 22 | Optional getUserWithAuthoritiesByLogin(String login); 23 | 24 | User getUserWithAuthorities(); 25 | } 26 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/user/dao/UserDao.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.user.dao; 2 | 3 | import java.util.List; 4 | import java.util.Optional; 5 | 6 | import com.vedri.mtp.core.user.User; 7 | 8 | public interface UserDao { 9 | 10 | Optional findOne(String id); 11 | 12 | Optional findOneByActivationKey(String activationKeyVal); 13 | 14 | Optional findOneByResetKey(String resetKeyVal); 15 | 16 | Optional findOneByEmail(String emailVal); 17 | 18 | Optional findOneByLogin(String loginVal); 19 | 20 | List findAll(); 21 | 22 | User save(User user); 23 | 24 | void delete(User user); 25 | } 26 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/web/ApplicationWebXml.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.web; 2 | 3 | import com.vedri.mtp.frontend.FrontendConfig; 4 | import org.springframework.boot.Banner; 5 | import org.springframework.boot.builder.SpringApplicationBuilder; 6 | import org.springframework.boot.context.web.SpringBootServletInitializer; 7 | 8 | /** 9 | * This is a helper Java class that provides an alternative to creating a web.xml. 10 | */ 11 | public class ApplicationWebXml extends SpringBootServletInitializer { 12 | 13 | @Override 14 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { 15 | return application.bannerMode(Banner.Mode.OFF).sources(FrontendConfig.class); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/web/filter/StaticResourcesProductionFilter.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.web.filter; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.*; 6 | import javax.servlet.http.HttpServletRequest; 7 | 8 | import org.apache.commons.lang3.StringUtils; 9 | 10 | /** 11 | * This filter is used in production, to serve static resources generated by "grunt build". 12 | *

13 | *

14 | * It is configured to serve resources from the "dist" directory, which is the Grunt 15 | * destination directory. 16 | *

17 | */ 18 | public class StaticResourcesProductionFilter implements Filter { 19 | 20 | @Override 21 | public void init(FilterConfig filterConfig) throws ServletException { 22 | // Nothing to initialize 23 | } 24 | 25 | @Override 26 | public void destroy() { 27 | // Nothing to destroy 28 | } 29 | 30 | @Override 31 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 32 | throws IOException, ServletException { 33 | HttpServletRequest httpRequest = (HttpServletRequest) request; 34 | String contextPath = ((HttpServletRequest) request).getContextPath(); 35 | String requestURI = httpRequest.getRequestURI(); 36 | requestURI = StringUtils.substringAfter(requestURI, contextPath); 37 | if (StringUtils.equals("/", requestURI)) { 38 | requestURI = "/index.html"; 39 | } 40 | String newURI = "/dist" + requestURI; 41 | request.getRequestDispatcher(newURI).forward(request, response); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/web/rest/admin/KeyAndPasswordDTO.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.web.rest.admin; 2 | 3 | import com.google.common.base.Objects; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | @Getter 8 | @Setter 9 | public class KeyAndPasswordDTO { 10 | 11 | private String key; 12 | private String newPassword; 13 | 14 | public KeyAndPasswordDTO() { 15 | } 16 | 17 | @Override 18 | public String toString() { 19 | return Objects.toStringHelper(this) 20 | .add("key", key) 21 | .add("newPassword", newPassword) 22 | .toString(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/web/rest/admin/LoggerDTO.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.web.rest.admin; 2 | 3 | import ch.qos.logback.classic.Logger; 4 | 5 | import com.fasterxml.jackson.annotation.JsonCreator; 6 | import com.google.common.base.Objects; 7 | import lombok.Getter; 8 | import lombok.Setter; 9 | 10 | @Getter 11 | @Setter 12 | public class LoggerDTO { 13 | 14 | private String name; 15 | 16 | private String level; 17 | 18 | public LoggerDTO(Logger logger) { 19 | this.name = logger.getName(); 20 | this.level = logger.getEffectiveLevel().toString(); 21 | } 22 | 23 | @JsonCreator 24 | public LoggerDTO() { 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return Objects.toStringHelper(this) 30 | .add("name", name) 31 | .add("level", level) 32 | .toString(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/web/rest/admin/ManagedUserDTO.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.web.rest.admin; 2 | 3 | import com.google.common.base.Objects; 4 | import com.vedri.mtp.core.user.User; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | /** 9 | * A DTO extending the UserDTO, which is meant to be used in the user management UI. 10 | */ 11 | @Getter 12 | @Setter 13 | public class ManagedUserDTO extends UserDTO { 14 | 15 | private String id; 16 | 17 | public ManagedUserDTO() { 18 | } 19 | 20 | public ManagedUserDTO(User user) { 21 | super(user); 22 | this.id = user.getId(); 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | return Objects.toStringHelper(this) 28 | .add("id", id) 29 | .toString(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/web/rest/errors/CustomParameterizedException.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.web.rest.errors; 2 | 3 | /** 4 | * Custom, parameterized exception, which can be translated on the client side. 5 | * For example: 6 | * 7 | *
 8 |  * throw new CustomParameterizedException("myCustomError", "hello", "world");
 9 |  * 
10 | * 11 | * Can be translated with: 12 | * 13 | *
14 |  * "error.myCustomError" :  "The server says {{params[0]}} to {{params[1]}}"
15 |  * 
16 | */ 17 | public class CustomParameterizedException extends RuntimeException { 18 | 19 | private static final long serialVersionUID = 1L; 20 | 21 | private final String message; 22 | private final String[] params; 23 | 24 | public CustomParameterizedException(String message, String... params) { 25 | super(message); 26 | this.message = message; 27 | this.params = params; 28 | } 29 | 30 | public ParameterizedErrorDTO getErrorDTO() { 31 | return new ParameterizedErrorDTO(message, params); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/web/rest/errors/ErrorConstants.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.web.rest.errors; 2 | 3 | public final class ErrorConstants { 4 | 5 | public static final String ERR_CONCURRENCY_FAILURE = "error.concurrencyFailure"; 6 | public static final String ERR_ACCESS_DENIED = "error.accessDenied"; 7 | public static final String ERR_VALIDATION = "error.validation"; 8 | public static final String ERR_METHOD_NOT_SUPPORTED = "error.methodNotSupported"; 9 | 10 | private ErrorConstants() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/web/rest/errors/ErrorDTO.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.web.rest.errors; 2 | 3 | import lombok.Getter; 4 | 5 | import java.io.Serializable; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * DTO for transfering error message with a list of field errors. 11 | */ 12 | @Getter 13 | public class ErrorDTO implements Serializable { 14 | 15 | private static final long serialVersionUID = 1L; 16 | 17 | private final String message; 18 | private final String description; 19 | 20 | private List fieldErrors; 21 | 22 | ErrorDTO(String message) { 23 | this(message, null); 24 | } 25 | 26 | ErrorDTO(String message, String description) { 27 | this.message = message; 28 | this.description = description; 29 | } 30 | 31 | ErrorDTO(String message, String description, List fieldErrors) { 32 | this.message = message; 33 | this.description = description; 34 | this.fieldErrors = fieldErrors; 35 | } 36 | 37 | public void add(String objectName, String field, String message) { 38 | if (fieldErrors == null) { 39 | fieldErrors = new ArrayList<>(); 40 | } 41 | fieldErrors.add(new FieldErrorDTO(objectName, field, message)); 42 | } 43 | 44 | public List getFieldErrors() { 45 | return fieldErrors; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/web/rest/errors/FieldErrorDTO.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.web.rest.errors; 2 | 3 | import lombok.Getter; 4 | 5 | import java.io.Serializable; 6 | 7 | @Getter 8 | public class FieldErrorDTO implements Serializable { 9 | 10 | private static final long serialVersionUID = 1L; 11 | 12 | private final String objectName; 13 | 14 | private final String field; 15 | 16 | private final String message; 17 | 18 | FieldErrorDTO(String dto, String field, String message) { 19 | this.objectName = dto; 20 | this.field = field; 21 | this.message = message; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/web/rest/errors/ParameterizedErrorDTO.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.web.rest.errors; 2 | 3 | import lombok.Getter; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * DTO for sending a parameterized error message. 9 | */ 10 | @Getter 11 | public class ParameterizedErrorDTO implements Serializable { 12 | 13 | private static final long serialVersionUID = 1L; 14 | private final String message; 15 | private final String[] params; 16 | 17 | public ParameterizedErrorDTO(String message, String... params) { 18 | this.message = message; 19 | this.params = params; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/web/security/AuthoritiesConstants.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.web.security; 2 | 3 | /** 4 | * Constants for Spring Security authorities. 5 | */ 6 | public final class AuthoritiesConstants { 7 | 8 | public static final String ADMIN = "ROLE_ADMIN"; 9 | 10 | public static final String USER = "ROLE_USER"; 11 | 12 | public static final String DEMO = "ROLE_DEMO"; 13 | 14 | public static final String ANONYMOUS = "ROLE_ANONYMOUS"; 15 | 16 | private AuthoritiesConstants() { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/web/security/Http401UnauthorizedEntryPoint.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.web.security; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.ServletException; 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.security.core.AuthenticationException; 13 | import org.springframework.security.web.AuthenticationEntryPoint; 14 | import org.springframework.stereotype.Component; 15 | 16 | /** 17 | * Returns a 401 error code (Unauthorized) to the client. 18 | */ 19 | @Slf4j 20 | @Component 21 | public class Http401UnauthorizedEntryPoint implements AuthenticationEntryPoint { 22 | 23 | /** 24 | * Always returns a 401 error code to the client. 25 | */ 26 | @Override 27 | public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException arg2) 28 | throws IOException, 29 | ServletException { 30 | 31 | log.debug("Pre-authenticated entry point called. Rejecting access"); 32 | response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Access Denied"); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/web/security/UserNotActivatedException.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.web.security; 2 | 3 | import org.springframework.security.core.AuthenticationException; 4 | 5 | /** 6 | * This exception is throw in case of a not activated user trying to authenticate. 7 | */ 8 | public class UserNotActivatedException extends AuthenticationException { 9 | 10 | public UserNotActivatedException(String message) { 11 | super(message); 12 | } 13 | 14 | public UserNotActivatedException(String message, Throwable t) { 15 | super(message, t); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/web/websocket/admin/ActivityDTO.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.web.websocket.admin; 2 | 3 | import com.google.common.base.Objects; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | /** 8 | * DTO for storing a user's activity. 9 | */ 10 | @Getter 11 | @Setter 12 | public class ActivityDTO { 13 | 14 | private String sessionId; 15 | 16 | private String userLogin; 17 | 18 | private String ipAddress; 19 | 20 | private String page; 21 | 22 | private String time; 23 | 24 | @Override 25 | public String toString() { 26 | return Objects.toStringHelper(this) 27 | .add("sessionId", sessionId) 28 | .add("userLogin", userLogin) 29 | .add("ipAddress", ipAddress) 30 | .add("page", page) 31 | .add("time", time) 32 | .toString(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/java/com/vedri/mtp/frontend/web/websocket/transaction/WebsocketSender.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.frontend.web.websocket.transaction; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.messaging.simp.SimpMessagingTemplate; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Slf4j 10 | @Component 11 | public class WebsocketSender { 12 | 13 | private final SimpMessagingTemplate template; 14 | 15 | @Autowired 16 | public WebsocketSender(SimpMessagingTemplate template) { 17 | this.template = template; 18 | } 19 | 20 | public void send(String topic, Object data) { 21 | template.convertAndSend(topic, data); 22 | } 23 | 24 | public void sendToUser(String user, String topic, Object data) { 25 | template.convertAndSendToUser(user, topic, data); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | 2 | akka { 3 | loglevel = "DEBUG" 4 | loggers = ["akka.event.slf4j.Slf4jLogger"] 5 | logging-filter = "akka.event.slf4j.Slf4jLoggingFilter" 6 | logger-startup-timeout = 60s 7 | log-dead-letters = off 8 | log-dead-letters-during-shutdown = off 9 | 10 | remote { 11 | log-remote-lifecycle-events = off 12 | netty.tcp { 13 | port = 2550 14 | hostname = "127.0.0.1" 15 | } 16 | } 17 | 18 | actor { 19 | provider = "akka.cluster.ClusterActorRefProvider" 20 | 21 | default-dispatcher { 22 | # Throughput for default Dispatcher, set to 1 for as fair as possible 23 | throughput = 10 24 | } 25 | } 26 | 27 | cluster { 28 | log-info = on 29 | seed-nodes = [] 30 | roles = ["analytics"] 31 | gossip-interval = 5s 32 | publish-stats-interval = 10s 33 | auto-down-unreachable-after = 10s 34 | #metrics.enabled=off TODO new metrics ext 35 | metrics.gossip-interval = 10s 36 | metrics.collect-interval = 10s 37 | } 38 | } 39 | 40 | nodeFrontend0 { 41 | akka { 42 | remote { 43 | netty.tcp { 44 | port = 9180 45 | } 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /mtp-frontend/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | messages: 3 | basename: classpath:/i18n/messages 4 | mvc: 5 | favicon: 6 | enabled: false 7 | security: 8 | basic: 9 | enabled: false 10 | server: 11 | port: 8080 12 | mtp: 13 | frontend: 14 | cluster: 15 | appName: MtpFrontend 16 | nodeName: nodeFrontend0 17 | akka: 18 | akkaSystemName: MtpFrontend 19 | logConfiguration: false 20 | logClusterMetrics: false 21 | cassandra: 22 | hosts: localhost 23 | port: 9042 24 | keyspace: mtp 25 | spark: 26 | master: local[10] 27 | cassandraHosts: localhost 28 | cleanerTtl: 7200 29 | batchInterval: 10000 30 | checkpointDir: /tmp/spark-frontend 31 | security: 32 | authentication: 33 | oauth: 34 | clientid: mtpapp 35 | secret: mySecretOAuthSecret 36 | # Token is valid 30 minutes 37 | tokenValidityInSeconds: 1800 38 | rememberme: 39 | # security key (this key should be unique for your application, and kept secret) 40 | key: 5b802eb2cabc806b60315220f1aac2948a834541 41 | swagger: 42 | title: MTP API 43 | description: MTP API documentation 44 | version: 0.5.0 45 | termsOfServiceUrl: 46 | contact: 47 | license: 48 | licenseUrl: -------------------------------------------------------------------------------- /mtp-frontend/src/main/resources/i18n/messages_en.properties: -------------------------------------------------------------------------------- 1 | # Error page 2 | error.title=Your request cannot be processed 3 | error.subtitle=Sorry, an error has occurred. 4 | error.status=Status: 5 | error.message=Message: 6 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page Not Found 6 | 7 | 54 | 55 | 56 |

Page Not Found

57 |

Sorry, but the page you were trying to view does not exist.

58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpugar/mtp/f9f4f811d422357d0dc19df016c5835017a3ee2f/mtp-frontend/src/main/webapp/apple-touch-icon.png -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/assets/images/development_ribbon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpugar/mtp/f9f4f811d422357d0dc19df016c5835017a3ee2f/mtp-frontend/src/main/webapp/assets/images/development_ribbon.png -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/assets/styles/documentation.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Your CSS files will be generated in this directory by "grunt sass" or "gulp sass" 3 | */ 4 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpugar/mtp/f9f4f811d422357d0dc19df016c5835017a3ee2f/mtp-frontend/src/main/webapp/favicon.ico -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/i18n/en/activate.json: -------------------------------------------------------------------------------- 1 | { 2 | "activate": { 3 | "title": "Activation", 4 | "messages": { 5 | "success": "Your user has been activated. Please sign in.", 6 | "error": "Your user could not be activated. Please use the registration form to sign up." 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/i18n/en/audits.json: -------------------------------------------------------------------------------- 1 | { 2 | "audits": { 3 | "title": "Audits", 4 | "filter": { 5 | "title": "Filter per date", 6 | "from": "from", 7 | "to": "to", 8 | "button": { 9 | "weeks": "Weeks", 10 | "today": "today", 11 | "clear": "clear", 12 | "close": "close" 13 | } 14 | }, 15 | "table": { 16 | "header": { 17 | "principal": "User", 18 | "date": "Date", 19 | "status": "State", 20 | "data": "Extra data" 21 | }, 22 | "data": { 23 | "remoteAddress": "Remote Address:" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/i18n/en/configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "configuration": { 3 | "title": "Configuration", 4 | "filter": "Filter (by prefix)", 5 | "table": { 6 | "prefix": "Prefix", 7 | "properties": "Properties" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/i18n/en/error.json: -------------------------------------------------------------------------------- 1 | { 2 | "error": { 3 | "title": "Error page!", 4 | "403": "You are not authorized to access the page." 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/i18n/en/health.json: -------------------------------------------------------------------------------- 1 | { 2 | "health": { 3 | "title": "Health checks", 4 | "refresh.button": "Refresh", 5 | "stacktrace": "Stacktrace", 6 | "details": { 7 | "details": "Details", 8 | "properties": "Properties", 9 | "name": "Name", 10 | "value": "Value", 11 | "error": "Error" 12 | }, 13 | "indicator": { 14 | "diskSpace": "Disk space", 15 | "mail": "Email", 16 | "cassandra": "Cassandra" 17 | }, 18 | "table": { 19 | "service": "Service name", 20 | "status": "Status" 21 | }, 22 | "status": { 23 | "UP": "UP", 24 | "DOWN": "DOWN" 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/i18n/en/login.json: -------------------------------------------------------------------------------- 1 | { 2 | "login": { 3 | "title": "Sign in", 4 | "form": { 5 | "password": "Password", 6 | "password.placeholder": "Your password", 7 | "rememberme": "Remember me", 8 | "button": "Sign in" 9 | }, 10 | "messages": { 11 | "error": { 12 | "authentication": "Failed to sign in! Please check your credentials and try again." 13 | } 14 | }, 15 | "password" : { 16 | "forgot": "Did you forget your password?" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/i18n/en/logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "logs": { 3 | "title": "Logs", 4 | "nbloggers": "There are {{ total }} loggers.", 5 | "filter": "Filter", 6 | "table": { 7 | "name": "Name", 8 | "level": "Level" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/i18n/en/main.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": { 3 | "title": "Welcome, MTP User!", 4 | "subtitle": "This is your homepage", 5 | "logged": { 6 | "message": "You are logged in as user \"{{username}}\"." 7 | }, 8 | "github": "Github" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/i18n/en/password.json: -------------------------------------------------------------------------------- 1 | { 2 | "password": { 3 | "title": "Password for [{{username}}]", 4 | "form": { 5 | "button": "Save" 6 | }, 7 | "messages": { 8 | "error": "An error has occurred! The password could not be changed.", 9 | "success": "Password changed!" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/i18n/en/register.json: -------------------------------------------------------------------------------- 1 | { 2 | "register": { 3 | "title": "Registration", 4 | "form": { 5 | "button": "Register" 6 | }, 7 | "messages": { 8 | "validate": { 9 | "login": { 10 | "required": "Your username is required.", 11 | "minlength": "Your username is required to be at least 1 character.", 12 | "maxlength": "Your username cannot be longer than 50 characters.", 13 | "pattern": "Your username can only contain lower-case letters and digits." 14 | } 15 | }, 16 | "success": "Registration saved! Please check your email for confirmation.", 17 | "error": { 18 | "fail": "Registration failed! Please try again later.", 19 | "userexists": "Login name already registered! Please choose another one.", 20 | "emailexists": "E-mail is already in use! Please choose another one." 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/i18n/en/reset.json: -------------------------------------------------------------------------------- 1 | { 2 | "reset": { 3 | "request": { 4 | "title": "Reset your password", 5 | "form": { 6 | "button": "Reset password" 7 | }, 8 | "messages": { 9 | "info": "Enter the e-mail address you used to register", 10 | "success": "Check your e-mails for details on how to reset your password.", 11 | "notfound": "E-Mail address isn't registered! Please check and try again" 12 | 13 | } 14 | 15 | }, 16 | "finish" : { 17 | "title": "Reset password", 18 | "form": { 19 | "button": "Validate new password" 20 | }, 21 | "messages": { 22 | "info": "Choose a new password", 23 | "success": "Your password has been reset. Please sign in.", 24 | "keymissing": "The reset key is missing.", 25 | "error": "Your password couldn't be reset. Remember a password request is only valid for 24 hours." 26 | } 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/i18n/en/sessions.json: -------------------------------------------------------------------------------- 1 | { 2 | "sessions": { 3 | "title": "Active sessions for [{{username}}]", 4 | "table": { 5 | "ipaddress": "IP address", 6 | "useragent": "User Agent", 7 | "date": "Date", 8 | "button": "Invalidate" 9 | }, 10 | "messages": { 11 | "success": "Session invalidated!", 12 | "error": "An error has occurred! The session could not be invalidated." 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/i18n/en/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "settings": { 3 | "title": "User settings for [{{username}}]", 4 | "form": { 5 | "firstname": "First Name", 6 | "firstname.placeholder": "Your first name", 7 | "lastname": "Last Name", 8 | "lastname.placeholder": "Your last name", 9 | "language": "Language", 10 | "button": "Save" 11 | }, 12 | "messages": { 13 | "error": { 14 | "fail": "An error has occurred! Settings could not be saved.", 15 | "emailexists": "E-mail is already in use! Please choose another one." 16 | }, 17 | "success": "Settings saved!", 18 | "validate": { 19 | "firstname": { 20 | "required": "Your first name is required.", 21 | "minlength": "Your first name is required to be at least 1 character", 22 | "maxlength": "Your first name cannot be longer than 50 characters" 23 | }, 24 | "lastname": { 25 | "required": "Your last name is required.", 26 | "minlength": "Your last name is required to be at least 1 character", 27 | "maxlength": "Your last name cannot be longer than 50 characters" 28 | } 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/i18n/en/tracker.json: -------------------------------------------------------------------------------- 1 | { 2 | "tracker": { 3 | "title": "Real-time user activities", 4 | "table": { 5 | "userlogin": "User", 6 | "ipaddress": "IP Address", 7 | "userAgent": "User agent", 8 | "page": "Current page", 9 | "time": "Time" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/i18n/en/transaction.json: -------------------------------------------------------------------------------- 1 | { 2 | "mtpApp": { 3 | "transaction": { 4 | "home": { 5 | "title": "Transactions" 6 | }, 7 | "detail": { 8 | "title": "Transaction" 9 | }, 10 | "partition": "Partition", 11 | "id": "ID", 12 | "receivedTime": "Received time", 13 | "timePlaced": "Time placed", 14 | "validationStatus": "Validation status", 15 | "validationStatusMap": { 16 | "OK": "OK", 17 | "InvalidAmount": "Invalid amount", 18 | "InvalidRate": "Invalid rate", 19 | "InvalidCountry": "Invalid country", 20 | "InvalidFromCurrency": "Invalid 'from' currency", 21 | "InvalidToCurrency": "Invalid 'to' currency" 22 | }, 23 | "userId": "User ID", 24 | "currencyFrom": "Currency from", 25 | "currencyTo": "Currency to", 26 | "amountSell": "Amount sell", 27 | "amountBuy": "Amount buy", 28 | "rate": "Rate", 29 | "originatingCountry": "Originating country", 30 | "nodeName": "Node name", 31 | "timeReceivedFrom": "Received time from", 32 | "timeReceivedTo": "Received time to" 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/i18n/en/transactions-by-country.json: -------------------------------------------------------------------------------- 1 | { 2 | "mtpApp": { 3 | "transactions-by-country": { 4 | "placed-time": { 5 | "title": "Transactions by Country (time placed)", 6 | "date": "Time placed" 7 | }, 8 | "received-time": { 9 | "title": "Transactions by Country (received time)", 10 | "date": "Received date" 11 | }, 12 | "title2": "Transactions count for current day.", 13 | "country": "Country", 14 | "count": "Count", 15 | "points": "Points", 16 | "hour-counts": "Count in Hour", 17 | "hour-points": "Points in Hour", 18 | "waiting-data": "Waiting data...", 19 | "received-data": "Received items count {{numberOfUpdates}}.", 20 | "update-date": "Last update date" 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/i18n/en/transactions-by-currency.json: -------------------------------------------------------------------------------- 1 | { 2 | "mtpApp": { 3 | "transactions-by-currency": { 4 | "placed-time": { 5 | "title": "Transactions by Currency (time placed)", 6 | "date": "Time placed" 7 | }, 8 | "received-time": { 9 | "title": "Transactions by Currency (received time)", 10 | "date": "Received date" 11 | }, 12 | "title2": "Transactions count for current day.", 13 | "currency": "Currency", 14 | "transactionCountFrom": "Count from", 15 | "amountFrom": "Amount from", 16 | "transactionCountTo": "Count to", 17 | "amountTo": "Amount to", 18 | "hour-counts": "Count in Hour", 19 | "hour-points": "Points in Hour", 20 | "waiting-data": "Waiting data...", 21 | "received-data": "Received items count {{numberOfUpdates}}.", 22 | "update-date": "Last update date" 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/i18n/en/transactions-by-status.json: -------------------------------------------------------------------------------- 1 | { 2 | "mtpApp": { 3 | "transactions-by-status": { 4 | "title": "Transactions by Status (received time)", 5 | "title2": "Transactions count in current hour.", 6 | "validationStatus": "Validation Status", 7 | "date": "Received time", 8 | "count": "Count", 9 | "points": "Points", 10 | "hour-counts": "Count in Hour", 11 | "hour-points": "Points in Hour", 12 | "validationStatusMap": { 13 | "OK": "OK", 14 | "InvalidAmount": "Invalid amount", 15 | "InvalidRate": "Invalid rate", 16 | "InvalidCountry": "Invalid country", 17 | "InvalidFromCurrency": "Invalid 'from' currency", 18 | "InvalidToCurrency": "Invalid 'to' currency" 19 | }, 20 | "waiting-data": "Waiting data...", 21 | "received-data": "Received items count {{numberOfUpdates}}.", 22 | "update-date": "Last update date" 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/i18n/en/user.management.json: -------------------------------------------------------------------------------- 1 | { 2 | "user-management": { 3 | "home": { 4 | "title": "Users", 5 | "createLabel": "Create a new user", 6 | "createOrEditLabel": "Create or edit a user" 7 | }, 8 | "delete": { 9 | "question": "Are you sure you want to delete user {{ login }}?" 10 | }, 11 | "detail": { 12 | "title": "User" 13 | }, 14 | "login": "Login", 15 | "firstName": "First name", 16 | "lastName": "Last name", 17 | "email": "Email", 18 | "activated": "Activated", 19 | "deactivated": "Deactivated", 20 | "profiles": "Profiles", 21 | "langKey": "Language", 22 | "createdBy": "Created by", 23 | "createdDate": "Created date", 24 | "lastModifiedBy": "Modified by", 25 | "lastModifiedDate": "Modified date" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/maps/images/flags32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpugar/mtp/f9f4f811d422357d0dc19df016c5835017a3ee2f/mtp-frontend/src/main/webapp/maps/images/flags32.png -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org/ 2 | 3 | User-agent: * 4 | Disallow: /api/account 5 | Disallow: /api/account/change_password 6 | Disallow: /api/account/sessions 7 | Disallow: /api/audits/ 8 | Disallow: /api/logs/ 9 | Disallow: /api/users/ 10 | Disallow: /metrics/ 11 | Disallow: /health/ 12 | Disallow: /trace/ 13 | Disallow: /dump/ 14 | Disallow: /shutdown/ 15 | Disallow: /beans/ 16 | Disallow: /configprops/ 17 | Disallow: /info/ 18 | Disallow: /autoconfig/ 19 | Disallow: /env/ 20 | Disallow: /trace/ 21 | Disallow: /v2/api-docs/ 22 | Disallow: /configuration/ 23 | Disallow: /protected/ 24 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/account/account.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .config(function ($stateProvider) { 5 | $stateProvider 6 | .state('account', { 7 | abstract: true, 8 | parent: 'site' 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/account/activate/activate.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .controller('ActivationController', function ($scope, $stateParams, Auth) { 5 | Auth.activateAccount({key: $stateParams.key}).then(function () { 6 | $scope.error = null; 7 | $scope.success = 'OK'; 8 | }).catch(function () { 9 | $scope.success = null; 10 | $scope.error = 'ERROR'; 11 | }); 12 | }); 13 | 14 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/account/activate/activate.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Activation

5 | 6 |
7 | Your user has been activated. Please sign in. 8 |
9 | 10 |
11 | Your user could not be activated. Please use the registration form to sign up. 12 |
13 | 14 |
15 |
16 |
17 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/account/activate/activate.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .config(function ($stateProvider) { 5 | $stateProvider 6 | .state('activate', { 7 | parent: 'account', 8 | url: '/activate?key', 9 | data: { 10 | authorities: [], 11 | pageTitle: 'activate.title' 12 | }, 13 | views: { 14 | 'content@': { 15 | templateUrl: 'scripts/app/account/activate/activate.html', 16 | controller: 'ActivationController' 17 | } 18 | }, 19 | resolve: { 20 | translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) { 21 | $translatePartialLoader.addPart('activate'); 22 | return $translate.refresh(); 23 | }] 24 | } 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/account/login/login.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .controller('LoginController', function ($rootScope, $scope, $state, $timeout, Auth) { 5 | $scope.user = {}; 6 | $scope.errors = {}; 7 | 8 | $scope.rememberMe = true; 9 | $timeout(function (){angular.element('[ng-model="username"]').focus();}); 10 | $scope.login = function (event) { 11 | event.preventDefault(); 12 | Auth.login({ 13 | username: $scope.username, 14 | password: $scope.password, 15 | rememberMe: $scope.rememberMe 16 | }).then(function () { 17 | $scope.authenticationError = false; 18 | if ($rootScope.previousStateName === 'register') { 19 | $state.go('home'); 20 | } else { 21 | $rootScope.back(); 22 | } 23 | }).catch(function () { 24 | $scope.authenticationError = true; 25 | }); 26 | }; 27 | }); 28 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/account/login/login.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Sign in

5 | 6 |
7 | Failed to sign in! Please check your credentials and try again. 8 |
9 |
10 |
11 |
12 |
13 | 14 | 15 |
16 |
17 | 18 | 20 |
21 | 22 |
23 |

24 |
25 |
26 |
27 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/account/login/login.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .config(function ($stateProvider) { 5 | $stateProvider 6 | .state('login', { 7 | parent: 'account', 8 | url: '/login', 9 | data: { 10 | authorities: [], 11 | pageTitle: 'login.title' 12 | }, 13 | views: { 14 | 'content@': { 15 | templateUrl: 'scripts/app/account/login/login.html', 16 | controller: 'LoginController' 17 | } 18 | }, 19 | resolve: { 20 | translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) { 21 | $translatePartialLoader.addPart('login'); 22 | return $translate.refresh(); 23 | }] 24 | } 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/account/password/password.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .controller('PasswordController', function ($scope, Auth, Principal) { 5 | Principal.identity().then(function(account) { 6 | $scope.account = account; 7 | }); 8 | 9 | $scope.success = null; 10 | $scope.error = null; 11 | $scope.doNotMatch = null; 12 | $scope.changePassword = function () { 13 | if ($scope.password !== $scope.confirmPassword) { 14 | $scope.doNotMatch = 'ERROR'; 15 | } else { 16 | $scope.doNotMatch = null; 17 | Auth.changePassword($scope.password).then(function () { 18 | $scope.error = null; 19 | $scope.success = 'OK'; 20 | }).catch(function () { 21 | $scope.success = null; 22 | $scope.error = 'ERROR'; 23 | }); 24 | } 25 | }; 26 | }); 27 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/account/password/password.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .config(function ($stateProvider) { 5 | $stateProvider 6 | .state('password', { 7 | parent: 'account', 8 | url: '/password', 9 | data: { 10 | authorities: ['ROLE_USER'], 11 | pageTitle: 'global.menu.account.password' 12 | }, 13 | views: { 14 | 'content@': { 15 | templateUrl: 'scripts/app/account/password/password.html', 16 | controller: 'PasswordController' 17 | } 18 | }, 19 | resolve: { 20 | translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) { 21 | $translatePartialLoader.addPart('password'); 22 | return $translate.refresh(); 23 | }] 24 | } 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/account/register/register.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .config(function ($stateProvider) { 5 | $stateProvider 6 | .state('register', { 7 | parent: 'account', 8 | url: '/register', 9 | data: { 10 | authorities: [], 11 | pageTitle: 'register.title' 12 | }, 13 | views: { 14 | 'content@': { 15 | templateUrl: 'scripts/app/account/register/register.html', 16 | controller: 'RegisterController' 17 | } 18 | }, 19 | resolve: { 20 | translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) { 21 | $translatePartialLoader.addPart('register'); 22 | return $translate.refresh(); 23 | }] 24 | } 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/account/reset/finish/reset.finish.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .controller('ResetFinishController', function ($scope, $stateParams, $timeout, Auth) { 5 | 6 | $scope.keyMissing = $stateParams.key === undefined; 7 | $scope.doNotMatch = null; 8 | 9 | $scope.resetAccount = {}; 10 | $timeout(function (){angular.element('[ng-model="resetAccount.password"]').focus();}); 11 | 12 | $scope.finishReset = function() { 13 | if ($scope.resetAccount.password !== $scope.confirmPassword) { 14 | $scope.doNotMatch = 'ERROR'; 15 | } else { 16 | Auth.resetPasswordFinish({key: $stateParams.key, newPassword: $scope.resetAccount.password}).then(function () { 17 | $scope.success = 'OK'; 18 | }).catch(function (response) { 19 | $scope.success = null; 20 | $scope.error = 'ERROR'; 21 | 22 | }); 23 | } 24 | 25 | }; 26 | }); 27 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/account/reset/finish/reset.finish.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .config(function ($stateProvider) { 5 | $stateProvider 6 | .state('finishReset', { 7 | parent: 'account', 8 | url: '/reset/finish?key', 9 | data: { 10 | authorities: [] 11 | }, 12 | views: { 13 | 'content@': { 14 | templateUrl: 'scripts/app/account/reset/finish/reset.finish.html', 15 | controller: 'ResetFinishController' 16 | } 17 | }, 18 | resolve: { 19 | translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) { 20 | $translatePartialLoader.addPart('reset'); 21 | return $translate.refresh(); 22 | }] 23 | } 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/account/reset/request/reset.request.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .controller('RequestResetController', function ($rootScope, $scope, $state, $timeout, Auth) { 5 | 6 | $scope.success = null; 7 | $scope.error = null; 8 | $scope.errorEmailNotExists = null; 9 | $scope.resetAccount = {}; 10 | $timeout(function (){angular.element('[ng-model="resetAccount.email"]').focus();}); 11 | 12 | $scope.requestReset = function () { 13 | 14 | $scope.error = null; 15 | $scope.errorEmailNotExists = null; 16 | 17 | Auth.resetPasswordInit($scope.resetAccount.email).then(function () { 18 | $scope.success = 'OK'; 19 | }).catch(function (response) { 20 | $scope.success = null; 21 | if (response.status === 400 && response.data === 'e-mail address not registered') { 22 | $scope.errorEmailNotExists = 'ERROR'; 23 | } else { 24 | $scope.error = 'ERROR'; 25 | } 26 | }); 27 | } 28 | 29 | }); 30 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/account/reset/request/reset.request.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .config(function ($stateProvider) { 5 | $stateProvider 6 | .state('requestReset', { 7 | parent: 'account', 8 | url: '/reset/request', 9 | data: { 10 | authorities: [] 11 | }, 12 | views: { 13 | 'content@': { 14 | templateUrl: 'scripts/app/account/reset/request/reset.request.html', 15 | controller: 'RequestResetController' 16 | } 17 | }, 18 | resolve: { 19 | translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) { 20 | $translatePartialLoader.addPart('reset'); 21 | return $translate.refresh(); 22 | }] 23 | } 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/account/settings/settings.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .controller('SettingsController', function ($scope, Principal, Auth, Language, $translate) { 5 | $scope.success = null; 6 | $scope.error = null; 7 | Principal.identity(true).then(function(account) { 8 | $scope.settingsAccount = account; 9 | }); 10 | 11 | $scope.save = function () { 12 | Auth.updateAccount($scope.settingsAccount).then(function() { 13 | $scope.error = null; 14 | $scope.success = 'OK'; 15 | Principal.identity().then(function(account) { 16 | $scope.settingsAccount = account; 17 | }); 18 | Language.getCurrent().then(function(current) { 19 | if ($scope.settingsAccount.langKey !== current) { 20 | $translate.use($scope.settingsAccount.langKey); 21 | } 22 | }); 23 | }).catch(function() { 24 | $scope.success = null; 25 | $scope.error = 'ERROR'; 26 | }); 27 | }; 28 | }); 29 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/account/settings/settings.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .config(function ($stateProvider) { 5 | $stateProvider 6 | .state('settings', { 7 | parent: 'account', 8 | url: '/settings', 9 | data: { 10 | authorities: ['ROLE_USER', 'ROLE_DEMO'], 11 | pageTitle: 'global.menu.account.settings' 12 | }, 13 | views: { 14 | 'content@': { 15 | templateUrl: 'scripts/app/account/settings/settings.html', 16 | controller: 'SettingsController' 17 | } 18 | }, 19 | resolve: { 20 | translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) { 21 | $translatePartialLoader.addPart('settings'); 22 | return $translate.refresh(); 23 | }] 24 | } 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/admin/admin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .config(function ($stateProvider) { 5 | $stateProvider 6 | .state('admin', { 7 | abstract: true, 8 | parent: 'site' 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/admin/audits/audits.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .controller('AuditsController', function ($scope, $filter, AuditsService) { 5 | $scope.onChangeDate = function () { 6 | var dateFormat = 'yyyy-MM-dd'; 7 | var fromDate = $filter('date')($scope.fromDate, dateFormat); 8 | var toDate = $filter('date')($scope.toDate, dateFormat); 9 | 10 | AuditsService.findByDates(fromDate, toDate).then(function (data) { 11 | $scope.audits = data; 12 | }); 13 | }; 14 | 15 | // Date picker configuration 16 | $scope.today = function () { 17 | // Today + 1 day - needed if the current day must be included 18 | var today = new Date(); 19 | $scope.toDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1); 20 | }; 21 | 22 | $scope.previousMonth = function () { 23 | var fromDate = new Date(); 24 | if (fromDate.getMonth() === 0) { 25 | fromDate = new Date(fromDate.getFullYear() - 1, 0, fromDate.getDate()); 26 | } else { 27 | fromDate = new Date(fromDate.getFullYear(), fromDate.getMonth() - 1, fromDate.getDate()); 28 | } 29 | 30 | $scope.fromDate = fromDate; 31 | }; 32 | 33 | $scope.today(); 34 | $scope.previousMonth(); 35 | $scope.onChangeDate(); 36 | }); 37 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/admin/audits/audits.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .config(function ($stateProvider) { 5 | $stateProvider 6 | .state('audits', { 7 | parent: 'admin', 8 | url: '/audits', 9 | data: { 10 | authorities: ['ROLE_ADMIN'], 11 | pageTitle: 'audits.title' 12 | }, 13 | views: { 14 | 'content@': { 15 | templateUrl: 'scripts/app/admin/audits/audits.html', 16 | controller: 'AuditsController' 17 | } 18 | }, 19 | resolve: { 20 | translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) { 21 | $translatePartialLoader.addPart('audits'); 22 | return $translate.refresh(); 23 | }] 24 | } 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/admin/configuration/configuration.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .controller('ConfigurationController', function ($scope, ConfigurationService) { 5 | ConfigurationService.get().then(function(configuration) { 6 | $scope.configuration = configuration; 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/admin/configuration/configuration.html: -------------------------------------------------------------------------------- 1 |
2 |

configuration

3 | 4 | Filter (by prefix) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 22 | 23 |
PrefixProperties
{{entry.prefix}} 17 |
18 |
{{key}}
19 |
{{value}}
20 |
21 |
24 |
25 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/admin/configuration/configuration.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .config(function ($stateProvider) { 5 | $stateProvider 6 | .state('configuration', { 7 | parent: 'admin', 8 | url: '/configuration', 9 | data: { 10 | authorities: ['ROLE_ADMIN'], 11 | pageTitle: 'configuration.title' 12 | }, 13 | views: { 14 | 'content@': { 15 | templateUrl: 'scripts/app/admin/configuration/configuration.html', 16 | controller: 'ConfigurationController' 17 | } 18 | }, 19 | resolve: { 20 | translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) { 21 | $translatePartialLoader.addPart('configuration'); 22 | return $translate.refresh(); 23 | }] 24 | } 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/admin/docs/docs.html: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/admin/docs/docs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .config(function ($stateProvider) { 5 | $stateProvider 6 | .state('docs', { 7 | parent: 'admin', 8 | url: '/docs', 9 | data: { 10 | authorities: ['ROLE_ADMIN'], 11 | pageTitle: 'global.menu.admin.apidocs' 12 | }, 13 | views: { 14 | 'content@': { 15 | templateUrl: 'scripts/app/admin/docs/docs.html' 16 | } 17 | }, 18 | resolve: { 19 | translatePartialLoader: ['$translate', function ($translate) { 20 | return $translate.refresh(); 21 | }] 22 | } 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/admin/health/health.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .config(function ($stateProvider) { 5 | $stateProvider 6 | .state('health', { 7 | parent: 'admin', 8 | url: '/health', 9 | data: { 10 | authorities: ['ROLE_ADMIN', 'ROLE_DEMO'], 11 | pageTitle: 'health.title' 12 | }, 13 | views: { 14 | 'content@': { 15 | templateUrl: 'scripts/app/admin/health/health.html', 16 | controller: 'HealthController' 17 | } 18 | }, 19 | resolve: { 20 | translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) { 21 | $translatePartialLoader.addPart('health'); 22 | return $translate.refresh(); 23 | }] 24 | } 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/admin/health/health.modal.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .controller('HealthModalController', function($scope, $modalInstance, currentHealth, baseName, subSystemName) { 5 | 6 | $scope.currentHealth = currentHealth; 7 | $scope.baseName = baseName, $scope.subSystemName = subSystemName; 8 | 9 | $scope.cancel = function() { 10 | $modalInstance.dismiss('cancel'); 11 | }; 12 | }); 13 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/admin/logs/logs.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .controller('LogsController', function ($scope, LogsService) { 5 | $scope.loggers = LogsService.findAll(); 6 | 7 | $scope.changeLevel = function (name, level) { 8 | LogsService.changeLevel({name: name, level: level}, function () { 9 | $scope.loggers = LogsService.findAll(); 10 | }); 11 | }; 12 | }); 13 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/admin/logs/logs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .config(function ($stateProvider) { 5 | $stateProvider 6 | .state('logs', { 7 | parent: 'admin', 8 | url: '/logs', 9 | data: { 10 | authorities: ['ROLE_ADMIN'], 11 | pageTitle: 'logs.title' 12 | }, 13 | views: { 14 | 'content@': { 15 | templateUrl: 'scripts/app/admin/logs/logs.html', 16 | controller: 'LogsController' 17 | } 18 | }, 19 | resolve: { 20 | translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) { 21 | $translatePartialLoader.addPart('logs'); 22 | return $translate.refresh(); 23 | }] 24 | } 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/admin/metrics/metrics.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .config(function ($stateProvider) { 5 | $stateProvider 6 | .state('metrics', { 7 | parent: 'admin', 8 | url: '/metrics', 9 | data: { 10 | authorities: ['ROLE_ADMIN', 'ROLE_DEMO'], 11 | pageTitle: 'metrics.title' 12 | }, 13 | views: { 14 | 'content@': { 15 | templateUrl: 'scripts/app/admin/metrics/metrics.html', 16 | controller: 'MetricsController' 17 | } 18 | }, 19 | resolve: { 20 | translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) { 21 | $translatePartialLoader.addPart('metrics'); 22 | return $translate.refresh(); 23 | }] 24 | } 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/admin/tracker/tracker.controller.js: -------------------------------------------------------------------------------- 1 | angular.module('mtpApp') 2 | .controller('TrackerController', function ($scope, AuthServerProvider, $cookies, $http, Tracker) { 3 | // This controller uses a Websocket connection to receive user activities in real-time. 4 | 5 | $scope.activities = []; 6 | Tracker.receive().then(null, null, function(activity) { 7 | showActivity(activity); 8 | }); 9 | 10 | function showActivity(activity) { 11 | var existingActivity = false; 12 | for (var index = 0; index < $scope.activities.length; index++) { 13 | if($scope.activities[index].sessionId == activity.sessionId) { 14 | existingActivity = true; 15 | if (activity.page == 'logout') { 16 | $scope.activities.splice(index, 1); 17 | } else { 18 | $scope.activities[index] = activity; 19 | } 20 | } 21 | } 22 | if (!existingActivity && (activity.page != 'logout')) { 23 | $scope.activities.push(activity); 24 | } 25 | }; 26 | }); 27 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/admin/tracker/tracker.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Real-time user activities

4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
UserIP AddressCurrent pageTime
{{activity.userLogin}}{{activity.ipAddress}}{{activity.page}}{{activity.time}}
25 |
26 |
27 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/admin/tracker/tracker.js: -------------------------------------------------------------------------------- 1 | angular.module('mtpApp') 2 | .config(function ($stateProvider) { 3 | $stateProvider 4 | .state('tracker', { 5 | parent: 'admin', 6 | url: '/tracker', 7 | data: { 8 | authorities: ['ROLE_ADMIN'], 9 | pageTitle: 'tracker.title' 10 | }, 11 | views: { 12 | 'content@': { 13 | templateUrl: 'scripts/app/admin/tracker/tracker.html', 14 | controller: 'TrackerController' 15 | } 16 | }, 17 | resolve: { 18 | mainTranslatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) { 19 | $translatePartialLoader.addPart('tracker'); 20 | return $translate.refresh(); 21 | }] 22 | }, 23 | onEnter: function(Tracker) { 24 | Tracker.subscribe(); 25 | }, 26 | onExit: function(Tracker) { 27 | Tracker.unsubscribe(); 28 | } 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/admin/user-management/user-management-detail.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .controller('UserManagementDetailController', function ($scope, $stateParams, User) { 5 | $scope.user = {}; 6 | $scope.load = function (login) { 7 | User.get({login: login}, function(result) { 8 | $scope.user = result; 9 | }); 10 | }; 11 | $scope.load($stateParams.login); 12 | }); 13 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/app.constants.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | // DO NOT EDIT THIS FILE, EDIT THE GRUNT TASK NGCONSTANT SETTINGS INSTEAD WHICH GENERATES THIS FILE 3 | angular.module('mtpApp') 4 | 5 | .constant('ENV', 'dev') 6 | 7 | .constant('VERSION', '0.5.0') 8 | 9 | ; -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/entities/entity.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .config(function ($stateProvider) { 5 | $stateProvider 6 | .state('entity', { 7 | abstract: true, 8 | parent: 'site' 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/entities/transaction/transaction-detail.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .controller('TransactionDetailController', function ($scope, $rootScope, $stateParams, $state, $translate, 5 | Transaction, Country) { 6 | 7 | $scope.query = $stateParams.query; 8 | $scope.transactionId = $stateParams.transactionId; 9 | 10 | $scope.load = function () { 11 | Transaction.get({transactionId: $scope.transactionId}, function (transaction) { 12 | 13 | $scope.transaction = transaction; 14 | 15 | if (transaction.originatingCountry) { 16 | Country.get({cca2: transaction.originatingCountry}, function (result) { 17 | $scope.country = result; 18 | }); 19 | } 20 | }); 21 | }; 22 | 23 | $scope.load(); 24 | }); 25 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/entities/transactions-by-currency/transactions-by-currency.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .controller('TransactionsByCurrencyController', function ($scope, $rootScope, $stateParams, $translate, 5 | ParseLinks, TransactionByCurrency, timeType) { 6 | $scope.data = {}; 7 | $scope.timeType = timeType; 8 | $scope.numberOfUpdates = 0; 9 | 10 | TransactionByCurrency.receive().then(null, null, function (data) { 11 | data.dateUTC = Date.UTC(data.year, data.month - 1, data.day, data.hour); 12 | data.date = new Date(data.dateUTC); 13 | data.currentDateUTC = new Date().getTime(); 14 | 15 | addTableData(data); 16 | }); 17 | 18 | function addTableData(data) { 19 | 20 | var key = data.currency; 21 | var row = $scope.data[key]; 22 | 23 | if (row && row.transactionCount != data.transactionCount) { 24 | $scope.numberOfUpdates++; 25 | $scope.data[key] = data; 26 | } else { 27 | $scope.numberOfUpdates++; 28 | $scope.data[key] = data; 29 | } 30 | } 31 | }); 32 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/error/accessdenied.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 |
7 |

Error Page!

8 | 9 |
You are not authorized to access the page. 10 |
11 |
12 |
13 |
14 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/error/error.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 |
7 |

Error Page!

8 | 9 |
10 |
{{errorMessage}} 11 |
12 |
13 |
14 |
15 |
16 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/main/main.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .controller('MainController', function ($scope, Principal) { 5 | Principal.identity().then(function(account) { 6 | $scope.account = account; 7 | $scope.isAuthenticated = Principal.isAuthenticated; 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/main/main.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 |
7 |

Welcome, MTP User!

8 |

This is your homepage

9 | 10 |
11 |
12 | You are logged in as user "{{account.login}}". 13 |
14 | 15 |
16 | If you want to sign in, you can try the default accounts:
- Demo (login="demo" and password="demo123"). 17 |
18 |
19 | 20 |

21 | Github! 22 |

23 |
24 |
25 |
26 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/app/main/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .config(function ($stateProvider) { 5 | $stateProvider 6 | .state('home', { 7 | parent: 'site', 8 | url: '/', 9 | data: { 10 | authorities: [] 11 | }, 12 | views: { 13 | 'content@': { 14 | templateUrl: 'scripts/app/main/main.html', 15 | controller: 'MainController' 16 | } 17 | }, 18 | resolve: { 19 | mainTranslatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate,$translatePartialLoader) { 20 | $translatePartialLoader.addPart('main'); 21 | return $translate.refresh(); 22 | }] 23 | } 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/admin/audits.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .factory('AuditsService', function ($http) { 5 | return { 6 | findAll: function () { 7 | return $http.get('api/audits/').then(function (response) { 8 | return response.data; 9 | }); 10 | }, 11 | findByDates: function (fromDate, toDate) { 12 | 13 | var formatDate = function (dateToFormat) { 14 | if (dateToFormat !== undefined && !angular.isString(dateToFormat)) { 15 | return dateToFormat.getYear() + '-' + dateToFormat.getMonth() + '-' + dateToFormat.getDay(); 16 | } 17 | return dateToFormat; 18 | }; 19 | 20 | return $http.get('api/audits/', {params: {fromDate: formatDate(fromDate), toDate: formatDate(toDate)}}).then(function (response) { 21 | return response.data; 22 | }); 23 | } 24 | }; 25 | }); 26 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/admin/configuration.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .factory('ConfigurationService', function ($rootScope, $filter, $http) { 5 | return { 6 | get: function() { 7 | return $http.get('configprops').then(function (response) { 8 | var properties = []; 9 | angular.forEach(response.data, function (data) { 10 | properties.push(data); 11 | }); 12 | var orderBy = $filter('orderBy'); 13 | return orderBy(properties, 'prefix'); 14 | }); 15 | } 16 | }; 17 | }); 18 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/admin/logs.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .factory('LogsService', function ($resource) { 5 | return $resource('api/logs', {}, { 6 | 'findAll': { method: 'GET', isArray: true}, 7 | 'changeLevel': { method: 'PUT'} 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/admin/monitoring.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .factory('MonitoringService', function ($rootScope, $http) { 5 | return { 6 | getMetrics: function () { 7 | return $http.get('metrics/metrics').then(function (response) { 8 | return response.data; 9 | }); 10 | }, 11 | 12 | checkHealth: function () { 13 | return $http.get('health').then(function (response) { 14 | return response.data; 15 | }); 16 | }, 17 | 18 | threadDump: function () { 19 | return $http.get('dump').then(function (response) { 20 | return response.data; 21 | }); 22 | } 23 | }; 24 | }); 25 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/auth/services/account.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .factory('Account', function Account($resource) { 5 | return $resource('api/account', {}, { 6 | 'get': { method: 'GET', params: {}, isArray: false, 7 | interceptor: { 8 | response: function(response) { 9 | // expose response 10 | return response; 11 | } 12 | } 13 | } 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/auth/services/activate.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .factory('Activate', function ($resource) { 5 | return $resource('api/activate', {}, { 6 | 'get': { method: 'GET', params: {}, isArray: false} 7 | }); 8 | }); 9 | 10 | 11 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/auth/services/password.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .factory('Password', function ($resource) { 5 | return $resource('api/account/change_password', {}, { 6 | }); 7 | }); 8 | 9 | angular.module('mtpApp') 10 | .factory('PasswordResetInit', function ($resource) { 11 | return $resource('api/account/reset_password/init', {}, { 12 | }) 13 | }); 14 | 15 | angular.module('mtpApp') 16 | .factory('PasswordResetFinish', function ($resource) { 17 | return $resource('api/account/reset_password/finish', {}, { 18 | }) 19 | }); 20 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/auth/services/register.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .factory('Register', function ($resource) { 5 | return $resource('api/register', {}, { 6 | }); 7 | }); 8 | 9 | 10 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/constants/general.constants.js: -------------------------------------------------------------------------------- 1 | angular.module('mtpApp') 2 | .constant('ChartConsts', { 3 | LineChartEntries: 120, 4 | DefaultChartConfig: { 5 | options: { 6 | chart: { 7 | type: 'line' 8 | }, 9 | tooltip: { 10 | style: { 11 | padding: 10, 12 | fontWeight: 'bold' 13 | } 14 | } 15 | }, 16 | loading: false, 17 | xAxis: { 18 | type: 'datetime', 19 | minRange: 30 * 1000 20 | }, 21 | useHighStocks: false 22 | } 23 | }) 24 | .constant('TableConsts', { 25 | Page: 1, 26 | PerPage: 20 27 | }); -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/constants/validation-status.constant.js: -------------------------------------------------------------------------------- 1 | angular.module('mtpApp') 2 | .constant('ValidationStatus', { 3 | OK: { 4 | description: "OK", 5 | index: 0 6 | }, 7 | InvalidAmount: { 8 | description: "Invalid amount", 9 | index: 1 10 | }, 11 | InvalidRate: { 12 | description: "Invalid rate", 13 | index: 2 14 | }, 15 | InvalidCountry: { 16 | description: "Invalid country", 17 | index: 3 18 | }, 19 | InvalidFromCurrency: { 20 | description: "Invalid 'from' currency", 21 | index: 4 22 | }, 23 | InvalidToCurrency: { 24 | description: "Invalid 'to' currency", 25 | index: 5 26 | } 27 | }); -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/entities/aggregation.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .factory('AggregationSubscription', function ($q, Websocket) { 5 | 6 | var cache = {}; 7 | 8 | var AggregationSubscriptionInstance= function(name) { 9 | this.name = name; 10 | this.subscriber = null; 11 | this.listener = $q.defer(); 12 | }; 13 | 14 | AggregationSubscriptionInstance.prototype.subscribe = function () { 15 | var self = this; 16 | Websocket.subscribe("/topic/" + self.name, function (data) { 17 | self.listener.notify(JSON.parse(data.body)); 18 | }).then(function (result) { 19 | self.subscriber = result; 20 | }); 21 | }; 22 | 23 | AggregationSubscriptionInstance.prototype.unsubscribe = function () { 24 | var self = this; 25 | Websocket.unsubscribe(self.subscriber); 26 | }; 27 | 28 | AggregationSubscriptionInstance.prototype.receive = function () { 29 | var self = this; 30 | return self.listener.promise; 31 | }; 32 | 33 | return { 34 | getInstance: function(name) { 35 | if (cache[name]) { 36 | return cache[name]; 37 | } else { 38 | cache[name] = new AggregationSubscription(name); 39 | return cache[name]; 40 | } 41 | } 42 | }; 43 | }); 44 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/entities/country.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .factory('Country', function ($resource) { 5 | return $resource('api/countries/:cca2', {}, { 6 | 'query': { method: 'GET', isArray: true}, 7 | 'get': { 8 | method: 'GET', 9 | transformResponse: function (data) { 10 | data = angular.fromJson(data); 11 | return data; 12 | } 13 | }, 14 | 'queryWithFilter': { method: 'GET', isArray: true} 15 | }); 16 | }); -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/entities/currency.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .factory('Currency', function ($resource) { 5 | return $resource('api/currencies/:code', {}, { 6 | 'queryWithFilter': { method: 'GET', isArray: true} 7 | }); 8 | }); -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/form/form.directive.js: -------------------------------------------------------------------------------- 1 | /* globals $ */ 2 | 'use strict'; 3 | 4 | angular.module('mtpApp') 5 | .directive('showValidation', function() { 6 | return { 7 | restrict: 'A', 8 | require: 'form', 9 | link: function (scope, element) { 10 | element.find('.form-group').each(function() { 11 | var $formGroup = $(this); 12 | var $inputs = $formGroup.find('input[ng-model],textarea[ng-model],select[ng-model]'); 13 | 14 | if ($inputs.length > 0) { 15 | $inputs.each(function() { 16 | var $input = $(this); 17 | scope.$watch(function() { 18 | return $input.hasClass('ng-invalid') && $input.hasClass('ng-dirty'); 19 | }, function(isInvalid) { 20 | $formGroup.toggleClass('has-error', isInvalid); 21 | }); 22 | }); 23 | } 24 | }); 25 | } 26 | }; 27 | }); 28 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/form/maxbytes.directive.js: -------------------------------------------------------------------------------- 1 | /* globals $ */ 2 | 'use strict'; 3 | 4 | angular.module('mtpApp') 5 | .directive('maxbytes', function ($q) { 6 | function endsWith(suffix, str) { 7 | return str.indexOf(suffix, str.length - suffix.length) !== -1; 8 | } 9 | 10 | function paddingSize(base64String) { 11 | if (endsWith('==', base64String)) { 12 | return 2; 13 | } 14 | if (endsWith('=', base64String)) { 15 | return 1; 16 | } 17 | return 0; 18 | } 19 | 20 | function numberOfBytes(base64String) { 21 | return base64String.length / 4 * 3 - paddingSize(base64String); 22 | } 23 | 24 | return { 25 | restrict: 'A', 26 | require: '?ngModel', 27 | link: function (scope, element, attrs, ngModel) { 28 | if (!ngModel) return; 29 | 30 | ngModel.$validators.maxbytes = function (modelValue) { 31 | return ngModel.$isEmpty(modelValue) || numberOfBytes(modelValue) <= attrs.maxbytes; 32 | }; 33 | } 34 | }; 35 | }); 36 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/form/minbytes.directive.js: -------------------------------------------------------------------------------- 1 | /* globals $ */ 2 | 'use strict'; 3 | 4 | angular.module('mtpApp') 5 | .directive('minbytes', function ($q) { 6 | function endsWith(suffix, str) { 7 | return str.indexOf(suffix, str.length - suffix.length) !== -1; 8 | } 9 | 10 | function paddingSize(base64String) { 11 | if (endsWith('==', base64String)) { 12 | return 2; 13 | } 14 | if (endsWith('=', base64String)) { 15 | return 1; 16 | } 17 | return 0; 18 | } 19 | 20 | function numberOfBytes(base64String) { 21 | return base64String.length / 4 * 3 - paddingSize(base64String); 22 | } 23 | 24 | return { 25 | restrict: 'A', 26 | require: '?ngModel', 27 | link: function (scope, element, attrs, ngModel) { 28 | if (!ngModel) return; 29 | 30 | ngModel.$validators.minbytes = function (modelValue) { 31 | return ngModel.$isEmpty(modelValue) || numberOfBytes(modelValue) >= attrs.minbytes; 32 | }; 33 | } 34 | }; 35 | }); 36 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/form/pager.directive.js: -------------------------------------------------------------------------------- 1 | /* globals $ */ 2 | 'use strict'; 3 | 4 | angular.module('mtpApp') 5 | .directive('mtpAppPager', function() { 6 | return { 7 | templateUrl: 'scripts/components/form/pager.html' 8 | }; 9 | }); 10 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/form/pager.html: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/form/pagination.directive.js: -------------------------------------------------------------------------------- 1 | /* globals $ */ 2 | 'use strict'; 3 | 4 | angular.module('mtpApp') 5 | .directive('mtpAppPagination', function() { 6 | return { 7 | templateUrl: 'scripts/components/form/pagination.html' 8 | }; 9 | }); 10 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/form/pagination.html: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/interceptor/auth.interceptor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .factory('authInterceptor', function ($rootScope, $q, $location, localStorageService) { 5 | return { 6 | // Add authorization token to headers 7 | request: function (config) { 8 | config.headers = config.headers || {}; 9 | var token = localStorageService.get('token'); 10 | 11 | if (token && token.expires_at && token.expires_at > new Date().getTime()) { 12 | config.headers.Authorization = 'Bearer ' + token.access_token; 13 | } 14 | 15 | return config; 16 | } 17 | }; 18 | }) 19 | .factory('authExpiredInterceptor', function ($rootScope, $q, $injector, localStorageService) { 20 | return { 21 | responseError: function (response) { 22 | // token has expired 23 | if (response.status === 401 && (response.data.error == 'invalid_token' || response.data.error == 'Unauthorized')) { 24 | localStorageService.remove('token'); 25 | var Principal = $injector.get('Principal'); 26 | if (Principal.isAuthenticated()) { 27 | var Auth = $injector.get('Auth'); 28 | Auth.authorize(true); 29 | } 30 | } 31 | return $q.reject(response); 32 | } 33 | }; 34 | }); 35 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/interceptor/errorhandler.interceptor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .factory('errorHandlerInterceptor', function ($q, $rootScope) { 5 | return { 6 | 'responseError': function (response) { 7 | if (!(response.status == 401 && response.data.path && response.data.path.indexOf("/api/account") == 0 )){ 8 | $rootScope.$emit('mtpApp.httpError', response); 9 | } 10 | return $q.reject(response); 11 | } 12 | }; 13 | }); -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/interceptor/notification.interceptor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .factory('notificationInterceptor', function ($q, AlertService) { 5 | return { 6 | response: function(response) { 7 | var alertKey = response.headers('X-mtpApp-alert'); 8 | if (angular.isString(alertKey)) { 9 | AlertService.success(alertKey, { param : response.headers('X-mtpApp-params')}); 10 | } 11 | return response; 12 | } 13 | }; 14 | }); 15 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/language/language.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .controller('LanguageController', function ($scope, $translate, Language, tmhDynamicLocale) { 5 | $scope.changeLanguage = function (languageKey) { 6 | $translate.use(languageKey); 7 | tmhDynamicLocale.set(languageKey); 8 | }; 9 | 10 | Language.getAll().then(function (languages) { 11 | $scope.languages = languages; 12 | }); 13 | }) 14 | .filter('findLanguageFromKey', function () { 15 | return function (lang) { 16 | return { 17 | "ca": "Català", 18 | "da": "Dansk", 19 | "de": "Deutsch", 20 | "en": "English", 21 | "es": "Español", 22 | "fr": "Français", 23 | "hu": "Magyar", 24 | "it": "Italiano", 25 | "ja": "日本語", 26 | "ko": "한국어", 27 | "nl": "Nederlands", 28 | "pl": "Polski", 29 | "pt-br": "Português (Brasil)", 30 | "pt-pt": "Português", 31 | "ro": "Română", 32 | "ru": "Русский", 33 | "sv": "Svenska", 34 | "tr": "Türkçe", 35 | "zh-cn": "中文(简体)", 36 | "zh-tw": "繁體中文" 37 | }[lang]; 38 | } 39 | }); 40 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/language/language.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .factory('Language', function ($q, $http, $translate, LANGUAGES) { 5 | return { 6 | getCurrent: function () { 7 | var deferred = $q.defer(); 8 | var language = $translate.storage().get('NG_TRANSLATE_LANG_KEY'); 9 | 10 | if (angular.isUndefined(language)) { 11 | language = 'en'; 12 | } 13 | 14 | deferred.resolve(language); 15 | return deferred.promise; 16 | }, 17 | getAll: function () { 18 | var deferred = $q.defer(); 19 | deferred.resolve(LANGUAGES); 20 | return deferred.promise; 21 | } 22 | }; 23 | }) 24 | 25 | /* 26 | Languages codes are ISO_639-1 codes, see http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes 27 | They are written in English to avoid character encoding issues (not a perfect solution) 28 | */ 29 | .constant('LANGUAGES', [ 30 | 'en' 31 | // TODO add new languages here 32 | ] 33 | ); 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/navbar/navbar.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .controller('NavbarController', function ($scope, $location, $state, Auth, Principal, ENV) { 5 | $scope.isAuthenticated = Principal.isAuthenticated; 6 | $scope.$state = $state; 7 | $scope.inProduction = ENV === 'prod'; 8 | 9 | $scope.logout = function () { 10 | Auth.logout(); 11 | $state.go('home'); 12 | }; 13 | }); 14 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/tracker/tracker.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .factory('Tracker', function ($rootScope, $cookies, $http, $q, Websocket) { 5 | var subscriber = null; 6 | var listener = $q.defer(); 7 | 8 | function sendActivity() { 9 | Websocket.send('/topic/activity', {'page': $rootScope.toState.name}); 10 | } 11 | 12 | return { 13 | subscribe: function () { 14 | Websocket.subscribe("/topic/tracker", function (data) { 15 | listener.notify(JSON.parse(data.body)); 16 | }).then(function (result) { 17 | subscriber = result; 18 | }); 19 | }, 20 | unsubscribe: function () { 21 | Websocket.unsubscribe(subscriber); 22 | }, 23 | receive: function () { 24 | return listener.promise; 25 | }, 26 | sendActivity: function () { 27 | sendActivity(); 28 | } 29 | }; 30 | }); 31 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/user/user.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .factory('User', function ($resource) { 5 | return $resource('api/users/:login', {}, { 6 | 'query': {method: 'GET', isArray: true}, 7 | 'get': { 8 | method: 'GET', 9 | transformResponse: function (data) { 10 | data = angular.fromJson(data); 11 | return data; 12 | } 13 | }, 14 | 'update': { method:'PUT' } 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/util/capitalize.filter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .filter('capitalize', function () { 5 | return function (input, scope) { 6 | if (input != null) 7 | input = input.toLowerCase(); 8 | return input.substring(0, 1).toUpperCase() + input.substring(1); 9 | } 10 | }); 11 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/util/datetime.filter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .filter('datetime', ['$filter', function($filter) { 5 | return function(input) { 6 | return $filter('date')(input, 'yyyy-MM-dd HH:mm:ss'); 7 | } 8 | }]) 9 | .filter('time', ['$filter', function($filter) { 10 | return function(input) { 11 | return $filter('date')(input, 'HH:mm:ss'); 12 | } 13 | }]); -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/util/dateutil.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .service('DateUtils', function ($filter) { 5 | this.convertLocaleDateToServer = function(date) { 6 | if (date) { 7 | return $filter('date')(date, 'yyyy-MM-dd'); 8 | } else { 9 | return null; 10 | } 11 | }; 12 | this.convertLocaleDateFromServer = function(date) { 13 | if (date) { 14 | var dateString = date.split("-"); 15 | return new Date(dateString[0], dateString[1] - 1, dateString[2]); 16 | } 17 | return null; 18 | }; 19 | this.convertDateTimeFromServer = function(date) { 20 | if (date) { 21 | return new Date(date); 22 | } else { 23 | return null; 24 | } 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/util/parse-links.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp') 4 | .service('ParseLinks', function () { 5 | this.parse = function (header) { 6 | if (header.length == 0) { 7 | throw new Error("input must not be of zero length"); 8 | } 9 | 10 | // Split parts by comma 11 | var parts = header.split(','); 12 | var links = {}; 13 | // Parse each part into a named link 14 | angular.forEach(parts, function (p) { 15 | var section = p.split(';'); 16 | if (section.length != 2) { 17 | throw new Error("section could not be split on ';'"); 18 | } 19 | var url = section[0].replace(/<(.*)>/, '$1').trim(); 20 | var queryString = {}; 21 | url.replace( 22 | new RegExp("([^?=&]+)(=([^&]*))?", "g"), 23 | function($0, $1, $2, $3) { queryString[$1] = $3; } 24 | ); 25 | var page = queryString['page']; 26 | if( angular.isString(page) ) { 27 | page = parseInt(page); 28 | } 29 | var name = section[1].replace(/rel="(.*)"/, '$1').trim(); 30 | links[name] = page; 31 | }); 32 | 33 | return links; 34 | } 35 | }); 36 | -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/scripts/components/util/remember.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('mtpApp').factory('RememberService', ['$location', function($location) { 4 | 5 | var stateStore = {}; 6 | 7 | return { 8 | addState: function(state) { 9 | var current = $location.path(); 10 | stateStore[current] = state; 11 | }, 12 | getState: function() { 13 | var current = $location.path(); 14 | return stateStore[current]; 15 | } 16 | }; 17 | }]); -------------------------------------------------------------------------------- /mtp-frontend/src/main/webapp/swagger-ui/images/throbber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpugar/mtp/f9f4f811d422357d0dc19df016c5835017a3ee2f/mtp-frontend/src/main/webapp/swagger-ui/images/throbber.gif -------------------------------------------------------------------------------- /mtp-model/src/main/cql/create_keyspace.cql: -------------------------------------------------------------------------------- 1 | 2 | -- NOTE: uses replication factor 1 3 | CREATE KEYSPACE IF NOT EXISTS mtp 4 | WITH REPLICATION = { 5 | 'class' : 'SimpleStrategy', 6 | 'replication_factor': 1 7 | }; -------------------------------------------------------------------------------- /mtp-model/src/main/cql/create_keyspace_prod.cql: -------------------------------------------------------------------------------- 1 | 2 | -- NOTE: uses replication factor 3 3 | CREATE KEYSPACE IF NOT EXISTS mtp 4 | WITH REPLICATION = { 5 | 'class' : 'SimpleStrategy', 6 | 'replication_factor': 3 7 | }; -------------------------------------------------------------------------------- /mtp-model/src/main/java/com/vedri/mtp/core/MtpConstants.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core; 2 | 3 | import org.joda.time.DateTimeZone; 4 | 5 | import java.math.BigDecimal; 6 | import java.text.DecimalFormatSymbols; 7 | import java.util.Locale; 8 | 9 | public interface MtpConstants { 10 | 11 | String TRANSACTION_DATE_FORMAT = "ddMMMyy HH:mm:ss"; 12 | 13 | // profiles 14 | String SPRING_PROFILE_DEVELOPMENT = "dev"; 15 | String SPRING_PROFILE_PRODUCTION = "prod"; 16 | 17 | // configs 18 | String CONFIG_PREFIX = "mtp.core."; 19 | 20 | // points 21 | String CURRENCY_POINTS = "EUR"; 22 | // Max counter value: 9223372036854.775807 23 | int CURRENCY_SCALE = 2; 24 | int RATE_SCALE = 4; 25 | int CURRENCY_POINTS_SCALE = 6; 26 | int CURRENCY_ROUNDING = BigDecimal.ROUND_HALF_UP; 27 | 28 | // number format 29 | String CURRENCY_FORMAT = "#.##"; 30 | String RATE_FORMAT = "#.####"; 31 | DecimalFormatSymbols DEFAULT_FORMAT_SYMBOLS = DecimalFormatSymbols.getInstance(Locale.ENGLISH); 32 | 33 | // date 34 | DateTimeZone DEFAULT_TIME_ZONE = DateTimeZone.UTC; 35 | } 36 | -------------------------------------------------------------------------------- /mtp-model/src/main/java/com/vedri/mtp/core/country/CountryManager.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.country; 2 | 3 | import java.util.List; 4 | import java.util.Set; 5 | 6 | public interface CountryManager { 7 | 8 | Country getCountryFromCca2(String cca2); 9 | 10 | Set getCountriesFromCurrency(String currency); 11 | 12 | List getCountries(); 13 | } 14 | -------------------------------------------------------------------------------- /mtp-model/src/main/java/com/vedri/mtp/core/country/dao/CountryDao.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.country.dao; 2 | 3 | import java.util.List; 4 | import java.util.Set; 5 | 6 | import com.vedri.mtp.core.country.Country; 7 | 8 | public interface CountryDao { 9 | 10 | List loadAll(); 11 | } 12 | -------------------------------------------------------------------------------- /mtp-model/src/main/java/com/vedri/mtp/core/currency/CacheCurrencyManager.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.currency; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | import java.util.stream.Collectors; 6 | 7 | import javax.annotation.PostConstruct; 8 | 9 | import lombok.extern.slf4j.Slf4j; 10 | 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Service; 13 | 14 | import com.vedri.mtp.core.country.CountryManager; 15 | 16 | @Service 17 | @Slf4j 18 | public class CacheCurrencyManager implements CurrencyManager { 19 | 20 | private final CountryManager countryManager; 21 | private volatile List currencies = Collections.emptyList(); 22 | 23 | @Autowired 24 | public CacheCurrencyManager(CountryManager countryManager) { 25 | this.countryManager = countryManager; 26 | } 27 | 28 | @PostConstruct 29 | public void init() { 30 | 31 | log.debug("Loading currencies"); 32 | currencies = countryManager.getCountries() 33 | .stream() 34 | .flatMap(country -> country.getCurrencies().stream()) 35 | .distinct() 36 | .collect(Collectors.toList()); 37 | 38 | log.info("Loaded {} currencies", currencies.size()); 39 | } 40 | 41 | @Override 42 | public List getCurrencies() { 43 | return currencies; 44 | } 45 | 46 | @Override 47 | public Currency getCurrencyFromCode(String code) { 48 | return new Currency(code); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /mtp-model/src/main/java/com/vedri/mtp/core/currency/Currency.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.currency; 2 | 3 | import lombok.*; 4 | 5 | import com.google.common.base.Objects; 6 | 7 | @AllArgsConstructor 8 | @NoArgsConstructor 9 | @Getter 10 | @Setter 11 | @ToString 12 | public class Currency { 13 | 14 | private String code; 15 | 16 | @Override 17 | public boolean equals(Object o) { 18 | if (this == o) 19 | return true; 20 | if (o == null || getClass() != o.getClass()) 21 | return false; 22 | Currency currency = (Currency) o; 23 | return Objects.equal(code, currency.code); 24 | } 25 | 26 | @Override 27 | public int hashCode() { 28 | return Objects.hashCode(code); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /mtp-model/src/main/java/com/vedri/mtp/core/currency/CurrencyManager.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.currency; 2 | 3 | import java.util.List; 4 | 5 | public interface CurrencyManager { 6 | 7 | List getCurrencies(); 8 | 9 | Currency getCurrencyFromCode(String code); 10 | } 11 | -------------------------------------------------------------------------------- /mtp-model/src/main/java/com/vedri/mtp/core/rate/NoRateException.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.rate; 2 | 3 | public class NoRateException extends Exception { 4 | 5 | private final Rate.Key key; 6 | 7 | public NoRateException(Rate.Key key) { 8 | this.key = key; 9 | } 10 | 11 | public NoRateException(Rate.Key key, String message) { 12 | super(message); 13 | this.key = key; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /mtp-model/src/main/java/com/vedri/mtp/core/rate/RateCalculator.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.rate; 2 | 3 | import org.joda.time.LocalDate; 4 | 5 | /** 6 | * Gives rate for input currencyFrom. currencyTo for specific date. 7 | */ 8 | public interface RateCalculator { 9 | 10 | default Rate sellRate(String currencyFrom, String currencyTo, LocalDate date) throws NoRateException { 11 | final Rate.Key key = new Rate.Key(currencyFrom, currencyTo, date); 12 | return sellRate(key); 13 | } 14 | 15 | Rate sellRate(final Rate.Key key) throws NoRateException; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /mtp-model/src/main/java/com/vedri/mtp/core/rate/dao/RateDao.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.rate.dao; 2 | 3 | import com.vedri.mtp.core.rate.Rate; 4 | 5 | /** 6 | * Rate DAO. 7 | */ 8 | public interface RateDao { 9 | 10 | Rate save(final Rate rate); 11 | 12 | Rate load(final Rate.Key key); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /mtp-model/src/main/java/com/vedri/mtp/core/support/cassandra/CassandraUtils.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.support.cassandra; 2 | 3 | import java.security.SecureRandom; 4 | import java.util.UUID; 5 | 6 | import org.joda.time.DateTime; 7 | 8 | public class CassandraUtils { 9 | 10 | private static final long NUM_100NS_INTERVALS_SINCE_UUID_EPOCH = 0x01b21dd213814000L; 11 | 12 | private static SecureRandom random = new SecureRandom(); 13 | 14 | public static UUID createUUIDFromMillis(long millis) { 15 | return new UUID(makeMSB(millis), random.nextLong()); 16 | } 17 | 18 | public static DateTime getDateTime(String uuidString) { 19 | UUID uuid = UUID.fromString(uuidString); 20 | return new DateTime(getTimeFromUUID(uuid)); 21 | } 22 | 23 | public static long getTimeFromUUID(UUID uuid) { 24 | return (uuid.timestamp() - NUM_100NS_INTERVALS_SINCE_UUID_EPOCH) / 10000; 25 | } 26 | 27 | private static long makeMSB(long currentTime) { 28 | long time; 29 | 30 | // UTC time 31 | long timeToUse = (currentTime * 10000) + NUM_100NS_INTERVALS_SINCE_UUID_EPOCH; 32 | 33 | // time low 34 | time = timeToUse << 32; 35 | 36 | // time mid 37 | time |= (timeToUse & 0xFFFF00000000L) >> 16; 38 | 39 | // time hi and version 40 | time |= 0x1000 | ((timeToUse >> 48) & 0x0FFF); // version 1 41 | return time; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /mtp-model/src/main/java/com/vedri/mtp/core/support/cassandra/ColumnUtils.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.support.cassandra; 2 | 3 | import com.google.common.base.CaseFormat; 4 | 5 | public class ColumnUtils { 6 | 7 | public static String getColumnName(String cammelCaseName) { 8 | return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, cammelCaseName); 9 | } 10 | 11 | public static > Field createField(Enum val) { 12 | return new Field<>(val); 13 | } 14 | 15 | public static class Field> { 16 | 17 | private final Enum enumVal; 18 | private final String underscore; 19 | 20 | public Field(Enum enumVal) { 21 | this.enumVal = enumVal; 22 | this.underscore = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, enumVal.name()); 23 | } 24 | 25 | public final String underscore() { 26 | return underscore; 27 | } 28 | 29 | public final String cammelCase() { 30 | return enumVal.name(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /mtp-model/src/main/java/com/vedri/mtp/core/transaction/TableName.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.transaction; 2 | 3 | public interface TableName { 4 | 5 | String TRANSACTION = "transaction"; 6 | 7 | String RT_AGGREGATION_BY_VALIDATION_STATUS = "rt_aggregation_by_validation_status"; 8 | String RT_AGGREGATION_BY_ORIGINATING_COUNTRY = "rt_aggregation_by_originating_country"; 9 | String RT_DAY_AGGREGATION_BY_ORIGINATING_COUNTRY = "rt_day_aggregation_by_originating_country"; 10 | String RT_AGGREGATION_BY_USER = "rt_aggregation_by_user"; 11 | String RT_AGGREGATION_BY_CURRENCY = "rt_aggregation_by_currency"; 12 | String RT_DAY_AGGREGATION_BY_CURRENCY = "rt_day_aggregation_by_currency"; 13 | 14 | String PT_AGGREGATION_BY_ORIGINATING_COUNTRY = "pt_aggregation_by_originating_country"; 15 | String PT_DAY_AGGREGATION_BY_ORIGINATING_COUNTRY = "pt_day_aggregation_by_originating_country"; 16 | String PT_AGGREGATION_BY_USER = "pt_aggregation_by_user"; 17 | String PT_AGGREGATION_BY_CURRENCY = "pt_aggregation_by_currency"; 18 | String PT_DAY_AGGREGATION_BY_CURRENCY = "pt_day_aggregation_by_currency"; 19 | } 20 | -------------------------------------------------------------------------------- /mtp-model/src/main/java/com/vedri/mtp/core/transaction/aggregation/TimeAggregation.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.transaction.aggregation; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import lombok.*; 5 | 6 | import com.vedri.mtp.core.support.cassandra.ColumnUtils; 7 | 8 | @AllArgsConstructor 9 | @NoArgsConstructor 10 | @Getter 11 | @Setter 12 | @ToString 13 | public abstract class TimeAggregation { 14 | 15 | @JsonIgnore 16 | private YearToHourTime yearToHourTime; 17 | 18 | public enum TimeFields { 19 | year, month, day, hour; 20 | 21 | public final ColumnUtils.Field F = ColumnUtils.createField(this); 22 | } 23 | 24 | public Integer getYear() { 25 | return yearToHourTime.getYear(); 26 | } 27 | 28 | public Integer getMonth() { 29 | return yearToHourTime.getMonth(); 30 | } 31 | 32 | public Integer getDay() { 33 | return yearToHourTime.getDay(); 34 | } 35 | 36 | public Integer getHour() { 37 | return yearToHourTime.getHour(); 38 | } 39 | 40 | public void setYear(Integer year) { 41 | yearToHourTime.setYear(year); 42 | } 43 | 44 | public void setMonth(Integer month) { 45 | yearToHourTime.setMonth(month); 46 | } 47 | 48 | public void setDay(Integer day) { 49 | yearToHourTime.setDay(day); 50 | } 51 | 52 | public void setHour(Integer hour) { 53 | yearToHourTime.setHour(hour); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /mtp-model/src/main/java/com/vedri/mtp/core/transaction/aggregation/TransactionValidationStatus.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.transaction.aggregation; 2 | 3 | public enum TransactionValidationStatus { 4 | OK, InvalidAmount, InvalidRate, InvalidCountry, InvalidFromCurrency, InvalidToCurrency, InvalidValidation, 5 | } 6 | -------------------------------------------------------------------------------- /mtp-model/src/main/java/com/vedri/mtp/core/transaction/aggregation/YearToHourTime.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.transaction.aggregation; 2 | 3 | import com.vedri.mtp.core.MtpConstants; 4 | import lombok.*; 5 | import org.joda.time.DateTime; 6 | import org.joda.time.DateTimeZone; 7 | 8 | import java.io.Serializable; 9 | 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | @Getter 13 | @Setter 14 | @ToString 15 | public class YearToHourTime implements Serializable { 16 | 17 | private Integer year; 18 | private Integer month; 19 | private Integer day; 20 | private Integer hour; 21 | 22 | public YearToHourTime(DateTime dateTime) { 23 | dateTime = dateTime.withZone(MtpConstants.DEFAULT_TIME_ZONE); 24 | year = dateTime.getYear(); 25 | month = dateTime.getMonthOfYear(); 26 | day = dateTime.getDayOfMonth(); 27 | hour = dateTime.getHourOfDay(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /mtp-model/src/main/java/com/vedri/mtp/core/transaction/aggregation/dao/YearToHourTimeUtil.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.transaction.aggregation.dao; 2 | 3 | import java.util.List; 4 | 5 | import com.google.common.base.Strings; 6 | import com.vedri.mtp.core.transaction.aggregation.YearToHourTime; 7 | 8 | public class YearToHourTimeUtil { 9 | 10 | private static final String YEAR_PART = " and year = ?"; 11 | private static final String MONTH_PART = " and month = ?"; 12 | private static final String DAY_PART = " and day = ?"; 13 | private static final String HOUR_PART = " and hour = ?"; 14 | 15 | public static String composeQuery(YearToHourTime yearToHourTime, String prefixQuery, List args) { 16 | 17 | StringBuilder stringBuilder = new StringBuilder(prefixQuery); 18 | 19 | if (yearToHourTime.getYear() != null) { 20 | stringBuilder.append(YEAR_PART); 21 | args.add(yearToHourTime.getYear()); 22 | } 23 | if (yearToHourTime.getMonth() != null) { 24 | stringBuilder.append(MONTH_PART); 25 | args.add(yearToHourTime.getMonth()); 26 | } 27 | if (yearToHourTime.getDay() != null) { 28 | stringBuilder.append(DAY_PART); 29 | args.add(yearToHourTime.getDay()); 30 | } 31 | if (yearToHourTime.getHour() != null) { 32 | stringBuilder.append(HOUR_PART); 33 | args.add(yearToHourTime.getHour()); 34 | } 35 | 36 | if (Strings.isNullOrEmpty(prefixQuery) && stringBuilder.length() > 4) { 37 | stringBuilder.delete(0, 4); 38 | } 39 | 40 | return stringBuilder.toString(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /mtp-model/src/main/java/com/vedri/mtp/core/transaction/dao/TransactionDao.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.core.transaction.dao; 2 | 3 | import java.util.List; 4 | import java.util.UUID; 5 | 6 | import org.joda.time.DateTime; 7 | 8 | import com.vedri.mtp.core.transaction.Transaction; 9 | 10 | public interface TransactionDao { 11 | 12 | Transaction save(final Transaction transaction); 13 | 14 | Transaction load(final String transactionId); 15 | 16 | Transaction load(final UUID timeUUID); 17 | 18 | List loadAll(DateTime timeReceivedFrom, DateTime timeReceivedTo, 19 | String filterUserId, String filterCurrencyFrom, String filterCurrencyTo, String filterOriginatingCountry, 20 | int filterOffset, int filterPageSize); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /mtp-model/src/main/spark/countries/countries.json.txt: -------------------------------------------------------------------------------- 1 | Source: https://github.com/mledoze/countries 2 | License: https://github.com/mledoze/countries/blob/master/LICENSE 3 | -------------------------------------------------------------------------------- /mtp-model/src/main/spark/countries/import_countries.script.scala: -------------------------------------------------------------------------------- 1 | 2 | // 10.1.6.217 3 | //val conf = new SparkConf(true) 4 | //conf.set("spark.cassandra.connection.host", "10.1.6.217") 5 | //val sc = new SparkContext(conf) 6 | //val sqlContext = new SQLContext(sc) 7 | 8 | 9 | /** 10 | * run command line and then copy paste in console: /opt/programs/spark-1.5.1-bin-hadoop2.6/bin/spark-shell --packages com.datastax.spark:spark-cassandra-connector_2.10:1.5.0-M2 --conf spark.cassandra.connection.host=10.1.6.217 11 | */ 12 | 13 | val countries = sqlContext.read.json("./*.json") 14 | val countryData = countries.selectExpr("name.common as common_name", "name.official as official_name", "cca2", "cca3", "currency as currencies") 15 | countryData.printSchema() 16 | 17 | val filteredCountryData = countryData.filter("cca2 is not null") 18 | filteredCountryData.write.format("org.apache.spark.sql.cassandra").options(Map("keyspace" -> "mtp", "table" -> "country")).save() 19 | 20 | //countryData.filter("cca2 is null").foreach(println) 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /mtp-processor/src/main/conf/spring-application.yml.template: -------------------------------------------------------------------------------- 1 | mtp: 2 | processor: 3 | cassandra: 4 | hosts: localhost 5 | spark: 6 | master: local[10] 7 | cassandraHosts: localhost 8 | batchInterval: 10000 9 | checkpointDir: /tmp 10 | kafkaServer: 11 | host: localhost 12 | advertisedHost: localhost 13 | zookeeper: 14 | connect: localhost:2181 15 | cfRate: 16 | serviceUrl: http://localhost:8080/test/rates 17 | -------------------------------------------------------------------------------- /mtp-processor/src/main/java/com/vedri/mtp/processor/MtpProcessorConstants.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.processor; 2 | 3 | 4 | public interface MtpProcessorConstants { 5 | 6 | String NAME = "MtpProcessor"; 7 | String CONFIG_PREFIX = "mtp.processor"; 8 | 9 | } 10 | -------------------------------------------------------------------------------- /mtp-processor/src/main/java/com/vedri/mtp/processor/ProcessorApplication.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.processor; 2 | 3 | import com.vedri.mtp.core.rate.cf.CfRateCalculator; 4 | import com.vedri.mtp.core.support.cassandra.CassandraConfiguration; 5 | import com.vedri.mtp.core.support.json.JacksonConfiguration; 6 | import com.vedri.mtp.core.support.kyro.KryoConfiguration; 7 | import com.vedri.mtp.processor.support.kafka.KafkaConfiguration; 8 | import com.vedri.mtp.processor.support.spark.SparkConfiguration; 9 | import org.springframework.context.ApplicationContext; 10 | 11 | import com.vedri.mtp.core.CoreConfig; 12 | import com.vedri.mtp.core.support.spring.AbstractApplication; 13 | 14 | public class ProcessorApplication extends AbstractApplication { 15 | 16 | @Override 17 | protected Class[] getConfigs() { 18 | return new Class[] { 19 | CoreConfig.class, ProcessorConfig.class, JacksonConfiguration.class, CassandraConfiguration.class, 20 | KafkaConfiguration.class, SparkConfiguration.class, KryoConfiguration.class 21 | }; 22 | } 23 | 24 | @Override 25 | protected void doStart(ApplicationContext context) throws Exception { 26 | 27 | final CfRateCalculator cfRateCalculator = context.getBean(CfRateCalculator.class); 28 | final ProcessorProperties processorProperties = context.getBean(ProcessorProperties.class); 29 | 30 | cfRateCalculator.setServiceUrl(processorProperties.getCfRate().getServiceUrl()); 31 | } 32 | 33 | public static void main(String[] args) throws Exception { 34 | new ProcessorApplication().startApplication(args); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /mtp-processor/src/main/java/com/vedri/mtp/processor/ProcessorConfig.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.processor; 2 | 3 | import com.vedri.mtp.core.CoreProperties; 4 | import com.vedri.mtp.processor.support.spark.CoreSparkProperties; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.ComponentScan; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | @ComponentScan(basePackages = { 12 | "com.vedri.mtp.core.country", 13 | "com.vedri.mtp.core.transaction", 14 | "com.vedri.mtp.core.rate", 15 | "com.vedri.mtp.core.support.serializer", 16 | "com.vedri.mtp.processor.streaming", 17 | "com.vedri.mtp.processor.transaction" 18 | }) 19 | @Configuration 20 | @EnableConfigurationProperties({ ProcessorProperties.class }) 21 | public class ProcessorConfig { 22 | 23 | @Autowired 24 | private ProcessorProperties processorProperties; 25 | 26 | @Bean 27 | public CoreProperties.Cluster clusterProperties() { 28 | return processorProperties.getCluster(); 29 | } 30 | 31 | @Bean 32 | public CoreProperties.Akka akkaProperties() { 33 | return processorProperties.getAkka(); 34 | } 35 | 36 | @Bean 37 | public CoreProperties.Cassandra cassandraProperties() { 38 | return processorProperties.getCassandra(); 39 | } 40 | 41 | @Bean 42 | public CoreSparkProperties.Spark spark() { 43 | return processorProperties.getSpark(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /mtp-processor/src/main/java/com/vedri/mtp/processor/streaming/ProcessorAkkaConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.processor.streaming; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | 5 | import akka.actor.ActorRef; 6 | import akka.actor.ActorSystem; 7 | 8 | import com.vedri.mtp.core.support.akka.ClusterAkkaConfiguration; 9 | import com.vedri.mtp.core.support.akka.SpringExtension; 10 | 11 | @Configuration 12 | public class ProcessorAkkaConfiguration extends ClusterAkkaConfiguration { 13 | 14 | @Override 15 | protected void doCreateClusteredRootActors(ActorSystem system, SpringExtension.SpringExt springExt) { 16 | super.doCreateClusteredRootActors(system, springExt); 17 | 18 | ActorRef clusterRootActor = system.actorOf(springExt.props(ProcessorRootActor.NAME), ProcessorRootActor.NAME); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /mtp-processor/src/main/java/com/vedri/mtp/processor/streaming/handler/CreateStreamBuilder.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.processor.streaming.handler; 2 | 3 | import java.util.Map; 4 | 5 | import kafka.serializer.DefaultDecoder; 6 | import kafka.serializer.StringDecoder; 7 | 8 | import org.apache.spark.storage.StorageLevel; 9 | import org.apache.spark.streaming.api.java.JavaPairInputDStream; 10 | import org.apache.spark.streaming.api.java.JavaStreamingContext; 11 | import org.apache.spark.streaming.kafka.KafkaUtils; 12 | 13 | import com.google.common.collect.Maps; 14 | 15 | public class CreateStreamBuilder extends StreamBuilder> { 16 | 17 | private static JavaStreamingContext streamingContext; 18 | private String topicName; 19 | private Map kafkaParams; 20 | 21 | public CreateStreamBuilder(String topicName, 22 | JavaStreamingContext streamingContext, Map kafkaParams) { 23 | super(null); 24 | this.streamingContext = streamingContext; 25 | this.topicName = topicName; 26 | this.kafkaParams = kafkaParams; 27 | } 28 | 29 | @Override 30 | protected JavaPairInputDStream doBuild(NoIO input) { 31 | 32 | final Map topics = Maps.newHashMap(); 33 | topics.put(topicName, 1); 34 | 35 | // String, String, StringDecoder, StringDecoder 36 | return KafkaUtils.createStream(streamingContext, 37 | String.class, byte[].class, StringDecoder.class, DefaultDecoder.class, 38 | kafkaParams, topics, StorageLevel.MEMORY_AND_DISK()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /mtp-processor/src/main/java/com/vedri/mtp/processor/streaming/handler/CreateTransactionBuilder.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.processor.streaming.handler; 2 | 3 | import kafka.serializer.Decoder; 4 | 5 | import org.apache.spark.streaming.api.java.JavaPairDStream; 6 | import org.apache.spark.streaming.api.java.JavaPairInputDStream; 7 | 8 | import com.vedri.mtp.core.transaction.Transaction; 9 | 10 | public class CreateTransactionBuilder 11 | extends StreamBuilder, JavaPairDStream> { 12 | 13 | private static Decoder decoder; 14 | 15 | public CreateTransactionBuilder(StreamBuilder> prevBuilder, 16 | final Decoder decoder) { 17 | super(prevBuilder); 18 | this.decoder = decoder; 19 | } 20 | 21 | @Override 22 | protected JavaPairDStream doBuild(JavaPairInputDStream stream) { 23 | 24 | // create transaction 25 | return stream.mapValues(data -> decoder.fromBytes(data)); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /mtp-processor/src/main/java/com/vedri/mtp/processor/streaming/handler/FilterOkTransactionStatusBuilder.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.processor.streaming.handler; 2 | 3 | import org.apache.spark.streaming.api.java.JavaDStream; 4 | 5 | import com.vedri.mtp.core.transaction.Transaction; 6 | import com.vedri.mtp.core.transaction.aggregation.TransactionValidationStatus; 7 | 8 | public class FilterOkTransactionStatusBuilder 9 | extends StreamBuilder, JavaDStream> { 10 | 11 | public FilterOkTransactionStatusBuilder(StreamBuilder> prevBuilder) { 12 | super(prevBuilder); 13 | } 14 | 15 | @Override 16 | protected JavaDStream doBuild(JavaDStream input) { 17 | final JavaDStream stream = 18 | input.filter(transaction -> transaction.getValidationStatus() == TransactionValidationStatus.OK); 19 | return stream.cache(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /mtp-processor/src/main/java/com/vedri/mtp/processor/streaming/handler/NoIO.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.processor.streaming.handler; 2 | 3 | import java.io.Serializable; 4 | 5 | public class NoIO implements Serializable { 6 | 7 | private NoIO() { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /mtp-processor/src/main/java/com/vedri/mtp/processor/streaming/handler/PlacedDayTransactionAggregationByCountryBuilder.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.processor.streaming.handler; 2 | 3 | import org.apache.spark.api.java.function.Function; 4 | import org.apache.spark.streaming.api.java.JavaDStream; 5 | import org.joda.time.DateTime; 6 | 7 | import com.vedri.mtp.core.MtpConstants; 8 | import com.vedri.mtp.core.transaction.TableName; 9 | import com.vedri.mtp.core.transaction.Transaction; 10 | import com.vedri.mtp.core.transaction.aggregation.TransactionAggregationByCountry; 11 | 12 | public class PlacedDayTransactionAggregationByCountryBuilder 13 | extends TimeTransactionAggregationByCountryBuilderTemplate { 14 | 15 | public PlacedDayTransactionAggregationByCountryBuilder( 16 | StreamBuilder> prevBuilder, String keyspace) { 17 | super(prevBuilder, keyspace, TableName.PT_DAY_AGGREGATION_BY_ORIGINATING_COUNTRY); 18 | } 19 | 20 | @Override 21 | protected Function mapFunction() { 22 | return transaction -> { 23 | final DateTime time = transaction 24 | .getPlacedTime() 25 | .withZone(MtpConstants.DEFAULT_TIME_ZONE); 26 | return new TransactionAggregationByCountry(transaction.getOriginatingCountry(), 27 | time.getYear(), time.getMonthOfYear(), 28 | time.getDayOfMonth(), null, 1, transaction.getAmountPoints()); 29 | }; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /mtp-processor/src/main/java/com/vedri/mtp/processor/streaming/handler/PlacedTimeTransactionAggregationByCountryBuilder.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.processor.streaming.handler; 2 | 3 | import com.vedri.mtp.core.MtpConstants; 4 | import com.vedri.mtp.core.transaction.TableName; 5 | import org.apache.spark.api.java.function.Function; 6 | import org.apache.spark.streaming.api.java.JavaDStream; 7 | import org.joda.time.DateTime; 8 | 9 | import com.vedri.mtp.core.transaction.Transaction; 10 | import com.vedri.mtp.core.transaction.aggregation.TransactionAggregationByCountry; 11 | 12 | public class PlacedTimeTransactionAggregationByCountryBuilder 13 | extends TimeTransactionAggregationByCountryBuilderTemplate { 14 | 15 | public PlacedTimeTransactionAggregationByCountryBuilder( 16 | StreamBuilder> prevBuilder, String keyspace) { 17 | super(prevBuilder, keyspace, TableName.PT_AGGREGATION_BY_ORIGINATING_COUNTRY); 18 | } 19 | 20 | @Override 21 | protected Function mapFunction() { 22 | return transaction -> { 23 | final DateTime time = transaction.getPlacedTime().withZone(MtpConstants.DEFAULT_TIME_ZONE); 24 | return new TransactionAggregationByCountry(transaction.getOriginatingCountry(), 25 | time.getYear(), time.getMonthOfYear(), 26 | time.getDayOfMonth(), time.getHourOfDay(), 1, transaction.getAmountPoints()); 27 | }; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /mtp-processor/src/main/java/com/vedri/mtp/processor/streaming/handler/PlacedTimeTransactionAggregationByUserBuilder.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.processor.streaming.handler; 2 | 3 | import com.vedri.mtp.core.MtpConstants; 4 | import com.vedri.mtp.core.transaction.TableName; 5 | import org.apache.spark.api.java.function.Function; 6 | import org.apache.spark.streaming.api.java.JavaDStream; 7 | import org.joda.time.DateTime; 8 | 9 | import com.vedri.mtp.core.transaction.Transaction; 10 | import com.vedri.mtp.core.transaction.aggregation.TransactionAggregationByUser; 11 | 12 | public class PlacedTimeTransactionAggregationByUserBuilder extends TimeTransactionAggregationByUserBuilderTemplate { 13 | 14 | public PlacedTimeTransactionAggregationByUserBuilder(StreamBuilder> prevBuilder, 15 | String keyspace) { 16 | super(prevBuilder, keyspace, TableName.PT_AGGREGATION_BY_USER); 17 | } 18 | 19 | @Override 20 | protected Function mapFunction() { 21 | return transaction -> { 22 | final DateTime time = transaction.getPlacedTime().withZone(MtpConstants.DEFAULT_TIME_ZONE); 23 | return new TransactionAggregationByUser(transaction.getUserId(), 24 | time.getYear(), time.getMonthOfYear(), 25 | time.getDayOfMonth(), time.getHourOfDay(), 1, transaction.getAmountPoints()); 26 | }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /mtp-processor/src/main/java/com/vedri/mtp/processor/streaming/handler/ReceivedDayTransactionAggregationByCountryBuilder.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.processor.streaming.handler; 2 | 3 | import com.vedri.mtp.core.MtpConstants; 4 | import com.vedri.mtp.core.transaction.TableName; 5 | import com.vedri.mtp.core.transaction.Transaction; 6 | import com.vedri.mtp.core.transaction.aggregation.TransactionAggregationByCountry; 7 | import org.apache.spark.api.java.function.Function; 8 | import org.apache.spark.streaming.api.java.JavaDStream; 9 | import org.joda.time.DateTime; 10 | 11 | public class ReceivedDayTransactionAggregationByCountryBuilder 12 | extends TimeTransactionAggregationByCountryBuilderTemplate { 13 | 14 | public ReceivedDayTransactionAggregationByCountryBuilder( 15 | StreamBuilder> prevBuilder, String keyspace) { 16 | super(prevBuilder, keyspace, TableName.RT_DAY_AGGREGATION_BY_ORIGINATING_COUNTRY); 17 | } 18 | 19 | @Override 20 | protected Function mapFunction() { 21 | return transaction -> { 22 | final DateTime time = transaction 23 | .getReceivedTime() 24 | .withZone(MtpConstants.DEFAULT_TIME_ZONE); 25 | return new TransactionAggregationByCountry(transaction.getOriginatingCountry(), 26 | time.getYear(), time.getMonthOfYear(), 27 | time.getDayOfMonth(), null, 1, transaction.getAmountPoints()); 28 | }; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /mtp-processor/src/main/java/com/vedri/mtp/processor/streaming/handler/ReceivedTimeTransactionAggregationByCountryBuilder.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.processor.streaming.handler; 2 | 3 | import com.vedri.mtp.core.MtpConstants; 4 | import com.vedri.mtp.core.transaction.TableName; 5 | import org.apache.spark.api.java.function.Function; 6 | import org.apache.spark.streaming.api.java.JavaDStream; 7 | import org.joda.time.DateTime; 8 | 9 | import com.vedri.mtp.core.transaction.Transaction; 10 | import com.vedri.mtp.core.transaction.aggregation.TransactionAggregationByCountry; 11 | 12 | public class ReceivedTimeTransactionAggregationByCountryBuilder 13 | extends TimeTransactionAggregationByCountryBuilderTemplate { 14 | 15 | public ReceivedTimeTransactionAggregationByCountryBuilder( 16 | StreamBuilder> prevBuilder, String keyspace) { 17 | super(prevBuilder, keyspace, TableName.RT_AGGREGATION_BY_ORIGINATING_COUNTRY); 18 | } 19 | 20 | @Override 21 | protected Function mapFunction() { 22 | return transaction -> { 23 | final DateTime time = transaction 24 | .getReceivedTime() 25 | .withZone(MtpConstants.DEFAULT_TIME_ZONE); 26 | return new TransactionAggregationByCountry(transaction.getOriginatingCountry(), 27 | time.getYear(), time.getMonthOfYear(), 28 | time.getDayOfMonth(), time.getHourOfDay(), 1, transaction.getAmountPoints()); 29 | }; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /mtp-processor/src/main/java/com/vedri/mtp/processor/streaming/handler/ReceivedTimeTransactionAggregationByUserBuilder.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.processor.streaming.handler; 2 | 3 | import com.vedri.mtp.core.MtpConstants; 4 | import com.vedri.mtp.core.transaction.TableName; 5 | import org.apache.spark.api.java.function.Function; 6 | import org.apache.spark.streaming.api.java.JavaDStream; 7 | import org.joda.time.DateTime; 8 | 9 | import com.vedri.mtp.core.transaction.Transaction; 10 | import com.vedri.mtp.core.transaction.aggregation.TransactionAggregationByUser; 11 | 12 | public class ReceivedTimeTransactionAggregationByUserBuilder extends TimeTransactionAggregationByUserBuilderTemplate { 13 | 14 | public ReceivedTimeTransactionAggregationByUserBuilder(StreamBuilder> prevBuilder, 15 | String keyspace) { 16 | super(prevBuilder, keyspace, TableName.RT_AGGREGATION_BY_USER); 17 | } 18 | 19 | @Override 20 | protected Function mapFunction() { 21 | return transaction -> { 22 | final DateTime time = transaction 23 | .getReceivedTime() 24 | .withZone(MtpConstants.DEFAULT_TIME_ZONE); 25 | return new TransactionAggregationByUser(transaction.getUserId(), 26 | time.getYear(), time.getMonthOfYear(), 27 | time.getDayOfMonth(), time.getHourOfDay(), 1, transaction.getAmountPoints()); 28 | }; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /mtp-processor/src/main/java/com/vedri/mtp/processor/streaming/handler/StreamBuilder.java: -------------------------------------------------------------------------------- 1 | package com.vedri.mtp.processor.streaming.handler; 2 | 3 | import lombok.NoArgsConstructor; 4 | 5 | import java.io.Serializable; 6 | 7 | @NoArgsConstructor 8 | public abstract class StreamBuilder implements Serializable { 9 | 10 | private StreamBuilder prevBuilder; 11 | private O result; 12 | 13 | protected StreamBuilder(StreamBuilder prevBuilder) { 14 | this.prevBuilder = prevBuilder; 15 | } 16 | 17 | public O getResult() { 18 | if (result == null) { 19 | build(); 20 | } 21 | return result; 22 | } 23 | 24 | public void build() { 25 | if (prevBuilder != null) { 26 | final I prevBuilderResult = prevBuilder.getResult(); 27 | this.result = doBuild(prevBuilderResult); 28 | } 29 | else { 30 | result = doBuild(null); 31 | } 32 | } 33 | 34 | protected abstract O doBuild(I input); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /mtp-processor/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | 2 | akka { 3 | loglevel = "DEBUG" 4 | loggers = ["akka.event.slf4j.Slf4jLogger"] 5 | logging-filter = "akka.event.slf4j.Slf4jLoggingFilter" 6 | logger-startup-timeout = 60s 7 | log-dead-letters = off 8 | log-dead-letters-during-shutdown = off 9 | 10 | remote { 11 | log-remote-lifecycle-events = off 12 | netty.tcp { 13 | port = 2550 14 | hostname = "127.0.0.1" 15 | } 16 | } 17 | 18 | actor { 19 | provider = "akka.cluster.ClusterActorRefProvider" 20 | 21 | default-dispatcher { 22 | # Throughput for default Dispatcher, set to 1 for as fair as possible 23 | throughput = 10 24 | } 25 | } 26 | 27 | cluster { 28 | log-info = on 29 | seed-nodes = [] 30 | roles = ["analytics"] 31 | gossip-interval = 5s 32 | publish-stats-interval = 10s 33 | auto-down-unreachable-after = 10s 34 | #metrics.enabled=off TODO new metrics ext 35 | metrics.gossip-interval = 10s 36 | metrics.collect-interval = 10s 37 | } 38 | } 39 | 40 | nodeProcessor0 { 41 | akka { 42 | remote { 43 | netty.tcp { 44 | port = 9170 45 | } 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /mtp-processor/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | mtp: 2 | processor: 3 | cluster: 4 | appName: MtpProcessor 5 | nodeName: nodeProcessor0 6 | akka: 7 | akkaSystemName: MtpProcessor 8 | logConfiguration: false 9 | logClusterMetrics: false 10 | cassandra: 11 | hosts: localhost 12 | port: 9042 13 | keyspace: mtp 14 | spark: 15 | master: local[10] 16 | cassandraHosts: localhost 17 | cleanerTtl: 7200 18 | batchInterval: 10000 19 | checkpointDir: /tmp/spark-processor 20 | kafkaServer: 21 | groupId: mtp.group 22 | host: localhost 23 | port: 9092 24 | advertisedHost: localhost 25 | advertisedPort: 9092 26 | topic: 27 | name: mtp.transaction 28 | numPartitions: 1 29 | replicationFactor: 1 30 | zookeeper: 31 | connect: localhost:2181 32 | cfRate: 33 | serviceUrl: http://localhost:8080/test/rates 34 | 35 | # hosts: localhost 36 | # ingest-rate: 1s 37 | # partitioner: 38 | # fqcn: kafka.producer.DefaultPartitioner 39 | # encoder: 40 | # fqcn: kafka.serializer.StringEncoder 41 | # decoder: 42 | # fqcn: kafka.serializer.StringDecoder 43 | # batch: 44 | # send: 45 | # size: 100 46 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include 'mtp-core', 'mtp-core-kafka', 'mtp-core-spark', 'mtp-model', 'mtp-client', 'mtp-consumption', 'mtp-processor', 'mtp-frontend' 2 | --------------------------------------------------------------------------------