diff --git a/.github/workflows/scp-deploy.yml b/.github/workflows/scp-deploy.yml
new file mode 100644
index 000000000..3b4f4ff4a
--- /dev/null
+++ b/.github/workflows/scp-deploy.yml
@@ -0,0 +1,35 @@
+name: Run SCP deploy
+
+on:
+ workflow_dispatch:
+
+jobs:
+ scp-deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up JDK 17
+ uses: actions/setup-java@v3
+ with:
+ java-version: 17
+ distribution: 'temurin'
+ server-id: jfrog-central
+ server-username: INTERNAL_USERNAME
+ server-password: INTERNAL_PASSWORD
+ cache: maven
+ - name: Run Maven Package Step
+ run: |
+ mvn -B -U package -Dmaven.test.skip=true
+ env:
+ INTERNAL_USERNAME: ${{ secrets.JFROG_USERNAME }}
+ INTERNAL_PASSWORD: ${{ secrets.JFROG_PASSWORD }}
+ - name: Set up SSH key
+ run: |
+ mkdir -p ~/.ssh
+ echo "${{ secrets.SCP_CERTIFICATE }}" > ~/.ssh/id_rsa
+ chmod 600 ~/.ssh/id_rsa
+ ssh-keyscan -t rsa ${{ secrets.SCP_HOST }} >> ~/.ssh/known_hosts
+ - name: Deploy powerauth-push-server.war
+ shell: bash
+ run: |
+ scp -i ~/.ssh/id_rsa **/target/powerauth-push-server-*.war ${{ secrets.SCP_USERNAME }}@${{ secrets.SCP_HOST }}:/opt/apache-tomcat/webapps/powerauth-push-server.war
diff --git a/docs-private/Developer-How-To-Start.md b/docs-private/Developer-How-To-Start.md
index 7bd73bfe0..f8be00deb 100644
--- a/docs-private/Developer-How-To-Start.md
+++ b/docs-private/Developer-How-To-Start.md
@@ -6,7 +6,6 @@
### Standalone Run
-- Enable maven profile `standalone`
- Use IntelliJ Idea run configuration at `../.run/PowerAuthPushServerJavaApplication.run.xml`
- Open [http://localhost:8089/powerauth-push-server/actuator/health](http://localhost:8089/powerauth-push-server/actuator/health) and you should get `{"status":"UP"}`
diff --git a/docs/Configuration-Properties.md b/docs/Configuration-Properties.md
index d3e0a8dfe..163695f6a 100644
--- a/docs/Configuration-Properties.md
+++ b/docs/Configuration-Properties.md
@@ -78,4 +78,10 @@ The Push Server uses the following public configuration properties:
| `powerauth.service.correlation-header.enabled` | `false` | Whether correlation header is enabled |
| `powerauth.service.correlation-header.name` | `X-Correlation-ID` | Correlation header name |
| `powerauth.service.correlation-header.value.validation-regexp` | `[a-zA-Z0-9\\-]{8,1024}` | Regular expression for correlation header value validation |
-| `logging.pattern.console` | [See application.properties](https://github.com/wultra/powerauth-push-server/blob/develop/powerauth-push-server/src/main/resources/application.properties#docucheck-keep-link) | Logging pattern for console which includes the correlation header value |
\ No newline at end of file
+| `logging.pattern.console` | [See application.properties](https://github.com/wultra/powerauth-push-server/blob/develop/powerauth-push-server/src/main/resources/application.properties#docucheck-keep-link) | Logging pattern for console which includes the correlation header value |
+
+
+## Monitoring and Observability
+
+The WAR file includes the `micrometer-registry-prometheus` dependency.
+Discuss its configuration with the [Spring Boot documentation](https://docs.spring.io/spring-boot/docs/3.1.x/reference/html/actuator.html#actuator.metrics).
diff --git a/docs/Migration-Instructions.md b/docs/Migration-Instructions.md
index 9e2d3a166..403c3dc30 100644
--- a/docs/Migration-Instructions.md
+++ b/docs/Migration-Instructions.md
@@ -2,6 +2,7 @@
This page contains PowerAuth Push Server migration instructions.
+- [PowerAuth Push Server 1.6.0](./PowerAuth-Push-Server-1.6.0.md)
- [PowerAuth Push Server 1.5.0](./PowerAuth-Push-Server-1.5.0.md)
- [PowerAuth Push Server 1.4.0](./PowerAuth-Push-Server-1.4.0.md)
- [PowerAuth Push Server 1.3.0](./PowerAuth-Push-Server-1.3.0.md)
diff --git a/docs/PowerAuth-Push-Server-1.6.0.md b/docs/PowerAuth-Push-Server-1.6.0.md
new file mode 100644
index 000000000..529969924
--- /dev/null
+++ b/docs/PowerAuth-Push-Server-1.6.0.md
@@ -0,0 +1,5 @@
+# Migration from 1.5.x to 1.6.x
+
+This guide contains instructions for migration from PowerAuth Push Server version `1.5.x` to version `1.6.x`.
+
+No migration steps nor database changes are required.
diff --git a/pom.xml b/pom.xml
index 80a490742..aa5ca7ea2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,13 +8,13 @@
io.getlime.security
powerauth-push-server-parent
- 1.5.0
+ 1.6.0
pom
org.springframework.boot
spring-boot-starter-parent
- 3.1.3
+ 3.1.6
@@ -69,21 +69,23 @@
- 1.7.0
- 1.5.0
- 1.5.1
- 1.5.0
+ 1.8.0
+ 1.6.0
+ 1.6.0
+ 1.6.0
- 0.15.2
+ 0.15.3
1.35.2
9.2.0
- 1.76
+ 1.77
7.4
+
+ 1.4.14
- 2.2.0
- 2.2.15
+ 2.3.0
+ 2.2.20
3.5.0
diff --git a/powerauth-push-client/pom.xml b/powerauth-push-client/pom.xml
index 28d49c818..c370aec4e 100644
--- a/powerauth-push-client/pom.xml
+++ b/powerauth-push-client/pom.xml
@@ -10,7 +10,7 @@
powerauth-push-server-parent
io.getlime.security
- 1.5.0
+ 1.6.0
diff --git a/powerauth-push-model/pom.xml b/powerauth-push-model/pom.xml
index db9d7da6d..a9743f6ed 100644
--- a/powerauth-push-model/pom.xml
+++ b/powerauth-push-model/pom.xml
@@ -11,7 +11,7 @@
powerauth-push-server-parent
io.getlime.security
- 1.5.0
+ 1.6.0
diff --git a/powerauth-push-model/src/main/java/io/getlime/push/model/base/PagedResponse.java b/powerauth-push-model/src/main/java/io/getlime/push/model/base/PagedResponse.java
index cf00a72c9..4fec76962 100644
--- a/powerauth-push-model/src/main/java/io/getlime/push/model/base/PagedResponse.java
+++ b/powerauth-push-model/src/main/java/io/getlime/push/model/base/PagedResponse.java
@@ -17,6 +17,7 @@
package io.getlime.push.model.base;
import io.getlime.core.rest.model.base.response.ObjectResponse;
+import lombok.EqualsAndHashCode;
/**
* Generic response class for paged results
@@ -25,6 +26,7 @@
*
* @param Type of the paged records.
*/
+@EqualsAndHashCode(callSuper = true)
public class PagedResponse extends ObjectResponse {
private int page;
diff --git a/powerauth-push-server/pom.xml b/powerauth-push-server/pom.xml
index 7d28c22ea..a008e75fe 100644
--- a/powerauth-push-server/pom.xml
+++ b/powerauth-push-server/pom.xml
@@ -11,9 +11,24 @@
io.getlime.security
powerauth-push-server-parent
- 1.5.0
+ 1.6.0
+
+
+
+ org.springframework.boot
+ spring-boot-starter-tomcat
+ provided
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-el
+ provided
+
+
+
+
@@ -30,12 +45,6 @@
org.springframework.boot
spring-boot-starter-web
-
-
- org.springframework.boot
- spring-boot-starter-tomcat
-
-
org.springframework.boot
@@ -48,12 +57,6 @@
org.springframework.boot
spring-boot-starter-validation
-
-
- org.apache.tomcat.embed
- tomcat-embed-el
-
-
org.springframework.boot
@@ -115,6 +118,12 @@
postgresql
+
+
+ io.micrometer
+ micrometer-registry-prometheus
+
+
net.logstash.logback
@@ -175,11 +184,6 @@
${bc.version}
test
-
- org.springframework.boot
- spring-boot-starter-tomcat
- test
-
@@ -241,18 +245,6 @@
-
-
- standalone
-
-
- org.springframework.boot
- spring-boot-starter-tomcat
- provided
-
-
-
-
liquibase
diff --git a/powerauth-push-server/src/main/java/io/getlime/push/configuration/OpenApiConfiguration.java b/powerauth-push-server/src/main/java/io/getlime/push/configuration/OpenApiConfiguration.java
index b3c758b5d..6cc68f490 100644
--- a/powerauth-push-server/src/main/java/io/getlime/push/configuration/OpenApiConfiguration.java
+++ b/powerauth-push-server/src/main/java/io/getlime/push/configuration/OpenApiConfiguration.java
@@ -21,10 +21,15 @@
import io.swagger.v3.oas.annotations.info.Contact;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.info.License;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.servers.Server;
+import jakarta.servlet.ServletContext;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import java.util.List;
+
/**
* Swagger configuration class for api documentation
*
@@ -63,4 +68,13 @@ public GroupedOpenApi pushApiGroup() {
.build();
}
+ @Bean
+ public OpenAPI openAPI(final ServletContext servletContext) {
+ final Server server = new Server()
+ .url(servletContext.getContextPath())
+ .description("Default Server URL");
+ return new OpenAPI()
+ .servers(List.of(server));
+ }
+
}
diff --git a/powerauth-push-server/src/main/java/io/getlime/push/repository/model/PushCampaignEntity.java b/powerauth-push-server/src/main/java/io/getlime/push/repository/model/PushCampaignEntity.java
index fbd7536bf..208624069 100644
--- a/powerauth-push-server/src/main/java/io/getlime/push/repository/model/PushCampaignEntity.java
+++ b/powerauth-push-server/src/main/java/io/getlime/push/repository/model/PushCampaignEntity.java
@@ -48,7 +48,7 @@ public class PushCampaignEntity implements Serializable {
/**
* Message.
*/
- @Column(name = "message", nullable = false, updatable = false)
+ @Column(name = "message", nullable = false, updatable = false, length = 4000)
private String message;
/**
diff --git a/powerauth-push-server/src/main/java/io/getlime/push/service/PayloadBuilder.java b/powerauth-push-server/src/main/java/io/getlime/push/service/PayloadBuilder.java
new file mode 100644
index 000000000..05bfc1ce0
--- /dev/null
+++ b/powerauth-push-server/src/main/java/io/getlime/push/service/PayloadBuilder.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2023 Wultra s.r.o.
+ *
+ * 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 io.getlime.push.service;
+
+import com.eatthepath.pushy.apns.util.ApnsPayloadBuilder;
+import com.eatthepath.pushy.apns.util.SimpleApnsPayloadBuilder;
+import io.getlime.push.model.entity.PushMessageBody;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Map;
+
+/**
+ * Convert {@link PushMessageBody} to platform dependant payload.
+ *
+ * @author Lubos Racansky, lubos.racansky@wultra.com
+ */
+@Slf4j
+final class PayloadBuilder {
+
+ private PayloadBuilder() {
+ throw new IllegalStateException("Should not be instantiated.");
+ }
+
+ /**
+ * Method to build APNs message payload.
+ *
+ * @param push Push message object with APNs data.
+ * @param isSilent Indicates if the message is silent or not.
+ * @return String with APNs JSON payload.
+ */
+ static String buildApnsPayload(final PushMessageBody push, final boolean isSilent) {
+ final ApnsPayloadBuilder payloadBuilder = new SimpleApnsPayloadBuilder();
+ if (!isSilent) { // include alert, body, sound and category only in case push message is not silent.
+ payloadBuilder
+ .setAlertTitle(push.getTitle())
+ .setLocalizedAlertTitle(push.getTitleLocKey(), push.getTitleLocArgs())
+ .setAlertBody(push.getBody())
+ .setLocalizedAlertMessage(push.getBodyLocKey(), push.getBodyLocArgs())
+ .setSound(push.getSound())
+ .setCategoryName(push.getCategory());
+ }
+
+ payloadBuilder
+ .setBadgeNumber(push.getBadge())
+ .setContentAvailable(isSilent)
+ .setThreadId(push.getCollapseKey());
+
+ final Map extras = push.getExtras();
+ if (extras != null) {
+ for (Map.Entry entry : extras.entrySet()) {
+ if (entry.getValue() != null) {
+ payloadBuilder.addCustomProperty(entry.getKey(), entry.getValue());
+ } else {
+ // Workaround for a known Apple issue, the JSON is valid but APNS would not send the push message.
+ logger.debug("Skipping extras key: {} because of null value.", entry.getKey());
+ }
+ }
+ }
+
+ return payloadBuilder.build();
+ }
+}
diff --git a/powerauth-push-server/src/main/java/io/getlime/push/service/PushSendingWorker.java b/powerauth-push-server/src/main/java/io/getlime/push/service/PushSendingWorker.java
index 5ffc7f788..844d06022 100644
--- a/powerauth-push-server/src/main/java/io/getlime/push/service/PushSendingWorker.java
+++ b/powerauth-push-server/src/main/java/io/getlime/push/service/PushSendingWorker.java
@@ -19,8 +19,6 @@
import com.eatthepath.pushy.apns.*;
import com.eatthepath.pushy.apns.auth.ApnsSigningKey;
import com.eatthepath.pushy.apns.proxy.HttpProxyHandlerFactory;
-import com.eatthepath.pushy.apns.util.ApnsPayloadBuilder;
-import com.eatthepath.pushy.apns.util.SimpleApnsPayloadBuilder;
import com.eatthepath.pushy.apns.util.SimpleApnsPushNotification;
import com.eatthepath.pushy.apns.util.TokenUtil;
import com.eatthepath.pushy.apns.util.concurrent.PushNotificationFuture;
@@ -379,7 +377,7 @@ void sendMessageToIos(final ApnsClient apnsClient, final PushMessageBody pushMes
final String token = TokenUtil.sanitizeTokenString(pushToken);
final boolean isSilent = attributes != null && attributes.getSilent(); // In case there are no attributes, the message is not silent
- final String payload = buildApnsPayload(pushMessageBody, isSilent);
+ final String payload = PayloadBuilder.buildApnsPayload(pushMessageBody, isSilent);
final Instant validUntil = pushMessageBody.getValidUntil();
final PushType pushType = isSilent ? PushType.BACKGROUND : PushType.ALERT; // iOS 13 and higher requires apns-push-type value to be set
final DeliveryPriority deliveryPriority = (Priority.NORMAL == priority) ? DeliveryPriority.CONSERVE_POWER : DeliveryPriority.IMMEDIATE;
@@ -442,34 +440,4 @@ void sendMessageToIos(final ApnsClient apnsClient, final PushMessageBody pushMes
});
}
- /**
- * Method to build APNs message payload.
- *
- * @param push Push message object with APNs data.
- * @param isSilent Indicates if the message is silent or not.
- * @return String with APNs JSON payload.
- */
- private String buildApnsPayload(PushMessageBody push, boolean isSilent) {
- final ApnsPayloadBuilder payloadBuilder = new SimpleApnsPayloadBuilder();
- if (!isSilent) { // include alert, body, sound and category only in case push message is not silent.
- payloadBuilder
- .setAlertTitle(push.getTitle())
- .setLocalizedAlertTitle(push.getTitleLocKey(), push.getTitleLocArgs())
- .setAlertBody(push.getBody())
- .setLocalizedAlertMessage(push.getBodyLocKey(), push.getBodyLocArgs())
- .setSound(push.getSound())
- .setCategoryName(push.getCategory());
- }
- payloadBuilder
- .setBadgeNumber(push.getBadge())
- .setContentAvailable(isSilent)
- .setThreadId(push.getCollapseKey());
- final Map extras = push.getExtras();
- if (extras != null) {
- for (Map.Entry entry : extras.entrySet()) {
- payloadBuilder.addCustomProperty(entry.getKey(), entry.getValue());
- }
- }
- return payloadBuilder.build();
- }
}
\ No newline at end of file
diff --git a/powerauth-push-server/src/main/resources/application.properties b/powerauth-push-server/src/main/resources/application.properties
index 119fbe933..d0f204675 100644
--- a/powerauth-push-server/src/main/resources/application.properties
+++ b/powerauth-push-server/src/main/resources/application.properties
@@ -7,8 +7,6 @@ spring.datasource.username=powerauth
spring.datasource.password=
spring.datasource.hikari.auto-commit=false
spring.datasource.driver-class-name=org.postgresql.Driver
-spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
-spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false
spring.jpa.properties.hibernate.connection.characterEncoding=utf8
spring.jpa.properties.hibernate.connection.useUnicode=true
@@ -38,6 +36,9 @@ powerauth.push.service.applicationName=powerauth-push
powerauth.push.service.applicationDisplayName=PowerAuth Push Server
powerauth.push.service.applicationEnvironment=
+banner.application.name=${powerauth.push.service.applicationDisplayName}
+banner.application.version=@project.version@
+
# PowerAuth Push Campaign Setup
powerauth.push.service.campaign.batchSize=100000
@@ -94,4 +95,10 @@ powerauth.service.correlation-header.enabled=false
powerauth.service.correlation-header.name=X-Correlation-ID
powerauth.service.correlation-header.value.validation-regexp=[a-zA-Z0-9\\-]{8,1024}
# For logging correlation HTTP headers enable the pattern and update correlation header name in the pattern
-#logging.pattern.console=%clr(%d{${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:%5p}) [%X{X-Correlation-ID}] %clr(%5p) %clr(${PID: }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}
\ No newline at end of file
+#logging.pattern.console=%clr(%d{${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:%5p}) [%X{X-Correlation-ID}] %clr(%5p) %clr(${PID: }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}
+
+# Monitoring
+#management.endpoint.metrics.enabled=true
+#management.endpoints.web.exposure.include=health, prometheus
+#management.endpoint.prometheus.enabled=true
+#management.prometheus.metrics.export.enabled=true
diff --git a/powerauth-push-server/src/main/resources/banner.txt b/powerauth-push-server/src/main/resources/banner.txt
new file mode 100644
index 000000000..7a82690b2
--- /dev/null
+++ b/powerauth-push-server/src/main/resources/banner.txt
@@ -0,0 +1,9 @@
+ ____ _ ____
+ | _ \ _ _ ___| |__ / ___| ___ _ ____ _____ _ __
+ | |_) | | | / __| '_ \ \___ \ / _ \ '__\ \ / / _ \ '__|
+ | __/| |_| \__ \ | | | ___) | __/ | \ V / __/ |
+ |_| \__,_|___/_| |_| |____/ \___|_| \_/ \___|_|
+
+${AnsiColor.GREEN} :: ${banner.application.name} (${banner.application.version}) :: ${AnsiColor.GREEN}
+${AnsiColor.RED} :: Spring Boot${spring-boot.formatted-version} :: ${AnsiColor.RED}
+${AnsiColor.DEFAULT}
\ No newline at end of file
diff --git a/powerauth-push-server/src/main/resources/templates/home.html b/powerauth-push-server/src/main/resources/templates/home.html
index d3bda9eef..b934e7100 100644
--- a/powerauth-push-server/src/main/resources/templates/home.html
+++ b/powerauth-push-server/src/main/resources/templates/home.html
@@ -67,7 +67,7 @@
diff --git a/powerauth-push-server/src/test/java/io/getlime/push/ApplicationTest.java b/powerauth-push-server/src/test/java/io/getlime/push/ApplicationTest.java
index 7ec24d7ce..a36906959 100644
--- a/powerauth-push-server/src/test/java/io/getlime/push/ApplicationTest.java
+++ b/powerauth-push-server/src/test/java/io/getlime/push/ApplicationTest.java
@@ -17,24 +17,21 @@
package io.getlime.push;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;
-import org.springframework.test.context.junit.jupiter.SpringExtension;
/**
* Verify test application start.
*
* @author Roman Strobl, roman.strobl@wultra.com
*/
-@ExtendWith(SpringExtension.class)
@SpringBootTest
@TestPropertySource(locations = "classpath:application-test.properties")
@ActiveProfiles("test")
-public class ApplicationTest {
+class ApplicationTest {
private static final Logger logger = LoggerFactory.getLogger(ApplicationTest.class);
diff --git a/powerauth-push-server/src/test/java/io/getlime/push/controller/rest/PushMessageControllerTest.java b/powerauth-push-server/src/test/java/io/getlime/push/controller/rest/PushMessageControllerTest.java
new file mode 100644
index 000000000..793168c3d
--- /dev/null
+++ b/powerauth-push-server/src/test/java/io/getlime/push/controller/rest/PushMessageControllerTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2023 Wultra s.r.o.
+ *
+ * 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 io.getlime.push.controller.rest;
+
+import io.getlime.push.model.entity.PushMessage;
+import io.getlime.push.model.enumeration.Mode;
+import io.getlime.push.service.PushMessageSenderService;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.web.servlet.MockMvc;
+
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+/**
+ * Test for {@link PushMessageController}.
+ *
+ * @author Lubos Racansky, lubos.racansky@wultra.com
+ */
+@SpringBootTest
+@ActiveProfiles("test")
+@AutoConfigureMockMvc
+class PushMessageControllerTest {
+
+ @MockBean
+ private PushMessageSenderService pushMessageSenderService;
+
+ @Captor
+ private ArgumentCaptor> pushMessagesCaptor;
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Test
+ void sendPushMessage() throws Exception {
+ mockMvc.perform(post("/push/message/send")
+ .content("""
+ {
+ "requestObject": {
+ "appId": "mobile-app",
+ "mode": "SYNCHRONOUS",
+ "message": {
+ "activationId": "49414e31-f3df-4cea-87e6-f214ca3b8412",
+ "userId": "123",
+ "priority": "HIGH",
+ "attributes": {
+ "personal": true,
+ "silent": true
+ },
+ "body": {
+ "title": "Balance update",
+ "titleLocKey": "balance.update.title",
+ "titleLocArgs": [],
+ "body": "Your balance is now $745.00",
+ "bodyLocKey": "balance.update.body",
+ "bodyLocArgs": [],
+ "badge": 3,
+ "sound": "default",
+ "icon": "custom-icon",
+ "category": "balance-update",
+ "collapseKey": "balance-update",
+ "validUntil": "2017-12-11T21:22:29.923Z",
+ "extras": {
+ "_comment": "Any custom data.",
+ "_foo": null
+ }
+ }
+ }
+ }
+ }
+ """)
+ .contentType(MediaType.APPLICATION_JSON)
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.status").value("OK"));
+
+ verify(pushMessageSenderService).sendPushMessage(eq("mobile-app"), eq(Mode.SYNCHRONOUS), pushMessagesCaptor.capture());
+
+ final List pushMessages = pushMessagesCaptor.getValue();
+ assertEquals(1, pushMessages.size());
+
+ final PushMessage pushMessage = pushMessages.iterator().next();
+ final Map extras = pushMessage.getBody().getExtras();
+ assertEquals("Any custom data.", extras.get("_comment"));
+ assertNull(extras.get("_foo"), "Null value is binded correctly, should be removed before sending to APNS.");
+ }
+
+}
diff --git a/powerauth-push-server/src/test/java/io/getlime/push/service/PayloadBuilderTest.java b/powerauth-push-server/src/test/java/io/getlime/push/service/PayloadBuilderTest.java
new file mode 100644
index 000000000..2d061ce2b
--- /dev/null
+++ b/powerauth-push-server/src/test/java/io/getlime/push/service/PayloadBuilderTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2023 Wultra s.r.o.
+ *
+ * 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 io.getlime.push.service;
+
+import io.getlime.push.model.entity.PushMessageBody;
+import org.junit.jupiter.api.Test;
+
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Test {@link PayloadBuilder}.
+ *
+ * @author Lubos Racansky, lubos.racansky@wultra.com
+ */
+class PayloadBuilderTest {
+
+ @Test
+ void testBuildApnsPayload() {
+ final Map extras = new HashMap<>();
+ extras.put("_comment", "Any custom data.");
+ extras.put("_foo", null);
+
+ final PushMessageBody pushMessageBody = new PushMessageBody();
+ pushMessageBody.setTitle("Balance update");
+ pushMessageBody.setTitleLocKey("balance.update.title");
+ pushMessageBody.setTitleLocArgs(new String[0]);
+ pushMessageBody.setBody("Your balance is now $745.00");
+ pushMessageBody.setBodyLocKey("balance.update.body");
+ pushMessageBody.setBodyLocArgs(new String[0]);
+ pushMessageBody.setBadge(3);
+ pushMessageBody.setSound("default");
+ pushMessageBody.setIcon("custom-icon");
+ pushMessageBody.setCategory("balance-update");
+ pushMessageBody.setCollapseKey("balance-update");
+ pushMessageBody.setValidUntil(Instant.parse("2017-12-11T21:22:29.923Z"));
+ pushMessageBody.setExtras(extras);
+
+ final String result = PayloadBuilder.buildApnsPayload(pushMessageBody, false);
+
+ assertFalse(result.contains("_foo"));
+ assertEquals("""
+ {"aps":{"badge":3,"alert":{"loc-key":"balance.update.body","body":"Your balance is now $745.00","title":"Balance update","title-loc-key":"balance.update.title"},"sound":"default","category":"balance-update","thread-id":"balance-update"},"_comment":"Any custom data."}""",
+ result);
+ }
+}
\ No newline at end of file
diff --git a/powerauth-push-server/src/test/java/io/getlime/push/tests/PushServerMultipleActivationsTests.java b/powerauth-push-server/src/test/java/io/getlime/push/tests/PushServerMultipleActivationsTests.java
index fd016f9aa..4128412d5 100644
--- a/powerauth-push-server/src/test/java/io/getlime/push/tests/PushServerMultipleActivationsTests.java
+++ b/powerauth-push-server/src/test/java/io/getlime/push/tests/PushServerMultipleActivationsTests.java
@@ -27,13 +27,11 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
-import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;
-import org.springframework.test.context.junit.jupiter.SpringExtension;
import java.util.*;
@@ -44,12 +42,11 @@
*
* @author Roman Strobl, roman.strobl@wultra.com
*/
-@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@TestPropertySource(locations = "classpath:application-test-multiple-activations.properties")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ActiveProfiles("test")
-public class PushServerMultipleActivationsTests {
+class PushServerMultipleActivationsTests {
private static final String MOCK_PUSH_TOKEN = "1234567890987654321234567890";
private static final String MOCK_PUSH_TOKEN_2 = "9876543212345678901234567890";
diff --git a/powerauth-push-server/src/test/java/io/getlime/push/tests/PushServerTests.java b/powerauth-push-server/src/test/java/io/getlime/push/tests/PushServerTests.java
index 9b1ec2c1c..58652e3c5 100644
--- a/powerauth-push-server/src/test/java/io/getlime/push/tests/PushServerTests.java
+++ b/powerauth-push-server/src/test/java/io/getlime/push/tests/PushServerTests.java
@@ -33,7 +33,6 @@
import io.getlime.push.repository.model.PushDeviceRegistrationEntity;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
@@ -42,7 +41,6 @@
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.jdbc.Sql;
-import org.springframework.test.context.junit.jupiter.SpringExtension;
import java.time.Instant;
import java.util.*;
@@ -58,7 +56,6 @@
* @author Martin Tupy, martin.tupy.work@gmail.com
* @author Roman Strobl, roman.strobl@wultra.com
*/
-@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@TestPropertySource(locations = "classpath:application-test.properties")
@ActiveProfiles("test")
@@ -362,8 +359,10 @@ void createDeviceDifferentActivationSamePushTokenTest() throws PushServerClientE
pushDeviceRepository.deleteAll(devices2);
}
- @SuppressWarnings("unchecked") // known parameters of HashMap
private ObjectResponse createCampaign() throws Exception {
+ final Map extras = new HashMap<>();
+ extras.put("_comment", "Any custom data.");
+
final PushMessageBody pushMessageBody = new PushMessageBody();
pushMessageBody.setTitle("Balance update");
pushMessageBody.setBody("Your balance is now $745.00");
@@ -372,7 +371,7 @@ private ObjectResponse createCampaign() throws Exception
pushMessageBody.setCategory("balance-update");
pushMessageBody.setCollapseKey("balance-update");
pushMessageBody.setValidUntil(Instant.now());
- pushMessageBody.setExtras((Map) new HashMap().put("_comment", "Any custom data."));
+ pushMessageBody.setExtras(extras);
return pushServerClient.createCampaign(powerAuthTestClient.getApplicationId(), pushMessageBody);
}
}
diff --git a/powerauth-push-server/src/test/resources/application-test-multiple-activations.properties b/powerauth-push-server/src/test/resources/application-test-multiple-activations.properties
index 3d1fd05c9..58a5195c3 100644
--- a/powerauth-push-server/src/test/resources/application-test-multiple-activations.properties
+++ b/powerauth-push-server/src/test/resources/application-test-multiple-activations.properties
@@ -23,7 +23,6 @@ spring.datasource.url=jdbc:h2:file:~/powerauth;MODE=LEGACY
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver
-spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
# Hibernate Configuration
spring.jpa.hibernate.ddl-auto=create-drop
diff --git a/powerauth-push-server/src/test/resources/application-test.properties b/powerauth-push-server/src/test/resources/application-test.properties
index bc0ffc546..16478d14f 100644
--- a/powerauth-push-server/src/test/resources/application-test.properties
+++ b/powerauth-push-server/src/test/resources/application-test.properties
@@ -17,11 +17,10 @@
spring.h2.console.enabled=true
spring.h2.console.path=/h2
# Datasource
-spring.datasource.url=jdbc:h2:file:~/powerauth;MODE=LEGACY
+spring.datasource.url=jdbc:h2:mem:powerauth;MODE=LEGACY
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver
-spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
# Hibernate Configuration
spring.jpa.hibernate.ddl-auto=create-drop