├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── benchmark ├── howto.txt ├── url-email-attachment.txt └── url-email.txt ├── build.gradle ├── codetripper.license ├── eclipse-java-google-style.xml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── intellij-java-google-style.xml ├── settings.gradle ├── sonar-project.properties └── src ├── main ├── java │ └── in │ │ └── codetripper │ │ └── communik │ │ ├── CommuniK.java │ │ ├── Constants.java │ │ ├── common │ │ └── CommunikFilter.java │ │ ├── config │ │ ├── CommunikCacheConfig.java │ │ ├── SwaggerConfig.java │ │ └── WebfluxConfig.java │ │ ├── domain │ │ ├── email │ │ │ ├── EmailConfiguration.java │ │ │ ├── EmailController.java │ │ │ ├── EmailDto.java │ │ │ ├── EmailId.java │ │ │ ├── EmailIdValidator.java │ │ │ ├── EmailList.java │ │ │ ├── EmailMapper.java │ │ │ ├── EmailNotifier.java │ │ │ ├── EmailService.java │ │ │ ├── EmailServiceImpl.java │ │ │ ├── SmtpEmailSender.java │ │ │ └── providers │ │ │ │ ├── DummyMailer.java │ │ │ │ ├── Gmail.java │ │ │ │ ├── MailGun.java │ │ │ │ └── SendGrid.java │ │ ├── notification │ │ │ ├── NoOpPersistenceAdapter.java │ │ │ ├── Notification.java │ │ │ ├── NotificationMapper.java │ │ │ ├── NotificationMessage.java │ │ │ ├── NotificationPersistence.java │ │ │ ├── NotificationPersistenceAdapter.java │ │ │ ├── NotificationService.java │ │ │ ├── NotificationStatusResponse.java │ │ │ ├── NotificationStorageResponse.java │ │ │ ├── Notifier.java │ │ │ ├── Status.java │ │ │ └── Type.java │ │ ├── provider │ │ │ ├── Provider.java │ │ │ ├── ProviderMapper.java │ │ │ ├── ProviderPersistence.java │ │ │ ├── ProviderService.java │ │ │ └── adapter │ │ │ │ ├── ProviderFileSystemPersistenceAdapter.java │ │ │ │ └── ProviderMongoPersistenceAdapter.java │ │ ├── sms │ │ │ ├── SmsController.java │ │ │ ├── SmsDto.java │ │ │ ├── SmsId.java │ │ │ ├── SmsMapper.java │ │ │ ├── SmsNotFoundAdvice.java │ │ │ ├── SmsNotFoundException.java │ │ │ ├── SmsNotifier.java │ │ │ ├── SmsResponse.java │ │ │ ├── SmsService.java │ │ │ ├── SmsServiceImpl.java │ │ │ └── providers │ │ │ │ ├── DummyNotifier.java │ │ │ │ ├── gupshup │ │ │ │ └── Gupchup.java │ │ │ │ └── twofactor │ │ │ │ └── TwoFactorSmsNotifier.java │ │ └── template │ │ │ ├── NotificationTemplate.java │ │ │ ├── NotificationTemplateController.java │ │ │ ├── NotificationTemplateDto.java │ │ │ ├── NotificationTemplateLocaleResolver.java │ │ │ ├── NotificationTemplateMapper.java │ │ │ ├── NotificationTemplatePersistence.java │ │ │ ├── NotificationTemplateService.java │ │ │ ├── NotificationTemplateServiceImpl.java │ │ │ └── adapter │ │ │ ├── FileSystemTemplateAdapter.java │ │ │ └── NotificationTemplatePersistenceAdaptor.java │ │ ├── exceptions │ │ ├── CustomErrorAttributes.java │ │ ├── ExceptionConstants.java │ │ ├── InvalidRequestException.java │ │ ├── NotificationNotFoundException.java │ │ ├── NotificationPersistenceException.java │ │ └── NotificationSendFailedException.java │ │ ├── messagegenerator │ │ ├── AttachmentHandler.java │ │ ├── FlyingSaucerPdfGenerator.java │ │ ├── FreeMarkerMessageGenerator.java │ │ ├── HtmlAttachmentHandler.java │ │ ├── MessageGenerationException.java │ │ ├── MessageGenerator.java │ │ ├── PdfAttachmentHandler.java │ │ ├── UrlAttachmentHandler.java │ │ └── UrlResource.java │ │ ├── repository │ │ └── mongo │ │ │ ├── MongoConfig.java │ │ │ ├── NotificationMessageRepoDto.java │ │ │ ├── NotificationMessageRepository.java │ │ │ ├── NotificationTemplateRepoDto.java │ │ │ ├── NotificationTemplateRepository.java │ │ │ ├── ProviderRepoDto.java │ │ │ └── ProviderRepository.java │ │ ├── trace │ │ ├── CommunikTracer.java │ │ ├── MongoTraceListener.java │ │ └── WebClientDecorator.java │ │ └── webhooks │ │ ├── WebhookClient.java │ │ ├── WebhookController.java │ │ └── WebhookHandler.java └── resources │ ├── application.properties │ ├── banner.txt │ ├── providers │ ├── 11001.json │ ├── 11002.json │ ├── 11404.json │ └── 12001.json │ ├── public │ └── error │ │ ├── 404.html │ │ ├── 405.html │ │ └── error.html │ ├── samplerequest.json │ └── templates │ ├── email-test-default.json │ ├── email-test-template-attachment.json │ ├── email-test-template.json │ └── sms-test-template.json └── test ├── java └── in │ └── codetripper │ └── communik │ ├── Integration.java │ ├── Slow.java │ ├── Unit.java │ └── domain │ ├── email │ ├── EmailControllerTest.java │ ├── EmailIntegrationTest.java │ └── EmailSampleRequest.java │ └── sms │ ├── SmsControllerTest.java │ ├── SmsIntegrationTest.java │ └── SmsSampleRequest.java └── resources ├── __files ├── gupchup.json ├── sendgrid.json └── twofactor.json └── mappings └── sample.json /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | /build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | 6 | /temp/ 7 | /src/main/java/in/codetripper/communik/temp/ 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | /out/ 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | 31 | ### VS Code ### 32 | .vscode/ 33 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | sudo: false 4 | 5 | dist: trusty 6 | 7 | cache: 8 | directories: 9 | - "$HOME/.gradle" 10 | 11 | os: 12 | - linux 13 | 14 | jdk: 15 | - openjdk12 16 | 17 | addons: 18 | sonarcloud: 19 | organization: "codetripper-github" 20 | 21 | script: 22 | - ./gradlew sonarqube 23 | 24 | after_success: 25 | - bash <(curl -s https://codecov.io/bash) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CommuniK (pronounced as communique)- reactive api for sending emails/SMS/chat notifications 2 | 3 | [![Build Status](https://travis-ci.org/CodeTripper/communiK.svg?branch=master)](https://travis-ci.org/CodeTripper/communiK) 4 | [![Maintainability](https://api.codeclimate.com/v1/badges/b9b8834a844bed8d2632/maintainability)](https://codeclimate.com/github/CodeTripper/communiK/maintainability) 5 | [![Test Coverage](https://api.codeclimate.com/v1/badges/b9b8834a844bed8d2632/test_coverage)](https://codeclimate.com/github/CodeTripper/communiK/test_coverage) 6 | 7 | 8 | 9 | It's a Store And Forward API built with Springboot/Webflux and MongoDb to send out notifications. 10 | 11 | ## Features: 12 | * Fully reactive 13 | * Centralizes your notification requirements 14 | * Send emails/sms/chats by calling a rest endpoint 15 | * Add your own Email/SMS providers by implementing the Notifier Interface 16 | * Configure blackouts - WIP 17 | * Send bulk notifications 18 | * Can store the notifications to a DB (mongodb/cassandra) before sending it 19 | * Register a notification template and pass dynamic data to it via Freemarker 20 | * Locale aware 21 | * Generate and send pdf attachments(via FlyingSaucer/Openpdf) 22 | * Generate and send html attachments (via Freemarker) 23 | * Download from a url and send attachments 24 | * Retry the failed notifications via multiple fallback providers 25 | * Call webhooks on any failure/success (WIP) 26 | * Audit the notifications. 27 | * Easily extendable, so one can change any part of the system as required 28 | * OpenTracing (Jaegar) integrated 29 | * Prometheus metrics (WIP) 30 | * SendMail/Mailgun for emails 31 | * Twilio (WIP) 32 | 33 | 34 | 35 | ## Why Communik? 36 | 37 | I can easily send all my notifications via Sendgrid/Mailgun, why do I need Communik? 38 | Well some reasons: 39 | 40 | * Take control of your templates. For example, if I need to move to sendgrid from mailgun, I don't need to transfer the templates 41 | * If I need to retry with a different provider, I dont need to worry about replicating the templates 42 | * One API for all your notification needs across your systems. 43 | 44 | ## Getting Started 45 | 46 | Run the springboot application Communik 47 | 48 | ### Optional 49 | 50 | If you want to store the messages, a mongoDb cluster or cassandra is required 51 | 52 | ``` 53 | 54 | ``` 55 | 56 | ### Running 57 | 58 | Go to the root of the project directory build and test the projects 59 | 60 | ``` 61 | $ ./gradlew 62 | ``` 63 | 64 | To run the communik application 65 | 66 | ``` 67 | $ ./gradlew run 68 | ``` 69 | 70 | 71 | ## Running the tests 72 | 73 | ``` 74 | $ ./gradlew test 75 | ``` 76 | 77 | 78 | ## Deployment 79 | 80 | TODO docker 81 | 82 | ## Built With 83 | 84 | * [Springboot](https://github.com/spring-projects/spring-boot) - The microservices framework 85 | * [Reactor](https://github.com/reactor/reactor) - The reactive framework used 86 | * [Gradle](https://github.com/gradle/gradle) - Dependency Management 87 | * [MongoDb](https://rometools.github.io/rome/) - To store the notifications 88 | * [Mapstruct](https://github.com/mapstruct/mapstruct) - To map the POJOs 89 | * [Springfox](https://github.com/springfox/springfox/) - To documentation 90 | * [FlyingSaucer](https://rometools.github.io/rome/) - To generate pdf attachments 91 | * [Lombok](https://github.com/rzwitserloot/lombok) - To do some magic! 92 | * [Freemarker](https://github.com/apache/freemarker) - Template Engine 93 | * [OpenTracing](https://github.com/opentracing) - Distributed Tracing Specs 94 | * [Jaegar](https://github.com/jaegertracing/jaeger) - Distributed Tracing Impl 95 | * [Seige](https://github.com/JoeDog/siege) - To run benchmark 96 | * [Wiremock](https://github.com/tomakehurst/wiremock) - To run mock servers 97 | * [www.ascii-art-generator.org](https://www.ascii-art-generator.org/) - To create the banner 98 | 99 | 100 | 101 | ## Contributing 102 | 103 | Please read [CONTRIBUTING.md](https://gist.github.com/PurpleBooth/b24679402957c63ec426) for details on our code of conduct, and the process for submitting pull requests to us. 104 | 105 | ## Versioning 106 | 107 | We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/your/project/tags). 108 | 109 | ## Authors 110 | 111 | * **Code Tripper** - *Initial work* - [Code Tripper](https://github.com/CodeTripper) 112 | 113 | See also the list of [contributors](https://github.com/your/project/contributors) who participated in this project. 114 | 115 | ## License 116 | 117 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details 118 | 119 | ## Acknowledgments 120 | 121 | * Hat tip to anyone whose code was used 122 | * Inspiration 123 | * etc 124 | 125 | 126 | ## Benchmarking 127 | 128 | Application Config 129 | 1. dummyEmailer running in 200ms delay 130 | 2. Log in INFO mode 131 | 3. Heap size -xmx2G -xms2G 132 | 133 | Running 134 | 1. Use siege - details in seige/howto.txt 135 | 2. JConsole to view threads and Heap 136 | 3. Run with 20 concurrent requests and repeat 10 times 137 | 138 | 139 | Results: 140 | 141 | 1. Transactions - 200 142 | 2. Elapsed Time - 17.2 secs 143 | 3. (Avg) Response Time - 1.63 secs (Lower the better) 144 | 4. Transaction Rate - 11.61 tps (Higher the better) 145 | 5. Threads - 31 (Should be always 31) 146 | 6. Heap - 250 MB 147 | -------------------------------------------------------------------------------- /benchmark/howto.txt: -------------------------------------------------------------------------------- 1 | # should have 31 threads 2 | siege -lbenchmark/benchmark.log -m "after stubbing out io call " -c 20 -r 10 -T "application/json" -f benchmark/url-email.txt -------------------------------------------------------------------------------- /benchmark/url-email-attachment.txt: -------------------------------------------------------------------------------- 1 | http://localhost:8080/email POST { "to" :["test@example.com"], "subject":"subject is good", "templateId":"email-test-template-attachment", "providerName":"dymmyMailer", "body": { "to":"HK", "message": "102000", "salary": 102000, "timestamp":"what timestamp", "mapTest": {"mykey":"myValue","mykey1":"myValue2"} } , "attachment": { "to":"HK", "message": "102000", "salary": 102000, "timestamp":"what timestamp", "mapTest": {"mykey":"myValue","mykey1":"myValue2"} } } -------------------------------------------------------------------------------- /benchmark/url-email.txt: -------------------------------------------------------------------------------- 1 | http://localhost:8080/email POST { "to" :["test@example.com"], "subject":"subject is good", "templateId":"email-test-template", "providerName":"dymmyMailer", "body": { "to":"HK", "message": "102000", "salary": 102000, "timestamp":"what timestamp", "mapTest": {"mykey":"myValue","mykey1":"myValue2"} } } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.1.4.RELEASE' 3 | id 'java' 4 | id 'jacoco' 5 | id 'net.ltgt.apt-idea' version '0.21' 6 | id "com.diffplug.gradle.spotless" version "3.23.0" 7 | id "org.sonarqube" version "2.7" 8 | 9 | } 10 | 11 | apply plugin: 'io.spring.dependency-management' 12 | apply plugin: 'net.ltgt.apt-idea' 13 | 14 | group = 'in.codetripper' 15 | version = '0.0.1-SNAPSHOT' 16 | sourceCompatibility = 12 17 | targetCompatibility = 12 18 | 19 | 20 | repositories { 21 | mavenCentral() 22 | maven { url 'http://oss.jfrog.org/artifactory/oss-snapshot-local/' } 23 | 24 | } 25 | 26 | ext { 27 | set('springCloudVersion', 'Greenwich.SR1') 28 | set('springStatemachineVersion', '2.0.1.RELEASE') 29 | set('mapstructVersion', '1.3.0.Final') 30 | set('flyingsaucerVersion', '9.1.18') 31 | set('errorproneVersion', '0.92') 32 | } 33 | 34 | dependencies { 35 | implementation 'org.springframework.boot:spring-boot-starter-actuator' 36 | implementation 'org.springframework.boot:spring-boot-starter-webflux' 37 | implementation 'org.springframework.boot:spring-boot-starter-data-mongodb-reactive' 38 | implementation 'org.springframework.boot:spring-boot-starter-freemarker' 39 | implementation 'org.springframework.boot:spring-boot-starter-mail' 40 | implementation 'org.springframework.boot:spring-boot-starter-validation' 41 | implementation 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix' 42 | implementation 'io.springfox:springfox-swagger2:3.0.0-SNAPSHOT' 43 | implementation 'io.springfox:springfox-swagger-ui:3.0.0-SNAPSHOT' 44 | implementation 'io.springfox:springfox-spring-webflux:3.0.0-SNAPSHOT' 45 | implementation 'com.github.ben-manes.caffeine:caffeine:2.7.0' 46 | implementation 'io.opentracing:opentracing-api:0.32.0' 47 | implementation 'io.opentracing:opentracing-util:0.32.0' 48 | implementation group: 'io.opentracing.contrib', name: 'opentracing-spring-jaeger-starter', version: '2.0.0' 49 | implementation group: 'io.opentracing.contrib', name: 'opentracing-spring-cloud-starter', version: '0.3.1' 50 | implementation "org.xhtmlrenderer:flying-saucer-core:${flyingsaucerVersion}" 51 | implementation "org.xhtmlrenderer:flying-saucer-pdf-openpdf:${flyingsaucerVersion}" 52 | 53 | 54 | 55 | compile group: 'org.mapstruct', name: 'mapstruct-jdk8', version: '1.3.0.Final' 56 | compileOnly 'org.mapstruct:mapstruct-processor:1.3.0.Final' 57 | annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}" 58 | implementation 'org.projectlombok:lombok:1.18.8' 59 | annotationProcessor 'org.projectlombok:lombok:1.18.8' 60 | 61 | runtimeOnly 'org.springframework.boot:spring-boot-devtools' 62 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 63 | testImplementation 'org.springframework.cloud:spring-cloud-starter-contract-stub-runner' 64 | testImplementation 'io.projectreactor:reactor-test:3.2.9.RELEASE' 65 | testCompile 'org.projectlombok:lombok:1.18.8' 66 | testCompile "com.github.tomakehurst:wiremock-jre8:2.23.2" 67 | 68 | } 69 | 70 | dependencyManagement { 71 | imports { 72 | mavenBom "org.springframework.statemachine:spring-statemachine-bom:${springStatemachineVersion}" 73 | mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" 74 | } 75 | } 76 | 77 | spotless { 78 | java { 79 | licenseHeaderFile 'codetripper.license' // License header file 80 | importOrder 'java', 'javax', 'org', 'com', 'in.codetripper', '' // A sequence of package names 81 | removeUnusedImports() 82 | eclipse().configFile 'eclipse-java-google-style.xml' // XML file dumped out by the Eclipse formatter 83 | 84 | } 85 | } 86 | 87 | clean{ 88 | dependsOn 'spotlessApply' 89 | } -------------------------------------------------------------------------------- /codetripper.license: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeTripper/communiK/666688fa27a040da4276c3c372faee96eae127df/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed May 15 18:27:41 IST 2019 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-5.2.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS='"-Xmx64m"' 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS="-Xmx64m" 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | } 5 | } 6 | rootProject.name = 'communik' 7 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | # must be unique in a given SonarQube instance 2 | sonar.projectKey=CodeTripper_communiK 3 | # this is the name and version displayed in the SonarQube UI. Was mandatory prior to SonarQube 6.1. 4 | sonar.projectName=CommuniK 5 | sonar.projectVersion=1.0 6 | 7 | sonar.sources=src/main 8 | sonar.tests=src/test 9 | 10 | # Encoding of the source code. Default is default system encoding 11 | sonar.sourceEncoding=UTF-8 -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/CommuniK.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik; 15 | 16 | import static in.codetripper.communik.Constants.APPLICATION_BASE_PACKAGE; 17 | import org.springframework.boot.SpringApplication; 18 | import org.springframework.boot.autoconfigure.SpringBootApplication; 19 | import org.springframework.context.annotation.ComponentScan; 20 | import org.springframework.data.mongodb.core.mapping.event.LoggingEventListener; 21 | 22 | @SpringBootApplication 23 | @ComponentScan(APPLICATION_BASE_PACKAGE) 24 | public class CommuniK { 25 | 26 | public static void main(String[] args) { 27 | SpringApplication.run(CommuniK.class, args); 28 | } 29 | 30 | 31 | public LoggingEventListener mongoEventListener() { 32 | return new LoggingEventListener(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik; 15 | 16 | public final class Constants { 17 | 18 | public static final int DB_READ_TIMEOUT = 5000; // TODO move to config 19 | public static final int DB_WRITE_TIMEOUT = 3000; 20 | public static final int PROVIDER_TIMEOUT = 4000; 21 | public static final String CACHE_DEFAULT = "DEFAULT"; 22 | public static final String CACHE_TEMPLATE = "TEMPLATE"; 23 | public static final String TRACE_EMAIL_OPERATION_NAME = "email.send"; 24 | public static final String APPLICATION_BASE_PACKAGE = "in.codetripper.communik"; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/common/CommunikFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.common; 15 | 16 | import org.reactivestreams.Publisher; 17 | import org.springframework.beans.factory.annotation.Autowired; 18 | import org.springframework.core.Ordered; 19 | import org.springframework.core.annotation.Order; 20 | import org.springframework.http.server.reactive.ServerHttpResponse; 21 | import org.springframework.stereotype.Component; 22 | import org.springframework.web.server.ServerWebExchange; 23 | import org.springframework.web.server.WebFilter; 24 | import org.springframework.web.server.WebFilterChain; 25 | import io.opentracing.Tracer; 26 | import lombok.RequiredArgsConstructor; 27 | import lombok.extern.slf4j.Slf4j; 28 | import reactor.core.publisher.Mono; 29 | 30 | /** 31 | * Filter to optionally record timings 32 | * 33 | * @author CodeTripper 34 | */ 35 | @Slf4j 36 | @Component 37 | @Order(Ordered.HIGHEST_PRECEDENCE + 1) 38 | @RequiredArgsConstructor 39 | public class CommunikFilter implements WebFilter { 40 | 41 | @Autowired 42 | private final Tracer tracer; 43 | 44 | 45 | @Override 46 | public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { 47 | return chain.filter(exchange).compose(call -> filter(exchange, call)); 48 | } 49 | 50 | private Publisher filter(ServerWebExchange exchange, Mono call) { 51 | long start = System.currentTimeMillis(); 52 | return call.doOnSuccess(done -> onSuccess(exchange, start)) 53 | .doOnError(cause -> onError(exchange, start, cause)); 54 | } 55 | 56 | private void onSuccess(ServerWebExchange exchange, long start) { 57 | record(exchange, start, null); 58 | } 59 | 60 | private void onError(ServerWebExchange exchange, long start, Throwable cause) { 61 | ServerHttpResponse response = exchange.getResponse(); 62 | if (response.isCommitted()) { 63 | record(exchange, start, cause); 64 | } else { 65 | response.beforeCommit(() -> { 66 | record(exchange, start, cause); 67 | return Mono.empty(); 68 | }); 69 | } 70 | } 71 | 72 | private void record(ServerWebExchange exchange, long start, Throwable cause) { 73 | log.info("processed {} in {} ms", exchange.getRequest().getPath(), 74 | System.currentTimeMillis() - start); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/config/CommunikCacheConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.config; 15 | 16 | import static in.codetripper.communik.Constants.CACHE_DEFAULT; 17 | import static in.codetripper.communik.Constants.CACHE_TEMPLATE; 18 | import java.util.Arrays; 19 | import java.util.concurrent.TimeUnit; 20 | import org.springframework.beans.factory.annotation.Value; 21 | import org.springframework.cache.CacheManager; 22 | import org.springframework.cache.annotation.CachingConfigurerSupport; 23 | import org.springframework.cache.annotation.EnableCaching; 24 | import org.springframework.cache.caffeine.CaffeineCache; 25 | import org.springframework.cache.support.SimpleCacheManager; 26 | import org.springframework.context.annotation.Bean; 27 | import org.springframework.context.annotation.Configuration; 28 | import com.github.benmanes.caffeine.cache.Caffeine; 29 | 30 | @Configuration 31 | @EnableCaching 32 | public class CommunikCacheConfig extends CachingConfigurerSupport { 33 | 34 | @Value("${notification.cache.template.ttl:60}") 35 | private int templateTtl; 36 | @Value("${notification.cache.default.ttl:300}") 37 | private int defaultTtl; 38 | 39 | @Override 40 | @Bean 41 | public CacheManager cacheManager() { 42 | CaffeineCache notificationCache = buildCache(CACHE_TEMPLATE, templateTtl); 43 | CaffeineCache defaultCache = buildCache(CACHE_DEFAULT, defaultTtl); 44 | SimpleCacheManager manager = new SimpleCacheManager(); 45 | manager.setCaches(Arrays.asList(notificationCache, defaultCache)); 46 | return manager; 47 | } 48 | 49 | /* 50 | * Can be used like this @Cacheable(cacheNames = CACHE_TEMPLATE, cacheManager = 51 | * "distributedCacheManager") 52 | */ 53 | @Bean 54 | public CacheManager distributedCacheManager() { 55 | CaffeineCache notificationCache = buildCache(CACHE_TEMPLATE, templateTtl); 56 | CaffeineCache defaultCache = buildCache(CACHE_DEFAULT, defaultTtl); 57 | SimpleCacheManager manager = new SimpleCacheManager(); 58 | manager.setCaches(Arrays.asList(notificationCache, defaultCache)); 59 | return manager; 60 | } 61 | 62 | private CaffeineCache buildCache(String name, int secondsToExpire) { 63 | return new CaffeineCache(name, Caffeine.newBuilder() 64 | .expireAfterWrite(secondsToExpire, TimeUnit.SECONDS).maximumSize(100).build()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.config; 15 | 16 | import java.util.Collections; 17 | import java.util.function.Predicate; 18 | import org.springframework.context.annotation.Bean; 19 | import org.springframework.context.annotation.ComponentScan; 20 | import org.springframework.context.annotation.Configuration; 21 | import in.codetripper.communik.domain.email.EmailController; 22 | import in.codetripper.communik.domain.sms.SmsController; 23 | import in.codetripper.communik.domain.template.NotificationTemplateController; 24 | import springfox.documentation.builders.PathSelectors; 25 | import springfox.documentation.builders.RequestHandlerSelectors; 26 | import springfox.documentation.service.ApiInfo; 27 | import springfox.documentation.service.Contact; 28 | import springfox.documentation.spi.DocumentationType; 29 | import springfox.documentation.spring.web.plugins.Docket; 30 | 31 | @Configuration 32 | @ComponentScan(basePackageClasses = {EmailController.class, SmsController.class, 33 | NotificationTemplateController.class}) 34 | public class SwaggerConfig { 35 | @Bean 36 | public Docket api() { 37 | return new Docket(DocumentationType.SWAGGER_2).select().apis(RequestHandlerSelectors.any()) 38 | .paths(PathSelectors.any()).paths(paths()).build().apiInfo(metaInfo()); 39 | } 40 | 41 | private Predicate paths() { 42 | return Predicate.not(PathSelectors.regex("/actuator.*")); 43 | } 44 | 45 | private ApiInfo metaInfo() { 46 | return new ApiInfo("Communik API", "Communik API - centralizes all your notifications", 47 | "API TOS", "Terms of service", 48 | new Contact("Code Tripper", "https://github.com/CodeTripper/", "admin@codetripper.in"), 49 | "Apache", "API license URL", Collections.emptyList()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/config/WebfluxConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.config; 15 | 16 | import org.springframework.web.reactive.config.ResourceHandlerRegistry; 17 | import lombok.experimental.UtilityClass; 18 | 19 | @UtilityClass 20 | public class WebfluxConfig { 21 | 22 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 23 | 24 | registry.addResourceHandler("/swagger-ui.html**") 25 | .addResourceLocations("classpath:/META-INF/resources/"); 26 | 27 | registry.addResourceHandler("/webjars/**") 28 | .addResourceLocations("classpath:/META-INF/resources/webjars/"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/email/EmailConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.email; 15 | 16 | import lombok.Data; 17 | 18 | @Data 19 | public class EmailConfiguration { 20 | 21 | private String host; 22 | private String username; 23 | private String password; 24 | private int port; 25 | private String protocol; 26 | private String defaultEncoding; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/email/EmailController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.email; 15 | 16 | 17 | import java.util.List; 18 | import javax.validation.Valid; 19 | import org.springframework.http.CacheControl; 20 | import org.springframework.http.MediaType; 21 | import org.springframework.http.server.reactive.ServerHttpRequest; 22 | import org.springframework.web.bind.annotation.PostMapping; 23 | import org.springframework.web.bind.annotation.RequestBody; 24 | import org.springframework.web.bind.annotation.RestController; 25 | import in.codetripper.communik.domain.notification.NotificationStatusResponse; 26 | import lombok.RequiredArgsConstructor; 27 | import lombok.extern.slf4j.Slf4j; 28 | import reactor.core.publisher.Flux; 29 | import reactor.core.publisher.Mono; 30 | 31 | @RestController 32 | @Slf4j 33 | @RequiredArgsConstructor 34 | 35 | public class EmailController { 36 | 37 | CacheControl ccNoStore = CacheControl.noStore(); 38 | private final EmailService emailService; 39 | 40 | @PostMapping(path = "/email", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 41 | public final Mono email(@Valid @RequestBody EmailDto emailDto, 42 | ServerHttpRequest serverHttpRequest) { 43 | String ipAddress = ""; 44 | if (serverHttpRequest.getRemoteAddress() != null) { 45 | ipAddress = serverHttpRequest.getRemoteAddress().getAddress().getHostAddress(); 46 | } 47 | emailDto.setIpAddress(ipAddress); 48 | log.debug("Received email request from {} with data {}", ipAddress, emailDto); 49 | return emailService.sendEmail(emailDto); 50 | } 51 | 52 | @PostMapping(path = "/emails", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 53 | public final Flux emails( 54 | @Valid @RequestBody List emailDtos) { 55 | log.debug("Received bulk email request with data {}", emailDtos); 56 | return Flux.fromIterable(emailDtos).flatMap(emailService::sendEmail); 57 | } 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/email/EmailDto.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.email; 15 | 16 | import java.io.Serializable; 17 | import java.util.LinkedHashMap; 18 | import java.util.List; 19 | import java.util.Locale; 20 | import java.util.Map; 21 | import javax.validation.constraints.NotBlank; 22 | import javax.validation.constraints.Size; 23 | import com.fasterxml.jackson.annotation.JsonAnySetter; 24 | import com.fasterxml.jackson.annotation.JsonIgnore; 25 | import in.codetripper.communik.domain.notification.Type; 26 | import lombok.Data; 27 | import lombok.NoArgsConstructor; 28 | import lombok.ToString; 29 | 30 | @Data 31 | @ToString 32 | public class EmailDto implements Serializable { 33 | 34 | @EmailList 35 | private List to; 36 | /* 37 | * private List cc; private List bcc; 38 | */ 39 | @NotBlank(message = "Subject must be between {min} and {max} characters long") 40 | @Size(min = 1, max = 100) 41 | private String subject; 42 | @JsonIgnore 43 | private Type type = Type.EMAIL; 44 | private Container body; 45 | private Container attachment; 46 | @Size(min = 1, max = 100, 47 | message = "The templateId must be between {min} and {max} characters long") 48 | private String templateId; 49 | @Size(min = 1, max = 100, 50 | message = "The providerName must be between {min} and {max} characters long") 51 | private String providerName; 52 | @Size(min = 1, max = 100, message = "The locale' must be between {min} and {max} characters long") 53 | private String locale = Locale.getDefault().toString(); 54 | // @Size(min = 1, max = 100, message = "The replyTo email id must be between {min} and {max} 55 | // characters long") 56 | private EmailId replyTo; 57 | @JsonIgnore 58 | private String ipAddress; 59 | @Size(min = 1, max = 100, 60 | message = "The mediaType must be between {min} and {max} characters long") 61 | private String mediaType; 62 | 63 | @Data 64 | @NoArgsConstructor 65 | public static class Container implements Serializable { 66 | 67 | private String message; 68 | Map data = new LinkedHashMap<>(); 69 | 70 | @JsonAnySetter 71 | void setDetail(String key, Object value) { 72 | data.put(key, value); 73 | 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/email/EmailId.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.email; 15 | 16 | import java.io.Serializable; 17 | import lombok.AllArgsConstructor; 18 | import lombok.Data; 19 | import lombok.NoArgsConstructor; 20 | 21 | @Data 22 | @AllArgsConstructor 23 | @NoArgsConstructor 24 | public class EmailId implements Serializable { 25 | 26 | private String name; 27 | private String id; 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/email/EmailIdValidator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.email; 15 | 16 | import java.util.Collection; 17 | import javax.validation.ConstraintValidator; 18 | import javax.validation.ConstraintValidatorContext; 19 | import org.hibernate.validator.internal.constraintvalidators.bv.EmailValidator; 20 | 21 | public class EmailIdValidator implements ConstraintValidator> { 22 | 23 | private final EmailValidator validator = new EmailValidator(); 24 | 25 | @Override 26 | public void initialize(EmailList emailList) { 27 | 28 | } 29 | 30 | @Override 31 | public boolean isValid(Collection value, ConstraintValidatorContext context) { 32 | if (value == null) { 33 | return false; 34 | } 35 | // TODO use Java 8 36 | for (EmailId s : value) { 37 | if (!validator.isValid(s.getId(), context)) { 38 | return false; 39 | } 40 | if (s.getId().length() > 319 || s.getName().length() > 319) { 41 | return false; 42 | } 43 | } 44 | return true; 45 | } 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/email/EmailList.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.email; 15 | 16 | import java.lang.annotation.Documented; 17 | import java.lang.annotation.ElementType; 18 | import java.lang.annotation.Retention; 19 | import java.lang.annotation.RetentionPolicy; 20 | import java.lang.annotation.Target; 21 | import javax.validation.Constraint; 22 | import javax.validation.Payload; 23 | 24 | @Documented 25 | @Constraint(validatedBy = EmailIdValidator.class) 26 | @Target({ElementType.METHOD, ElementType.FIELD}) 27 | @Retention(RetentionPolicy.RUNTIME) 28 | public @interface EmailList { 29 | 30 | String message() default "Invalid Email Id List"; 31 | 32 | Class[] groups() default {}; 33 | 34 | Class[] payload() default {}; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/email/EmailMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.email; 15 | 16 | import org.mapstruct.Mapper; 17 | import org.springframework.stereotype.Component; 18 | import in.codetripper.communik.domain.notification.NotificationMessage; 19 | 20 | 21 | @Mapper(componentModel = "spring") 22 | @Component 23 | public interface EmailMapper { 24 | 25 | NotificationMessage emailDtoToEmail(EmailDto emailDto); 26 | 27 | // EmailDto emailToEmailDto(NotificationMessage email); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/email/EmailNotifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.email; 15 | 16 | import in.codetripper.communik.domain.notification.Notifier; 17 | 18 | public interface EmailNotifier extends Notifier { 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/email/EmailService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.email; 15 | 16 | import in.codetripper.communik.domain.notification.NotificationService; 17 | import in.codetripper.communik.domain.notification.NotificationStatusResponse; 18 | import in.codetripper.communik.domain.notification.Type; 19 | import in.codetripper.communik.repository.mongo.NotificationMessageRepoDto; 20 | import reactor.core.publisher.Flux; 21 | import reactor.core.publisher.Mono; 22 | 23 | // create base service and move common methods up 24 | public interface EmailService extends NotificationService { 25 | 26 | Mono sendEmail(EmailDto emailDto); 27 | 28 | Flux getAllEmails(); 29 | 30 | default Type getType() { 31 | return Type.EMAIL; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/email/SmtpEmailSender.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.email; 15 | 16 | import java.util.List; 17 | import java.util.Properties; 18 | import javax.mail.MessagingException; 19 | import javax.mail.internet.MimeMessage; 20 | import org.springframework.mail.MailException; 21 | import org.springframework.mail.javamail.JavaMailSenderImpl; 22 | import org.springframework.mail.javamail.MimeMessageHelper; 23 | import in.codetripper.communik.domain.notification.NotificationMessage; 24 | import in.codetripper.communik.domain.notification.NotificationStatusResponse; 25 | import in.codetripper.communik.exceptions.NotificationSendFailedException; 26 | import lombok.extern.slf4j.Slf4j; 27 | import reactor.core.publisher.Mono; 28 | 29 | @Slf4j 30 | /* 31 | * WARNING DO NOT USE. SMTP is blocking.This is only for test purpose. 32 | */ 33 | public abstract class SmtpEmailSender implements EmailNotifier { 34 | 35 | private final JavaMailSenderImpl sender; 36 | 37 | public SmtpEmailSender() { 38 | sender = new JavaMailSenderImpl(); 39 | // props = sender.getJavaMailProperties(); 40 | } 41 | 42 | @Override 43 | public final Mono send(NotificationMessage email) 44 | throws NotificationSendFailedException { 45 | log.debug("starting email sender"); 46 | try { 47 | preProcess(email); 48 | process(email); 49 | postProcess(email); 50 | } catch (MessagingException | MailException e) { 51 | throw new NotificationSendFailedException("Unable to sendEmail Email", e); 52 | } 53 | return null; 54 | } 55 | 56 | private boolean process(NotificationMessage email) 57 | throws MailException, MessagingException { 58 | EmailConfiguration emailConfiguration = getMailConfiguration(); 59 | sender.setHost(emailConfiguration.getHost()); 60 | sender.setPassword(emailConfiguration.getPassword()); 61 | sender.setUsername(emailConfiguration.getUsername()); 62 | sender.setPort(emailConfiguration.getPort()); 63 | sender.setProtocol(emailConfiguration.getProtocol()); 64 | sender.setJavaMailProperties(getMailProperties()); 65 | MimeMessage message = sender.createMimeMessage(); 66 | MimeMessageHelper helper = new MimeMessageHelper(message, true); 67 | List list = email.getTo(); 68 | helper.setTo(list.toArray(new String[0])); 69 | helper.setSubject(email.getSubject()); 70 | helper.setText(email.getBodyTobeSent()); 71 | 72 | // if (email.getAttachment() != null) { 73 | /* 74 | * FileSystemResource file = new FileSystemResource(new 75 | * File(email.getAttachment().getMessage())); 76 | */ 77 | // helper.addAttachment("CoolImage.jpg", null); 78 | // } 79 | sender.send(message); 80 | return true; 81 | 82 | } 83 | 84 | protected abstract EmailConfiguration getMailConfiguration(); 85 | 86 | protected abstract Properties getMailProperties(); 87 | 88 | protected abstract Properties preProcess(NotificationMessage email); 89 | 90 | protected abstract Properties postProcess(NotificationMessage email); 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/email/providers/Gmail.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.email.providers; 15 | 16 | import java.util.Properties; 17 | import org.springframework.stereotype.Service; 18 | import in.codetripper.communik.domain.email.EmailConfiguration; 19 | import in.codetripper.communik.domain.email.EmailId; 20 | import in.codetripper.communik.domain.email.SmtpEmailSender; 21 | import in.codetripper.communik.domain.notification.NotificationMessage; 22 | import in.codetripper.communik.domain.provider.Provider; 23 | import in.codetripper.communik.domain.provider.ProviderService; 24 | import lombok.RequiredArgsConstructor; 25 | import lombok.extern.slf4j.Slf4j; 26 | 27 | @Service 28 | @Slf4j 29 | @RequiredArgsConstructor() 30 | public class Gmail extends SmtpEmailSender { 31 | 32 | private final ProviderService providerService; 33 | private String providerId = "11003"; 34 | 35 | @Override 36 | protected EmailConfiguration getMailConfiguration() { 37 | Provider provider = providerService.getProvider(providerId); 38 | EmailConfiguration emailConfiguration = new EmailConfiguration(); 39 | emailConfiguration.setHost(provider.getServer().getHost()); 40 | emailConfiguration.setUsername(provider.getBasicAuthentication().getUserId()); 41 | emailConfiguration.setPassword(provider.getBasicAuthentication().getPassword()); 42 | emailConfiguration.setPort(provider.getServer().getPort()); 43 | return emailConfiguration; 44 | 45 | } 46 | 47 | @Override 48 | protected Properties getMailProperties() { 49 | Provider provider = providerService.getProvider(providerId); 50 | Properties props = new Properties(); 51 | props.put("mail.transport.protocol", provider.getServer().getProtocol()); 52 | props.put("mail.smtp.auth", "true"); 53 | props.put("mail.smtp.starttls.enable", provider.getServer().isTls()); 54 | props.put("mail.debug", "false"); 55 | return props; 56 | } 57 | 58 | @Override 59 | protected Properties preProcess(NotificationMessage email) { 60 | return null; 61 | } 62 | 63 | @Override 64 | protected Properties postProcess(NotificationMessage email) { 65 | return null; 66 | } 67 | 68 | 69 | @Override 70 | public boolean isPrimary() { 71 | return false; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/notification/NoOpPersistenceAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.notification; 15 | 16 | import java.util.UUID; 17 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 18 | import org.springframework.stereotype.Repository; 19 | import in.codetripper.communik.repository.mongo.NotificationMessageRepoDto; 20 | import lombok.extern.slf4j.Slf4j; 21 | import reactor.core.publisher.Flux; 22 | import reactor.core.publisher.Mono; 23 | 24 | @Slf4j 25 | @Repository 26 | @ConditionalOnProperty(value = "notification.persistence", havingValue = "noop") 27 | public class NoOpPersistenceAdapter implements NotificationPersistence { 28 | 29 | public Mono store(NotificationMessage notificationMessage) { 30 | NotificationStorageResponse notificationStorageResponse = new NotificationStorageResponse(); 31 | notificationStorageResponse.setStatus(true); 32 | notificationStorageResponse.setId(UUID.randomUUID().toString()); 33 | return Mono.just(notificationStorageResponse); 34 | } 35 | 36 | public Mono update(NotificationMessage notificationMessage) { 37 | NotificationStorageResponse notificationStorageResponse = new NotificationStorageResponse(); 38 | notificationStorageResponse.setId(notificationMessage.getId()); 39 | notificationStorageResponse.setStatus(true); 40 | return Mono.just(notificationStorageResponse); 41 | } 42 | 43 | public Mono status(String id) { 44 | return Mono.empty(); 45 | } 46 | 47 | public Flux getAll() { 48 | return Flux.empty(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/notification/Notification.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.notification; 15 | 16 | import static in.codetripper.communik.Constants.DB_WRITE_TIMEOUT; 17 | import static in.codetripper.communik.Constants.PROVIDER_TIMEOUT; 18 | import java.time.Duration; 19 | import java.time.LocalDateTime; 20 | import java.util.List; 21 | import org.springframework.stereotype.Component; 22 | import in.codetripper.communik.exceptions.NotificationPersistenceException; 23 | import in.codetripper.communik.exceptions.NotificationSendFailedException; 24 | import lombok.NonNull; 25 | import lombok.RequiredArgsConstructor; 26 | import lombok.extern.slf4j.Slf4j; 27 | import reactor.core.publisher.Mono; 28 | 29 | /** 30 | * The core domain class. Contains all the business logic to store, forward and retry notifications 31 | * 32 | * @author CodeTripper 33 | */ 34 | @Component 35 | @Slf4j 36 | @RequiredArgsConstructor 37 | public class Notification { 38 | 39 | private final NotificationPersistence notificationPersistence; 40 | 41 | public Mono sendNotification( 42 | @NonNull NotificationMessage message) { 43 | log.debug("About to persist notification for {}", message); 44 | message.setStatus(Status.NOTIFICATION_NEW); 45 | return notificationPersistence.store(message).timeout(Duration.ofMillis(DB_WRITE_TIMEOUT)) 46 | .map(status -> updateMessage(message, status)).flatMap(this::notify) 47 | .onErrorResume(error -> retry(error, message)) 48 | .flatMap(status -> updateToRepository(message)).timeout(Duration.ofMillis(DB_WRITE_TIMEOUT)) 49 | .flatMap(this::createResponse) 50 | .onErrorMap(error -> new NotificationSendFailedException(error.getMessage())); 51 | 52 | } 53 | 54 | private Mono notify(@NonNull NotificationMessage message) { 55 | NotificationMessage.Notifiers notifiers = message.getNotifiers(); 56 | return notifiers.getPrimary().send(message).timeout(Duration.ofMillis(PROVIDER_TIMEOUT)); 57 | } 58 | 59 | @NonNull 60 | private NotificationMessage updateMessage(@NonNull NotificationMessage message, 61 | NotificationStorageResponse status) { 62 | message.setStatus(Status.NOTIFICATION_STORED); 63 | message.setId(status.getId()); 64 | return message; 65 | } 66 | 67 | private Mono retry(Throwable error, NotificationMessage message) { 68 | log.info("primary provider failed to send notification ", error); 69 | if (error instanceof NotificationPersistenceException) { 70 | return Mono.error(error); 71 | } else { 72 | log.warn("retrying again as primary provider failed to send notification"); 73 | NotificationMessage.Notifiers notifiers = message.getNotifiers(); 74 | return notifiers.getBackup().stream().findFirst() 75 | .map(backupNotifier -> backupNotifier.send(message) 76 | .timeout(Duration.ofMillis(PROVIDER_TIMEOUT))) 77 | .orElseGet(() -> Mono.error(new NotificationSendFailedException(error.getMessage()))); 78 | } 79 | } 80 | 81 | private Mono createResponse(NotificationStorageResponse status) { 82 | NotificationStatusResponse notificationStatusResponse = new NotificationStatusResponse(); 83 | notificationStatusResponse.setResponseId(status.getId()); 84 | notificationStatusResponse.setStatus(200); 85 | notificationStatusResponse.setMessage("SUCCESS"); 86 | notificationStatusResponse.setTimestamp(LocalDateTime.now()); 87 | return Mono.just(notificationStatusResponse); 88 | } 89 | 90 | private Mono updateToRepository( 91 | @NonNull NotificationMessage notificationMessage) { 92 | notificationMessage.setStatus(Status.NOTIFICATION_SUCCESS); 93 | notificationMessage.setAttempts(1); 94 | NotificationMessage.Action action = new NotificationMessage.Action<>(); 95 | action.setEnded(LocalDateTime.now()); 96 | action.setStatus(false); 97 | NotificationMessage.Notifiers primary = notificationMessage.getNotifiers(); 98 | Notifier test = primary.getPrimary(); 99 | action.setNotifier(test.getClass().getName()); 100 | notificationMessage.setActions(List.of(action)); 101 | return notificationPersistence.update(notificationMessage); 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/notification/NotificationMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.notification; 15 | 16 | import org.mapstruct.Mapper; 17 | import org.springframework.stereotype.Component; 18 | import in.codetripper.communik.repository.mongo.NotificationMessageRepoDto; 19 | 20 | 21 | @Mapper(componentModel = "spring") 22 | @Component 23 | public interface NotificationMapper { 24 | 25 | 26 | NotificationMessage mapDtoToMessage(NotificationMessageRepoDto notificationMessageDto); 27 | 28 | NotificationMessageRepoDto mapMessageToDto(NotificationMessage email); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/notification/NotificationMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.notification; 15 | 16 | import java.io.Serializable; 17 | import java.time.LocalDateTime; 18 | import java.time.LocalTime; 19 | import java.util.LinkedHashMap; 20 | import java.util.List; 21 | import java.util.Map; 22 | import javax.validation.constraints.NotNull; 23 | import lombok.Data; 24 | import lombok.NoArgsConstructor; 25 | import lombok.ToString; 26 | import lombok.extern.slf4j.Slf4j; 27 | // TODO change the below hack to Superbuilder when milestone 25 is released Idea plugin 28 | // https://github.com/mplushnikov/lombok-intellij-plugin/milestone/31 29 | 30 | /** 31 | * Base class to be used to send messages to the notifier providers 32 | * 33 | * @author CodeTripper 34 | */ 35 | 36 | @Data 37 | @ToString 38 | @Slf4j 39 | @NoArgsConstructor 40 | 41 | public class NotificationMessage implements Serializable { 42 | 43 | private String id; // from DB 44 | private List to; 45 | private String bodyTobeSent; 46 | 47 | private @NotNull Container body; 48 | private List attachments; 49 | private Meta meta; 50 | private Status status; 51 | private Notifiers notifiers; 52 | private List actions; 53 | private List blackouts; 54 | private LocalDateTime lastUpdated; 55 | private int attempts; 56 | private int deadLine; 57 | private String templateId; 58 | private String locale; 59 | private T from; 60 | private String mediaType; 61 | private String subject; 62 | private List cc; 63 | private List bcc; 64 | private T replyTo; 65 | 66 | @Data 67 | @NoArgsConstructor 68 | public static class Container implements Serializable { 69 | 70 | private String message; 71 | Map data = new LinkedHashMap<>(); 72 | } 73 | 74 | @Data 75 | @NoArgsConstructor 76 | public static class Attachment implements Serializable { 77 | 78 | private String mediaType; 79 | private byte[] content; 80 | private String name; 81 | private String placement; 82 | } 83 | /* 84 | * All meta data of the message to ne here. Immutable 85 | */ 86 | @Data 87 | public static class Meta implements Serializable { 88 | 89 | private Type type; 90 | private String senderIp; 91 | private String category; 92 | private String lob; 93 | private int maxRetry; // from template 94 | private @NotNull LocalDateTime created; // by Timestam 95 | private @NotNull LocalDateTime expireBy; 96 | 97 | } 98 | 99 | @Data 100 | public static class Notifiers implements Serializable { 101 | 102 | private Notifier primary; 103 | private List> backup; 104 | } 105 | 106 | @Data 107 | public static class Action implements Serializable { 108 | 109 | private String notifier; 110 | private String requestId; 111 | private String responseId; 112 | private @NotNull LocalDateTime started; 113 | private @NotNull LocalDateTime ended; 114 | private @NotNull LocalDateTime callbackAt; 115 | private boolean status; 116 | } 117 | 118 | /* 119 | * Should be populated from category, but should be overridable 120 | */ 121 | @Data 122 | public static class BlackOut implements Serializable { 123 | 124 | // by category 125 | private Type type; 126 | private LocalTime start; 127 | private LocalTime end; 128 | } 129 | 130 | 131 | /* 132 | * public NotificationMessage(Type type, String message, String to, String senderIp, Status 133 | * status, String templateId) { this.id = UUID.randomUUID().toString(); this.type = type; 134 | * this.message = message; this.to = to; this.senderIp = senderIp; this.status = status; 135 | * this.created = LocalDateTime.now(); this.templateId = templateId; } 136 | */ 137 | 138 | public final void setStatus(Status status) { 139 | this.status = status; 140 | } 141 | 142 | public final Status getStatus() { 143 | return this.status; 144 | } 145 | 146 | public final int getAttempts() { 147 | return this.actions != null ? this.actions.size() : 0; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/notification/NotificationPersistence.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.notification; 15 | 16 | 17 | import reactor.core.publisher.Flux; 18 | import reactor.core.publisher.Mono; 19 | 20 | public interface NotificationPersistence { 21 | 22 | Mono store(NotificationMessage notificationMessage); 23 | 24 | Mono update(NotificationMessage notificationMessage); 25 | 26 | Mono> status(String id); 27 | 28 | Flux> getAll(); 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/notification/NotificationPersistenceAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.notification; 15 | 16 | import static in.codetripper.communik.exceptions.ExceptionConstants.NOTIFICATION_PERSISTENCE_ID_NOT_PRESENT; 17 | import static in.codetripper.communik.exceptions.ExceptionConstants.NOTIFICATION_PERSISTENCE_UNABLE_TO_SAVE; 18 | import static in.codetripper.communik.exceptions.ExceptionConstants.NOTIFICATION_PERSISTENCE_UNABLE_TO_UPDATE; 19 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 20 | import org.springframework.stereotype.Repository; 21 | import com.google.common.base.Strings; 22 | import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; 23 | import in.codetripper.communik.exceptions.NotificationPersistenceException; 24 | import in.codetripper.communik.repository.mongo.NotificationMessageRepoDto; 25 | import in.codetripper.communik.repository.mongo.NotificationMessageRepository; 26 | import lombok.RequiredArgsConstructor; 27 | import lombok.extern.slf4j.Slf4j; 28 | import reactor.core.publisher.Flux; 29 | import reactor.core.publisher.Mono; 30 | 31 | @Repository 32 | @Slf4j 33 | @RequiredArgsConstructor 34 | @ConditionalOnProperty(value = "notification.persistence", havingValue = "mongo", 35 | matchIfMissing = true) 36 | public class NotificationPersistenceAdapter implements NotificationPersistence { 37 | 38 | private final NotificationMessageRepository notificationRepository; 39 | private final NotificationMapper notificationMapper; 40 | 41 | 42 | @Override 43 | @HystrixCommand 44 | public Mono store(NotificationMessage notificationMessage) { 45 | 46 | NotificationMessageRepoDto notificationMessageDto = 47 | notificationMapper.mapMessageToDto(notificationMessage); 48 | return notificationRepository.insert(notificationMessageDto).map(this::createResponse) 49 | .onErrorMap(error -> new NotificationPersistenceException( 50 | NOTIFICATION_PERSISTENCE_UNABLE_TO_SAVE, error)) 51 | .doOnSuccess(message -> log.debug("Saved message to Mongo with data {}", message)) 52 | .doOnError(error -> log.error("could not save message to Mongo with data ", error)); 53 | 54 | } 55 | 56 | 57 | @Override 58 | @HystrixCommand 59 | public Mono update(NotificationMessage notificationMessage) { 60 | log.debug("Received for updation to Mongo pre mapped data {}", notificationMessage); 61 | if (Strings.isNullOrEmpty(notificationMessage.getId())) { 62 | throw new NotificationPersistenceException(NOTIFICATION_PERSISTENCE_ID_NOT_PRESENT); 63 | } 64 | NotificationMessageRepoDto notificationMessageDto = 65 | notificationMapper.mapMessageToDto(notificationMessage); 66 | log.debug("Received for updation to Mongo post mapped data {}", notificationMessageDto); 67 | return notificationRepository.save(notificationMessageDto).map(this::createResponse) 68 | .onErrorMap(error -> new NotificationPersistenceException( 69 | NOTIFICATION_PERSISTENCE_UNABLE_TO_UPDATE, error)) 70 | .doOnSuccess(message -> log.debug("Updated message to Mongo with data {}", message)) 71 | .doOnError((error -> log.error("could not update message to Mongo with data ", error))); 72 | } 73 | 74 | private NotificationStorageResponse createResponse(NotificationMessageRepoDto message) { 75 | NotificationStorageResponse notificationStorageResponse = new NotificationStorageResponse(); 76 | notificationStorageResponse.setId(message.getId()); 77 | notificationStorageResponse.setStatus(true); 78 | return notificationStorageResponse; 79 | } 80 | 81 | @Override 82 | @HystrixCommand 83 | public Mono> status(String id) { 84 | // NotificationMessageRepoDto notificationMessageRepoDto = notificationRepository.findById(id); 85 | return null; // FIXME 86 | 87 | } 88 | 89 | @Override 90 | @HystrixCommand 91 | public Flux> getAll() { 92 | // return notificationRepository.findAll(); 93 | return null; // FIXME 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/notification/NotificationService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.notification; 15 | 16 | public interface NotificationService { 17 | 18 | Type getType(); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/notification/NotificationStatusResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.notification; 15 | 16 | import java.io.Serializable; 17 | import java.time.LocalDateTime; 18 | import lombok.Data; 19 | 20 | @Data 21 | public class NotificationStatusResponse implements Serializable { 22 | 23 | private LocalDateTime timestamp; 24 | private int status; 25 | private String message; 26 | private String traceId; 27 | private String responseId; 28 | private String providerResponseId; 29 | private String providerResponseMessage; 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/notification/NotificationStorageResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.notification; 15 | 16 | import java.io.Serializable; 17 | import lombok.Data; 18 | 19 | @Data 20 | public class NotificationStorageResponse implements Serializable { 21 | 22 | private boolean status; 23 | private String id; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/notification/Notifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.notification; 15 | 16 | import in.codetripper.communik.exceptions.NotificationSendFailedException; 17 | import reactor.core.publisher.Mono; 18 | 19 | public interface Notifier { 20 | 21 | Mono send(NotificationMessage notificationMessage) 22 | throws NotificationSendFailedException; 23 | 24 | boolean isPrimary(); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/notification/Status.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.notification; 15 | 16 | public enum Status { 17 | NOTIFICATION_NEW, NOTIFICATION_STORED, NOTIFICATION_SENT, NOTIFICATION_SUCCESS, NOTIFICATION_FAILED, NOTIFICATION_RETRY_FAILED, NOTIFICATION_INPROGRESS 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/notification/Type.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.notification; 15 | 16 | public enum Type { 17 | SMS, EMAIL 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/provider/Provider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.provider; 15 | 16 | import java.io.Serializable; 17 | import java.time.LocalDateTime; 18 | import java.util.List; 19 | import lombok.Data; 20 | import lombok.NoArgsConstructor; 21 | import lombok.ToString; 22 | 23 | @Data 24 | @ToString 25 | public class Provider implements Serializable { 26 | 27 | private String id; 28 | private String name; 29 | private String comment; 30 | private List email; 31 | private List contact; 32 | private String type; 33 | private int timeOutInMs; 34 | private boolean active; 35 | private boolean primary; 36 | private LocalDateTime activatedOn; 37 | private Endpoints endpoints; 38 | private String authType; 39 | private T from; 40 | private BearerAuthentication bearerAuthentication; 41 | private BasicAuthentication basicAuthentication; 42 | private Server server; 43 | 44 | @Data 45 | @NoArgsConstructor 46 | public static class BearerAuthentication implements Serializable { 47 | 48 | private String apiKey; 49 | private String authUrl; 50 | } 51 | 52 | @Data 53 | @NoArgsConstructor 54 | public static class BasicAuthentication implements Serializable { 55 | 56 | private String userId; 57 | private String password; 58 | } 59 | 60 | @Data 61 | @NoArgsConstructor 62 | public static class Endpoints implements Serializable { 63 | 64 | private String base; 65 | private String sendUri; 66 | private String statusUri; 67 | } 68 | 69 | @Data 70 | @NoArgsConstructor 71 | public static class Server implements Serializable { 72 | 73 | private String host; 74 | private int port; 75 | private String protocol; 76 | private boolean tls; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/provider/ProviderMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.provider; 15 | 16 | import org.mapstruct.Mapper; 17 | import org.springframework.stereotype.Component; 18 | import in.codetripper.communik.repository.mongo.ProviderRepoDto; 19 | 20 | @Mapper(componentModel = "spring") 21 | @Component 22 | public interface ProviderMapper { 23 | 24 | Provider mapDtoToNotifier(ProviderRepoDto notifierDto); 25 | 26 | ProviderRepoDto mapNotifierToDto(Provider provider); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/provider/ProviderPersistence.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.provider; 15 | 16 | 17 | import java.io.IOException; 18 | import reactor.core.publisher.Flux; 19 | import reactor.core.publisher.Mono; 20 | 21 | public interface ProviderPersistence { 22 | 23 | Mono getProvider(String id); 24 | 25 | Flux getAll() throws IOException; 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/provider/ProviderService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.provider; 15 | 16 | import java.io.IOException; 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | import javax.annotation.PostConstruct; 20 | import org.springframework.stereotype.Service; 21 | import lombok.RequiredArgsConstructor; 22 | import lombok.extern.slf4j.Slf4j; 23 | import reactor.core.publisher.Flux; 24 | 25 | @Service 26 | @Slf4j 27 | @RequiredArgsConstructor 28 | public class ProviderService { 29 | 30 | private final ProviderPersistence providerPersistence; 31 | private Map providerMap; 32 | 33 | @PostConstruct 34 | private void init() { 35 | providerMap = new HashMap<>(); 36 | log.info("ProviderService initialization providerMap on startup..."); 37 | try { 38 | getAllProviders().filter(Provider::isActive) 39 | .doOnNext(item -> providerMap.put(item.getId(), item)) 40 | .doOnError(error -> log.error("Error while initializing providers", error)).subscribe(); 41 | } catch (IOException e) { 42 | log.error("Unable to initialize providers map", e); 43 | } 44 | } 45 | 46 | public Provider getProvider(String id) { 47 | log.debug("searching providerid {} from providerMap {}", id, providerMap); 48 | return providerMap.get(id); 49 | } 50 | 51 | public Flux getAllProviders() throws IOException { 52 | log.info("Getting all providers..."); 53 | return providerPersistence.getAll(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/provider/adapter/ProviderFileSystemPersistenceAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.provider.adapter; 15 | 16 | import java.io.IOException; 17 | import java.util.Arrays; 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 20 | import org.springframework.core.io.Resource; 21 | import org.springframework.core.io.ResourceLoader; 22 | import org.springframework.core.io.support.ResourcePatternUtils; 23 | import org.springframework.stereotype.Service; 24 | import com.fasterxml.jackson.databind.ObjectMapper; 25 | import in.codetripper.communik.domain.provider.Provider; 26 | import in.codetripper.communik.domain.provider.ProviderPersistence; 27 | import lombok.RequiredArgsConstructor; 28 | import lombok.extern.slf4j.Slf4j; 29 | import reactor.core.publisher.Flux; 30 | import reactor.core.publisher.Mono; 31 | 32 | @Service 33 | @Slf4j 34 | @RequiredArgsConstructor 35 | @ConditionalOnProperty(value = "notification.provider.location", havingValue = "filesystem") 36 | public class ProviderFileSystemPersistenceAdapter implements ProviderPersistence { 37 | 38 | private final ResourceLoader resourceLoader; 39 | 40 | 41 | @Value("${notification.provider.location.filesystem:classpath:providers}") 42 | private String providersPath; 43 | 44 | @Override 45 | public Mono getProvider(String id) { 46 | Resource resource = resourceLoader.getResource(providersPath + "/" + id + ".json"); 47 | log.debug("template resource {}", id); 48 | Provider provider = apply(resource); 49 | log.debug("returning template {}", provider); 50 | return Mono.justOrEmpty(provider); 51 | 52 | } 53 | 54 | @Override 55 | public Flux getAll() throws IOException { 56 | return Flux.fromIterable(Arrays.asList(loadResources(providersPath + "/*.json"))) 57 | .map(ProviderFileSystemPersistenceAdapter::apply); 58 | 59 | } 60 | 61 | private static Provider apply(Resource p) { 62 | Provider provider = null; 63 | ObjectMapper objectMapper = new ObjectMapper(); 64 | try { 65 | provider = objectMapper.readValue(p.getFile(), Provider.class); 66 | } catch (IOException e) { 67 | log.error("Error while mapping all provider jsons", e); 68 | } 69 | return provider; 70 | } 71 | 72 | Resource[] loadResources(String pattern) throws IOException { 73 | return ResourcePatternUtils.getResourcePatternResolver(resourceLoader).getResources(pattern); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/provider/adapter/ProviderMongoPersistenceAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.provider.adapter; 15 | 16 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 17 | import org.springframework.stereotype.Service; 18 | import in.codetripper.communik.domain.provider.Provider; 19 | import in.codetripper.communik.domain.provider.ProviderMapper; 20 | import in.codetripper.communik.domain.provider.ProviderPersistence; 21 | import in.codetripper.communik.repository.mongo.ProviderRepository; 22 | import lombok.RequiredArgsConstructor; 23 | import lombok.extern.slf4j.Slf4j; 24 | import reactor.core.publisher.Flux; 25 | import reactor.core.publisher.Mono; 26 | 27 | @Service 28 | @Slf4j 29 | @RequiredArgsConstructor 30 | @ConditionalOnProperty(value = "notification.provider.location", havingValue = "mongo", 31 | matchIfMissing = true) 32 | public class ProviderMongoPersistenceAdapter implements ProviderPersistence { 33 | 34 | private final ProviderRepository mongoNotifierRepository; 35 | private final ProviderMapper notifierMapper; 36 | 37 | @Override 38 | public Mono getProvider(String id) { 39 | log.info("Getting provider details for provider {}", id); 40 | return mongoNotifierRepository.findById(id).map(notifierMapper::mapDtoToNotifier) 41 | .doOnSuccess(message -> log.debug("Got provider {}", message)) 42 | .doOnError(error -> log.error("Exception while retrieving provider from mongo", error)); 43 | } 44 | 45 | @Override 46 | public Flux getAll() { 47 | return mongoNotifierRepository.findAll().map(notifierMapper::mapDtoToNotifier); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/sms/SmsController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.sms; 15 | 16 | import java.util.List; 17 | import javax.validation.Valid; 18 | import org.springframework.http.MediaType; 19 | import org.springframework.http.server.reactive.ServerHttpRequest; 20 | import org.springframework.web.bind.annotation.GetMapping; 21 | import org.springframework.web.bind.annotation.PathVariable; 22 | import org.springframework.web.bind.annotation.PostMapping; 23 | import org.springframework.web.bind.annotation.RequestBody; 24 | import org.springframework.web.bind.annotation.RestController; 25 | import in.codetripper.communik.domain.notification.NotificationStatusResponse; 26 | import lombok.RequiredArgsConstructor; 27 | import lombok.extern.slf4j.Slf4j; 28 | import reactor.core.publisher.Flux; 29 | import reactor.core.publisher.Mono; 30 | 31 | @RestController 32 | @Slf4j 33 | @RequiredArgsConstructor 34 | public class SmsController { 35 | 36 | private final SmsService smsService; 37 | 38 | @PostMapping(path = "/sms", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 39 | public Mono sms(@Valid @RequestBody SmsDto smsDto, 40 | ServerHttpRequest serverHttpRequest) { 41 | log.debug("Inside SMSController"); 42 | String ipAddress = ""; 43 | if (serverHttpRequest.getRemoteAddress() != null) { 44 | ipAddress = serverHttpRequest.getRemoteAddress().getAddress().getHostAddress(); 45 | } 46 | smsDto.setIpAddress(ipAddress); 47 | return smsService.sendSms(smsDto); 48 | } 49 | 50 | @GetMapping(path = "/sms/{id}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 51 | public String getSmsStatus(@PathVariable String id) { 52 | NotificationStatusResponse notificationResponse = smsService.getSmsStatus(id); 53 | return "SUCCESS"; 54 | } 55 | 56 | @PostMapping(path = "/smses", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 57 | public final Flux smses(@Valid @RequestBody List smsDtos) { 58 | log.debug("Received bulk sms request with data {}", smsDtos); 59 | return Flux.fromIterable(smsDtos).flatMap(smsService::sendSms); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/sms/SmsDto.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.sms; 15 | 16 | import java.io.Serializable; 17 | import java.util.LinkedHashMap; 18 | import java.util.List; 19 | import java.util.Map; 20 | import javax.validation.constraints.NotEmpty; 21 | import com.fasterxml.jackson.annotation.JsonAnySetter; 22 | import com.fasterxml.jackson.annotation.JsonIgnore; 23 | import in.codetripper.communik.domain.notification.Type; 24 | import lombok.Data; 25 | import lombok.NoArgsConstructor; 26 | import lombok.ToString; 27 | 28 | @Data 29 | @ToString 30 | public class SmsDto implements Serializable { 31 | 32 | @NotEmpty(message = "To field cannot be empty") 33 | private List to; 34 | private Type type = Type.SMS; 35 | private Container body; 36 | private String templateId; 37 | private String providerName; 38 | private String locale; 39 | @JsonIgnore 40 | private String ipAddress; 41 | 42 | @Data 43 | @NoArgsConstructor 44 | public static class Container implements Serializable { 45 | 46 | private String message; 47 | Map data = new LinkedHashMap<>(); 48 | 49 | @JsonAnySetter 50 | void setDetail(String key, Object value) { 51 | data.put(key, value); 52 | 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/sms/SmsId.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.sms; 15 | 16 | import java.io.Serializable; 17 | import lombok.AllArgsConstructor; 18 | import lombok.Data; 19 | import lombok.NoArgsConstructor; 20 | 21 | @Data 22 | @NoArgsConstructor 23 | @AllArgsConstructor 24 | public class SmsId implements Serializable { 25 | 26 | private int countryCode; 27 | private long mobileNo; 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/sms/SmsMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.sms; 15 | 16 | import org.mapstruct.Mapper; 17 | import org.springframework.stereotype.Component; 18 | import in.codetripper.communik.domain.notification.NotificationMessage; 19 | 20 | 21 | @Mapper(componentModel = "spring") 22 | @Component 23 | public interface SmsMapper { 24 | 25 | NotificationMessage smsDtoToSms(SmsDto smsDto); 26 | 27 | SmsDto smsToSmsDto(NotificationMessage sms); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/sms/SmsNotFoundAdvice.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.sms; 15 | 16 | import org.springframework.http.HttpStatus; 17 | import org.springframework.web.bind.annotation.ControllerAdvice; 18 | import org.springframework.web.bind.annotation.ExceptionHandler; 19 | import org.springframework.web.bind.annotation.ResponseBody; 20 | import org.springframework.web.bind.annotation.ResponseStatus; 21 | 22 | @ControllerAdvice 23 | class SmsNotFoundAdvice { 24 | 25 | @ResponseBody 26 | @ExceptionHandler(SmsNotFoundException.class) 27 | @ResponseStatus(HttpStatus.NOT_FOUND) 28 | String smsNotFoundHandler(SmsNotFoundException ex) { 29 | return ex.getMessage(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/sms/SmsNotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.sms; 15 | 16 | import in.codetripper.communik.exceptions.NotificationNotFoundException; 17 | 18 | public class SmsNotFoundException extends NotificationNotFoundException { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/sms/SmsNotifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.sms; 15 | 16 | import in.codetripper.communik.domain.notification.Notifier; 17 | 18 | public interface SmsNotifier extends Notifier { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/sms/SmsResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.sms; 15 | 16 | import in.codetripper.communik.domain.notification.NotificationStatusResponse; 17 | 18 | class SmsResponse extends NotificationStatusResponse { 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/sms/SmsService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.sms; 15 | 16 | import in.codetripper.communik.domain.notification.NotificationService; 17 | import in.codetripper.communik.domain.notification.NotificationStatusResponse; 18 | import in.codetripper.communik.domain.notification.Type; 19 | import reactor.core.publisher.Mono; 20 | 21 | interface SmsService extends NotificationService { 22 | 23 | Mono sendSms(SmsDto smsDTO); 24 | 25 | NotificationStatusResponse getSmsStatus(String id); 26 | 27 | default Type getType() { 28 | return Type.SMS; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/sms/providers/DummyNotifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.sms.providers; 15 | 16 | public class DummyNotifier { 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/sms/providers/gupshup/Gupchup.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.sms.providers.gupshup; 15 | 16 | import java.time.LocalDateTime; 17 | import org.springframework.http.MediaType; 18 | import org.springframework.stereotype.Service; 19 | import org.springframework.web.reactive.function.BodyInserters; 20 | import org.springframework.web.reactive.function.client.WebClient; 21 | import org.springframework.web.reactive.function.client.WebClientException; 22 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 23 | import in.codetripper.communik.domain.notification.NotificationMessage; 24 | import in.codetripper.communik.domain.notification.NotificationStatusResponse; 25 | import in.codetripper.communik.domain.notification.Type; 26 | import in.codetripper.communik.domain.provider.Provider; 27 | import in.codetripper.communik.domain.provider.ProviderService; 28 | import in.codetripper.communik.domain.sms.SmsId; 29 | import in.codetripper.communik.domain.sms.SmsNotifier; 30 | import in.codetripper.communik.exceptions.NotificationSendFailedException; 31 | import lombok.Data; 32 | import lombok.RequiredArgsConstructor; 33 | import lombok.extern.slf4j.Slf4j; 34 | import reactor.core.publisher.Mono; 35 | 36 | 37 | @Service 38 | @Slf4j 39 | @RequiredArgsConstructor 40 | public class Gupchup implements SmsNotifier { 41 | 42 | private final ProviderService providerService; 43 | String providerId = "12001"; 44 | 45 | @Override 46 | public Mono send(NotificationMessage sms) 47 | throws NotificationSendFailedException { 48 | Mono response = null; 49 | Provider provider = providerService.getProvider(providerId); 50 | GupchupRequest gupchupRequest = new GupchupRequest(); 51 | gupchupRequest.setBody(sms.getBodyTobeSent()); 52 | String to = (String) sms.getTo().get(0); 53 | gupchupRequest.setTo(to); 54 | // gupchupRequest.setResponseId(sms.get); from where? 55 | if (provider.getType().equalsIgnoreCase(Type.SMS.toString())) { 56 | log.debug("Sending sms via provider: {} with data {}", provider, sms); 57 | try { 58 | WebClient client = WebClient.create(provider.getEndpoints().getBase()); 59 | response = client.post().uri(provider.getEndpoints().getSendUri()) 60 | .contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromObject(gupchupRequest)) 61 | .retrieve().bodyToMono(GupchupResponse.class).map(gupchupResponse -> { 62 | NotificationStatusResponse notificationStatusResponse = 63 | new NotificationStatusResponse(); 64 | notificationStatusResponse.setStatus(200); 65 | notificationStatusResponse.setTimestamp(LocalDateTime.now()); 66 | return notificationStatusResponse; 67 | }).doOnSuccess((message -> log.debug("sent sms successfully"))).doOnError((error -> { 68 | log.debug("sms sending failed", error); 69 | NotificationStatusResponse notificationStatusResponse = 70 | new NotificationStatusResponse(); 71 | notificationStatusResponse.setStatus(500); 72 | notificationStatusResponse.setTimestamp(LocalDateTime.now()); 73 | })); 74 | } catch (WebClientException webClientException) { 75 | log.error("webClientException", webClientException); 76 | throw new NotificationSendFailedException("webClientException received", 77 | webClientException); 78 | } catch (Exception ex) { 79 | log.error("ex", ex); 80 | throw new NotificationSendFailedException("webClientException received", ex); 81 | } 82 | } else { 83 | log.warn("Wrong providerid {} configured for {} ", providerId, Gupchup.class); 84 | } 85 | return response; 86 | } 87 | 88 | @Override 89 | public boolean isPrimary() { 90 | return providerService.getProvider(providerId).isPrimary(); 91 | } 92 | 93 | @JsonIgnoreProperties(ignoreUnknown = true) 94 | @Data 95 | public static class GupchupResponse { 96 | 97 | private boolean status; 98 | private String responseId; 99 | } 100 | 101 | @JsonIgnoreProperties(ignoreUnknown = true) 102 | @Data 103 | public static class GupchupRequest { 104 | 105 | private String to; 106 | private String body; 107 | private String requestId; 108 | } 109 | 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/sms/providers/twofactor/TwoFactorSmsNotifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.sms.providers.twofactor; 15 | 16 | import org.springframework.stereotype.Service; 17 | import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; 18 | import in.codetripper.communik.domain.notification.NotificationMessage; 19 | import in.codetripper.communik.domain.notification.NotificationStatusResponse; 20 | import in.codetripper.communik.domain.provider.ProviderService; 21 | import in.codetripper.communik.domain.sms.SmsId; 22 | import in.codetripper.communik.domain.sms.SmsNotifier; 23 | import in.codetripper.communik.exceptions.NotificationSendFailedException; 24 | import lombok.RequiredArgsConstructor; 25 | import reactor.core.publisher.Mono; 26 | 27 | @Service 28 | @RequiredArgsConstructor 29 | public class TwoFactorSmsNotifier implements SmsNotifier { 30 | 31 | private ProviderService providerService; 32 | String providerId = "12002"; 33 | 34 | @Override 35 | @HystrixCommand() 36 | public Mono send(NotificationMessage sms) 37 | throws NotificationSendFailedException { 38 | return null; 39 | } 40 | 41 | @Override 42 | public boolean isPrimary() { 43 | return false; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/template/NotificationTemplate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.template; 15 | 16 | import java.io.Serializable; 17 | import java.time.LocalDateTime; 18 | import java.util.List; 19 | import in.codetripper.communik.domain.notification.Type; 20 | import lombok.Data; 21 | import lombok.NoArgsConstructor; 22 | import lombok.ToString; 23 | 24 | 25 | @ToString 26 | @Data 27 | public class NotificationTemplate implements Serializable { 28 | 29 | private String id; 30 | private String name; 31 | private String category; 32 | private String lob; 33 | private Type type; 34 | private boolean active; 35 | private String providerId; 36 | private LocalDateTime created; 37 | private LocalDateTime updated; 38 | private String owner; 39 | private String body; 40 | private List attachments; 41 | private List bcc; 42 | private List cc; 43 | private T replyTo; 44 | private T from; 45 | private String mediaType; 46 | 47 | @Data 48 | @NoArgsConstructor 49 | public static class Container { 50 | 51 | private String mediaType; 52 | private String method; 53 | private String source; 54 | private String name; 55 | private String placement; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/template/NotificationTemplateController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.template; 15 | 16 | import java.net.URI; 17 | import java.util.Arrays; 18 | import javax.validation.constraints.NotBlank; 19 | import org.reactivestreams.Publisher; 20 | import org.springframework.http.MediaType; 21 | import org.springframework.http.ResponseEntity; 22 | import org.springframework.web.bind.annotation.DeleteMapping; 23 | import org.springframework.web.bind.annotation.GetMapping; 24 | import org.springframework.web.bind.annotation.PathVariable; 25 | import org.springframework.web.bind.annotation.PostMapping; 26 | import org.springframework.web.bind.annotation.PutMapping; 27 | import org.springframework.web.bind.annotation.RequestBody; 28 | import org.springframework.web.bind.annotation.RestController; 29 | import lombok.RequiredArgsConstructor; 30 | import lombok.extern.slf4j.Slf4j; 31 | import reactor.core.publisher.Flux; 32 | import reactor.core.publisher.Mono; 33 | 34 | 35 | @RestController 36 | @Slf4j 37 | @RequiredArgsConstructor 38 | public class NotificationTemplateController { 39 | 40 | private final NotificationTemplateService templateService; 41 | private final NotificationTemplateMapper templateMapper; 42 | private static final String BASE_PATH = "template"; 43 | 44 | @PostMapping(path = BASE_PATH, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 45 | public Publisher> create( 46 | @RequestBody NotificationTemplateDto templateDto) { 47 | log.debug("notificationTemplate controler:{}", templateDto); 48 | NotificationTemplate notificationTemplate = templateMapper.templateDtoToTemplate(templateDto); 49 | return this.templateService.create(notificationTemplate) 50 | .map(p -> ResponseEntity.created(URI.create(BASE_PATH + "/" + p.getId())).build()); 51 | } 52 | 53 | @GetMapping(path = BASE_PATH + "/{id}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 54 | public Mono getTemplate(@NotBlank @PathVariable String id) { 55 | return templateService.get(id); 56 | } 57 | 58 | @GetMapping(path = BASE_PATH + "s", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 59 | public Flux getTemplates() { 60 | return templateService.getAll(); 61 | } 62 | 63 | @GetMapping(path = BASE_PATH + "/test", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 64 | public Flux getTest() { 65 | return Flux.fromIterable(Arrays.asList("foo", "bar")); 66 | } 67 | 68 | @PutMapping(path = BASE_PATH + "/{id}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 69 | public Mono updateTemplate(@NotBlank @PathVariable String id, 70 | @RequestBody NotificationTemplateDto templateDto) { 71 | templateDto.setId(id); 72 | NotificationTemplate notificationTemplate = templateMapper.templateDtoToTemplate(templateDto); 73 | return this.templateService.update(notificationTemplate); 74 | 75 | } 76 | 77 | @DeleteMapping(path = BASE_PATH + "/{id}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 78 | public Mono deleteTemplate(@NotBlank @PathVariable String id) { 79 | return templateService.delete(id); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/template/NotificationTemplateDto.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.template; 15 | 16 | import java.io.Serializable; 17 | import java.time.LocalDateTime; 18 | import java.util.List; 19 | import in.codetripper.communik.domain.notification.Type; 20 | import lombok.Data; 21 | import lombok.NoArgsConstructor; 22 | import lombok.ToString; 23 | 24 | 25 | @ToString 26 | @Data 27 | public class NotificationTemplateDto implements Serializable { 28 | 29 | private String id; 30 | private String name; 31 | private String category; 32 | private String lob; 33 | private Type type; 34 | private boolean active; 35 | private LocalDateTime created; 36 | private LocalDateTime updated; 37 | private String owner; 38 | private String body; 39 | private List attachments; 40 | private List bcc; 41 | private List cc; 42 | private T replyTo; 43 | private T from; 44 | private String mediaType; 45 | 46 | @Data 47 | @NoArgsConstructor 48 | public static class Container implements Serializable { 49 | 50 | private String mediaType; 51 | private String method; 52 | private String source; 53 | private String name; 54 | private String placement; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/template/NotificationTemplateLocaleResolver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.template; 15 | 16 | public class NotificationTemplateLocaleResolver { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/template/NotificationTemplateMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.template; 15 | 16 | import org.mapstruct.Mapper; 17 | import org.springframework.stereotype.Component; 18 | import in.codetripper.communik.repository.mongo.NotificationTemplateRepoDto; 19 | 20 | 21 | @Mapper(componentModel = "spring") 22 | @Component 23 | public interface NotificationTemplateMapper { 24 | 25 | NotificationTemplateRepoDto templateToTemplateRepoDto(NotificationTemplate notificationTemplate); 26 | 27 | NotificationTemplate templateRepoDtotoTemplate(NotificationTemplateRepoDto templateRepoDto); 28 | 29 | NotificationTemplateDto templateToTemplateDto(NotificationTemplate notificationTemplate); 30 | 31 | NotificationTemplate templateDtoToTemplate(NotificationTemplateDto templateDto); 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/template/NotificationTemplatePersistence.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.template; 15 | 16 | import reactor.core.publisher.Flux; 17 | import reactor.core.publisher.Mono; 18 | 19 | public interface NotificationTemplatePersistence { 20 | 21 | Mono create(NotificationTemplate notificationTemplate); 22 | 23 | Mono update(NotificationTemplate notificationTemplate); 24 | 25 | Flux getAll(); 26 | 27 | Mono get(String id); 28 | 29 | Mono delete(String id); 30 | 31 | String getDefaultTemplate(); 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/template/NotificationTemplateService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.template; 15 | 16 | import reactor.core.publisher.Flux; 17 | import reactor.core.publisher.Mono; 18 | 19 | public interface NotificationTemplateService { 20 | 21 | Mono create(NotificationTemplate notificationTemplate); 22 | 23 | Mono update(NotificationTemplate notificationTemplate); 24 | 25 | Flux getAll(); 26 | 27 | Mono get(String id); 28 | 29 | Mono delete(String id); 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/template/NotificationTemplateServiceImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.template; 15 | 16 | import static in.codetripper.communik.Constants.CACHE_TEMPLATE; 17 | import java.time.LocalDateTime; 18 | import org.springframework.cache.annotation.Cacheable; 19 | import org.springframework.stereotype.Service; 20 | import lombok.RequiredArgsConstructor; 21 | import lombok.extern.slf4j.Slf4j; 22 | import reactor.core.publisher.Flux; 23 | import reactor.core.publisher.Mono; 24 | 25 | /* 26 | * All domain logic to reside here , no reference of DTos here. 27 | */ 28 | @Service 29 | @Slf4j 30 | @RequiredArgsConstructor 31 | 32 | public class NotificationTemplateServiceImpl implements NotificationTemplateService { 33 | 34 | private final NotificationTemplatePersistence templatePersistence; 35 | 36 | // TODO add validation 37 | @Override 38 | public Mono create(NotificationTemplate notificationTemplate) { 39 | notificationTemplate.setCreated(LocalDateTime.now()); 40 | return templatePersistence.create(notificationTemplate); 41 | } 42 | 43 | @Override 44 | public Mono update(NotificationTemplate notificationTemplate) { 45 | notificationTemplate.setUpdated(LocalDateTime.now()); 46 | notificationTemplate.setActive(true); 47 | return templatePersistence.update(notificationTemplate); 48 | } 49 | 50 | @Override 51 | public Flux getAll() { 52 | return templatePersistence.getAll(); 53 | } 54 | 55 | @Override 56 | @Cacheable(cacheNames = CACHE_TEMPLATE) 57 | public Mono get(String id) { 58 | log.debug("Getting template for id {}", id); 59 | return templatePersistence.get(id); 60 | } 61 | 62 | @Override 63 | public Mono delete(String id) { 64 | return templatePersistence.delete(id); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/template/adapter/FileSystemTemplateAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.template.adapter; 15 | 16 | import java.io.IOException; 17 | import org.springframework.beans.factory.annotation.Value; 18 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 19 | import org.springframework.core.io.Resource; 20 | import org.springframework.core.io.ResourceLoader; 21 | import org.springframework.stereotype.Repository; 22 | import com.fasterxml.jackson.databind.ObjectMapper; 23 | import com.google.common.base.Strings; 24 | import in.codetripper.communik.domain.template.NotificationTemplate; 25 | import in.codetripper.communik.domain.template.NotificationTemplatePersistence; 26 | import lombok.RequiredArgsConstructor; 27 | import lombok.extern.slf4j.Slf4j; 28 | import reactor.core.publisher.Flux; 29 | import reactor.core.publisher.Mono; 30 | 31 | @Repository 32 | @ConditionalOnProperty(value = "notification.template.location", havingValue = "filesystem") 33 | @RequiredArgsConstructor 34 | @Slf4j 35 | public class FileSystemTemplateAdapter implements NotificationTemplatePersistence { 36 | 37 | private final ResourceLoader resourceLoader; 38 | 39 | @Value("${notification.template.default.enabled:false}") 40 | private Boolean isDefaultTemplateEnabled; 41 | 42 | @Value("${notification.template.default.name}") 43 | private String defaultTemplateName; 44 | 45 | @Value("${notification.template.location.filesystem:classpath:templates}") 46 | private String templatePath; 47 | 48 | 49 | @Override 50 | public Mono create(NotificationTemplate notificationTemplate) { 51 | return null; 52 | } 53 | 54 | @Override 55 | public Mono update(NotificationTemplate notificationTemplate) { 56 | return null; 57 | } 58 | 59 | @Override 60 | public Flux getAll() { 61 | return null; 62 | } 63 | 64 | 65 | @Override 66 | public Mono get(String id) { 67 | if (Strings.isNullOrEmpty(id) && isDefaultTemplateEnabled) { 68 | id = getDefaultTemplate(); 69 | } 70 | Resource resource = resourceLoader.getResource(templatePath + "/" + id + ".json"); 71 | log.debug("template resource {}", id); 72 | NotificationTemplate notificationTemplate = null; 73 | ObjectMapper objectMapper = new ObjectMapper(); 74 | try { 75 | notificationTemplate = objectMapper.readValue(resource.getFile(), NotificationTemplate.class); 76 | } catch (IOException e) { 77 | return Mono.error(e); 78 | } 79 | log.debug("returning template {}", notificationTemplate); 80 | return Mono.justOrEmpty(notificationTemplate); 81 | } 82 | 83 | @Override 84 | public Mono delete(String id) { 85 | return null; 86 | } 87 | 88 | @Override 89 | public String getDefaultTemplate() { 90 | return defaultTemplateName; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/domain/template/adapter/NotificationTemplatePersistenceAdaptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.template.adapter; 15 | 16 | import org.springframework.beans.factory.annotation.Value; 17 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 18 | import org.springframework.stereotype.Service; 19 | import com.google.common.base.Strings; 20 | import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; 21 | import in.codetripper.communik.domain.template.NotificationTemplate; 22 | import in.codetripper.communik.domain.template.NotificationTemplateMapper; 23 | import in.codetripper.communik.domain.template.NotificationTemplatePersistence; 24 | import in.codetripper.communik.repository.mongo.NotificationTemplateRepoDto; 25 | import in.codetripper.communik.repository.mongo.NotificationTemplateRepository; 26 | import lombok.RequiredArgsConstructor; 27 | import lombok.extern.slf4j.Slf4j; 28 | import reactor.core.publisher.Flux; 29 | import reactor.core.publisher.Mono; 30 | 31 | @Service 32 | @Slf4j 33 | @RequiredArgsConstructor 34 | @ConditionalOnProperty(value = "notification.template.location", havingValue = "mongo", 35 | matchIfMissing = true) 36 | public class NotificationTemplatePersistenceAdaptor implements NotificationTemplatePersistence { 37 | 38 | private final NotificationTemplateRepository templateRepository; 39 | private final NotificationTemplateMapper templateMapper; 40 | 41 | @Value("${notification.template.default.enabled:false}") 42 | private Boolean isDefaultTemplateEnabled; 43 | 44 | @Value("${notification.template.default.name}") 45 | private String defaultTemplateName; 46 | 47 | @Override 48 | @HystrixCommand() 49 | public Mono create(NotificationTemplate notificationTemplate) { 50 | log.debug("notificationTemplate NotificationTemplateServiceImpl:{}", notificationTemplate); 51 | NotificationTemplateRepoDto templateRepoDto = 52 | templateMapper.templateToTemplateRepoDto(notificationTemplate); 53 | return this.templateRepository.insert(templateRepoDto) 54 | .map(templateMapper::templateRepoDtotoTemplate); 55 | } 56 | 57 | @Override 58 | @HystrixCommand() 59 | public Mono update(NotificationTemplate notificationTemplate) { 60 | log.debug("notificationTemplate NotificationTemplateServiceImpl:{}", notificationTemplate); 61 | NotificationTemplateRepoDto templateRepoDto = 62 | templateMapper.templateToTemplateRepoDto(notificationTemplate); 63 | return this.templateRepository.save(templateRepoDto) 64 | .map(templateMapper::templateRepoDtotoTemplate); 65 | } 66 | 67 | @Override 68 | @HystrixCommand() 69 | public Flux getAll() { 70 | return this.templateRepository.findAll().map(templateMapper::templateRepoDtotoTemplate); 71 | } 72 | 73 | @Override 74 | @HystrixCommand() 75 | public Mono get(String id) { 76 | if (Strings.isNullOrEmpty(id) && isDefaultTemplateEnabled) { 77 | id = getDefaultTemplate(); 78 | } 79 | return this.templateRepository.findById(id).map(templateMapper::templateRepoDtotoTemplate); 80 | } 81 | 82 | @Override 83 | @HystrixCommand() 84 | public Mono delete(String id) { 85 | return this.templateRepository.deleteById(id); 86 | } 87 | 88 | @Override 89 | public String getDefaultTemplate() { 90 | return defaultTemplateName; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/exceptions/CustomErrorAttributes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.exceptions; 15 | 16 | import static in.codetripper.communik.exceptions.ExceptionConstants.ERROR_BAD_REQUEST; 17 | import static in.codetripper.communik.exceptions.ExceptionConstants.ERROR_UNABLE_TO_PROCESS; 18 | import java.util.Map; 19 | import org.springframework.boot.web.reactive.error.DefaultErrorAttributes; 20 | import org.springframework.stereotype.Component; 21 | import org.springframework.web.reactive.function.server.ServerRequest; 22 | import io.opentracing.Tracer; 23 | import lombok.extern.slf4j.Slf4j; 24 | 25 | @Slf4j 26 | @Component 27 | class CustomErrorAttributes extends DefaultErrorAttributes { 28 | private final Tracer tracer; 29 | 30 | private CustomErrorAttributes(Tracer tracer) { 31 | super(false); 32 | this.tracer = tracer; 33 | } 34 | 35 | @Override 36 | public Map getErrorAttributes(ServerRequest request, boolean includeStackTrace) { 37 | final var error = getError(request); 38 | final var errorAttributes = super.getErrorAttributes(request, false); 39 | log.debug("tracer {}", tracer.scopeManager().activeSpan()); 40 | if (tracer.scopeManager().activeSpan() != null) { 41 | errorAttributes.put(ErrorAttribute.TRACE_ID.value, 42 | tracer.scopeManager().activeSpan().context().toTraceId()); 43 | } 44 | if (error instanceof NotificationSendFailedException 45 | || error instanceof NotificationPersistenceException) { 46 | log.debug("Caught an instance of: {}, err: {}", NotificationSendFailedException.class, error); 47 | errorAttributes.replace(ErrorAttribute.STATUS.value, 500); 48 | errorAttributes.replace(ErrorAttribute.ERROR.value, ERROR_UNABLE_TO_PROCESS); 49 | return errorAttributes; 50 | } else if (error instanceof InvalidRequestException) { 51 | log.debug("Caught an instance of: {}, err: {}", InvalidRequestException.class, error); 52 | errorAttributes.replace(ErrorAttribute.STATUS.value, 400); 53 | errorAttributes.replace(ErrorAttribute.ERROR.value, ERROR_BAD_REQUEST); 54 | return errorAttributes; 55 | } 56 | return errorAttributes; 57 | } 58 | 59 | 60 | enum ErrorAttribute { 61 | STATUS("status"), ERROR("error"), TRACE_ID("traceId"); 62 | 63 | private final String value; 64 | 65 | ErrorAttribute(String value) { 66 | this.value = value; 67 | } 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/exceptions/ExceptionConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.exceptions; 15 | 16 | public interface ExceptionConstants { 17 | 18 | String INVALID_REQUEST_TEMPLATE_MISMATCH = "Notification type and Template mismatch"; 19 | String INVALID_REQUEST_TEMPLATE_NOT_FOUND = "Template not found"; 20 | String VALIDATION_EMAIL_INVALID_EMAIL = "Email should be valid"; 21 | String VALIDATION_EMAIL_EMPTY_EMAIL = "Email should not be blank"; 22 | String NOTIFICATION_PERSISTENCE_UNABLE_TO_SAVE = "Unable to save notification"; 23 | String NOTIFICATION_PERSISTENCE_UNABLE_TO_UPDATE = "Unable to update notification"; 24 | String NOTIFICATION_PERSISTENCE_ID_NOT_PRESENT = 25 | "Unable to update notification as id is not present"; 26 | String NOTIFICATION_PERSISTENCE_DB_TIMED_OUT = "DB TimedOut"; 27 | String NOTIFICATION_SEND_FAILURE = "Unable to send notification"; 28 | String NO_PRIMARY_PROVIDER = "No provider configured"; 29 | String ERROR_UNABLE_TO_PROCESS = "Unable to process request"; 30 | String ERROR_BAD_REQUEST = "Invalid request received"; 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/exceptions/InvalidRequestException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.exceptions; 15 | 16 | public class InvalidRequestException extends RuntimeException { 17 | 18 | public InvalidRequestException(String s) { 19 | super(s); 20 | } 21 | 22 | public InvalidRequestException(String s, Throwable e) { 23 | super(s, e); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/exceptions/NotificationNotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.exceptions; 15 | 16 | public class NotificationNotFoundException extends RuntimeException { 17 | 18 | public NotificationNotFoundException() { 19 | 20 | } 21 | 22 | public NotificationNotFoundException(String s) { 23 | // Call constructor of parent Exception 24 | super(s); 25 | } 26 | 27 | public NotificationNotFoundException(String s, Throwable e) { 28 | // Call constructor of parent Exception 29 | super(s, e); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/exceptions/NotificationPersistenceException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.exceptions; 15 | 16 | public class NotificationPersistenceException extends RuntimeException { 17 | 18 | public NotificationPersistenceException(String s) { 19 | super(s); 20 | } 21 | 22 | public NotificationPersistenceException(String s, Throwable e) { 23 | super(s, e); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/exceptions/NotificationSendFailedException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.exceptions; 15 | 16 | public class NotificationSendFailedException extends RuntimeException { 17 | 18 | public NotificationSendFailedException(String s) { 19 | super(s); 20 | } 21 | 22 | public NotificationSendFailedException(String s, Throwable e) { 23 | // Call constructor of parent Exception 24 | super(s, e); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/messagegenerator/AttachmentHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.messagegenerator; 15 | 16 | import reactor.core.publisher.Mono; 17 | 18 | public interface AttachmentHandler { 19 | 20 | Mono get(String source, T data, String locale); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/messagegenerator/FlyingSaucerPdfGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.messagegenerator; 15 | 16 | import java.io.ByteArrayOutputStream; 17 | import org.springframework.stereotype.Service; 18 | import org.xhtmlrenderer.pdf.ITextRenderer; 19 | import lombok.extern.slf4j.Slf4j; 20 | import reactor.core.publisher.Mono; 21 | 22 | @Slf4j 23 | @Service("pdf") 24 | public class FlyingSaucerPdfGenerator implements MessageGenerator { 25 | 26 | 27 | public Mono generateMessage(String template, T data, String locale) 28 | throws MessageGenerationException { 29 | return Mono.create(sink -> { 30 | ITextRenderer renderer = new ITextRenderer(); 31 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 32 | renderer.setDocumentFromString(template); 33 | renderer.layout(); 34 | renderer.createPDF(outputStream); 35 | sink.success(outputStream.toByteArray()); 36 | }); 37 | 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/messagegenerator/FreeMarkerMessageGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.messagegenerator; 15 | 16 | import java.io.IOException; 17 | import java.io.StringReader; 18 | import java.io.StringWriter; 19 | import java.util.Locale; 20 | import org.springframework.context.annotation.Primary; 21 | import org.springframework.stereotype.Service; 22 | import freemarker.core.Environment; 23 | import freemarker.template.Configuration; 24 | import freemarker.template.Template; 25 | import freemarker.template.TemplateException; 26 | import lombok.RequiredArgsConstructor; 27 | import lombok.extern.slf4j.Slf4j; 28 | import reactor.core.publisher.Mono; 29 | 30 | @Slf4j 31 | @RequiredArgsConstructor 32 | @Service("html") 33 | @Primary 34 | public class FreeMarkerMessageGenerator implements MessageGenerator { 35 | 36 | private final Configuration configuration; 37 | 38 | @Override 39 | public Mono generateMessage(String template, T data, String locale) { 40 | StringWriter result = new StringWriter(); 41 | try { 42 | Template t = new Template("name", new StringReader(template), configuration); 43 | Environment env = t.createProcessingEnvironment(data, result); 44 | env.setLocale(Locale.forLanguageTag(locale)); 45 | env.setNumberFormat(",##0.00"); // TODO check 46 | env.process(); 47 | } catch (IOException | TemplateException e) { 48 | log.error("Unable to generate message", e); 49 | throw new MessageGenerationException("Unable to create message from template", e); 50 | } 51 | return Mono.just(result.toString()); 52 | } 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/messagegenerator/HtmlAttachmentHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.messagegenerator; 15 | 16 | import org.springframework.beans.factory.annotation.Qualifier; 17 | import org.springframework.stereotype.Service; 18 | import lombok.RequiredArgsConstructor; 19 | import lombok.extern.slf4j.Slf4j; 20 | import reactor.core.publisher.Mono; 21 | 22 | @Service("generate-html") 23 | @Slf4j 24 | @RequiredArgsConstructor 25 | public class HtmlAttachmentHandler implements AttachmentHandler { 26 | 27 | @Qualifier("html") 28 | private final MessageGenerator messageGenerator; 29 | 30 | @Override 31 | public Mono get(String source, T data, String locale) { 32 | return messageGenerator.generateMessage(source, data, locale).map(String::getBytes); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/messagegenerator/MessageGenerationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.messagegenerator; 15 | 16 | public class MessageGenerationException extends RuntimeException { 17 | 18 | public MessageGenerationException(String s) { 19 | super(s); 20 | } 21 | 22 | public MessageGenerationException(String s, Exception e) { 23 | super(s, e); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/messagegenerator/MessageGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.messagegenerator; 15 | 16 | 17 | import reactor.core.publisher.Mono; 18 | 19 | public interface MessageGenerator { 20 | 21 | Mono generateMessage(String template, T data, String locale); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/messagegenerator/PdfAttachmentHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.messagegenerator; 15 | 16 | import org.springframework.beans.factory.annotation.Qualifier; 17 | import org.springframework.stereotype.Service; 18 | import lombok.RequiredArgsConstructor; 19 | import lombok.extern.slf4j.Slf4j; 20 | import reactor.core.publisher.Mono; 21 | 22 | @Slf4j 23 | @RequiredArgsConstructor 24 | @Service("generate-pdf") 25 | public class PdfAttachmentHandler implements AttachmentHandler { 26 | 27 | @Qualifier("pdf") 28 | private final MessageGenerator messageGenerator; 29 | 30 | @Override 31 | public Mono get(String source, T data, String locale) { 32 | return messageGenerator.generateMessage(source, data, locale); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/messagegenerator/UrlAttachmentHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.messagegenerator; 15 | 16 | import java.io.IOException; 17 | import java.nio.file.Files; 18 | import org.springframework.beans.factory.annotation.Qualifier; 19 | import org.springframework.stereotype.Service; 20 | import lombok.RequiredArgsConstructor; 21 | import lombok.extern.slf4j.Slf4j; 22 | import reactor.core.publisher.Mono; 23 | 24 | @Slf4j 25 | @RequiredArgsConstructor 26 | @Service("download") 27 | public class UrlAttachmentHandler implements AttachmentHandler { 28 | 29 | @Qualifier("url") 30 | private final UrlResource urlResource; 31 | 32 | @Override 33 | public Mono get(String source, T data, String locale) { 34 | return urlResource.download(source).map(t -> { 35 | byte[] bytes = new byte[0]; 36 | try { 37 | bytes = Files.readAllBytes(t); 38 | } catch (IOException e) { 39 | log.error("Error while downloading", e); 40 | } 41 | return bytes; 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/messagegenerator/UrlResource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.messagegenerator; 15 | 16 | import java.io.IOException; 17 | import java.nio.channels.WritableByteChannel; 18 | import java.nio.file.Files; 19 | import java.nio.file.Path; 20 | import java.nio.file.StandardOpenOption; 21 | import org.springframework.core.io.buffer.DataBuffer; 22 | import org.springframework.core.io.buffer.DataBufferUtils; 23 | import org.springframework.stereotype.Service; 24 | import org.springframework.web.reactive.function.client.WebClient; 25 | import lombok.extern.slf4j.Slf4j; 26 | import reactor.core.publisher.Flux; 27 | import reactor.core.publisher.Mono; 28 | 29 | @Slf4j 30 | @Service("url") 31 | public class UrlResource { 32 | 33 | public Mono download(String url) { 34 | Mono result = null; 35 | Flux data = WebClient.create(url).get().retrieve().bodyToFlux(DataBuffer.class); 36 | try { 37 | log.debug("downloading url {}", url); 38 | Path file = Files.createTempFile("communik-url", null); 39 | WritableByteChannel channel = Files.newByteChannel(file, StandardOpenOption.WRITE); 40 | result = 41 | DataBufferUtils.write(data, channel).map(DataBufferUtils::release).then(Mono.just(file)); 42 | } catch (IOException e) { 43 | log.error("Error while downloading", e); 44 | } 45 | return result; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/repository/mongo/MongoConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.repository.mongo; 15 | 16 | import org.springframework.beans.factory.annotation.Value; 17 | import org.springframework.context.annotation.Bean; 18 | import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration; 19 | import org.springframework.data.mongodb.core.ReactiveMongoTemplate; 20 | import org.springframework.stereotype.Component; 21 | import com.mongodb.ConnectionString; 22 | import com.mongodb.MongoClientSettings; 23 | import com.mongodb.reactivestreams.client.MongoClient; 24 | import com.mongodb.reactivestreams.client.MongoClients; 25 | import in.codetripper.communik.trace.MongoTraceListener; 26 | import lombok.RequiredArgsConstructor; 27 | import lombok.extern.slf4j.Slf4j; 28 | 29 | @Slf4j 30 | @Component 31 | @RequiredArgsConstructor 32 | public class MongoConfig extends AbstractReactiveMongoConfiguration { 33 | 34 | private final MongoTraceListener mongoTracer; 35 | @Value("${mongodb.dbname:communik}") 36 | private String dbName; 37 | 38 | @Value("${mongodb.uri:mongodb://localhost:27017}") 39 | private String uri; 40 | 41 | @Override 42 | public MongoClient reactiveMongoClient() { 43 | log.debug("Creating MongoClient with trace"); 44 | MongoClient mongoClient = MongoClients 45 | .create(MongoClientSettings.builder().applyConnectionString(new ConnectionString(uri)) 46 | .addCommandListener(mongoTracer.getListener()).build()); 47 | return mongoClient; 48 | } 49 | 50 | @Override 51 | protected String getDatabaseName() { 52 | return dbName; 53 | } 54 | 55 | @Bean 56 | public ReactiveMongoTemplate reactiveMongoTemplate() { 57 | return new ReactiveMongoTemplate(reactiveMongoClient(), getDatabaseName()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/repository/mongo/NotificationMessageRepoDto.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.repository.mongo; 15 | 16 | import java.io.Serializable; 17 | import java.time.LocalDateTime; 18 | import java.util.List; 19 | import javax.validation.constraints.NotNull; 20 | import org.springframework.data.annotation.Id; 21 | import org.springframework.data.mongodb.core.mapping.Document; 22 | import in.codetripper.communik.domain.notification.NotificationMessage; 23 | import in.codetripper.communik.domain.notification.Status; 24 | import lombok.Data; 25 | 26 | @Data 27 | @Document(collection = "Notifications") 28 | public class NotificationMessageRepoDto implements Serializable { 29 | 30 | private @Id String id; 31 | private @NotNull List to; 32 | private @NotNull NotificationMessage.Container body; 33 | private List cc; 34 | private List bcc; 35 | private NotificationMessage.Container attachment; 36 | private NotificationMessage.Meta meta; 37 | private Status status; 38 | private LocalDateTime lastUpdated; 39 | private int attempts; 40 | private String subject; 41 | private String templateId; 42 | private String mediaType; 43 | private List blackouts; 44 | private List> actions; 45 | 46 | public final int getAttempts() { 47 | return this.actions != null ? this.actions.size() : 0; 48 | } 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/repository/mongo/NotificationMessageRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.repository.mongo; 15 | 16 | import org.springframework.data.mongodb.repository.ReactiveMongoRepository; 17 | 18 | public interface NotificationMessageRepository 19 | extends ReactiveMongoRepository, String> { 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/repository/mongo/NotificationTemplateRepoDto.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.repository.mongo; 15 | 16 | import java.io.Serializable; 17 | import java.time.LocalDateTime; 18 | import java.util.List; 19 | import org.springframework.data.mongodb.core.mapping.Document; 20 | import in.codetripper.communik.domain.notification.Type; 21 | import lombok.Data; 22 | import lombok.NoArgsConstructor; 23 | 24 | @Document(collection = "NotificationTemplate") 25 | @Data 26 | public class NotificationTemplateRepoDto implements Serializable { 27 | 28 | private String id; 29 | private String name; 30 | private String category; 31 | private String lob; 32 | private Type type; 33 | private boolean active; 34 | private LocalDateTime created; 35 | private LocalDateTime updated; 36 | private String owner; 37 | private String body; 38 | private List attachments; 39 | private List bcc; 40 | private List cc; 41 | private T replyTo; 42 | private String mediaType; 43 | 44 | @Data 45 | @NoArgsConstructor 46 | public static class Container implements Serializable { 47 | 48 | private String mediaType; 49 | private String method; 50 | private String source; 51 | private String name; 52 | private String placement; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/repository/mongo/NotificationTemplateRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.repository.mongo; 15 | 16 | import org.springframework.data.mongodb.repository.ReactiveMongoRepository; 17 | 18 | public interface NotificationTemplateRepository 19 | extends ReactiveMongoRepository { 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/repository/mongo/ProviderRepoDto.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.repository.mongo; 15 | 16 | import java.io.Serializable; 17 | import java.time.LocalDateTime; 18 | import java.util.List; 19 | import org.springframework.data.annotation.Id; 20 | import org.springframework.data.mongodb.core.mapping.Document; 21 | import in.codetripper.communik.domain.provider.Provider; 22 | import lombok.Data; 23 | import lombok.NoArgsConstructor; 24 | 25 | @Data 26 | @Document(collection = "Providers") 27 | public class ProviderRepoDto implements Serializable { 28 | 29 | private @Id String id; 30 | private String name; 31 | private String comment; 32 | private List email; 33 | private List contact; 34 | private String type; 35 | private int timeOutInMs; 36 | private boolean active; 37 | private boolean primary; 38 | private LocalDateTime activatedOn; 39 | private Endpoints endpoints; 40 | private String authType; 41 | private K from; 42 | private BearerAuthentication bearerAuthentication; 43 | private BasicAuthentication basicAuthentication; 44 | private Provider.Server server; 45 | 46 | @Data 47 | @NoArgsConstructor 48 | public static class BearerAuthentication implements Serializable { 49 | 50 | private String apiKey; 51 | private String authUrl; 52 | } 53 | 54 | @Data 55 | @NoArgsConstructor 56 | public static class BasicAuthentication implements Serializable { 57 | 58 | private String userId; 59 | private String password; 60 | } 61 | 62 | @Data 63 | @NoArgsConstructor 64 | public static class Endpoints implements Serializable { 65 | 66 | private String base; 67 | private String sendUri; 68 | private String statusUri; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/repository/mongo/ProviderRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.repository.mongo; 15 | 16 | import org.springframework.data.mongodb.repository.ReactiveMongoRepository; 17 | 18 | public interface ProviderRepository extends ReactiveMongoRepository, String> { 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/trace/CommunikTracer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.trace; 15 | 16 | import org.springframework.beans.factory.annotation.Value; 17 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 18 | import org.springframework.context.annotation.Bean; 19 | import org.springframework.context.annotation.Configuration; 20 | import io.jaegertracing.internal.JaegerTracer; 21 | import io.jaegertracing.internal.reporters.InMemoryReporter; 22 | import io.jaegertracing.internal.samplers.ConstSampler; 23 | import io.jaegertracing.internal.samplers.ProbabilisticSampler; 24 | import io.jaegertracing.spi.Reporter; 25 | import io.jaegertracing.spi.Sampler; 26 | import lombok.extern.slf4j.Slf4j; 27 | 28 | @Configuration 29 | @Slf4j 30 | public class CommunikTracer { 31 | 32 | @Value("${spring.application.name}") 33 | private String name; 34 | @Value("${opentracing.jaeger.samplingrate}") 35 | private double samplingRate; 36 | 37 | @Bean 38 | @ConditionalOnProperty(value = "opentracing.jaeger.enabled", havingValue = "true", 39 | matchIfMissing = true) 40 | public io.opentracing.Tracer tracer() { 41 | log.info("Building a jaegar tracer with a sampling rate of {}", samplingRate); 42 | if (samplingRate == 1.0) { 43 | log.warn(" You should not have jaegar tracer with a sampling rate of {} in production", 44 | samplingRate); 45 | } 46 | 47 | return new JaegerTracer.Builder(name).withSampler(new ProbabilisticSampler(samplingRate)) 48 | .build(); 49 | } 50 | 51 | 52 | @Bean 53 | @ConditionalOnProperty(value = "opentracing.jaeger.enabled", havingValue = "false") 54 | public io.opentracing.Tracer jaegerTracer() { 55 | log.warn("Building a dummy jaegar tracer as tracing.jaeger.enabled is false"); 56 | final Reporter reporter = new InMemoryReporter(); 57 | final Sampler sampler = new ConstSampler(false); 58 | return new JaegerTracer.Builder("untraced-service").withReporter(reporter).withSampler(sampler) 59 | .build(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/trace/MongoTraceListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.trace; 15 | 16 | import org.springframework.stereotype.Component; 17 | import io.opentracing.Tracer; 18 | import io.opentracing.contrib.mongo.common.TracingCommandListener; 19 | import io.opentracing.contrib.mongo.common.providers.PrefixSpanNameProvider; 20 | import lombok.RequiredArgsConstructor; 21 | import lombok.extern.slf4j.Slf4j; 22 | 23 | @Component 24 | @RequiredArgsConstructor 25 | @Slf4j 26 | public class MongoTraceListener { 27 | 28 | private final Tracer tracer; 29 | private String PREFIX = "mongo."; 30 | 31 | public TracingCommandListener getListener() { 32 | log.info("Tracing is enabled for MongoDb"); 33 | // TODO disable mongo document log 34 | return new TracingCommandListener.Builder(tracer) 35 | .withSpanNameProvider(new PrefixSpanNameProvider(PREFIX)).build(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/trace/WebClientDecorator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.trace; 15 | 16 | import java.util.HashMap; 17 | import java.util.Map; 18 | import org.springframework.web.reactive.function.client.ClientRequest; 19 | import org.springframework.web.reactive.function.client.ClientResponse; 20 | import io.opentracing.Span; 21 | import io.opentracing.contrib.spring.web.client.WebClientSpanDecorator; 22 | import io.opentracing.tag.Tags; 23 | import lombok.extern.slf4j.Slf4j; 24 | 25 | @Slf4j 26 | public class WebClientDecorator implements WebClientSpanDecorator { 27 | 28 | static final String COMPONENT_NAME = "webclient"; 29 | private final String operationName; 30 | private final String peerService; 31 | 32 | public WebClientDecorator(String operationName, String peerService) { 33 | this.operationName = operationName; 34 | this.peerService = peerService; 35 | } 36 | 37 | @Override 38 | public void onRequest(final ClientRequest clientRequest, final Span span) { 39 | span.setOperationName(operationName); 40 | Tags.COMPONENT.set(span, COMPONENT_NAME); 41 | Tags.HTTP_URL.set(span, clientRequest.url().toString()); 42 | Tags.HTTP_METHOD.set(span, clientRequest.method().toString()); 43 | Tags.PEER_SERVICE.set(span, peerService); 44 | if (clientRequest.url().getPort() != -1) { 45 | Tags.PEER_PORT.set(span, clientRequest.url().getPort()); 46 | } 47 | } 48 | 49 | @Override 50 | public void onResponse(final ClientRequest clientRequest, final ClientResponse clientResponse, 51 | final Span span) { 52 | Tags.HTTP_STATUS.set(span, clientResponse.rawStatusCode()); 53 | } 54 | 55 | @Override 56 | public void onError(final ClientRequest clientRequest, final Throwable throwable, 57 | final Span span) { 58 | Tags.ERROR.set(span, Boolean.TRUE); 59 | span.log(errorLogs(throwable)); 60 | } 61 | 62 | @Override 63 | public void onCancel(final ClientRequest httpRequest, final Span span) { 64 | final Map logs = new HashMap<>(2); 65 | logs.put("event", "cancelled"); 66 | logs.put("message", "The subscription was cancelled"); 67 | span.log(logs); 68 | } 69 | 70 | static Map errorLogs(final Throwable throwable) { 71 | final Map errorLogs = new HashMap<>(2); 72 | errorLogs.put("event", Tags.ERROR.getKey()); 73 | errorLogs.put("error.object", throwable); 74 | return errorLogs; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/webhooks/WebhookClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.webhooks; 15 | 16 | import java.util.List; 17 | import in.codetripper.communik.domain.notification.Status; 18 | import in.codetripper.communik.domain.notification.Type; 19 | 20 | public class WebhookClient { 21 | 22 | private String clientId; 23 | private String clientName; 24 | private List interests; 25 | private List notificationTypes; 26 | private String webhook; 27 | private String active; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/webhooks/WebhookController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.webhooks; 15 | 16 | import java.net.URI; 17 | import javax.validation.constraints.NotBlank; 18 | import org.reactivestreams.Publisher; 19 | import org.springframework.http.MediaType; 20 | import org.springframework.http.ResponseEntity; 21 | import org.springframework.web.bind.annotation.DeleteMapping; 22 | import org.springframework.web.bind.annotation.GetMapping; 23 | import org.springframework.web.bind.annotation.PathVariable; 24 | import org.springframework.web.bind.annotation.PostMapping; 25 | import org.springframework.web.bind.annotation.PutMapping; 26 | import org.springframework.web.bind.annotation.RequestBody; 27 | import org.springframework.web.bind.annotation.RestController; 28 | import in.codetripper.communik.domain.template.NotificationTemplate; 29 | import in.codetripper.communik.domain.template.NotificationTemplateDto; 30 | import in.codetripper.communik.domain.template.NotificationTemplateMapper; 31 | import in.codetripper.communik.domain.template.NotificationTemplateService; 32 | import lombok.RequiredArgsConstructor; 33 | import lombok.extern.slf4j.Slf4j; 34 | import reactor.core.publisher.Flux; 35 | import reactor.core.publisher.Mono; 36 | 37 | 38 | @RestController 39 | @Slf4j 40 | @RequiredArgsConstructor 41 | public class WebhookController { 42 | 43 | private final NotificationTemplateService templateService; 44 | private final MediaType mediaType = MediaType.APPLICATION_JSON_UTF8; 45 | private final NotificationTemplateMapper templateMapper; 46 | private static final String BASE_PATH = "webhook"; 47 | 48 | @PostMapping(value = BASE_PATH, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 49 | public Publisher> create( 50 | @RequestBody NotificationTemplateDto templateDto) { 51 | log.debug("template controler:{}", templateDto); 52 | NotificationTemplate template = templateMapper.templateDtoToTemplate(templateDto); 53 | return this.templateService.create(template) 54 | .map(p -> ResponseEntity.created(URI.create(BASE_PATH + "/" + p.getId())).build()); 55 | } 56 | 57 | @GetMapping(value = BASE_PATH + "/{id}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 58 | public Mono getTemplate(@NotBlank @PathVariable String id) { 59 | return templateService.get(id); 60 | } 61 | 62 | @GetMapping(value = BASE_PATH + "s", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 63 | public Flux getTemplates() { 64 | return templateService.getAll(); 65 | } 66 | 67 | @PutMapping(value = BASE_PATH + "/{id}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 68 | public Mono updateTemplate(@NotBlank @PathVariable String id, 69 | @RequestBody NotificationTemplateDto templateDto) { 70 | templateDto.setId(id); 71 | NotificationTemplate notificationTemplate = templateMapper.templateDtoToTemplate(templateDto); 72 | return this.templateService.update(notificationTemplate); 73 | 74 | } 75 | 76 | @DeleteMapping(value = BASE_PATH + "/{id}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 77 | public Mono deleteTemplate(@NotBlank @PathVariable String id) { 78 | return templateService.delete(id); 79 | } 80 | 81 | @PostMapping(value = BASE_PATH + "/ping", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 82 | public Publisher> ping( 83 | @RequestBody NotificationTemplateDto templateDto) { 84 | log.debug("notification controler:{}", templateDto); 85 | NotificationTemplate notificationTemplate = templateMapper.templateDtoToTemplate(templateDto); 86 | return this.templateService.create(notificationTemplate) 87 | .map(p -> ResponseEntity.created(URI.create(BASE_PATH + "/" + p.getId())).build()); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/in/codetripper/communik/webhooks/WebhookHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.webhooks; 15 | 16 | import java.beans.PropertyChangeEvent; 17 | import java.beans.PropertyChangeListener; 18 | import java.util.List; 19 | import org.springframework.stereotype.Component; 20 | import org.springframework.web.reactive.function.BodyInserters; 21 | import org.springframework.web.reactive.function.client.WebClient; 22 | import org.springframework.web.reactive.function.client.WebClientException; 23 | import in.codetripper.communik.domain.notification.NotificationMessage; 24 | import lombok.extern.slf4j.Slf4j; 25 | 26 | @SuppressWarnings("ALL") 27 | @Component 28 | @Slf4j 29 | public class WebhookHandler implements PropertyChangeListener { 30 | 31 | private List webbookClients; 32 | 33 | // initialize webhookClients on startup from DB 34 | public void addWebhookClient(WebhookClient webhookClient) { 35 | webbookClients.add(webhookClient); 36 | } 37 | 38 | public void removeWebhookClient(WebhookClient webhookClient) { 39 | webbookClients.remove(webhookClient); 40 | } 41 | 42 | @Override 43 | public void propertyChange(PropertyChangeEvent evt) { 44 | log.debug("Webhook: " + evt.getOldValue()); 45 | hook((NotificationMessage) evt.getNewValue()); 46 | // TODO List of webhook listeners, stored as well as runtime 47 | // iterate through webhookClients and check active compare their interests with o and call the 48 | // webhook non blocking 49 | 50 | } 51 | 52 | private boolean hook(NotificationMessage notificationMessage) { 53 | // TODO Connection refused check 54 | try { 55 | WebClient client = WebClient.create("http://localhost:9999"); 56 | String t = 57 | client.post().uri("/api/webhook").body(BodyInserters.fromObject(notificationMessage)) 58 | .retrieve().bodyToMono(String.class).block(); 59 | } catch (WebClientException webClientException) { 60 | log.error("webClientException"); 61 | } 62 | log.debug("webhook called:" + notificationMessage); 63 | // create payload 64 | // call post 65 | return true; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=Communik 2 | # false in prod 3 | spring.devtools.restart.enabled=true 4 | spring.devtools.add-properties=true 5 | logging.level.in.codetripper.communik=DEBUG 6 | logging.level.reactor.netty.http.client.HttpClient=DEBUG 7 | mongodb.dbname=test 8 | mongodb.uri=mongodb://localhost:27017 9 | opentracing.jaeger.samplingrate=1.0 10 | opentracing.jaeger.enabled=true 11 | notification.persistence=noop 12 | notification.template.location=filesystem 13 | notification.provider.location=filesystem 14 | notification.template.default.enabled=true 15 | notification.template.default.name=email-test-default -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | _____ _ _ __ 2 | / ____| (_) |/ / 3 | | | ___ _ __ ___ _ __ ___ _ _ _ __ _| ' / 4 | | | / _ \| '_ ` _ \| '_ ` _ \| | | | '_ \| | < 5 | | |___| (_) | | | | | | | | | | | |_| | | | | | . \ 6 | \_____\___/|_| |_| |_|_| |_| |_|\__,_|_| |_|_|_|\_\ 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/main/resources/providers/11001.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 11001, 3 | "name": "MailGun", 4 | "comment": "Used for transaction mails", 5 | "email": [ 6 | "help@mailgun.com" 7 | ], 8 | "contact": [ 9 | "2342664564", 10 | "123213213" 11 | ], 12 | "type": "EMAIL", 13 | "timeOutInMs": 3000, 14 | "active": true, 15 | "from": { 16 | "id": "test@example.com", 17 | "name": "Mailgun Tester" 18 | }, 19 | "primary": false, 20 | "endpoints": { 21 | "base": "", 22 | "sendUri": "messages" 23 | }, 24 | "from": "Mailgun Sandbox ", 25 | "authType": "basic", 26 | "basicAuthentication": { 27 | "userId": "api", 28 | "password": "" 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/resources/providers/11002.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 11002, 3 | "name": "SendGrid", 4 | "comment": "Used for testing mails", 5 | "email": [ 6 | "help@sendgrid.com" 7 | ], 8 | "contact": [ 9 | "2342664564", 10 | "123213213" 11 | ], 12 | "type": "EMAIL", 13 | "timeOutInMs": 3000, 14 | "active": true, 15 | "primary": false, 16 | "from": { 17 | "id": "test@example.com", 18 | "name": "SendGrid Tester" 19 | }, 20 | "endpoints": { 21 | "base": "https://api.sendgrid.com/v3/", 22 | "sendUri": "mail/send", 23 | "statusUri": "/sms/sendgrid/status" 24 | }, 25 | "authType": "bearer", 26 | "bearerAuthentication": { 27 | "apiKey": "", 28 | "authUrl": "http://localhost:9999/sms/sendgrid" 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/resources/providers/11404.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 11404, 3 | "name": "DummyMailer", 4 | "comment": "Used for testing mails", 5 | "email": [ 6 | "help@sendgrid.com" 7 | ], 8 | "contact": [ 9 | "2342664564", 10 | "123213213" 11 | ], 12 | "type": "EMAIL", 13 | "timeOutInMs": 3000, 14 | "active": true, 15 | "primary": true, 16 | "from": { 17 | "id": "test@example.com", 18 | "name": "Dummy Tester" 19 | }, 20 | "endpoints": { 21 | "base": "http://localhost:9999/", 22 | "sendUri": "/email/sendgrid", 23 | "statusUri": "/sms/gupchup/status" 24 | }, 25 | "authType": "basic", 26 | "basicAuthentication": { 27 | "userId": "test", 28 | "password": "test" 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/resources/providers/12001.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 12001, 3 | "name": "Gupchup", 4 | "comment": "Used for transaction mails", 5 | "email": [ 6 | "help@gupchul.com" 7 | ], 8 | "contact": [ 9 | "2342664564", 10 | "123213213" 11 | ], 12 | "type": "SMS", 13 | "timeOutInMs": 3000, 14 | "active": true, 15 | "endpoints": { 16 | "base": "http://localhost:9999/", 17 | "sendUri": "/sms/gupchup", 18 | "statusUri": "/sms/gupchup/status" 19 | }, 20 | "authType": "basic", 21 | "basicAuthentication": { 22 | "userId": "Gupchup", 23 | "password": "http://localhost:9999/sms/Gupchup" 24 | }, 25 | "primary": false 26 | } -------------------------------------------------------------------------------- /src/main/resources/public/error/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

