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 @@
Quick Links
Contact Us
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