diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3ee78394..256284e3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,22 +22,11 @@ jobs: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v2 - - uses: actions/setup-java@v1 + - uses: actions/setup-java@v3 with: - java-version: 11 - - - name: Cache - uses: actions/cache@v2.0.0 - with: - # Cache gradle directories - path: | - ~/.gradle/caches/jars-3 - ~/.gradle/caches/modules-2/files-2.1/ - ~/.gradle/caches/modules-2/metadata-2.96/ - ~/.gradle/native - ~/.gradle/wrapper - # Key for restoring and saving the cache - key: ${{ runner.os }}-gradle + distribution: 'temurin' + java-version: '11' + cache: 'gradle' # Compile the code - name: Compile code diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5610788f..ce3fa6de 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,22 +14,12 @@ jobs: steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v2 - - uses: actions/setup-java@v1 - with: - java-version: 11 - - name: Cache - uses: actions/cache@v2.0.0 + - uses: actions/setup-java@v3 with: - # A list of files, directories, and wildcard patterns to cache and restore - path: | - ~/.gradle/caches/jars-3 - ~/.gradle/caches/modules-2/files-2.1/ - ~/.gradle/caches/modules-2/metadata-2.96/ - ~/.gradle/native - ~/.gradle/wrapper - # An explicit key for restoring and saving the cache - key: ${{ runner.os }}-gradle + distribution: 'temurin' + java-version: '11' + cache: 'gradle' # Compile code - name: Compile code diff --git a/build.gradle b/build.gradle index 99b1b83a..69a31e39 100644 --- a/build.gradle +++ b/build.gradle @@ -3,9 +3,14 @@ plugins { id "com.github.lkishalmi.gatling" version "3.3.4" id 'com.github.johnrengelman.shadow' version '7.1.2' id 'org.springframework.boot' version "2.6.6" + id "org.jetbrains.kotlin.plugin.spring" version "1.6.20" + id "org.jetbrains.kotlin.plugin.jpa" version "1.6.20" + id "org.jetbrains.kotlin.jvm" version "1.6.20" + id "org.jetbrains.kotlin.kapt" version "1.6.20" } apply plugin: 'checkstyle' +apply plugin: 'kotlin' apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'io.spring.dependency-management' @@ -21,8 +26,14 @@ repositories { bootJar { mainClass = 'org.radarbase.appserver.AppserverApplication' + duplicatesStrategy = 'include' } +jar { + duplicatesStrategy = 'include' +} + + //apply from: 'gradle/liquibase.gradle' ext { @@ -37,6 +48,8 @@ ext { } sourceSets { + main.kotlin.srcDirs += 'src/main/java' + integrationTest { java { compileClasspath += main.output + test.output + test.compileClasspath @@ -47,6 +60,15 @@ sourceSets { } } +kapt { + keepJavacAnnotationProcessors = true +} + +allOpen { + annotation("javax.persistence.Entity") + annotation("javax.persistence.Embeddable") + annotation("javax.persistence.MappedSuperclass") +} dependencies { implementation('org.springframework.boot:spring-boot-starter-data-jpa') @@ -57,6 +79,15 @@ dependencies { implementation('org.springframework.boot:spring-boot-starter-actuator') implementation('org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:' + springBootVersion) implementation('org.springframework.security.oauth:spring-security-oauth2:' + springOauth2Version) + + implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.jetbrains.kotlin:kotlin-reflect") + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + + kapt("org.springframework.boot:spring-boot-configuration-processor") + kapt("org.springframework:spring-context-indexer:5.3.18") + kapt("org.projectlombok:lombok:" + lombokVersion) + implementation group: 'com.pivovarit', name: 'throwing-function', version: throwingFunctionVersion implementation(group: 'de.codecentric', name: 'spring-boot-admin-starter-client', version: bootAdminVersion) @@ -95,7 +126,9 @@ dependencies { testImplementation('org.springframework.boot:spring-boot-starter-test') { exclude group: 'org.junit', module: 'junit' + //exclude group: 'org.mockito', module: 'mockito-core' } + //testImplementation("com.ninja-squad:springmockk:3.1.1") testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: junit5Version testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: junit5Version @@ -105,6 +138,7 @@ dependencies { testImplementation group: 'org.junit.platform', name: 'junit-platform-engine', version: '1.8.2' gatling('com.fasterxml.jackson.datatype:jackson-datatype-jsr310') + } checkstyle { @@ -114,6 +148,10 @@ checkstyle { sourceSets = [it.sourceSets.main] } +pmd { + toolVersion = "6.44.0" +} + javadoc { options.addBooleanOption('html5', true) destinationDir = new File("${project.rootDir}/src/main/resources/static/java-docs".toString()) @@ -183,3 +221,13 @@ test { } } } +compileKotlin { + kotlinOptions { + jvmTarget = "11" + } +} +compileTestKotlin { + kotlinOptions { + jvmTarget = "11" + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f..41d9927a 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradlew.bat b/gradlew.bat index 477c8966..107acd32 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -86,4 +86,4 @@ exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal -:omega \ No newline at end of file +:omega diff --git a/src/main/java/org/radarbase/appserver/AppserverApplication.java b/src/main/java/org/radarbase/appserver/AppserverApplication.kt similarity index 68% rename from src/main/java/org/radarbase/appserver/AppserverApplication.java rename to src/main/java/org/radarbase/appserver/AppserverApplication.kt index cb893625..2e29b209 100644 --- a/src/main/java/org/radarbase/appserver/AppserverApplication.java +++ b/src/main/java/org/radarbase/appserver/AppserverApplication.kt @@ -18,20 +18,23 @@ * * * */ +package org.radarbase.appserver -package org.radarbase.appserver; +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration +import org.springframework.boot.runApplication -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration; - -/** @author yatharthranjan */ +/** @author yatharthranjan + */ @SpringBootApplication( - exclude = {SolrAutoConfiguration.class}, - scanBasePackages = {"org.radarbase.appserver", "org.radarbase.fcm"}) -public class AppserverApplication { + exclude = [SolrAutoConfiguration::class], + scanBasePackages = [ + "org.radarbase.appserver", + "org.radarbase.fcm", + ], +) +class AppserverApplication - public static void main(String[] args) { - SpringApplication.run(AppserverApplication.class, args); - } -} +fun main(args: Array) { + runApplication(*args) +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/config/ApplicationConfig.java b/src/main/java/org/radarbase/appserver/config/ApplicationConfig.kt similarity index 73% rename from src/main/java/org/radarbase/appserver/config/ApplicationConfig.java rename to src/main/java/org/radarbase/appserver/config/ApplicationConfig.kt index a3ccd963..c143ca1a 100644 --- a/src/main/java/org/radarbase/appserver/config/ApplicationConfig.java +++ b/src/main/java/org/radarbase/appserver/config/ApplicationConfig.kt @@ -18,19 +18,20 @@ * * * */ +package org.radarbase.appserver.config -package org.radarbase.appserver.config; - -import org.radarbase.fcm.config.FcmServerConfig; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.jpa.repository.config.EnableJpaAuditing; -import org.springframework.scheduling.annotation.EnableAsync; -import org.springframework.transaction.annotation.EnableTransactionManagement; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing +import org.springframework.boot.context.properties.EnableConfigurationProperties +import org.radarbase.fcm.config.FcmServerConfig +import org.springframework.context.annotation.Configuration +import org.springframework.transaction.annotation.EnableTransactionManagement +import org.springframework.scheduling.annotation.EnableAsync @Configuration @EnableJpaAuditing -@EnableConfigurationProperties({FcmServerConfig.class}) +@EnableConfigurationProperties( + FcmServerConfig::class +) @EnableTransactionManagement @EnableAsync -public class ApplicationConfig {} +class ApplicationConfig \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/converter/Converter.java b/src/main/java/org/radarbase/appserver/converter/Converter.kt similarity index 52% rename from src/main/java/org/radarbase/appserver/converter/Converter.java rename to src/main/java/org/radarbase/appserver/converter/Converter.kt index b5a9d781..699d0c0c 100644 --- a/src/main/java/org/radarbase/appserver/converter/Converter.java +++ b/src/main/java/org/radarbase/appserver/converter/Converter.kt @@ -18,33 +18,28 @@ * * * */ +package org.radarbase.appserver.converter -package org.radarbase.appserver.converter; - -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; /** - * Generic converter class for conversions between entity {@link org.radarbase.appserver.entity} and - * DTO {@link org.radarbase.appserver.dto} objects. + * Generic converter class for conversions between entity [org.radarbase.appserver.entity] and + * DTO [org.radarbase.appserver.dto] objects. * * @param the entity object class * @param the DTO object class * @author yatharthranjan - *