Hey !! Where did you get this link!

5 |

Check our Communik page

6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/public/error/405.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

Hey !! You are supposed to be using a Rest Client and not the browser!

5 |

Check our Communik page

6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/public/error/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

Oops!! Something went wrong!

5 |

Our Engineers are on it

6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/samplerequest.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "SMS", 4 | "category": "transactional", 5 | "lob": "bank", 6 | "to": "+919999999999", 7 | "message": "Your salary of Rs 11000 has been credited" 8 | }, 9 | { 10 | "type": "SMS", 11 | "category": "transactional", 12 | "lob": "bank", 13 | "to": "+919916989922", 14 | "body": { 15 | "template": "sdfjq2378dsk8", 16 | "data": [ 17 | { 18 | "salary": 1222 19 | } 20 | ] 21 | } 22 | }, 23 | { 24 | "type": "EMAIL", 25 | "category": "transactional", 26 | "lob": "bank", 27 | "to": "test@example.com", 28 | "subject": "Your salary is here", 29 | "body": { 30 | "template": "sdfjq2378dsk8", 31 | "data": [ 32 | { 33 | "salary": 1222, 34 | "statement": 1222 35 | } 36 | ] 37 | }, 38 | "attachment": { 39 | "template": "sdfjq2378dsk7", 40 | "data": [ 41 | { 42 | "salary": 1222 43 | } 44 | ] 45 | } 46 | } 47 | ] -------------------------------------------------------------------------------- /src/main/resources/templates/email-test-default.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 102, 3 | "name": "defaultTemplate", 4 | "category": "Transactional", 5 | "lob": "all", 6 | "type": "EMAIL", 7 | "active": true, 8 | "owner": "HK", 9 | "replyTo": "noreply@example.com", 10 | "from": "noreply@example.com" 11 | } -------------------------------------------------------------------------------- /src/main/resources/templates/email-test-template-attachment.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 101, 3 | "name": "templateTest", 4 | "category": "Transactional", 5 | "lob": "bank", 6 | "type": "EMAIL", 7 | "active": true, 8 | "owner": "HK", 9 | "replyTo": "noreply@example.com", 10 | "from": "from@example.com", 11 | "mediaType": "text/html", 12 | "body": "<#assign currentDateTime = .now>\n Hi Mr ${data.to}
Your salary ${data.salary?string.currency} and ${data.timestamp}has been <#list data.mapTest as key, value> ${key} = ${value} credited on ${currentDateTime}\n
Team", 13 | "attachments": [ 14 | { 15 | "mediaType": "application/pdf", 16 | "method": "generate-pdf", 17 | "placement": "attachment", 18 | "name": "statement.pdf", 19 | "source": "This is my test pdf" 20 | }, 21 | { 22 | "mediaType": "application/html", 23 | "method": "generate-html", 24 | "placement": "attachment", 25 | "name": "statement.html", 26 | "source": "This is my test html" 27 | }, 28 | { 29 | "mediaType": "image/png", 30 | "method": "download", 31 | "placement": "inline", 32 | "name": "logo.png", 33 | "source": "https://www.wikipedia.org/portal/wikipedia.org/assets/img/Wikipedia-logo-v2.png" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /src/main/resources/templates/email-test-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 101, 3 | "name": "templateTest", 4 | "category": "Transactional", 5 | "lob": "bank", 6 | "type": "EMAIL", 7 | "active": true, 8 | "owner": "HK", 9 | "replyTo": "test@example.com", 10 | "from": "test@example.com", 11 | "body": "<#assign currentDateTime = .now>\n Hi Mr ${data.to}
Your salary ${data.salary?string.currency} and ${data.timestamp}has been <#list data.mapTest as key, value> ${key} = ${value} credited on ${currentDateTime}\n
Team" 12 | } -------------------------------------------------------------------------------- /src/main/resources/templates/sms-test-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 201, 3 | "name": "templateTest", 4 | "category": "Tansactional", 5 | "lob": "bank", 6 | "type": "SMS", 7 | "active": true, 8 | "owner": "HK", 9 | "body": "Hi ${data.to} Your salary ${message} and has been credited on " 10 | } -------------------------------------------------------------------------------- /src/test/java/in/codetripper/communik/Integration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik; 15 | 16 | public interface Integration extends Slow { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/in/codetripper/communik/Slow.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik; 15 | 16 | public interface Slow { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/in/codetripper/communik/Unit.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik; 15 | 16 | public interface Unit { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/in/codetripper/communik/domain/email/EmailIntegrationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.email; 15 | 16 | import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; 17 | import java.util.Arrays; 18 | import java.util.Collections; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | import org.junit.After; 22 | import org.junit.Before; 23 | import org.junit.Test; 24 | import org.junit.experimental.categories.Category; 25 | import org.junit.runner.RunWith; 26 | import org.springframework.beans.factory.annotation.Autowired; 27 | import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; 28 | import org.springframework.boot.test.context.SpringBootTest; 29 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; 30 | import org.springframework.http.MediaType; 31 | import org.springframework.test.context.junit4.SpringRunner; 32 | import org.springframework.test.web.reactive.server.WebTestClient; 33 | import org.springframework.web.reactive.function.BodyInserters; 34 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 35 | import com.github.tomakehurst.wiremock.WireMockServer; 36 | import in.codetripper.communik.Integration; 37 | import lombok.Data; 38 | import lombok.NoArgsConstructor; 39 | 40 | @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) 41 | @AutoConfigureWebTestClient 42 | @RunWith(SpringRunner.class) 43 | @Category(Integration.class) 44 | public class EmailIntegrationTest { 45 | 46 | // TODO add in memory mongo 47 | private EmailSampleRequest emailSampleRequest; 48 | @Autowired 49 | private WebTestClient webClient; 50 | 51 | private WireMockServer wireMockServer; 52 | 53 | @Before 54 | public void setup() throws Exception { 55 | wireMockServer = new WireMockServer(wireMockConfig().port(9999)); // No-args constructor will 56 | // start on port 8080, no 57 | // HTTPS 58 | wireMockServer.start(); 59 | 60 | 61 | } 62 | 63 | @After 64 | public void clean() { 65 | wireMockServer.stop(); 66 | } 67 | 68 | @Test 69 | public void testEmail() throws Exception { 70 | emailSampleRequest = new EmailSampleRequest(); 71 | EmailId TO = new EmailId("HK", "test@example.com"); 72 | emailSampleRequest.setTo(Arrays.asList(TO)); 73 | emailSampleRequest.setLocale("en_IN"); 74 | emailSampleRequest.setTemplateId("email-test-template"); 75 | emailSampleRequest.setProviderName("dummyMailer"); 76 | emailSampleRequest.setSubject("test subject"); 77 | Map body = new HashMap<>(); 78 | body.put("to", "Mr HK"); 79 | body.put("salary", 125000); 80 | body.put("timestamp", 125000); 81 | var dynamicData = new HashMap<>(); 82 | dynamicData.put("mykey", "Mr HK"); 83 | dynamicData.put("mykey1", 125000); 84 | body.put("mapTest", dynamicData); 85 | emailSampleRequest.setBody(body); 86 | webClient.post().uri("/email").contentType(MediaType.APPLICATION_JSON) 87 | .body(BodyInserters.fromObject(emailSampleRequest)).exchange().expectStatus().isOk() 88 | .expectBody(EmailSampleResponse.class).returnResult(); 89 | 90 | } 91 | 92 | @Test 93 | public void testEmailWithoutTemplateId() throws Exception { 94 | 95 | emailSampleRequest = new EmailSampleRequest(); 96 | EmailId TO = new EmailId("HK", "test@example.com"); 97 | emailSampleRequest.setTo(Arrays.asList(TO)); 98 | emailSampleRequest.setLocale("en_IN"); 99 | emailSampleRequest.setSubject("test subject"); 100 | emailSampleRequest.setProviderName("dummyMailer"); 101 | Map body = new HashMap<>(); 102 | body.put("message", "This is my test email"); 103 | emailSampleRequest.setBody(body); 104 | webClient.post().uri("/email").contentType(MediaType.APPLICATION_JSON) 105 | .body(BodyInserters.fromObject(emailSampleRequest)).exchange().expectStatus().isOk() 106 | .expectBody(EmailSampleResponse.class).returnResult(); 107 | 108 | } 109 | 110 | @Test 111 | public void testEmailWithoutProviderName() throws Exception { 112 | 113 | emailSampleRequest = new EmailSampleRequest(); 114 | EmailId TO = new EmailId("HK", "test@example.com"); 115 | emailSampleRequest.setTo(Collections.singletonList(TO)); 116 | emailSampleRequest.setLocale("en_IN"); 117 | emailSampleRequest.setSubject("test subject"); 118 | Map body = new HashMap<>(); 119 | body.put("message", "This is my test email"); 120 | emailSampleRequest.setBody(body); 121 | webClient.post().uri("/email").contentType(MediaType.APPLICATION_JSON) 122 | .body(BodyInserters.fromObject(emailSampleRequest)).exchange().expectStatus().isOk() 123 | .expectBody(EmailSampleResponse.class).returnResult(); 124 | 125 | } 126 | 127 | 128 | @JsonIgnoreProperties(ignoreUnknown = false) 129 | @Data 130 | @NoArgsConstructor 131 | public static class EmailSampleResponse { 132 | 133 | private String timestamp; 134 | private int status; 135 | private String message; 136 | private String responseId; 137 | private String traceId; 138 | private String providerResponseId; 139 | private String providerResponseMessage; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/test/java/in/codetripper/communik/domain/email/EmailSampleRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.email; 15 | 16 | import java.util.List; 17 | import java.util.Map; 18 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 19 | import in.codetripper.communik.domain.notification.Type; 20 | 21 | @JsonIgnoreProperties(ignoreUnknown = true) 22 | public class EmailSampleRequest { 23 | 24 | private List to; 25 | private String subject; 26 | private Type type; 27 | private Map body; 28 | private Map attachment; 29 | private String templateId; 30 | private String providerName; 31 | private String locale; 32 | private String replyTo; 33 | private String ipAddress; 34 | 35 | 36 | public List getTo() { 37 | return to; 38 | } 39 | 40 | public void setTo(List to) { 41 | this.to = to; 42 | } 43 | 44 | public String getSubject() { 45 | return subject; 46 | } 47 | 48 | public void setSubject(String subject) { 49 | this.subject = subject; 50 | } 51 | 52 | public Type getType() { 53 | return type; 54 | } 55 | 56 | public void setType(Type type) { 57 | this.type = type; 58 | } 59 | 60 | public Map getBody() { 61 | return body; 62 | } 63 | 64 | public void setBody(Map body) { 65 | this.body = body; 66 | } 67 | 68 | public Map getAttachment() { 69 | return attachment; 70 | } 71 | 72 | public void setAttachment(Map attachment) { 73 | this.attachment = attachment; 74 | } 75 | 76 | public String getProviderName() { 77 | return providerName; 78 | } 79 | 80 | public void setProviderName(String providerName) { 81 | this.providerName = providerName; 82 | } 83 | 84 | public String getLocale() { 85 | return locale; 86 | } 87 | 88 | public void setLocale(String locale) { 89 | this.locale = locale; 90 | } 91 | 92 | public String getReplyTo() { 93 | return replyTo; 94 | } 95 | 96 | public void setReplyTo(String replyTo) { 97 | this.replyTo = replyTo; 98 | } 99 | 100 | public String getIpAddress() { 101 | return ipAddress; 102 | } 103 | 104 | public void setIpAddress(String ipAddress) { 105 | this.ipAddress = ipAddress; 106 | } 107 | 108 | public String getTemplateId() { 109 | return templateId; 110 | } 111 | 112 | public void setTemplateId(String templateId) { 113 | this.templateId = templateId; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/test/java/in/codetripper/communik/domain/sms/SmsControllerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.sms; 15 | 16 | import static org.junit.Assert.assertEquals; 17 | import static org.junit.Assert.assertTrue; 18 | import static org.mockito.ArgumentMatchers.any; 19 | import static org.mockito.BDDMockito.given; 20 | import java.util.Arrays; 21 | import java.util.HashMap; 22 | import java.util.Map; 23 | import java.util.UUID; 24 | import org.junit.Before; 25 | import org.junit.Test; 26 | import org.junit.experimental.categories.Category; 27 | import org.junit.runner.RunWith; 28 | import org.springframework.beans.factory.annotation.Autowired; 29 | import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; 30 | import org.springframework.boot.test.mock.mockito.MockBean; 31 | import org.springframework.http.MediaType; 32 | import org.springframework.test.context.junit4.SpringRunner; 33 | import org.springframework.test.web.reactive.server.WebTestClient; 34 | import org.springframework.web.reactive.function.BodyInserters; 35 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 36 | import in.codetripper.communik.Unit; 37 | import in.codetripper.communik.domain.notification.NotificationStatusResponse; 38 | import reactor.core.publisher.Mono; 39 | 40 | @WebFluxTest(controllers = SmsController.class) 41 | @RunWith(SpringRunner.class) 42 | @Category(Unit.class) 43 | 44 | public class SmsControllerTest { 45 | 46 | private SmsSampleRequest emailSampleRequest; 47 | @Autowired 48 | private WebTestClient webClient; 49 | @MockBean 50 | private SmsService smsService; 51 | 52 | @Before 53 | public void setup() throws Exception { 54 | String TO = "+919999999999"; 55 | String LOCALE = "en_IN"; 56 | String TEMPLATE = "sms-test-template"; 57 | String PROVIDER = "dummyMailer"; 58 | String MESSAGE = "MR HK"; 59 | emailSampleRequest = new SmsSampleRequest(); 60 | emailSampleRequest.setTo(Arrays.asList(TO)); 61 | emailSampleRequest.setLocale(LOCALE); 62 | emailSampleRequest.setTemplateId(TEMPLATE); 63 | emailSampleRequest.setProviderName(PROVIDER); 64 | Map b1 = new HashMap<>(); 65 | emailSampleRequest.setBody(b1); 66 | SmsDto smsDto = new SmsDto(); 67 | smsDto.setTo(Arrays.asList(TO)); 68 | smsDto.setLocale(LOCALE); 69 | smsDto.setProviderName(PROVIDER); 70 | smsDto.setTemplateId(TEMPLATE); 71 | SmsDto.Container container = new SmsDto.Container(); 72 | Map b = new HashMap<>(); 73 | container.setData(b); 74 | smsDto.setBody(container); 75 | 76 | NotificationStatusResponse notificationStatusResponse = new NotificationStatusResponse(); 77 | notificationStatusResponse.setStatus(200); 78 | notificationStatusResponse.setMessage("SUCCESS"); 79 | notificationStatusResponse.setResponseId(UUID.randomUUID().toString()); 80 | given(smsService.sendSms(any(SmsDto.class))).willReturn(Mono.just(notificationStatusResponse)); 81 | // given(smsService.sendEmail(any(EmailDto.class))).willReturn(Mono.just(notificationStatusResponse)); 82 | } 83 | 84 | 85 | @Test 86 | public void testSms() throws Exception { 87 | 88 | SmsSampleResponse response = 89 | webClient.post().uri("/sms").contentType(MediaType.APPLICATION_JSON) 90 | .body(BodyInserters.fromObject(emailSampleRequest)).exchange().expectStatus().isOk() 91 | .expectBody(SmsSampleResponse.class).returnResult().getResponseBody(); 92 | 93 | assertEquals("SUCCESS", response.getMessage()); 94 | assertTrue(!response.getResponseId().isEmpty()); 95 | } 96 | 97 | 98 | @JsonIgnoreProperties(ignoreUnknown = true) 99 | 100 | public static class SmsSampleResponse { 101 | 102 | private String timestamp; 103 | private int status; 104 | private String message; 105 | private String responseId; 106 | private String traceId; 107 | private String providerResponseId; 108 | private String providerResponseMessage; 109 | 110 | public String getTimestamp() { 111 | return timestamp; 112 | } 113 | 114 | public void setTimestamp(String timestamp) { 115 | this.timestamp = timestamp; 116 | } 117 | 118 | public int getStatus() { 119 | return status; 120 | } 121 | 122 | public void setStatus(int status) { 123 | this.status = status; 124 | } 125 | 126 | public String getMessage() { 127 | return message; 128 | } 129 | 130 | public void setMessage(String message) { 131 | this.message = message; 132 | } 133 | 134 | public String getResponseId() { 135 | return responseId; 136 | } 137 | 138 | public void setResponseId(String responseId) { 139 | this.responseId = responseId; 140 | } 141 | 142 | public String getTraceId() { 143 | return traceId; 144 | } 145 | 146 | public void setTraceId(String traceId) { 147 | this.traceId = traceId; 148 | } 149 | 150 | public String getProviderResponseId() { 151 | return providerResponseId; 152 | } 153 | 154 | public void setProviderResponseId(String providerResponseId) { 155 | this.providerResponseId = providerResponseId; 156 | } 157 | 158 | public String getProviderResponseMessage() { 159 | return providerResponseMessage; 160 | } 161 | 162 | public void setProviderResponseMessage(String providerResponseMessage) { 163 | this.providerResponseMessage = providerResponseMessage; 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/test/java/in/codetripper/communik/domain/sms/SmsIntegrationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.sms; 15 | 16 | import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; 17 | import java.util.Arrays; 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | import org.junit.After; 21 | import org.junit.Before; 22 | import org.junit.Test; 23 | import org.junit.experimental.categories.Category; 24 | import org.junit.runner.RunWith; 25 | import org.springframework.beans.factory.annotation.Autowired; 26 | import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; 27 | import org.springframework.boot.test.context.SpringBootTest; 28 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; 29 | import org.springframework.http.MediaType; 30 | import org.springframework.test.context.junit4.SpringRunner; 31 | import org.springframework.test.web.reactive.server.WebTestClient; 32 | import org.springframework.web.reactive.function.BodyInserters; 33 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 34 | import com.github.tomakehurst.wiremock.WireMockServer; 35 | import in.codetripper.communik.Integration; 36 | import lombok.Data; 37 | import lombok.NoArgsConstructor; 38 | 39 | @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) 40 | @AutoConfigureWebTestClient 41 | @RunWith(SpringRunner.class) 42 | @Category(Integration.class) 43 | public class SmsIntegrationTest { 44 | 45 | private SmsSampleRequest smsSampleRequest; 46 | @Autowired 47 | private WebTestClient webClient; 48 | private WireMockServer wireMockServer; 49 | 50 | @Before 51 | public void setup() throws Exception { 52 | wireMockServer = new WireMockServer(wireMockConfig().port(9999)); // No-args constructor will 53 | wireMockServer.start(); 54 | 55 | smsSampleRequest = new SmsSampleRequest(); 56 | smsSampleRequest.setTo(Arrays.asList("9999999999")); 57 | smsSampleRequest.setLocale("en_IN"); 58 | smsSampleRequest.setTemplateId("sms-test-template"); 59 | smsSampleRequest.setProviderName("gupchup"); 60 | Map body = new HashMap<>(); 61 | body.put("to", "Mr HK"); 62 | body.put("message", 125000); 63 | body.put("timestamp", 125000); 64 | var dynamicData = new HashMap<>(); 65 | dynamicData.put("mykey", "Mr HK"); 66 | dynamicData.put("mykey1", 125000); 67 | body.put("mapTest", dynamicData); 68 | smsSampleRequest.setBody(body); 69 | } 70 | 71 | @After 72 | public void clean() { 73 | wireMockServer.stop(); 74 | } 75 | 76 | @Test 77 | public void testSms() throws Exception { 78 | 79 | // BDDMockito.given(this.emailService.sendEmail()).willReturn() 80 | 81 | webClient.post().uri("/sms").contentType(MediaType.APPLICATION_JSON) 82 | .body(BodyInserters.fromObject(smsSampleRequest)).exchange().expectStatus().isOk() 83 | .expectBody(SmsSampleResponse.class).returnResult(); 84 | 85 | } 86 | 87 | 88 | @JsonIgnoreProperties(ignoreUnknown = false) 89 | @Data 90 | @NoArgsConstructor 91 | public static class SmsSampleResponse { 92 | 93 | private String timestamp; 94 | private int status; 95 | private String message; 96 | private String responseId; 97 | private String traceId; 98 | private String providerResponseId; 99 | private String providerResponseMessage; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/test/java/in/codetripper/communik/domain/sms/SmsSampleRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 CodeTripper 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package in.codetripper.communik.domain.sms; 15 | 16 | import java.util.List; 17 | import java.util.Map; 18 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 19 | 20 | @JsonIgnoreProperties(ignoreUnknown = true) 21 | 22 | public class SmsSampleRequest { 23 | 24 | private List to; 25 | private String locale; 26 | private String templateId; 27 | private String providerName; 28 | private Map body; 29 | 30 | public List getTo() { 31 | return to; 32 | } 33 | 34 | public void setTo(List to) { 35 | this.to = to; 36 | } 37 | 38 | public String getLocale() { 39 | return locale; 40 | } 41 | 42 | public void setLocale(String locale) { 43 | this.locale = locale; 44 | } 45 | 46 | public String getTemplateId() { 47 | return templateId; 48 | } 49 | 50 | public void setTemplateId(String templateId) { 51 | this.templateId = templateId; 52 | } 53 | 54 | public String getProviderName() { 55 | return providerName; 56 | } 57 | 58 | public void setProviderName(String providerName) { 59 | this.providerName = providerName; 60 | } 61 | 62 | public Map getBody() { 63 | return body; 64 | } 65 | 66 | public void setBody(Map body) { 67 | this.body = body; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/test/resources/__files/gupchup.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": true 3 | } 4 | -------------------------------------------------------------------------------- /src/test/resources/__files/sendgrid.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": true, 3 | "id": "12232323" 4 | } 5 | -------------------------------------------------------------------------------- /src/test/resources/__files/twofactor.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "success" 3 | } 4 | -------------------------------------------------------------------------------- /src/test/resources/mappings/sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "mappings": [ 3 | { 4 | "request": { 5 | "method": "POST", 6 | "url": "/api/webhook" 7 | }, 8 | "response": { 9 | "status": 200 10 | } 11 | }, 12 | { 13 | "request": { 14 | "method": "POST", 15 | "url": "/sms/gupchup" 16 | }, 17 | "response": { 18 | "status": 200, 19 | "fixedDelayMilliseconds": 500, 20 | "bodyFileName": "gupchup.json", 21 | "headers": { 22 | "Content-Type": "application/json" 23 | } 24 | } 25 | }, 26 | { 27 | "request": { 28 | "method": "POST", 29 | "url": "/email/sendgrid" 30 | }, 31 | "response": { 32 | "status": 200, 33 | "bodyFileName": "sendgrid.json", 34 | "headers": { 35 | "Content-Type": "application/json" 36 | } 37 | } 38 | }, 39 | { 40 | "request": { 41 | "method": "POST", 42 | "url": "/sms/twofactor" 43 | }, 44 | "response": { 45 | "status": 200, 46 | "bodyFileName": "twofactor.json" 47 | } 48 | } 49 | ] 50 | } 51 | --------------------------------------------------------------------------------