diff --git a/build.gradle b/build.gradle index 83ca068..72a1d4a 100644 --- a/build.gradle +++ b/build.gradle @@ -1,108 +1,85 @@ plugins { id 'application' - id 'com.github.johnrengelman.shadow' version '7.1.1' - - // Used by release.gradle + id 'com.github.johnrengelman.shadow' version '8.1.1' id 'maven-publish' - id 'signing' - id 'io.codearte.nexus-staging' version '0.30.0' } -apply plugin: 'application' -apply plugin: 'com.github.johnrengelman.shadow' - -group 'nl.martijndwars' -version '5.1.2' +group 'com.nexxto' +version '6.0.1' repositories { - mavenLocal() mavenCentral() + maven { + name = "GitHubPackages" + url = "https://maven.pkg.github.com/nexxto/webpush-java" + credentials { + username = System.getenv("GPR_USER") + password = System.getenv("GPR_TOKEN") + } + } } -dependencies { - // For CLI - implementation group: 'com.beust', name: 'jcommander', version: '1.81' - - // For making HTTP requests - implementation group: 'org.apache.httpcomponents', name: 'httpasyncclient', version: '4.1.5' - - // For making async HTTP requests - implementation group: 'org.asynchttpclient', name: 'async-http-client', version: '2.12.4' - - // For cryptographic operations - shadow group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.70' - - // For creating and signing JWT - implementation group: 'org.bitbucket.b_c', name: 'jose4j', version: '0.9.6' - - // For parsing JSON - testImplementation group: 'com.google.code.gson', name: 'gson', version: '2.8.9' - - // For making HTTP requests - testImplementation group: 'org.apache.httpcomponents', name: 'fluent-hc', version: '4.5.13' - - // For testing, obviously - testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.8.1' - - // For running JUnit tests - testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.8.1' - - // For turning InputStream to String - testImplementation group: 'commons-io', name: 'commons-io', version: '2.11.0' - - // For reading the demo vapid keypair from a pem file - testImplementation group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: '1.70' - - // For verifying Base64Encoder results in unit tests - testImplementation group: 'com.google.guava', name: 'guava', version: '33.4.8-jre' +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + } + } + repositories { + maven { + name = "GitHubPackages" + url = "https://maven.pkg.github.com/nexxto/webpush-java" + credentials { + username = project.findProperty("gprUser") ?: System.getenv("GPR_USER") + password = project.findProperty("gprToken") ?: System.getenv("GPR_TOKEN") + } + } + } } -wrapper { - gradleVersion = '5.1' -} -compileJava { - sourceCompatibility = 1.8 - targetCompatibility = 1.8 -} -compileTestJava { - sourceCompatibility = 1.8 +dependencies { + implementation 'com.beust:jcommander:1.81' + implementation 'org.apache.httpcomponents:httpasyncclient:4.1.5' + implementation 'org.asynchttpclient:async-http-client:2.12.4' + implementation 'org.bitbucket.b_c:jose4j:0.9.6' + + // Include BouncyCastle in the fat JAR + shadow 'org.bouncycastle:bcprov-jdk18on:1.81' + + testImplementation 'com.google.code.gson:gson:2.8.9' + testImplementation 'org.apache.httpcomponents:fluent-hc:4.5.13' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' + testImplementation 'commons-io:commons-io:2.11.0' + testImplementation 'org.bouncycastle:bcprov-jdk18on:1.81' + testImplementation 'com.google.guava:guava:33.4.8-jre' } -mainClassName = 'nl.martijndwars.webpush.cli.Cli' - -run { - classpath configurations.shadow.files +wrapper { + gradleVersion = '8.5' + distributionType = Wrapper.DistributionType.ALL } -test { - useJUnitPlatform() - - testLogging { - events 'PASSED', 'FAILED', 'SKIPPED' - showStandardStreams true - exceptionFormat 'full' +java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) // Use your installed JDK 21 } - - exclude '**/SeleniumTests.class' } -task javadocJar(type: Jar) { - classifier = 'javadoc' - from javadoc +tasks.withType(JavaCompile).configureEach { + options.release = 17 // Compile as if targeting Java 17 } -task sourcesJar(type: Jar) { - classifier = 'sources' - from sourceSets.main.allSource +application { + mainClass = 'nl.martijndwars.webpush.cli.Cli' } -artifacts { - archives javadocJar - archives sourcesJar -} - -if (hasProperty('release')) { - apply from: 'release.gradle' +testing { + suites { + test { + useJUnitJupiter() + } + } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ffed3a2..d0d403e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c787..c53aefa 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -32,10 +32,10 @@ # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». # # Important for patching: # diff --git a/src/test/java/nl/martijndwars/webpush/selenium/BrowserTest.java b/src/test/java/nl/martijndwars/webpush/selenium/BrowserTest.java deleted file mode 100644 index 37ed6d2..0000000 --- a/src/test/java/nl/martijndwars/webpush/selenium/BrowserTest.java +++ /dev/null @@ -1,78 +0,0 @@ -package nl.martijndwars.webpush.selenium; - -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; -import nl.martijndwars.webpush.Notification; -import nl.martijndwars.webpush.PushService; -import nl.martijndwars.webpush.Subscription; -import org.apache.http.HttpResponse; -import org.junit.jupiter.api.function.Executable; - -import java.security.GeneralSecurityException; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class BrowserTest implements Executable { - public static final String GCM_API_KEY = "AIzaSyBAU0VfXoskxUSg81K5VgLgwblHbZWe6tA"; - public static final String PUBLIC_KEY = "BNFDO1MUnNpx0SuQyQcAAWYETa2+W8z/uc5sxByf/UZLHwAhFLwEDxS5iB654KHiryq0AxDhFXS7DVqXDKjjN+8="; - public static final String PRIVATE_KEY = "AM0aAyoIryzARADnIsSCwg1p1aWFAL3Idc8dNXpf74MH"; - public static final String VAPID_SUBJECT = "http://localhost:8090"; - - private TestingService testingService; - private Configuration configuration; - private int testSuiteId; - - public BrowserTest(TestingService testingService, Configuration configuration, int testSuiteId) { - this.configuration = configuration; - this.testingService = testingService; - this.testSuiteId = testSuiteId; - } - - /** - * Execute the test for the given browser configuration. - * - * @throws Throwable - */ - @Override - public void execute() throws Throwable { - PushService pushService = getPushService(); - - JsonObject test = testingService.getSubscription(testSuiteId, configuration); - - int testId = test.get("testId").getAsInt(); - - Subscription subscription = new Gson().fromJson(test.get("subscription").getAsJsonObject(), Subscription.class); - - String message = "Hëllö, world!"; - Notification notification = new Notification(subscription, message); - - HttpResponse response = pushService.send(notification); - assertEquals(201, response.getStatusLine().getStatusCode()); - - JsonArray messages = testingService.getNotificationStatus(testSuiteId, testId); - assertEquals(1, messages.size()); - assertEquals(new JsonPrimitive(message), messages.get(0)); - } - - protected PushService getPushService() throws GeneralSecurityException { - PushService pushService; - - if (!configuration.isVapid()) { - pushService = new PushService(GCM_API_KEY); - } else { - pushService = new PushService(PUBLIC_KEY, PRIVATE_KEY, VAPID_SUBJECT); - } - return pushService; - } - - /** - * The name used by JUnit to display the test. - * - * @return - */ - public String getDisplayName() { - return "Browser " + configuration.browser + ", version " + configuration.version + ", vapid " + configuration.isVapid(); - } -} diff --git a/src/test/java/nl/martijndwars/webpush/selenium/Configuration.java b/src/test/java/nl/martijndwars/webpush/selenium/Configuration.java deleted file mode 100644 index 5b4e4e1..0000000 --- a/src/test/java/nl/martijndwars/webpush/selenium/Configuration.java +++ /dev/null @@ -1,24 +0,0 @@ -package nl.martijndwars.webpush.selenium; - -public class Configuration { - protected final String browser; - protected final String version; - protected final String publicKey; - protected final String gcmSenderId; - - Configuration(String browser, String version, String publicKey, String gcmSenderId) { - this.browser = browser; - this.version = version; - this.publicKey = publicKey; - this.gcmSenderId = gcmSenderId; - } - - public boolean isVapid() { - return publicKey != null && !publicKey.isEmpty(); - } - - @Override - public String toString() { - return browser + ", " + version + ", " + publicKey; - } -} diff --git a/src/test/java/nl/martijndwars/webpush/selenium/SeleniumTests.java b/src/test/java/nl/martijndwars/webpush/selenium/SeleniumTests.java deleted file mode 100644 index 1054486..0000000 --- a/src/test/java/nl/martijndwars/webpush/selenium/SeleniumTests.java +++ /dev/null @@ -1,81 +0,0 @@ -package nl.martijndwars.webpush.selenium; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.DynamicTest; -import org.junit.jupiter.api.TestFactory; - -import java.io.IOException; -import java.security.Security; -import java.util.Base64; -import java.util.stream.Stream; - -import static org.junit.jupiter.api.DynamicTest.dynamicTest; - -/** - * SeleniumTest performs integration testing. - */ -public class SeleniumTests { - protected static final String GCM_SENDER_ID = "759071690750"; - protected static final String PUBLIC_KEY = "BNFDO1MUnNpx0SuQyQcAAWYETa2+W8z/uc5sxByf/UZLHwAhFLwEDxS5iB654KHiryq0AxDhFXS7DVqXDKjjN+8="; - - protected static TestingService testingService = new TestingService("http://localhost:8090/api/"); - protected static int testSuiteId; - - public SeleniumTests() { - Security.addProvider(new BouncyCastleProvider()); - } - - /** - * End the test suite. - * - * @throws IOException - */ - @AfterAll - public static void tearDown() throws IOException { - testingService.endTestSuite(testSuiteId); - } - - /** - * Generate a stream of tests based on the configurations. - * - * @return - */ - @TestFactory - public Stream dynamicTests() throws IOException { - testSuiteId = testingService.startTestSuite(); - - return getConfigurations().map(configuration -> { - BrowserTest browserTest = new BrowserTest(testingService, configuration, testSuiteId); - - return dynamicTest(browserTest.getDisplayName(), browserTest); - }); - } - - /** - * Get browser configurations to test. - * - * @return - */ - protected Stream getConfigurations() { - String PUBLIC_KEY_NO_PADDING = Base64.getUrlEncoder().withoutPadding().encodeToString( - Base64.getUrlDecoder().decode(PUBLIC_KEY) - ); - - return Stream.of( - new Configuration("chrome", "stable", null, GCM_SENDER_ID), - new Configuration("chrome", "beta", null, GCM_SENDER_ID), - //new Configuration("chrome", "unstable", null, GCM_SENDER_ID), See #90 - - new Configuration("firefox", "stable", null, GCM_SENDER_ID), - new Configuration("firefox", "beta", null, GCM_SENDER_ID), - - new Configuration("chrome", "stable", PUBLIC_KEY_NO_PADDING, null), - new Configuration("chrome", "beta", PUBLIC_KEY_NO_PADDING, null), - //new Configuration("chrome", "unstable", PUBLIC_KEY_NO_PADDING, null), See #90 - - new Configuration("firefox", "stable", PUBLIC_KEY_NO_PADDING, null), - new Configuration("firefox", "beta", PUBLIC_KEY_NO_PADDING, null) - ); - } -} diff --git a/src/test/java/nl/martijndwars/webpush/selenium/TestingService.java b/src/test/java/nl/martijndwars/webpush/selenium/TestingService.java deleted file mode 100644 index a89f9a9..0000000 --- a/src/test/java/nl/martijndwars/webpush/selenium/TestingService.java +++ /dev/null @@ -1,160 +0,0 @@ -package nl.martijndwars.webpush.selenium; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpEntity; -import org.apache.http.client.fluent.Request; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.util.EntityUtils; - -import java.io.IOException; - -import static java.nio.charset.StandardCharsets.UTF_8; - -/** - * Java wrapper for interacting with the Web Push Testing Service. - */ -public class TestingService { - private String baseUrl; - - public TestingService(String baseUrl) { - this.baseUrl = baseUrl; - } - - /** - * Start a new test suite. - * - * @return - */ - public int startTestSuite() throws IOException { - String startTestSuite = request(baseUrl + "start-test-suite/"); - - JsonElement root = JsonParser.parseString(startTestSuite); - - return root - .getAsJsonObject() - .get("data") - .getAsJsonObject() - .get("testSuiteId") - .getAsInt(); - } - - /** - * Get a test ID and subscription for the given test case. - * - * @param testSuiteId - * @param configuration - * @return - * @throws IOException - */ - public JsonObject getSubscription(int testSuiteId, Configuration configuration) throws IOException { - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("testSuiteId", testSuiteId); - jsonObject.addProperty("browserName", configuration.browser); - jsonObject.addProperty("browserVersion", configuration.version); - - if (configuration.gcmSenderId != null) { - jsonObject.addProperty("gcmSenderId", configuration.gcmSenderId); - } - - if (configuration.publicKey != null) { - jsonObject.addProperty("vapidPublicKey", configuration.publicKey); - } - - HttpEntity entity = new StringEntity(jsonObject.toString(), ContentType.APPLICATION_JSON); - - String getSubscription = request(baseUrl + "get-subscription/", entity); - - return getData(getSubscription); - } - - /** - * Get the notification status for the given test case. - * - * @param testSuiteId - * @param testId - * @return - * @throws IOException - */ - public JsonArray getNotificationStatus(int testSuiteId, int testId) throws IOException { - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("testSuiteId", testSuiteId); - jsonObject.addProperty("testId", testId); - - HttpEntity entity = new StringEntity(jsonObject.toString(), ContentType.APPLICATION_JSON); - - String notificationStatus = request(baseUrl + "get-notification-status/", entity); - - return getData(notificationStatus).get("messages").getAsJsonArray(); - } - - /** - * End the given test suite. - * - * @return - */ - public boolean endTestSuite(int testSuiteId) throws IOException { - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("testSuiteId", testSuiteId); - - HttpEntity entity = new StringEntity(jsonObject.toString(), ContentType.APPLICATION_JSON); - - String endTestSuite = request(baseUrl + "end-test-suite/", entity); - - return getData(endTestSuite).get("success").getAsBoolean(); - } - - /** - * Perform HTTP request and return response. - * - * @param uri - * @return - */ - protected String request(String uri) throws IOException { - return request(uri, null); - } - - /** - * Perform HTTP request and return response. - * - * @param uri - * @return - */ - protected String request(String uri, HttpEntity entity) throws IOException { - return Request.Post(uri).body(entity).execute().handleResponse(httpResponse -> { - String json = EntityUtils.toString(httpResponse.getEntity()); - - if (httpResponse.getStatusLine().getStatusCode() != 200) { - JsonElement root = JsonParser.parseString(json); - JsonObject error = root.getAsJsonObject().get("error").getAsJsonObject(); - - String errorId = error.get("id").getAsString(); - String errorMessage = error.get("message").getAsString(); - - String body = IOUtils.toString(entity.getContent(), UTF_8); - - throw new IllegalStateException("Error while requesting " + uri + " with body " + body + " (" + errorId + ": " + errorMessage); - } - - return json; - }); - } - - /** - * Get the a JSON object of the data in the JSON response. - * - * @param response - */ - protected JsonObject getData(String response) { - JsonElement root = JsonParser.parseString(response); - - return root - .getAsJsonObject() - .get("data") - .getAsJsonObject(); - } -}