TODO - Use MapStruct for mapping entities and DTOs (http://mapstruct.org/) - */ -public interface Converter { - - T dtoToEntity(S s); - - S entityToDto(T t); + * + * TODO - Use MapStruct for mapping entities and DTOs (http://mapstruct.org/) + */ +interface Converter { + fun dtoToEntity(s: S): T + fun entityToDto(t: T): S - default List dtosToEntities(Collection ss) { - return ss.parallelStream().map(this::dtoToEntity).collect(Collectors.toList()); - } + fun dtosToEntities(ss: MutableCollection): MutableList { + return ss.map { s: S -> dtoToEntity(s) }.toMutableList() + } - default List entitiesToDtos(Collection ts) { - return ts.parallelStream().map(this::entityToDto).collect(Collectors.toList()); - } -} + fun entitiesToDtos(ts: MutableCollection): MutableList { + return ts.map { t: T -> entityToDto(t) }.toMutableList() + } +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/converter/DataMessageConverter.java b/src/main/java/org/radarbase/appserver/converter/DataMessageConverter.java deleted file mode 100644 index 440e0444..00000000 --- a/src/main/java/org/radarbase/appserver/converter/DataMessageConverter.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * - * * - * * * Copyright 2018 King's College London - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * * - * * - * - */ - -package org.radarbase.appserver.converter; - -import org.radarbase.appserver.dto.fcm.FcmDataMessageDto; -import org.radarbase.appserver.entity.DataMessage; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.context.annotation.Scope; -import org.springframework.stereotype.Component; - -/** - * Converter {@link Converter} class for {@link DataMessage} entity. - * - * @author yatharthranjan - */ -@Component -@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON) -public class DataMessageConverter implements Converter { - - @Override - public DataMessage dtoToEntity(FcmDataMessageDto dataMessageDto) { - - return new DataMessage.DataMessageBuilder() - .mutableContent(dataMessageDto.isMutableContent()) - .priority(dataMessageDto.getPriority()) - .fcmCondition(dataMessageDto.getFcmCondition()) - .fcmTopic(dataMessageDto.getFcmTopic()) - .fcmMessageId(String.valueOf(dataMessageDto.hashCode())) - .appPackage(dataMessageDto.getAppPackage()) - .sourceType(dataMessageDto.getSourceType()) - .sourceId(dataMessageDto.getSourceId()) - .ttlSeconds(dataMessageDto.getTtlSeconds()) - .scheduledTime(dataMessageDto.getScheduledTime()) - .dataMap(dataMessageDto.getDataMap()) - .build(); - } - - @Override - public FcmDataMessageDto entityToDto(DataMessage dataMessage) { - return new FcmDataMessageDto(dataMessage); - } -} diff --git a/src/main/java/org/radarbase/appserver/converter/DataMessageConverter.kt b/src/main/java/org/radarbase/appserver/converter/DataMessageConverter.kt new file mode 100644 index 00000000..8365312f --- /dev/null +++ b/src/main/java/org/radarbase/appserver/converter/DataMessageConverter.kt @@ -0,0 +1,57 @@ +/* + * + * * + * * * Copyright 2018 King's College London + * * * + * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * you may not use this file except in compliance with the License. + * * * You may obtain a copy of the License at + * * * + * * * http://www.apache.org/licenses/LICENSE-2.0 + * * * + * * * Unless required by applicable law or agreed to in writing, software + * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * See the License for the specific language governing permissions and + * * * limitations under the License. + * * * + * * + * + */ +package org.radarbase.appserver.converter + +import org.radarbase.appserver.dto.fcm.FcmDataMessageDto +import org.radarbase.appserver.entity.DataMessage +import org.springframework.beans.factory.config.ConfigurableBeanFactory +import org.springframework.context.annotation.Scope +import org.springframework.stereotype.Component + +/** + * Converter [Converter] class for [DataMessage] entity. + * + * @author yatharthranjan + */ +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON) +class DataMessageConverter : Converter { + + override fun dtoToEntity(dataMessageDto: FcmDataMessageDto): DataMessage { + return DataMessage().apply { + mutableContent = dataMessageDto.isMutableContent + priority = dataMessageDto.priority + fcmCondition = dataMessageDto.fcmCondition + fcmTopic = dataMessageDto.fcmTopic + fcmMessageId = dataMessageDto.hashCode().toString() + appPackage = dataMessageDto.appPackage + sourceType = dataMessageDto.sourceType + sourceId = dataMessageDto.sourceId + ttlSeconds = dataMessageDto.ttlSeconds + scheduledTime = dataMessageDto.scheduledTime + dataMap = dataMessageDto.dataMap + } + } + + override fun entityToDto(dataMessage: DataMessage): FcmDataMessageDto { + return FcmDataMessageDto(dataMessage) + } +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/converter/NotificationConverter.java b/src/main/java/org/radarbase/appserver/converter/NotificationConverter.java deleted file mode 100644 index 9629d36c..00000000 --- a/src/main/java/org/radarbase/appserver/converter/NotificationConverter.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * - * * - * * * Copyright 2018 King's College London - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * * - * * - * - */ - -package org.radarbase.appserver.converter; - -import org.radarbase.appserver.dto.fcm.FcmNotificationDto; -import org.radarbase.appserver.entity.Notification; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.context.annotation.Scope; -import org.springframework.stereotype.Component; - -/** - * Converter {@link Converter} class for {@link Notification} entity. - * - * @author yatharthranjan - */ -@Component -@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON) -public class NotificationConverter implements Converter { - - @Override - public Notification dtoToEntity(FcmNotificationDto notificationDto) { - - return new Notification.NotificationBuilder() - .body(notificationDto.getBody()) - .scheduledTime(notificationDto.getScheduledTime()) - .title(notificationDto.getTitle()) - .sourceId(notificationDto.getSourceId()) - .type(notificationDto.getType()) - .ttlSeconds(notificationDto.getTtlSeconds()) - .fcmMessageId(String.valueOf(notificationDto.hashCode())) - .appPackage(notificationDto.getAppPackage()) - .sourceType(notificationDto.getSourceType()) - .additionalData(notificationDto.getAdditionalData()) - .androidChannelId(notificationDto.getAndroidChannelId()) - .bodyLocArgs(notificationDto.getBodyLocArgs()) - .bodyLocKey(notificationDto.getBodyLocKey()) - .titleLocKey(notificationDto.getTitleLocKey()) - .titleLocArgs(notificationDto.getTitleLocArgs()) - .badge(notificationDto.getBadge()) - .clickAction(notificationDto.getClickAction()) - .color(notificationDto.getColor()) - .fcmCondition(notificationDto.getFcmCondition()) - .fcmTopic(notificationDto.getFcmTopic()) - .icon(notificationDto.getIcon()) - .mutableContent(notificationDto.isMutableContent()) - .priority(notificationDto.getPriority()) - .sound(notificationDto.getSound()) - .subtitle(notificationDto.getSubtitle()) - .tag(notificationDto.getTag()) - .build(); - } - - @Override - public FcmNotificationDto entityToDto(Notification notification) { - return new FcmNotificationDto(notification); - } -} diff --git a/src/main/java/org/radarbase/appserver/converter/NotificationConverter.kt b/src/main/java/org/radarbase/appserver/converter/NotificationConverter.kt new file mode 100644 index 00000000..97252983 --- /dev/null +++ b/src/main/java/org/radarbase/appserver/converter/NotificationConverter.kt @@ -0,0 +1,72 @@ +/* + * + * * + * * * Copyright 2018 King's College London + * * * + * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * you may not use this file except in compliance with the License. + * * * You may obtain a copy of the License at + * * * + * * * http://www.apache.org/licenses/LICENSE-2.0 + * * * + * * * Unless required by applicable law or agreed to in writing, software + * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * See the License for the specific language governing permissions and + * * * limitations under the License. + * * * + * * + * + */ +package org.radarbase.appserver.converter + +import org.radarbase.appserver.dto.fcm.FcmNotificationDto +import org.radarbase.appserver.entity.Notification +import org.springframework.beans.factory.config.ConfigurableBeanFactory +import org.springframework.context.annotation.Scope +import org.springframework.stereotype.Component + +/** + * Converter [Converter] class for [Notification] entity. + * + * @author yatharthranjan + */ +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON) +class NotificationConverter : Converter { + + override fun dtoToEntity(notificationDto: FcmNotificationDto): Notification { + return Notification().apply { + body = notificationDto.body + scheduledTime = notificationDto.scheduledTime + title = notificationDto.title + sourceId = notificationDto.sourceId + type = notificationDto.type + ttlSeconds = notificationDto.ttlSeconds + fcmMessageId = notificationDto.hashCode().toString() + appPackage = notificationDto.appPackage + sourceType = notificationDto.sourceType + additionalData = notificationDto.additionalData + androidChannelId = notificationDto.androidChannelId + bodyLocArgs = notificationDto.bodyLocArgs + bodyLocKey = notificationDto.bodyLocKey + titleLocArgs = notificationDto.titleLocArgs + titleLocKey = notificationDto.titleLocKey + badge = notificationDto.badge + clickAction = notificationDto.clickAction + color = notificationDto.color + fcmCondition = notificationDto.fcmCondition + fcmTopic = notificationDto.fcmTopic + icon = notificationDto.icon + mutableContent = notificationDto.isMutableContent + priority = notificationDto.priority + sound = notificationDto.sound + subtitle = notificationDto.subtitle + tag = notificationDto.tag + } + } + + override fun entityToDto(notification: Notification): FcmNotificationDto { + return FcmNotificationDto(notification) + } +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/converter/ProjectConverter.java b/src/main/java/org/radarbase/appserver/converter/ProjectConverter.kt similarity index 51% rename from src/main/java/org/radarbase/appserver/converter/ProjectConverter.java rename to src/main/java/org/radarbase/appserver/converter/ProjectConverter.kt index ab7f00ba..b29b77eb 100644 --- a/src/main/java/org/radarbase/appserver/converter/ProjectConverter.java +++ b/src/main/java/org/radarbase/appserver/converter/ProjectConverter.kt @@ -18,37 +18,31 @@ * * * */ +package org.radarbase.appserver.converter -package org.radarbase.appserver.converter; - -import org.radarbase.appserver.dto.ProjectDto; -import org.radarbase.appserver.entity.Project; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.context.annotation.Scope; -import org.springframework.stereotype.Component; +import org.springframework.beans.factory.config.ConfigurableBeanFactory +import org.radarbase.appserver.dto.ProjectDto +import org.radarbase.appserver.entity.Project +import org.springframework.context.annotation.Scope +import org.springframework.stereotype.Component /** - * Converter {@link Converter} class for {@link Project} entity. + * Converter [Converter] class for [Project] entity. * * @author yatharthranjan */ @Component @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON) -public class ProjectConverter implements Converter { - - @Override - public Project dtoToEntity(ProjectDto projectDto) { - - return new Project().setProjectId(projectDto.getProjectId()); - } - - @Override - public ProjectDto entityToDto(Project project) { +class ProjectConverter : Converter { + override fun dtoToEntity(projectDto: ProjectDto): Project { + return Project().setProjectId(projectDto.projectId) + } - return new ProjectDto() - .setId(project.getId()) - .setProjectId(project.getProjectId()) - .setCreatedAt(project.getCreatedAt().toInstant()) - .setUpdatedAt(project.getUpdatedAt().toInstant()); - } -} + override fun entityToDto(project: Project): ProjectDto { + return ProjectDto() + .setId(project.id) + .setProjectId(project.projectId) + .setCreatedAt(project.createdAt?.toInstant()) + .setUpdatedAt(project.updatedAt?.toInstant()) + } +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/converter/UserConverter.java b/src/main/java/org/radarbase/appserver/converter/UserConverter.java deleted file mode 100644 index aa930799..00000000 --- a/src/main/java/org/radarbase/appserver/converter/UserConverter.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * - * * - * * * Copyright 2018 King's College London - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * * - * * - * - */ - -package org.radarbase.appserver.converter; - -import java.time.LocalDateTime; -import java.time.ZoneOffset; -import org.radarbase.appserver.dto.fcm.FcmUserDto; -import org.radarbase.appserver.entity.User; -import org.radarbase.appserver.entity.UserMetrics; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.context.annotation.Scope; -import org.springframework.stereotype.Component; - -/** - * Converter {@link Converter} class for {@link User} entity and {@link FcmUserDto} DTO. - * - * @author yatharthranjan - */ -@Component -@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON) -public class UserConverter implements Converter { - - public static UserMetrics getValidUserMetrics(FcmUserDto fcmUserDto) { - UserMetrics userMetrics; - if (fcmUserDto.getLastOpened() == null && fcmUserDto.getLastDelivered() == null) { - userMetrics = new UserMetrics(LocalDateTime.now().toInstant(ZoneOffset.UTC), null); - } else if (fcmUserDto.getLastDelivered() == null) { - userMetrics = new UserMetrics(fcmUserDto.getLastOpened(), null); - } else if (fcmUserDto.getLastOpened() == null) { - userMetrics = - new UserMetrics( - LocalDateTime.now().toInstant(ZoneOffset.UTC), fcmUserDto.getLastDelivered()); - } else { - userMetrics = new UserMetrics(fcmUserDto.getLastOpened(), fcmUserDto.getLastDelivered()); - } - - return userMetrics; - } - - @Override - public User dtoToEntity(FcmUserDto fcmUserDto) { - - return new User() - .setFcmToken(fcmUserDto.getFcmToken()) - .setSubjectId(fcmUserDto.getSubjectId()) - .setUserMetrics(getValidUserMetrics(fcmUserDto)) - .setEnrolmentDate(fcmUserDto.getEnrolmentDate()) - .setTimezone(fcmUserDto.getTimezone()) - .setLanguage(fcmUserDto.getLanguage()); - } - - @Override - public FcmUserDto entityToDto(User user) { - return new FcmUserDto(user); - } -} diff --git a/src/main/java/org/radarbase/appserver/converter/UserConverter.kt b/src/main/java/org/radarbase/appserver/converter/UserConverter.kt new file mode 100644 index 00000000..a06f2cb4 --- /dev/null +++ b/src/main/java/org/radarbase/appserver/converter/UserConverter.kt @@ -0,0 +1,73 @@ +/* + * + * * + * * * Copyright 2018 King's College London + * * * + * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * you may not use this file except in compliance with the License. + * * * You may obtain a copy of the License at + * * * + * * * http://www.apache.org/licenses/LICENSE-2.0 + * * * + * * * Unless required by applicable law or agreed to in writing, software + * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * See the License for the specific language governing permissions and + * * * limitations under the License. + * * * + * * + * + */ +package org.radarbase.appserver.converter + +import org.radarbase.appserver.dto.fcm.FcmUserDto +import org.radarbase.appserver.entity.User +import org.radarbase.appserver.entity.UserMetrics +import org.springframework.beans.factory.config.ConfigurableBeanFactory +import org.springframework.context.annotation.Scope +import org.springframework.stereotype.Component +import java.time.LocalDateTime +import java.time.ZoneOffset + +/** + * Converter [Converter] class for [User] entity and [FcmUserDto] DTO. + * + * @author yatharthranjan + */ +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON) +class UserConverter : Converter { + override fun dtoToEntity(fcmUserDto: FcmUserDto): User { + return User() + .setFcmToken(fcmUserDto.fcmToken) + .setSubjectId(fcmUserDto.subjectId) + .setUsermetrics(getValidUserMetrics(fcmUserDto)) + .setEnrolmentDate(fcmUserDto.enrolmentDate) + .setTimezone(fcmUserDto.timezone) + .setLanguage(fcmUserDto.language) + } + + override fun entityToDto(user: User): FcmUserDto { + return FcmUserDto(user) + } + + companion object { + @JvmStatic + fun getValidUserMetrics(fcmUserDto: FcmUserDto): UserMetrics { + val userMetrics: UserMetrics = + if (fcmUserDto.lastOpened == null && fcmUserDto.lastDelivered == null) { + UserMetrics(LocalDateTime.now().toInstant(ZoneOffset.UTC), null) + } else if (fcmUserDto.lastDelivered == null) { + UserMetrics(fcmUserDto.lastOpened, null) + } else if (fcmUserDto.lastOpened == null) { + UserMetrics( + LocalDateTime.now().toInstant(ZoneOffset.UTC), + fcmUserDto.lastDelivered + ) + } else { + UserMetrics(fcmUserDto.lastOpened, fcmUserDto.lastDelivered) + } + return userMetrics + } + } +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/dto/fcm/FcmDataMessageDto.java b/src/main/java/org/radarbase/appserver/dto/fcm/FcmDataMessageDto.java index 22ee1474..6b976662 100644 --- a/src/main/java/org/radarbase/appserver/dto/fcm/FcmDataMessageDto.java +++ b/src/main/java/org/radarbase/appserver/dto/fcm/FcmDataMessageDto.java @@ -39,7 +39,6 @@ /** * @author yatharthranjan */ -@Getter @ToString @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) @@ -86,10 +85,70 @@ public class FcmDataMessageDto implements Serializable { @DateTimeFormat(iso = ISO.DATE_TIME) private Instant updatedAt; + public Long getId() { + return id; + } + + public Instant getScheduledTime() { + return scheduledTime; + } + + public boolean isDelivered() { + return delivered; + } + + public int getTtlSeconds() { + return ttlSeconds; + } + + public String getSourceId() { + return sourceId; + } + + public String getFcmMessageId() { + return fcmMessageId; + } + + public String getFcmTopic() { + return fcmTopic; + } + + public String getFcmCondition() { + return fcmCondition; + } + + public String getAppPackage() { + return appPackage; + } + + public String getSourceType() { + return sourceType; + } + + public Map getDataMap() { + return dataMap; + } + + public String getPriority() { + return priority; + } + + public boolean isMutableContent() { + return mutableContent; + } + + public Instant getCreatedAt() { + return createdAt; + } + + public Instant getUpdatedAt() { + return updatedAt; + } + public FcmDataMessageDto(DataMessage dataMessageEntity) { this.id = dataMessageEntity.getId(); this.scheduledTime = dataMessageEntity.getScheduledTime(); - this.delivered = dataMessageEntity.isDelivered(); + this.delivered = dataMessageEntity.getDelivered(); this.fcmMessageId = dataMessageEntity.getFcmMessageId(); this.sourceId = dataMessageEntity.getSourceId(); this.appPackage = dataMessageEntity.getAppPackage(); @@ -106,7 +165,7 @@ public FcmDataMessageDto(DataMessage dataMessageEntity) { this.fcmTopic = dataMessageEntity.getFcmTopic(); this.fcmCondition = dataMessageEntity.getFcmCondition(); this.priority = dataMessageEntity.getPriority(); - this.mutableContent = dataMessageEntity.isMutableContent(); + this.mutableContent = dataMessageEntity.getMutableContent(); } public FcmDataMessageDto setCreatedAt(Instant createdAt) { diff --git a/src/main/java/org/radarbase/appserver/dto/fcm/FcmNotificationDto.java b/src/main/java/org/radarbase/appserver/dto/fcm/FcmNotificationDto.java index f194e7e7..97444549 100644 --- a/src/main/java/org/radarbase/appserver/dto/fcm/FcmNotificationDto.java +++ b/src/main/java/org/radarbase/appserver/dto/fcm/FcmNotificationDto.java @@ -37,7 +37,6 @@ import org.springframework.format.annotation.DateTimeFormat.ISO; /** @author yatharthranjan */ -@Getter @ToString @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) @@ -117,12 +116,132 @@ public class FcmNotificationDto implements Serializable { @DateTimeFormat(iso = ISO.DATE_TIME) private Instant updatedAt; + public Long getId() { + return id; + } + + public Instant getScheduledTime() { + return scheduledTime; + } + + public boolean isDelivered() { + return delivered; + } + + public String getTitle() { + return title; + } + + public String getBody() { + return body; + } + + public int getTtlSeconds() { + return ttlSeconds; + } + + public String getSourceId() { + return sourceId; + } + + public String getFcmMessageId() { + return fcmMessageId; + } + + public String getFcmTopic() { + return fcmTopic; + } + + public String getFcmCondition() { + return fcmCondition; + } + + public String getType() { + return type; + } + + public String getAppPackage() { + return appPackage; + } + + public String getSourceType() { + return sourceType; + } + + public Map getAdditionalData() { + return additionalData; + } + + public String getPriority() { + return priority; + } + + public String getSound() { + return sound; + } + + public String getBadge() { + return badge; + } + + public String getSubtitle() { + return subtitle; + } + + public String getIcon() { + return icon; + } + + public String getColor() { + return color; + } + + public String getBodyLocKey() { + return bodyLocKey; + } + + public String getBodyLocArgs() { + return bodyLocArgs; + } + + public String getTitleLocKey() { + return titleLocKey; + } + + public String getTitleLocArgs() { + return titleLocArgs; + } + + public String getAndroidChannelId() { + return androidChannelId; + } + + public String getTag() { + return tag; + } + + public String getClickAction() { + return clickAction; + } + + public boolean isMutableContent() { + return mutableContent; + } + + public Instant getCreatedAt() { + return createdAt; + } + + public Instant getUpdatedAt() { + return updatedAt; + } + public FcmNotificationDto(Notification notificationEntity) { this.id = notificationEntity.getId(); this.scheduledTime = notificationEntity.getScheduledTime(); this.title = notificationEntity.getTitle(); this.body = notificationEntity.getBody(); - this.delivered = notificationEntity.isDelivered(); + this.delivered = notificationEntity.getDelivered(); this.fcmMessageId = notificationEntity.getFcmMessageId(); this.sourceId = notificationEntity.getSourceId(); this.type = notificationEntity.getType(); @@ -152,7 +271,7 @@ public FcmNotificationDto(Notification notificationEntity) { this.androidChannelId = notificationEntity.getAndroidChannelId(); this.tag = notificationEntity.getTag(); this.clickAction = notificationEntity.getClickAction(); - this.mutableContent = notificationEntity.isMutableContent(); + this.mutableContent = notificationEntity.getMutableContent(); } public FcmNotificationDto setCreatedAt(Instant createdAt) { diff --git a/src/main/java/org/radarbase/appserver/dto/fcm/FcmUserDto.java b/src/main/java/org/radarbase/appserver/dto/fcm/FcmUserDto.java index 751463d3..e59a7dd7 100644 --- a/src/main/java/org/radarbase/appserver/dto/fcm/FcmUserDto.java +++ b/src/main/java/org/radarbase/appserver/dto/fcm/FcmUserDto.java @@ -34,7 +34,6 @@ import org.springframework.format.annotation.DateTimeFormat; /** @author yatharthranjan */ -@Getter @EqualsAndHashCode @ToString @NoArgsConstructor @@ -151,4 +150,48 @@ public FcmUserDto setLanguage(String language) { this.language = language; return this; } + + public Long getId() { + return id; + } + + public String getProjectId() { + return projectId; + } + + public String getSubjectId() { + return subjectId; + } + + public Instant getLastOpened() { + return lastOpened; + } + + public Instant getLastDelivered() { + return lastDelivered; + } + + public Instant getCreatedAt() { + return createdAt; + } + + public Instant getUpdatedAt() { + return updatedAt; + } + + public Instant getEnrolmentDate() { + return enrolmentDate; + } + + public String getTimezone() { + return timezone; + } + + public String getFcmToken() { + return fcmToken; + } + + public String getLanguage() { + return language; + } } diff --git a/src/main/java/org/radarbase/appserver/entity/AuditModel.java b/src/main/java/org/radarbase/appserver/entity/AuditModel.kt similarity index 54% rename from src/main/java/org/radarbase/appserver/entity/AuditModel.java rename to src/main/java/org/radarbase/appserver/entity/AuditModel.kt index 88030f03..faa8efaf 100644 --- a/src/main/java/org/radarbase/appserver/entity/AuditModel.java +++ b/src/main/java/org/radarbase/appserver/entity/AuditModel.kt @@ -18,42 +18,35 @@ * * * */ +package org.radarbase.appserver.entity -package org.radarbase.appserver.entity; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import java.util.Date; -import javax.persistence.Column; -import javax.persistence.EntityListeners; -import javax.persistence.MappedSuperclass; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import lombok.Data; -import org.springframework.data.annotation.CreatedDate; -import org.springframework.data.annotation.LastModifiedDate; -import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import org.springframework.data.annotation.CreatedDate +import org.springframework.data.annotation.LastModifiedDate +import org.springframework.data.jpa.domain.support.AuditingEntityListener +import java.util.* +import javax.persistence.* /** * Enables Auditing on various JPA entities. This automatically adds and updates the `create at` and * `updated at` attributes for any entity that extends this class. * * @see AuditingEntityListener + * * @author yatharthranjan */ @MappedSuperclass -@EntityListeners(AuditingEntityListener.class) -@JsonIgnoreProperties( - value = {"createdAt", "updatedAt"}, - allowGetters = true) -@Data +@EntityListeners(AuditingEntityListener::class) +@JsonIgnoreProperties(value = ["createdAt", "updatedAt"], allowGetters = true) abstract class AuditModel { - @Temporal(TemporalType.TIMESTAMP) - @Column(name = "created_at", nullable = false, updatable = false) - @CreatedDate - private Date createdAt; + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "created_at", nullable = false, updatable = false) + @CreatedDate + var createdAt: Date? = null + + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "updated_at", nullable = false) + @LastModifiedDate + var updatedAt: Date? = null - @Temporal(TemporalType.TIMESTAMP) - @Column(name = "updated_at", nullable = false) - @LastModifiedDate - private Date updatedAt; -} +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/entity/DataMessage.java b/src/main/java/org/radarbase/appserver/entity/DataMessage.java deleted file mode 100644 index ba567cd4..00000000 --- a/src/main/java/org/radarbase/appserver/entity/DataMessage.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * - * * - * * * Copyright 2018 King's College London - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * * - * * - * - */ - -package org.radarbase.appserver.entity; - -import java.time.Instant; -import java.util.Map; -import java.util.Objects; -import javax.persistence.CollectionTable; -import javax.persistence.Column; -import javax.persistence.ElementCollection; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.Inheritance; -import javax.persistence.InheritanceType; -import javax.persistence.MapKeyColumn; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; -import org.radarbase.appserver.dto.fcm.FcmDataMessageDto; -import org.springframework.lang.Nullable; - -/** - * {@link Entity} for persisting data messages. The corresponding DTO is {@link FcmDataMessageDto}. - * This also includes information for scheduling the data message through the Firebase Cloud - * Messaging(FCM) system. - * - * @author yatharthranjan - * @see Scheduled - * @see org.radarbase.appserver.service.scheduler.DataMessageSchedulerService - */ -@Entity -@Table( - name = "data_messages", - uniqueConstraints = { - @UniqueConstraint( - columnNames = { - "user_id", - "source_id", - "scheduled_time", - "ttl_seconds", - "delivered", - "dry_run" - }) - }) -@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) -@Getter -@Setter -@ToString -@NoArgsConstructor -@SuppressWarnings("PMD.AvoidFieldNameMatchingMethodName") -public class DataMessage extends Message { - private static final long serialVersionUID = 4L; - - @Nullable - @ElementCollection(fetch = FetchType.EAGER) - @CollectionTable(name = "data_message_map") - @MapKeyColumn(name = "key", nullable = true) - @Column(name = "value") - private Map dataMap; - - @NoArgsConstructor - public static class DataMessageBuilder { - transient Long id; - transient User user; - transient String sourceId; - transient Instant scheduledTime; - transient int ttlSeconds; - transient String fcmMessageId; - transient String fcmTopic; - transient String fcmCondition; - transient boolean delivered; - transient boolean validated; - transient String appPackage; - transient String sourceType; - transient boolean dryRun; - transient String priority; - transient boolean mutableContent; - transient Map dataMap; - - public DataMessageBuilder(DataMessage dataMessage) { - this.id = dataMessage.getId(); - this.user = dataMessage.getUser(); - this.sourceId = dataMessage.getSourceId(); - this.scheduledTime = dataMessage.getScheduledTime(); - this.ttlSeconds = dataMessage.getTtlSeconds(); - this.fcmMessageId = dataMessage.getFcmMessageId(); - this.fcmTopic = dataMessage.getFcmTopic(); - this.fcmCondition = dataMessage.getFcmCondition(); - this.delivered = dataMessage.isDelivered(); - this.validated = dataMessage.isValidated(); - this.appPackage = dataMessage.getAppPackage(); - this.sourceType = dataMessage.getSourceType(); - this.dryRun = dataMessage.isDryRun(); - this.mutableContent = dataMessage.isMutableContent(); - this.dataMap = dataMessage.getDataMap(); - } - - public DataMessageBuilder id(Long id) { - this.id = id; - return this; - } - - public DataMessageBuilder user(User user) { - this.user = user; - return this; - } - - public DataMessageBuilder sourceId(String sourceId) { - this.sourceId = sourceId; - return this; - } - - public DataMessageBuilder scheduledTime(Instant scheduledTime) { - this.scheduledTime = scheduledTime; - return this; - } - - public DataMessageBuilder ttlSeconds(int ttlSeconds) { - this.ttlSeconds = ttlSeconds; - return this; - } - - public DataMessageBuilder fcmMessageId(String fcmMessageId) { - this.fcmMessageId = fcmMessageId; - return this; - } - - public DataMessageBuilder fcmTopic(String fcmTopic) { - this.fcmTopic = fcmTopic; - return this; - } - - public DataMessageBuilder fcmCondition(String fcmCondition) { - this.fcmCondition = fcmCondition; - return this; - } - - public DataMessageBuilder delivered(boolean delivered) { - this.delivered = delivered; - return this; - } - - public DataMessageBuilder appPackage(String appPackage) { - this.appPackage = appPackage; - return this; - } - - public DataMessageBuilder sourceType(String sourceType) { - this.sourceType = sourceType; - return this; - } - - public DataMessageBuilder dryRun(boolean dryRun) { - this.dryRun = dryRun; - return this; - } - - public DataMessageBuilder priority(String priority) { - this.priority = priority; - return this; - } - - public DataMessageBuilder mutableContent(boolean mutableContent) { - this.mutableContent = mutableContent; - return this; - } - - - public DataMessageBuilder dataMap(Map dataMap) { - this.dataMap = dataMap; - return this; - } - - public DataMessage build() { - DataMessage dataMessage = new DataMessage(); - dataMessage.setId(this.id); - dataMessage.setUser(this.user); - dataMessage.setSourceId(this.sourceId); - dataMessage.setScheduledTime(this.scheduledTime); - dataMessage.setTtlSeconds(this.ttlSeconds); - dataMessage.setFcmMessageId(this.fcmMessageId); - dataMessage.setFcmTopic(this.fcmTopic); - dataMessage.setFcmCondition(this.fcmCondition); - dataMessage.setDelivered(this.delivered); - dataMessage.setValidated(this.validated); - dataMessage.setAppPackage(this.appPackage); - dataMessage.setSourceType(this.sourceType); - dataMessage.setDryRun(this.dryRun); - dataMessage.setPriority(this.priority); - dataMessage.setMutableContent(this.mutableContent); - dataMessage.setDataMap(this.dataMap); - - return dataMessage; - } - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof DataMessage)) { - return false; - } - return super.equals(o); - } - - @Override - public int hashCode() { - return Objects.hash( - super.hashCode(), - getDataMap()); - } - -} diff --git a/src/main/java/org/radarbase/appserver/entity/DataMessage.kt b/src/main/java/org/radarbase/appserver/entity/DataMessage.kt new file mode 100644 index 00000000..0fc58c83 --- /dev/null +++ b/src/main/java/org/radarbase/appserver/entity/DataMessage.kt @@ -0,0 +1,112 @@ +/* + * + * * + * * * Copyright 2018 King's College London + * * * + * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * you may not use this file except in compliance with the License. + * * * You may obtain a copy of the License at + * * * + * * * http://www.apache.org/licenses/LICENSE-2.0 + * * * + * * * Unless required by applicable law or agreed to in writing, software + * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * See the License for the specific language governing permissions and + * * * limitations under the License. + * * * + * * + * + */ +package org.radarbase.appserver.entity + +import java.time.Instant +import java.util.* +import javax.persistence.* + +/** + * [Entity] for persisting data messages. The corresponding DTO is [FcmDataMessageDto]. + * This also includes information for scheduling the data message through the Firebase Cloud + * Messaging(FCM) system. + * + * @author yatharthranjan + * @see Scheduled + * + * @see org.radarbase.appserver.service.scheduler.DataMessageSchedulerService + */ +@Entity +@Table( + name = "data_messages", + uniqueConstraints = [UniqueConstraint(columnNames = ["user_id", "source_id", "scheduled_time", "ttl_seconds", "delivered", "dry_run"])] +) +@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) +class DataMessage : Message() { + + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable(name = "data_message_map") + @MapKeyColumn(name = "key", nullable = true) + @Column(name = "value") + var dataMap: Map? = null + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + return if (other !is DataMessage) { + false + } else super.equals(other) + } + + override fun hashCode(): Int { + return Objects.hash( + super.hashCode(), + dataMap + ) + } + + @JvmOverloads + fun copy( + id: Long? = this.id, + user: User? = this.user, + scheduledTime: Instant? = this.scheduledTime, + sourceId: String? = this.sourceId, + sourceType: String? = this.sourceType, + ttlSeconds: Int = this.ttlSeconds, + fcmMessageId: String = this.fcmMessageId, + fcmTopic: String? = this.fcmTopic, + fcmCondition: String? = this.fcmCondition, + appPackage: String? = this.appPackage, + dryRun: Boolean = this.dryRun, + delivered: Boolean = this.delivered, + validated: Boolean = this.validated, + priority: String? = this.priority, + mutableContent: Boolean = this.mutableContent, + ): DataMessage { + return DataMessage().apply { + this.id = id + this.user = user + this.scheduledTime = scheduledTime + this.sourceId = sourceId + this.sourceType = sourceType + this.ttlSeconds = ttlSeconds + this.fcmMessageId = fcmMessageId + this.fcmTopic = fcmTopic + this.fcmCondition = fcmCondition + this.appPackage = appPackage + this.dryRun = dryRun + this.delivered = delivered + this.validated = validated + this.priority = priority + this.mutableContent = mutableContent + } + } + + override fun toString(): String { + return "DataMessage(id=$id, dataMap=$dataMap)" + } + + + companion object { + private const val serialVersionUID = 4L + } +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/entity/DataMessageStateEvent.java b/src/main/java/org/radarbase/appserver/entity/DataMessageStateEvent.java deleted file mode 100644 index 2bac04a0..00000000 --- a/src/main/java/org/radarbase/appserver/entity/DataMessageStateEvent.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * - * * - * * * Copyright 2018 King's College London - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * * - * * - * - */ - -package org.radarbase.appserver.entity; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import java.time.Instant; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.Table; -import javax.validation.constraints.NotNull; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.hibernate.annotations.OnDelete; -import org.hibernate.annotations.OnDeleteAction; -import org.radarbase.appserver.event.state.MessageState; - -@Entity -@Getter -@Table(name = "data_message_state_events") -@NoArgsConstructor -public class DataMessageStateEvent extends MessageStateEvent { - private static final long serialVersionUID = 876253616328520L; - - @Id - @GeneratedValue(strategy = GenerationType.AUTO) - private Long id; - - @NotNull - @ManyToOne(fetch = FetchType.EAGER, optional = false) - @JoinColumn(name = "data_message_id", nullable = false) - @OnDelete(action = OnDeleteAction.CASCADE) - @JsonIgnore - private DataMessage dataMessage; - - public DataMessageStateEvent( - @NotNull DataMessage dataMessage, - @NotNull MessageState state, - @NotNull Instant time, - String associatedInfo) { - super(state, time, associatedInfo); - this.dataMessage = dataMessage; - } -} diff --git a/src/main/java/org/radarbase/appserver/entity/DataMessageStateEvent.kt b/src/main/java/org/radarbase/appserver/entity/DataMessageStateEvent.kt new file mode 100644 index 00000000..ccd7b912 --- /dev/null +++ b/src/main/java/org/radarbase/appserver/entity/DataMessageStateEvent.kt @@ -0,0 +1,41 @@ +/* + * + * * + * * * Copyright 2018 King's College London + * * * + * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * you may not use this file except in compliance with the License. + * * * You may obtain a copy of the License at + * * * + * * * http://www.apache.org/licenses/LICENSE-2.0 + * * * + * * * Unless required by applicable law or agreed to in writing, software + * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * See the License for the specific language governing permissions and + * * * limitations under the License. + * * * + * * + * + */ +package org.radarbase.appserver.entity + +import com.fasterxml.jackson.annotation.JsonIgnore +import org.hibernate.annotations.OnDelete +import org.hibernate.annotations.OnDeleteAction +import javax.persistence.* + +@Entity +@Table(name = "data_message_state_events") +class DataMessageStateEvent : MessageStateEvent() { + + @field:JsonIgnore + @field:OnDelete(action = OnDeleteAction.CASCADE) + @field:JoinColumn(name = "data_message_id", nullable = false) + @field:ManyToOne(fetch = FetchType.EAGER, optional = false) + lateinit var dataMessage: DataMessage + + companion object { + private const val serialVersionUID = 876253616328520L + } +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/entity/Message.java b/src/main/java/org/radarbase/appserver/entity/Message.java deleted file mode 100644 index 1c3840c0..00000000 --- a/src/main/java/org/radarbase/appserver/entity/Message.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * - * * - * * * Copyright 2018 King's College London - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * * - * * - * - */ - -package org.radarbase.appserver.entity; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import java.io.Serializable; -import java.time.Instant; -import java.util.Objects; -import javax.persistence.Column; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.MappedSuperclass; -import javax.validation.constraints.NotNull; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import org.hibernate.annotations.OnDelete; -import org.hibernate.annotations.OnDeleteAction; -import org.springframework.lang.Nullable; - -@MappedSuperclass -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -@SuppressWarnings("PMD.AvoidFieldNameMatchingMethodName") -public class Message extends AuditModel implements Serializable, Scheduled { - - private static final long serialVersionUID = -367424816328519L; - - @Id - @GeneratedValue(strategy = GenerationType.AUTO) - private Long id; - - @NotNull - @ManyToOne(fetch = FetchType.LAZY, optional = false) - @JoinColumn(name = "user_id", nullable = false) - @OnDelete(action = OnDeleteAction.CASCADE) - @JsonIgnore - private User user; - - @Nullable - @Column(name = "source_id") - private String sourceId; - - @NotNull - @Column(name = "scheduled_time", nullable = false) - private Instant scheduledTime; - - @Column(name = "ttl_seconds") - private int ttlSeconds; - - @Column(name = "fcm_message_id", unique = true) - private String fcmMessageId; - - // for use with the FCM admin SDK - @Column(name = "fcm_topic") - @Nullable - private String fcmTopic; - - // for use with the FCM admin SDK - @Column(name = "fcm_condition") - @Nullable - private String fcmCondition; - - // TODO: REMOVE DELIVERED AND VALIDATED. These can be handled by state lifecycle. - private boolean delivered; - - private boolean validated; - - @Nullable - @Column(name = "app_package") - private String appPackage; - - // Source Type from the Management Portal - @Nullable - @Column(name = "source_type") - private String sourceType; - - @Column(name = "dry_run") - - // for use with the FCM admin SDK - private boolean dryRun; - - private String priority; - - @Column(name = "mutable_content") - private boolean mutableContent; - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof Message)) { - return false; - } - Message that = (Message) o; - return getTtlSeconds() == that.getTtlSeconds() - && isDelivered() == that.isDelivered() - && isDryRun() == that.isDryRun() - && Objects.equals(getUser(), that.getUser()) - && Objects.equals(getSourceId(), that.getSourceId()) - && Objects.equals(getScheduledTime(), that.getScheduledTime()) - && Objects.equals(getAppPackage(), that.getAppPackage()); - } - - @Override - public int hashCode() { - return Objects.hash( - getUser(), - getSourceId(), - getScheduledTime(), - getTtlSeconds(), - isDelivered(), - isDryRun(), - getAppPackage(), - getSourceType()); - } -} diff --git a/src/main/java/org/radarbase/appserver/entity/Message.kt b/src/main/java/org/radarbase/appserver/entity/Message.kt new file mode 100644 index 00000000..f8408aad --- /dev/null +++ b/src/main/java/org/radarbase/appserver/entity/Message.kt @@ -0,0 +1,119 @@ +/* + * + * * + * * * Copyright 2018 King's College London + * * * + * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * you may not use this file except in compliance with the License. + * * * You may obtain a copy of the License at + * * * + * * * http://www.apache.org/licenses/LICENSE-2.0 + * * * + * * * Unless required by applicable law or agreed to in writing, software + * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * See the License for the specific language governing permissions and + * * * limitations under the License. + * * * + * * + * + */ +package org.radarbase.appserver.entity + +import com.fasterxml.jackson.annotation.JsonIgnore +import org.hibernate.annotations.OnDelete +import org.hibernate.annotations.OnDeleteAction +import org.springframework.lang.Nullable +import java.io.Serializable +import java.time.Instant +import java.util.* +import javax.persistence.* + +@MappedSuperclass +abstract class Message : AuditModel(), Serializable, Scheduled { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + var id: Long? = null + + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "user_id", nullable = false) + @OnDelete(action = OnDeleteAction.CASCADE) + @JsonIgnore + var user: User? = null + + @Nullable + @Column(name = "source_id") + var sourceId: String? = null + + @Column(name = "scheduled_time", nullable = false) + override var scheduledTime: Instant? = null + + @Column(name = "ttl_seconds") + var ttlSeconds: Int = 0 + + @Column(name = "fcm_message_id", unique = true) + lateinit var fcmMessageId: String + + // for use with the FCM admin SDK + @Column(name = "fcm_topic") + @Nullable + var fcmTopic: String? = null + + // for use with the FCM admin SDK + @Column(name = "fcm_condition") + @Nullable + var fcmCondition: String? = null + + // TODO: REMOVE DELIVERED AND VALIDATED. These can be handled by state lifecycle. + var delivered: Boolean = false + var validated: Boolean = false + + @Nullable + @Column(name = "app_package") + var appPackage: String? = null + + // Source Type from the Management Portal + @Nullable + @Column(name = "source_type") + var sourceType: String? = null + + @Column(name = "dry_run") // for use with the FCM admin SDK + var dryRun: Boolean = false + + var priority: String? = null + + @Column(name = "mutable_content") + var mutableContent: Boolean = false + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (other !is Message) { + return false + } + return (ttlSeconds == other.ttlSeconds && delivered == other.delivered + && dryRun == other.dryRun && user == other.user + && sourceId == other.sourceId + && scheduledTime == other.scheduledTime + && appPackage == other.appPackage) + } + + override fun hashCode(): Int { + return Objects.hash( + user, + sourceId, + scheduledTime, + ttlSeconds, + delivered, + dryRun, + appPackage, + sourceType + ) + } + + companion object { + private const val serialVersionUID = -367424816328519L + } +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/entity/MessageStateEvent.java b/src/main/java/org/radarbase/appserver/entity/MessageStateEvent.java deleted file mode 100644 index 83814b58..00000000 --- a/src/main/java/org/radarbase/appserver/entity/MessageStateEvent.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * - * * - * * * Copyright 2018 King's College London - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * * - * * - * - */ - -package org.radarbase.appserver.entity; - -import java.io.Serializable; -import java.time.Instant; -import javax.persistence.Column; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.MappedSuperclass; -import javax.validation.constraints.NotNull; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.radarbase.appserver.event.state.MessageState; - -@MappedSuperclass -@Getter -@NoArgsConstructor -public abstract class MessageStateEvent implements Serializable { - private static final long serialVersionUID = 876253616328519L; - - @Id - @GeneratedValue(strategy = GenerationType.AUTO) - private Long id; - - @NotNull - @Enumerated(EnumType.STRING) - @Column(nullable = false) - private MessageState state; - - @NotNull - @Column(nullable = false) - private Instant time; - - @Column(name = "associated_info", length = 1250) - private String associatedInfo; - - public MessageStateEvent( - @NotNull MessageState state, - @NotNull Instant time, - String associatedInfo) { - this.state = state; - this.time = time; - this.associatedInfo = associatedInfo; - } -} diff --git a/src/main/java/org/radarbase/appserver/entity/MessageStateEvent.kt b/src/main/java/org/radarbase/appserver/entity/MessageStateEvent.kt new file mode 100644 index 00000000..903c54eb --- /dev/null +++ b/src/main/java/org/radarbase/appserver/entity/MessageStateEvent.kt @@ -0,0 +1,48 @@ +/* + * + * * + * * * Copyright 2018 King's College London + * * * + * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * you may not use this file except in compliance with the License. + * * * You may obtain a copy of the License at + * * * + * * * http://www.apache.org/licenses/LICENSE-2.0 + * * * + * * * Unless required by applicable law or agreed to in writing, software + * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * See the License for the specific language governing permissions and + * * * limitations under the License. + * * * + * * + * + */ +package org.radarbase.appserver.entity + +import org.radarbase.appserver.event.state.MessageState +import java.io.Serializable +import java.time.Instant +import javax.persistence.* +import javax.validation.constraints.NotNull + +@MappedSuperclass +abstract class MessageStateEvent : Serializable { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + var id: Long? = null + + @field:Column(nullable = false) + @field:Enumerated(EnumType.STRING) + lateinit var state: MessageState + + @field:Column(nullable = false) + lateinit var time: @NotNull Instant + + @field:Column(name = "associated_info", length = 1250) + var associatedInfo: String? = null + + companion object { + private const val serialVersionUID = 876253616328519L + } +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/entity/Notification.java b/src/main/java/org/radarbase/appserver/entity/Notification.java deleted file mode 100644 index 925a9e8e..00000000 --- a/src/main/java/org/radarbase/appserver/entity/Notification.java +++ /dev/null @@ -1,411 +0,0 @@ -/* - * - * * - * * * Copyright 2018 King's College London - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * * - * * - * - */ - -package org.radarbase.appserver.entity; - -import java.time.Instant; -import java.util.Map; -import java.util.Objects; -import javax.persistence.Column; -import javax.persistence.ElementCollection; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.MapKeyColumn; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; -import javax.validation.constraints.NotNull; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; -import org.radarbase.appserver.dto.fcm.FcmNotificationDto; -import org.springframework.lang.Nullable; - -/** - * {@link Entity} for persisting notifications. The corresponding DTO is {@link FcmNotificationDto}. - * This also includes information for scheduling the notification through the Firebase Cloud - * Messaging(FCM) system. - * - * @author yatharthranjan - * @see Scheduled - * @see org.radarbase.appserver.service.scheduler.NotificationSchedulerService - */ -@Table( - name = "notifications", - uniqueConstraints = { - @UniqueConstraint( - columnNames = { - "user_id", - "source_id", - "scheduled_time", - "title", - "body", - "type", - "ttl_seconds", - "delivered", - "dry_run" - }) - }) -@Entity -@Getter -@ToString -@NoArgsConstructor -@Setter -@SuppressWarnings("PMD.AvoidFieldNameMatchingMethodName") -public class Notification extends Message { - - private static final long serialVersionUID = 6L; - - @NotNull - @Column(nullable = false) - private String title; - - private String body; - - // Type of notification. In terms of aRMT - PHQ8, RSES, ESM, etc. - @Nullable - private String type; - - private String sound; - - // For IOS - private String badge; - - // For IOS - private String subtitle; - - // For android - private String icon; - - // For android. Color of the icon - private String color; - - @Column(name = "body_loc_key") - private String bodyLocKey; - - @Column(name = "body_loc_args") - private String bodyLocArgs; - - @Column(name = "title_loc_key") - private String titleLocKey; - - @Column(name = "title_loc_args") - private String titleLocArgs; - - // For android - @Column(name = "android_channel_id") - private String androidChannelId; - - // For android - private String tag; - - @Column(name = "click_action") - private String clickAction; - - @Nullable - @ElementCollection(fetch = FetchType.EAGER) - @MapKeyColumn(name = "additional_key", nullable = true) - @Column(name = "additional_value") - private Map additionalData; - - @NoArgsConstructor - public static class NotificationBuilder { - transient Long id; - transient User user; - transient String sourceId; - transient Instant scheduledTime; - transient int ttlSeconds; - transient String fcmMessageId; - transient String fcmTopic; - transient String fcmCondition; - transient boolean delivered; - transient boolean validated; - transient String appPackage; - transient String sourceType; - transient boolean dryRun; - transient String priority; - transient boolean mutableContent; - transient String title; - transient String body; - transient String type; - transient String sound; - transient String badge; - transient String subtitle; - transient String icon; - transient String color; - transient String bodyLocKey; - transient String bodyLocArgs; - transient String titleLocKey; - transient String titleLocArgs; - transient String androidChannelId; - transient String tag; - transient String clickAction; - transient Map additionalData; - - - public NotificationBuilder(Notification notification) { - this.id = notification.getId(); - this.user = notification.getUser(); - this.sourceId = notification.getSourceId(); - this.scheduledTime = notification.getScheduledTime(); - this.ttlSeconds = notification.getTtlSeconds(); - this.fcmMessageId = notification.getFcmMessageId(); - this.fcmTopic = notification.getFcmTopic(); - this.fcmCondition = notification.getFcmCondition(); - this.delivered = notification.isDelivered(); - this.validated = notification.isValidated(); - this.appPackage = notification.getAppPackage(); - this.sourceType = notification.getSourceType(); - this.dryRun = notification.isDryRun(); - this.mutableContent = notification.isMutableContent(); - this.additionalData = notification.getAdditionalData(); - this.title = notification.getTitle(); - this.body = notification.getBody(); - this.type = notification.getType(); - this.sound = notification.getSound(); - this.badge = notification.getBadge(); - this.subtitle = notification.getSubtitle(); - this.icon = notification.getIcon(); - this.color = notification.getColor(); - this.bodyLocKey = notification.getBodyLocKey(); - this.bodyLocArgs = notification.getBodyLocArgs(); - this.titleLocKey = notification.getTitleLocKey(); - this.titleLocArgs = notification.getTitleLocArgs(); - this.androidChannelId = notification.getAndroidChannelId(); - this.tag = notification.getTag(); - this.clickAction = notification.getClickAction(); - this.additionalData = notification.getAdditionalData(); - } - - public NotificationBuilder id(Long id) { - this.id = id; - return this; - } - - public NotificationBuilder user(User user) { - this.user = user; - return this; - } - - public NotificationBuilder sourceId(String sourceId) { - this.sourceId = sourceId; - return this; - } - - public NotificationBuilder scheduledTime(Instant scheduledTime) { - this.scheduledTime = scheduledTime; - return this; - } - - public NotificationBuilder ttlSeconds(int ttlSeconds) { - this.ttlSeconds = ttlSeconds; - return this; - } - - public NotificationBuilder fcmMessageId(String fcmMessageId) { - this.fcmMessageId = fcmMessageId; - return this; - } - - public NotificationBuilder fcmTopic(String fcmTopic) { - this.fcmTopic = fcmTopic; - return this; - } - - public NotificationBuilder fcmCondition(String fcmCondition) { - this.fcmCondition = fcmCondition; - return this; - } - - public NotificationBuilder delivered(boolean delivered) { - this.delivered = delivered; - return this; - } - - public NotificationBuilder appPackage(String appPackage) { - this.appPackage = appPackage; - return this; - } - - public NotificationBuilder sourceType(String sourceType) { - this.sourceType = sourceType; - return this; - } - - public NotificationBuilder dryRun(boolean dryRun) { - this.dryRun = dryRun; - return this; - } - - public NotificationBuilder priority(String priority) { - this.priority = priority; - return this; - } - - public NotificationBuilder mutableContent(boolean mutableContent) { - this.mutableContent = mutableContent; - return this; - } - - - public NotificationBuilder title(String title) { - this.title = title; - return this; - } - - public NotificationBuilder body(String body) { - this.body = body; - return this; - } - - public NotificationBuilder type(String type) { - this.type = type; - return this; - } - - public NotificationBuilder sound(String sound) { - this.sound = sound; - return this; - } - - public NotificationBuilder badge(String badge) { - this.badge = badge; - return this; - } - - public NotificationBuilder subtitle(String subtitle) { - this.subtitle = subtitle; - return this; - } - - public NotificationBuilder icon(String icon) { - this.icon = icon; - return this; - } - - public NotificationBuilder color(String color) { - this.color = color; - return this; - } - - public NotificationBuilder bodyLocKey(String bodyLocKey) { - this.bodyLocKey = bodyLocKey; - return this; - } - - public NotificationBuilder bodyLocArgs(String bodyLocArgs) { - this.bodyLocArgs = bodyLocArgs; - return this; - } - - public NotificationBuilder titleLocKey(String titleLocKey) { - this.titleLocKey = titleLocKey; - return this; - } - - public NotificationBuilder titleLocArgs(String titleLocArgs) { - this.titleLocArgs = titleLocArgs; - return this; - } - - public NotificationBuilder androidChannelId(String androidChannelId) { - this.androidChannelId = androidChannelId; - return this; - } - - public NotificationBuilder tag(String tag) { - this.tag = tag; - return this; - } - - public NotificationBuilder clickAction(String clickAction) { - this.clickAction = clickAction; - return this; - } - - public NotificationBuilder additionalData(Map additionalData) { - this.additionalData = additionalData; - return this; - } - - public Notification build() { - Notification notification = new Notification(); - notification.setId(this.id); - notification.setUser(this.user); - notification.setSourceId(this.sourceId); - notification.setScheduledTime(this.scheduledTime); - notification.setTtlSeconds(this.ttlSeconds); - notification.setFcmMessageId(this.fcmMessageId); - notification.setFcmTopic(this.fcmTopic); - notification.setFcmCondition(this.fcmCondition); - notification.setDelivered(this.delivered); - notification.setValidated(this.validated); - notification.setAppPackage(this.appPackage); - notification.setSourceType(this.sourceType); - notification.setDryRun(this.dryRun); - notification.setPriority(this.priority); - notification.setMutableContent(this.mutableContent); - notification.setAdditionalData(this.additionalData); - notification.setTitle(this.title); - notification.setBody(this.body); - notification.setType(this.type); - notification.setSound(this.sound); - notification.setBadge(this.badge); - notification.setSubtitle(this.subtitle); - notification.setIcon(this.icon); - notification.setColor(this.color); - notification.setBodyLocKey(this.bodyLocKey); - notification.setBodyLocArgs(this.bodyLocArgs); - notification.setTitleLocKey(this.titleLocKey); - notification.setTitleLocArgs(this.titleLocArgs); - notification.setAndroidChannelId(this.androidChannelId); - notification.setTag(this.tag); - notification.setClickAction(this.clickAction); - notification.setAdditionalData(this.additionalData); - - return notification; - } - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof Notification)) { - return false; - } - Notification that = (Notification) o; - return super.equals(o) - && Objects.equals(getTitle(), that.getTitle()) - && Objects.equals(getBody(), that.getBody()) - && Objects.equals(getType(), that.getType()); - } - - @Override - public int hashCode() { - return Objects.hash( - super.hashCode(), - getTitle(), - getBody(), - getType()); - } -} diff --git a/src/main/java/org/radarbase/appserver/entity/Notification.kt b/src/main/java/org/radarbase/appserver/entity/Notification.kt new file mode 100644 index 00000000..30c191d3 --- /dev/null +++ b/src/main/java/org/radarbase/appserver/entity/Notification.kt @@ -0,0 +1,196 @@ +/* + * + * * + * * * Copyright 2018 King's College London + * * * + * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * you may not use this file except in compliance with the License. + * * * You may obtain a copy of the License at + * * * + * * * http://www.apache.org/licenses/LICENSE-2.0 + * * * + * * * Unless required by applicable law or agreed to in writing, software + * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * See the License for the specific language governing permissions and + * * * limitations under the License. + * * * + * * + * + */ +package org.radarbase.appserver.entity + +import org.springframework.lang.Nullable +import java.time.Instant +import java.util.* +import javax.persistence.* + +/** + * [Entity] for persisting notifications. The corresponding DTO is [FcmNotificationDto]. + * This also includes information for scheduling the notification through the Firebase Cloud + * Messaging(FCM) system. + * + * @author yatharthranjan + * @see Scheduled + * + * @see org.radarbase.appserver.service.scheduler.NotificationSchedulerService + */ +@Table( + name = "notifications", + uniqueConstraints = [UniqueConstraint(columnNames = ["user_id", "source_id", "scheduled_time", "title", "body", "type", "ttl_seconds", "delivered", "dry_run"])] +) +@Entity +class Notification : Message() { + + @Column(nullable = false) + lateinit var title: String + var body: String? = null + + // Type of notification. In terms of aRMT - PHQ8, RSES, ESM, etc. + @Nullable + var type: String? = null + var sound: String? = null + + // For IOS + var badge: String? = null + + // For IOS + var subtitle: String? = null + + // For android + var icon: String? = null + + // For android. Color of the icon + var color: String? = null + + @Column(name = "body_loc_key") + var bodyLocKey: String? = null + + @Column(name = "body_loc_args") + var bodyLocArgs: String? = null + + @Column(name = "title_loc_key") + var titleLocKey: String? = null + + @Column(name = "title_loc_args") + var titleLocArgs: String? = null + + // For android + @Column(name = "android_channel_id") + var androidChannelId: String? = null + + // For android + var tag: String? = null + + @Column(name = "click_action") + var clickAction: String? = null + + @Nullable + @ElementCollection(fetch = FetchType.EAGER) + @MapKeyColumn(name = "additional_key", nullable = true) + @Column(name = "additional_value") + var additionalData: Map? = null + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (other !is Notification) { + return false + } + return (super.equals(other) + && title == other.title + && body == other.body + && type == other.type) + } + + override fun hashCode(): Int { + return Objects.hash( + super.hashCode(), + title, + body, + type + ) + } + + @JvmOverloads + fun copy( + id: Long? = this.id, + user: User? = this.user, + scheduledTime: Instant? = this.scheduledTime, + sourceId: String? = this.sourceId, + sourceType: String? = this.sourceType, + ttlSeconds: Int = this.ttlSeconds, + fcmMessageId: String = this.fcmMessageId, + fcmTopic: String? = this.fcmTopic, + fcmCondition: String? = this.fcmCondition, + appPackage: String? = this.appPackage, + title: String = this.title, + body: String? = this.body, + type: String? = this.type, + sound: String? = this.sound, + badge: String? = this.badge, + subtitle: String? = this.subtitle, + icon: String? = this.icon, + color: String? = this.color, + bodyLocKey: String? = this.bodyLocKey, + bodyLocArgs: String? = this.bodyLocArgs, + titleLocKey: String? = this.titleLocKey, + titleLocArgs: String? = this.titleLocArgs, + androidChannelId: String? = this.androidChannelId, + tag: String? = this.tag, + clickAction: String? = this.clickAction, + additionalData: Map? = this.additionalData, + dryRun: Boolean = this.dryRun, + delivered: Boolean = this.delivered, + validated: Boolean = this.validated, + priority: String? = this.priority, + mutableContent: Boolean = this.mutableContent, + ): Notification { + return Notification().apply { + this.id = id + this.user = user + this.scheduledTime = scheduledTime + this.sourceId = sourceId + this.sourceType = sourceType + this.ttlSeconds = ttlSeconds + this.fcmMessageId = fcmMessageId + this.fcmTopic = fcmTopic + this.fcmCondition = fcmCondition + this.appPackage = appPackage + this.title = title + this.body = body + this.type = type + this.sound = sound + this.badge = badge + this.subtitle = subtitle + this.icon = icon + this.color = color + this.bodyLocKey = bodyLocKey + this.bodyLocArgs = bodyLocArgs + this.titleLocKey = titleLocKey + this.titleLocArgs = titleLocArgs + this.androidChannelId = androidChannelId + this.tag = tag + this.clickAction = clickAction + this.additionalData = additionalData + this.dryRun = dryRun + this.delivered = delivered + this.validated = validated + this.priority = priority + this.mutableContent = mutableContent + } + } + + override fun toString(): String { + return "Notification(id=$id, title=$title, body=$body, type=$type, sound=$sound, " + + "badge=$badge, subtitle=$subtitle, icon=$icon, color=$color, " + + "bodyLocKey=$bodyLocKey, bodyLocArgs=$bodyLocArgs, titleLocKey=$titleLocKey," + + " titleLocArgs=$titleLocArgs, androidChannelId=$androidChannelId, tag=$tag," + + " clickAction=$clickAction, additionalData=$additionalData)" + } + + companion object { + private const val serialVersionUID = 6L + } +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/entity/NotificationStateEvent.java b/src/main/java/org/radarbase/appserver/entity/NotificationStateEvent.java deleted file mode 100644 index f1ac6e31..00000000 --- a/src/main/java/org/radarbase/appserver/entity/NotificationStateEvent.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * - * * - * * * Copyright 2018 King's College London - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * * - * * - * - */ - -package org.radarbase.appserver.entity; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import java.time.Instant; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.Table; -import javax.validation.constraints.NotNull; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.hibernate.annotations.OnDelete; -import org.hibernate.annotations.OnDeleteAction; -import org.radarbase.appserver.event.state.MessageState; - -@Entity -@Getter -@Table(name = "notification_state_events") -@NoArgsConstructor -public class NotificationStateEvent extends MessageStateEvent { - private static final long serialVersionUID = 876253616328518L; - - @Id - @GeneratedValue(strategy = GenerationType.AUTO) - private Long id; - - @NotNull - @ManyToOne(fetch = FetchType.EAGER, optional = false) - @JoinColumn(name = "notification_id", nullable = false) - @OnDelete(action = OnDeleteAction.CASCADE) - @JsonIgnore - private Notification notification; - - public NotificationStateEvent( - @NotNull Notification notification, - @NotNull MessageState state, - @NotNull Instant time, - String associatedInfo) { - super(state, time, associatedInfo); - this.notification = notification; - } -} diff --git a/src/main/java/org/radarbase/appserver/entity/NotificationStateEvent.kt b/src/main/java/org/radarbase/appserver/entity/NotificationStateEvent.kt new file mode 100644 index 00000000..716471da --- /dev/null +++ b/src/main/java/org/radarbase/appserver/entity/NotificationStateEvent.kt @@ -0,0 +1,41 @@ +/* + * + * * + * * * Copyright 2018 King's College London + * * * + * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * you may not use this file except in compliance with the License. + * * * You may obtain a copy of the License at + * * * + * * * http://www.apache.org/licenses/LICENSE-2.0 + * * * + * * * Unless required by applicable law or agreed to in writing, software + * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * See the License for the specific language governing permissions and + * * * limitations under the License. + * * * + * * + * + */ +package org.radarbase.appserver.entity + +import com.fasterxml.jackson.annotation.JsonIgnore +import org.hibernate.annotations.OnDelete +import org.hibernate.annotations.OnDeleteAction +import javax.persistence.* + +@Entity +@Table(name = "notification_state_events") +class NotificationStateEvent : MessageStateEvent() { + + @field:JsonIgnore + @field:OnDelete(action = OnDeleteAction.CASCADE) + @field:JoinColumn(name = "notification_id", nullable = false) + @field:ManyToOne(fetch = FetchType.EAGER, optional = false) + lateinit var notification: Notification + + companion object { + private const val serialVersionUID = 876253616328518L + } +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/entity/Project.java b/src/main/java/org/radarbase/appserver/entity/Project.java index c94b391b..5ea5d338 100644 --- a/src/main/java/org/radarbase/appserver/entity/Project.java +++ b/src/main/java/org/radarbase/appserver/entity/Project.java @@ -41,7 +41,6 @@ */ @Table(name = "projects") @Entity -@Getter @ToString @NoArgsConstructor public class Project extends AuditModel implements Serializable { @@ -66,6 +65,14 @@ public Project setProjectId(String projectId) { return this; } + public Long getId() { + return id; + } + + public String getProjectId() { + return projectId; + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/src/main/java/org/radarbase/appserver/entity/Scheduled.java b/src/main/java/org/radarbase/appserver/entity/Scheduled.kt similarity index 75% rename from src/main/java/org/radarbase/appserver/entity/Scheduled.java rename to src/main/java/org/radarbase/appserver/entity/Scheduled.kt index d247af59..6dfc2ebd 100644 --- a/src/main/java/org/radarbase/appserver/entity/Scheduled.java +++ b/src/main/java/org/radarbase/appserver/entity/Scheduled.kt @@ -18,22 +18,19 @@ * * * */ +package org.radarbase.appserver.entity -package org.radarbase.appserver.entity; - -import java.time.Instant; -import org.radarbase.appserver.service.scheduler.quartz.SchedulerServiceImpl; +import java.time.Instant /** - * Functional Interface to signify a class that can be scheduled using the {@link - * SchedulerServiceImpl}. + * Functional Interface to signify a class that can be scheduled using the [ ]. * * @see Notification + * * @see SchedulerServiceImpl + * * @author yatharthranjan */ -@FunctionalInterface -public interface Scheduled { - - Instant getScheduledTime(); -} +interface Scheduled { + val scheduledTime: Instant? +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/entity/User.java b/src/main/java/org/radarbase/appserver/entity/User.java index 61567a0c..3ce2f1bc 100644 --- a/src/main/java/org/radarbase/appserver/entity/User.java +++ b/src/main/java/org/radarbase/appserver/entity/User.java @@ -41,7 +41,6 @@ import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; -import lombok.Getter; import lombok.ToString; import org.hibernate.annotations.OnDelete; import org.hibernate.annotations.OnDeleteAction; @@ -59,7 +58,6 @@ @UniqueConstraint(columnNames = {"subject_id", "fcm_token", "project_id"}) }) @Entity -@Getter @ToString public class User extends AuditModel implements Serializable { @@ -100,6 +98,38 @@ public class User extends AuditModel implements Serializable { @Column(name = "language") private String language; + public Long getId() { + return id; + } + + public String getSubjectId() { + return subjectId; + } + + public String getFcmToken() { + return fcmToken; + } + + public Project getProject() { + return project; + } + + public Instant getEnrolmentDate() { + return enrolmentDate; + } + + public UserMetrics getUsermetrics() { + return usermetrics; + } + + public String getTimezone() { + return timezone; + } + + public String getLanguage() { + return language; + } + public User setSubjectId(String subjectId) { this.subjectId = subjectId; return this; @@ -120,7 +150,7 @@ public User setEnrolmentDate(Instant enrolmentDate) { return this; } - public User setUserMetrics(UserMetrics userMetrics) { + public User setUsermetrics(UserMetrics userMetrics) { this.usermetrics = userMetrics; return this; } diff --git a/src/main/java/org/radarbase/appserver/entity/UserMetrics.java b/src/main/java/org/radarbase/appserver/entity/UserMetrics.java index c9ac53d7..5e9bba83 100644 --- a/src/main/java/org/radarbase/appserver/entity/UserMetrics.java +++ b/src/main/java/org/radarbase/appserver/entity/UserMetrics.java @@ -44,7 +44,6 @@ */ @Table(name = "user_metrics") @Entity -@Getter @ToString public class UserMetrics extends AuditModel implements Serializable { @@ -52,7 +51,6 @@ public class UserMetrics extends AuditModel implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) - @Setter private Long id; // The most recent time when the app was opened @@ -88,4 +86,27 @@ public UserMetrics setUser(User user) { this.user = user; return this; } + + public void setId(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + + @Nullable + public Instant getLastOpened() { + return lastOpened; + } + + @Nullable + public Instant getLastDelivered() { + return lastDelivered; + } + + @NonNull + public User getUser() { + return user; + } } diff --git a/src/main/java/org/radarbase/appserver/event/state/MessageStateEventListener.java b/src/main/java/org/radarbase/appserver/event/state/MessageStateEventListener.java index 9fee4f0a..10c2e1fc 100644 --- a/src/main/java/org/radarbase/appserver/event/state/MessageStateEventListener.java +++ b/src/main/java/org/radarbase/appserver/event/state/MessageStateEventListener.java @@ -60,8 +60,11 @@ public void onNotificationStateChange(NotificationStateEventDto event) { String info = convertMapToString(event.getAdditionalInfo()); log.debug("ID: {}, STATE: {}", event.getNotification().getId(), event.getState()); org.radarbase.appserver.entity.NotificationStateEvent eventEntity = - new org.radarbase.appserver.entity.NotificationStateEvent( - event.getNotification(), event.getState(), event.getTime(), info); + new org.radarbase.appserver.entity.NotificationStateEvent(); + eventEntity.setNotification(event.getNotification()); + eventEntity.setState(event.getState()); + eventEntity.setTime(event.getTime()); + eventEntity.setAssociatedInfo(info); notificationStateEventService.addNotificationStateEvent(eventEntity); } @@ -70,14 +73,17 @@ public void onDataMessageStateChange(DataMessageStateEventDto event) { String info = convertMapToString(event.getAdditionalInfo()); log.debug("ID: {}, STATE: {}", event.getDataMessage().getId(), event.getState()); org.radarbase.appserver.entity.DataMessageStateEvent eventEntity = - new org.radarbase.appserver.entity.DataMessageStateEvent( - event.getDataMessage(), event.getState(), event.getTime(), info); + new org.radarbase.appserver.entity.DataMessageStateEvent(); + eventEntity.setDataMessage(event.getDataMessage()); + eventEntity.setState(event.getState()); + eventEntity.setTime(event.getTime()); + eventEntity.setAssociatedInfo(info); dataMessageStateEventService.addDataMessageStateEvent(eventEntity); } public String convertMapToString(Map additionalInfoMap) { String info = null; - if (additionalInfoMap != null) { + if (additionalInfoMap!=null) { try { info = objectMapper.writeValueAsString(additionalInfoMap); } catch (JsonProcessingException exc) { diff --git a/src/main/java/org/radarbase/appserver/repository/DataMessageRepository.java b/src/main/java/org/radarbase/appserver/repository/DataMessageRepository.java deleted file mode 100644 index 376aab38..00000000 --- a/src/main/java/org/radarbase/appserver/repository/DataMessageRepository.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * - * * - * * * Copyright 2018 King's College London - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * * - * * - * - */ - -package org.radarbase.appserver.repository; - -import java.time.Instant; -import java.util.List; -import java.util.Optional; -import org.radarbase.appserver.entity.DataMessage; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.rest.core.annotation.RepositoryRestResource; -import org.springframework.stereotype.Repository; - -/** - * @author yatharthranjan - */ -@Repository -@RepositoryRestResource(exported = false) -public interface DataMessageRepository extends JpaRepository { - - List findByUserId(Long userId); - - void deleteByUserId(Long userId); - - boolean existsByUserIdAndSourceIdAndScheduledTimeAndTtlSeconds( - Long userId, - String sourceId, - Instant scheduledTime, - int ttlSeconds); - - boolean existsByIdAndUserId(Long id, Long userId); - - void deleteByFcmMessageId(String fcmMessageId); - - void deleteByIdAndUserId(Long id, Long userId); - - Optional findByFcmMessageId(String fcmMessageId); - - Optional findByIdAndUserId(long id, long userId); -} diff --git a/src/main/java/org/radarbase/appserver/repository/DataMessageRepository.kt b/src/main/java/org/radarbase/appserver/repository/DataMessageRepository.kt new file mode 100644 index 00000000..991b0d7b --- /dev/null +++ b/src/main/java/org/radarbase/appserver/repository/DataMessageRepository.kt @@ -0,0 +1,50 @@ +/* + * + * * + * * * Copyright 2018 King's College London + * * * + * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * you may not use this file except in compliance with the License. + * * * You may obtain a copy of the License at + * * * + * * * http://www.apache.org/licenses/LICENSE-2.0 + * * * + * * * Unless required by applicable law or agreed to in writing, software + * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * See the License for the specific language governing permissions and + * * * limitations under the License. + * * * + * * + * + */ +package org.radarbase.appserver.repository + +import org.radarbase.appserver.entity.DataMessage +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.rest.core.annotation.RepositoryRestResource +import org.springframework.stereotype.Repository +import java.time.Instant +import java.util.* + +/** + * @author yatharthranjan + */ +@Repository +@RepositoryRestResource(exported = false) +interface DataMessageRepository : JpaRepository { + fun findByUserId(userId: Long?): List? + fun deleteByUserId(userId: Long?) + fun existsByUserIdAndSourceIdAndScheduledTimeAndTtlSeconds( + userId: Long?, + sourceId: String?, + scheduledTime: Instant?, + ttlSeconds: Int + ): Boolean + + fun existsByIdAndUserId(id: Long?, userId: Long?): Boolean + fun deleteByFcmMessageId(fcmMessageId: String?) + fun deleteByIdAndUserId(id: Long?, userId: Long?) + fun findByFcmMessageId(fcmMessageId: String?): Optional? + fun findByIdAndUserId(id: Long, userId: Long): Optional? +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/repository/NotificationStateEventRepository.java b/src/main/java/org/radarbase/appserver/repository/DataMessageStateEventRepository.kt similarity index 62% rename from src/main/java/org/radarbase/appserver/repository/NotificationStateEventRepository.java rename to src/main/java/org/radarbase/appserver/repository/DataMessageStateEventRepository.kt index c7a7392d..8d1344fa 100644 --- a/src/main/java/org/radarbase/appserver/repository/NotificationStateEventRepository.java +++ b/src/main/java/org/radarbase/appserver/repository/DataMessageStateEventRepository.kt @@ -18,21 +18,16 @@ * * * */ +package org.radarbase.appserver.repository -package org.radarbase.appserver.repository; - -import java.util.List; -import org.radarbase.appserver.entity.NotificationStateEvent; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.rest.core.annotation.RepositoryRestResource; -import org.springframework.stereotype.Repository; +import org.radarbase.appserver.entity.DataMessageStateEvent +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.rest.core.annotation.RepositoryRestResource +import org.springframework.stereotype.Repository @Repository @RepositoryRestResource(exported = false) -public interface NotificationStateEventRepository extends - JpaRepository { - - List findByNotificationId(long notificationId); - - long countByNotificationId(long notificationId); -} +interface DataMessageStateEventRepository : JpaRepository { + fun findByDataMessageId(dataMessageId: Long): List? + fun countByDataMessageId(dataMessageId: Long): Long +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/repository/NotificationRepository.java b/src/main/java/org/radarbase/appserver/repository/NotificationRepository.java deleted file mode 100644 index 4c82bfb9..00000000 --- a/src/main/java/org/radarbase/appserver/repository/NotificationRepository.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * - * * - * * * Copyright 2018 King's College London - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * * - * * - * - */ - -package org.radarbase.appserver.repository; - -import java.time.Instant; -import java.util.List; -import java.util.Optional; -import javax.validation.constraints.NotNull; -import org.radarbase.appserver.entity.Notification; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.rest.core.annotation.RepositoryRestResource; -import org.springframework.stereotype.Repository; - -/** @author yatharthranjan */ -@Repository -@RepositoryRestResource(exported = false) -public interface NotificationRepository extends JpaRepository { - - List findByUserId(Long userId); - - void deleteByUserId(Long userId); - - boolean existsByUserIdAndSourceIdAndScheduledTimeAndTitleAndBodyAndTypeAndTtlSeconds( - Long userId, - String sourceId, - Instant scheduledTime, - String title, - String body, - String type, - int ttlSeconds); - - Optional findByUserIdAndSourceIdAndScheduledTimeAndTitleAndBodyAndTypeAndTtlSeconds( - Long userId, - String sourceId, - Instant scheduledTime, - String title, - String body, - String type, - int ttlSeconds); - - boolean existsByIdAndUserId(Long id, Long userId); - - boolean existsById(@NotNull Long id); - - void deleteByFcmMessageId(String fcmMessageId); - - void deleteByIdAndUserId(Long id, Long userId); - - Optional findByFcmMessageId(String fcmMessageId); - - Optional findByIdAndUserId(long id, long userId); -} diff --git a/src/main/java/org/radarbase/appserver/repository/NotificationRepository.kt b/src/main/java/org/radarbase/appserver/repository/NotificationRepository.kt new file mode 100644 index 00000000..d81325a5 --- /dev/null +++ b/src/main/java/org/radarbase/appserver/repository/NotificationRepository.kt @@ -0,0 +1,64 @@ +/* + * + * * + * * * Copyright 2018 King's College London + * * * + * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * you may not use this file except in compliance with the License. + * * * You may obtain a copy of the License at + * * * + * * * http://www.apache.org/licenses/LICENSE-2.0 + * * * + * * * Unless required by applicable law or agreed to in writing, software + * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * See the License for the specific language governing permissions and + * * * limitations under the License. + * * * + * * + * + */ +package org.radarbase.appserver.repository + +import org.radarbase.appserver.entity.Notification +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.rest.core.annotation.RepositoryRestResource +import org.springframework.stereotype.Repository +import java.time.Instant +import java.util.* +import javax.validation.constraints.NotNull + +/** @author yatharthranjan + */ +@Repository +@RepositoryRestResource(exported = false) +interface NotificationRepository : JpaRepository { + fun findByUserId(userId: Long?): List + fun deleteByUserId(userId: Long?) + fun existsByUserIdAndSourceIdAndScheduledTimeAndTitleAndBodyAndTypeAndTtlSeconds( + userId: Long?, + sourceId: String?, + scheduledTime: Instant?, + title: String?, + body: String?, + type: String?, + ttlSeconds: Int + ): Boolean + + fun findByUserIdAndSourceIdAndScheduledTimeAndTitleAndBodyAndTypeAndTtlSeconds( + userId: Long?, + sourceId: String?, + scheduledTime: Instant?, + title: String?, + body: String?, + type: String?, + ttlSeconds: Int + ): Optional? + + fun existsByIdAndUserId(id: Long?, userId: Long?): Boolean + override fun existsById(id: @NotNull Long?): Boolean + fun deleteByFcmMessageId(fcmMessageId: String?) + fun deleteByIdAndUserId(id: Long?, userId: Long?) + fun findByFcmMessageId(fcmMessageId: String?): Optional? + fun findByIdAndUserId(id: Long, userId: Long): Optional? +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/repository/DataMessageStateEventRepository.java b/src/main/java/org/radarbase/appserver/repository/NotificationStateEventRepository.kt similarity index 62% rename from src/main/java/org/radarbase/appserver/repository/DataMessageStateEventRepository.java rename to src/main/java/org/radarbase/appserver/repository/NotificationStateEventRepository.kt index 2c724c3d..b9b15fc6 100644 --- a/src/main/java/org/radarbase/appserver/repository/DataMessageStateEventRepository.java +++ b/src/main/java/org/radarbase/appserver/repository/NotificationStateEventRepository.kt @@ -18,21 +18,16 @@ * * * */ +package org.radarbase.appserver.repository -package org.radarbase.appserver.repository; - -import java.util.List; -import org.radarbase.appserver.entity.DataMessageStateEvent; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.rest.core.annotation.RepositoryRestResource; -import org.springframework.stereotype.Repository; +import org.radarbase.appserver.entity.NotificationStateEvent +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.rest.core.annotation.RepositoryRestResource +import org.springframework.stereotype.Repository @Repository @RepositoryRestResource(exported = false) -public interface DataMessageStateEventRepository extends - JpaRepository { - - List findByDataMessageId(long dataMessageId); - - long countByDataMessageId(long dataMessageId); -} +interface NotificationStateEventRepository : JpaRepository { + fun findByNotificationId(notificationId: Long): List? + fun countByNotificationId(notificationId: Long): Long +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/repository/ProjectRepository.java b/src/main/java/org/radarbase/appserver/repository/ProjectRepository.kt similarity index 64% rename from src/main/java/org/radarbase/appserver/repository/ProjectRepository.java rename to src/main/java/org/radarbase/appserver/repository/ProjectRepository.kt index 8c0209e9..3157c51b 100644 --- a/src/main/java/org/radarbase/appserver/repository/ProjectRepository.java +++ b/src/main/java/org/radarbase/appserver/repository/ProjectRepository.kt @@ -18,21 +18,19 @@ * * * */ +package org.radarbase.appserver.repository -package org.radarbase.appserver.repository; +import org.radarbase.appserver.entity.Project +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.rest.core.annotation.RepositoryRestResource +import org.springframework.stereotype.Repository +import java.util.* -import java.util.Optional; -import org.radarbase.appserver.entity.Project; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.rest.core.annotation.RepositoryRestResource; -import org.springframework.stereotype.Repository; - -/** @author yatharthranjan */ +/** @author yatharthranjan + */ @Repository @RepositoryRestResource(exported = false) -public interface ProjectRepository extends JpaRepository { - - Optional findByProjectId(String projectId); - - Boolean existsByProjectId(String projectId); -} +interface ProjectRepository : JpaRepository { + fun findByProjectId(projectId: String?): Optional? + fun existsByProjectId(projectId: String?): Boolean? +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/repository/UserRepository.java b/src/main/java/org/radarbase/appserver/repository/UserRepository.kt similarity index 54% rename from src/main/java/org/radarbase/appserver/repository/UserRepository.java rename to src/main/java/org/radarbase/appserver/repository/UserRepository.kt index 29c5549d..8349d5cb 100644 --- a/src/main/java/org/radarbase/appserver/repository/UserRepository.java +++ b/src/main/java/org/radarbase/appserver/repository/UserRepository.kt @@ -18,29 +18,23 @@ * * * */ +package org.radarbase.appserver.repository -package org.radarbase.appserver.repository; +import org.radarbase.appserver.entity.User +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.rest.core.annotation.RepositoryRestResource +import org.springframework.stereotype.Repository +import java.util.* +import javax.validation.constraints.NotNull -import java.util.List; -import java.util.Optional; -import javax.validation.constraints.NotNull; -import org.radarbase.appserver.entity.User; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.rest.core.annotation.RepositoryRestResource; -import org.springframework.stereotype.Repository; - -/** @author yatharthranjan */ +/** @author yatharthranjan + */ @Repository @RepositoryRestResource(exported = false) -public interface UserRepository extends JpaRepository { - - Optional findBySubjectId(String subjectId); - - List findByProjectId(Long projectId); - - Optional findBySubjectIdAndProjectId(String subjectId, Long projectId); - - Optional findByFcmToken(String fcmToken); - - void deleteById(@NotNull Long id); -} +interface UserRepository : JpaRepository { + fun findBySubjectId(subjectId: String?): Optional? + fun findByProjectId(projectId: Long?): List? + fun findBySubjectIdAndProjectId(subjectId: String?, projectId: Long?): Optional? + fun findByFcmToken(fcmToken: String?): Optional? + override fun deleteById(id: @NotNull Long?) +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/service/FcmDataMessageService.java b/src/main/java/org/radarbase/appserver/service/FcmDataMessageService.java index a75a980d..483d6e0b 100644 --- a/src/main/java/org/radarbase/appserver/service/FcmDataMessageService.java +++ b/src/main/java/org/radarbase/appserver/service/FcmDataMessageService.java @@ -97,7 +97,9 @@ public FcmDataMessages getAllDataMessages() { @Transactional(readOnly = true) public FcmDataMessageDto getDataMessageById(long id) { Optional dataMessage = dataMessageRepository.findById(id); - return dataMessageConverter.entityToDto(dataMessage.orElseGet(DataMessage::new)); + return dataMessageConverter.entityToDto(dataMessage.orElseThrow(() -> { + throw new NotFoundException("Data Message with id " + id + "not found"); + })); } @Transactional(readOnly = true) @@ -143,7 +145,8 @@ public boolean checkIfDataMessageExists(FcmDataMessageDto dataMessageDto, String if (user.isEmpty()) { throw new NotFoundException(INVALID_SUBJECT_ID_MESSAGE); } - DataMessage dataMessage = new DataMessage.DataMessageBuilder(dataMessageConverter.dtoToEntity(dataMessageDto)).user(user.get()).build(); + DataMessage dataMessage = dataMessageConverter.dtoToEntity(dataMessageDto); + dataMessage.setUser(user.get()); List dataMessages = this.dataMessageRepository.findByUserId(user.get().getId()); return dataMessages.contains(dataMessage); @@ -173,9 +176,10 @@ public FcmDataMessageDto addDataMessage( dataMessageDto.getScheduledTime(), dataMessageDto.getTtlSeconds())) { - DataMessage dataMessageSaved = - this.dataMessageRepository.saveAndFlush( - new DataMessage.DataMessageBuilder(dataMessageConverter.dtoToEntity(dataMessageDto)).user(user).build()); + DataMessage dataMessage = dataMessageConverter.dtoToEntity(dataMessageDto); + dataMessage.setUser(user); + + DataMessage dataMessageSaved = this.dataMessageRepository.saveAndFlush(dataMessage); user.getUsermetrics().setLastOpened(Instant.now()); this.userRepository.save(user); addDataMessageStateEvent( @@ -190,7 +194,7 @@ public FcmDataMessageDto addDataMessage( private void addDataMessageStateEvent( DataMessage dataMessage, MessageState state, Instant time) { - if (dataMessageStateEventPublisher != null) { + if (dataMessageStateEventPublisher!=null) { DataMessageStateEventDto dataMessageStateEvent = new DataMessageStateEventDto(this, dataMessage, state, null, time); dataMessageStateEventPublisher.publishEvent(dataMessageStateEvent); @@ -201,7 +205,7 @@ private void addDataMessageStateEvent( public FcmDataMessageDto updateDataMessage( FcmDataMessageDto dataMessageDto, String subjectId, String projectId) { - if (dataMessageDto.getId() == null) { + if (dataMessageDto.getId()==null) { throw new InvalidNotificationDetailsException( "ID must be supplied for updating the data message"); } @@ -215,17 +219,16 @@ public FcmDataMessageDto updateDataMessage( throw new NotFoundException("Data message does not exist. Please create first"); } - DataMessage newDataMessage = new DataMessage.DataMessageBuilder(dataMessage.get()) - .scheduledTime(dataMessageDto.getScheduledTime()) - .sourceId(dataMessageDto.getSourceId()) - .ttlSeconds(dataMessageDto.getTtlSeconds()) - .user(user) - .fcmMessageId(String.valueOf(dataMessageDto.hashCode())) - .build(); + DataMessage newDataMessage = dataMessage.get().copy(dataMessage.get().getId(), user); + newDataMessage.setSourceId(dataMessageDto.getSourceId()); + newDataMessage.setScheduledTime(dataMessageDto.getScheduledTime()); + newDataMessage.setTtlSeconds(dataMessageDto.getTtlSeconds()); + newDataMessage.setFcmMessageId(String.valueOf(dataMessageDto.hashCode())); + DataMessage dataMessageSaved = this.dataMessageRepository.saveAndFlush(newDataMessage); addDataMessageStateEvent( dataMessageSaved, MessageState.UPDATED, dataMessageSaved.getUpdatedAt().toInstant()); - if (!dataMessage.get().isDelivered()) { + if (!dataMessage.get().getDelivered()) { this.schedulerService.updateScheduled(dataMessageSaved); } return dataMessageConverter.entityToDto(dataMessageSaved); @@ -249,7 +252,8 @@ public void updateDeliveryStatus(String fcmMessageId, boolean isDelivered) { dataMessage.ifPresentOrElse( (DataMessage d) -> { - DataMessage newDataMessage = new DataMessage.DataMessageBuilder(d).delivered(isDelivered).build(); + DataMessage newDataMessage = d.copy(); + newDataMessage.setDelivered(isDelivered); this.dataMessageRepository.save(newDataMessage); }, () -> { @@ -294,8 +298,8 @@ public FcmDataMessages addDataMessages( List newDataMessages = dataMessageDtos.getDataMessages().stream() .map(dataMessageConverter::dtoToEntity) - .map(d -> new DataMessage.DataMessageBuilder(d).user(user).build()) - .filter(dataMessage -> !dataMessages.contains(dataMessage)) + .map(d -> d.copy(d.getId(), user)) + .filter((DataMessage dataMessage) -> !dataMessages.contains(dataMessage)) .collect(Collectors.toList()); List savedDataMessages = this.dataMessageRepository.saveAll(newDataMessages); diff --git a/src/main/java/org/radarbase/appserver/service/FcmNotificationService.java b/src/main/java/org/radarbase/appserver/service/FcmNotificationService.java index 905066f0..42b8573e 100644 --- a/src/main/java/org/radarbase/appserver/service/FcmNotificationService.java +++ b/src/main/java/org/radarbase/appserver/service/FcmNotificationService.java @@ -28,7 +28,6 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; - import org.radarbase.appserver.converter.NotificationConverter; import org.radarbase.appserver.dto.fcm.FcmNotificationDto; import org.radarbase.appserver.dto.fcm.FcmNotifications; @@ -98,7 +97,9 @@ public FcmNotifications getAllNotifications() { @Transactional(readOnly = true) public FcmNotificationDto getNotificationById(long id) { Optional notification = notificationRepository.findById(id); - return notificationConverter.entityToDto(notification.orElseGet(Notification::new)); + return notificationConverter.entityToDto(notification.orElseThrow(() -> { + throw new NotFoundException("Notification with id " + id + "not found"); + })); } @Transactional(readOnly = true) @@ -144,7 +145,9 @@ public boolean checkIfNotificationExists(FcmNotificationDto notificationDto, Str if (user.isEmpty()) { throw new NotFoundException(INVALID_SUBJECT_ID_MESSAGE); } - Notification notification = new Notification.NotificationBuilder(notificationConverter.dtoToEntity(notificationDto)).user(user.get()).build(); + + Notification notification = notificationConverter.dtoToEntity(notificationDto).copy(); + notification.setUser(user.get()); List notifications = this.notificationRepository.findByUserId(user.get().getId()); return notifications.contains(notification); @@ -180,7 +183,9 @@ public FcmNotificationDto addNotification( if (notification.isEmpty()) { Notification notificationSaved = this.notificationRepository.saveAndFlush( - new Notification.NotificationBuilder(notificationConverter.dtoToEntity(notificationDto)).user(user).build()); + notificationConverter.dtoToEntity(notificationDto) + .copy(notificationDto.getId(), user) + ); user.getUsermetrics().setLastOpened(Instant.now()); this.userRepository.save(user); addNotificationStateEvent( @@ -212,11 +217,12 @@ public FcmNotificationDto addNotification( notificationDto.getTtlSeconds()); if (notification.isEmpty()) { - Notification notificationSaved = - this.notificationRepository.saveAndFlush( - new Notification.NotificationBuilder(notificationConverter.dtoToEntity(notificationDto)).user(user).build()); + Notification notif = notificationConverter.dtoToEntity(notificationDto); + notif.setUser(user); + Notification notificationSaved = this.notificationRepository.saveAndFlush(notif); user.getUsermetrics().setLastOpened(Instant.now()); this.userRepository.save(user); + addNotificationStateEvent( notificationSaved, MessageState.ADDED, notificationSaved.getCreatedAt().toInstant()); this.schedulerService.schedule(notificationSaved); @@ -230,7 +236,7 @@ public FcmNotificationDto addNotification( private void addNotificationStateEvent( Notification notification, MessageState state, Instant time) { - if (notificationStateEventPublisher != null) { + if (notificationStateEventPublisher!=null) { NotificationStateEventDto notificationStateEvent = new NotificationStateEventDto(this, notification, state, null, time); notificationStateEventPublisher.publishEvent(notificationStateEvent); @@ -242,7 +248,7 @@ private void addNotificationStateEvent( public FcmNotificationDto updateNotification( FcmNotificationDto notificationDto, String subjectId, String projectId) { - if (notificationDto.getId() == null) { + if (notificationDto.getId()==null) { throw new InvalidNotificationDetailsException( "ID must be supplied for updating the notification"); } @@ -256,20 +262,24 @@ public FcmNotificationDto updateNotification( throw new NotFoundException("Notification does not exist. Please create first"); } - Notification newNotification = new Notification.NotificationBuilder(notification.get()) - .body(notificationDto.getBody()) - .scheduledTime(notificationDto.getScheduledTime()) - .sourceId(notificationDto.getSourceId()) - .title(notificationDto.getTitle()) - .ttlSeconds(notificationDto.getTtlSeconds()) - .type(notificationDto.getType()) - .user(user) - .fcmMessageId(String.valueOf(notificationDto.hashCode())) - .build(); + Notification newNotification = notification.get().copy( + notification.get().getId(), + user, + notificationDto.getScheduledTime(), + notificationDto.getSourceId(), + notificationDto.getSourceType(), + notificationDto.getTtlSeconds(), + String.valueOf(notificationDto.hashCode()) + ); + newNotification.setType(notificationDto.getType()); + newNotification.setTitle(notificationDto.getTitle()); + newNotification.setBody(notificationDto.getBody()); + newNotification.setAdditionalData(notificationDto.getAdditionalData()); + Notification notificationSaved = this.notificationRepository.saveAndFlush(newNotification); addNotificationStateEvent( notificationSaved, MessageState.UPDATED, notificationSaved.getUpdatedAt().toInstant()); - if (!notification.get().isDelivered()) { + if (!notification.get().getDelivered()) { this.schedulerService.updateScheduled(notificationSaved); } return notificationConverter.entityToDto(notificationSaved); @@ -321,7 +331,8 @@ public void updateDeliveryStatus(String fcmMessageId, boolean isDelivered) { notification.ifPresentOrElse( (Notification n) -> { - Notification newNotif = new Notification.NotificationBuilder(n).delivered(isDelivered).build(); + Notification newNotif = n.copy(); + n.setDelivered(isDelivered); this.notificationRepository.save(newNotif); }, () -> { @@ -370,7 +381,7 @@ public FcmNotifications addNotifications( List newNotifications = notificationDtos.getNotifications().stream() .map(notificationConverter::dtoToEntity) - .map(n -> new Notification.NotificationBuilder(n).user(user).build()) + .map(n -> n.copy(n.getId(), user)) .filter(notification -> !notifications.contains(notification)) .collect(Collectors.toList()); @@ -394,7 +405,7 @@ public FcmNotifications addNotifications( List newNotifications = notificationDtos.getNotifications().stream() .map(notificationConverter::dtoToEntity) - .map(n -> new Notification.NotificationBuilder(n).user(user).build()) + .map(n -> n.copy(n.getId(), user)) .filter(notification -> !notifications.contains(notification)) .collect(Collectors.toList()); diff --git a/src/main/java/org/radarbase/appserver/service/UserService.java b/src/main/java/org/radarbase/appserver/service/UserService.java index e445537b..42e8e89a 100644 --- a/src/main/java/org/radarbase/appserver/service/UserService.java +++ b/src/main/java/org/radarbase/appserver/service/UserService.java @@ -192,7 +192,7 @@ public FcmUserDto updateUser(FcmUserDto userDto) { User updatedUser = user.get() .setFcmToken(userDto.getFcmToken()) - .setUserMetrics(UserConverter.getValidUserMetrics(userDto)) + .setUsermetrics(UserConverter.getValidUserMetrics(userDto)) .setEnrolmentDate(userDto.getEnrolmentDate()) .setTimezone(userDto.getTimezone()); // maintain a bi-directional relationship diff --git a/src/main/java/org/radarbase/appserver/service/scheduler/DataMessageSchedulerService.java b/src/main/java/org/radarbase/appserver/service/scheduler/DataMessageSchedulerService.java deleted file mode 100644 index 57c4c396..00000000 --- a/src/main/java/org/radarbase/appserver/service/scheduler/DataMessageSchedulerService.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * - * * - * * * Copyright 2018 King's College London - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * * - * * - * - */ - -package org.radarbase.appserver.service.scheduler; - -import java.util.Objects; -import lombok.extern.slf4j.Slf4j; -import org.radarbase.appserver.entity.DataMessage; -import org.radarbase.appserver.service.scheduler.quartz.SchedulerService; -import org.radarbase.fcm.downstream.FcmSender; -import org.radarbase.fcm.model.FcmDataMessage; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Service; - -/** - * {@link Service} for scheduling Data Messages to be sent through FCM at the {@link - * org.radarbase.appserver.entity.Scheduled} time. It also provided functions for updating/ deleting - * already scheduled Data Messsage Jobs. - * - * @author yatharthranjan - */ -@Service -@Slf4j -@SuppressWarnings("PMD.DataflowAnomalyAnalysis") -public class DataMessageSchedulerService extends MessageSchedulerService { - - public DataMessageSchedulerService( - @Autowired @Qualifier("fcmSenderProps") FcmSender fcmSender, - @Autowired SchedulerService schedulerService) { - super(fcmSender, schedulerService); - } - - private static FcmDataMessage createMessageFromDataMessage(DataMessage dataMessage) { - - String to = - Objects.requireNonNullElseGet( - dataMessage.getFcmTopic(), dataMessage.getUser()::getFcmToken); - return FcmDataMessage.builder() - .to(to) - .condition(dataMessage.getFcmCondition()) - .priority(dataMessage.getPriority()) - .mutableContent(dataMessage.isMutableContent()) - .deliveryReceiptRequested(IS_DELIVERY_RECEIPT_REQUESTED) - .messageId(String.valueOf(dataMessage.getFcmMessageId())) - .timeToLive(Objects.requireNonNullElse(dataMessage.getTtlSeconds(), 2_419_200)) - .data(dataMessage.getDataMap()) - .build(); - } - - public void send(DataMessage dataMessage) throws Exception { - fcmSender.send(createMessageFromDataMessage(dataMessage)); - } -} diff --git a/src/main/java/org/radarbase/appserver/service/scheduler/DataMessageSchedulerService.kt b/src/main/java/org/radarbase/appserver/service/scheduler/DataMessageSchedulerService.kt new file mode 100644 index 00000000..e8878a68 --- /dev/null +++ b/src/main/java/org/radarbase/appserver/service/scheduler/DataMessageSchedulerService.kt @@ -0,0 +1,68 @@ +/* + * + * * + * * * Copyright 2018 King's College London + * * * + * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * you may not use this file except in compliance with the License. + * * * You may obtain a copy of the License at + * * * + * * * http://www.apache.org/licenses/LICENSE-2.0 + * * * + * * * Unless required by applicable law or agreed to in writing, software + * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * See the License for the specific language governing permissions and + * * * limitations under the License. + * * * + * * + * + */ +package org.radarbase.appserver.service.scheduler + +import org.radarbase.appserver.entity.DataMessage +import org.radarbase.appserver.service.scheduler.quartz.SchedulerService +import org.radarbase.fcm.downstream.FcmSender +import org.radarbase.fcm.model.FcmDataMessage +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.stereotype.Service +import kotlin.random.Random + +/** + * [Service] for scheduling Data Messages to be sent through FCM at the [ ] time. It also provided functions for updating/ deleting + * already scheduled Data Messsage Jobs. + * + * @author yatharthranjan + */ +@Service +class DataMessageSchedulerService( + @Autowired @Qualifier("fcmSenderProps") fcmSender: FcmSender?, + @Autowired schedulerService: SchedulerService? +) : MessageSchedulerService(fcmSender, schedulerService) { + @Throws(Exception::class) + override fun send(dataMessage: DataMessage?) { + dataMessage?.let { + fcmSender.send(createMessageFromDataMessage(it)) + } + } + + companion object { + private fun createMessageFromDataMessage(dataMessage: DataMessage): FcmDataMessage { + val to = checkNotNull( + dataMessage.fcmTopic ?: dataMessage.user?.fcmToken + ) { "FCM Topic or User FCM Token is not set" } + + return FcmDataMessage( + to = to, + condition = dataMessage.fcmCondition, + priority = dataMessage.priority, + mutableContent = dataMessage.mutableContent, + deliveryReceiptRequested = IS_DELIVERY_RECEIPT_REQUESTED, + messageId = dataMessage.fcmMessageId, + timeToLive = dataMessage.ttlSeconds, + data = dataMessage.dataMap, + ) + } + } +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/service/scheduler/NotificationSchedulerService.java b/src/main/java/org/radarbase/appserver/service/scheduler/NotificationSchedulerService.java deleted file mode 100644 index 42688303..00000000 --- a/src/main/java/org/radarbase/appserver/service/scheduler/NotificationSchedulerService.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * - * * - * * * Copyright 2018 King's College London - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * * - * * - * - */ - -package org.radarbase.appserver.service.scheduler; - -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import lombok.extern.slf4j.Slf4j; -import org.radarbase.appserver.entity.Notification; -import org.radarbase.appserver.service.scheduler.quartz.SchedulerService; -import org.radarbase.fcm.downstream.FcmSender; -import org.radarbase.fcm.model.FcmNotificationMessage; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Service; - -/** - * {@link Service} for scheduling Notifications to be sent through FCM at the {@link - * org.radarbase.appserver.entity.Scheduled} time. It also provided functions for updating/ deleting - * already scheduled Notification Jobs. - * - * @author yatharthranjan - */ -@Service -@Slf4j -public class NotificationSchedulerService extends MessageSchedulerService { - - public NotificationSchedulerService( - @Autowired @Qualifier("fcmSenderProps") FcmSender fcmSender, - @Autowired SchedulerService schedulerService) { - super(fcmSender, schedulerService); - } - - private static Map getNotificationMap(Notification notification) { - Map notificationMap = new HashMap<>(); - notificationMap.put("body", notification.getBody()); - notificationMap.put("title", notification.getTitle()); - notificationMap.put("sound", "default"); - - putIfNotNull(notificationMap, "sound", notification.getSound()); - putIfNotNull(notificationMap, "badge", notification.getBadge()); - putIfNotNull(notificationMap, "click_action", notification.getClickAction()); - putIfNotNull(notificationMap, "subtitle", notification.getSubtitle()); - putIfNotNull(notificationMap, "body_loc_key", notification.getBodyLocKey()); - putIfNotNull(notificationMap, "body_loc_args", notification.getBodyLocArgs()); - putIfNotNull(notificationMap, "title_loc_key", notification.getTitleLocKey()); - putIfNotNull(notificationMap, "title_loc_args", notification.getTitleLocArgs()); - putIfNotNull(notificationMap, "android_channel_id", notification.getAndroidChannelId()); - putIfNotNull(notificationMap, "icon", notification.getIcon()); - putIfNotNull(notificationMap, "tag", notification.getTag()); - putIfNotNull(notificationMap, "color", notification.getColor()); - - return notificationMap; - } - - private static FcmNotificationMessage createMessageFromNotification(Notification notification) { - - String to = - Objects.requireNonNullElseGet( - notification.getFcmTopic(), notification.getUser()::getFcmToken); - return FcmNotificationMessage.builder() - .to(to) - .condition(notification.getFcmCondition()) - .priority(notification.getPriority()) - .mutableContent(notification.isMutableContent()) - .deliveryReceiptRequested(IS_DELIVERY_RECEIPT_REQUESTED) - .messageId(String.valueOf(notification.getFcmMessageId())) - .timeToLive(Objects.requireNonNullElse(notification.getTtlSeconds(), 2_419_200)) - .notification(getNotificationMap(notification)) - .data(notification.getAdditionalData()) - .build(); - } - - - public void send(Notification notification) throws Exception { - fcmSender.send(createMessageFromNotification(notification)); - } -} diff --git a/src/main/java/org/radarbase/appserver/service/scheduler/NotificationSchedulerService.kt b/src/main/java/org/radarbase/appserver/service/scheduler/NotificationSchedulerService.kt new file mode 100644 index 00000000..8a60efa6 --- /dev/null +++ b/src/main/java/org/radarbase/appserver/service/scheduler/NotificationSchedulerService.kt @@ -0,0 +1,89 @@ +/* + * + * * + * * * Copyright 2018 King's College London + * * * + * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * you may not use this file except in compliance with the License. + * * * You may obtain a copy of the License at + * * * + * * * http://www.apache.org/licenses/LICENSE-2.0 + * * * + * * * Unless required by applicable law or agreed to in writing, software + * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * See the License for the specific language governing permissions and + * * * limitations under the License. + * * * + * * + * + */ +package org.radarbase.appserver.service.scheduler + +import org.radarbase.appserver.entity.Notification +import org.radarbase.appserver.service.scheduler.quartz.SchedulerService +import org.radarbase.fcm.downstream.FcmSender +import org.radarbase.fcm.model.FcmNotificationMessage +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.stereotype.Service +import kotlin.random.Random + +/** + * [Service] for scheduling Notifications to be sent through FCM at the [ ] time. It also provided functions for updating/ deleting + * already scheduled Notification Jobs. + * + * @author yatharthranjan + */ +@Service +class NotificationSchedulerService( + @Autowired @Qualifier("fcmSenderProps") fcmSender: FcmSender?, + @Autowired schedulerService: SchedulerService? +) : MessageSchedulerService(fcmSender, schedulerService) { + @Throws(Exception::class) + override fun send(notification: Notification?) { + notification?.let { + fcmSender.send(createMessageFromNotification(it)) + } + } + + companion object { + private fun getNotificationMap(notification: Notification): Map { + val notificationMap: MutableMap = HashMap() + notificationMap["body"] = notification.body ?: "" + notificationMap["title"] = notification.title ?: "Alert from RADAR-Base" + notificationMap["sound"] = "default" + putIfNotNull(notificationMap, "sound", notification.sound) + putIfNotNull(notificationMap, "badge", notification.badge) + putIfNotNull(notificationMap, "click_action", notification.clickAction) + putIfNotNull(notificationMap, "subtitle", notification.subtitle) + putIfNotNull(notificationMap, "body_loc_key", notification.bodyLocKey) + putIfNotNull(notificationMap, "body_loc_args", notification.bodyLocArgs) + putIfNotNull(notificationMap, "title_loc_key", notification.titleLocKey) + putIfNotNull(notificationMap, "title_loc_args", notification.titleLocArgs) + putIfNotNull(notificationMap, "android_channel_id", notification.androidChannelId) + putIfNotNull(notificationMap, "icon", notification.icon) + putIfNotNull(notificationMap, "tag", notification.tag) + putIfNotNull(notificationMap, "color", notification.color) + return notificationMap + } + + private fun createMessageFromNotification(notification: Notification): FcmNotificationMessage { + val to = notification.fcmTopic ?: notification.user?.fcmToken + ?: throw IllegalArgumentException("FCM Topic or User FCM Token is not set") + + return FcmNotificationMessage( + to = to, + condition = notification.fcmCondition, + priority = notification.priority, + mutableContent = notification.mutableContent, + deliveryReceiptRequested = IS_DELIVERY_RECEIPT_REQUESTED, + messageId = notification.fcmMessageId + ?: Random(System.currentTimeMillis()).nextLong().toString(), + timeToLive = notification.ttlSeconds, + notification = getNotificationMap(notification), + data = notification.additionalData, + ) + } + } +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/appserver/service/scheduler/quartz/SimpleQuartzNamingStrategy.java b/src/main/java/org/radarbase/appserver/service/scheduler/quartz/SimpleQuartzNamingStrategy.java index 545439a8..ef30e06b 100644 --- a/src/main/java/org/radarbase/appserver/service/scheduler/quartz/SimpleQuartzNamingStrategy.java +++ b/src/main/java/org/radarbase/appserver/service/scheduler/quartz/SimpleQuartzNamingStrategy.java @@ -31,16 +31,15 @@ public class SimpleQuartzNamingStrategy implements QuartzNamingStrategy { @Override public String getTriggerName(String userName, String messageId) { - return new StringBuilder(TRIGGER_PREFIX) - .append(userName) - .append('-') - .append(messageId) - .toString(); + return TRIGGER_PREFIX + + userName + + '-' + + messageId; } @Override public String getJobKeyName(String userName, String messageId) { - return new StringBuilder(JOB_PREFIX).append(userName).append('-').append(messageId).toString(); + return JOB_PREFIX + userName + '-' + messageId; } @Override diff --git a/src/main/java/org/radarbase/fcm/common/CcsClient.java b/src/main/java/org/radarbase/fcm/common/CcsClient.java deleted file mode 100644 index 8e20ca12..00000000 --- a/src/main/java/org/radarbase/fcm/common/CcsClient.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * - * * - * * * Copyright 2018 King's College London - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * * - * * - * - */ - -package org.radarbase.fcm.common; - -/** - * A stereotype to specify if a class is a client of the FCM CCS server. - * - * @author yatharthranjan - */ -public interface CcsClient {} diff --git a/src/main/java/org/radarbase/fcm/common/ObjectMapperFactory.java b/src/main/java/org/radarbase/fcm/common/ObjectMapperFactory.java deleted file mode 100644 index cc9a5fd9..00000000 --- a/src/main/java/org/radarbase/fcm/common/ObjectMapperFactory.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * - * * - * * * Copyright 2018 King's College London - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * * - * * - * - */ - -package org.radarbase.fcm.common; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import org.springframework.beans.factory.config.AbstractFactoryBean; -import org.springframework.stereotype.Component; - -/** - * A Factory Bean that provides {@link ObjectMapper} so that can be {@link - * org.springframework.beans.factory.annotation.Autowired} and same instance can be used everywhere. - * - * @see AbstractFactoryBean for more details - * @author yatharthranjan - */ -@Component -public class ObjectMapperFactory extends AbstractFactoryBean { - - // TODO Remove this if can directly autowire from Spring context - - @Override - public Class getObjectType() { - return ObjectMapper.class; - } - - @Override - protected ObjectMapper createInstance() { - return new ObjectMapper().registerModule(new JavaTimeModule()); - } -} diff --git a/src/main/java/org/radarbase/fcm/common/ObjectMapperFactory.kt b/src/main/java/org/radarbase/fcm/common/ObjectMapperFactory.kt new file mode 100644 index 00000000..03d08eea --- /dev/null +++ b/src/main/java/org/radarbase/fcm/common/ObjectMapperFactory.kt @@ -0,0 +1,53 @@ +/* + * + * * + * * * Copyright 2018 King's College London + * * * + * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * you may not use this file except in compliance with the License. + * * * You may obtain a copy of the License at + * * * + * * * http://www.apache.org/licenses/LICENSE-2.0 + * * * + * * * Unless required by applicable law or agreed to in writing, software + * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * See the License for the specific language governing permissions and + * * * limitations under the License. + * * * + * * + * + */ +package org.radarbase.fcm.common + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +import com.fasterxml.jackson.module.kotlin.KotlinFeature +import com.fasterxml.jackson.module.kotlin.KotlinModule +import org.springframework.beans.factory.config.AbstractFactoryBean +import org.springframework.stereotype.Component + +/** + * A Factory Bean that provides [ObjectMapper] so that can be [ ] and same instance can be used everywhere. + * + * @see AbstractFactoryBean for more details + * + * @author yatharthranjan + */ +@Component +class ObjectMapperFactory : AbstractFactoryBean() { + // TODO Remove this if can directly autowire from Spring context + override fun getObjectType(): Class<*> { + return ObjectMapper::class.java + } + + override fun createInstance(): ObjectMapper { + return ObjectMapper() + .registerModule( + KotlinModule.Builder() + .configure(KotlinFeature.NullIsSameAsDefault, true) + .build() + ) + .registerModule(JavaTimeModule()) + } +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/fcm/config/FcmSenderConfig.java b/src/main/java/org/radarbase/fcm/config/FcmSenderConfig.java deleted file mode 100644 index d0d8061c..00000000 --- a/src/main/java/org/radarbase/fcm/config/FcmSenderConfig.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.radarbase.fcm.config; - -import org.radarbase.fcm.downstream.FcmSender; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class FcmSenderConfig { - - private final transient FcmServerConfig fcmServerConfig; - - public FcmSenderConfig(FcmServerConfig fcmServerConfig) { - this.fcmServerConfig = fcmServerConfig; - } - - @Bean("fcmSenderProps") - @SuppressWarnings("unchecked") - public FcmSender getFcmSender() throws Exception { - Class senderClass = - (Class) Class.forName(fcmServerConfig.getFcmsender()); - return senderClass.getConstructor().newInstance(); - } -} diff --git a/src/main/java/org/radarbase/fcm/config/FcmSenderConfig.kt b/src/main/java/org/radarbase/fcm/config/FcmSenderConfig.kt new file mode 100644 index 00000000..2434c2e0 --- /dev/null +++ b/src/main/java/org/radarbase/fcm/config/FcmSenderConfig.kt @@ -0,0 +1,15 @@ +package org.radarbase.fcm.config + +import org.radarbase.fcm.downstream.FcmSender +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +open class FcmSenderConfig(private val fcmServerConfig: FcmServerConfig) { + @Throws(Exception::class) + @Bean("fcmSenderProps") + open fun fcmSender(): FcmSender { + val senderClass = Class.forName(fcmServerConfig.fcmsender) as Class + return senderClass.getConstructor().newInstance() + } +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/fcm/config/FcmServerConfig.java b/src/main/java/org/radarbase/fcm/config/FcmServerConfig.kt similarity index 79% rename from src/main/java/org/radarbase/fcm/config/FcmServerConfig.java rename to src/main/java/org/radarbase/fcm/config/FcmServerConfig.kt index 122f30ea..56c7a5ae 100644 --- a/src/main/java/org/radarbase/fcm/config/FcmServerConfig.java +++ b/src/main/java/org/radarbase/fcm/config/FcmServerConfig.kt @@ -18,23 +18,18 @@ * * * */ +package org.radarbase.fcm.config -package org.radarbase.fcm.config; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; +import com.fasterxml.jackson.annotation.JsonProperty +import org.springframework.boot.context.properties.ConfigurationProperties /** * Loads Configuration required to connect to the FCM server. * * @author yatharthranjan */ -@Data @ConfigurationProperties(value = "fcmserver") -public class FcmServerConfig { - - @JsonProperty("fcmsender") - private String fcmsender; - -} +data class FcmServerConfig( + @JsonProperty("fcmsender") + val fcmsender: String = "org.radarbase.fcm.downstream.AdminSdkFcmSender" +) \ No newline at end of file diff --git a/src/main/java/org/radarbase/fcm/downstream/AdminSdkFcmSender.java b/src/main/java/org/radarbase/fcm/downstream/AdminSdkFcmSender.java deleted file mode 100644 index 27e3e89c..00000000 --- a/src/main/java/org/radarbase/fcm/downstream/AdminSdkFcmSender.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * - * * - * * * Copyright 2018 King's College London - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * * - * * - * - */ - -package org.radarbase.fcm.downstream; - -import com.google.auth.oauth2.GoogleCredentials; -import com.google.firebase.FirebaseApp; -import com.google.firebase.FirebaseOptions; -import com.google.firebase.messaging.AndroidConfig; -import com.google.firebase.messaging.AndroidConfig.Priority; -import com.google.firebase.messaging.AndroidNotification; -import com.google.firebase.messaging.ApnsConfig; -import com.google.firebase.messaging.Aps; -import com.google.firebase.messaging.ApsAlert; -import com.google.firebase.messaging.FcmOptions; -import com.google.firebase.messaging.FirebaseMessaging; -import com.google.firebase.messaging.FirebaseMessagingException; -import com.google.firebase.messaging.Message; -import com.google.firebase.messaging.Notification; -import java.io.IOException; -import java.time.Duration; -import java.time.Instant; -import java.util.HashMap; -import java.util.Map; -import lombok.extern.slf4j.Slf4j; -import org.radarbase.fcm.model.FcmDataMessage; -import org.radarbase.fcm.model.FcmDownstreamMessage; -import org.radarbase.fcm.model.FcmNotificationMessage; - -/** - * When authorizing via a service account, you have to set the GOOGLE_APPLICATION_CREDENTIALS - * environment variable. For More info, see - * https://firebase.google.com/docs/admin/setup#initialize-sdk - * - * @author yatharthranjan - */ -@Slf4j -@SuppressWarnings({"PMD.DataflowAnomalyAnalysis", "PMD.AvoidDuplicateLiterals"}) -public class AdminSdkFcmSender implements FcmSender { - - public AdminSdkFcmSender() throws IOException { - // TODO also take config from application properties - FirebaseOptions options = - new FirebaseOptions.Builder() - .setCredentials(GoogleCredentials.getApplicationDefault()) - .build(); - - try { - FirebaseApp.initializeApp(options); - } catch (IllegalStateException exc) { - log.warn("Firebase app was already initialised. {}", exc.getMessage()); - } - } - - @Override - public void send(FcmDownstreamMessage downstreamMessage) throws FirebaseMessagingException { - // TODO Add support for WebPush as well - String priority = downstreamMessage.getPriority(); - - Message.Builder message = - Message.builder() - .setToken(downstreamMessage.getTo()) - .setFcmOptions(FcmOptions.builder().build()) - .setCondition(downstreamMessage.getCondition()); - - int ttl = downstreamMessage.getTimeToLive() * 1000; // Convert to milliseconds - - if (downstreamMessage instanceof FcmNotificationMessage) { - FcmNotificationMessage notificationMessage = (FcmNotificationMessage) downstreamMessage; - - message - .setAndroidConfig( - AndroidConfig.builder() - .setCollapseKey(downstreamMessage.getCollapseKey()) - .setPriority(priority == null ? Priority.HIGH : Priority.valueOf(priority)) - .setTtl(ttl) - .setNotification(getAndroidNotification(notificationMessage)) - .putAllData(notificationMessage.getData()) - .build()) - .setApnsConfig( - getApnsConfigBuilder(notificationMessage, ttl) - .putHeader("apns-push-type", "alert") - .build()) - .putAllData(notificationMessage.getData()) - .setCondition(notificationMessage.getCondition()) - .setNotification( - Notification.builder() - .setBody( - String.valueOf( - notificationMessage.getNotification().getOrDefault("body", ""))) - .setTitle( - String.valueOf( - notificationMessage.getNotification().getOrDefault("title", ""))) - .setImage( - String.valueOf( - notificationMessage.getNotification().getOrDefault("image_url", ""))) - .build()); - } else if (downstreamMessage instanceof FcmDataMessage) { - FcmDataMessage dataMessage = (FcmDataMessage) downstreamMessage; - message - .setAndroidConfig( - AndroidConfig.builder() - .setCollapseKey(downstreamMessage.getCollapseKey()) - .setPriority(priority == null ? Priority.NORMAL : Priority.valueOf(priority)) - .setTtl(ttl) - .putAllData(dataMessage.getData()) - .build()) - .setApnsConfig(getApnsConfigBuilder(dataMessage, ttl).build()) - .setCondition(dataMessage.getCondition()) - .putAllData(dataMessage.getData()); - } else { - throw new IllegalArgumentException( - "The Message type is not known." + downstreamMessage.getClass()); - } - - String response = FirebaseMessaging.getInstance().send(message.build()); - log.info("Message Sent with response : {}", response); - } - - private AndroidNotification getAndroidNotification(FcmNotificationMessage notificationMessage) { - AndroidNotification.Builder builder = - AndroidNotification.builder() - .setBody(String.valueOf(notificationMessage.getNotification().getOrDefault("body", ""))) - .setTitle( - String.valueOf(notificationMessage.getNotification().getOrDefault("title", ""))) - .setChannelId( - getString(notificationMessage.getNotification().get("android_channel_id"))) - .setColor(getString(notificationMessage.getNotification().get("color"))) - .setTag(getString(notificationMessage.getNotification().get("tag"))) - .setIcon(getString(notificationMessage.getNotification().get("icon"))) - .setSound(getString(notificationMessage.getNotification().get("sound"))) - .setClickAction(getString(notificationMessage.getNotification().get("click_action"))); - - String bodyLocKey = getString(notificationMessage.getNotification().get("body_loc_key")); - String titleLocKey = getString(notificationMessage.getNotification().get("title_loc_key")); - - if (bodyLocKey != null) { - builder - .setBodyLocalizationKey( - getString(notificationMessage.getNotification().get("body_loc_key"))) - .addBodyLocalizationArg( - getString(notificationMessage.getNotification().get("body_loc_args"))); - } - - if (titleLocKey != null) { - builder - .addTitleLocalizationArg( - getString(notificationMessage.getNotification().get("title_loc_args"))) - .setTitleLocalizationKey( - getString(notificationMessage.getNotification().get("title_loc_key"))); - } - - return builder.build(); - } - - /** - * Get the APNS config builder. More info on values and keys - - * https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns/ - * and - * https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification - */ - private ApnsConfig.Builder getApnsConfigBuilder(FcmDownstreamMessage message, int ttl) { - ApnsConfig.Builder config = ApnsConfig.builder(); - - if (message.getCollapseKey() != null) - config.putHeader("apns-collapse-id", message.getCollapseKey()); - - // The date at which the notification is no longer valid. This value is a UNIX epoch - // expressed in seconds (UTC). - config.putHeader( - "apns-expiration", - String.valueOf(Instant.now().plus(Duration.ofMillis(ttl)).toEpochMilli() / 1000)); - - if (message instanceof FcmNotificationMessage) { - FcmNotificationMessage notificationMessage = (FcmNotificationMessage) message; - Map apnsData = new HashMap<>(notificationMessage.getData()); - - ApsAlert.Builder apsAlertBuilder = ApsAlert.builder(); - String title = getString(notificationMessage.getNotification().get("title")); - if (title != null) apsAlertBuilder.setTitle(title); - - String body = getString(notificationMessage.getNotification().get("body")); - if (body != null) apsAlertBuilder.setBody(body); - - String titleLocKey = getString(notificationMessage.getNotification().get("title_loc_key")); - if (titleLocKey != null) apsAlertBuilder.setTitleLocalizationKey(titleLocKey); - - String titleLocArgs = getString(notificationMessage.getNotification().get("title_loc_args")); - if (titleLocKey != null && titleLocArgs != null) - apsAlertBuilder.addTitleLocalizationArg(titleLocArgs); - - String bodyLocKey = getString(notificationMessage.getNotification().get("body_loc_key")); - if (bodyLocKey != null) apsAlertBuilder.setLocalizationKey("body_loc_key"); - - String bodyLocArgs = getString(notificationMessage.getNotification().get("body_loc_args")); - if (bodyLocKey != null && bodyLocArgs != null) - apsAlertBuilder.addLocalizationArg(bodyLocArgs); - - Aps.Builder apsBuilder = Aps.builder(); - String sound = getString(notificationMessage.getNotification().get("sound")); - if (sound != null) apsBuilder.setSound(sound); - - String badge = getString(notificationMessage.getNotification().get("badge")); - if (badge != null) apsBuilder.setBadge(Integer.parseInt(badge)); - - String category = getString(notificationMessage.getNotification().get("category")); - if (category != null) apsBuilder.setCategory(category); - - String threadId = getString(notificationMessage.getNotification().get("thread_id")); - if (threadId != null) apsBuilder.setThreadId(threadId); - - if (notificationMessage.getContentAvailable() != null) - apsBuilder.setContentAvailable(notificationMessage.getContentAvailable()); - - if (notificationMessage.getMutableContent() != null) - apsBuilder.setMutableContent(notificationMessage.getMutableContent()); - - return config - .putAllCustomData(apnsData) - .setAps(apsBuilder.setAlert(apsAlertBuilder.build()).build()) - .putHeader("apns-push-type", "alert"); - - } else if (message instanceof FcmDataMessage) { - FcmDataMessage dataMessage = (FcmDataMessage) message; - Map apnsData = new HashMap<>(dataMessage.getData()); - - return config - .putAllCustomData(apnsData) - .setAps(Aps.builder().setContentAvailable(true).setSound("default").build()) - .putHeader("apns-push-type", "background") // No alert is shown - .putHeader("apns-priority", "5"); // 5 required in case of background type - } else { - throw new IllegalArgumentException("The Message type is not known." + message.getClass()); - } - } - - private String getString(Object obj) { - return obj == null ? null : String.valueOf(obj); - } - - @Override - public boolean doesProvideDeliveryReceipt() { - return false; - } -} diff --git a/src/main/java/org/radarbase/fcm/downstream/AdminSdkFcmSender.kt b/src/main/java/org/radarbase/fcm/downstream/AdminSdkFcmSender.kt new file mode 100644 index 00000000..c52c5ec7 --- /dev/null +++ b/src/main/java/org/radarbase/fcm/downstream/AdminSdkFcmSender.kt @@ -0,0 +1,92 @@ +/* + * + * * + * * * Copyright 2018 King's College London + * * * + * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * you may not use this file except in compliance with the License. + * * * You may obtain a copy of the License at + * * * + * * * http://www.apache.org/licenses/LICENSE-2.0 + * * * + * * * Unless required by applicable law or agreed to in writing, software + * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * See the License for the specific language governing permissions and + * * * limitations under the License. + * * * + * * + * + */ +package org.radarbase.fcm.downstream + +import com.google.auth.oauth2.GoogleCredentials +import com.google.firebase.FirebaseApp +import com.google.firebase.FirebaseOptions +import com.google.firebase.messaging.* +import org.radarbase.fcm.model.FcmDownstreamMessage +import org.radarbase.fcm.model.FcmNotificationMessage +import org.slf4j.LoggerFactory + +/** + * When authorizing via a service account, you have to set the GOOGLE_APPLICATION_CREDENTIALS + * environment variable. For More info, see + * https://firebase.google.com/docs/admin/setup#initialize-sdk + * + * @author yatharthranjan + */ +class AdminSdkFcmSender : FcmSender { + init { + // TODO also take config from application properties + val options = FirebaseOptions.builder() + .setCredentials(GoogleCredentials.getApplicationDefault()) + .build() + try { + FirebaseApp.initializeApp(options) + } catch (exc: IllegalStateException) { + logger.warn("Firebase app was already initialised. {}", exc.message) + } + } + + @Throws(FirebaseMessagingException::class) + override fun send(downstreamMessage: FcmDownstreamMessage) { + // TODO Add support for WebPush as well + val message = Message.builder() + .setToken(downstreamMessage.to) + .setFcmOptions(FcmOptions.builder().build()) + .setCondition(downstreamMessage.condition) + .setAndroidConfig(downstreamMessage.getAndroidConfig()) + .setApnsConfig(downstreamMessage.getApnsConfig()) + .putAllData(downstreamMessage.data) + .setCondition(downstreamMessage.condition) + + if (downstreamMessage is FcmNotificationMessage) { + message + .setNotification( + Notification.builder() + .setBody( + downstreamMessage.notification.getOrDefault("body", "").toString() + ) + .setTitle( + downstreamMessage.notification.getOrDefault("title", "").toString() + ) + .setImage( + downstreamMessage.notification.getOrDefault("image_url", "") + .toString() + ) + .build() + ) + } + + val response = FirebaseMessaging.getInstance().send(message.build()) + logger.info("Message Sent with response : {}", response) + } + + override fun doesProvideDeliveryReceipt(): Boolean { + return false + } + + companion object { + private val logger = LoggerFactory.getLogger(AdminSdkFcmSender::class.java) + } +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/fcm/downstream/FcmSender.java b/src/main/java/org/radarbase/fcm/downstream/FcmSender.kt similarity index 77% rename from src/main/java/org/radarbase/fcm/downstream/FcmSender.java rename to src/main/java/org/radarbase/fcm/downstream/FcmSender.kt index 93aed68e..b2a833dc 100644 --- a/src/main/java/org/radarbase/fcm/downstream/FcmSender.java +++ b/src/main/java/org/radarbase/fcm/downstream/FcmSender.kt @@ -18,19 +18,18 @@ * * * */ +package org.radarbase.fcm.downstream -package org.radarbase.fcm.downstream; - -import org.radarbase.fcm.model.FcmDownstreamMessage; +import org.radarbase.fcm.model.FcmDownstreamMessage /** * Generic contract for sending messages to devices via Firebase. * @see org.radarbase.fcm.downstream.AdminSdkFcmSender + * * @author yatharthranjan */ -public interface FcmSender { - - void send(FcmDownstreamMessage message) throws Exception; - - boolean doesProvideDeliveryReceipt(); -} +interface FcmSender { + @Throws(Exception::class) + fun send(downstreamMessage: FcmDownstreamMessage) + fun doesProvideDeliveryReceipt(): Boolean +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/fcm/model/FcmDataMessage.java b/src/main/java/org/radarbase/fcm/model/FcmDataMessage.java deleted file mode 100644 index 84e445b4..00000000 --- a/src/main/java/org/radarbase/fcm/model/FcmDataMessage.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * - * * - * * * Copyright 2018 King's College London - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * * - * * - * - */ - -package org.radarbase.fcm.model; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.Map; -import lombok.Getter; -import lombok.experimental.SuperBuilder; - -/** - * @author yatharthranjan - */ -@JsonInclude(JsonInclude.Include.NON_NULL) -@SuperBuilder -@Getter -public class FcmDataMessage extends FcmDownstreamMessage { - - @JsonProperty - private Map data; -} diff --git a/src/main/java/org/radarbase/fcm/model/FcmDataMessage.kt b/src/main/java/org/radarbase/fcm/model/FcmDataMessage.kt new file mode 100644 index 00000000..b2369aad --- /dev/null +++ b/src/main/java/org/radarbase/fcm/model/FcmDataMessage.kt @@ -0,0 +1,107 @@ +/* + * + * * + * * * Copyright 2018 King's College London + * * * + * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * you may not use this file except in compliance with the License. + * * * You may obtain a copy of the License at + * * * + * * * http://www.apache.org/licenses/LICENSE-2.0 + * * * + * * * Unless required by applicable law or agreed to in writing, software + * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * See the License for the specific language governing permissions and + * * * limitations under the License. + * * * + * * + * + */ +package org.radarbase.fcm.model + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonProperty +import com.google.firebase.messaging.AndroidConfig +import com.google.firebase.messaging.ApnsConfig +import com.google.firebase.messaging.Aps +import com.google.firebase.messaging.WebpushConfig +import java.time.Duration +import java.time.Instant +import javax.validation.constraints.NotEmpty + +/** + * @author yatharthranjan + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +class FcmDataMessage( + @JsonProperty + override val to: @NotEmpty String, + + @JsonProperty + override val condition: String? = null, + + @JsonProperty("message_id") + override val messageId: @NotEmpty String, + + @JsonProperty("collapse_key") + override val collapseKey: String? = null, + + @JsonProperty + override val priority: String? = null, + + @JsonProperty("content_available") + override val contentAvailable: Boolean? = null, + + @JsonProperty("mutable_content") + override val mutableContent: Boolean? = null, + + @JsonProperty("time_to_live") + override val timeToLive: Int = 2419200, // 4 weeks + + @JsonProperty("delivery_receipt_requested") + override val deliveryReceiptRequested: Boolean = false, + + @JsonProperty("dry_run") + override val dryRun: Boolean = false, + + @JsonProperty + override val data: Map? = null, +) : FcmDownstreamMessage { + + private val ttl = timeToLive * 1000L + + override fun getAndroidConfig(): AndroidConfig = AndroidConfig.builder().apply { + collapseKey?.let { setCollapseKey(collapseKey) } + setPriority(AndroidConfig.Priority.valueOf(priority ?: "HIGH")) + setTtl(ttl) + data?.let { putAllData(data) } + }.build() + + override fun getApnsConfig(): ApnsConfig { + val config = ApnsConfig.builder() + collapseKey?.let { + config.putHeader("apns-collapse-id", collapseKey) + } + + // The date at which the notification is no longer valid. This value is a UNIX epoch + // expressed in seconds (UTC). + return config.apply { + + putHeader( + "apns-expiration", ( + Instant.now() + .plus(Duration.ofSeconds(timeToLive.toLong())) + .toEpochMilli() / 1000).toString() + ) + data?.let { putAllCustomData(data) } + setAps(Aps.builder().setContentAvailable(true).setSound("default").build()) + putHeader("apns-push-type", "background") // No alert is shown + putHeader("apns-priority", "5") // 5 required in case of background type + }.build() + } + + override fun getWebPushConfig(): WebpushConfig { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/fcm/model/FcmDownstreamMessage.java b/src/main/java/org/radarbase/fcm/model/FcmDownstreamMessage.java deleted file mode 100644 index 440dfc1b..00000000 --- a/src/main/java/org/radarbase/fcm/model/FcmDownstreamMessage.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * - * * - * * * Copyright 2018 King's College London - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * * - * * - * - */ - -package org.radarbase.fcm.model; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import javax.validation.constraints.NotEmpty; -import lombok.Getter; -import lombok.experimental.SuperBuilder; - -/** @author yatharthranjan */ -@JsonInclude(JsonInclude.Include.NON_NULL) -@SuperBuilder -@Getter -public abstract class FcmDownstreamMessage implements FcmMessage { - - @JsonProperty @NotEmpty private String to; - - @JsonProperty private String condition; - - @NotEmpty - @JsonProperty("message_id") - private String messageId; - - @JsonProperty("collapse_key") - private String collapseKey; - - @JsonProperty private String priority; - - @JsonProperty("content_available") - private Boolean contentAvailable; - - @JsonProperty("mutable_content") - private Boolean mutableContent; - - @JsonProperty("time_to_live") - private Integer timeToLive; - - @JsonProperty("delivery_receipt_requested") - private Boolean deliveryReceiptRequested; - - @JsonProperty("dry_run") - private Boolean dryRun; -} diff --git a/src/main/java/org/radarbase/fcm/model/FcmDownstreamMessage.kt b/src/main/java/org/radarbase/fcm/model/FcmDownstreamMessage.kt new file mode 100644 index 00000000..278f8560 --- /dev/null +++ b/src/main/java/org/radarbase/fcm/model/FcmDownstreamMessage.kt @@ -0,0 +1,48 @@ +/* + * + * * + * * * Copyright 2018 King's College London + * * * + * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * you may not use this file except in compliance with the License. + * * * You may obtain a copy of the License at + * * * + * * * http://www.apache.org/licenses/LICENSE-2.0 + * * * + * * * Unless required by applicable law or agreed to in writing, software + * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * See the License for the specific language governing permissions and + * * * limitations under the License. + * * * + * * + * + */ +package org.radarbase.fcm.model + +import com.fasterxml.jackson.annotation.JsonInclude +import com.google.firebase.messaging.AndroidConfig +import com.google.firebase.messaging.ApnsConfig +import com.google.firebase.messaging.WebpushConfig +import javax.validation.constraints.NotEmpty + +/** @author yatharthranjan + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +interface FcmDownstreamMessage : FcmMessage { + val to: @NotEmpty String? + val condition: String? + val messageId: @NotEmpty String? + val collapseKey: String? + val priority: String? + val contentAvailable: Boolean? + val mutableContent: Boolean? + val timeToLive: Int + val deliveryReceiptRequested: Boolean + val dryRun: Boolean + val data: Map? + + fun getAndroidConfig(): AndroidConfig + fun getApnsConfig(): ApnsConfig + fun getWebPushConfig(): WebpushConfig +} \ No newline at end of file diff --git a/src/main/java/org/radarbase/fcm/model/FcmMessage.java b/src/main/java/org/radarbase/fcm/model/FcmMessage.kt similarity index 88% rename from src/main/java/org/radarbase/fcm/model/FcmMessage.java rename to src/main/java/org/radarbase/fcm/model/FcmMessage.kt index 3fa908a2..2105819f 100644 --- a/src/main/java/org/radarbase/fcm/model/FcmMessage.java +++ b/src/main/java/org/radarbase/fcm/model/FcmMessage.kt @@ -18,8 +18,8 @@ * * * */ +package org.radarbase.fcm.model -package org.radarbase.fcm.model; - -/** @author yatharthranjan */ -public interface FcmMessage {} +/** @author yatharthranjan + */ +interface FcmMessage \ No newline at end of file diff --git a/src/main/java/org/radarbase/fcm/model/FcmNotificationMessage.java b/src/main/java/org/radarbase/fcm/model/FcmNotificationMessage.java deleted file mode 100644 index 2be62b78..00000000 --- a/src/main/java/org/radarbase/fcm/model/FcmNotificationMessage.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * - * * - * * * Copyright 2018 King's College London - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * * - * * - * - */ - -package org.radarbase.fcm.model; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.Map; -import lombok.Getter; -import lombok.experimental.SuperBuilder; - -/** @author yatharthranjan */ -@JsonInclude(JsonInclude.Include.NON_NULL) -@SuperBuilder -@Getter -public class FcmNotificationMessage extends FcmDownstreamMessage { - - // TODO Add specific Notification model and data model classes instead of using Maps. - - @JsonProperty private Map notification; - - @JsonProperty private Map data; -} diff --git a/src/main/java/org/radarbase/fcm/model/FcmNotificationMessage.kt b/src/main/java/org/radarbase/fcm/model/FcmNotificationMessage.kt new file mode 100644 index 00000000..3978759f --- /dev/null +++ b/src/main/java/org/radarbase/fcm/model/FcmNotificationMessage.kt @@ -0,0 +1,179 @@ +/* + * + * * + * * * Copyright 2018 King's College London + * * * + * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * you may not use this file except in compliance with the License. + * * * You may obtain a copy of the License at + * * * + * * * http://www.apache.org/licenses/LICENSE-2.0 + * * * + * * * Unless required by applicable law or agreed to in writing, software + * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * See the License for the specific language governing permissions and + * * * limitations under the License. + * * * + * * + * + */ +package org.radarbase.fcm.model + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonProperty +import com.google.firebase.messaging.* +import java.time.Duration +import java.time.Instant +import javax.validation.constraints.NotEmpty + +/** @author yatharthranjan + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +class FcmNotificationMessage( + @JsonProperty + override val to: @NotEmpty String, + + @JsonProperty + override val condition: String? = null, + + @JsonProperty("message_id") + override val messageId: @NotEmpty String, + + @JsonProperty("collapse_key") + override val collapseKey: String? = null, + + @JsonProperty + override val priority: String? = null, + + @JsonProperty("content_available") + override val contentAvailable: Boolean? = null, + + @JsonProperty("mutable_content") + override val mutableContent: Boolean? = null, + + @JsonProperty("time_to_live") + override val timeToLive: Int = 2419200, // 4 weeks + + @JsonProperty("delivery_receipt_requested") + override val deliveryReceiptRequested: Boolean = false, + + @JsonProperty("dry_run") + override val dryRun: Boolean = false, + + @JsonProperty + override val data: Map? = null, + + // TODO Add specific Notification model and data model classes instead of using Maps. + @JsonProperty + val notification: Map, + + ) : FcmDownstreamMessage { + override fun getAndroidConfig(): AndroidConfig = AndroidConfig.builder() + .setCollapseKey(collapseKey) + .setPriority(AndroidConfig.Priority.valueOf(priority ?: "HIGH")) + .setTtl((timeToLive * 1000).toLong()) + .setNotification(getAndroidNotification()) + .putAllData(data) + .build() + + override fun getApnsConfig(): ApnsConfig { + val config = ApnsConfig.builder() + collapseKey?.let { + config.putHeader("apns-collapse-id", collapseKey) + } + + // The date at which the notification is no longer valid. This value is a UNIX epoch + // expressed in seconds (UTC). + config.putHeader( + "apns-expiration", ( + Instant.now() + .plus(Duration.ofSeconds(timeToLive.toLong())) + .toEpochMilli() / 1000).toString() + ) + + val apsAlertBuilder = ApsAlert.builder() + notification["title"]?.let { apsAlertBuilder.setTitle(it.toString()) } + notification["body"]?.let { apsAlertBuilder.setBody(it.toString()) } + notification["title_loc_key"]?.let { apsAlertBuilder.setTitleLocalizationKey(it.toString()) } + notification["title_loc_key"]?.let { + notification["title_loc_args"]?.let { + apsAlertBuilder.addTitleLocalizationArg( + it.toString() + ) + } + } + notification["body_loc_key"]?.let { apsAlertBuilder.setLocalizationKey(it.toString()) } + notification["body_loc_key"]?.let { + notification["body_loc_args"]?.let { + apsAlertBuilder + .addLocalizationArg( + it + .toString() + ) + } + } + + val apsBuilder = Aps.builder() + notification["sound"]?.let { apsBuilder.setSound(it.toString()) } + notification["badge"]?.let { apsBuilder.setBadge(it.toString().toInt()) } + + notification["category"]?.let { apsBuilder.setCategory(it.toString()) } + notification["thread_id"]?.let { apsBuilder.setThreadId(it.toString()) } + + contentAvailable?.let { + apsBuilder.setContentAvailable(contentAvailable) + } + mutableContent?.let { + apsBuilder.setMutableContent(mutableContent) + } + return config + .putAllCustomData(data) + .setAps(apsBuilder.setAlert(apsAlertBuilder.build()).build()) + .putHeader("apns-push-type", "alert") + .build() + + + } + + override fun getWebPushConfig(): WebpushConfig { + TODO("Not yet implemented") + } + + private fun getAndroidNotification(): AndroidNotification { + return AndroidNotification.builder().apply { + setBody(notification.getOrDefault("body", "").toString()) + setTitle(notification.getOrDefault("title", "").toString()) + notification["android_channel_id"]?.let { + setChannelId(it.toString()) + } + notification["color"]?.let { + setColor(it.toString()) + } + notification["icon"]?.let { + setIcon(it.toString()) + } + notification["image"]?.let { + setImage(it.toString()) + } + notification["tag"]?.let { + setTag(it.toString()) + } + notification["click_action"]?.let { + setClickAction(it.toString()) + } + notification["body_loc_key"]?.let { + setBodyLocalizationKey(it.toString()) + addBodyLocalizationArg(notification["body_loc_args"].toString()) + } + + notification["title_loc_key"]?.let { + setTitleLocalizationKey(it.toString()) + addTitleLocalizationArg(notification["title_loc_args"].toString()) + } + notification["sound"]?.let { + setSound(it.toString()) + } + }.build() + } +} \ No newline at end of file diff --git a/src/test/java/org/radarbase/appserver/repository/DataMessageRepositoryTest.java b/src/test/java/org/radarbase/appserver/repository/DataMessageRepositoryTest.java deleted file mode 100644 index 491a3bdb..00000000 --- a/src/test/java/org/radarbase/appserver/repository/DataMessageRepositoryTest.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * - * * - * * * Copyright 2018 King's College London - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * * - * * - * - */ - -package org.radarbase.appserver.repository; - -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.radarbase.appserver.controller.RadarUserControllerTest.TIMEZONE; - -import java.time.Duration; -import java.time.Instant; -import javax.persistence.PersistenceException; -import javax.validation.ConstraintViolationException; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.radarbase.appserver.entity.DataMessage; -import org.radarbase.appserver.entity.Project; -import org.radarbase.appserver.entity.User; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; -import org.springframework.data.jpa.repository.config.EnableJpaAuditing; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -@ExtendWith(SpringExtension.class) -@DataJpaTest -@EnableJpaAuditing -public class DataMessageRepositoryTest { - public static final String DATA_MESSAGE_FCM_MESSAGE_ID = "12345"; - public static final String DATA_MESSAGE_SOURCE_ID = "test"; - @Autowired - private transient TestEntityManager entityManager; - @Autowired - private transient DataMessageRepository dataMessageRepository; - private transient Long id; - private transient User user; - private transient Instant scheduledTime; - - /** - * Insert a DataMessage Before each test. - */ - @BeforeEach - public void initDataMessage() { - // given - Project project = new Project().setProjectId("test-project"); - entityManager.persist(project); - - this.user = - new User() - .setFcmToken("xxxx") - .setEnrolmentDate(Instant.now()) - .setProject(project) - .setTimezone(TIMEZONE) - .setLanguage("en") - .setSubjectId("test-user"); - entityManager.persist(this.user); - - this.scheduledTime = Instant.now().plus(Duration.ofSeconds(100)); - - DataMessage dataMessage = - new DataMessage.DataMessageBuilder() - .user(user) - .fcmMessageId(DATA_MESSAGE_FCM_MESSAGE_ID) - .scheduledTime(this.scheduledTime) - .sourceId(DATA_MESSAGE_SOURCE_ID) - .ttlSeconds(86400) - .delivered(false) - .build(); - - this.id = (Long) entityManager.persistAndGetId(dataMessage); - entityManager.flush(); - } - - @Test - public void whenInsertWithTransientUser_thenThrowException() { - // given - DataMessage dataMessage = - new DataMessage.DataMessageBuilder() - .user(new User()) - .fcmMessageId(DATA_MESSAGE_FCM_MESSAGE_ID) - .scheduledTime(Instant.now().plus(Duration.ofSeconds(100))) - .sourceId(DATA_MESSAGE_SOURCE_ID) - .ttlSeconds(86400) - .delivered(false) - .build(); - - IllegalStateException ex = - assertThrows( - IllegalStateException.class, - () -> { - entityManager.persist(dataMessage); - entityManager.flush(); - }); - - assertTrue(ex.getMessage().contains("Not-null property references a transient value")); - } - - @Test - public void whenInsertWithoutUser_thenThrowException() { - // given - DataMessage dataMessage = - new DataMessage.DataMessageBuilder() - .fcmMessageId(DATA_MESSAGE_FCM_MESSAGE_ID) - .scheduledTime(Instant.now().plus(Duration.ofSeconds(100))) - .sourceId(DATA_MESSAGE_SOURCE_ID) - .ttlSeconds(86400) - .delivered(false) - .build(); - - assertThrows( - PersistenceException.class, - () -> { - entityManager.persist(dataMessage); - entityManager.flush(); - }); - } - - @Test - public void whenInsertWithUserButTransientProject_thenThrowException() { - // given - User user = - new User() - .setFcmToken("xxxx") - .setEnrolmentDate(Instant.now()) - .setProject(new Project()) - .setTimezone(TIMEZONE) - .setLanguage("en") - .setSubjectId("test-user"); - - IllegalStateException ex = - assertThrows( - IllegalStateException.class, - () -> { - entityManager.persist(user); - entityManager.flush(); - }); - - assertTrue(ex.getMessage().contains("Not-null property references a transient value")); - - DataMessage dataMessage = - new DataMessage.DataMessageBuilder() - .user(user) - .fcmMessageId(DATA_MESSAGE_FCM_MESSAGE_ID) - .scheduledTime(Instant.now().plus(Duration.ofSeconds(100))) - .sourceId(DATA_MESSAGE_SOURCE_ID) - .ttlSeconds(86400) - .delivered(false) - .build(); - - ex = - assertThrows( - IllegalStateException.class, - () -> { - entityManager.persist(dataMessage); - entityManager.flush(); - }); - - assertTrue(ex.getMessage().contains("Not-null property references a transient value")); - } - - @Test - public void whenExists_thenReturnTrue() { - // when - boolean exists = - dataMessageRepository - .existsByUserIdAndSourceIdAndScheduledTimeAndTtlSeconds( - this.user.getId(), - DATA_MESSAGE_SOURCE_ID, - this.scheduledTime, - 86400); - - // then - assertTrue(exists); - assertTrue(dataMessageRepository.existsById(this.id)); - } - - @Test - public void whenDeleteDataMessageById_thenExistsFalse() { - // when - dataMessageRepository.deleteById(this.id); - - // then - DataMessage dataMessage = entityManager.find(DataMessage.class, this.id); - assertNull(dataMessage); - } - - @Test - public void whenDeleteDataMessageByUserId_thenExistsFalse() { - // when - dataMessageRepository.deleteByUserId(this.user.getId()); - - // then - DataMessage dataMessage = entityManager.find(DataMessage.class, this.id); - assertNull(dataMessage); - } -} diff --git a/src/test/java/org/radarbase/appserver/repository/DataMessageRepositoryTest.kt b/src/test/java/org/radarbase/appserver/repository/DataMessageRepositoryTest.kt new file mode 100644 index 00000000..0a9a4727 --- /dev/null +++ b/src/test/java/org/radarbase/appserver/repository/DataMessageRepositoryTest.kt @@ -0,0 +1,200 @@ +/* + * + * * + * * * Copyright 2018 King's College London + * * * + * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * you may not use this file except in compliance with the License. + * * * You may obtain a copy of the License at + * * * + * * * http://www.apache.org/licenses/LICENSE-2.0 + * * * + * * * Unless required by applicable law or agreed to in writing, software + * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * See the License for the specific language governing permissions and + * * * limitations under the License. + * * * + * * + * + */ +package org.radarbase.appserver.repository + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.radarbase.appserver.controller.RadarUserControllerTest +import org.radarbase.appserver.entity.DataMessage +import org.radarbase.appserver.entity.Project +import org.radarbase.appserver.entity.User +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager +import org.springframework.data.jpa.repository.config.EnableJpaAuditing +import org.springframework.data.jpa.repository.config.EnableJpaRepositories +import org.springframework.test.context.junit.jupiter.SpringExtension +import java.time.Duration +import java.time.Instant +import javax.persistence.PersistenceException + +@ExtendWith(SpringExtension::class) +@DataJpaTest +@EnableJpaAuditing +@EnableJpaRepositories("org.radarbase.appserver.repository") +class DataMessageRepositoryTest { + @Autowired + @Transient + lateinit var entityManager: TestEntityManager + + @Autowired + @Transient + lateinit var dataMessageRepository: DataMessageRepository + + @Transient + private var id: Long? = null + + @Transient + private lateinit var user: User + + @Transient + private lateinit var scheduledTime: Instant + + /** + * Insert a DataMessage Before each test. + */ + @BeforeEach + fun initDataMessage() { + // given + val project = Project().setProjectId("test-project") + entityManager.persist(project) + user = User() + .setFcmToken("xxxx") + .setEnrolmentDate(Instant.now()) + .setProject(project) + .setTimezone(RadarUserControllerTest.TIMEZONE) + .setLanguage("en") + .setSubjectId("test-user") + entityManager.persist(user) + scheduledTime = Instant.now().plus(Duration.ofSeconds(100)) + val dataMessage = DataMessage() + dataMessage.fcmMessageId = DATA_MESSAGE_FCM_MESSAGE_ID + dataMessage.sourceId = DATA_MESSAGE_SOURCE_ID + dataMessage.scheduledTime = scheduledTime + dataMessage.user = user + dataMessage.ttlSeconds = 86400 + dataMessage.delivered = false + id = entityManager.persistAndGetId(dataMessage) as Long + entityManager.flush() + } + + @Test + fun whenInsertWithTransientUser_thenThrowException() { + // given + val dataMessage = DataMessage() + dataMessage.user = User() + dataMessage.sourceId = DATA_MESSAGE_SOURCE_ID + dataMessage.scheduledTime = Instant.now().plus(Duration.ofSeconds(100)) + dataMessage.ttlSeconds = 86400 + dataMessage.delivered = false + dataMessage.fcmMessageId = DATA_MESSAGE_FCM_MESSAGE_ID + val ex = Assertions.assertThrows( + IllegalStateException::class.java + ) { + entityManager.persist(dataMessage) + entityManager.flush() + } + Assertions.assertTrue(ex.message!!.contains("Not-null property references a transient value")) + } + + @Test + fun whenInsertWithoutUser_thenThrowException() { + // given + val dataMessage = DataMessage() + dataMessage.sourceId = DATA_MESSAGE_SOURCE_ID + dataMessage.scheduledTime = Instant.now().plus(Duration.ofSeconds(100)) + dataMessage.ttlSeconds = 86400 + dataMessage.delivered = false + dataMessage.fcmMessageId = DATA_MESSAGE_FCM_MESSAGE_ID + Assertions.assertThrows( + PersistenceException::class.java + ) { + entityManager.persist(dataMessage) + entityManager.flush() + } + } + + @Test + fun whenInsertWithUserButTransientProject_thenThrowException() { + // given + val user = User() + .setFcmToken("xxxx") + .setEnrolmentDate(Instant.now()) + .setProject(Project()) + .setTimezone(RadarUserControllerTest.TIMEZONE) + .setLanguage("en") + .setSubjectId("test-user") + var ex = Assertions.assertThrows( + IllegalStateException::class.java + ) { + entityManager.persist(user) + entityManager.flush() + } + Assertions.assertTrue(ex.message!!.contains("Not-null property references a transient value")) + val dataMessage = DataMessage() + dataMessage.user = user + dataMessage.sourceId = DATA_MESSAGE_SOURCE_ID + dataMessage.scheduledTime = Instant.now().plus(Duration.ofSeconds(100)) + dataMessage.ttlSeconds = 86400 + dataMessage.delivered = false + dataMessage.fcmMessageId = DATA_MESSAGE_FCM_MESSAGE_ID + ex = Assertions.assertThrows( + IllegalStateException::class.java + ) { + entityManager.persist(dataMessage) + entityManager.flush() + } + Assertions.assertTrue(ex.message!!.contains("Not-null property references a transient value")) + } + + @Test + fun whenExists_thenReturnTrue() { + // when + val exists = dataMessageRepository + .existsByUserIdAndSourceIdAndScheduledTimeAndTtlSeconds( + user.id, + DATA_MESSAGE_SOURCE_ID, + scheduledTime, + 86400 + ) + + // then + Assertions.assertTrue(exists) + Assertions.assertTrue(dataMessageRepository.existsById(id)) + } + + @Test + fun whenDeleteDataMessageById_thenExistsFalse() { + // when + dataMessageRepository.deleteById(id) + + // then + val dataMessage = entityManager.find(DataMessage::class.java, id) + Assertions.assertNull(dataMessage) + } + + @Test + fun whenDeleteDataMessageByUserId_thenExistsFalse() { + // when + dataMessageRepository.deleteByUserId(user.id) + + // then + val dataMessage = entityManager.find(DataMessage::class.java, id) + Assertions.assertNull(dataMessage) + } + + companion object { + const val DATA_MESSAGE_FCM_MESSAGE_ID = "12345" + const val DATA_MESSAGE_SOURCE_ID = "test" + } +} \ No newline at end of file diff --git a/src/test/java/org/radarbase/appserver/repository/NotificationRepositoryTest.java b/src/test/java/org/radarbase/appserver/repository/NotificationRepositoryTest.java deleted file mode 100644 index 7d7d7ce0..00000000 --- a/src/test/java/org/radarbase/appserver/repository/NotificationRepositoryTest.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * - * * - * * * Copyright 2018 King's College London - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * * - * * - * - */ - -package org.radarbase.appserver.repository; - -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.radarbase.appserver.controller.RadarUserControllerTest.TIMEZONE; - -import java.time.Duration; -import java.time.Instant; -import javax.persistence.PersistenceException; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.radarbase.appserver.entity.Notification; -import org.radarbase.appserver.entity.Project; -import org.radarbase.appserver.entity.User; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; -import org.springframework.data.jpa.repository.config.EnableJpaAuditing; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -@ExtendWith(SpringExtension.class) -@DataJpaTest -@EnableJpaAuditing -public class NotificationRepositoryTest { - public static final String NOTIFICATION_BODY = "Test notif"; - public static final String NOTIFICATION_TITLE = "Testing"; - public static final String NOTIFICATION_FCM_MESSAGE_ID = "12345"; - public static final String NOTIFICATION_SOURCE_ID = "test"; - @Autowired - private transient TestEntityManager entityManager; - @Autowired - private transient NotificationRepository notificationRepository; - private transient Long id; - private transient User user; - private transient Instant scheduledTime; - - /** - * Insert a Notification Before each test. - */ - @BeforeEach - public void initNotification() { - // given - Project project = new Project().setProjectId("test-project"); - entityManager.persist(project); - - this.user = - new User() - .setFcmToken("xxxx") - .setEnrolmentDate(Instant.now()) - .setProject(project) - .setTimezone(TIMEZONE) - .setLanguage("en") - .setSubjectId("test-user"); - entityManager.persist(this.user); - - this.scheduledTime = Instant.now().plus(Duration.ofSeconds(100)); - - Notification notification = - new Notification.NotificationBuilder() - .user(user) - .body(NOTIFICATION_BODY) - .title(NOTIFICATION_TITLE) - .fcmMessageId(NOTIFICATION_FCM_MESSAGE_ID) - .scheduledTime(this.scheduledTime) - .sourceId(NOTIFICATION_SOURCE_ID) - .ttlSeconds(86400) - .delivered(false) - .build(); - - this.id = (Long) entityManager.persistAndGetId(notification); - entityManager.flush(); - } - - @Test - public void whenInsertWithTransientUser_thenThrowException() { - // given - Notification notification = - new Notification.NotificationBuilder() - .user(new User()) - .body(NOTIFICATION_BODY) - .title(NOTIFICATION_TITLE) - .fcmMessageId(NOTIFICATION_FCM_MESSAGE_ID) - .scheduledTime(Instant.now().plus(Duration.ofSeconds(100))) - .sourceId(NOTIFICATION_SOURCE_ID) - .ttlSeconds(86400) - .delivered(false) - .build(); - - IllegalStateException ex = - assertThrows( - IllegalStateException.class, - () -> { - entityManager.persist(notification); - entityManager.flush(); - }); - - assertTrue(ex.getMessage().contains("Not-null property references a transient value")); - } - - @Test - public void whenInsertWithoutUser_thenThrowException() { - // given - Notification notification = - new Notification.NotificationBuilder() - .body(NOTIFICATION_BODY) - .title(NOTIFICATION_TITLE) - .fcmMessageId(NOTIFICATION_FCM_MESSAGE_ID) - .scheduledTime(Instant.now().plus(Duration.ofSeconds(100))) - .sourceId(NOTIFICATION_SOURCE_ID) - .ttlSeconds(86400) - .delivered(false) - .build(); - - assertThrows( - PersistenceException.class, - () -> { - entityManager.persist(notification); - entityManager.flush(); - }); - } - - @Test - public void whenInsertWithUserButTransientProject_thenThrowException() { - // given - User user = - new User() - .setFcmToken("xxxx") - .setEnrolmentDate(Instant.now()) - .setProject(new Project()) - .setTimezone(TIMEZONE) - .setLanguage("en") - .setSubjectId("test-user"); - - IllegalStateException ex = - assertThrows( - IllegalStateException.class, - () -> { - entityManager.persist(user); - entityManager.flush(); - }); - - assertTrue(ex.getMessage().contains("Not-null property references a transient value")); - - Notification notification = - new Notification.NotificationBuilder() - .user(user) - .body(NOTIFICATION_BODY) - .title(NOTIFICATION_TITLE) - .fcmMessageId(NOTIFICATION_FCM_MESSAGE_ID) - .scheduledTime(Instant.now().plus(Duration.ofSeconds(100))) - .sourceId(NOTIFICATION_SOURCE_ID) - .ttlSeconds(86400) - .delivered(false) - .build(); - - ex = - assertThrows( - IllegalStateException.class, - () -> { - entityManager.persist(notification); - entityManager.flush(); - }); - - assertTrue(ex.getMessage().contains("Not-null property references a transient value")); - } - - @Test - public void whenExists_thenReturnTrue() { - // when - boolean exists = - notificationRepository - .existsByUserIdAndSourceIdAndScheduledTimeAndTitleAndBodyAndTypeAndTtlSeconds( - this.user.getId(), - NOTIFICATION_SOURCE_ID, - this.scheduledTime, - NOTIFICATION_TITLE, - NOTIFICATION_BODY, - null, - 86400); - - // then - assertTrue(exists); - assertTrue(notificationRepository.existsById(this.id)); - } - - @Test - public void whenDeleteNotificationById_thenExistsFalse() { - // when - notificationRepository.deleteById(this.id); - - // then - Notification notification = entityManager.find(Notification.class, this.id); - assertNull(notification); - } - - @Test - public void whenDeleteNotificationByUserId_thenExistsFalse() { - // when - notificationRepository.deleteByUserId(this.user.getId()); - - // then - Notification notification = entityManager.find(Notification.class, this.id); - assertNull(notification); - } -} diff --git a/src/test/java/org/radarbase/appserver/repository/NotificationRepositoryTest.kt b/src/test/java/org/radarbase/appserver/repository/NotificationRepositoryTest.kt new file mode 100644 index 00000000..2f0cd60a --- /dev/null +++ b/src/test/java/org/radarbase/appserver/repository/NotificationRepositoryTest.kt @@ -0,0 +1,217 @@ +/* + * + * * + * * * Copyright 2018 King's College London + * * * + * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * you may not use this file except in compliance with the License. + * * * You may obtain a copy of the License at + * * * + * * * http://www.apache.org/licenses/LICENSE-2.0 + * * * + * * * Unless required by applicable law or agreed to in writing, software + * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * See the License for the specific language governing permissions and + * * * limitations under the License. + * * * + * * + * + */ +package org.radarbase.appserver.repository + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.radarbase.appserver.controller.RadarUserControllerTest +import org.radarbase.appserver.entity.Notification +import org.radarbase.appserver.entity.Project +import org.radarbase.appserver.entity.User +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager +import org.springframework.data.jpa.repository.config.EnableJpaAuditing +import org.springframework.data.jpa.repository.config.EnableJpaRepositories +import org.springframework.test.context.junit.jupiter.SpringExtension +import java.time.Duration +import java.time.Instant +import javax.persistence.PersistenceException + +@ExtendWith(SpringExtension::class) +@DataJpaTest +@EnableJpaAuditing +@EnableJpaRepositories("org.radarbase.appserver.repository") +class NotificationRepositoryTest { + @Autowired + @Transient + lateinit var entityManager: TestEntityManager + + @Autowired + @Transient + lateinit var notificationRepository: NotificationRepository + + @Transient + private var id: Long? = null + + @Transient + private lateinit var user: User + + @Transient + private lateinit var scheduledTime: Instant + + /** + * Insert a Notification Before each test. + */ + @BeforeEach + fun initNotification() { + // given + val project = Project().setProjectId("test-project") + entityManager.persist(project) + user = User() + .setFcmToken("xxxx") + .setEnrolmentDate(Instant.now()) + .setProject(project) + .setTimezone(RadarUserControllerTest.TIMEZONE) + .setLanguage("en") + .setSubjectId("test-user") + entityManager.persist(user) + scheduledTime = Instant.now().plus(Duration.ofSeconds(100)) + val notification = Notification() + notification.body = NOTIFICATION_BODY + notification.title = NOTIFICATION_TITLE + notification.fcmMessageId = NOTIFICATION_FCM_MESSAGE_ID + notification.sourceId = NOTIFICATION_SOURCE_ID + notification.scheduledTime = scheduledTime + notification.user = user + notification.ttlSeconds = 86400 + notification.delivered = false + id = entityManager.persistAndGetId(notification) as Long + entityManager.flush() + } + + @Test + fun whenInsertWithTransientUser_thenThrowException() { + // given + val notification = Notification() + notification.body = NOTIFICATION_BODY + notification.title = NOTIFICATION_TITLE + notification.fcmMessageId = NOTIFICATION_FCM_MESSAGE_ID + notification.sourceId = NOTIFICATION_SOURCE_ID + notification.scheduledTime = Instant.now().plus(Duration.ofSeconds(100)) + notification.user = User() + notification.ttlSeconds = 86400 + notification.delivered = false + val ex = Assertions.assertThrows( + IllegalStateException::class.java + ) { + entityManager.persist(notification) + entityManager.flush() + } + Assertions.assertTrue(ex.message!!.contains("Not-null property references a transient value")) + } + + @Test + fun whenInsertWithoutUser_thenThrowException() { + // given + val notification = Notification() + notification.body = NOTIFICATION_BODY + notification.title = NOTIFICATION_TITLE + notification.fcmMessageId = NOTIFICATION_FCM_MESSAGE_ID + notification.sourceId = NOTIFICATION_SOURCE_ID + notification.scheduledTime = Instant.now().plus(Duration.ofSeconds(100)) + notification.ttlSeconds = 86400 + notification.delivered = false + Assertions.assertThrows( + PersistenceException::class.java + ) { + entityManager.persist(notification) + entityManager.flush() + } + } + + @Test + fun whenInsertWithUserButTransientProject_thenThrowException() { + // given + val user = User() + .setFcmToken("xxxx") + .setEnrolmentDate(Instant.now()) + .setProject(Project()) + .setTimezone(RadarUserControllerTest.TIMEZONE) + .setLanguage("en") + .setSubjectId("test-user") + var ex = Assertions.assertThrows( + IllegalStateException::class.java + ) { + entityManager.persist(user) + entityManager.flush() + } + Assertions.assertTrue(ex.message!!.contains("Not-null property references a transient value")) + val notification = Notification() + notification.body = NOTIFICATION_BODY + notification.title = NOTIFICATION_TITLE + notification.fcmMessageId = NOTIFICATION_FCM_MESSAGE_ID + notification.sourceId = NOTIFICATION_SOURCE_ID + notification.scheduledTime = Instant.now().plus(Duration.ofSeconds(100)) + notification.user = user + notification.ttlSeconds = 86400 + notification.delivered = false + ex = Assertions.assertThrows( + IllegalStateException::class.java + ) { + entityManager.persist(notification) + entityManager.flush() + } + Assertions.assertTrue(ex.message!!.contains("Not-null property references a transient value")) + } + + @Test + fun whenExists_thenReturnTrue() { + // when + val exists = notificationRepository + .existsByUserIdAndSourceIdAndScheduledTimeAndTitleAndBodyAndTypeAndTtlSeconds( + user.id, + NOTIFICATION_SOURCE_ID, + scheduledTime, + NOTIFICATION_TITLE, + NOTIFICATION_BODY, + null, + 86400 + ) + + // then + Assertions.assertTrue(exists) + Assertions.assertTrue(notificationRepository.existsById(id)) + } + + @Test + fun whenDeleteNotificationById_thenExistsFalse() { + // when + notificationRepository.deleteById(id) + + // then + val notification = entityManager.find( + Notification::class.java, id + ) + Assertions.assertNull(notification) + } + + @Test + fun whenDeleteNotificationByUserId_thenExistsFalse() { + // when + notificationRepository.deleteByUserId(user.id) + + // then + val notification = entityManager.find( + Notification::class.java, id + ) + Assertions.assertNull(notification) + } + + companion object { + const val NOTIFICATION_BODY = "Test notif" + const val NOTIFICATION_TITLE = "Testing" + const val NOTIFICATION_FCM_MESSAGE_ID = "12345" + const val NOTIFICATION_SOURCE_ID = "test" + } +} \ No newline at end of file diff --git a/src/test/java/org/radarbase/appserver/repository/UserRepositoryTest.java b/src/test/java/org/radarbase/appserver/repository/UserRepositoryTest.java deleted file mode 100644 index 40ce7f27..00000000 --- a/src/test/java/org/radarbase/appserver/repository/UserRepositoryTest.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * - * * - * * * Copyright 2018 King's College London - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * * - * * - * - */ - -package org.radarbase.appserver.repository; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.radarbase.appserver.controller.FcmNotificationControllerTest.USER_ID; -import static org.radarbase.appserver.controller.RadarUserControllerTest.FCM_TOKEN_1; -import static org.radarbase.appserver.controller.RadarUserControllerTest.TIMEZONE; - -import java.time.Instant; -import javax.persistence.PersistenceException; - -import org.hibernate.exception.ConstraintViolationException; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.radarbase.appserver.entity.Project; -import org.radarbase.appserver.entity.User; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; -import org.springframework.data.jpa.repository.config.EnableJpaAuditing; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -@ExtendWith(SpringExtension.class) -@DataJpaTest -@EnableJpaAuditing -class UserRepositoryTest { - - @Autowired - private transient TestEntityManager entityManager; - - @Autowired - private transient UserRepository userRepository; - - private transient Project project; - private transient Long projectId; - private transient Long userId; - - @BeforeEach - void setUp() { - - this.project = new Project().setProjectId("test-project"); - this.projectId = entityManager.persistAndGetId(project, Long.class); - - User user = - new User() - .setFcmToken(FCM_TOKEN_1) - .setEnrolmentDate(Instant.now()) - .setProject(project) - .setTimezone(TIMEZONE) - .setLanguage("en") - .setSubjectId(USER_ID); - this.userId = entityManager.persistAndGetId(user, Long.class); - entityManager.flush(); - } - - @Test - public void whenInsertWithTransientProject_thenThrowException() { - User user1 = - new User() - .setFcmToken(FCM_TOKEN_1) - .setEnrolmentDate(Instant.now()) - .setProject(new Project()) - .setTimezone(TIMEZONE) - .setLanguage("en") - .setSubjectId(USER_ID); - - IllegalStateException ex = - assertThrows( - IllegalStateException.class, - () -> { - entityManager.persist(user1); - entityManager.flush(); - }); - - assertTrue(ex.getMessage().contains("Not-null property references a transient value")); - } - - @Test - public void whenFindUserBySubjectId_thenReturnUser() { - - assertEquals( - userRepository.findBySubjectId(USER_ID).get(), entityManager.find(User.class, this.userId)); - } - - @Test - public void whenFindByProjectId_thenReturnUsers() { - assertEquals( - userRepository.findByProjectId(this.projectId).get(0), - entityManager.find(User.class, this.userId)); - } - - @Test - public void whenFindBySubjectIdAndProjectId_thenReturnUser() { - assertEquals( - userRepository.findBySubjectIdAndProjectId(USER_ID, this.projectId).get(), - entityManager.find(User.class, this.userId)); - } - - @Test - public void whenFindByFcmToken_thenReturnUser() { - assertEquals( - userRepository.findByFcmToken(FCM_TOKEN_1).get(), - entityManager.find(User.class, this.userId)); - } - - @Test - public void whenInsertWithExistingFcmToken_thenThrowException() { - User user1 = - new User() - .setFcmToken(FCM_TOKEN_1) - .setEnrolmentDate(Instant.now()) - .setProject(this.project) - .setTimezone(TIMEZONE) - .setLanguage("en") - .setSubjectId(USER_ID + "-2"); - - PersistenceException ex = - assertThrows( - PersistenceException.class, - () -> { - entityManager.persistAndGetId(user1, Long.class); - entityManager.flush(); - }); - - assertEquals(ConstraintViolationException.class, ex.getCause().getClass()); - } -} diff --git a/src/test/java/org/radarbase/appserver/repository/UserRepositoryTest.kt b/src/test/java/org/radarbase/appserver/repository/UserRepositoryTest.kt new file mode 100644 index 00000000..e180ae4d --- /dev/null +++ b/src/test/java/org/radarbase/appserver/repository/UserRepositoryTest.kt @@ -0,0 +1,148 @@ +/* + * + * * + * * * Copyright 2018 King's College London + * * * + * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * you may not use this file except in compliance with the License. + * * * You may obtain a copy of the License at + * * * + * * * http://www.apache.org/licenses/LICENSE-2.0 + * * * + * * * Unless required by applicable law or agreed to in writing, software + * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * See the License for the specific language governing permissions and + * * * limitations under the License. + * * * + * * + * + */ +package org.radarbase.appserver.repository + +import org.hibernate.exception.ConstraintViolationException +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.radarbase.appserver.controller.FcmNotificationControllerTest +import org.radarbase.appserver.controller.RadarUserControllerTest +import org.radarbase.appserver.entity.Project +import org.radarbase.appserver.entity.User +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager +import org.springframework.data.jpa.repository.config.EnableJpaAuditing +import org.springframework.test.context.junit.jupiter.SpringExtension +import java.time.Instant +import javax.persistence.PersistenceException + +@ExtendWith(SpringExtension::class) +@DataJpaTest +@EnableJpaAuditing +internal class UserRepositoryTest { + @Autowired + @Transient + lateinit var entityManager: TestEntityManager + + @Autowired + @Transient + lateinit var userRepository: UserRepository + + @Transient + private var project: Project? = null + + @Transient + private var projectId: Long? = null + + @Transient + private var userId: Long? = null + + @BeforeEach + fun setUp() { + project = Project().setProjectId("test-project") + projectId = entityManager.persistAndGetId(project) as Long + val user = User() + .setFcmToken(RadarUserControllerTest.FCM_TOKEN_1) + .setEnrolmentDate(Instant.now()) + .setProject(project) + .setTimezone(RadarUserControllerTest.TIMEZONE) + .setLanguage("en") + .setSubjectId(FcmNotificationControllerTest.USER_ID) + userId = entityManager.persistAndGetId(user) as Long + entityManager.flush() + } + + @Test + fun whenInsertWithTransientProject_thenThrowException() { + val user1 = User() + .setFcmToken(RadarUserControllerTest.FCM_TOKEN_1) + .setEnrolmentDate(Instant.now()) + .setProject(Project()) + .setTimezone(RadarUserControllerTest.TIMEZONE) + .setLanguage("en") + .setSubjectId(FcmNotificationControllerTest.USER_ID) + val ex = Assertions.assertThrows( + IllegalStateException::class.java + ) { + entityManager.persist(user1) + entityManager.flush() + } + Assertions.assertTrue(ex.message!!.contains("Not-null property references a transient value")) + } + + @Test + fun whenFindUserBySubjectId_thenReturnUser() { + Assertions.assertEquals( + userRepository.findBySubjectId(FcmNotificationControllerTest.USER_ID)!!.get(), + entityManager.find( + User::class.java, userId + ) + ) + } + + @Test + fun whenFindByProjectId_thenReturnUsers() { + Assertions.assertEquals( + userRepository.findByProjectId(projectId)!![0], + entityManager.find(User::class.java, userId) + ) + } + + @Test + fun whenFindBySubjectIdAndProjectId_thenReturnUser() { + Assertions.assertEquals( + userRepository.findBySubjectIdAndProjectId( + FcmNotificationControllerTest.USER_ID, + projectId + )!!.get(), + entityManager.find(User::class.java, userId) + ) + } + + @Test + fun whenFindByFcmToken_thenReturnUser() { + Assertions.assertEquals( + userRepository.findByFcmToken(RadarUserControllerTest.FCM_TOKEN_1)!!.get(), + entityManager.find(User::class.java, userId) + ) + } + + @Test + fun whenInsertWithExistingFcmToken_thenThrowException() { + val user1 = User() + .setFcmToken(RadarUserControllerTest.FCM_TOKEN_1) + .setEnrolmentDate(Instant.now()) + .setProject(project) + .setTimezone(RadarUserControllerTest.TIMEZONE) + .setLanguage("en") + .setSubjectId(FcmNotificationControllerTest.USER_ID + "-2") + val ex = Assertions.assertThrows( + PersistenceException::class.java + ) { + entityManager.persistAndGetId(user1) as Long + entityManager.flush() + } + Assertions.assertEquals(ConstraintViolationException::class.java, ex.cause!!.javaClass) + } +} \ No newline at end of file diff --git a/src/test/java/org/radarbase/appserver/service/FcmNotificationServiceTest.java b/src/test/java/org/radarbase/appserver/service/FcmNotificationServiceTest.java index 089812ac..3ebc9602 100644 --- a/src/test/java/org/radarbase/appserver/service/FcmNotificationServiceTest.java +++ b/src/test/java/org/radarbase/appserver/service/FcmNotificationServiceTest.java @@ -30,18 +30,17 @@ import static org.radarbase.appserver.controller.FcmNotificationControllerTest.PROJECT_ID; import static org.radarbase.appserver.controller.FcmNotificationControllerTest.USER_ID; import static org.radarbase.appserver.controller.RadarUserControllerTest.FCM_TOKEN_1; +import static org.radarbase.appserver.controller.RadarUserControllerTest.TIMEZONE; import static org.radarbase.appserver.repository.NotificationRepositoryTest.NOTIFICATION_BODY; import static org.radarbase.appserver.repository.NotificationRepositoryTest.NOTIFICATION_FCM_MESSAGE_ID; import static org.radarbase.appserver.repository.NotificationRepositoryTest.NOTIFICATION_SOURCE_ID; import static org.radarbase.appserver.repository.NotificationRepositoryTest.NOTIFICATION_TITLE; -import static org.radarbase.appserver.controller.RadarUserControllerTest.TIMEZONE; import java.time.Duration; import java.time.Instant; import java.util.Date; import java.util.List; import java.util.Optional; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -79,15 +78,19 @@ class FcmNotificationServiceTest { private static final String NOTIFICATION_TITLE_4 = "Testing4"; private static User user; private final transient Instant scheduledTime = Instant.now().plus(Duration.ofSeconds(100)); + @MockBean private transient NotificationSchedulerService schedulerService; // TODO Make this generic when NotificationService interface is complete @Autowired private transient FcmNotificationService notificationService; + @MockBean private transient NotificationRepository notificationRepository; + @MockBean private transient UserRepository userRepository; + @MockBean private transient ProjectRepository projectRepository; @@ -106,24 +109,22 @@ void setUp() { .setEnrolmentDate(Instant.now()) .setFcmToken(FCM_TOKEN_1) .setSubjectId(USER_ID + NEW_SUFFIX) - .setUserMetrics( + .setUsermetrics( new UserMetrics().setLastOpened(Instant.now()).setLastDelivered(Instant.now())) .setId(2L); Mockito.when(userRepository.save(Mockito.any())).thenReturn(userNew); - Notification notification3 = - new Notification.NotificationBuilder() - .body(NOTIFICATION_BODY) - .title(NOTIFICATION_TITLE_3) - .scheduledTime(scheduledTime) - .sourceId(NOTIFICATION_SOURCE_ID) - .fcmMessageId("1234567") - .ttlSeconds(86400) - .delivered(false) - .user(user) - .id(3L) - .build(); + Notification notification3 = new Notification(); + notification3.setUser(user); + notification3.setId(3L); + notification3.setTitle(NOTIFICATION_TITLE_3); + notification3.setScheduledTime(scheduledTime); + notification3.setSourceId(NOTIFICATION_SOURCE_ID); + notification3.setBody(NOTIFICATION_BODY); + notification3.setFcmMessageId("1234567"); + notification3.setTtlSeconds(86400); + notification3.setCreatedAt(new Date()); Mockito.when(notificationRepository.save(notification3)).thenReturn(notification3); @@ -132,29 +133,26 @@ void setUp() { Mockito.when(notificationRepository.findById(3L)).thenReturn(Optional.of(notification3)); Mockito.when( - notificationRepository - .existsByUserIdAndSourceIdAndScheduledTimeAndTitleAndBodyAndTypeAndTtlSeconds( - 1L, - NOTIFICATION_SOURCE_ID, - scheduledTime, - NOTIFICATION_TITLE_3, - NOTIFICATION_BODY, - null, - 86400)) + notificationRepository + .existsByUserIdAndSourceIdAndScheduledTimeAndTitleAndBodyAndTypeAndTtlSeconds( + 1L, + NOTIFICATION_SOURCE_ID, + scheduledTime, + NOTIFICATION_TITLE_3, + NOTIFICATION_BODY, + null, + 86400)) .thenReturn(false); - Notification notification4 = - new Notification.NotificationBuilder() - .body(NOTIFICATION_BODY) - .title(NOTIFICATION_TITLE_4) - .scheduledTime(scheduledTime) - .sourceId(NOTIFICATION_SOURCE_ID) - .fcmMessageId("12345678") - .ttlSeconds(86400) - .delivered(false) - .user(userNew) - .id(4L) - .build(); + Notification notification4 = new Notification(); + notification4.setId(4L); + notification4.setUser(userNew); + notification4.setSourceId(NOTIFICATION_SOURCE_ID); + notification4.setScheduledTime(scheduledTime); + notification4.setTitle(NOTIFICATION_TITLE_4); + notification4.setBody(NOTIFICATION_BODY); + notification4.setFcmMessageId("12345678"); + notification4.setTtlSeconds(86400); notification4.setCreatedAt(new Date()); notification4.setUpdatedAt(new Date()); @@ -164,29 +162,28 @@ void setUp() { Mockito.when(notificationRepository.findById(4L)).thenReturn(Optional.of(notification4)); Mockito.when( - notificationRepository - .existsByUserIdAndSourceIdAndScheduledTimeAndTitleAndBodyAndTypeAndTtlSeconds( - 2L, - NOTIFICATION_SOURCE_ID, - scheduledTime, - NOTIFICATION_TITLE_4, - NOTIFICATION_BODY, - null, - 86400)) + notificationRepository + .existsByUserIdAndSourceIdAndScheduledTimeAndTitleAndBodyAndTypeAndTtlSeconds( + 2L, + NOTIFICATION_SOURCE_ID, + scheduledTime, + NOTIFICATION_TITLE_4, + NOTIFICATION_BODY, + null, + 86400)) .thenReturn(false); - Notification notification5 = - new Notification.NotificationBuilder() - .body(NOTIFICATION_BODY + " Updated") - .title("Testing 2 Updated") - .user(user) - .scheduledTime(scheduledTime) - .sourceId(NOTIFICATION_SOURCE_ID) - .fcmMessageId(FCM_MESSAGE_ID) - .ttlSeconds(86400) - .delivered(false) - .id(5L) - .build(); + Notification notification5 = new Notification(); + notification5.setId(5L); + notification5.setUser(user); + notification5.setSourceId(NOTIFICATION_SOURCE_ID); + notification5.setScheduledTime(scheduledTime); + notification5.setTitle("Testing 2 Updated"); + notification5.setBody(NOTIFICATION_BODY + " Updated"); + notification5.setDelivered(false); + notification5.setFcmMessageId(FCM_MESSAGE_ID); + notification5.setTtlSeconds(86400); + notification5.setCreatedAt(new Date()); notification5.setUpdatedAt(new Date()); @@ -211,7 +208,7 @@ private void setUpProjectAndUser() { .setTimezone(TIMEZONE) .setLanguage("en") .setSubjectId(USER_ID) - .setUserMetrics( + .setUsermetrics( new UserMetrics().setLastOpened(Instant.now()).setLastDelivered(Instant.now())) .setId(1L); @@ -224,33 +221,28 @@ private void setUpProjectAndUser() { } private void setUpNotification1And2() { - Notification notification1 = - new Notification.NotificationBuilder() - .user(user) - .body(NOTIFICATION_BODY) - .title(NOTIFICATION_TITLE) - .scheduledTime(scheduledTime) - .sourceId(NOTIFICATION_SOURCE_ID) - .fcmMessageId(NOTIFICATION_FCM_MESSAGE_ID) - .ttlSeconds(86400) - .delivered(false) - .id(1L) - .build(); + Notification notification1 = new Notification(); + notification1.setId(1L); + notification1.setUser(user); + notification1.setSourceId(NOTIFICATION_SOURCE_ID); + notification1.setScheduledTime(scheduledTime); + notification1.setTitle(NOTIFICATION_TITLE); + notification1.setBody(NOTIFICATION_BODY); + notification1.setFcmMessageId(NOTIFICATION_FCM_MESSAGE_ID); + notification1.setTtlSeconds(86400); + notification1.setUpdatedAt(new Date()); notification1.setCreatedAt(new Date()); - Notification notification2 = - new Notification.NotificationBuilder() - .user(user) - .body(NOTIFICATION_BODY) - .title(NOTIFICATION_TITLE_2) - .scheduledTime(scheduledTime) - .sourceId(NOTIFICATION_SOURCE_ID) - .fcmMessageId(FCM_MESSAGE_ID) - .ttlSeconds(86400) - .delivered(false) - .id(2L) - .build(); + Notification notification2 = new Notification(); + notification2.setId(2L); + notification2.setUser(user); + notification2.setSourceId(NOTIFICATION_SOURCE_ID); + notification2.setScheduledTime(scheduledTime); + notification2.setTitle(NOTIFICATION_TITLE_2); + notification2.setBody(NOTIFICATION_BODY); + notification2.setFcmMessageId(FCM_MESSAGE_ID); + notification2.setTtlSeconds(86400); notification2.setCreatedAt(new Date()); notification2.setUpdatedAt(new Date()); @@ -272,34 +264,34 @@ private void setUpNotification1And2() { Mockito.when(notificationRepository.findById(2L)).thenReturn(Optional.of(notification2)); Mockito.when( - notificationRepository - .existsByUserIdAndSourceIdAndScheduledTimeAndTitleAndBodyAndTypeAndTtlSeconds( - 1L, - NOTIFICATION_SOURCE_ID, - scheduledTime, - NOTIFICATION_TITLE_1, - NOTIFICATION_BODY, - null, - 86400)) + notificationRepository + .existsByUserIdAndSourceIdAndScheduledTimeAndTitleAndBodyAndTypeAndTtlSeconds( + 1L, + NOTIFICATION_SOURCE_ID, + scheduledTime, + NOTIFICATION_TITLE_1, + NOTIFICATION_BODY, + null, + 86400)) .thenReturn(true); Mockito.when( - notificationRepository - .existsByUserIdAndSourceIdAndScheduledTimeAndTitleAndBodyAndTypeAndTtlSeconds( - 1L, - NOTIFICATION_SOURCE_ID, - scheduledTime, - NOTIFICATION_TITLE_2, - NOTIFICATION_BODY, - null, - 86400)) + notificationRepository + .existsByUserIdAndSourceIdAndScheduledTimeAndTitleAndBodyAndTypeAndTtlSeconds( + 1L, + NOTIFICATION_SOURCE_ID, + scheduledTime, + NOTIFICATION_TITLE_2, + NOTIFICATION_BODY, + null, + 86400)) .thenReturn(true); Mockito.when( - notificationRepository - .existsByIdAndUserId( - 1L, - 1L)) + notificationRepository + .existsByIdAndUserId( + 1L, + 1L)) .thenReturn(true); Mockito.when(notificationRepository.findByIdAndUserId(1L, 1L)).thenReturn(Optional.of(notification1)); @@ -372,7 +364,11 @@ void checkIfNotificationExists() { // A random notification should not exist assertFalse( notificationService.checkIfNotificationExists( - new FcmNotificationDto().setScheduledTime(Instant.now()), USER_ID)); + new FcmNotificationDto() + .setTitle("") + .setFcmMessageId(FCM_MESSAGE_ID) + .setScheduledTime(Instant.now()), + USER_ID)); } @Test @@ -393,8 +389,9 @@ void addNotification() { .setTtlSeconds(86400) .setDelivered(false); - notificationService.addNotification(notificationDto, USER_ID, PROJECT_ID); - FcmNotificationDto savedNotification = notificationService.getNotificationById(3L); + FcmNotificationDto result = notificationService.addNotification(notificationDto, USER_ID, + PROJECT_ID); + FcmNotificationDto savedNotification = notificationService.getNotificationById(result.getId()); assertEquals(NOTIFICATION_TITLE_3, savedNotification.getTitle()); assertEquals("1234567", savedNotification.getFcmMessageId()); diff --git a/src/test/java/org/radarbase/appserver/service/UserServiceTest.java b/src/test/java/org/radarbase/appserver/service/UserServiceTest.java index dae99fbd..5910562b 100644 --- a/src/test/java/org/radarbase/appserver/service/UserServiceTest.java +++ b/src/test/java/org/radarbase/appserver/service/UserServiceTest.java @@ -63,7 +63,7 @@ class UserServiceTest { @MockBean private transient ProjectRepository projectRepository; - private transient Instant enrolmentDate = Instant.now().plus(Duration.ofSeconds(100)); + private final transient Instant enrolmentDate = Instant.now().plus(Duration.ofSeconds(100)); @BeforeEach void setUp() { @@ -113,7 +113,7 @@ void setUp() { .setEnrolmentDate(enrolmentDate) .setLanguage("es") .setTimezone("Europe/Bucharest") - .setUserMetrics( + .setUsermetrics( new UserMetrics().setLastDelivered(enrolmentDate).setLastOpened(enrolmentDate)); Mockito.when(userRepository.save(userUpdated)).thenReturn(userUpdated.setId(1L)); diff --git a/src/test/java/org/radarbase/appserver/service/scheduler/NotificationSchedulerServiceTest.java b/src/test/java/org/radarbase/appserver/service/scheduler/NotificationSchedulerServiceTest.java index 5c6225ab..361610d1 100644 --- a/src/test/java/org/radarbase/appserver/service/scheduler/NotificationSchedulerServiceTest.java +++ b/src/test/java/org/radarbase/appserver/service/scheduler/NotificationSchedulerServiceTest.java @@ -77,6 +77,7 @@ class NotificationSchedulerServiceTest { private static final String JOB_DETAIL_ID = "message-jobdetail-test-subject-1"; + private static final String NOTIFICATION_TITLE = "Testing"; private static Notification notification; @Autowired private transient NotificationSchedulerService notificationSchedulerService; @@ -96,20 +97,17 @@ void setUp() throws SchedulerException { .setEnrolmentDate(Instant.now()) .setFcmToken("xxxx"); - notification = - new Notification.NotificationBuilder() - .delivered(false) - .ttlSeconds(900) - .sourceId("aRMT") - .scheduledTime(Instant.now().plus(Duration.ofSeconds(3))) - .fcmMessageId("xxxx") - .title("Testing") - .body("Testing") - .user(user) - .type("ESM") - .appPackage("aRMT") - .id(1L) - .build(); + notification = new Notification(); + notification.setId(1L); + notification.setUser(user); + notification.setSourceId("aRMT"); + notification.setScheduledTime(Instant.now().plus(Duration.ofSeconds(3))); + notification.setTtlSeconds(900); + notification.setFcmMessageId("xxxx"); + notification.setAppPackage("aRMT"); + notification.setTitle(NOTIFICATION_TITLE); + notification.setBody(NOTIFICATION_TITLE); + notification.setType("ESM"); } @Test @@ -144,13 +142,12 @@ void updateScheduledNotification() throws SchedulerException { NotificationSchedulerService.getTriggerForMessage(notification, jobDetail.getObject()); scheduler.scheduleJob(jobDetail.getObject(), triggerFactoryBean.getObject()); - Notification notification2 = - new Notification.NotificationBuilder(notification) - .fcmMessageId("yyyy") - .body("New body") - .title("New Title") - .scheduledTime(Instant.now().plus(Duration.ofSeconds(100))) - .build(); + Notification notification2 = notification.copy(); + notification2.setScheduledTime(Instant.now().plus(Duration.ofSeconds(3))); + notification2.setTtlSeconds(900); + notification2.setFcmMessageId("yyyy"); + notification2.setTitle(NOTIFICATION_TITLE); + notification2.setBody(NOTIFICATION_TITLE); // when notificationSchedulerService.updateScheduled(notification2);