From eeecf9620142a4d8e9c153e4d19a75f705d533d7 Mon Sep 17 00:00:00 2001 From: Thomas Lutz Date: Tue, 17 Mar 2020 23:39:37 +0100 Subject: [PATCH 001/467] Cleanup and update dependencies (inspired by https://github.com/matomo-org/piwik-java-tracker/pull/25) - UriBuilder (jersey-common) replaced and dependency removed - Transitive httpcore dependency removed - Runtime dependencies removed - Plugin versions specified - Java11 compatibility - Compiler Target 1.8 --- .gitignore | 4 + pom.xml | 98 ++++++------------- .../org/piwik/java/tracking/PiwikTracker.java | 34 ++++--- .../piwik/java/tracking/PiwikTrackerTest.java | 64 ++++++------ 4 files changed, 87 insertions(+), 113 deletions(-) diff --git a/.gitignore b/.gitignore index 1221a76d..bdf8136c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,7 @@ info # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* + +# IDEA files +*.iml +.idea \ No newline at end of file diff --git a/pom.xml b/pom.xml index ec4a1136..0a42f04d 100644 --- a/pom.xml +++ b/pom.xml @@ -1,16 +1,16 @@ 4.0.0 - + org.piwik.java.tracking piwik-java-tracker - 1.3-SNAPSHOT + 1.4-SNAPSHOT jar - + Piwik Java Tracker Official Java implementation of the Piwik Tracking HTTP API. https://github.com/piwik/piwik-java-tracker - + BSD 3-Clause License @@ -24,6 +24,11 @@ Brett Csorba brett.csorba@gmail.com + + tholu + Thomas Lutz + thomaslutz.de@gmail.com + @@ -46,21 +51,22 @@ UTF-8 - + org.apache.maven.plugins maven-compiler-plugin - 3.3 + 3.8.1 - 1.7 - 1.7 + 1.8 + 1.8 org.apache.maven.plugins maven-source-plugin + 3.2.1 attach-sources @@ -73,6 +79,7 @@ org.apache.maven.plugins maven-javadoc-plugin + 3.2.0 attach-javadocs @@ -85,7 +92,7 @@ org.pitest pitest-maven - 1.1.7 + 1.4.11 org.piwik.java.tracking* @@ -98,12 +105,12 @@ org.eluder.coveralls coveralls-maven-plugin - 4.1.0 + 4.3.0 org.jacoco jacoco-maven-plugin - 0.7.5.201505241946 + 0.8.5 prepare-agent @@ -130,7 +137,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.3 + 1.6.8 true ossrh @@ -142,79 +149,32 @@ - - org.glassfish.hk2 - hk2-api - 2.4.0-b27 - runtime - - - org.glassfish.hk2 - hk2-locator - 2.4.0-b27 - runtime - - - org.glassfish.hk2 - hk2-utils - 2.4.0-b27 - runtime - org.apache.httpcomponents httpclient - 4.4 - - - org.apache.httpcomponents - httpcore - 4.4 - - - org.glassfish.hk2.external - javax.inject - 2.4.0-b27 - runtime - - - org.glassfish.hk2.external - aopalliance-repackaged - 2.4.0-b27 - runtime - - - javaee - javaee-api - 5 - runtime + 4.5.12 org.glassfish javax.json - 1.0.4 - - - org.glassfish.jersey.core - jersey-common - 2.20 - - - org.glassfish.jersey.bundles.repackaged - jersey-guava - 2.20 - runtime + 1.1.4 org.mockito - mockito-all - 1.9.5 + mockito-core + 3.3.3 test junit junit - 4.10 + 4.13 test + + javax.xml.bind + jaxb-api + 2.3.0 + diff --git a/src/main/java/org/piwik/java/tracking/PiwikTracker.java b/src/main/java/org/piwik/java/tracking/PiwikTracker.java index 2e7f9f1b..1f221c3a 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikTracker.java +++ b/src/main/java/org/piwik/java/tracking/PiwikTracker.java @@ -12,6 +12,7 @@ import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.utils.URIBuilder; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClientBuilder; @@ -20,8 +21,9 @@ import javax.json.Json; import javax.json.JsonArrayBuilder; import javax.json.JsonObjectBuilder; -import javax.ws.rs.core.UriBuilder; import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; /** * A class that sends {@link PiwikRequest}s to a specified Piwik server. @@ -31,7 +33,7 @@ public class PiwikTracker{ private static final String AUTH_TOKEN = "token_auth"; private static final String REQUESTS = "requests"; private static final int DEFAULT_TIMEOUT = 5000; - private final UriBuilder uriBuilder; + private final URIBuilder uriBuilder; private final int timeout; private String proxyHost = null; private int proxyPort = 0; @@ -54,7 +56,7 @@ public PiwikTracker(String hostUrl){ * @param timeout the timeout of the sent request in milliseconds */ public PiwikTracker(String hostUrl, int timeout){ - uriBuilder = UriBuilder.fromPath(hostUrl); + uriBuilder = new URIBuilder(URI.create(hostUrl)); this.timeout = timeout; } @@ -72,7 +74,7 @@ public PiwikTracker(String hostUrl, String proxyHost, int proxyPort){ } public PiwikTracker(String hostUrl, String proxyHost, int proxyPort, int timeout){ - uriBuilder = UriBuilder.fromPath(hostUrl); + uriBuilder = new URIBuilder(URI.create(hostUrl)); this.proxyHost = proxyHost; this.proxyPort = proxyPort; this.timeout = timeout; @@ -86,13 +88,18 @@ public PiwikTracker(String hostUrl, String proxyHost, int proxyPort, int timeout */ public HttpResponse sendRequest(PiwikRequest request) throws IOException{ HttpClient client = getHttpClient(); - uriBuilder.replaceQuery(request.getUrlEncodedQueryString()); - HttpGet get = new HttpGet(uriBuilder.build()); + uriBuilder.setCustomQuery(request.getQueryString()); + HttpGet get = null; try { + get = new HttpGet(uriBuilder.build()); return client.execute(get); + } catch (URISyntaxException e) { + throw new IOException(e); } finally { - get.releaseConnection(); + if (get != null) { + get.releaseConnection(); + } } } @@ -135,14 +142,19 @@ public HttpResponse sendBulkRequest(Iterable requests, String auth } HttpClient client = getHttpClient(); - HttpPost post = new HttpPost(uriBuilder.build()); - post.setEntity(new StringEntity(ob.build().toString(), - ContentType.APPLICATION_JSON)); + HttpPost post = null; try { + post = new HttpPost(uriBuilder.build()); + post.setEntity(new StringEntity(ob.build().toString(), + ContentType.APPLICATION_JSON)); return client.execute(post); + } catch (URISyntaxException e) { + throw new IOException(e); } finally { - post.releaseConnection(); + if (post != null) { + post.releaseConnection(); + } } } diff --git a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java b/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java index 59be0503..81ab58ea 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java +++ b/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java @@ -1,6 +1,6 @@ -/* +/* * Piwik Java Tracker - * + * * @link https://github.com/piwik/piwik-java-tracker * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause */ @@ -34,23 +34,23 @@ */ public class PiwikTrackerTest{ PiwikTracker piwikTracker; - + public PiwikTrackerTest(){ } - + @BeforeClass public static void setUpClass(){ } - + @AfterClass public static void tearDownClass(){ } - + @Before public void setUp(){ piwikTracker = spy(new PiwikTracker("http://test.com")); } - + @After public void tearDown(){ } @@ -70,24 +70,23 @@ public void testSendRequest() throws Exception{ PiwikRequest request = mock(PiwikRequest.class); HttpClient client = mock(HttpClient.class); HttpResponse response = mock(HttpResponse.class); - + doReturn(client).when(piwikTracker).getHttpClient(); - doReturn("query").when(request).getUrlEncodedQueryString(); + doReturn("query").when(request).getQueryString(); doReturn(response).when(client).execute(argThat(new CorrectGetRequest("http://test.com?query"))); - + assertEquals(response, piwikTracker.sendRequest(request)); } - - class CorrectGetRequest extends ArgumentMatcher{ + + static class CorrectGetRequest implements ArgumentMatcher { String url; - + public CorrectGetRequest(String url){ this.url = url; } @Override - public boolean matches(Object argument){ - HttpGet get = (HttpGet)argument; + public boolean matches(HttpGet get){ return url.equals(get.getURI().toString()); } } @@ -99,9 +98,9 @@ public boolean matches(Object argument){ public void testSendBulkRequest_Iterable() throws Exception{ List requests = new ArrayList<>(); HttpResponse response = mock(HttpResponse.class); - + doReturn(response).when(piwikTracker).sendBulkRequest(requests, null); - + assertEquals(response, piwikTracker.sendBulkRequest(requests)); } @@ -114,11 +113,11 @@ public void testSendBulkRequest_Iterable_StringTT() throws Exception{ List requests = new ArrayList<>(); HttpClient client = mock(HttpClient.class); PiwikRequest request = mock(PiwikRequest.class); - + doReturn("query").when(request).getQueryString(); - requests.add(request); + requests.add(request); doReturn(client).when(piwikTracker).getHttpClient(); - + piwikTracker.sendBulkRequest(requests, "1"); fail("Exception should have been thrown."); } @@ -132,12 +131,12 @@ public void testSendBulkRequest_Iterable_StringFF() throws Exception{ HttpClient client = mock(HttpClient.class); PiwikRequest request = mock(PiwikRequest.class); HttpResponse response = mock(HttpResponse.class); - + doReturn("query").when(request).getQueryString(); - requests.add(request); + requests.add(request); doReturn(client).when(piwikTracker).getHttpClient(); doReturn(response).when(client).execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"]}"))); - + assertEquals(response, piwikTracker.sendBulkRequest(requests, null)); } @Test @@ -146,26 +145,25 @@ public void testSendBulkRequest_Iterable_StringFT() throws Exception{ HttpClient client = mock(HttpClient.class); PiwikRequest request = mock(PiwikRequest.class); HttpResponse response = mock(HttpResponse.class); - + doReturn("query").when(request).getQueryString(); - requests.add(request); + requests.add(request); doReturn(client).when(piwikTracker).getHttpClient(); doReturn(response).when(client).execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"],\"token_auth\":\"12345678901234567890123456789012\"}"))); - + assertEquals(response, piwikTracker.sendBulkRequest(requests, "12345678901234567890123456789012")); } - - class CorrectPostRequest extends ArgumentMatcher{ + + static class CorrectPostRequest implements ArgumentMatcher { String body; - + public CorrectPostRequest(String body){ this.body = body; } @Override - public boolean matches(Object argument){ + public boolean matches(HttpPost post){ try{ - HttpPost post = (HttpPost)argument; InputStream bais = post.getEntity().getContent(); byte[] bytes = new byte[bais.available()]; bais.read(bytes); @@ -186,7 +184,7 @@ public boolean matches(Object argument){ public void testGetHttpClient(){ assertNotNull(piwikTracker.getHttpClient()); } - + /** * Test of getHttpClient method, of class PiwikTracker, with proxy. */ @@ -195,6 +193,6 @@ public void testGetHttpClientWithProxy(){ piwikTracker = new PiwikTracker("http://test.com", "http://proxy", 8080); HttpClient httpClient = piwikTracker.getHttpClient(); - assertNotNull(piwikTracker.getHttpClient()); + assertNotNull(httpClient); } } From 7a2834c3bb90432b32afebdcd6ea616e453925eb Mon Sep 17 00:00:00 2001 From: Thomas Lutz Date: Tue, 17 Mar 2020 23:44:26 +0100 Subject: [PATCH 002/467] Switch SDK to fix Travis tests --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 58810641..13a50d11 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ install: mvn install -DskipTests=true -Dgpg.skip=true jdk: - - oraclejdk8 + - openjdk8 language: java after_success: - mvn clean test jacoco:report coveralls:report From cfa5da0a8ae354890c62846b2e941b6a1d739308 Mon Sep 17 00:00:00 2001 From: Samuel Albrecht Date: Sat, 21 Mar 2020 14:31:31 +0100 Subject: [PATCH 003/467] dep: added httpasyncclient 4.1.4 to pom.xml --- pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pom.xml b/pom.xml index 0a42f04d..e36c6c23 100644 --- a/pom.xml +++ b/pom.xml @@ -154,6 +154,11 @@ httpclient 4.5.12 + + org.apache.httpcomponents + httpasyncclient + 4.1.4 + org.glassfish javax.json From d5d4393f6e7f84c489d2e971127195a0e0edba5b Mon Sep 17 00:00:00 2001 From: Samuel Albrecht Date: Sat, 21 Mar 2020 14:32:12 +0100 Subject: [PATCH 004/467] add: getHttpAsyncClient() --- .../org/piwik/java/tracking/PiwikTracker.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/main/java/org/piwik/java/tracking/PiwikTracker.java b/src/main/java/org/piwik/java/tracking/PiwikTracker.java index 1f221c3a..c1b1d9dc 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikTracker.java +++ b/src/main/java/org/piwik/java/tracking/PiwikTracker.java @@ -16,6 +16,8 @@ import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; +import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; import org.apache.http.impl.conn.DefaultProxyRoutePlanner; import javax.json.Json; @@ -181,4 +183,28 @@ protected HttpClient getHttpClient(){ return builder.build(); } + + /** + * Get an async HTTP client. With proxy if a proxy is provided in the constructor. + * @return an async HTTP client + */ + protected CloseableHttpAsyncClient getHttpAsyncClient(){ + + HttpAsyncClientBuilder builder = HttpAsyncClientBuilder.create(); + + if(proxyHost != null && proxyPort != 0) { + HttpHost proxy = new HttpHost(proxyHost, proxyPort); + DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy); + builder.setRoutePlanner(routePlanner); + } + + RequestConfig.Builder config = RequestConfig.custom() + .setConnectTimeout(timeout) + .setConnectionRequestTimeout(timeout) + .setSocketTimeout(timeout); + + builder.setDefaultRequestConfig(config.build()); + + return builder.build(); + } } From ff802483ff2fb8a0f526b68900ab7b03476ce57c Mon Sep 17 00:00:00 2001 From: Samuel Albrecht Date: Sat, 21 Mar 2020 14:32:34 +0100 Subject: [PATCH 005/467] add: test for getHttpAsyncClient() --- .../java/org/piwik/java/tracking/PiwikTrackerTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java b/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java index 81ab58ea..407ff001 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java +++ b/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java @@ -14,6 +14,7 @@ import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.junit.After; import org.junit.AfterClass; import static org.junit.Assert.assertEquals; @@ -185,6 +186,14 @@ public void testGetHttpClient(){ assertNotNull(piwikTracker.getHttpClient()); } + /** + * Test of getHttpAsyncClient method, of class PiwikTracker. + */ + @Test + public void testGetHttpAsyncClient(){ + assertNotNull(piwikTracker.getHttpAsyncClient()); + } + /** * Test of getHttpClient method, of class PiwikTracker, with proxy. */ From e7be2d085dc152f4e58e94282b3495e63b70a4d3 Mon Sep 17 00:00:00 2001 From: Samuel Albrecht Date: Sat, 21 Mar 2020 14:32:52 +0100 Subject: [PATCH 006/467] add: sendRequestAsync() --- .../org/piwik/java/tracking/PiwikTracker.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/main/java/org/piwik/java/tracking/PiwikTracker.java b/src/main/java/org/piwik/java/tracking/PiwikTracker.java index c1b1d9dc..c987bf37 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikTracker.java +++ b/src/main/java/org/piwik/java/tracking/PiwikTracker.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.util.concurrent.Future; /** * A class that sends {@link PiwikRequest}s to a specified Piwik server. @@ -105,6 +106,29 @@ public HttpResponse sendRequest(PiwikRequest request) throws IOException{ } } + /** + * Send a request. + * @param request request to send + * @return future with response from this request + * @throws IOException thrown if there was a problem with this connection + */ + public Future sendRequestAsync(PiwikRequest request) throws IOException{ + CloseableHttpAsyncClient client = getHttpAsyncClient(); + uriBuilder.setCustomQuery(request.getQueryString()); + HttpGet get = null; + + try { + get = new HttpGet(uriBuilder.build()); + return client.execute(get,null); + } catch (URISyntaxException e) { + throw new IOException(e); + } finally { + if (get != null) { + get.releaseConnection(); + } + } + } + /** * Send multiple requests in a single HTTP call. More efficient than sending * several individual requests. From ef4e1cf87662907feb1ff776fa6b155f90f571e4 Mon Sep 17 00:00:00 2001 From: Samuel Albrecht Date: Sat, 21 Mar 2020 14:33:02 +0100 Subject: [PATCH 007/467] add: test for sendRequestAsync() --- .../piwik/java/tracking/PiwikTrackerTest.java | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java b/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java index 407ff001..abc2e2d4 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java +++ b/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java @@ -10,6 +10,8 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Future; + import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; @@ -24,6 +26,8 @@ import org.junit.BeforeClass; import org.junit.Test; import org.mockito.ArgumentMatcher; + +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -74,11 +78,32 @@ public void testSendRequest() throws Exception{ doReturn(client).when(piwikTracker).getHttpClient(); doReturn("query").when(request).getQueryString(); - doReturn(response).when(client).execute(argThat(new CorrectGetRequest("http://test.com?query"))); + doReturn(response).when(client) + .execute(argThat(new CorrectGetRequest("http://test.com?query"))); assertEquals(response, piwikTracker.sendRequest(request)); } + /** + * Test of sendRequestAsync method, of class PiwikTracker. + */ + @Test + public void testSendRequestAsync() throws Exception{ + PiwikRequest request = mock(PiwikRequest.class); + CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); + HttpResponse response = mock(HttpResponse.class); + Future future = mock(Future.class); + + doReturn(client).when(piwikTracker).getHttpAsyncClient(); + doReturn("query").when(request).getQueryString(); + doReturn(response).when(future).get(); + doReturn(true).when(future).isDone(); + doReturn(future).when(client) + .execute(argThat(new CorrectGetRequest("http://test.com?query")), any()); + + assertEquals(response, piwikTracker.sendRequestAsync(request).get()); + } + static class CorrectGetRequest implements ArgumentMatcher { String url; From cf87e950958aa33100339b64cc5aa8a38a530832 Mon Sep 17 00:00:00 2001 From: Samuel Albrecht Date: Sat, 21 Mar 2020 14:56:07 +0100 Subject: [PATCH 008/467] add: sendBulkRequestAsync() --- .../org/piwik/java/tracking/PiwikTracker.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/main/java/org/piwik/java/tracking/PiwikTracker.java b/src/main/java/org/piwik/java/tracking/PiwikTracker.java index c987bf37..ddbf83b2 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikTracker.java +++ b/src/main/java/org/piwik/java/tracking/PiwikTracker.java @@ -140,6 +140,17 @@ public HttpResponse sendBulkRequest(Iterable requests) throws IOEx return sendBulkRequest(requests, null); } + /** + * Send multiple requests in a single HTTP call. More efficient than sending + * several individual requests. + * @param requests the requests to send + * @return future with response from these requests + * @throws IOException thrown if there was a problem with this connection + */ + public Future sendBulkRequestAsync(Iterable requests) throws IOException{ + return sendBulkRequestAsync(requests, null); + } + /** * Send multiple requests in a single HTTP call. More efficient than sending * several individual requests. Specify the AuthToken if parameters that require @@ -184,6 +195,50 @@ public HttpResponse sendBulkRequest(Iterable requests, String auth } } + /** + * Send multiple requests in a single HTTP call. More efficient than sending + * several individual requests. Specify the AuthToken if parameters that require + * an auth token is used. + * @param requests the requests to send + * @param authToken specify if any of the parameters use require AuthToken + * @return the response from these requests + * @throws IOException thrown if there was a problem with this connection + */ + public Future sendBulkRequestAsync(Iterable requests, String authToken) throws IOException{ + if (authToken != null && authToken.length() != PiwikRequest.AUTH_TOKEN_LENGTH){ + throw new IllegalArgumentException(authToken+" is not "+PiwikRequest.AUTH_TOKEN_LENGTH+" characters long."); + } + + JsonObjectBuilder ob = Json.createObjectBuilder(); + JsonArrayBuilder ab = Json.createArrayBuilder(); + + for (PiwikRequest request : requests){ + ab.add("?"+request.getQueryString()); + } + + ob.add(REQUESTS, ab); + + if (authToken != null){ + ob.add(AUTH_TOKEN, authToken); + } + + CloseableHttpAsyncClient client = getHttpAsyncClient(); + HttpPost post = null; + + try { + post = new HttpPost(uriBuilder.build()); + post.setEntity(new StringEntity(ob.build().toString(), + ContentType.APPLICATION_JSON)); + return client.execute(post,null); + } catch (URISyntaxException e) { + throw new IOException(e); + } finally { + if (post != null) { + post.releaseConnection(); + } + } + } + /** * Get a HTTP client. With proxy if a proxy is provided in the constructor. * @return a HTTP client From 53d4bfd4bee4f05f5bcd2c294b9a2fe4a82ab5ab Mon Sep 17 00:00:00 2001 From: Samuel Albrecht Date: Sat, 21 Mar 2020 14:56:29 +0100 Subject: [PATCH 009/467] add: tests for sendBulkRequestAsync() --- .../piwik/java/tracking/PiwikTrackerTest.java | 77 ++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java b/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java index abc2e2d4..f611a2c3 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java +++ b/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java @@ -175,11 +175,86 @@ public void testSendBulkRequest_Iterable_StringFT() throws Exception{ doReturn("query").when(request).getQueryString(); requests.add(request); doReturn(client).when(piwikTracker).getHttpClient(); - doReturn(response).when(client).execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"],\"token_auth\":\"12345678901234567890123456789012\"}"))); + doReturn(response).when(client) + .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"],\"token_auth\":\"12345678901234567890123456789012\"}"))); assertEquals(response, piwikTracker.sendBulkRequest(requests, "12345678901234567890123456789012")); } + /** + * Test of sendBulkRequestAsync method, of class PiwikTracker. + */ + @Test + public void testSendBulkRequestAsync_Iterable() throws Exception{ + List requests = new ArrayList<>(); + HttpResponse response = mock(HttpResponse.class); + Future future = mock(Future.class); + doReturn(response).when(future).get(); + doReturn(true).when(future).isDone(); + + doReturn(future).when(piwikTracker).sendBulkRequestAsync(requests, null); + + assertEquals(response, piwikTracker.sendBulkRequestAsync(requests).get()); + } + + /** + * Test of sendBulkRequestAsync method, of class PiwikTracker. + */ + @Test + public void testSendBulkRequestAsync_Iterable_StringTT() throws Exception{ + try{ + List requests = new ArrayList<>(); + CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); + PiwikRequest request = mock(PiwikRequest.class); + + doReturn("query").when(request).getQueryString(); + requests.add(request); + doReturn(client).when(piwikTracker).getHttpAsyncClient(); + + piwikTracker.sendBulkRequestAsync(requests, "1"); + fail("Exception should have been thrown."); + } + catch(IllegalArgumentException e){ + assertEquals("1 is not 32 characters long.", e.getLocalizedMessage()); + } + } + @Test + public void testSendBulkRequestAsync_Iterable_StringFF() throws Exception{ + List requests = new ArrayList<>(); + CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); + PiwikRequest request = mock(PiwikRequest.class); + HttpResponse response = mock(HttpResponse.class); + Future future = mock(Future.class); + doReturn(response).when(future).get(); + doReturn(true).when(future).isDone(); + + doReturn("query").when(request).getQueryString(); + requests.add(request); + doReturn(client).when(piwikTracker).getHttpAsyncClient(); + doReturn(future).when(client) + .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"]}")), any()); + + assertEquals(response, piwikTracker.sendBulkRequestAsync(requests, null).get()); + } + @Test + public void testSendBulkRequestAsync_Iterable_StringFT() throws Exception{ + List requests = new ArrayList<>(); + CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); + PiwikRequest request = mock(PiwikRequest.class); + HttpResponse response = mock(HttpResponse.class); + Future future = mock(Future.class); + doReturn(response).when(future).get(); + doReturn(true).when(future).isDone(); + + doReturn("query").when(request).getQueryString(); + requests.add(request); + doReturn(client).when(piwikTracker).getHttpAsyncClient(); + doReturn(future).when(client) + .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"],\"token_auth\":\"12345678901234567890123456789012\"}")), any()); + + assertEquals(response, piwikTracker.sendBulkRequestAsync(requests, "12345678901234567890123456789012").get()); + } + static class CorrectPostRequest implements ArgumentMatcher { String body; From 976947d7d9d07a3b4eafd1c7b77e94711762c552 Mon Sep 17 00:00:00 2001 From: Samuel Albrecht Date: Sat, 21 Mar 2020 14:56:44 +0100 Subject: [PATCH 010/467] add: deprecation markers for sync methods --- src/main/java/org/piwik/java/tracking/PiwikTracker.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/org/piwik/java/tracking/PiwikTracker.java b/src/main/java/org/piwik/java/tracking/PiwikTracker.java index ddbf83b2..6e82f508 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikTracker.java +++ b/src/main/java/org/piwik/java/tracking/PiwikTracker.java @@ -88,7 +88,9 @@ public PiwikTracker(String hostUrl, String proxyHost, int proxyPort, int timeout * @param request request to send * @return the response from this request * @throws IOException thrown if there was a problem with this connection + * @deprecated use sendRequestAsync instead */ + @Deprecated public HttpResponse sendRequest(PiwikRequest request) throws IOException{ HttpClient client = getHttpClient(); uriBuilder.setCustomQuery(request.getQueryString()); @@ -135,7 +137,9 @@ public Future sendRequestAsync(PiwikRequest request) throws IOExce * @param requests the requests to send * @return the response from these requests * @throws IOException thrown if there was a problem with this connection + * @deprecated use sendBulkRequestAsync instead */ + @Deprecated public HttpResponse sendBulkRequest(Iterable requests) throws IOException{ return sendBulkRequest(requests, null); } @@ -159,7 +163,9 @@ public Future sendBulkRequestAsync(Iterable requests * @param authToken specify if any of the parameters use require AuthToken * @return the response from these requests * @throws IOException thrown if there was a problem with this connection + * @deprecated use sendBulkRequestAsync instead */ + @Deprecated public HttpResponse sendBulkRequest(Iterable requests, String authToken) throws IOException{ if (authToken != null && authToken.length() != PiwikRequest.AUTH_TOKEN_LENGTH){ throw new IllegalArgumentException(authToken+" is not "+PiwikRequest.AUTH_TOKEN_LENGTH+" characters long."); From 373b90cdc0bd51cad4db7f708f3d4db709e37bfa Mon Sep 17 00:00:00 2001 From: Samuel Albrecht Date: Mon, 23 Mar 2020 15:32:15 +0100 Subject: [PATCH 011/467] del: deprecated call to releaseConnection() sa: https://stackoverflow.com/q/30889984/ --- .../org/piwik/java/tracking/PiwikTracker.java | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/main/java/org/piwik/java/tracking/PiwikTracker.java b/src/main/java/org/piwik/java/tracking/PiwikTracker.java index 6e82f508..72842d14 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikTracker.java +++ b/src/main/java/org/piwik/java/tracking/PiwikTracker.java @@ -101,10 +101,6 @@ public HttpResponse sendRequest(PiwikRequest request) throws IOException{ return client.execute(get); } catch (URISyntaxException e) { throw new IOException(e); - } finally { - if (get != null) { - get.releaseConnection(); - } } } @@ -124,10 +120,6 @@ public Future sendRequestAsync(PiwikRequest request) throws IOExce return client.execute(get,null); } catch (URISyntaxException e) { throw new IOException(e); - } finally { - if (get != null) { - get.releaseConnection(); - } } } @@ -194,10 +186,6 @@ public HttpResponse sendBulkRequest(Iterable requests, String auth return client.execute(post); } catch (URISyntaxException e) { throw new IOException(e); - } finally { - if (post != null) { - post.releaseConnection(); - } } } @@ -238,10 +226,6 @@ public Future sendBulkRequestAsync(Iterable requests return client.execute(post,null); } catch (URISyntaxException e) { throw new IOException(e); - } finally { - if (post != null) { - post.releaseConnection(); - } } } From 24bef23a0a50b84043478ca97f5648facbb691bc Mon Sep 17 00:00:00 2001 From: Samuel Albrecht Date: Mon, 23 Mar 2020 15:32:30 +0100 Subject: [PATCH 012/467] add: missing client.start() call --- src/main/java/org/piwik/java/tracking/PiwikTracker.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/piwik/java/tracking/PiwikTracker.java b/src/main/java/org/piwik/java/tracking/PiwikTracker.java index 72842d14..47d330a4 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikTracker.java +++ b/src/main/java/org/piwik/java/tracking/PiwikTracker.java @@ -112,6 +112,7 @@ public HttpResponse sendRequest(PiwikRequest request) throws IOException{ */ public Future sendRequestAsync(PiwikRequest request) throws IOException{ CloseableHttpAsyncClient client = getHttpAsyncClient(); + client.start(); uriBuilder.setCustomQuery(request.getQueryString()); HttpGet get = null; @@ -217,6 +218,7 @@ public Future sendBulkRequestAsync(Iterable requests } CloseableHttpAsyncClient client = getHttpAsyncClient(); + client.start(); HttpPost post = null; try { From d840434245ac18cd7ef7a200b3e30df287ab10c5 Mon Sep 17 00:00:00 2001 From: Samuel Albrecht Date: Mon, 23 Mar 2020 15:35:04 +0100 Subject: [PATCH 013/467] add: test requests with server on localhost --- .../piwik/java/tracking/PiwikTrackerTest.java | 78 ++++++++++++++++++- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java b/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java index f611a2c3..98889adc 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java +++ b/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java @@ -8,10 +8,20 @@ import java.io.IOException; import java.io.InputStream; +import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.Future; +import com.sun.net.httpserver.HttpServer; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpExchange; + +import java.io.OutputStream; +import java.net.InetSocketAddress; + +import org.apache.http.util.EntityUtils; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; @@ -19,9 +29,11 @@ import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.junit.After; import org.junit.AfterClass; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; + import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; @@ -33,12 +45,26 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +// https://stackoverflow.com/a/3732328 +class Handler implements HttpHandler { + @Override + public void handle(HttpExchange exchange) throws IOException { + String response = "OK"; + exchange.sendResponseHeaders(200, response.length()); + OutputStream os = exchange.getResponseBody(); + os.write(response.getBytes()); + os.close(); + } +} + /** * * @author brettcsorba */ public class PiwikTrackerTest{ PiwikTracker piwikTracker; + PiwikTracker localTracker; + HttpServer server; public PiwikTrackerTest(){ } @@ -52,12 +78,24 @@ public static void tearDownClass(){ } @Before - public void setUp(){ + public void setUp() { + // test with mocks piwikTracker = spy(new PiwikTracker("http://test.com")); + + // test with local server + localTracker = new PiwikTracker("http://localhost:8001/test"); + try { + server = HttpServer.create(new InetSocketAddress(8001), 0); + server.createContext("/test", new Handler()); + server.setExecutor(null); // creates a default executor + server.start(); + } catch (IOException ex) { + } } @After - public void tearDown(){ + public void tearDown() { + server.stop(0); } /** @@ -104,6 +142,42 @@ public void testSendRequestAsync() throws Exception{ assertEquals(response, piwikTracker.sendRequestAsync(request).get()); } + /** + * Test sync API with local server + */ + @Test + public void testWithLocalServer() throws Exception { + // one + PiwikRequest request = new PiwikRequest(3, new URL("http://test.com")); + HttpResponse response = localTracker.sendRequest(request); + String msg = EntityUtils.toString(response.getEntity()); + assertEquals("OK", msg); + + // bulk + List requests = Arrays.asList(request); + HttpResponse responseBulk = localTracker.sendBulkRequest(requests); + String msgBulk = EntityUtils.toString(responseBulk.getEntity()); + assertEquals("OK", msgBulk); + } + + /** + * Test async API with local server + */ + @Test + public void testWithLocalServerAsync() throws Exception { + // one + PiwikRequest request = new PiwikRequest(3, new URL("http://test.com")); + HttpResponse response = localTracker.sendRequestAsync(request).get(); + String msg = EntityUtils.toString(response.getEntity()); + assertEquals("OK", msg); + + // bulk + List requests = Arrays.asList(request); + HttpResponse responseBulk = localTracker.sendBulkRequestAsync(requests).get(); + String msgBulk = EntityUtils.toString(responseBulk.getEntity()); + assertEquals("OK", msgBulk); + } + static class CorrectGetRequest implements ArgumentMatcher { String url; From 17f3768032dc0da718eda2f800c9d8d1365649b8 Mon Sep 17 00:00:00 2001 From: Samuel Albrecht Date: Mon, 23 Mar 2020 15:35:31 +0100 Subject: [PATCH 014/467] fmt: idea auto formatting --- .../piwik/java/tracking/PiwikTrackerTest.java | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java b/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java index 98889adc..efc7217e 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java +++ b/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java @@ -58,23 +58,22 @@ public void handle(HttpExchange exchange) throws IOException { } /** - * * @author brettcsorba */ -public class PiwikTrackerTest{ +public class PiwikTrackerTest { PiwikTracker piwikTracker; PiwikTracker localTracker; HttpServer server; - public PiwikTrackerTest(){ + public PiwikTrackerTest() { } @BeforeClass - public static void setUpClass(){ + public static void setUpClass() { } @AfterClass - public static void tearDownClass(){ + public static void tearDownClass() { } @Before @@ -102,14 +101,14 @@ public void tearDown() { * Test of addParameter method, of class PiwikTracker. */ @Test - public void testAddParameter(){ + public void testAddParameter() { } /** * Test of sendRequest method, of class PiwikTracker. */ @Test - public void testSendRequest() throws Exception{ + public void testSendRequest() throws Exception { PiwikRequest request = mock(PiwikRequest.class); HttpClient client = mock(HttpClient.class); HttpResponse response = mock(HttpResponse.class); @@ -126,7 +125,7 @@ public void testSendRequest() throws Exception{ * Test of sendRequestAsync method, of class PiwikTracker. */ @Test - public void testSendRequestAsync() throws Exception{ + public void testSendRequestAsync() throws Exception { PiwikRequest request = mock(PiwikRequest.class); CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); HttpResponse response = mock(HttpResponse.class); @@ -181,12 +180,12 @@ public void testWithLocalServerAsync() throws Exception { static class CorrectGetRequest implements ArgumentMatcher { String url; - public CorrectGetRequest(String url){ + public CorrectGetRequest(String url) { this.url = url; } @Override - public boolean matches(HttpGet get){ + public boolean matches(HttpGet get) { return url.equals(get.getURI().toString()); } } @@ -195,7 +194,7 @@ public boolean matches(HttpGet get){ * Test of sendBulkRequest method, of class PiwikTracker. */ @Test - public void testSendBulkRequest_Iterable() throws Exception{ + public void testSendBulkRequest_Iterable() throws Exception { List requests = new ArrayList<>(); HttpResponse response = mock(HttpResponse.class); @@ -208,8 +207,8 @@ public void testSendBulkRequest_Iterable() throws Exception{ * Test of sendBulkRequest method, of class PiwikTracker. */ @Test - public void testSendBulkRequest_Iterable_StringTT() throws Exception{ - try{ + public void testSendBulkRequest_Iterable_StringTT() throws Exception { + try { List requests = new ArrayList<>(); HttpClient client = mock(HttpClient.class); PiwikRequest request = mock(PiwikRequest.class); @@ -220,13 +219,13 @@ public void testSendBulkRequest_Iterable_StringTT() throws Exception{ piwikTracker.sendBulkRequest(requests, "1"); fail("Exception should have been thrown."); - } - catch(IllegalArgumentException e){ + } catch (IllegalArgumentException e) { assertEquals("1 is not 32 characters long.", e.getLocalizedMessage()); } } + @Test - public void testSendBulkRequest_Iterable_StringFF() throws Exception{ + public void testSendBulkRequest_Iterable_StringFF() throws Exception { List requests = new ArrayList<>(); HttpClient client = mock(HttpClient.class); PiwikRequest request = mock(PiwikRequest.class); @@ -239,8 +238,9 @@ public void testSendBulkRequest_Iterable_StringFF() throws Exception{ assertEquals(response, piwikTracker.sendBulkRequest(requests, null)); } + @Test - public void testSendBulkRequest_Iterable_StringFT() throws Exception{ + public void testSendBulkRequest_Iterable_StringFT() throws Exception { List requests = new ArrayList<>(); HttpClient client = mock(HttpClient.class); PiwikRequest request = mock(PiwikRequest.class); @@ -259,7 +259,7 @@ public void testSendBulkRequest_Iterable_StringFT() throws Exception{ * Test of sendBulkRequestAsync method, of class PiwikTracker. */ @Test - public void testSendBulkRequestAsync_Iterable() throws Exception{ + public void testSendBulkRequestAsync_Iterable() throws Exception { List requests = new ArrayList<>(); HttpResponse response = mock(HttpResponse.class); Future future = mock(Future.class); @@ -275,8 +275,8 @@ public void testSendBulkRequestAsync_Iterable() throws Exception{ * Test of sendBulkRequestAsync method, of class PiwikTracker. */ @Test - public void testSendBulkRequestAsync_Iterable_StringTT() throws Exception{ - try{ + public void testSendBulkRequestAsync_Iterable_StringTT() throws Exception { + try { List requests = new ArrayList<>(); CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); PiwikRequest request = mock(PiwikRequest.class); @@ -287,13 +287,13 @@ public void testSendBulkRequestAsync_Iterable_StringTT() throws Exception{ piwikTracker.sendBulkRequestAsync(requests, "1"); fail("Exception should have been thrown."); - } - catch(IllegalArgumentException e){ + } catch (IllegalArgumentException e) { assertEquals("1 is not 32 characters long.", e.getLocalizedMessage()); } } + @Test - public void testSendBulkRequestAsync_Iterable_StringFF() throws Exception{ + public void testSendBulkRequestAsync_Iterable_StringFF() throws Exception { List requests = new ArrayList<>(); CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); PiwikRequest request = mock(PiwikRequest.class); @@ -310,8 +310,9 @@ public void testSendBulkRequestAsync_Iterable_StringFF() throws Exception{ assertEquals(response, piwikTracker.sendBulkRequestAsync(requests, null).get()); } + @Test - public void testSendBulkRequestAsync_Iterable_StringFT() throws Exception{ + public void testSendBulkRequestAsync_Iterable_StringFT() throws Exception { List requests = new ArrayList<>(); CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); PiwikRequest request = mock(PiwikRequest.class); @@ -332,20 +333,19 @@ public void testSendBulkRequestAsync_Iterable_StringFT() throws Exception{ static class CorrectPostRequest implements ArgumentMatcher { String body; - public CorrectPostRequest(String body){ + public CorrectPostRequest(String body) { this.body = body; } @Override - public boolean matches(HttpPost post){ - try{ + public boolean matches(HttpPost post) { + try { InputStream bais = post.getEntity().getContent(); byte[] bytes = new byte[bais.available()]; bais.read(bytes); String str = new String(bytes); return body.equals(str); - } - catch(IOException e){ + } catch (IOException e) { fail("Exception should not have been throw."); } return false; @@ -356,7 +356,7 @@ public boolean matches(HttpPost post){ * Test of getHttpClient method, of class PiwikTracker. */ @Test - public void testGetHttpClient(){ + public void testGetHttpClient() { assertNotNull(piwikTracker.getHttpClient()); } @@ -364,7 +364,7 @@ public void testGetHttpClient(){ * Test of getHttpAsyncClient method, of class PiwikTracker. */ @Test - public void testGetHttpAsyncClient(){ + public void testGetHttpAsyncClient() { assertNotNull(piwikTracker.getHttpAsyncClient()); } @@ -372,7 +372,7 @@ public void testGetHttpAsyncClient(){ * Test of getHttpClient method, of class PiwikTracker, with proxy. */ @Test - public void testGetHttpClientWithProxy(){ + public void testGetHttpClientWithProxy() { piwikTracker = new PiwikTracker("http://test.com", "http://proxy", 8080); HttpClient httpClient = piwikTracker.getHttpClient(); From 6c530e4580b5c170c1880f5265e8d0c88a7c4f31 Mon Sep 17 00:00:00 2001 From: Thomas Lutz Date: Mon, 23 Mar 2020 20:07:23 +0100 Subject: [PATCH 015/467] Fix some badges in README --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0474c41b..4574ac86 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@ Piwik Java Tracker ================ [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/piwik-java-tracker/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/piwik-java-tracker) ## Code Status -[![Build Status](https://travis-ci.org/piwik/piwik-java-tracker.svg?branch=master)](https://travis-ci.org/piwik/piwik-java-tracker) +[![Build Status](https://travis-ci.org/matomo-org/piwik-java-tracker.svg?branch=master)](https://travis-ci.org/piwik/piwik-java-tracker) [![Coverage Status](https://coveralls.io/repos/piwik/piwik-java-tracker/badge.svg?branch=master&service=github)](https://coveralls.io/github/piwik/piwik-java-tracker?branch=master) -[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/piwik/piwik-java-tracker.svg)](http://isitmaintained.com/project/piwik/piwik-java-tracker "Average time to resolve an issue") -[![Percentage of issues still open](http://isitmaintained.com/badge/open/piwik/piwik-java-tracker.svg)](http://isitmaintained.com/project/piwik/piwik-java-tracker "Percentage of issues still open") +[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/matomo-org/piwik-java-tracker.svg)](http://isitmaintained.com/project/matomo-org/piwik-java-tracker "Average time to resolve an issue") +[![Percentage of issues still open](http://isitmaintained.com/badge/open/matomo-org/piwik-java-tracker.svg)](http://isitmaintained.com/project/matomo-org/piwik-java-tracker "Percentage of issues still open") Official Java implementation of the [Piwik Tracking HTTP API](http://developer.piwik.org/api-reference/tracking-api). @@ -39,7 +39,7 @@ See [Tracking Campaigns](http://piwik.org/docs/tracking-campaigns/) for more inf All HTTP query parameters denoted on the [Piwik Tracking HTTP API](http://developer.piwik.org/api-reference/tracking-api) can be set using the appropriate getters and setters. See PiwikRequest.java for the mappings of the parameters to their corresponding Java getters/setters. -####Some parameters are dependent on the state of other parameters: +Some parameters are dependent on the state of other parameters: EcommerceEnabled must be called before the following parameters are set: EcommerceId and EcommerceRevenue. EcommerceId and EcommerceRevenue must be set before the following parameters are set: EcommerceDiscount, EcommerceItem, EcommerceLastOrderTimestamp, EcommerceShippingCost, EcommerceSubtotal, and EcommerceTax. From 3f44beca0632a64349dc2b3896638d39ed7b4f40 Mon Sep 17 00:00:00 2001 From: Thomas Lutz Date: Mon, 23 Mar 2020 20:16:15 +0100 Subject: [PATCH 016/467] Move Handler class inside Test, some code cleanup --- .../piwik/java/tracking/PiwikTrackerTest.java | 72 ++++++++----------- 1 file changed, 30 insertions(+), 42 deletions(-) diff --git a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java b/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java index efc7217e..e4589287 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java +++ b/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java @@ -6,61 +6,49 @@ */ package org.piwik.java.tracking; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.Future; - -import com.sun.net.httpserver.HttpServer; -import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpExchange; - -import java.io.OutputStream; -import java.net.InetSocketAddress; - -import org.apache.http.util.EntityUtils; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; -import org.junit.After; -import org.junit.AfterClass; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; - -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; +import org.apache.http.util.EntityUtils; +import org.junit.*; import org.mockito.ArgumentMatcher; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Future; + +import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Matchers.argThat; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; - -// https://stackoverflow.com/a/3732328 -class Handler implements HttpHandler { - @Override - public void handle(HttpExchange exchange) throws IOException { - String response = "OK"; - exchange.sendResponseHeaders(200, response.length()); - OutputStream os = exchange.getResponseBody(); - os.write(response.getBytes()); - os.close(); - } -} +import static org.mockito.Mockito.*; /** * @author brettcsorba */ public class PiwikTrackerTest { + // https://stackoverflow.com/a/3732328 + static class Handler implements HttpHandler { + @Override + public void handle(HttpExchange exchange) throws IOException { + String response = "OK"; + exchange.sendResponseHeaders(200, response.length()); + OutputStream os = exchange.getResponseBody(); + os.write(response.getBytes()); + os.close(); + } + } + PiwikTracker piwikTracker; PiwikTracker localTracker; HttpServer server; @@ -153,7 +141,7 @@ public void testWithLocalServer() throws Exception { assertEquals("OK", msg); // bulk - List requests = Arrays.asList(request); + List requests = Collections.singletonList(request); HttpResponse responseBulk = localTracker.sendBulkRequest(requests); String msgBulk = EntityUtils.toString(responseBulk.getEntity()); assertEquals("OK", msgBulk); @@ -171,7 +159,7 @@ public void testWithLocalServerAsync() throws Exception { assertEquals("OK", msg); // bulk - List requests = Arrays.asList(request); + List requests = Collections.singletonList(request); HttpResponse responseBulk = localTracker.sendBulkRequestAsync(requests).get(); String msgBulk = EntityUtils.toString(responseBulk.getEntity()); assertEquals("OK", msgBulk); From cc72f905d75b937ee48b9b6eb5aaf03e56dbdd02 Mon Sep 17 00:00:00 2001 From: Thomas Lutz Date: Tue, 24 Mar 2020 00:47:44 +0100 Subject: [PATCH 017/467] Fix some URLs, comment outdated badge --- README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4574ac86..8425c4fb 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,14 @@ Piwik Java Tracker ================ [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/piwik-java-tracker/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/piwik-java-tracker) ## Code Status -[![Build Status](https://travis-ci.org/matomo-org/piwik-java-tracker.svg?branch=master)](https://travis-ci.org/piwik/piwik-java-tracker) -[![Coverage Status](https://coveralls.io/repos/piwik/piwik-java-tracker/badge.svg?branch=master&service=github)](https://coveralls.io/github/piwik/piwik-java-tracker?branch=master) +[![Build Status](https://travis-ci.org/matomo-org/piwik-java-tracker.svg?branch=master)](https://travis-ci.org/piwik/piwik-java-tracker) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/matomo-org/piwik-java-tracker.svg)](http://isitmaintained.com/project/matomo-org/piwik-java-tracker "Average time to resolve an issue") [![Percentage of issues still open](http://isitmaintained.com/badge/open/matomo-org/piwik-java-tracker.svg)](http://isitmaintained.com/project/matomo-org/piwik-java-tracker "Percentage of issues still open") -Official Java implementation of the [Piwik Tracking HTTP API](http://developer.piwik.org/api-reference/tracking-api). +Official Java implementation of the [Piwik Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api). ## Javadoc -The Javadoc for this project is hosted as a Github page for this repo. The latest Javadoc can be found [here](http://piwik.github.io/piwik-java-tracker/javadoc/HEAD/index.html). Javadoc for the latest and all releases can be found [here](http://piwik.github.io/piwik-java-tracker/javadoc/index.html). +The Javadoc for this project is hosted as a Github page for this repo. The latest Javadoc can be found [here](https://matomo-org.github.io/piwik-java-tracker/javadoc/HEAD/index.html). Javadoc for the latest and all releases can be found [here](https://matomo-org.github.io/piwik-java-tracker/javadoc/index.html). ## Using this API ### Create a Request @@ -35,9 +34,9 @@ Note that if you want to be able to track campaigns using Referrers > Cam ```java URL actionUrl = new URL("http://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore"); ``` -See [Tracking Campaigns](http://piwik.org/docs/tracking-campaigns/) for more information. +See [Tracking Campaigns](https://matomo.org/docs/tracking-campaigns/) for more information. -All HTTP query parameters denoted on the [Piwik Tracking HTTP API](http://developer.piwik.org/api-reference/tracking-api) can be set using the appropriate getters and setters. See PiwikRequest.java for the mappings of the parameters to their corresponding Java getters/setters. +All HTTP query parameters denoted on the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate getters and setters. See PiwikRequest.java for the mappings of the parameters to their corresponding Java getters/setters. Some parameters are dependent on the state of other parameters: EcommerceEnabled must be called before the following parameters are set: EcommerceId and EcommerceRevenue. From b733f6fa9778af465a998a57aaf3ab63efef9f8e Mon Sep 17 00:00:00 2001 From: Thomas Lutz Date: Tue, 24 Mar 2020 00:52:55 +0100 Subject: [PATCH 018/467] Fix Travis Link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8425c4fb..d85b9c49 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Piwik Java Tracker ================ [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/piwik-java-tracker/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/piwik-java-tracker) ## Code Status -[![Build Status](https://travis-ci.org/matomo-org/piwik-java-tracker.svg?branch=master)](https://travis-ci.org/piwik/piwik-java-tracker) +[![Build Status](https://travis-ci.org/matomo-org/piwik-java-tracker.svg?branch=master)](https://travis-ci.org/matomo-org/piwik-java-tracker) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/matomo-org/piwik-java-tracker.svg)](http://isitmaintained.com/project/matomo-org/piwik-java-tracker "Average time to resolve an issue") [![Percentage of issues still open](http://isitmaintained.com/badge/open/matomo-org/piwik-java-tracker.svg)](http://isitmaintained.com/project/matomo-org/piwik-java-tracker "Percentage of issues still open") From 1469403e0322790aa8a8431e0042ac2770534c32 Mon Sep 17 00:00:00 2001 From: Thomas Lutz Date: Thu, 26 Mar 2020 17:05:18 +0100 Subject: [PATCH 019/467] Update artifactID, names and URLs in pom.xml and README --- README.md | 10 +++++----- pom.xml | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index d85b9c49..08c7ac3a 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -Piwik Java Tracker +Matomo Java Tracker ================ [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/piwik-java-tracker/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/piwik-java-tracker) ## Code Status -[![Build Status](https://travis-ci.org/matomo-org/piwik-java-tracker.svg?branch=master)](https://travis-ci.org/matomo-org/piwik-java-tracker) -[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/matomo-org/piwik-java-tracker.svg)](http://isitmaintained.com/project/matomo-org/piwik-java-tracker "Average time to resolve an issue") -[![Percentage of issues still open](http://isitmaintained.com/badge/open/matomo-org/piwik-java-tracker.svg)](http://isitmaintained.com/project/matomo-org/piwik-java-tracker "Percentage of issues still open") +[![Build Status](https://travis-ci.org/matomo-org/matomo-java-tracker.svg?branch=master)](https://travis-ci.org/matomo-org/piwik-java-tracker) +[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/matomo-org/matomo-java-tracker.svg)](http://isitmaintained.com/project/matomo-org/matomo-java-tracker "Average time to resolve an issue") +[![Percentage of issues still open](http://isitmaintained.com/badge/open/matomo-org/matomo-java-tracker.svg)](http://isitmaintained.com/project/matomo-org/matomo-java-tracker "Percentage of issues still open") -Official Java implementation of the [Piwik Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api). +Official Java implementation of the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api). ## Javadoc The Javadoc for this project is hosted as a Github page for this repo. The latest Javadoc can be found [here](https://matomo-org.github.io/piwik-java-tracker/javadoc/HEAD/index.html). Javadoc for the latest and all releases can be found [here](https://matomo-org.github.io/piwik-java-tracker/javadoc/index.html). diff --git a/pom.xml b/pom.xml index e36c6c23..b99f1941 100644 --- a/pom.xml +++ b/pom.xml @@ -3,18 +3,18 @@ 4.0.0 org.piwik.java.tracking - piwik-java-tracker + matomo-java-tracker 1.4-SNAPSHOT jar - Piwik Java Tracker - Official Java implementation of the Piwik Tracking HTTP API. - https://github.com/piwik/piwik-java-tracker + Matomo Java Tracker + Official Java implementation of the Matomo Tracking HTTP API. + https://github.com/matomo-org/matomo-java-tracker BSD 3-Clause License - https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE + https://github.com/matomo-org/matomo-java-tracker/blob/master/LICENSE @@ -32,9 +32,9 @@ - scm:git@github.com:piwik/piwik-java-tracker.git - scm:git@github.com:piwik/piwik-java-tracker.git - git@github.com:piwik/piwik-java-tracker.git + scm:git@github.com:matomo-org/matomo-java-tracker.git + scm:git@github.com:pmatomo-org/matomo-java-tracker.git + git@github.com:matomo-org/matomo-java-tracker.git From 0429bec0241c5b484f033e3fae3c974e895f390c Mon Sep 17 00:00:00 2001 From: Thomas Lutz Date: Fri, 7 Aug 2020 02:07:51 +0200 Subject: [PATCH 020/467] Prepare 1.5 Release - Skip multiple versions due to major changes (Async, Artifact Renaming) - Specify Java Release for maven-javadoc-plugin - Reformat pom.xml - Update mockito-core to latest version --- pom.xml | 129 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 66 insertions(+), 63 deletions(-) diff --git a/pom.xml b/pom.xml index b99f1941..c36836fb 100644 --- a/pom.xml +++ b/pom.xml @@ -1,10 +1,10 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.piwik.java.tracking matomo-java-tracker - 1.4-SNAPSHOT + 1.5 jar Matomo Java Tracker @@ -64,86 +64,89 @@ - org.apache.maven.plugins - maven-source-plugin - 3.2.1 - - - attach-sources - + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + attach-sources + jar - - - + + + - org.apache.maven.plugins - maven-javadoc-plugin - 3.2.0 - - - attach-javadocs - + org.apache.maven.plugins + maven-javadoc-plugin + 3.2.0 + + 8 + + + + attach-javadocs + jar - - - + + + - org.pitest - pitest-maven - 1.4.11 - + org.pitest + pitest-maven + 1.4.11 + org.piwik.java.tracking* org.piwik.java.tracking* - + - org.eluder.coveralls - coveralls-maven-plugin - 4.3.0 + org.eluder.coveralls + coveralls-maven-plugin + 4.3.0 - org.jacoco - jacoco-maven-plugin - 0.8.5 - - - prepare-agent - - prepare-agent - - - + org.jacoco + jacoco-maven-plugin + 0.8.5 + + + prepare-agent + + prepare-agent + + + - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - verify - - sign - - - + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.8 - true - - ossrh - https://oss.sonatype.org/ - false - + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.8 + true + + ossrh + https://oss.sonatype.org/ + false + @@ -167,7 +170,7 @@ org.mockito mockito-core - 3.3.3 + 3.4.6 test From a7162727a3f4e20b1bfe73579c73e3e2f9bfaa9e Mon Sep 17 00:00:00 2001 From: Thomas Lutz Date: Fri, 7 Aug 2020 02:15:35 +0200 Subject: [PATCH 021/467] Comment release in maven-javadoc-plugin to fix Travis build (only works in later JDK versions) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c36836fb..9d87b368 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ maven-javadoc-plugin 3.2.0 - 8 + From 61b7b1f04930592773ad646f0381338cb484b12e Mon Sep 17 00:00:00 2001 From: Thomas Lutz Date: Tue, 11 Aug 2020 09:36:19 +0200 Subject: [PATCH 022/467] Bump snapshot version, update README --- README.md | 27 +++++++++++++-------------- pom.xml | 2 +- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 08c7ac3a..b62ae238 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,16 @@ Matomo Java Tracker ================ -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/piwik-java-tracker/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/piwik-java-tracker) + +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/matomo-java-tracker/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/matomo-java-tracker) ## Code Status -[![Build Status](https://travis-ci.org/matomo-org/matomo-java-tracker.svg?branch=master)](https://travis-ci.org/matomo-org/piwik-java-tracker) -[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/matomo-org/matomo-java-tracker.svg)](http://isitmaintained.com/project/matomo-org/matomo-java-tracker "Average time to resolve an issue") -[![Percentage of issues still open](http://isitmaintained.com/badge/open/matomo-org/matomo-java-tracker.svg)](http://isitmaintained.com/project/matomo-org/matomo-java-tracker "Percentage of issues still open") +[![Build Status](https://travis-ci.org/matomo-org/matomo-java-tracker.svg?branch=master)](https://travis-ci.org/matomo-org/matomo-java-tracker) +[![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/matomo-org/matomo-java-tracker.svg)](https://isitmaintained.com/project/matomo-org/matomo-java-tracker "Average time to resolve an issue") +[![Percentage of issues still open](https://isitmaintained.com/badge/open/matomo-org/matomo-java-tracker.svg)](https://isitmaintained.com/project/matomo-org/matomo-java-tracker "Percentage of issues still open") Official Java implementation of the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api). ## Javadoc -The Javadoc for this project is hosted as a Github page for this repo. The latest Javadoc can be found [here](https://matomo-org.github.io/piwik-java-tracker/javadoc/HEAD/index.html). Javadoc for the latest and all releases can be found [here](https://matomo-org.github.io/piwik-java-tracker/javadoc/index.html). +The Javadoc for this project is hosted as a Github page for this repo. The latest Javadoc can be found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/HEAD/index.html). Javadoc for the latest and all releases can be found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/index.html). ## Using this API ### Create a Request @@ -30,20 +31,20 @@ responseAsImage = false; Overwrite these properties as desired. -Note that if you want to be able to track campaigns using Referrers > Campaigns, you must add the correct URL parameters to your actionUrl. For example, +Note that if you want to be able to track campaigns using Referrers > Campaigns, you must add the correct URL parameters to your actionUrl. For example, ```java URL actionUrl = new URL("http://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore"); ``` See [Tracking Campaigns](https://matomo.org/docs/tracking-campaigns/) for more information. -All HTTP query parameters denoted on the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate getters and setters. See PiwikRequest.java for the mappings of the parameters to their corresponding Java getters/setters. +All HTTP query parameters denoted on the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate getters and setters. See PiwikRequest.java for the mappings of the parameters to their corresponding Java getters/setters. Some parameters are dependent on the state of other parameters: -EcommerceEnabled must be called before the following parameters are set: EcommerceId and EcommerceRevenue. +EcommerceEnabled must be called before the following parameters are set: EcommerceId and EcommerceRevenue. EcommerceId and EcommerceRevenue must be set before the following parameters are set: EcommerceDiscount, EcommerceItem, EcommerceLastOrderTimestamp, EcommerceShippingCost, EcommerceSubtotal, and EcommerceTax. -AuthToken must be set before the following parameters are set: VisitorCity, VisitorCountry, VisitorIp, VisitorLatitude, VisitorLongitude, and VisitorRegion. +AuthToken must be set before the following parameters are set: VisitorCity, VisitorCountry, VisitorIp, VisitorLatitude, VisitorLongitude, and VisitorRegion. ### Sending Requests Create a PiwikTracker through @@ -90,20 +91,18 @@ mvn clean ## Contribute Have a fantastic feature idea? Spot a bug? We would absolutely love for you to contribute to this project! Please feel free to: + * Fork this project * Create a feature branch from the dev branch * Write awesome code that does awesome things -* Write awesome test that test your awesome code +* Write awesome test to test your awesome code * Verify that everything is working as it should by running mvn test. If everything passes, you may want to make sure that your tests are covering everything you think they are! Run mvn org.pitest:pitest-maven:mutationCoverage to find out! * Commit this code to your repository * Submit a pull request from your branch to our dev branch and let us know why you made the changes you did * We'll take a look at your request and work to get it integrated with the repo! -## Contact -brett.csorba@gmail.com - ## License -This software is released under the BSD 3-Clause license. See [LICENSE](LICENSE). +This software is released under the BSD 3-Clause license. See [LICENSE](LICENSE). ## Copyright Copyright (c) 2015 General Electric Company. All rights reserved. diff --git a/pom.xml b/pom.xml index 9d87b368..c426a6d6 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker - 1.5 + 1.6-SNAPSHOT jar Matomo Java Tracker From 14bb2e8feccd573569f647aaaa026470a660e549 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 22:54:51 +0000 Subject: [PATCH 023/467] Bump junit from 4.13 to 4.13.1 Bumps [junit](https://github.com/junit-team/junit4) from 4.13 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.13...r4.13.1) Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c426a6d6..1dffc97e 100644 --- a/pom.xml +++ b/pom.xml @@ -176,7 +176,7 @@ junit junit - 4.13 + 4.13.1 test From e28c23cb10783cc4825e2ca268a8cf2f880eaeb2 Mon Sep 17 00:00:00 2001 From: Daniel Nordhoff-Vergien Date: Tue, 8 Dec 2020 14:10:43 +0100 Subject: [PATCH 024/467] #42 - reuse http async client to prevent resource leak --- .../org/piwik/java/tracking/PiwikTracker.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/piwik/java/tracking/PiwikTracker.java b/src/main/java/org/piwik/java/tracking/PiwikTracker.java index 47d330a4..3b258cf5 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikTracker.java +++ b/src/main/java/org/piwik/java/tracking/PiwikTracker.java @@ -38,8 +38,9 @@ public class PiwikTracker{ private static final int DEFAULT_TIMEOUT = 5000; private final URIBuilder uriBuilder; private final int timeout; - private String proxyHost = null; - private int proxyPort = 0; + private final String proxyHost; + private final int proxyPort; + private CloseableHttpAsyncClient asyncClient = null; /** * Creates a tracker that will send {@link PiwikRequest}s to the specified @@ -61,6 +62,8 @@ public PiwikTracker(String hostUrl){ public PiwikTracker(String hostUrl, int timeout){ uriBuilder = new URIBuilder(URI.create(hostUrl)); this.timeout = timeout; + this.proxyHost = null; + this.proxyPort = 0; } /** @@ -261,6 +264,10 @@ protected HttpClient getHttpClient(){ */ protected CloseableHttpAsyncClient getHttpAsyncClient(){ + if(asyncClient != null) { + return asyncClient; + } + HttpAsyncClientBuilder builder = HttpAsyncClientBuilder.create(); if(proxyHost != null && proxyPort != 0) { @@ -276,6 +283,8 @@ protected CloseableHttpAsyncClient getHttpAsyncClient(){ builder.setDefaultRequestConfig(config.build()); - return builder.build(); + asyncClient = builder.build(); + + return asyncClient; } } From bd59b0ee58cec7d6ec082061c2a46c327bcde71d Mon Sep 17 00:00:00 2001 From: Thomas Lutz Date: Sun, 27 Dec 2020 02:08:37 +0100 Subject: [PATCH 025/467] Update dependencies - Bump plugins and other dependencies (httpclient, mockito-core) - Fix typo in README - Replace deprecated usages of new Integer(), new Long(), new Double() --- README.md | 2 +- pom.xml | 8 ++-- .../piwik/java/tracking/PiwikRequestTest.java | 42 +++++++++---------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index b62ae238..7b2ec7fe 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ If you have multiple requests to wish to track, it may be more efficient to send ```java HttpResponse response = tracker.sendBulkRequest(requests); ``` -If some of the parameters that you've specified in the bulk request requre AuthToken to be set, this can also be set in the bulk request through +If some of the parameters that you've specified in the bulk request require AuthToken to be set, this can also be set in the bulk request through ```java HttpResponse response = tracker.sendBulkRequest(requests, authToken); ``` diff --git a/pom.xml b/pom.xml index 1dffc97e..7c4d9665 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,7 @@ org.pitest pitest-maven - 1.4.11 + 1.5.2 org.piwik.java.tracking* @@ -113,7 +113,7 @@ org.jacoco jacoco-maven-plugin - 0.8.5 + 0.8.6 prepare-agent @@ -155,7 +155,7 @@ org.apache.httpcomponents httpclient - 4.5.12 + 4.5.13 org.apache.httpcomponents @@ -170,7 +170,7 @@ org.mockito mockito-core - 3.4.6 + 3.6.28 test diff --git a/src/test/java/org/piwik/java/tracking/PiwikRequestTest.java b/src/test/java/org/piwik/java/tracking/PiwikRequestTest.java index 162a5210..e599028a 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikRequestTest.java +++ b/src/test/java/org/piwik/java/tracking/PiwikRequestTest.java @@ -40,7 +40,7 @@ public void tearDown(){ @Test public void testConstructor() throws Exception{ request = new PiwikRequest(3, new URL("http://test.com")); - assertEquals(new Integer(3), request.getSiteId()); + assertEquals(Integer.valueOf(3), request.getSiteId()); assertTrue(request.getRequired()); assertEquals(new URL("http://test.com"), request.getActionUrl()); assertNotNull(request.getVisitorId()); @@ -66,7 +66,7 @@ public void testActionName(){ @Test public void testActionTime(){ request.setActionTime(1000L); - assertEquals(new Long(1000L), request.getActionTime()); + assertEquals(Long.valueOf(1000L), request.getActionTime()); } /** @@ -242,7 +242,7 @@ public void testContentTarget() throws Exception{ @Test public void testCurrentHour(){ request.setCurrentHour(1); - assertEquals(new Integer(1), request.getCurrentHour()); + assertEquals(Integer.valueOf(1), request.getCurrentHour()); } /** @@ -251,7 +251,7 @@ public void testCurrentHour(){ @Test public void testCurrentMinute(){ request.setCurrentMinute(2); - assertEquals(new Integer(2), request.getCurrentMinute()); + assertEquals(Integer.valueOf(2), request.getCurrentMinute()); } /** @@ -260,7 +260,7 @@ public void testCurrentMinute(){ @Test public void testCurrentSecond(){ request.setCurrentSecond(3); - assertEquals(new Integer(3), request.getCurrentSecond()); + assertEquals(Integer.valueOf(3), request.getCurrentSecond()); } /** @@ -391,7 +391,7 @@ public void testDownloadUrl() throws Exception{ @Test public void testEnableEcommerce(){ request.enableEcommerce(); - assertEquals(new Integer(0), request.getGoalId()); + assertEquals(Integer.valueOf(0), request.getGoalId()); } /** @@ -483,7 +483,7 @@ public void testEcommerceDiscountT(){ request.setEcommerceRevenue(2.0); request.setEcommerceDiscount(1.0); - assertEquals(new Double(1.0), request.getEcommerceDiscount()); + assertEquals(Double.valueOf(1.0), request.getEcommerceDiscount()); } @Test public void testEcommerceDiscountTE(){ @@ -585,7 +585,7 @@ public void testEcommerceLastOrderTimestampT(){ request.setEcommerceRevenue(2.0); request.setEcommerceLastOrderTimestamp(1000L); - assertEquals(new Long(1000L), request.getEcommerceLastOrderTimestamp()); + assertEquals(Long.valueOf(1000L), request.getEcommerceLastOrderTimestamp()); } @Test public void testEcommerceLastOrderTimestampTE(){ @@ -614,7 +614,7 @@ public void testEcommerceRevenueT(){ request.setEcommerceId("1"); request.setEcommerceRevenue(20.0); - assertEquals(new Double(20.0), request.getEcommerceRevenue()); + assertEquals(Double.valueOf(20.0), request.getEcommerceRevenue()); } @Test public void testEcommerceRevenueTE(){ @@ -644,7 +644,7 @@ public void testEcommerceShippingCostT(){ request.setEcommerceRevenue(2.0); request.setEcommerceShippingCost(20.0); - assertEquals(new Double(20.0), request.getEcommerceShippingCost()); + assertEquals(Double.valueOf(20.0), request.getEcommerceShippingCost()); } @Test public void testEcommerceShippingCostTE(){ @@ -674,7 +674,7 @@ public void testEcommerceSubtotalT(){ request.setEcommerceRevenue(2.0); request.setEcommerceSubtotal(20.0); - assertEquals(new Double(20.0), request.getEcommerceSubtotal()); + assertEquals(Double.valueOf(20.0), request.getEcommerceSubtotal()); } @Test public void testEcommerceSubtotalTE(){ @@ -704,7 +704,7 @@ public void testEcommerceTaxT(){ request.setEcommerceRevenue(2.0); request.setEcommerceTax(20.0); - assertEquals(new Double(20.0), request.getEcommerceTax()); + assertEquals(Double.valueOf(20.0), request.getEcommerceTax()); } @Test public void testEcommerceTaxTE(){ @@ -779,7 +779,7 @@ public void testEventValue(){ @Test public void testGoalId(){ request.setGoalId(1); - assertEquals(new Integer(1), request.getGoalId()); + assertEquals(Integer.valueOf(1), request.getGoalId()); } /** @@ -801,7 +801,7 @@ public void testGoalRevenueTF(){ request.setGoalId(1); request.setGoalRevenue(20.0); - assertEquals(new Double(20.0), request.getGoalRevenue()); + assertEquals(Double.valueOf(20.0), request.getGoalRevenue()); } @Test public void testGoalRevenueF(){ @@ -1150,7 +1150,7 @@ public void testSearchResultsCountTT(){ public void testSearchResultsCountTF(){ request.setSearchQuery("query"); request.setSearchResultsCount(100L); - assertEquals(new Long(100L), request.getSearchResultsCount()); + assertEquals(Long.valueOf(100L), request.getSearchResultsCount()); } @Test public void testSearchResultsCountF(){ @@ -1164,7 +1164,7 @@ public void testSearchResultsCountF(){ @Test public void testSiteId(){ request.setSiteId(2); - assertEquals(new Integer(2), request.getSiteId()); + assertEquals(Integer.valueOf(2), request.getSiteId()); } /** @@ -1319,7 +1319,7 @@ public void testVisitorCustomIdF(){ @Test public void testVisitorFirstVisitTimestamp(){ request.setVisitorFirstVisitTimestamp(1000L); - assertEquals(new Long(1000L), request.getVisitorFirstVisitTimestamp()); + assertEquals(Long.valueOf(1000L), request.getVisitorFirstVisitTimestamp()); } /** @@ -1392,7 +1392,7 @@ public void testVisitorIpF(){ public void testVisitorLatitudeT(){ request.setAuthToken("12345678901234567890123456789012"); request.setVisitorLatitude(10.5); - assertEquals(new Double(10.5), request.getVisitorLatitude()); + assertEquals(Double.valueOf(10.5), request.getVisitorLatitude()); } @Test public void testVisitorLatitudeTE(){ @@ -1418,7 +1418,7 @@ public void testVisitorLatitudeF(){ public void testVisitorLongitudeT(){ request.setAuthToken("12345678901234567890123456789012"); request.setVisitorLongitude(20.5); - assertEquals(new Double(20.5), request.getVisitorLongitude()); + assertEquals(Double.valueOf(20.5), request.getVisitorLongitude()); } @Test public void testVisitorLongitudeTE(){ @@ -1443,7 +1443,7 @@ public void testVisitorLongitudeF(){ @Test public void testVisitorPreviousVisitTimestamp(){ request.setVisitorPreviousVisitTimestamp(1000L); - assertEquals(new Long(1000L), request.getVisitorPreviousVisitTimestamp()); + assertEquals(Long.valueOf(1000L), request.getVisitorPreviousVisitTimestamp()); } /** @@ -1480,7 +1480,7 @@ public void testVisitorRegionF(){ @Test public void testVisitorVisitCount(){ request.setVisitorVisitCount(100); - assertEquals(new Integer(100), request.getVisitorVisitCount()); + assertEquals(Integer.valueOf(100), request.getVisitorVisitCount()); } /** From 6822e53ac20dbaac8dfaa8afa6b19ecb43396a88 Mon Sep 17 00:00:00 2001 From: Thomas Lutz Date: Sun, 27 Dec 2020 02:16:04 +0100 Subject: [PATCH 026/467] Reformat Code - Use IntelliJ IDEA standard Java Code Formatting --- README.md | 94 +- pom.xml | 2 +- .../piwik/java/tracking/CustomVariable.java | 72 +- .../java/tracking/CustomVariableList.java | 127 +- .../piwik/java/tracking/EcommerceItem.java | 291 +- .../org/piwik/java/tracking/PiwikDate.java | 95 +- .../piwik/java/tracking/PiwikJsonArray.java | 109 +- .../piwik/java/tracking/PiwikJsonObject.java | 192 +- .../org/piwik/java/tracking/PiwikLocale.java | 76 +- .../org/piwik/java/tracking/PiwikRequest.java | 3999 +++++++++-------- .../org/piwik/java/tracking/PiwikTracker.java | 477 +- .../java/tracking/CustomVariableListTest.java | 140 +- .../java/tracking/CustomVariableTest.java | 70 +- .../java/tracking/EcommerceItemTest.java | 221 +- .../piwik/java/tracking/PiwikDateTest.java | 84 +- .../java/tracking/PiwikJsonArrayTest.java | 107 +- .../java/tracking/PiwikJsonObjectTest.java | 114 +- .../piwik/java/tracking/PiwikLocaleTest.java | 101 +- .../piwik/java/tracking/PiwikRequestTest.java | 3118 ++++++------- .../piwik/java/tracking/PiwikTrackerTest.java | 652 +-- 20 files changed, 5206 insertions(+), 4935 deletions(-) diff --git a/README.md b/README.md index 7b2ec7fe..1ac02a3b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,9 @@ Matomo Java Tracker ================ [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/matomo-java-tracker/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/matomo-java-tracker) + ## Code Status + [![Build Status](https://travis-ci.org/matomo-org/matomo-java-tracker.svg?branch=master)](https://travis-ci.org/matomo-org/matomo-java-tracker) [![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/matomo-org/matomo-java-tracker.svg)](https://isitmaintained.com/project/matomo-org/matomo-java-tracker "Average time to resolve an issue") [![Percentage of issues still open](https://isitmaintained.com/badge/open/matomo-org/matomo-java-tracker.svg)](https://isitmaintained.com/project/matomo-org/matomo-java-tracker "Percentage of issues still open") @@ -10,99 +12,141 @@ Matomo Java Tracker Official Java implementation of the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api). ## Javadoc -The Javadoc for this project is hosted as a Github page for this repo. The latest Javadoc can be found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/HEAD/index.html). Javadoc for the latest and all releases can be found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/index.html). + +The Javadoc for this project is hosted as a Github page for this repo. The latest Javadoc can be +found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/HEAD/index.html). Javadoc for the latest and all +releases can be found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/index.html). ## Using this API + ### Create a Request -Each PiwikRequest represents an action the user has taken that you want tracked by your Piwik server. Create a PiwikRequest through + +Each PiwikRequest represents an action the user has taken that you want tracked by your Piwik server. Create a +PiwikRequest through + ```java -PiwikRequest request = new PiwikRequest(siteId, actionUrl); +PiwikRequest request=new PiwikRequest(siteId,actionUrl); ``` The following parameters are also enabled by default: ```java -required = true; -visitorId = random 16 character hex string; -randomValue = random 20 character hex string; -apiVersion = 1; -responseAsImage = false; +required=true; + visitorId=random 16character hex string; + randomValue=random 20character hex string; + apiVersion=1; + responseAsImage=false; ``` Overwrite these properties as desired. -Note that if you want to be able to track campaigns using Referrers > Campaigns, you must add the correct URL parameters to your actionUrl. For example, +Note that if you want to be able to track campaigns using Referrers > Campaigns, you must add the correct +URL parameters to your actionUrl. For example, + ```java -URL actionUrl = new URL("http://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore"); +URL actionUrl=new URL("http://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore"); ``` -See [Tracking Campaigns](https://matomo.org/docs/tracking-campaigns/) for more information. -All HTTP query parameters denoted on the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate getters and setters. See PiwikRequest.java for the mappings of the parameters to their corresponding Java getters/setters. +See [Tracking Campaigns](https://matomo.org/docs/tracking-campaigns/) for more information. + +All HTTP query parameters denoted on +the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate +getters and setters. See PiwikRequest.java for the mappings of the parameters to their corresponding +Java getters/setters. Some parameters are dependent on the state of other parameters: -EcommerceEnabled must be called before the following parameters are set: EcommerceId and EcommerceRevenue. +EcommerceEnabled must be called before the following parameters are set: EcommerceId and +EcommerceRevenue. -EcommerceId and EcommerceRevenue must be set before the following parameters are set: EcommerceDiscount, EcommerceItem, EcommerceLastOrderTimestamp, EcommerceShippingCost, EcommerceSubtotal, and EcommerceTax. +EcommerceId and EcommerceRevenue must be set before the following parameters are +set: EcommerceDiscount, EcommerceItem, EcommerceLastOrderTimestamp, +EcommerceShippingCost, EcommerceSubtotal, and EcommerceTax. -AuthToken must be set before the following parameters are set: VisitorCity, VisitorCountry, VisitorIp, VisitorLatitude, VisitorLongitude, and VisitorRegion. +AuthToken must be set before the following parameters are set: VisitorCity, +VisitorCountry, VisitorIp, VisitorLatitude, VisitorLongitude, and VisitorRegion +. ### Sending Requests + Create a PiwikTracker through + ```java -PiwikTracker tracker = new PiwikTracker(hostUrl); +PiwikTracker tracker=new PiwikTracker(hostUrl); ``` -where hostUrl is the url endpoint of the Piwik server. Usually in the format http://your-piwik-domain.tld/piwik.php. + +where hostUrl is the url endpoint of the Piwik server. Usually in the +format http://your-piwik-domain.tld/piwik.php. To send a single request, call + ```java -HttpResponse response = tracker.sendRequest(request); +HttpResponse response=tracker.sendRequest(request); ``` -If you have multiple requests to wish to track, it may be more efficient to send them in a single HTTP call. To do this, send a bulk request. Place your requests in an Iterable data structure and call +If you have multiple requests to wish to track, it may be more efficient to send them in a single HTTP call. To do this, +send a bulk request. Place your requests in an Iterable data structure and call + ```java -HttpResponse response = tracker.sendBulkRequest(requests); +HttpResponse response=tracker.sendBulkRequest(requests); ``` -If some of the parameters that you've specified in the bulk request require AuthToken to be set, this can also be set in the bulk request through + +If some of the parameters that you've specified in the bulk request require AuthToken to be set, this can also be set in +the bulk request through + ```java -HttpResponse response = tracker.sendBulkRequest(requests, authToken); +HttpResponse response=tracker.sendBulkRequest(requests,authToken); ``` + ## Install + This project can be tested and built by calling + ```shell mvn package ``` + The built jars and javadoc can be found in target Test this project using + ```shell mvn test ``` -This project also supports [Pitest](http://pitest.org/) mutation testing. This report can be generated by calling +This project also supports [Pitest](http://pitest.org/) mutation testing. This report can be generated by calling + ```shell mvn org.pitest:pitest-maven:mutationCoverage ``` + and will produce an html report at target/pit-reports/YYYYMMDDHHMI Clean this project using + ```shell mvn clean ``` ## Contribute -Have a fantastic feature idea? Spot a bug? We would absolutely love for you to contribute to this project! Please feel free to: + +Have a fantastic feature idea? Spot a bug? We would absolutely love for you to contribute to this project! Please feel +free to: * Fork this project * Create a feature branch from the dev branch * Write awesome code that does awesome things * Write awesome test to test your awesome code -* Verify that everything is working as it should by running mvn test. If everything passes, you may want to make sure that your tests are covering everything you think they are! Run mvn org.pitest:pitest-maven:mutationCoverage to find out! +* Verify that everything is working as it should by running mvn test. If everything passes, you may + want to make sure that your tests are covering everything you think they are! Run mvn org.pitest: + pitest-maven:mutationCoverage to find out! * Commit this code to your repository * Submit a pull request from your branch to our dev branch and let us know why you made the changes you did * We'll take a look at your request and work to get it integrated with the repo! ## License + This software is released under the BSD 3-Clause license. See [LICENSE](LICENSE). ## Copyright + Copyright (c) 2015 General Electric Company. All rights reserved. diff --git a/pom.xml b/pom.xml index 7c4d9665..0aa7c2b3 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ maven-javadoc-plugin 3.2.0 - + diff --git a/src/main/java/org/piwik/java/tracking/CustomVariable.java b/src/main/java/org/piwik/java/tracking/CustomVariable.java index 9300ef62..392b7473 100644 --- a/src/main/java/org/piwik/java/tracking/CustomVariable.java +++ b/src/main/java/org/piwik/java/tracking/CustomVariable.java @@ -1,6 +1,6 @@ -/* +/* * Piwik Java Tracker - * + * * @link https://github.com/piwik/piwik-java-tracker * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause */ @@ -8,41 +8,45 @@ /** * A user defined custom variable. + * * @author brettcsorba */ -public final class CustomVariable{ - private final String key; - private final String value; - - /** - * Create a new CustomVariable - * @param key the key of this CustomVariable - * @param value the value of this CustomVariable - */ - public CustomVariable(String key, String value){ - if (key == null){ - throw new NullPointerException("Key cannot be null."); - } - if (value == null){ - throw new NullPointerException("Value cannot be null."); - } - this.key = key; - this.value = value; - } +public final class CustomVariable { + private final String key; + private final String value; - /** - * Get the key of this CustomVariable - * @return the key of this CustomVariable - */ - public String getKey() { - return key; + /** + * Create a new CustomVariable + * + * @param key the key of this CustomVariable + * @param value the value of this CustomVariable + */ + public CustomVariable(String key, String value) { + if (key == null) { + throw new NullPointerException("Key cannot be null."); } - - /** - * Get the value of this CustomVariable - * @return the value of this CustomVariable - */ - public String getValue() { - return value; + if (value == null) { + throw new NullPointerException("Value cannot be null."); } + this.key = key; + this.value = value; + } + + /** + * Get the key of this CustomVariable + * + * @return the key of this CustomVariable + */ + public String getKey() { + return key; + } + + /** + * Get the value of this CustomVariable + * + * @return the value of this CustomVariable + */ + public String getValue() { + return value; + } } diff --git a/src/main/java/org/piwik/java/tracking/CustomVariableList.java b/src/main/java/org/piwik/java/tracking/CustomVariableList.java index 2392030c..a8ade45f 100644 --- a/src/main/java/org/piwik/java/tracking/CustomVariableList.java +++ b/src/main/java/org/piwik/java/tracking/CustomVariableList.java @@ -6,93 +6,92 @@ */ package org.piwik.java.tracking; +import javax.json.Json; +import javax.json.JsonArrayBuilder; +import javax.json.JsonObjectBuilder; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; -import javax.json.Json; -import javax.json.JsonArrayBuilder; -import javax.json.JsonObjectBuilder; /** - * * @author brettcsorba */ -class CustomVariableList{ - private final Map map = new HashMap<>(); - - void add(CustomVariable cv){ - boolean found = false; - for (Entry entry : map.entrySet()){ - if (entry.getValue().getKey().equals(cv.getKey())){ - map.put(entry.getKey(), cv); - found = true; - } - } - if (!found){ - int i = 1; - while (map.containsKey(i)) { - ++i; - } +class CustomVariableList { + private final Map map = new HashMap<>(); - map.put(i, cv); - } + void add(CustomVariable cv) { + boolean found = false; + for (Entry entry : map.entrySet()) { + if (entry.getValue().getKey().equals(cv.getKey())) { + map.put(entry.getKey(), cv); + found = true; + } } + if (!found) { + int i = 1; + while (map.containsKey(i)) { + ++i; + } - void add(CustomVariable cv, int index){ - if (index <= 0){ - throw new IllegalArgumentException("Index must be greater than 0."); - } - map.put(index, cv); + map.put(i, cv); } + } - CustomVariable get(int index){ - if (index <= 0){ - throw new IllegalArgumentException("Index must be greater than 0."); - } - return map.get(index); + void add(CustomVariable cv, int index) { + if (index <= 0) { + throw new IllegalArgumentException("Index must be greater than 0."); } + map.put(index, cv); + } - String get(String key){ - Iterator> i = map.entrySet().iterator(); - while (i.hasNext()){ - CustomVariable value = i.next().getValue(); - if (value.getKey().equals(key)){ - return value.getValue(); - } - } - return null; + CustomVariable get(int index) { + if (index <= 0) { + throw new IllegalArgumentException("Index must be greater than 0."); } + return map.get(index); + } - void remove(int index){ - map.remove(index); + String get(String key) { + Iterator> i = map.entrySet().iterator(); + while (i.hasNext()) { + CustomVariable value = i.next().getValue(); + if (value.getKey().equals(key)) { + return value.getValue(); + } } + return null; + } - void remove(String key){ - Iterator> i = map.entrySet().iterator(); - while (i.hasNext()){ - Entry entry = i.next(); - if (entry.getValue().getKey().equals(key)){ - i.remove(); - } - } - } + void remove(int index) { + map.remove(index); + } - boolean isEmpty(){ - return map.isEmpty(); + void remove(String key) { + Iterator> i = map.entrySet().iterator(); + while (i.hasNext()) { + Entry entry = i.next(); + if (entry.getValue().getKey().equals(key)) { + i.remove(); + } } + } - @Override - public String toString(){ - JsonObjectBuilder ob = Json.createObjectBuilder(); + boolean isEmpty() { + return map.isEmpty(); + } - for (Entry entry : map.entrySet()){ - JsonArrayBuilder ab = Json.createArrayBuilder(); - ab.add(entry.getValue().getKey()); - ab.add(entry.getValue().getValue()); - ob.add(entry.getKey().toString(), ab); - } + @Override + public String toString() { + JsonObjectBuilder ob = Json.createObjectBuilder(); - return ob.build().toString(); + for (Entry entry : map.entrySet()) { + JsonArrayBuilder ab = Json.createArrayBuilder(); + ab.add(entry.getValue().getKey()); + ab.add(entry.getValue().getValue()); + ob.add(entry.getKey().toString(), ab); } + + return ob.build().toString(); + } } diff --git a/src/main/java/org/piwik/java/tracking/EcommerceItem.java b/src/main/java/org/piwik/java/tracking/EcommerceItem.java index b8225311..616d2e63 100644 --- a/src/main/java/org/piwik/java/tracking/EcommerceItem.java +++ b/src/main/java/org/piwik/java/tracking/EcommerceItem.java @@ -1,6 +1,6 @@ -/* +/* * Piwik Java Tracker - * + * * @link https://github.com/piwik/piwik-java-tracker * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause */ @@ -13,142 +13,157 @@ /** * Represents an item in an ecommerce order. - * + * * @author brettcsorba */ -public class EcommerceItem implements JsonValue{ - private String sku; - private String name; - private String category; - private Double price; - private Integer quantity; - - /** - * Construct an EcommerceItem from its sku, name, category, price, and - * quantity of the order. - * @param sku the item's sku - * @param name the item's name - * @param category the item's category - * @param price the item's price - * @param quantity the quantity of this item in this order - */ - public EcommerceItem(String sku, String name, String category, Double price, Integer quantity){ - this.sku = sku; - this.name = name; - this.category = category; - this.price = price; - this.quantity = quantity; - } - - /** - * Get an item's sku. - * @return the item's sku - */ - public String getSku(){ - return sku; - } - - /** - * Set an item's sku. - * @param sku the sku to set - */ - public void setSku(String sku){ - this.sku = sku; - } - - /** - * Get an item's name. - * @return the item's name - */ - public String getName(){ - return name; - } - - /** - * Set an item's name. - * @param name the name to set - */ - public void setName(String name){ - this.name = name; - } - - /** - * Get an item's category. - * @return an item's category - */ - public String getCategory(){ - return category; - } - - /** - * Set an item's category. - * @param category the category to set - */ - public void setCategory(String category){ - this.category = category; - } - - /** - * Get an item's price. - * @return an item's price - */ - public Double getPrice(){ - return price; - } - - /** - * Set an item's price. - * @param price the price to set - */ - public void setPrice(Double price){ - this.price = price; - } - - /** - * Get the quantity of this item in this order. - * @return the quantity of this item in the order - */ - public Integer getQuantity(){ - return quantity; - } - - /** - * Set the quantity of this item in this order - * @param quantity the quantity of this item to set - */ - public void setQuantity(Integer quantity){ - this.quantity = quantity; - } - - /** - * Get the JSON value type of EcommerceItem. - * @return ValueType.ARRAY - */ - @Override - public ValueType getValueType(){ - return ValueType.ARRAY; - } - - /** - * Returns the value of this EcommerceItem as a JSON Array string. - * @return this as a JSON array string - */ - @Override - public String toString(){ - return toJsonArray().toString(); - } - /** - * Returns the value of this EcommerceItem as a JsonArray. - * @return this as a JsonArray - */ - JsonArray toJsonArray(){ - JsonArrayBuilder ab = Json.createArrayBuilder(); - ab.add(sku); - ab.add(name); - ab.add(category); - ab.add(price); - ab.add(quantity); - - return ab.build(); - } +public class EcommerceItem implements JsonValue { + private String sku; + private String name; + private String category; + private Double price; + private Integer quantity; + + /** + * Construct an EcommerceItem from its sku, name, category, price, and + * quantity of the order. + * + * @param sku the item's sku + * @param name the item's name + * @param category the item's category + * @param price the item's price + * @param quantity the quantity of this item in this order + */ + public EcommerceItem(String sku, String name, String category, Double price, Integer quantity) { + this.sku = sku; + this.name = name; + this.category = category; + this.price = price; + this.quantity = quantity; + } + + /** + * Get an item's sku. + * + * @return the item's sku + */ + public String getSku() { + return sku; + } + + /** + * Set an item's sku. + * + * @param sku the sku to set + */ + public void setSku(String sku) { + this.sku = sku; + } + + /** + * Get an item's name. + * + * @return the item's name + */ + public String getName() { + return name; + } + + /** + * Set an item's name. + * + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * Get an item's category. + * + * @return an item's category + */ + public String getCategory() { + return category; + } + + /** + * Set an item's category. + * + * @param category the category to set + */ + public void setCategory(String category) { + this.category = category; + } + + /** + * Get an item's price. + * + * @return an item's price + */ + public Double getPrice() { + return price; + } + + /** + * Set an item's price. + * + * @param price the price to set + */ + public void setPrice(Double price) { + this.price = price; + } + + /** + * Get the quantity of this item in this order. + * + * @return the quantity of this item in the order + */ + public Integer getQuantity() { + return quantity; + } + + /** + * Set the quantity of this item in this order + * + * @param quantity the quantity of this item to set + */ + public void setQuantity(Integer quantity) { + this.quantity = quantity; + } + + /** + * Get the JSON value type of EcommerceItem. + * + * @return ValueType.ARRAY + */ + @Override + public ValueType getValueType() { + return ValueType.ARRAY; + } + + /** + * Returns the value of this EcommerceItem as a JSON Array string. + * + * @return this as a JSON array string + */ + @Override + public String toString() { + return toJsonArray().toString(); + } + + /** + * Returns the value of this EcommerceItem as a JsonArray. + * + * @return this as a JsonArray + */ + JsonArray toJsonArray() { + JsonArrayBuilder ab = Json.createArrayBuilder(); + ab.add(sku); + ab.add(name); + ab.add(category); + ab.add(price); + ab.add(quantity); + + return ab.build(); + } } diff --git a/src/main/java/org/piwik/java/tracking/PiwikDate.java b/src/main/java/org/piwik/java/tracking/PiwikDate.java index bb10053e..66fe885c 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikDate.java +++ b/src/main/java/org/piwik/java/tracking/PiwikDate.java @@ -1,6 +1,6 @@ -/* +/* * Piwik Java Tracker - * + * * @link https://github.com/piwik/piwik-java-tracker * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause */ @@ -12,51 +12,54 @@ /** * A datetime object that will return the datetime in the format {@code yyyy-MM-dd hh:mm:ss}. - * + * * @author brettcsorba */ -public class PiwikDate extends Date{ - SimpleDateFormat format; - - /** - * Allocates a Date object and initializes it so that it represents the time - * at which it was allocated, measured to the nearest millisecond. - */ - public PiwikDate(){ - super(); - format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - format.setTimeZone(TimeZone.getTimeZone("UTC")); - } +public class PiwikDate extends Date { + SimpleDateFormat format; - /** - * Allocates a Date object and initializes it to represent the specified number - * of milliseconds since the standard base time known as "the epoch", namely - * January 1, 1970, 00:00:00 GMT. - * @param date the milliseconds since January 1, 1970, 00:00:00 GMT. - */ - public PiwikDate(long date){ - super(date); - format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - format.setTimeZone(TimeZone.getTimeZone("UTC")); - } - - /** - * Sets the time zone of the String that will be returned by {@link #toString()}. - * Defaults to UTC. - * @param zone the TimeZone to set - */ - public void setTimeZone(TimeZone zone){ - format.setTimeZone(zone); - } - - /** - * Converts this PiwikDate object to a String of the form:
- *
- * {@code yyyy-MM-dd hh:mm:ss}. - * @return a string representation of this PiwikDate - */ - @Override - public String toString(){ - return format.format(this); - } + /** + * Allocates a Date object and initializes it so that it represents the time + * at which it was allocated, measured to the nearest millisecond. + */ + public PiwikDate() { + super(); + format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + format.setTimeZone(TimeZone.getTimeZone("UTC")); + } + + /** + * Allocates a Date object and initializes it to represent the specified number + * of milliseconds since the standard base time known as "the epoch", namely + * January 1, 1970, 00:00:00 GMT. + * + * @param date the milliseconds since January 1, 1970, 00:00:00 GMT. + */ + public PiwikDate(long date) { + super(date); + format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + format.setTimeZone(TimeZone.getTimeZone("UTC")); + } + + /** + * Sets the time zone of the String that will be returned by {@link #toString()}. + * Defaults to UTC. + * + * @param zone the TimeZone to set + */ + public void setTimeZone(TimeZone zone) { + format.setTimeZone(zone); + } + + /** + * Converts this PiwikDate object to a String of the form:
+ *
+ * {@code yyyy-MM-dd hh:mm:ss}. + * + * @return a string representation of this PiwikDate + */ + @Override + public String toString() { + return format.format(this); + } } diff --git a/src/main/java/org/piwik/java/tracking/PiwikJsonArray.java b/src/main/java/org/piwik/java/tracking/PiwikJsonArray.java index 01177c43..bdf9e6ee 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikJsonArray.java +++ b/src/main/java/org/piwik/java/tracking/PiwikJsonArray.java @@ -1,69 +1,72 @@ -/* +/* * Piwik Java Tracker - * + * * @link https://github.com/piwik/piwik-java-tracker * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause */ package org.piwik.java.tracking; -import java.util.ArrayList; -import java.util.List; import javax.json.Json; import javax.json.JsonArrayBuilder; import javax.json.JsonValue; +import java.util.ArrayList; +import java.util.List; /** * Object representing a JSON array required by some Piwik query parameters. - * + * * @author brettcsorba */ -public class PiwikJsonArray{ - List list = new ArrayList<>(); - - /** - * Get the value stored at the specified index. - * @param index the index of the value to return - * @return the value stored at the specified index - */ - public JsonValue get(int index){ - return list.get(index); - } - - /** - * Add a value to the end of this array. - * @param value value to add to the end of the array - */ - public void add(JsonValue value){ - list.add(value); - } - - /** - * Set the value at the specified index to the specified value. - * @param index the index of the value to set - * @param value the value to set at the specified index - */ - public void set(int index, JsonValue value){ - list.set(index, value); - } - - /** - * Returns a JSON encoded array string representing this object. - * @return returns the current array as a JSON encode string - */ - @Override - public String toString(){ - JsonArrayBuilder ab = Json.createArrayBuilder(); - - for (int x = 0; x < list.size(); ++x){ - JsonValue value = list.get(x); - if (value instanceof EcommerceItem){ - ab.add(((EcommerceItem)value).toJsonArray()); - } - else{ - ab.add(value); - } - } - - return ab.build().toString(); +public class PiwikJsonArray { + List list = new ArrayList<>(); + + /** + * Get the value stored at the specified index. + * + * @param index the index of the value to return + * @return the value stored at the specified index + */ + public JsonValue get(int index) { + return list.get(index); + } + + /** + * Add a value to the end of this array. + * + * @param value value to add to the end of the array + */ + public void add(JsonValue value) { + list.add(value); + } + + /** + * Set the value at the specified index to the specified value. + * + * @param index the index of the value to set + * @param value the value to set at the specified index + */ + public void set(int index, JsonValue value) { + list.set(index, value); + } + + /** + * Returns a JSON encoded array string representing this object. + * + * @return returns the current array as a JSON encode string + */ + @Override + public String toString() { + JsonArrayBuilder ab = Json.createArrayBuilder(); + + for (int x = 0; x < list.size(); ++x) { + JsonValue value = list.get(x); + if (value instanceof EcommerceItem) { + ab.add(((EcommerceItem) value).toJsonArray()); + } else { + ab.add(value); + } } + + return ab.build().toString(); + } } diff --git a/src/main/java/org/piwik/java/tracking/PiwikJsonObject.java b/src/main/java/org/piwik/java/tracking/PiwikJsonObject.java index 505a3dde..0b83a651 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikJsonObject.java +++ b/src/main/java/org/piwik/java/tracking/PiwikJsonObject.java @@ -1,111 +1,117 @@ -/* +/* * Piwik Java Tracker - * + * * @link https://github.com/piwik/piwik-java-tracker * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause */ package org.piwik.java.tracking; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Map.Entry; import javax.json.Json; import javax.json.JsonArrayBuilder; import javax.json.JsonObjectBuilder; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; /** - * Object representing the custom variable array required by some + * Object representing the custom variable array required by some * Piwik query parameters. An array is represented by an object that has - * the index of the entry as the key (1 indexed) and a 2 entry array representing + * the index of the entry as the key (1 indexed) and a 2 entry array representing * a custom key and custom value as the value. - * + * * @author brettcsorba */ @Deprecated -public class PiwikJsonObject{ - Map map = new LinkedHashMap<>(); - - /** - * Gets the custom value stored at this custom key. - * @param key key used to lookup value - * @return value stored at specified key, null if not present - */ - public String get(String key){ - return map.get(key); - } - - /** - * Returns true if this object contains no custom key-value pairs. - * @return true if this object contains no custom key-value pairs - */ - public boolean isEmpty(){ - return size() == 0; - } - - /** - * Puts a custom value at this custom key. - * @param key key to store value at - * @param value value to store at specified key - * @return previous value stored at key if present, null otherwise - */ - public String put(String key, String value){ - return map.put(key, value); - } - - /** - * Removes the custom value stored at this custom key. - * @param key key used to lookup value to remove - * @return the value that was removed, null if no value was there to remove - */ - public String remove(String key){ - return map.remove(key); - } - - /** - * Returns the number of custom key-value pairs. - * @return the number of custom key-value pairs - */ - public int size(){ - return map.size(); - } - - /** - * Produces the JSON string representing this object.
- *
- * For example, if the following values were put into the object
- *
- * {@code ("key1", "value1") } and {@code ("key2", "value2") }
- *
- * {@code {"1": ["key1", "value1"], "2": ["key2": "value2"]} }
- *
- * would be produced. The produced JSON will be ordered according to the - * order the values were put in. Removing an object will cause the values - * to backfill accordingly.
- *
- * For example, if the following values were put into the object
- *
- * {@code ("key1", "value1")}, {@code ("key2", "value2")}, and {@code ("key3", "value3")}
- *
- * and {@code ("key2", "value2") } was then removed, then
- *
- * {@code {"1": ["key1", "value1"], "2": ["key3": "value3"]} }
- *
- * would be produced. - *
- * @return the JSON string representation of this object - */ - @Override - public String toString(){ - JsonObjectBuilder ob = Json.createObjectBuilder(); - - int x = 1; - for (Entry entry : map.entrySet()){ - JsonArrayBuilder ab = Json.createArrayBuilder(); - ab.add(entry.getKey()); - ab.add(entry.getValue()); - ob.add(Integer.toString(x++), ab); - } - - return ob.build().toString(); +public class PiwikJsonObject { + Map map = new LinkedHashMap<>(); + + /** + * Gets the custom value stored at this custom key. + * + * @param key key used to lookup value + * @return value stored at specified key, null if not present + */ + public String get(String key) { + return map.get(key); + } + + /** + * Returns true if this object contains no custom key-value pairs. + * + * @return true if this object contains no custom key-value pairs + */ + public boolean isEmpty() { + return size() == 0; + } + + /** + * Puts a custom value at this custom key. + * + * @param key key to store value at + * @param value value to store at specified key + * @return previous value stored at key if present, null otherwise + */ + public String put(String key, String value) { + return map.put(key, value); + } + + /** + * Removes the custom value stored at this custom key. + * + * @param key key used to lookup value to remove + * @return the value that was removed, null if no value was there to remove + */ + public String remove(String key) { + return map.remove(key); + } + + /** + * Returns the number of custom key-value pairs. + * + * @return the number of custom key-value pairs + */ + public int size() { + return map.size(); + } + + /** + * Produces the JSON string representing this object.
+ *
+ * For example, if the following values were put into the object
+ *
+ * {@code ("key1", "value1") } and {@code ("key2", "value2") }
+ *
+ * {@code {"1": ["key1", "value1"], "2": ["key2": "value2"]} }
+ *
+ * would be produced. The produced JSON will be ordered according to the + * order the values were put in. Removing an object will cause the values + * to backfill accordingly.
+ *
+ * For example, if the following values were put into the object
+ *
+ * {@code ("key1", "value1")}, {@code ("key2", "value2")}, and {@code ("key3", "value3")}
+ *
+ * and {@code ("key2", "value2") } was then removed, then
+ *
+ * {@code {"1": ["key1", "value1"], "2": ["key3": "value3"]} }
+ *
+ * would be produced. + *
+ * + * @return the JSON string representation of this object + */ + @Override + public String toString() { + JsonObjectBuilder ob = Json.createObjectBuilder(); + + int x = 1; + for (Entry entry : map.entrySet()) { + JsonArrayBuilder ab = Json.createArrayBuilder(); + ab.add(entry.getKey()); + ab.add(entry.getValue()); + ob.add(Integer.toString(x++), ab); } + + return ob.build().toString(); + } } diff --git a/src/main/java/org/piwik/java/tracking/PiwikLocale.java b/src/main/java/org/piwik/java/tracking/PiwikLocale.java index 45f14338..a573026a 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikLocale.java +++ b/src/main/java/org/piwik/java/tracking/PiwikLocale.java @@ -1,6 +1,6 @@ -/* +/* * Piwik Java Tracker - * + * * @link https://github.com/piwik/piwik-java-tracker * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause */ @@ -10,42 +10,46 @@ /** * Object representing a locale required by some Piwik query parameters. - * + * * @author brettcsorba */ -public class PiwikLocale{ - private Locale locale; - - /** - * Create this PiwikLocale from a Locale. - * @param locale the locale to create this object from - */ - public PiwikLocale(Locale locale){ - this.locale = locale; - } +public class PiwikLocale { + private Locale locale; - /** - * Gets the locale. - * @return the locale - */ - public Locale getLocale(){ - return locale; - } + /** + * Create this PiwikLocale from a Locale. + * + * @param locale the locale to create this object from + */ + public PiwikLocale(Locale locale) { + this.locale = locale; + } - /** - * Sets the locale. - * @param locale the locale to set - */ - public void setLocale(Locale locale){ - this.locale = locale; - } - - /** - * Returns the locale's lowercase country code. - * @return the locale's lowercase country code - */ - @Override - public String toString(){ - return locale.getCountry().toLowerCase(); - } + /** + * Gets the locale. + * + * @return the locale + */ + public Locale getLocale() { + return locale; + } + + /** + * Sets the locale. + * + * @param locale the locale to set + */ + public void setLocale(Locale locale) { + this.locale = locale; + } + + /** + * Returns the locale's lowercase country code. + * + * @return the locale's lowercase country code + */ + @Override + public String toString() { + return locale.getCountry().toLowerCase(); + } } diff --git a/src/main/java/org/piwik/java/tracking/PiwikRequest.java b/src/main/java/org/piwik/java/tracking/PiwikRequest.java index bf27a70e..1a07283a 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikRequest.java +++ b/src/main/java/org/piwik/java/tracking/PiwikRequest.java @@ -1,11 +1,14 @@ -/* +/* * Piwik Java Tracker - * + * * @link https://github.com/piwik/piwik-java-tracker * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause */ package org.piwik.java.tracking; +import javax.json.JsonValue; +import javax.xml.bind.DatatypeConverter; +import javax.xml.bind.TypeConstraintException; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLEncoder; @@ -17,1942 +20,2096 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Random; -import javax.json.JsonValue; -import javax.xml.bind.DatatypeConverter; -import javax.xml.bind.TypeConstraintException; /** * A class that implements the * Piwik Tracking HTTP API. These requests can be sent using {@link PiwikTracker}. + * * @author brettcsorba */ -public class PiwikRequest{ - public static final int ID_LENGTH = 16; - public static final int AUTH_TOKEN_LENGTH = 32; - - private static final String ACTION_NAME = "action_name"; - private static final String ACTION_TIME = "gt_ms"; - private static final String ACTION_URL = "url"; - private static final String API_VERSION = "apiv"; - private static final String AUTH_TOKEN = "token_auth"; - private static final String CAMPAIGN_KEYWORD = "_rck"; - private static final String CAMPAIGN_NAME = "_rcn"; - private static final String CHARACTER_SET = "cs"; - private static final String CONTENT_INTERACTION = "c_i"; - private static final String CONTENT_NAME = "c_n"; - private static final String CONTENT_PIECE = "c_p"; - private static final String CONTENT_TARGET = "c_t"; - private static final String CURRENT_HOUR = "h"; - private static final String CURRENT_MINUTE = "m"; - private static final String CURRENT_SECOND = "s"; - private static final String DEVICE_RESOLUTION = "res"; - private static final String DOWNLOAD_URL = "download"; - private static final String ECOMMERCE_DISCOUNT = "ec_dt"; - private static final String ECOMMERCE_ID = "ec_id"; - private static final String ECOMMERCE_ITEMS = "ec_items"; - private static final String ECOMMERCE_LAST_ORDER_TIMESTAMP = "_ects"; - private static final String ECOMMERCE_REVENUE = "revenue"; - private static final String ECOMMERCE_SHIPPING_COST = "ec_sh"; - private static final String ECOMMERCE_SUBTOTAL = "ec_st"; - private static final String ECOMMERCE_TAX = "ec_tx"; - private static final String EVENT_ACTION = "e_a"; - private static final String EVENT_CATEGORY = "e_c"; - private static final String EVENT_NAME = "e_n"; - private static final String EVENT_VALUE = "e_v"; - private static final String HEADER_ACCEPT_LANGUAGE = "lang"; - private static final String GOAL_ID = "idgoal"; - private static final String GOAL_REVENUE = "revenue"; - private static final String HEADER_USER_AGENT = "ua"; - private static final String NEW_VISIT = "new_visit"; - private static final String OUTLINK_URL = "link"; - private static final String PAGE_CUSTOM_VARIABLE = "cvar"; - private static final String PLUGIN_DIRECTOR = "dir"; - private static final String PLUGIN_FLASH = "fla"; - private static final String PLUGIN_GEARS = "gears"; - private static final String PLUGIN_JAVA = "java"; - private static final String PLUGIN_PDF = "pdf"; - private static final String PLUGIN_QUICKTIME = "qt"; - private static final String PLUGIN_REAL_PLAYER = "realp"; - private static final String PLUGIN_SILVERLIGHT = "ag"; - private static final String PLUGIN_WINDOWS_MEDIA = "wma"; - private static final String RANDOM_VALUE = "rand"; - private static final String REFERRER_URL = "urlref"; - private static final String REQUEST_DATETIME = "cdt"; - private static final String REQUIRED = "rec"; - private static final String RESPONSE_AS_IMAGE = "send_image"; - private static final String SEARCH_CATEGORY = "search_cat"; - private static final String SEARCH_QUERY = "search"; - private static final String SEARCH_RESULTS_COUNT = "search_count"; - private static final String SITE_ID = "idsite"; - private static final String TRACK_BOT_REQUESTS = "bots"; - private static final String VISIT_CUSTOM_VARIABLE = "_cvar"; - private static final String USER_ID = "uid"; - private static final String VISITOR_CITY = "city"; - private static final String VISITOR_COUNTRY = "country"; - private static final String VISITOR_CUSTOM_ID = "cid"; - private static final String VISITOR_FIRST_VISIT_TIMESTAMP = "_idts"; - private static final String VISITOR_ID = "_id"; - private static final String VISITOR_IP = "cip"; - private static final String VISITOR_LATITUDE = "lat"; - private static final String VISITOR_LONGITUDE = "long"; - private static final String VISITOR_PREVIOUS_VISIT_TIMESTAMP = "_viewts"; - private static final String VISITOR_REGION = "region"; - private static final String VISITOR_VISIT_COUNT = "_idvc"; - - private static final int RANDOM_VALUE_LENGTH = 20; - private static final long REQUEST_DATETIME_AUTH_LIMIT = 14400000L; - - private final Map parameters = new HashMap<>(); - private final Map customTrackingParameters = new HashMap<>(); - - /** - * Create a new request from the id of the site being tracked and the full - * url for the current action. This constructor also sets: - *
-     * {@code
-     * Required = true
-     * Visior Id = random 16 character hex string
-     * Random Value = random 20 character hex string
-     * API version = 1
-     * Response as Image = false
-     * }
-     * 
- * Overwrite these values yourself as desired. - * @param siteId the id of the website we're tracking a visit/action for - * @param actionUrl the full URL for the current action - */ - public PiwikRequest(Integer siteId, URL actionUrl){ - setParameter(SITE_ID, siteId); - setBooleanParameter(REQUIRED, true); - setParameter(ACTION_URL, actionUrl); - setParameter(VISITOR_ID, getRandomHexString(ID_LENGTH)); - setParameter(RANDOM_VALUE, getRandomHexString(RANDOM_VALUE_LENGTH)); - setParameter(API_VERSION, "1"); - setBooleanParameter(RESPONSE_AS_IMAGE, false); - } - - /** - * Get the title of the action being tracked - * @return the title of the action being tracked - */ - - public String getActionName(){ - return (String)getParameter(ACTION_NAME); - } - - /** - * Set the title of the action being tracked. It is possible to - * use slashes / - * to set one or several categories for this action. - * For example, Help / Feedback - * will create the Action Feedback in the category Help. - * - * @param actionName the title of the action to set. A null value will remove this parameter - */ - public void setActionName(String actionName){ - setParameter(ACTION_NAME, actionName); - } - - /** - * Get the amount of time it took the server to generate this action, in milliseconds. - * @return the amount of time - */ - public Long getActionTime(){ - return (Long)getParameter(ACTION_TIME); - } - - /** - * Set the amount of time it took the server to generate this action, in milliseconds. - * This value is used to process the - * Page speed report - * Avg. generation time column in the Page URL and Page Title reports, - * as well as a site wide running average of the speed of your server. - * @param actionTime the amount of time to set. A null value will remove this parameter - */ - public void setActionTime(Long actionTime){ - setParameter(ACTION_TIME, actionTime); - } - - /** - * Get the full URL for the current action. - * @return the full URL - * @throws TypeConstraintException if the stored Action URL is a string - */ - public URL getActionUrl(){ - return returnAsUrl(ACTION_URL, "Action URL", "getActionUrlAsString"); - } - - /** - * Get the full URL for the current action. - * @return the full URL - * @throws TypeConstraintException if the stored Action URL is a URL - */ - public String getActionUrlAsString(){ - return returnAsString(ACTION_URL, "Action URL", "getActionUrl"); - } - - /** - * Set the full URL for the current action. - * @param actionUrl the full URL to set. A null value will remove this parameter - */ - public void setActionUrl(URL actionUrl){ - setParameter(ACTION_URL, actionUrl); - } - - /** - * Set the full URL for the current action. - * @param actionUrl the full URL to set. A null value will remove this parameter - */ - public void setActionUrlWithString(String actionUrl){ - setParameter(ACTION_URL, actionUrl); - } - - /** - * Get the api version - * @return the api version - */ - public String getApiVersion(){ - return (String)getParameter(API_VERSION); - } - - /** - * Set the api version to use (currently always set to 1) - * @param apiVersion the api version to set. A null value will remove this parameter - */ - public void setApiVersion(String apiVersion){ - setParameter(API_VERSION, apiVersion); - } - - /** - * Get the authorization key. - * @return the authorization key - */ - public String getAuthToken(){ - return (String)getParameter(AUTH_TOKEN); - } - - /** - * Set the {@value #AUTH_TOKEN_LENGTH} character authorization key used to authenticate the API request. - * @param authToken the authorization key to set. A null value will remove this parameter - */ - public void setAuthToken(String authToken){ - if (authToken != null && authToken.length() != AUTH_TOKEN_LENGTH){ - throw new IllegalArgumentException(authToken+" is not "+AUTH_TOKEN_LENGTH+" characters long."); - } - setParameter(AUTH_TOKEN, authToken); - } +public class PiwikRequest { + public static final int ID_LENGTH = 16; + public static final int AUTH_TOKEN_LENGTH = 32; - /** - * Verifies that AuthToken has been set for this request. Will throw an - * {@link IllegalStateException} if not. - */ - public void verifyAuthTokenSet(){ - if (getAuthToken() == null){ - throw new IllegalStateException("AuthToken must be set before this value can be set."); - } - } - - /** - * Get the campaign keyword - * @return the campaign keyword - */ - public String getCampaignKeyword(){ - return (String)getParameter(CAMPAIGN_KEYWORD); - } - - /** - * Set the Campaign Keyword (see - * Tracking Campaigns). - * Used to populate the Referrers > Campaigns report (clicking on a - * campaign loads all keywords for this campaign). Note: this parameter - * will only be used for the first pageview of a visit. - * @param campaignKeyword the campaign keyword to set. A null value will remove this parameter - */ - public void setCampaignKeyword(String campaignKeyword){ - setParameter(CAMPAIGN_KEYWORD, campaignKeyword); - } - - /** - * Get the campaign name - * @return the campaign name - */ - public String getCampaignName(){ - return (String)getParameter(CAMPAIGN_NAME); - } - - /** - * Set the Campaign Name (see - * Tracking Campaigns). - * Used to populate the Referrers > Campaigns report. Note: this parameter - * will only be used for the first pageview of a visit. - * @param campaignName the campaign name to set. A null value will remove this parameter - */ - public void setCampaignName(String campaignName){ - setParameter(CAMPAIGN_NAME, campaignName); - } - - /** - * Get the charset of the page being tracked - * @return the charset - */ - public Charset getCharacterSet(){ - return (Charset)getParameter(CHARACTER_SET); - } - - /** - * The charset of the page being tracked. Specify the charset if the data - * you send to Piwik is encoded in a different character set than the default - * utf-8. - * @param characterSet the charset to set. A null value will remove this parameter - */ - public void setCharacterSet(Charset characterSet){ - setParameter(CHARACTER_SET, characterSet); - } - - /** - * Get the name of the interaction with the content - * @return the name of the interaction - */ - public String getContentInteraction(){ - return (String)getParameter(CONTENT_INTERACTION); - } - - /** - * Set the name of the interaction with the content. For instance a 'click'. - * @param contentInteraction the name of the interaction to set. A null value will remove this parameter - */ - public void setContentInteraction(String contentInteraction){ - setParameter(CONTENT_INTERACTION, contentInteraction); - } - - /** - * Get the name of the content - * @return the name - */ - public String getContentName(){ - return (String)getParameter(CONTENT_NAME); - } - - /** - * Set the name of the content. For instance 'Ad Foo Bar'. - * @param contentName the name to set. A null value will remove this parameter - */ - public void setContentName(String contentName){ - setParameter(CONTENT_NAME, contentName); - } - - /** - * Get the content piece. - * @return the content piece. - */ - public String getContentPiece(){ - return (String)getParameter(CONTENT_PIECE); - } - - /** - * Set the actual content piece. For instance the path to an image, video, audio, any text. - * @param contentPiece the content piece to set. A null value will remove this parameter - */ - public void setContentPiece(String contentPiece){ - setParameter(CONTENT_PIECE, contentPiece); - } - - /** - * Get the content target - * @return the target - * @throws TypeConstraintException if the stored Content Target is a string - */ - public URL getContentTarget(){ - return returnAsUrl(CONTENT_TARGET, "Content Target", "getContentTargetAsString"); - } - - /** - * Get the content target - * @return the target - * @throws TypeConstraintException if the stored Content Target is a URL - */ - public String getContentTargetAsString(){ - return returnAsString(CONTENT_TARGET, "Content Target", "getContentTarget"); - } - - /** - * Set the target of the content. For instance the URL of a landing page. - * @param contentTarget the target to set. A null value will remove this parameter - */ - public void setContentTarget(URL contentTarget){ - setParameter(CONTENT_TARGET, contentTarget); - } - - /** - * Set the target of the content. For instance the URL of a landing page. - * @param contentTarget the target to set. A null value will remove this parameter - */ - public void setContentTargetWithString(String contentTarget){ - setParameter(CONTENT_TARGET, contentTarget); - } - - /** - * Get the current hour. - * @return the current hour - */ - public Integer getCurrentHour(){ - return (Integer)getParameter(CURRENT_HOUR); - } - - /** - * Set the current hour (local time). - * @param currentHour the hour to set. A null value will remove this parameter - */ - public void setCurrentHour(Integer currentHour){ - setParameter(CURRENT_HOUR, currentHour); - } - - /** - * Get the current minute. - * @return the current minute - */ - public Integer getCurrentMinute(){ - return (Integer)getParameter(CURRENT_MINUTE); - } - - /** - * Set the current minute (local time). - * @param currentMinute the minute to set. A null value will remove this parameter - */ - public void setCurrentMinute(Integer currentMinute){ - setParameter(CURRENT_MINUTE, currentMinute); - } - - /** - * Get the current second - * @return the current second - */ - public Integer getCurrentSecond(){ - return (Integer)getParameter(CURRENT_SECOND); - } - - /** - * Set the current second (local time). - * @param currentSecond the second to set. A null value will remove this parameter - */ - public void setCurrentSecond(Integer currentSecond){ - setParameter(CURRENT_SECOND, currentSecond); - } - - /** - * Gets the list of objects currently stored at the specified custom tracking - * parameter. An empty list will be returned if there are no objects set at - * that key. - * @param key the key of the parameter whose list of objects to get. Cannot be null - * @return the list of objects currently stored at the specified key - */ - public List getCustomTrackingParameter(String key){ - if (key == null){ - throw new NullPointerException("Key cannot be null."); - } - List l = customTrackingParameters.get(key); - if (l == null){ - return new ArrayList(0); - } - return new ArrayList(l); - } - - /** - * Set a custom tracking parameter whose toString() value will be sent to - * the Piwik server. These parameters are stored separately from named Piwik - * parameters, meaning it is not possible to overwrite or clear named Piwik - * parameters with this method. A custom parameter that has the same name - * as a named Piwik parameter will be sent in addition to that named parameter. - * @param key the parameter's key. Cannot be null - * @param value the parameter's value. Removes the parameter if null - */ - public void setCustomTrackingParameter(String key, Object value){ - if (key == null){ - throw new NullPointerException("Key cannot be null."); - } - if (value == null){ - customTrackingParameters.remove(key); - } - else{ - List l = new ArrayList(); - l.add(value); - customTrackingParameters.put(key, l); - } - } - - /** - * Add a custom tracking parameter to the specified key. This allows users - * to have multiple parameters with the same name and different values, - * commonly used during situations where list parameters are needed - * @param key the parameter's key. Cannot be null - * @param value the parameter's value. Cannot be null - */ - public void addCustomTrackingParameter(String key, Object value){ - if (key == null){ - throw new NullPointerException("Key cannot be null."); - } - if (value == null){ - throw new NullPointerException("Cannot add a null custom tracking parameter."); - } - else{ - List l = customTrackingParameters.get(key); - if (l == null){ - l = new ArrayList(); - customTrackingParameters.put(key, l); - } - l.add(value); - } - } - - /** - * Removes all custom tracking parameters - */ - public void clearCustomTrackingParameter(){ - customTrackingParameters.clear(); - } - - /** - * Get the resolution of the device - * @return the resolution - */ - public String getDeviceResolution(){ - return (String)getParameter(DEVICE_RESOLUTION); - } - - /** - * Set the resolution of the device the visitor is using, eg 1280x1024. - * @param deviceResolution the resolution to set. A null value will remove this parameter - */ - public void setDeviceResolution(String deviceResolution){ - setParameter(DEVICE_RESOLUTION, deviceResolution); - } - - /** - * Get the url of a file the user had downloaded - * @return the url - * @throws TypeConstraintException if the stored Download URL is a String - */ - public URL getDownloadUrl(){ - return returnAsUrl(DOWNLOAD_URL, "Download URL", "getDownloadUrlAsString"); - } - - /** - * Get the url of a file the user had downloaded - * @return the url - * @throws TypeConstraintException if the stored Download URL is a URL - */ - public String getDownloadUrlAsString(){ - return returnAsString(DOWNLOAD_URL, "Download URL", "getDownloadUrl"); - } - - /** - * Set the url of a file the user has downloaded. Used for tracking downloads. - * We recommend to also set the url parameter to this same value. - * @param downloadUrl the url to set. A null value will remove this parameter - */ - public void setDownloadUrl(URL downloadUrl){ - setParameter(DOWNLOAD_URL, downloadUrl); - } - - /** - * Set the url of a file the user has downloaded. Used for tracking downloads. - * We recommend to also set the url parameter to this same value. - * @param downloadUrl the url to set. A null value will remove this parameter - */ - public void setDownloadUrlWithString(String downloadUrl){ - setParameter(DOWNLOAD_URL, downloadUrl); - } - - /** - * Sets idgoal=0 in the request to track an ecommerce interaction: - * cart update or an ecommerce order. - */ - public void enableEcommerce(){ - setGoalId(0); - } - - /** - * Verifies that Ecommerce has been enabled for the request. Will throw an - * {@link IllegalStateException} if not. - */ - public void verifyEcommerceEnabled(){ - if (getGoalId() == null || getGoalId() != 0){ - throw new IllegalStateException("GoalId must be \"0\". Try calling enableEcommerce first before calling this method."); - } - } - - /** - * Verifies that Ecommerce has been enabled and that Ecommerce Id and - * Ecommerce Revenue have been set for the request. Will throw an - * {@link IllegalStateException} if not. - * - */ - public void verifyEcommerceState(){ - verifyEcommerceEnabled(); - if (getEcommerceId() == null){ - throw new IllegalStateException("EcommerceId must be set before this value can be set."); - } - if (getEcommerceRevenue() == null){ - throw new IllegalStateException("EcommerceRevenue must be set before this value can be set."); - } - } - - /** - * Get the discount offered. - * @return the discount - */ - public Double getEcommerceDiscount(){ - return (Double)getParameter(ECOMMERCE_DISCOUNT); - } - - /** - * Set the discount offered. Ecommerce must be enabled, and EcommerceId and - * EcommerceRevenue must first be set. - * @param discount the discount to set. A null value will remove this parameter - */ - public void setEcommerceDiscount(Double discount){ - if (discount != null){ - verifyEcommerceState(); - } - setParameter(ECOMMERCE_DISCOUNT, discount); - } - - /** - * Get the id of this order. - * @return the id - */ - public String getEcommerceId(){ - return (String)getParameter(ECOMMERCE_ID); - } - - /** - * Set the unique string identifier for the ecommerce order (required when - * tracking an ecommerce order). Ecommerce must be enabled. - * @param id the id to set. A null value will remove this parameter - */ - public void setEcommerceId(String id){ - if (id != null){ - verifyEcommerceEnabled(); - } - setParameter(ECOMMERCE_ID, id); - } - - /** - * Get the {@link EcommerceItem} at the specified index - * @param index the index of the {@link EcommerceItem} to return - * @return the {@link EcommerceItem} at the specified index - */ - public EcommerceItem getEcommerceItem(int index){ - return (EcommerceItem)getFromJsonArray(ECOMMERCE_ITEMS, index); - } - - /** - * Add an {@link EcommerceItem} to this order. Ecommerce must be enabled, - * and EcommerceId and EcommerceRevenue must first be set. - * @param item the {@link EcommerceItem} to add. Cannot be null - */ - public void addEcommerceItem(EcommerceItem item){ - if (item != null){ - verifyEcommerceState(); - } - addToJsonArray(ECOMMERCE_ITEMS, item); - } - - /** - * Clears all {@link EcommerceItem} from this order. - */ - public void clearEcommerceItems(){ - removeJsonArray(ECOMMERCE_ITEMS); - } - - /** - * Get the timestamp of the customer's last ecommerce order - * @return the timestamp - */ - public Long getEcommerceLastOrderTimestamp(){ - return (Long)getParameter(ECOMMERCE_LAST_ORDER_TIMESTAMP); - } - - /** - * Set the UNUX timestamp of this customer's last ecommerce order. This value - * is used to process the "Days since last order" report. Ecommerce must be - * enabled, and EcommerceId and EcommerceRevenue must first be set. - * @param timestamp the timestamp to set. A null value will remove this parameter - */ - public void setEcommerceLastOrderTimestamp(Long timestamp){ - if (timestamp != null){ - verifyEcommerceState(); - } - setParameter(ECOMMERCE_LAST_ORDER_TIMESTAMP, timestamp); - } - - /** - * Get the grand total of the ecommerce order. - * @return the grand total - */ - public Double getEcommerceRevenue(){ - return (Double)getParameter(ECOMMERCE_REVENUE); - } - - /** - * Set the grand total of the ecommerce order (required when tracking an - * ecommerce order). Ecommerce must be enabled. - * @param revenue the grand total to set. A null value will remove this parameter - */ - public void setEcommerceRevenue(Double revenue){ - if (revenue != null){ - verifyEcommerceEnabled(); - } - setParameter(ECOMMERCE_REVENUE, revenue); - } - - /** - * Get the shipping cost of the ecommerce order. - * @return the shipping cost - */ - public Double getEcommerceShippingCost(){ - return (Double)getParameter(ECOMMERCE_SHIPPING_COST); - } - - /** - * Set the shipping cost of the ecommerce order. Ecommerce must be enabled, - * and EcommerceId and EcommerceRevenue must first be set. - * @param shippingCost the shipping cost to set. A null value will remove this parameter - */ - public void setEcommerceShippingCost(Double shippingCost){ - if (shippingCost != null){ - verifyEcommerceState(); - } - setParameter(ECOMMERCE_SHIPPING_COST, shippingCost); - } - - /** - * Get the subtotal of the ecommerce order; excludes shipping. - * @return the subtotal - */ - public Double getEcommerceSubtotal(){ - return (Double)getParameter(ECOMMERCE_SUBTOTAL); - } - - /** - * Set the subtotal of the ecommerce order; excludes shipping. Ecommerce - * must be enabled and EcommerceId and EcommerceRevenue must first be set. - * @param subtotal the subtotal to set. A null value will remove this parameter - */ - public void setEcommerceSubtotal(Double subtotal){ - if (subtotal != null){ - verifyEcommerceState(); - } - setParameter(ECOMMERCE_SUBTOTAL, subtotal); - } - - /** - * Get the tax amount of the ecommerce order. - * @return the tax amount - */ - public Double getEcommerceTax(){ - return (Double)getParameter(ECOMMERCE_TAX); - } - - /** - * Set the tax amount of the ecommerce order. Ecommerce must be enabled, and - * EcommerceId and EcommerceRevenue must first be set. - * @param tax the tax amount to set. A null value will remove this parameter - */ - public void setEcommerceTax(Double tax){ - if (tax != null){ - verifyEcommerceState(); - } - setParameter(ECOMMERCE_TAX, tax); - } - - /** - * Get the event action. - * @return the event action - */ - public String getEventAction(){ - return getNonEmptyStringParameter(EVENT_ACTION); - } - - /** - * Set the event action. Must not be empty. (eg. Play, Pause, Duration, - * Add Playlist, Downloaded, Clicked...). - * @param eventAction the event action to set. A null value will remove this parameter - */ - public void setEventAction(String eventAction){ - setNonEmptyStringParameter(EVENT_ACTION, eventAction); - } - - /** - * Get the event category. - * @return the event category - */ - public String getEventCategory(){ - return getNonEmptyStringParameter(EVENT_CATEGORY); - } - - /** - * Set the event category. Must not be empty. (eg. Videos, Music, Games...). - * @param eventCategory the event category to set. A null value will remove this parameter - */ - public void setEventCategory(String eventCategory){ - setNonEmptyStringParameter(EVENT_CATEGORY, eventCategory); - } - - /** - * Get the event name. - * @return the event name - */ - public String getEventName(){ - return (String)getParameter(EVENT_NAME); - } - - /** - * Set the event name. (eg. a Movie name, or Song name, or File name...). - * @param eventName the event name to set. A null value will remove this parameter - */ - public void setEventName(String eventName){ - setParameter(EVENT_NAME, eventName); - } - - /** - * Get the event value. - * @return the event value - */ - public Number getEventValue(){ - return (Number)getParameter(EVENT_VALUE); - } - - /** - * Set the event value. Must be a float or integer value (numeric), not a string. - * @param eventValue the event value to set. A null value will remove this parameter - */ - public void setEventValue(Number eventValue){ - setParameter(EVENT_VALUE, eventValue); - } - - /** - * Get the goal id - * @return the goal id - */ - public Integer getGoalId(){ - return (Integer)getParameter(GOAL_ID); - } - - /** - * Set the goal id. If specified, the tracking request will trigger a - * conversion for the goal of the website being tracked with this id. - * @param goalId the goal id to set. A null value will remove this parameter - */ - public void setGoalId(Integer goalId){ - setParameter(GOAL_ID, goalId); - } - - /** - * Get the goal revenue. - * @return the goal revenue - */ - public Double getGoalRevenue(){ - return (Double)getParameter(GOAL_REVENUE); - } - - /** - * Set a monetary value that was generated as revenue by this goal conversion. - * Only used if idgoal is specified in the request. - * @param goalRevenue the goal revenue to set. A null value will remove this parameter - */ - public void setGoalRevenue(Double goalRevenue){ - if (goalRevenue != null && getGoalId() == null){ - throw new IllegalStateException("GoalId must be set before GoalRevenue can be set."); - } - setParameter(GOAL_REVENUE, goalRevenue); - } - - /** - * Get the Accept-Language HTTP header - * @return the Accept-Language HTTP header - */ - public String getHeaderAcceptLanguage(){ - return (String)getParameter(HEADER_ACCEPT_LANGUAGE); - } - - /** - * Set an override value for the Accept-Language HTTP header - * field. This value is used to detect the visitor's country if - * GeoIP is not enabled. - * @param acceptLangage the Accept-Language HTTP header to set. A null value will remove this parameter - */ - public void setHeaderAcceptLanguage(String acceptLangage){ - setParameter(HEADER_ACCEPT_LANGUAGE, acceptLangage); - } - - /** - * Get the User-Agent HTTP header - * @return the User-Agent HTTP header - */ - public String getHeaderUserAgent(){ - return (String)getParameter(HEADER_USER_AGENT); - } - - /** - * Set an override value for the User-Agent HTTP header field. - * The user agent is used to detect the operating system and browser used. - * @param userAgent the User-Agent HTTP header tos et - */ - public void setHeaderUserAgent(String userAgent){ - setParameter(HEADER_USER_AGENT, userAgent); - } - - /** - * Get if this request will force a new visit. - * @return true if this request will force a new visit - */ - public Boolean getNewVisit(){ - return getBooleanParameter(NEW_VISIT); - } - - /** - * If set to true, will force a new visit to be created for this action. - * @param newVisit if this request will force a new visit - */ - public void setNewVisit(Boolean newVisit){ - setBooleanParameter(NEW_VISIT, newVisit); - } - - /** - * Get the outlink url - * @return the outlink url - * @throws TypeConstraintException if the stored Outlink URL is a String - */ - public URL getOutlinkUrl(){ - return returnAsUrl(OUTLINK_URL, "Outlink URL", "getOutlinkUrlAsString"); - } - - /** - * Get the outlink url - * @return the outlink url - * @throws TypeConstraintException if the stored Outlink URL is a URL - */ - public String getOutlinkUrlAsString(){ - return returnAsString(OUTLINK_URL, "Outlink URL", "getOutlinkUrl"); - } - - /** - * Set an external URL the user has opened. Used for tracking outlink clicks. - * We recommend to also set the url parameter to this same value. - * @param outlinkUrl the outlink url to set. A null value will remove this parameter - */ - public void setOutlinkUrl(URL outlinkUrl){ - setParameter(OUTLINK_URL, outlinkUrl); - } - - /** - * Set an external URL the user has opened. Used for tracking outlink clicks. - * We recommend to also set the url parameter to this same value. - * @param outlinkUrl the outlink url to set. A null value will remove this parameter - */ - public void setOutlinkUrlWithString(String outlinkUrl){ - setParameter(OUTLINK_URL, outlinkUrl); - } - - /** - * Get the page custom variable at the specified key. - * @param key the key of the variable to get - * @return the variable at the specified key, null if key is not present - * @deprecated Use the {@link #getPageCustomVariable(int)} method instead. - */ - @Deprecated - public String getPageCustomVariable(String key){ - return getCustomVariable(PAGE_CUSTOM_VARIABLE, key); - } - - /** - * Get the page custom variable at the specified index. - * @param index the index of the variable to get. Must be greater than 0 - * @return the variable at the specified key, null if nothing at this index - */ - public CustomVariable getPageCustomVariable(int index){ - return getCustomVariable(PAGE_CUSTOM_VARIABLE, index); - } - - /** - * Set a page custom variable with the specified key and value at the first available index. - * All page custom variables with this key will be overwritten or deleted - * @param key the key of the variable to set - * @param value the value of the variable to set at the specified key. A null value will remove this custom variable - * @deprecated Use the {@link #setPageCustomVariable(CustomVariable, int)} method instead. - */ - @Deprecated - public void setPageCustomVariable(String key, String value){ - if (value == null){ - removeCustomVariable(PAGE_CUSTOM_VARIABLE, key); - } else { - setCustomVariable(PAGE_CUSTOM_VARIABLE, new CustomVariable(key, value), null); - } - } - - /** - * Set a page custom variable at the specified index. - * @param customVariable the CustomVariable to set. A null value will remove the CustomVariable at the specified index - * @param index the index of he CustomVariable to set - */ - public void setPageCustomVariable(CustomVariable customVariable, int index){ - setCustomVariable(PAGE_CUSTOM_VARIABLE, customVariable, index); - } - - /** - * Check if the visitor has the Director plugin. - * @return true if visitor has the Director plugin - */ - public Boolean getPluginDirector(){ - return getBooleanParameter(PLUGIN_DIRECTOR); - } - - /** - * Set if the visitor has the Director plugin. - * @param director true if the visitor has the Director plugin - */ - public void setPluginDirector(Boolean director){ - setBooleanParameter(PLUGIN_DIRECTOR, director); - } - - /** - * Check if the visitor has the Flash plugin. - * @return true if the visitor has the Flash plugin - */ - public Boolean getPluginFlash(){ - return getBooleanParameter(PLUGIN_FLASH); - } - - /** - * Set if the visitor has the Flash plugin. - * @param flash true if the visitor has the Flash plugin - */ - public void setPluginFlash(Boolean flash){ - setBooleanParameter(PLUGIN_FLASH, flash); - } - - /** - * Check if the visitor has the Gears plugin. - * @return true if the visitor has the Gears plugin - */ - public Boolean getPluginGears(){ - return getBooleanParameter(PLUGIN_GEARS); - } - - /** - * Set if the visitor has the Gears plugin. - * @param gears true if the visitor has the Gears plugin - */ - public void setPluginGears(Boolean gears){ - setBooleanParameter(PLUGIN_GEARS, gears); - } - - /** - * Check if the visitor has the Java plugin. - * @return true if the visitor has the Java plugin - */ - public Boolean getPluginJava(){ - return getBooleanParameter(PLUGIN_JAVA); - } - - /** - * Set if the visitor has the Java plugin. - * @param java true if the visitor has the Java plugin - */ - public void setPluginJava(Boolean java){ - setBooleanParameter(PLUGIN_JAVA, java); - } - - /** - * Check if the visitor has the PDF plugin. - * @return true if the visitor has the PDF plugin - */ - public Boolean getPluginPDF(){ - return getBooleanParameter(PLUGIN_PDF); - } - - /** - * Set if the visitor has the PDF plugin. - * @param pdf true if the visitor has the PDF plugin - */ - public void setPluginPDF(Boolean pdf){ - setBooleanParameter(PLUGIN_PDF, pdf); - } - - /** - * Check if the visitor has the Quicktime plugin. - * @return true if the visitor has the Quicktime plugin - */ - public Boolean getPluginQuicktime(){ - return getBooleanParameter(PLUGIN_QUICKTIME); - } - - /** - * Set if the visitor has the Quicktime plugin. - * @param quicktime true if the visitor has the Quicktime plugin - */ - public void setPluginQuicktime(Boolean quicktime){ - setBooleanParameter(PLUGIN_QUICKTIME, quicktime); - } - - /** - * Check if the visitor has the RealPlayer plugin. - * @return true if the visitor has the RealPlayer plugin - */ - public Boolean getPluginRealPlayer(){ - return getBooleanParameter(PLUGIN_REAL_PLAYER); - } - - /** - * Set if the visitor has the RealPlayer plugin. - * @param realPlayer true if the visitor has the RealPlayer plugin - */ - public void setPluginRealPlayer(Boolean realPlayer){ - setBooleanParameter(PLUGIN_REAL_PLAYER, realPlayer); - } - - /** - * Check if the visitor has the Silverlight plugin. - * @return true if the visitor has the Silverlight plugin - */ - public Boolean getPluginSilverlight(){ - return getBooleanParameter(PLUGIN_SILVERLIGHT); - } - - /** - * Set if the visitor has the Silverlight plugin. - * @param silverlight true if the visitor has the Silverlight plugin - */ - public void setPluginSilverlight(Boolean silverlight){ - setBooleanParameter(PLUGIN_SILVERLIGHT, silverlight); - } - - /** - * Check if the visitor has the Windows Media plugin. - * @return true if the visitor has the Windows Media plugin - */ - public Boolean getPluginWindowsMedia(){ - return getBooleanParameter(PLUGIN_WINDOWS_MEDIA); - } - - /** - * Set if the visitor has the Windows Media plugin. - * @param windowsMedia true if the visitor has the Windows Media plugin - */ - public void setPluginWindowsMedia(Boolean windowsMedia){ - setBooleanParameter(PLUGIN_WINDOWS_MEDIA, windowsMedia); - } - - /** - * Get the random value for this request - * @return the random value - */ - public String getRandomValue(){ - return (String)getParameter(RANDOM_VALUE); - } - - /** - * Set a random value that is generated before each request. Using it helps - * avoid the tracking request being cached by the browser or a proxy. - * @param randomValue the random value to set. A null value will remove this parameter - */ - public void setRandomValue(String randomValue){ - setParameter(RANDOM_VALUE, randomValue); - } - - /** - * Get the referrer url - * @return the referrer url - * @throws TypeConstraintException if the stored Referrer URL is a String - */ - public URL getReferrerUrl(){ - return returnAsUrl(REFERRER_URL, "Referrer URL", "getReferrerUrlAsString"); - } - - /** - * Get the referrer url - * @return the referrer url - * @throws TypeConstraintException if the stored Referrer URL is a URL - */ - public String getReferrerUrlAsString(){ - return returnAsString(REFERRER_URL, "Referrer URL", "getReferrerUrl"); - } - - /** - * Set the full HTTP Referrer URL. This value is used to determine how someone - * got to your website (ie, through a website, search engine or campaign). - * @param referrerUrl the referrer url to set. A null value will remove this parameter - */ - public void setReferrerUrl(URL referrerUrl){ - setParameter(REFERRER_URL, referrerUrl); - } - - /** - * Set the full HTTP Referrer URL. This value is used to determine how someone - * got to your website (ie, through a website, search engine or campaign). - * @param referrerUrl the referrer url to set. A null value will remove this parameter - */ - public void setReferrerUrlWithString(String referrerUrl){ - setParameter(REFERRER_URL, referrerUrl); - } - - /** - * Get the datetime of the request - * @return the datetime of the request - */ - public PiwikDate getRequestDatetime(){ - return (PiwikDate)getParameter(REQUEST_DATETIME); - } - - /** - * Set the datetime of the request (normally the current time is used). - * This can be used to record visits and page views in the past. The datetime - * must be sent in UTC timezone. Note: if you record data in the past, you will - * need to force Piwik to re-process - * reports for the past dates. If you set the Request Datetime to a datetime - * older than four hours then Auth Token must be set. If you set - * Request Datetime with a datetime in the last four hours then you - * don't need to pass Auth Token. - * @param datetime the datetime of the request to set. A null value will remove this parameter - */ - public void setRequestDatetime(PiwikDate datetime){ - if (datetime != null && new Date().getTime()-datetime.getTime() > REQUEST_DATETIME_AUTH_LIMIT && getAuthToken() == null){ - throw new IllegalStateException("Because you are trying to set RequestDatetime for a time greater than 4 hours ago, AuthToken must be set first."); - } - setParameter(REQUEST_DATETIME, datetime); - } - - /** - * Get if this request will be tracked. - * @return true if request will be tracked - */ - public Boolean getRequired(){ - return getBooleanParameter(REQUIRED); - } - - /** - * Set if this request will be tracked by the Piwik server. - * @param required true if request will be tracked - */ - public void setRequired(Boolean required){ - setBooleanParameter(REQUIRED, required); - } - - /** - * Get if the response will be an image. - * @return true if the response will be an an image - */ - public Boolean getResponseAsImage(){ - return getBooleanParameter(RESPONSE_AS_IMAGE); - } - - /** - * Set if the response will be an image. If set to false, Piwik will respond - * with a HTTP 204 response code instead of a GIF image. This improves performance - * and can fix errors if images are not allowed to be obtained directly - * (eg Chrome Apps). Available since Piwik 2.10.0. - - * @param responseAsImage true if the response will be an image - */ - public void setResponseAsImage(Boolean responseAsImage){ - setBooleanParameter(RESPONSE_AS_IMAGE, responseAsImage); - } - - /** - * Get the search category - * @return the search category - */ - public String getSearchCategory(){ - return (String)getParameter(SEARCH_CATEGORY); - } - - /** - * Specify a search category with this parameter. SearchQuery must first be - * set. - * @param searchCategory the search category to set. A null value will remove this parameter - */ - public void setSearchCategory(String searchCategory){ - if (searchCategory != null && getSearchQuery() == null){ - throw new IllegalStateException("SearchQuery must be set before SearchCategory can be set."); - } - setParameter(SEARCH_CATEGORY, searchCategory); - } - - /** - * Get the search query. - * @return the search query - */ - public String getSearchQuery(){ - return (String)getParameter(SEARCH_QUERY); - } - - /** - * Set the search query. When specified, the request will not be tracked as - * a normal pageview but will instead be tracked as a Site Search request. - * @param searchQuery the search query to set. A null value will remove this parameter - */ - public void setSearchQuery(String searchQuery){ - setParameter(SEARCH_QUERY, searchQuery); - } - - /** - * Get the search results count. - * @return the search results count - */ - public Long getSearchResultsCount(){ - return (Long)getParameter(SEARCH_RESULTS_COUNT); - } - - /** - * We recommend to set the - * search count to the number of search results displayed on the results page. - * When keywords are tracked with {@code Search Results Count=0} they will appear in - * the "No Result Search Keyword" report. SearchQuery must first be set. - * @param searchResultsCount the search results count to set. A null value will remove this parameter - */ - public void setSearchResultsCount(Long searchResultsCount){ - if (searchResultsCount != null && getSearchQuery() == null){ - throw new IllegalStateException("SearchQuery must be set before SearchResultsCount can be set."); - } - setParameter(SEARCH_RESULTS_COUNT, searchResultsCount); - } - - /** - * Get the id of the website we're tracking. - * @return the id of the website - */ - public Integer getSiteId(){ - return (Integer)getParameter(SITE_ID); - } - - /** - * Set the ID of the website we're tracking a visit/action for. - * @param siteId the id of the website to set. A null value will remove this parameter - */ - public void setSiteId(Integer siteId){ - setParameter(SITE_ID, siteId); - } - - /** - * Set if bot requests should be tracked - * @return true if bot requests should be tracked - */ - public Boolean getTrackBotRequests(){ - return getBooleanParameter(TRACK_BOT_REQUESTS); - } - - /** - * By default Piwik does not track bots. If you use the Tracking Java API, - * you may be interested in tracking bot requests. To enable Bot Tracking in - * Piwik, set Track Bot Requests to true. - * @param trackBotRequests true if bot requests should be tracked - */ - public void setTrackBotRequests(Boolean trackBotRequests){ - setBooleanParameter(TRACK_BOT_REQUESTS, trackBotRequests); - } - - /** - * Get the visit custom variable at the specified key. - * @param key the key of the variable to get - * @return the variable at the specified key, null if key is not present - * @deprecated Use the {@link #getVisitCustomVariable(int)} method instead. - */ - @Deprecated - public String getUserCustomVariable(String key){ - return getCustomVariable(VISIT_CUSTOM_VARIABLE, key); - } - - /** - * Get the visit custom variable at the specified index. - * @param index the index of the variable to get - * @return the variable at the specified index, null if nothing at this index - */ - public CustomVariable getVisitCustomVariable(int index){ - return getCustomVariable(VISIT_CUSTOM_VARIABLE, index); - } - - /** - * Set a visit custom variable with the specified key and value at the first available index. - * All visit custom variables with this key will be overwritten or deleted - * @param key the key of the variable to set - * @param value the value of the variable to set at the specified key. A null value will remove this parameter - * @deprecated Use the {@link #setVisitCustomVariable(CustomVariable, int)} method instead. - */ - @Deprecated - public void setUserCustomVariable(String key, String value){ - if (value == null){ - removeCustomVariable(VISIT_CUSTOM_VARIABLE, key); - } else { - setCustomVariable(VISIT_CUSTOM_VARIABLE, new CustomVariable(key, value), null); - } - } - /** - * Set a user custom variable at the specified key. - * @param customVariable the CustomVariable to set. A null value will remove the custom variable at the specified index - * @param index the index to set the customVariable at. - */ - public void setVisitCustomVariable(CustomVariable customVariable, int index){ - setCustomVariable(VISIT_CUSTOM_VARIABLE, customVariable, index); - } - - /** - * Get the user id for this request. - * @return the user id - */ - public String getUserId(){ - return (String)getParameter(USER_ID); - } - - /** - * Set the user id for this request. - * User id is any non empty unique string identifying the user (such as an email - * address or a username). To access this value, users must be logged-in in your - * system so you can fetch this user id from your system, and pass it to Piwik. - * The user id appears in the visitor log, the Visitor profile, and you can - * Segment - * reports for one or several user ids. When specified, the user id will be - * "enforced". This means that if there is no recent visit with this user id, - * a new one will be created. If a visit is found in the last 30 minutes with - * your specified user id, then the new action will be recorded to this existing visit. - * @param userId the user id to set. A null value will remove this parameter - */ - public void setUserId(String userId){ - setNonEmptyStringParameter(USER_ID, userId); - } - - /** - * Get the visitor's city. - * @return the visitor's city - */ - public String getVisitorCity(){ - return (String)getParameter(VISITOR_CITY); - } - - /** - * Set an override value for the city. The name of the city the visitor is - * located in, eg, Tokyo. AuthToken must first be set. - * @param city the visitor's city to set. A null value will remove this parameter - */ - public void setVisitorCity(String city){ - if (city != null){ - verifyAuthTokenSet(); - } - setParameter(VISITOR_CITY, city); - } - - /** - * Get the visitor's country. - * @return the visitor's country - */ - public PiwikLocale getVisitorCountry(){ - return (PiwikLocale)getParameter(VISITOR_COUNTRY); - } - - /** - * Set an override value for the country. AuthToken must first be set. - * @param country the visitor's country to set. A null value will remove this parameter - */ - public void setVisitorCountry(PiwikLocale country){ - if (country != null){ - verifyAuthTokenSet(); - } - setParameter(VISITOR_COUNTRY, country); - } - - /** - * Get the visitor's custom id. - * @return the visitor's custom id - */ - public String getVisitorCustomId(){ - return (String)getParameter(VISITOR_CUSTOM_ID); - } - - /** - * Set a custom visitor ID for this request. You must set this value to exactly - * a {@value #ID_LENGTH} character hexadecimal string (containing only characters 01234567890abcdefABCDEF). - * We recommended to set the UserId rather than the VisitorCustomId. - * @param visitorCustomId the visitor's custom id to set. A null value will remove this parameter - */ - public void setVisitorCustomId(String visitorCustomId){ - if (visitorCustomId != null){ - if (visitorCustomId.length() != ID_LENGTH){ - throw new IllegalArgumentException(visitorCustomId+" is not "+ID_LENGTH+" characters long."); - } - // Verify visitorID is a 16 character hexadecimal string - else if (!visitorCustomId.matches("[0-9A-Fa-f]+")){ - throw new IllegalArgumentException(visitorCustomId+" is not a hexadecimal string."); - } - } - setParameter(VISITOR_CUSTOM_ID, visitorCustomId); - } - - /** - * Get the timestamp of the visitor's first visit. - * @return the timestamp of the visitor's first visit - */ - public Long getVisitorFirstVisitTimestamp(){ - return (Long)getParameter(VISITOR_FIRST_VISIT_TIMESTAMP); - } - - /** - * Set the UNIX timestamp of this visitor's first visit. This could be set - * to the date where the user first started using your software/app, or when - * he/she created an account. This parameter is used to populate the - * Goals > Days to Conversion report. - * @param timestamp the timestamp of the visitor's first visit to set. A null value will remove this parameter - */ - public void setVisitorFirstVisitTimestamp(Long timestamp){ - setParameter(VISITOR_FIRST_VISIT_TIMESTAMP, timestamp); - } - - /** - * Get the visitor's id. - * @return the visitor's id - */ - public String getVisitorId(){ - return (String)getParameter(VISITOR_ID); - } - - /** - * Set the unique visitor ID, must be a {@value #ID_LENGTH} characters hexadecimal string. - * Every unique visitor must be assigned a different ID and this ID must not - * change after it is assigned. If this value is not set Piwik will still - * track visits, but the unique visitors metric might be less accurate. - * @param visitorId the visitor id to set. A null value will remove this parameter - */ - public void setVisitorId(String visitorId){ - if (visitorId != null){ - if (visitorId.length() != ID_LENGTH){ - throw new IllegalArgumentException(visitorId+" is not "+ID_LENGTH+" characters long."); - } - // Verify visitorID is a 16 character hexadecimal string - else if (!visitorId.matches("[0-9A-Fa-f]+")){ - throw new IllegalArgumentException(visitorId+" is not a hexadecimal string."); - } - } - setParameter(VISITOR_ID, visitorId); - } - - /** - * Get the visitor's ip. - * @return the visitor's ip - */ - public String getVisitorIp(){ - return (String)getParameter(VISITOR_IP); - } - - /** - * Set the override value for the visitor IP (both IPv4 and IPv6 notations - * supported). AuthToken must first be set. - * @param visitorIp the visitor's ip to set. A null value will remove this parameter - */ - public void setVisitorIp(String visitorIp){ - if (visitorIp != null){ - verifyAuthTokenSet(); - } - setParameter(VISITOR_IP, visitorIp); - } - - /** - * Get the visitor's latitude. - * @return the visitor's latitude - */ - public Double getVisitorLatitude(){ - return (Double)getParameter(VISITOR_LATITUDE); - } - - /** - * Set an override value for the visitor's latitude, eg 22.456. AuthToken - * must first be set. - * @param latitude the visitor's latitude to set. A null value will remove this parameter - */ - public void setVisitorLatitude(Double latitude){ - if (latitude != null){ - verifyAuthTokenSet(); - } - setParameter(VISITOR_LATITUDE, latitude); - } - - /** - * Get the visitor's longitude. - * @return the visitor's longitude - */ - public Double getVisitorLongitude(){ - return (Double)getParameter(VISITOR_LONGITUDE); - } - - /** - * Set an override value for the visitor's longitude, eg 22.456. AuthToken - * must first be set. - * @param longitude the visitor's longitude to set. A null value will remove this parameter - */ - public void setVisitorLongitude(Double longitude){ - if (longitude != null){ - verifyAuthTokenSet(); - } - setParameter(VISITOR_LONGITUDE, longitude); - } - - /** - * Get the timestamp of the visitor's previous visit. - * @return the timestamp of the visitor's previous visit - */ - public Long getVisitorPreviousVisitTimestamp(){ - return (Long)getParameter(VISITOR_PREVIOUS_VISIT_TIMESTAMP); - } - - /** - * Set the UNIX timestamp of this visitor's previous visit. This parameter - * is used to populate the report - * Visitors > Engagement > Visits by days since last visit. - * @param timestamp the timestamp of the visitor's previous visit to set. A null value will remove this parameter - */ - public void setVisitorPreviousVisitTimestamp(Long timestamp){ - setParameter(VISITOR_PREVIOUS_VISIT_TIMESTAMP, timestamp); - } - - /** - * Get the visitor's region. - * @return the visitor's region - */ - public String getVisitorRegion(){ - return (String)getParameter(VISITOR_REGION); - } - - /** - * Set an override value for the region. Should be set to the two letter - * region code as defined by - * MaxMind's GeoIP databases. - * See here - * for a list of them for every country (the region codes are located in the - * second column, to the left of the region name and to the right of the country - * code). - * @param region the visitor's region to set. A null value will remove this parameter - */ - public void setVisitorRegion(String region){ - if (region != null){ - verifyAuthTokenSet(); - } - setParameter(VISITOR_REGION, region); - } - - /** - * Get the count of visits for this visitor. - * @return the count of visits for this visitor - */ - public Integer getVisitorVisitCount(){ - return (Integer)getParameter(VISITOR_VISIT_COUNT); - } - - /** - * Set the current count of visits for this visitor. To set this value correctly, - * it would be required to store the value for each visitor in your application - * (using sessions or persisting in a database). Then you would manually increment - * the counts by one on each new visit or "session", depending on how you choose - * to define a visit. This value is used to populate the report - * Visitors > Engagement > Visits by visit number. - * @param visitorVisitCount the count of visits for this visitor to set. A null value will remove this parameter - */ - public void setVisitorVisitCount(Integer visitorVisitCount){ - setParameter(VISITOR_VISIT_COUNT, visitorVisitCount); - } - - /** - * Get the query string represented by this object. - * @return the query string represented by this object - */ - - public String getQueryString(){ - StringBuilder sb = new StringBuilder(); - for (Entry parameter : parameters.entrySet()){ - if (sb.length() > 0){ - sb.append("&"); - } - sb.append(parameter.getKey()); - sb.append("="); - sb.append(parameter.getValue().toString()); - } - for (Entry customTrackingParameter : customTrackingParameters.entrySet()){ - for (Object o : customTrackingParameter.getValue()){ - if (sb.length() > 0){ - sb.append("&"); - } - sb.append(customTrackingParameter.getKey()); - sb.append("="); - sb.append(o.toString()); - } - } - - return sb.toString(); - } - - /** - * Get the url encoded query string represented by this object. - * @return the url encoded query string represented by this object - */ - public String getUrlEncodedQueryString(){ - StringBuilder sb = new StringBuilder(); - for (Entry parameter : parameters.entrySet()){ - if (sb.length() > 0){ - sb.append("&"); - } - try { - StringBuilder sb2 = new StringBuilder(); - sb2.append(parameter.getKey()); - sb2.append("="); - sb2.append(URLEncoder.encode(parameter.getValue().toString(), "UTF-8")); - sb.append(sb2); - } - catch (UnsupportedEncodingException e) { - System.err.println(e.getMessage()); - } - } - for (Entry customTrackingParameter : customTrackingParameters.entrySet()){ - for (Object o : customTrackingParameter.getValue()){ - if (sb.length() > 0){ - sb.append("&"); - } - try { - StringBuilder sb2 = new StringBuilder(); - sb2.append(URLEncoder.encode(customTrackingParameter.getKey(), "UTF-8")); - sb2.append("="); - sb2.append(URLEncoder.encode(o.toString(), "UTF-8")); - sb.append(sb2); - } - catch (UnsupportedEncodingException e) { - System.err.println(e.getMessage()); - } - } - } - - return sb.toString(); - } - - /** - * Get a random hexadecimal string of a specified length. - * @param length length of the string to produce - * @return a random string consisting only of hexadecimal characters - */ - public static String getRandomHexString(int length){ - byte[] bytes = new byte[length/2]; - new Random().nextBytes(bytes); - return DatatypeConverter.printHexBinary(bytes); - } - - /** - * Get a stored parameter. - * @param key the parameter's key - * @return the stored parameter's value - */ - private Object getParameter(String key){ - return parameters.get(key); - } - - /** - * Set a stored parameter. - * @param key the parameter's key - * @param value the parameter's value. Removes the parameter if null - */ - private void setParameter(String key, Object value){ - if (value == null){ - parameters.remove(key); - } - else{ - parameters.put(key, value); - } - } - - /** - * Get a stored parameter that is a non-empty string. - * @param key the parameter's key - * @return the stored parameter's value - */ - private String getNonEmptyStringParameter(String key){ - return (String)parameters.get(key); - } - - /** - * Set a stored parameter and verify it is a non-empty string. - * @param key the parameter's key - * @param value the parameter's value. Cannot be the empty. Removes the parameter if null - * string - */ - private void setNonEmptyStringParameter(String key, String value){ - if (value == null){ - parameters.remove(key); - } - else if (value.length() == 0){ - throw new IllegalArgumentException("Value cannot be empty."); - } - else{ - parameters.put(key, value); - } - } - - /** - * Get a stored parameter that is a boolean. - * @param key the parameter's key - * @return the stored parameter's value - */ - private Boolean getBooleanParameter(String key){ - Integer i = (Integer)parameters.get(key); - if (i == null){ - return null; - } - return i.equals(1); - } - - /** - * Set a stored parameter that is a boolean. This value will be stored as "1" - * for true and "0" for false. - * @param key the parameter's key - * @param value the parameter's value. Removes the parameter if null - */ - private void setBooleanParameter(String key, Boolean value){ - if (value == null){ - parameters.remove(key); - } - else if (value){ - parameters.put(key, 1); - } - else{ - parameters.put(key, 0); - } - } - - /** - * Get a value that is stored in a json object at the specified parameter. - * @param parameter the parameter to retrieve the json object from - * @param key the key of the value. Cannot be null - * @return the value - */ - private CustomVariable getCustomVariable(String parameter, int index){ - CustomVariableList cvl = (CustomVariableList)parameters.get(parameter); - if (cvl == null){ - return null; + private static final String ACTION_NAME = "action_name"; + private static final String ACTION_TIME = "gt_ms"; + private static final String ACTION_URL = "url"; + private static final String API_VERSION = "apiv"; + private static final String AUTH_TOKEN = "token_auth"; + private static final String CAMPAIGN_KEYWORD = "_rck"; + private static final String CAMPAIGN_NAME = "_rcn"; + private static final String CHARACTER_SET = "cs"; + private static final String CONTENT_INTERACTION = "c_i"; + private static final String CONTENT_NAME = "c_n"; + private static final String CONTENT_PIECE = "c_p"; + private static final String CONTENT_TARGET = "c_t"; + private static final String CURRENT_HOUR = "h"; + private static final String CURRENT_MINUTE = "m"; + private static final String CURRENT_SECOND = "s"; + private static final String DEVICE_RESOLUTION = "res"; + private static final String DOWNLOAD_URL = "download"; + private static final String ECOMMERCE_DISCOUNT = "ec_dt"; + private static final String ECOMMERCE_ID = "ec_id"; + private static final String ECOMMERCE_ITEMS = "ec_items"; + private static final String ECOMMERCE_LAST_ORDER_TIMESTAMP = "_ects"; + private static final String ECOMMERCE_REVENUE = "revenue"; + private static final String ECOMMERCE_SHIPPING_COST = "ec_sh"; + private static final String ECOMMERCE_SUBTOTAL = "ec_st"; + private static final String ECOMMERCE_TAX = "ec_tx"; + private static final String EVENT_ACTION = "e_a"; + private static final String EVENT_CATEGORY = "e_c"; + private static final String EVENT_NAME = "e_n"; + private static final String EVENT_VALUE = "e_v"; + private static final String HEADER_ACCEPT_LANGUAGE = "lang"; + private static final String GOAL_ID = "idgoal"; + private static final String GOAL_REVENUE = "revenue"; + private static final String HEADER_USER_AGENT = "ua"; + private static final String NEW_VISIT = "new_visit"; + private static final String OUTLINK_URL = "link"; + private static final String PAGE_CUSTOM_VARIABLE = "cvar"; + private static final String PLUGIN_DIRECTOR = "dir"; + private static final String PLUGIN_FLASH = "fla"; + private static final String PLUGIN_GEARS = "gears"; + private static final String PLUGIN_JAVA = "java"; + private static final String PLUGIN_PDF = "pdf"; + private static final String PLUGIN_QUICKTIME = "qt"; + private static final String PLUGIN_REAL_PLAYER = "realp"; + private static final String PLUGIN_SILVERLIGHT = "ag"; + private static final String PLUGIN_WINDOWS_MEDIA = "wma"; + private static final String RANDOM_VALUE = "rand"; + private static final String REFERRER_URL = "urlref"; + private static final String REQUEST_DATETIME = "cdt"; + private static final String REQUIRED = "rec"; + private static final String RESPONSE_AS_IMAGE = "send_image"; + private static final String SEARCH_CATEGORY = "search_cat"; + private static final String SEARCH_QUERY = "search"; + private static final String SEARCH_RESULTS_COUNT = "search_count"; + private static final String SITE_ID = "idsite"; + private static final String TRACK_BOT_REQUESTS = "bots"; + private static final String VISIT_CUSTOM_VARIABLE = "_cvar"; + private static final String USER_ID = "uid"; + private static final String VISITOR_CITY = "city"; + private static final String VISITOR_COUNTRY = "country"; + private static final String VISITOR_CUSTOM_ID = "cid"; + private static final String VISITOR_FIRST_VISIT_TIMESTAMP = "_idts"; + private static final String VISITOR_ID = "_id"; + private static final String VISITOR_IP = "cip"; + private static final String VISITOR_LATITUDE = "lat"; + private static final String VISITOR_LONGITUDE = "long"; + private static final String VISITOR_PREVIOUS_VISIT_TIMESTAMP = "_viewts"; + private static final String VISITOR_REGION = "region"; + private static final String VISITOR_VISIT_COUNT = "_idvc"; + + private static final int RANDOM_VALUE_LENGTH = 20; + private static final long REQUEST_DATETIME_AUTH_LIMIT = 14400000L; + + private final Map parameters = new HashMap<>(); + private final Map customTrackingParameters = new HashMap<>(); + + /** + * Create a new request from the id of the site being tracked and the full + * url for the current action. This constructor also sets: + *
+   * {@code
+   * Required = true
+   * Visior Id = random 16 character hex string
+   * Random Value = random 20 character hex string
+   * API version = 1
+   * Response as Image = false
+   * }
+   * 
+ * Overwrite these values yourself as desired. + * + * @param siteId the id of the website we're tracking a visit/action for + * @param actionUrl the full URL for the current action + */ + public PiwikRequest(Integer siteId, URL actionUrl) { + setParameter(SITE_ID, siteId); + setBooleanParameter(REQUIRED, true); + setParameter(ACTION_URL, actionUrl); + setParameter(VISITOR_ID, getRandomHexString(ID_LENGTH)); + setParameter(RANDOM_VALUE, getRandomHexString(RANDOM_VALUE_LENGTH)); + setParameter(API_VERSION, "1"); + setBooleanParameter(RESPONSE_AS_IMAGE, false); + } + + /** + * Get the title of the action being tracked + * + * @return the title of the action being tracked + */ + + public String getActionName() { + return (String) getParameter(ACTION_NAME); + } + + /** + * Set the title of the action being tracked. It is possible to + * use slashes / + * to set one or several categories for this action. + * For example, Help / Feedback + * will create the Action Feedback in the category Help. + * + * @param actionName the title of the action to set. A null value will remove this parameter + */ + public void setActionName(String actionName) { + setParameter(ACTION_NAME, actionName); + } + + /** + * Get the amount of time it took the server to generate this action, in milliseconds. + * + * @return the amount of time + */ + public Long getActionTime() { + return (Long) getParameter(ACTION_TIME); + } + + /** + * Set the amount of time it took the server to generate this action, in milliseconds. + * This value is used to process the + * Page speed report + * Avg. generation time column in the Page URL and Page Title reports, + * as well as a site wide running average of the speed of your server. + * + * @param actionTime the amount of time to set. A null value will remove this parameter + */ + public void setActionTime(Long actionTime) { + setParameter(ACTION_TIME, actionTime); + } + + /** + * Get the full URL for the current action. + * + * @return the full URL + * @throws TypeConstraintException if the stored Action URL is a string + */ + public URL getActionUrl() { + return returnAsUrl(ACTION_URL, "Action URL", "getActionUrlAsString"); + } + + /** + * Get the full URL for the current action. + * + * @return the full URL + * @throws TypeConstraintException if the stored Action URL is a URL + */ + public String getActionUrlAsString() { + return returnAsString(ACTION_URL, "Action URL", "getActionUrl"); + } + + /** + * Set the full URL for the current action. + * + * @param actionUrl the full URL to set. A null value will remove this parameter + */ + public void setActionUrl(URL actionUrl) { + setParameter(ACTION_URL, actionUrl); + } + + /** + * Set the full URL for the current action. + * + * @param actionUrl the full URL to set. A null value will remove this parameter + */ + public void setActionUrlWithString(String actionUrl) { + setParameter(ACTION_URL, actionUrl); + } + + /** + * Get the api version + * + * @return the api version + */ + public String getApiVersion() { + return (String) getParameter(API_VERSION); + } + + /** + * Set the api version to use (currently always set to 1) + * + * @param apiVersion the api version to set. A null value will remove this parameter + */ + public void setApiVersion(String apiVersion) { + setParameter(API_VERSION, apiVersion); + } + + /** + * Get the authorization key. + * + * @return the authorization key + */ + public String getAuthToken() { + return (String) getParameter(AUTH_TOKEN); + } + + /** + * Set the {@value #AUTH_TOKEN_LENGTH} character authorization key used to authenticate the API request. + * + * @param authToken the authorization key to set. A null value will remove this parameter + */ + public void setAuthToken(String authToken) { + if (authToken != null && authToken.length() != AUTH_TOKEN_LENGTH) { + throw new IllegalArgumentException(authToken + " is not " + AUTH_TOKEN_LENGTH + " characters long."); + } + setParameter(AUTH_TOKEN, authToken); + } + + /** + * Verifies that AuthToken has been set for this request. Will throw an + * {@link IllegalStateException} if not. + */ + public void verifyAuthTokenSet() { + if (getAuthToken() == null) { + throw new IllegalStateException("AuthToken must be set before this value can be set."); + } + } + + /** + * Get the campaign keyword + * + * @return the campaign keyword + */ + public String getCampaignKeyword() { + return (String) getParameter(CAMPAIGN_KEYWORD); + } + + /** + * Set the Campaign Keyword (see + * Tracking Campaigns). + * Used to populate the Referrers > Campaigns report (clicking on a + * campaign loads all keywords for this campaign). Note: this parameter + * will only be used for the first pageview of a visit. + * + * @param campaignKeyword the campaign keyword to set. A null value will remove this parameter + */ + public void setCampaignKeyword(String campaignKeyword) { + setParameter(CAMPAIGN_KEYWORD, campaignKeyword); + } + + /** + * Get the campaign name + * + * @return the campaign name + */ + public String getCampaignName() { + return (String) getParameter(CAMPAIGN_NAME); + } + + /** + * Set the Campaign Name (see + * Tracking Campaigns). + * Used to populate the Referrers > Campaigns report. Note: this parameter + * will only be used for the first pageview of a visit. + * + * @param campaignName the campaign name to set. A null value will remove this parameter + */ + public void setCampaignName(String campaignName) { + setParameter(CAMPAIGN_NAME, campaignName); + } + + /** + * Get the charset of the page being tracked + * + * @return the charset + */ + public Charset getCharacterSet() { + return (Charset) getParameter(CHARACTER_SET); + } + + /** + * The charset of the page being tracked. Specify the charset if the data + * you send to Piwik is encoded in a different character set than the default + * utf-8. + * + * @param characterSet the charset to set. A null value will remove this parameter + */ + public void setCharacterSet(Charset characterSet) { + setParameter(CHARACTER_SET, characterSet); + } + + /** + * Get the name of the interaction with the content + * + * @return the name of the interaction + */ + public String getContentInteraction() { + return (String) getParameter(CONTENT_INTERACTION); + } + + /** + * Set the name of the interaction with the content. For instance a 'click'. + * + * @param contentInteraction the name of the interaction to set. A null value will remove this parameter + */ + public void setContentInteraction(String contentInteraction) { + setParameter(CONTENT_INTERACTION, contentInteraction); + } + + /** + * Get the name of the content + * + * @return the name + */ + public String getContentName() { + return (String) getParameter(CONTENT_NAME); + } + + /** + * Set the name of the content. For instance 'Ad Foo Bar'. + * + * @param contentName the name to set. A null value will remove this parameter + */ + public void setContentName(String contentName) { + setParameter(CONTENT_NAME, contentName); + } + + /** + * Get the content piece. + * + * @return the content piece. + */ + public String getContentPiece() { + return (String) getParameter(CONTENT_PIECE); + } + + /** + * Set the actual content piece. For instance the path to an image, video, audio, any text. + * + * @param contentPiece the content piece to set. A null value will remove this parameter + */ + public void setContentPiece(String contentPiece) { + setParameter(CONTENT_PIECE, contentPiece); + } + + /** + * Get the content target + * + * @return the target + * @throws TypeConstraintException if the stored Content Target is a string + */ + public URL getContentTarget() { + return returnAsUrl(CONTENT_TARGET, "Content Target", "getContentTargetAsString"); + } + + /** + * Get the content target + * + * @return the target + * @throws TypeConstraintException if the stored Content Target is a URL + */ + public String getContentTargetAsString() { + return returnAsString(CONTENT_TARGET, "Content Target", "getContentTarget"); + } + + /** + * Set the target of the content. For instance the URL of a landing page. + * + * @param contentTarget the target to set. A null value will remove this parameter + */ + public void setContentTarget(URL contentTarget) { + setParameter(CONTENT_TARGET, contentTarget); + } + + /** + * Set the target of the content. For instance the URL of a landing page. + * + * @param contentTarget the target to set. A null value will remove this parameter + */ + public void setContentTargetWithString(String contentTarget) { + setParameter(CONTENT_TARGET, contentTarget); + } + + /** + * Get the current hour. + * + * @return the current hour + */ + public Integer getCurrentHour() { + return (Integer) getParameter(CURRENT_HOUR); + } + + /** + * Set the current hour (local time). + * + * @param currentHour the hour to set. A null value will remove this parameter + */ + public void setCurrentHour(Integer currentHour) { + setParameter(CURRENT_HOUR, currentHour); + } + + /** + * Get the current minute. + * + * @return the current minute + */ + public Integer getCurrentMinute() { + return (Integer) getParameter(CURRENT_MINUTE); + } + + /** + * Set the current minute (local time). + * + * @param currentMinute the minute to set. A null value will remove this parameter + */ + public void setCurrentMinute(Integer currentMinute) { + setParameter(CURRENT_MINUTE, currentMinute); + } + + /** + * Get the current second + * + * @return the current second + */ + public Integer getCurrentSecond() { + return (Integer) getParameter(CURRENT_SECOND); + } + + /** + * Set the current second (local time). + * + * @param currentSecond the second to set. A null value will remove this parameter + */ + public void setCurrentSecond(Integer currentSecond) { + setParameter(CURRENT_SECOND, currentSecond); + } + + /** + * Gets the list of objects currently stored at the specified custom tracking + * parameter. An empty list will be returned if there are no objects set at + * that key. + * + * @param key the key of the parameter whose list of objects to get. Cannot be null + * @return the list of objects currently stored at the specified key + */ + public List getCustomTrackingParameter(String key) { + if (key == null) { + throw new NullPointerException("Key cannot be null."); + } + List l = customTrackingParameters.get(key); + if (l == null) { + return new ArrayList(0); + } + return new ArrayList(l); + } + + /** + * Set a custom tracking parameter whose toString() value will be sent to + * the Piwik server. These parameters are stored separately from named Piwik + * parameters, meaning it is not possible to overwrite or clear named Piwik + * parameters with this method. A custom parameter that has the same name + * as a named Piwik parameter will be sent in addition to that named parameter. + * + * @param key the parameter's key. Cannot be null + * @param value the parameter's value. Removes the parameter if null + */ + public void setCustomTrackingParameter(String key, Object value) { + if (key == null) { + throw new NullPointerException("Key cannot be null."); + } + if (value == null) { + customTrackingParameters.remove(key); + } else { + List l = new ArrayList(); + l.add(value); + customTrackingParameters.put(key, l); + } + } + + /** + * Add a custom tracking parameter to the specified key. This allows users + * to have multiple parameters with the same name and different values, + * commonly used during situations where list parameters are needed + * + * @param key the parameter's key. Cannot be null + * @param value the parameter's value. Cannot be null + */ + public void addCustomTrackingParameter(String key, Object value) { + if (key == null) { + throw new NullPointerException("Key cannot be null."); + } + if (value == null) { + throw new NullPointerException("Cannot add a null custom tracking parameter."); + } else { + List l = customTrackingParameters.get(key); + if (l == null) { + l = new ArrayList(); + customTrackingParameters.put(key, l); + } + l.add(value); + } + } + + /** + * Removes all custom tracking parameters + */ + public void clearCustomTrackingParameter() { + customTrackingParameters.clear(); + } + + /** + * Get the resolution of the device + * + * @return the resolution + */ + public String getDeviceResolution() { + return (String) getParameter(DEVICE_RESOLUTION); + } + + /** + * Set the resolution of the device the visitor is using, eg 1280x1024. + * + * @param deviceResolution the resolution to set. A null value will remove this parameter + */ + public void setDeviceResolution(String deviceResolution) { + setParameter(DEVICE_RESOLUTION, deviceResolution); + } + + /** + * Get the url of a file the user had downloaded + * + * @return the url + * @throws TypeConstraintException if the stored Download URL is a String + */ + public URL getDownloadUrl() { + return returnAsUrl(DOWNLOAD_URL, "Download URL", "getDownloadUrlAsString"); + } + + /** + * Get the url of a file the user had downloaded + * + * @return the url + * @throws TypeConstraintException if the stored Download URL is a URL + */ + public String getDownloadUrlAsString() { + return returnAsString(DOWNLOAD_URL, "Download URL", "getDownloadUrl"); + } + + /** + * Set the url of a file the user has downloaded. Used for tracking downloads. + * We recommend to also set the url parameter to this same value. + * + * @param downloadUrl the url to set. A null value will remove this parameter + */ + public void setDownloadUrl(URL downloadUrl) { + setParameter(DOWNLOAD_URL, downloadUrl); + } + + /** + * Set the url of a file the user has downloaded. Used for tracking downloads. + * We recommend to also set the url parameter to this same value. + * + * @param downloadUrl the url to set. A null value will remove this parameter + */ + public void setDownloadUrlWithString(String downloadUrl) { + setParameter(DOWNLOAD_URL, downloadUrl); + } + + /** + * Sets idgoal=0 in the request to track an ecommerce interaction: + * cart update or an ecommerce order. + */ + public void enableEcommerce() { + setGoalId(0); + } + + /** + * Verifies that Ecommerce has been enabled for the request. Will throw an + * {@link IllegalStateException} if not. + */ + public void verifyEcommerceEnabled() { + if (getGoalId() == null || getGoalId() != 0) { + throw new IllegalStateException("GoalId must be \"0\". Try calling enableEcommerce first before calling this method."); + } + } + + /** + * Verifies that Ecommerce has been enabled and that Ecommerce Id and + * Ecommerce Revenue have been set for the request. Will throw an + * {@link IllegalStateException} if not. + */ + public void verifyEcommerceState() { + verifyEcommerceEnabled(); + if (getEcommerceId() == null) { + throw new IllegalStateException("EcommerceId must be set before this value can be set."); + } + if (getEcommerceRevenue() == null) { + throw new IllegalStateException("EcommerceRevenue must be set before this value can be set."); + } + } + + /** + * Get the discount offered. + * + * @return the discount + */ + public Double getEcommerceDiscount() { + return (Double) getParameter(ECOMMERCE_DISCOUNT); + } + + /** + * Set the discount offered. Ecommerce must be enabled, and EcommerceId and + * EcommerceRevenue must first be set. + * + * @param discount the discount to set. A null value will remove this parameter + */ + public void setEcommerceDiscount(Double discount) { + if (discount != null) { + verifyEcommerceState(); + } + setParameter(ECOMMERCE_DISCOUNT, discount); + } + + /** + * Get the id of this order. + * + * @return the id + */ + public String getEcommerceId() { + return (String) getParameter(ECOMMERCE_ID); + } + + /** + * Set the unique string identifier for the ecommerce order (required when + * tracking an ecommerce order). Ecommerce must be enabled. + * + * @param id the id to set. A null value will remove this parameter + */ + public void setEcommerceId(String id) { + if (id != null) { + verifyEcommerceEnabled(); + } + setParameter(ECOMMERCE_ID, id); + } + + /** + * Get the {@link EcommerceItem} at the specified index + * + * @param index the index of the {@link EcommerceItem} to return + * @return the {@link EcommerceItem} at the specified index + */ + public EcommerceItem getEcommerceItem(int index) { + return (EcommerceItem) getFromJsonArray(ECOMMERCE_ITEMS, index); + } + + /** + * Add an {@link EcommerceItem} to this order. Ecommerce must be enabled, + * and EcommerceId and EcommerceRevenue must first be set. + * + * @param item the {@link EcommerceItem} to add. Cannot be null + */ + public void addEcommerceItem(EcommerceItem item) { + if (item != null) { + verifyEcommerceState(); + } + addToJsonArray(ECOMMERCE_ITEMS, item); + } + + /** + * Clears all {@link EcommerceItem} from this order. + */ + public void clearEcommerceItems() { + removeJsonArray(ECOMMERCE_ITEMS); + } + + /** + * Get the timestamp of the customer's last ecommerce order + * + * @return the timestamp + */ + public Long getEcommerceLastOrderTimestamp() { + return (Long) getParameter(ECOMMERCE_LAST_ORDER_TIMESTAMP); + } + + /** + * Set the UNUX timestamp of this customer's last ecommerce order. This value + * is used to process the "Days since last order" report. Ecommerce must be + * enabled, and EcommerceId and EcommerceRevenue must first be set. + * + * @param timestamp the timestamp to set. A null value will remove this parameter + */ + public void setEcommerceLastOrderTimestamp(Long timestamp) { + if (timestamp != null) { + verifyEcommerceState(); + } + setParameter(ECOMMERCE_LAST_ORDER_TIMESTAMP, timestamp); + } + + /** + * Get the grand total of the ecommerce order. + * + * @return the grand total + */ + public Double getEcommerceRevenue() { + return (Double) getParameter(ECOMMERCE_REVENUE); + } + + /** + * Set the grand total of the ecommerce order (required when tracking an + * ecommerce order). Ecommerce must be enabled. + * + * @param revenue the grand total to set. A null value will remove this parameter + */ + public void setEcommerceRevenue(Double revenue) { + if (revenue != null) { + verifyEcommerceEnabled(); + } + setParameter(ECOMMERCE_REVENUE, revenue); + } + + /** + * Get the shipping cost of the ecommerce order. + * + * @return the shipping cost + */ + public Double getEcommerceShippingCost() { + return (Double) getParameter(ECOMMERCE_SHIPPING_COST); + } + + /** + * Set the shipping cost of the ecommerce order. Ecommerce must be enabled, + * and EcommerceId and EcommerceRevenue must first be set. + * + * @param shippingCost the shipping cost to set. A null value will remove this parameter + */ + public void setEcommerceShippingCost(Double shippingCost) { + if (shippingCost != null) { + verifyEcommerceState(); + } + setParameter(ECOMMERCE_SHIPPING_COST, shippingCost); + } + + /** + * Get the subtotal of the ecommerce order; excludes shipping. + * + * @return the subtotal + */ + public Double getEcommerceSubtotal() { + return (Double) getParameter(ECOMMERCE_SUBTOTAL); + } + + /** + * Set the subtotal of the ecommerce order; excludes shipping. Ecommerce + * must be enabled and EcommerceId and EcommerceRevenue must first be set. + * + * @param subtotal the subtotal to set. A null value will remove this parameter + */ + public void setEcommerceSubtotal(Double subtotal) { + if (subtotal != null) { + verifyEcommerceState(); + } + setParameter(ECOMMERCE_SUBTOTAL, subtotal); + } + + /** + * Get the tax amount of the ecommerce order. + * + * @return the tax amount + */ + public Double getEcommerceTax() { + return (Double) getParameter(ECOMMERCE_TAX); + } + + /** + * Set the tax amount of the ecommerce order. Ecommerce must be enabled, and + * EcommerceId and EcommerceRevenue must first be set. + * + * @param tax the tax amount to set. A null value will remove this parameter + */ + public void setEcommerceTax(Double tax) { + if (tax != null) { + verifyEcommerceState(); + } + setParameter(ECOMMERCE_TAX, tax); + } + + /** + * Get the event action. + * + * @return the event action + */ + public String getEventAction() { + return getNonEmptyStringParameter(EVENT_ACTION); + } + + /** + * Set the event action. Must not be empty. (eg. Play, Pause, Duration, + * Add Playlist, Downloaded, Clicked...). + * + * @param eventAction the event action to set. A null value will remove this parameter + */ + public void setEventAction(String eventAction) { + setNonEmptyStringParameter(EVENT_ACTION, eventAction); + } + + /** + * Get the event category. + * + * @return the event category + */ + public String getEventCategory() { + return getNonEmptyStringParameter(EVENT_CATEGORY); + } + + /** + * Set the event category. Must not be empty. (eg. Videos, Music, Games...). + * + * @param eventCategory the event category to set. A null value will remove this parameter + */ + public void setEventCategory(String eventCategory) { + setNonEmptyStringParameter(EVENT_CATEGORY, eventCategory); + } + + /** + * Get the event name. + * + * @return the event name + */ + public String getEventName() { + return (String) getParameter(EVENT_NAME); + } + + /** + * Set the event name. (eg. a Movie name, or Song name, or File name...). + * + * @param eventName the event name to set. A null value will remove this parameter + */ + public void setEventName(String eventName) { + setParameter(EVENT_NAME, eventName); + } + + /** + * Get the event value. + * + * @return the event value + */ + public Number getEventValue() { + return (Number) getParameter(EVENT_VALUE); + } + + /** + * Set the event value. Must be a float or integer value (numeric), not a string. + * + * @param eventValue the event value to set. A null value will remove this parameter + */ + public void setEventValue(Number eventValue) { + setParameter(EVENT_VALUE, eventValue); + } + + /** + * Get the goal id + * + * @return the goal id + */ + public Integer getGoalId() { + return (Integer) getParameter(GOAL_ID); + } + + /** + * Set the goal id. If specified, the tracking request will trigger a + * conversion for the goal of the website being tracked with this id. + * + * @param goalId the goal id to set. A null value will remove this parameter + */ + public void setGoalId(Integer goalId) { + setParameter(GOAL_ID, goalId); + } + + /** + * Get the goal revenue. + * + * @return the goal revenue + */ + public Double getGoalRevenue() { + return (Double) getParameter(GOAL_REVENUE); + } + + /** + * Set a monetary value that was generated as revenue by this goal conversion. + * Only used if idgoal is specified in the request. + * + * @param goalRevenue the goal revenue to set. A null value will remove this parameter + */ + public void setGoalRevenue(Double goalRevenue) { + if (goalRevenue != null && getGoalId() == null) { + throw new IllegalStateException("GoalId must be set before GoalRevenue can be set."); + } + setParameter(GOAL_REVENUE, goalRevenue); + } + + /** + * Get the Accept-Language HTTP header + * + * @return the Accept-Language HTTP header + */ + public String getHeaderAcceptLanguage() { + return (String) getParameter(HEADER_ACCEPT_LANGUAGE); + } + + /** + * Set an override value for the Accept-Language HTTP header + * field. This value is used to detect the visitor's country if + * GeoIP is not enabled. + * + * @param acceptLangage the Accept-Language HTTP header to set. A null value will remove this parameter + */ + public void setHeaderAcceptLanguage(String acceptLangage) { + setParameter(HEADER_ACCEPT_LANGUAGE, acceptLangage); + } + + /** + * Get the User-Agent HTTP header + * + * @return the User-Agent HTTP header + */ + public String getHeaderUserAgent() { + return (String) getParameter(HEADER_USER_AGENT); + } + + /** + * Set an override value for the User-Agent HTTP header field. + * The user agent is used to detect the operating system and browser used. + * + * @param userAgent the User-Agent HTTP header tos et + */ + public void setHeaderUserAgent(String userAgent) { + setParameter(HEADER_USER_AGENT, userAgent); + } + + /** + * Get if this request will force a new visit. + * + * @return true if this request will force a new visit + */ + public Boolean getNewVisit() { + return getBooleanParameter(NEW_VISIT); + } + + /** + * If set to true, will force a new visit to be created for this action. + * + * @param newVisit if this request will force a new visit + */ + public void setNewVisit(Boolean newVisit) { + setBooleanParameter(NEW_VISIT, newVisit); + } + + /** + * Get the outlink url + * + * @return the outlink url + * @throws TypeConstraintException if the stored Outlink URL is a String + */ + public URL getOutlinkUrl() { + return returnAsUrl(OUTLINK_URL, "Outlink URL", "getOutlinkUrlAsString"); + } + + /** + * Get the outlink url + * + * @return the outlink url + * @throws TypeConstraintException if the stored Outlink URL is a URL + */ + public String getOutlinkUrlAsString() { + return returnAsString(OUTLINK_URL, "Outlink URL", "getOutlinkUrl"); + } + + /** + * Set an external URL the user has opened. Used for tracking outlink clicks. + * We recommend to also set the url parameter to this same value. + * + * @param outlinkUrl the outlink url to set. A null value will remove this parameter + */ + public void setOutlinkUrl(URL outlinkUrl) { + setParameter(OUTLINK_URL, outlinkUrl); + } + + /** + * Set an external URL the user has opened. Used for tracking outlink clicks. + * We recommend to also set the url parameter to this same value. + * + * @param outlinkUrl the outlink url to set. A null value will remove this parameter + */ + public void setOutlinkUrlWithString(String outlinkUrl) { + setParameter(OUTLINK_URL, outlinkUrl); + } + + /** + * Get the page custom variable at the specified key. + * + * @param key the key of the variable to get + * @return the variable at the specified key, null if key is not present + * @deprecated Use the {@link #getPageCustomVariable(int)} method instead. + */ + @Deprecated + public String getPageCustomVariable(String key) { + return getCustomVariable(PAGE_CUSTOM_VARIABLE, key); + } + + /** + * Get the page custom variable at the specified index. + * + * @param index the index of the variable to get. Must be greater than 0 + * @return the variable at the specified key, null if nothing at this index + */ + public CustomVariable getPageCustomVariable(int index) { + return getCustomVariable(PAGE_CUSTOM_VARIABLE, index); + } + + /** + * Set a page custom variable with the specified key and value at the first available index. + * All page custom variables with this key will be overwritten or deleted + * + * @param key the key of the variable to set + * @param value the value of the variable to set at the specified key. A null value will remove this custom variable + * @deprecated Use the {@link #setPageCustomVariable(CustomVariable, int)} method instead. + */ + @Deprecated + public void setPageCustomVariable(String key, String value) { + if (value == null) { + removeCustomVariable(PAGE_CUSTOM_VARIABLE, key); + } else { + setCustomVariable(PAGE_CUSTOM_VARIABLE, new CustomVariable(key, value), null); + } + } + + /** + * Set a page custom variable at the specified index. + * + * @param customVariable the CustomVariable to set. A null value will remove the CustomVariable at the specified index + * @param index the index of he CustomVariable to set + */ + public void setPageCustomVariable(CustomVariable customVariable, int index) { + setCustomVariable(PAGE_CUSTOM_VARIABLE, customVariable, index); + } + + /** + * Check if the visitor has the Director plugin. + * + * @return true if visitor has the Director plugin + */ + public Boolean getPluginDirector() { + return getBooleanParameter(PLUGIN_DIRECTOR); + } + + /** + * Set if the visitor has the Director plugin. + * + * @param director true if the visitor has the Director plugin + */ + public void setPluginDirector(Boolean director) { + setBooleanParameter(PLUGIN_DIRECTOR, director); + } + + /** + * Check if the visitor has the Flash plugin. + * + * @return true if the visitor has the Flash plugin + */ + public Boolean getPluginFlash() { + return getBooleanParameter(PLUGIN_FLASH); + } + + /** + * Set if the visitor has the Flash plugin. + * + * @param flash true if the visitor has the Flash plugin + */ + public void setPluginFlash(Boolean flash) { + setBooleanParameter(PLUGIN_FLASH, flash); + } + + /** + * Check if the visitor has the Gears plugin. + * + * @return true if the visitor has the Gears plugin + */ + public Boolean getPluginGears() { + return getBooleanParameter(PLUGIN_GEARS); + } + + /** + * Set if the visitor has the Gears plugin. + * + * @param gears true if the visitor has the Gears plugin + */ + public void setPluginGears(Boolean gears) { + setBooleanParameter(PLUGIN_GEARS, gears); + } + + /** + * Check if the visitor has the Java plugin. + * + * @return true if the visitor has the Java plugin + */ + public Boolean getPluginJava() { + return getBooleanParameter(PLUGIN_JAVA); + } + + /** + * Set if the visitor has the Java plugin. + * + * @param java true if the visitor has the Java plugin + */ + public void setPluginJava(Boolean java) { + setBooleanParameter(PLUGIN_JAVA, java); + } + + /** + * Check if the visitor has the PDF plugin. + * + * @return true if the visitor has the PDF plugin + */ + public Boolean getPluginPDF() { + return getBooleanParameter(PLUGIN_PDF); + } + + /** + * Set if the visitor has the PDF plugin. + * + * @param pdf true if the visitor has the PDF plugin + */ + public void setPluginPDF(Boolean pdf) { + setBooleanParameter(PLUGIN_PDF, pdf); + } + + /** + * Check if the visitor has the Quicktime plugin. + * + * @return true if the visitor has the Quicktime plugin + */ + public Boolean getPluginQuicktime() { + return getBooleanParameter(PLUGIN_QUICKTIME); + } + + /** + * Set if the visitor has the Quicktime plugin. + * + * @param quicktime true if the visitor has the Quicktime plugin + */ + public void setPluginQuicktime(Boolean quicktime) { + setBooleanParameter(PLUGIN_QUICKTIME, quicktime); + } + + /** + * Check if the visitor has the RealPlayer plugin. + * + * @return true if the visitor has the RealPlayer plugin + */ + public Boolean getPluginRealPlayer() { + return getBooleanParameter(PLUGIN_REAL_PLAYER); + } + + /** + * Set if the visitor has the RealPlayer plugin. + * + * @param realPlayer true if the visitor has the RealPlayer plugin + */ + public void setPluginRealPlayer(Boolean realPlayer) { + setBooleanParameter(PLUGIN_REAL_PLAYER, realPlayer); + } + + /** + * Check if the visitor has the Silverlight plugin. + * + * @return true if the visitor has the Silverlight plugin + */ + public Boolean getPluginSilverlight() { + return getBooleanParameter(PLUGIN_SILVERLIGHT); + } + + /** + * Set if the visitor has the Silverlight plugin. + * + * @param silverlight true if the visitor has the Silverlight plugin + */ + public void setPluginSilverlight(Boolean silverlight) { + setBooleanParameter(PLUGIN_SILVERLIGHT, silverlight); + } + + /** + * Check if the visitor has the Windows Media plugin. + * + * @return true if the visitor has the Windows Media plugin + */ + public Boolean getPluginWindowsMedia() { + return getBooleanParameter(PLUGIN_WINDOWS_MEDIA); + } + + /** + * Set if the visitor has the Windows Media plugin. + * + * @param windowsMedia true if the visitor has the Windows Media plugin + */ + public void setPluginWindowsMedia(Boolean windowsMedia) { + setBooleanParameter(PLUGIN_WINDOWS_MEDIA, windowsMedia); + } + + /** + * Get the random value for this request + * + * @return the random value + */ + public String getRandomValue() { + return (String) getParameter(RANDOM_VALUE); + } + + /** + * Set a random value that is generated before each request. Using it helps + * avoid the tracking request being cached by the browser or a proxy. + * + * @param randomValue the random value to set. A null value will remove this parameter + */ + public void setRandomValue(String randomValue) { + setParameter(RANDOM_VALUE, randomValue); + } + + /** + * Get the referrer url + * + * @return the referrer url + * @throws TypeConstraintException if the stored Referrer URL is a String + */ + public URL getReferrerUrl() { + return returnAsUrl(REFERRER_URL, "Referrer URL", "getReferrerUrlAsString"); + } + + /** + * Get the referrer url + * + * @return the referrer url + * @throws TypeConstraintException if the stored Referrer URL is a URL + */ + public String getReferrerUrlAsString() { + return returnAsString(REFERRER_URL, "Referrer URL", "getReferrerUrl"); + } + + /** + * Set the full HTTP Referrer URL. This value is used to determine how someone + * got to your website (ie, through a website, search engine or campaign). + * + * @param referrerUrl the referrer url to set. A null value will remove this parameter + */ + public void setReferrerUrl(URL referrerUrl) { + setParameter(REFERRER_URL, referrerUrl); + } + + /** + * Set the full HTTP Referrer URL. This value is used to determine how someone + * got to your website (ie, through a website, search engine or campaign). + * + * @param referrerUrl the referrer url to set. A null value will remove this parameter + */ + public void setReferrerUrlWithString(String referrerUrl) { + setParameter(REFERRER_URL, referrerUrl); + } + + /** + * Get the datetime of the request + * + * @return the datetime of the request + */ + public PiwikDate getRequestDatetime() { + return (PiwikDate) getParameter(REQUEST_DATETIME); + } + + /** + * Set the datetime of the request (normally the current time is used). + * This can be used to record visits and page views in the past. The datetime + * must be sent in UTC timezone. Note: if you record data in the past, you will + * need to force Piwik to re-process + * reports for the past dates. If you set the Request Datetime to a datetime + * older than four hours then Auth Token must be set. If you set + * Request Datetime with a datetime in the last four hours then you + * don't need to pass Auth Token. + * + * @param datetime the datetime of the request to set. A null value will remove this parameter + */ + public void setRequestDatetime(PiwikDate datetime) { + if (datetime != null && new Date().getTime() - datetime.getTime() > REQUEST_DATETIME_AUTH_LIMIT && getAuthToken() == null) { + throw new IllegalStateException("Because you are trying to set RequestDatetime for a time greater than 4 hours ago, AuthToken must be set first."); + } + setParameter(REQUEST_DATETIME, datetime); + } + + /** + * Get if this request will be tracked. + * + * @return true if request will be tracked + */ + public Boolean getRequired() { + return getBooleanParameter(REQUIRED); + } + + /** + * Set if this request will be tracked by the Piwik server. + * + * @param required true if request will be tracked + */ + public void setRequired(Boolean required) { + setBooleanParameter(REQUIRED, required); + } + + /** + * Get if the response will be an image. + * + * @return true if the response will be an an image + */ + public Boolean getResponseAsImage() { + return getBooleanParameter(RESPONSE_AS_IMAGE); + } + + /** + * Set if the response will be an image. If set to false, Piwik will respond + * with a HTTP 204 response code instead of a GIF image. This improves performance + * and can fix errors if images are not allowed to be obtained directly + * (eg Chrome Apps). Available since Piwik 2.10.0. + * + * @param responseAsImage true if the response will be an image + */ + public void setResponseAsImage(Boolean responseAsImage) { + setBooleanParameter(RESPONSE_AS_IMAGE, responseAsImage); + } + + /** + * Get the search category + * + * @return the search category + */ + public String getSearchCategory() { + return (String) getParameter(SEARCH_CATEGORY); + } + + /** + * Specify a search category with this parameter. SearchQuery must first be + * set. + * + * @param searchCategory the search category to set. A null value will remove this parameter + */ + public void setSearchCategory(String searchCategory) { + if (searchCategory != null && getSearchQuery() == null) { + throw new IllegalStateException("SearchQuery must be set before SearchCategory can be set."); + } + setParameter(SEARCH_CATEGORY, searchCategory); + } + + /** + * Get the search query. + * + * @return the search query + */ + public String getSearchQuery() { + return (String) getParameter(SEARCH_QUERY); + } + + /** + * Set the search query. When specified, the request will not be tracked as + * a normal pageview but will instead be tracked as a Site Search request. + * + * @param searchQuery the search query to set. A null value will remove this parameter + */ + public void setSearchQuery(String searchQuery) { + setParameter(SEARCH_QUERY, searchQuery); + } + + /** + * Get the search results count. + * + * @return the search results count + */ + public Long getSearchResultsCount() { + return (Long) getParameter(SEARCH_RESULTS_COUNT); + } + + /** + * We recommend to set the + * search count to the number of search results displayed on the results page. + * When keywords are tracked with {@code Search Results Count=0} they will appear in + * the "No Result Search Keyword" report. SearchQuery must first be set. + * + * @param searchResultsCount the search results count to set. A null value will remove this parameter + */ + public void setSearchResultsCount(Long searchResultsCount) { + if (searchResultsCount != null && getSearchQuery() == null) { + throw new IllegalStateException("SearchQuery must be set before SearchResultsCount can be set."); + } + setParameter(SEARCH_RESULTS_COUNT, searchResultsCount); + } + + /** + * Get the id of the website we're tracking. + * + * @return the id of the website + */ + public Integer getSiteId() { + return (Integer) getParameter(SITE_ID); + } + + /** + * Set the ID of the website we're tracking a visit/action for. + * + * @param siteId the id of the website to set. A null value will remove this parameter + */ + public void setSiteId(Integer siteId) { + setParameter(SITE_ID, siteId); + } + + /** + * Set if bot requests should be tracked + * + * @return true if bot requests should be tracked + */ + public Boolean getTrackBotRequests() { + return getBooleanParameter(TRACK_BOT_REQUESTS); + } + + /** + * By default Piwik does not track bots. If you use the Tracking Java API, + * you may be interested in tracking bot requests. To enable Bot Tracking in + * Piwik, set Track Bot Requests to true. + * + * @param trackBotRequests true if bot requests should be tracked + */ + public void setTrackBotRequests(Boolean trackBotRequests) { + setBooleanParameter(TRACK_BOT_REQUESTS, trackBotRequests); + } + + /** + * Get the visit custom variable at the specified key. + * + * @param key the key of the variable to get + * @return the variable at the specified key, null if key is not present + * @deprecated Use the {@link #getVisitCustomVariable(int)} method instead. + */ + @Deprecated + public String getUserCustomVariable(String key) { + return getCustomVariable(VISIT_CUSTOM_VARIABLE, key); + } + + /** + * Get the visit custom variable at the specified index. + * + * @param index the index of the variable to get + * @return the variable at the specified index, null if nothing at this index + */ + public CustomVariable getVisitCustomVariable(int index) { + return getCustomVariable(VISIT_CUSTOM_VARIABLE, index); + } + + /** + * Set a visit custom variable with the specified key and value at the first available index. + * All visit custom variables with this key will be overwritten or deleted + * + * @param key the key of the variable to set + * @param value the value of the variable to set at the specified key. A null value will remove this parameter + * @deprecated Use the {@link #setVisitCustomVariable(CustomVariable, int)} method instead. + */ + @Deprecated + public void setUserCustomVariable(String key, String value) { + if (value == null) { + removeCustomVariable(VISIT_CUSTOM_VARIABLE, key); + } else { + setCustomVariable(VISIT_CUSTOM_VARIABLE, new CustomVariable(key, value), null); + } + } + + /** + * Set a user custom variable at the specified key. + * + * @param customVariable the CustomVariable to set. A null value will remove the custom variable at the specified index + * @param index the index to set the customVariable at. + */ + public void setVisitCustomVariable(CustomVariable customVariable, int index) { + setCustomVariable(VISIT_CUSTOM_VARIABLE, customVariable, index); + } + + /** + * Get the user id for this request. + * + * @return the user id + */ + public String getUserId() { + return (String) getParameter(USER_ID); + } + + /** + * Set the user id for this request. + * User id is any non empty unique string identifying the user (such as an email + * address or a username). To access this value, users must be logged-in in your + * system so you can fetch this user id from your system, and pass it to Piwik. + * The user id appears in the visitor log, the Visitor profile, and you can + * Segment + * reports for one or several user ids. When specified, the user id will be + * "enforced". This means that if there is no recent visit with this user id, + * a new one will be created. If a visit is found in the last 30 minutes with + * your specified user id, then the new action will be recorded to this existing visit. + * + * @param userId the user id to set. A null value will remove this parameter + */ + public void setUserId(String userId) { + setNonEmptyStringParameter(USER_ID, userId); + } + + /** + * Get the visitor's city. + * + * @return the visitor's city + */ + public String getVisitorCity() { + return (String) getParameter(VISITOR_CITY); + } + + /** + * Set an override value for the city. The name of the city the visitor is + * located in, eg, Tokyo. AuthToken must first be set. + * + * @param city the visitor's city to set. A null value will remove this parameter + */ + public void setVisitorCity(String city) { + if (city != null) { + verifyAuthTokenSet(); + } + setParameter(VISITOR_CITY, city); + } + + /** + * Get the visitor's country. + * + * @return the visitor's country + */ + public PiwikLocale getVisitorCountry() { + return (PiwikLocale) getParameter(VISITOR_COUNTRY); + } + + /** + * Set an override value for the country. AuthToken must first be set. + * + * @param country the visitor's country to set. A null value will remove this parameter + */ + public void setVisitorCountry(PiwikLocale country) { + if (country != null) { + verifyAuthTokenSet(); + } + setParameter(VISITOR_COUNTRY, country); + } + + /** + * Get the visitor's custom id. + * + * @return the visitor's custom id + */ + public String getVisitorCustomId() { + return (String) getParameter(VISITOR_CUSTOM_ID); + } + + /** + * Set a custom visitor ID for this request. You must set this value to exactly + * a {@value #ID_LENGTH} character hexadecimal string (containing only characters 01234567890abcdefABCDEF). + * We recommended to set the UserId rather than the VisitorCustomId. + * + * @param visitorCustomId the visitor's custom id to set. A null value will remove this parameter + */ + public void setVisitorCustomId(String visitorCustomId) { + if (visitorCustomId != null) { + if (visitorCustomId.length() != ID_LENGTH) { + throw new IllegalArgumentException(visitorCustomId + " is not " + ID_LENGTH + " characters long."); + } + // Verify visitorID is a 16 character hexadecimal string + else if (!visitorCustomId.matches("[0-9A-Fa-f]+")) { + throw new IllegalArgumentException(visitorCustomId + " is not a hexadecimal string."); + } + } + setParameter(VISITOR_CUSTOM_ID, visitorCustomId); + } + + /** + * Get the timestamp of the visitor's first visit. + * + * @return the timestamp of the visitor's first visit + */ + public Long getVisitorFirstVisitTimestamp() { + return (Long) getParameter(VISITOR_FIRST_VISIT_TIMESTAMP); + } + + /** + * Set the UNIX timestamp of this visitor's first visit. This could be set + * to the date where the user first started using your software/app, or when + * he/she created an account. This parameter is used to populate the + * Goals > Days to Conversion report. + * + * @param timestamp the timestamp of the visitor's first visit to set. A null value will remove this parameter + */ + public void setVisitorFirstVisitTimestamp(Long timestamp) { + setParameter(VISITOR_FIRST_VISIT_TIMESTAMP, timestamp); + } + + /** + * Get the visitor's id. + * + * @return the visitor's id + */ + public String getVisitorId() { + return (String) getParameter(VISITOR_ID); + } + + /** + * Set the unique visitor ID, must be a {@value #ID_LENGTH} characters hexadecimal string. + * Every unique visitor must be assigned a different ID and this ID must not + * change after it is assigned. If this value is not set Piwik will still + * track visits, but the unique visitors metric might be less accurate. + * + * @param visitorId the visitor id to set. A null value will remove this parameter + */ + public void setVisitorId(String visitorId) { + if (visitorId != null) { + if (visitorId.length() != ID_LENGTH) { + throw new IllegalArgumentException(visitorId + " is not " + ID_LENGTH + " characters long."); + } + // Verify visitorID is a 16 character hexadecimal string + else if (!visitorId.matches("[0-9A-Fa-f]+")) { + throw new IllegalArgumentException(visitorId + " is not a hexadecimal string."); + } + } + setParameter(VISITOR_ID, visitorId); + } + + /** + * Get the visitor's ip. + * + * @return the visitor's ip + */ + public String getVisitorIp() { + return (String) getParameter(VISITOR_IP); + } + + /** + * Set the override value for the visitor IP (both IPv4 and IPv6 notations + * supported). AuthToken must first be set. + * + * @param visitorIp the visitor's ip to set. A null value will remove this parameter + */ + public void setVisitorIp(String visitorIp) { + if (visitorIp != null) { + verifyAuthTokenSet(); + } + setParameter(VISITOR_IP, visitorIp); + } + + /** + * Get the visitor's latitude. + * + * @return the visitor's latitude + */ + public Double getVisitorLatitude() { + return (Double) getParameter(VISITOR_LATITUDE); + } + + /** + * Set an override value for the visitor's latitude, eg 22.456. AuthToken + * must first be set. + * + * @param latitude the visitor's latitude to set. A null value will remove this parameter + */ + public void setVisitorLatitude(Double latitude) { + if (latitude != null) { + verifyAuthTokenSet(); + } + setParameter(VISITOR_LATITUDE, latitude); + } + + /** + * Get the visitor's longitude. + * + * @return the visitor's longitude + */ + public Double getVisitorLongitude() { + return (Double) getParameter(VISITOR_LONGITUDE); + } + + /** + * Set an override value for the visitor's longitude, eg 22.456. AuthToken + * must first be set. + * + * @param longitude the visitor's longitude to set. A null value will remove this parameter + */ + public void setVisitorLongitude(Double longitude) { + if (longitude != null) { + verifyAuthTokenSet(); + } + setParameter(VISITOR_LONGITUDE, longitude); + } + + /** + * Get the timestamp of the visitor's previous visit. + * + * @return the timestamp of the visitor's previous visit + */ + public Long getVisitorPreviousVisitTimestamp() { + return (Long) getParameter(VISITOR_PREVIOUS_VISIT_TIMESTAMP); + } + + /** + * Set the UNIX timestamp of this visitor's previous visit. This parameter + * is used to populate the report + * Visitors > Engagement > Visits by days since last visit. + * + * @param timestamp the timestamp of the visitor's previous visit to set. A null value will remove this parameter + */ + public void setVisitorPreviousVisitTimestamp(Long timestamp) { + setParameter(VISITOR_PREVIOUS_VISIT_TIMESTAMP, timestamp); + } + + /** + * Get the visitor's region. + * + * @return the visitor's region + */ + public String getVisitorRegion() { + return (String) getParameter(VISITOR_REGION); + } + + /** + * Set an override value for the region. Should be set to the two letter + * region code as defined by + * MaxMind's GeoIP databases. + * See here + * for a list of them for every country (the region codes are located in the + * second column, to the left of the region name and to the right of the country + * code). + * + * @param region the visitor's region to set. A null value will remove this parameter + */ + public void setVisitorRegion(String region) { + if (region != null) { + verifyAuthTokenSet(); + } + setParameter(VISITOR_REGION, region); + } + + /** + * Get the count of visits for this visitor. + * + * @return the count of visits for this visitor + */ + public Integer getVisitorVisitCount() { + return (Integer) getParameter(VISITOR_VISIT_COUNT); + } + + /** + * Set the current count of visits for this visitor. To set this value correctly, + * it would be required to store the value for each visitor in your application + * (using sessions or persisting in a database). Then you would manually increment + * the counts by one on each new visit or "session", depending on how you choose + * to define a visit. This value is used to populate the report + * Visitors > Engagement > Visits by visit number. + * + * @param visitorVisitCount the count of visits for this visitor to set. A null value will remove this parameter + */ + public void setVisitorVisitCount(Integer visitorVisitCount) { + setParameter(VISITOR_VISIT_COUNT, visitorVisitCount); + } + + /** + * Get the query string represented by this object. + * + * @return the query string represented by this object + */ + + public String getQueryString() { + StringBuilder sb = new StringBuilder(); + for (Entry parameter : parameters.entrySet()) { + if (sb.length() > 0) { + sb.append("&"); + } + sb.append(parameter.getKey()); + sb.append("="); + sb.append(parameter.getValue().toString()); + } + for (Entry customTrackingParameter : customTrackingParameters.entrySet()) { + for (Object o : customTrackingParameter.getValue()) { + if (sb.length() > 0) { + sb.append("&"); } - - return cvl.get(index); + sb.append(customTrackingParameter.getKey()); + sb.append("="); + sb.append(o.toString()); + } } - - private String getCustomVariable(String parameter, String key){ - if (key == null){ - throw new NullPointerException("Key cannot be null."); + + return sb.toString(); + } + + /** + * Get the url encoded query string represented by this object. + * + * @return the url encoded query string represented by this object + */ + public String getUrlEncodedQueryString() { + StringBuilder sb = new StringBuilder(); + for (Entry parameter : parameters.entrySet()) { + if (sb.length() > 0) { + sb.append("&"); + } + try { + StringBuilder sb2 = new StringBuilder(); + sb2.append(parameter.getKey()); + sb2.append("="); + sb2.append(URLEncoder.encode(parameter.getValue().toString(), "UTF-8")); + sb.append(sb2); + } catch (UnsupportedEncodingException e) { + System.err.println(e.getMessage()); + } + } + for (Entry customTrackingParameter : customTrackingParameters.entrySet()) { + for (Object o : customTrackingParameter.getValue()) { + if (sb.length() > 0) { + sb.append("&"); } - - CustomVariableList cvl = (CustomVariableList)parameters.get(parameter); - if (cvl == null){ - return null; + try { + StringBuilder sb2 = new StringBuilder(); + sb2.append(URLEncoder.encode(customTrackingParameter.getKey(), "UTF-8")); + sb2.append("="); + sb2.append(URLEncoder.encode(o.toString(), "UTF-8")); + sb.append(sb2); + } catch (UnsupportedEncodingException e) { + System.err.println(e.getMessage()); } - - return cvl.get(key); + } } - - /** - * Store a value in a json object at the specified parameter. - * @param parameter the parameter to store the json object at - * @param key the key of the value. Cannot be null - * @param value the value. Removes the parameter if null - */ - private void setCustomVariable(String parameter, CustomVariable customVariable, Integer index){ - CustomVariableList cvl = (CustomVariableList)parameters.get(parameter); - if (cvl == null){ - cvl = new CustomVariableList(); - parameters.put(parameter, cvl); - } - - if (customVariable == null){ - cvl.remove(index); - if (cvl.isEmpty()){ - parameters.remove(parameter); - } - } - else if (index == null){ - cvl.add(customVariable); - } - else { - cvl.add(customVariable, index); - } + + return sb.toString(); + } + + /** + * Get a random hexadecimal string of a specified length. + * + * @param length length of the string to produce + * @return a random string consisting only of hexadecimal characters + */ + public static String getRandomHexString(int length) { + byte[] bytes = new byte[length / 2]; + new Random().nextBytes(bytes); + return DatatypeConverter.printHexBinary(bytes); + } + + /** + * Get a stored parameter. + * + * @param key the parameter's key + * @return the stored parameter's value + */ + private Object getParameter(String key) { + return parameters.get(key); + } + + /** + * Set a stored parameter. + * + * @param key the parameter's key + * @param value the parameter's value. Removes the parameter if null + */ + private void setParameter(String key, Object value) { + if (value == null) { + parameters.remove(key); + } else { + parameters.put(key, value); + } + } + + /** + * Get a stored parameter that is a non-empty string. + * + * @param key the parameter's key + * @return the stored parameter's value + */ + private String getNonEmptyStringParameter(String key) { + return (String) parameters.get(key); + } + + /** + * Set a stored parameter and verify it is a non-empty string. + * + * @param key the parameter's key + * @param value the parameter's value. Cannot be the empty. Removes the parameter if null + * string + */ + private void setNonEmptyStringParameter(String key, String value) { + if (value == null) { + parameters.remove(key); + } else if (value.length() == 0) { + throw new IllegalArgumentException("Value cannot be empty."); + } else { + parameters.put(key, value); + } + } + + /** + * Get a stored parameter that is a boolean. + * + * @param key the parameter's key + * @return the stored parameter's value + */ + private Boolean getBooleanParameter(String key) { + Integer i = (Integer) parameters.get(key); + if (i == null) { + return null; + } + return i.equals(1); + } + + /** + * Set a stored parameter that is a boolean. This value will be stored as "1" + * for true and "0" for false. + * + * @param key the parameter's key + * @param value the parameter's value. Removes the parameter if null + */ + private void setBooleanParameter(String key, Boolean value) { + if (value == null) { + parameters.remove(key); + } else if (value) { + parameters.put(key, 1); + } else { + parameters.put(key, 0); + } + } + + /** + * Get a value that is stored in a json object at the specified parameter. + * + * @param parameter the parameter to retrieve the json object from + * @param key the key of the value. Cannot be null + * @return the value + */ + private CustomVariable getCustomVariable(String parameter, int index) { + CustomVariableList cvl = (CustomVariableList) parameters.get(parameter); + if (cvl == null) { + return null; } - - private void removeCustomVariable(String parameter, String key){ - if (key == null){ - throw new NullPointerException("Key cannot be null."); - } - CustomVariableList cvl = (CustomVariableList)parameters.get(parameter); - if (cvl != null){ - cvl.remove(key); - if (cvl.isEmpty()){ - parameters.remove(parameter); - } - } + + return cvl.get(index); + } + + private String getCustomVariable(String parameter, String key) { + if (key == null) { + throw new NullPointerException("Key cannot be null."); } - - /** - * Get the value at the specified index from the json array at the specified - * parameter. - * @param key the key of the json array to access - * @param index the index of the value in the json array - * @return the value at the index in the json array - */ - private JsonValue getFromJsonArray(String key, int index){ - PiwikJsonArray a = (PiwikJsonArray)parameters.get(key); - if (a == null){ - return null; - } - - return a.get(index); + + CustomVariableList cvl = (CustomVariableList) parameters.get(parameter); + if (cvl == null) { + return null; } - /** - * Add a value to the json array at the specified parameter - * @param key the key of the json array to add to - * @param value the value to add. Cannot be null - */ - private void addToJsonArray(String key, JsonValue value){ - if (value == null){ - throw new NullPointerException("Value cannot be null."); - } - - PiwikJsonArray a = (PiwikJsonArray)parameters.get(key); - if (a == null){ - a = new PiwikJsonArray(); - parameters.put(key, a); - } - a.add(value); + + return cvl.get(key); + } + + /** + * Store a value in a json object at the specified parameter. + * + * @param parameter the parameter to store the json object at + * @param key the key of the value. Cannot be null + * @param value the value. Removes the parameter if null + */ + private void setCustomVariable(String parameter, CustomVariable customVariable, Integer index) { + CustomVariableList cvl = (CustomVariableList) parameters.get(parameter); + if (cvl == null) { + cvl = new CustomVariableList(); + parameters.put(parameter, cvl); } - - /** - * Removes the json array at the specified parameter - * @param key the key of the json array to remove - */ - private void removeJsonArray(String key){ - parameters.remove(key); + + if (customVariable == null) { + cvl.remove(index); + if (cvl.isEmpty()) { + parameters.remove(parameter); + } + } else if (index == null) { + cvl.add(customVariable); + } else { + cvl.add(customVariable, index); + } + } + + private void removeCustomVariable(String parameter, String key) { + if (key == null) { + throw new NullPointerException("Key cannot be null."); + } + CustomVariableList cvl = (CustomVariableList) parameters.get(parameter); + if (cvl != null) { + cvl.remove(key); + if (cvl.isEmpty()) { + parameters.remove(parameter); + } + } + } + + /** + * Get the value at the specified index from the json array at the specified + * parameter. + * + * @param key the key of the json array to access + * @param index the index of the value in the json array + * @return the value at the index in the json array + */ + private JsonValue getFromJsonArray(String key, int index) { + PiwikJsonArray a = (PiwikJsonArray) parameters.get(key); + if (a == null) { + return null; } - - private URL returnAsUrl(String parameter, String name, String altMethod){ - Object obj = getParameter(parameter); - if (obj == null){ - return null; - } - if (obj instanceof URL){ - return (URL)obj; - } - throw new TypeConstraintException("The stored " + name + - " is a String, not a URL. Use \""+ - altMethod + "\" instead."); + + return a.get(index); + } + + /** + * Add a value to the json array at the specified parameter + * + * @param key the key of the json array to add to + * @param value the value to add. Cannot be null + */ + private void addToJsonArray(String key, JsonValue value) { + if (value == null) { + throw new NullPointerException("Value cannot be null."); } - - private String returnAsString(String parameter, String name, String altMethod){ - Object obj = getParameter(parameter); - if (obj == null){ - return null; - } - if (obj instanceof String){ - return (String)obj; - } - throw new TypeConstraintException("The stored " + name + - " is a URL, not a String. Use \""+ - altMethod + "\" instead."); + + PiwikJsonArray a = (PiwikJsonArray) parameters.get(key); + if (a == null) { + a = new PiwikJsonArray(); + parameters.put(key, a); } + a.add(value); + } + + /** + * Removes the json array at the specified parameter + * + * @param key the key of the json array to remove + */ + private void removeJsonArray(String key) { + parameters.remove(key); + } + + private URL returnAsUrl(String parameter, String name, String altMethod) { + Object obj = getParameter(parameter); + if (obj == null) { + return null; + } + if (obj instanceof URL) { + return (URL) obj; + } + throw new TypeConstraintException("The stored " + name + + " is a String, not a URL. Use \"" + + altMethod + "\" instead."); + } + + private String returnAsString(String parameter, String name, String altMethod) { + Object obj = getParameter(parameter); + if (obj == null) { + return null; + } + if (obj instanceof String) { + return (String) obj; + } + throw new TypeConstraintException("The stored " + name + + " is a URL, not a String. Use \"" + + altMethod + "\" instead."); + } } diff --git a/src/main/java/org/piwik/java/tracking/PiwikTracker.java b/src/main/java/org/piwik/java/tracking/PiwikTracker.java index 3b258cf5..7b892036 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikTracker.java +++ b/src/main/java/org/piwik/java/tracking/PiwikTracker.java @@ -1,6 +1,6 @@ -/* +/* * Piwik Java Tracker - * + * * @link https://github.com/piwik/piwik-java-tracker * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause */ @@ -16,9 +16,9 @@ import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.DefaultProxyRoutePlanner; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; -import org.apache.http.impl.conn.DefaultProxyRoutePlanner; import javax.json.Json; import javax.json.JsonArrayBuilder; @@ -30,261 +30,272 @@ /** * A class that sends {@link PiwikRequest}s to a specified Piwik server. + * * @author brettcsorba */ -public class PiwikTracker{ - private static final String AUTH_TOKEN = "token_auth"; - private static final String REQUESTS = "requests"; - private static final int DEFAULT_TIMEOUT = 5000; - private final URIBuilder uriBuilder; - private final int timeout; - private final String proxyHost; - private final int proxyPort; - private CloseableHttpAsyncClient asyncClient = null; - - /** - * Creates a tracker that will send {@link PiwikRequest}s to the specified - * Tracking HTTP API endpoint. - * @param hostUrl url endpoint to send requests to. Usually in the format - * http://your-piwik-domain.tld/piwik.php. - */ - public PiwikTracker(String hostUrl){ - this(hostUrl, DEFAULT_TIMEOUT); +public class PiwikTracker { + private static final String AUTH_TOKEN = "token_auth"; + private static final String REQUESTS = "requests"; + private static final int DEFAULT_TIMEOUT = 5000; + private final URIBuilder uriBuilder; + private final int timeout; + private final String proxyHost; + private final int proxyPort; + private CloseableHttpAsyncClient asyncClient = null; + + /** + * Creates a tracker that will send {@link PiwikRequest}s to the specified + * Tracking HTTP API endpoint. + * + * @param hostUrl url endpoint to send requests to. Usually in the format + * http://your-piwik-domain.tld/piwik.php. + */ + public PiwikTracker(String hostUrl) { + this(hostUrl, DEFAULT_TIMEOUT); + } + + /** + * Creates a tracker that will send {@link PiwikRequest}s to the specified + * Tracking HTTP API endpoint. + * + * @param hostUrl url endpoint to send requests to. Usually in the format + * http://your-piwik-domain.tld/piwik.php. + * @param timeout the timeout of the sent request in milliseconds + */ + public PiwikTracker(String hostUrl, int timeout) { + uriBuilder = new URIBuilder(URI.create(hostUrl)); + this.timeout = timeout; + this.proxyHost = null; + this.proxyPort = 0; + } + + /** + * Creates a tracker that will send {@link PiwikRequest}s to the specified + * Tracking HTTP API endpoint via the provided proxy + * + * @param hostUrl url endpoint to send requests to. Usually in the format + * http://your-piwik-domain.tld/piwik.php. + * @param proxyHost url endpoint for the proxy + * @param proxyPort proxy server port number + */ + public PiwikTracker(String hostUrl, String proxyHost, int proxyPort) { + this(hostUrl, proxyHost, proxyPort, DEFAULT_TIMEOUT); + } + + public PiwikTracker(String hostUrl, String proxyHost, int proxyPort, int timeout) { + uriBuilder = new URIBuilder(URI.create(hostUrl)); + this.proxyHost = proxyHost; + this.proxyPort = proxyPort; + this.timeout = timeout; + } + + /** + * Send a request. + * + * @param request request to send + * @return the response from this request + * @throws IOException thrown if there was a problem with this connection + * @deprecated use sendRequestAsync instead + */ + @Deprecated + public HttpResponse sendRequest(PiwikRequest request) throws IOException { + HttpClient client = getHttpClient(); + uriBuilder.setCustomQuery(request.getQueryString()); + HttpGet get = null; + + try { + get = new HttpGet(uriBuilder.build()); + return client.execute(get); + } catch (URISyntaxException e) { + throw new IOException(e); } - - /** - * Creates a tracker that will send {@link PiwikRequest}s to the specified - * Tracking HTTP API endpoint. - * @param hostUrl url endpoint to send requests to. Usually in the format - * http://your-piwik-domain.tld/piwik.php. - * @param timeout the timeout of the sent request in milliseconds - */ - public PiwikTracker(String hostUrl, int timeout){ - uriBuilder = new URIBuilder(URI.create(hostUrl)); - this.timeout = timeout; - this.proxyHost = null; - this.proxyPort = 0; + } + + /** + * Send a request. + * + * @param request request to send + * @return future with response from this request + * @throws IOException thrown if there was a problem with this connection + */ + public Future sendRequestAsync(PiwikRequest request) throws IOException { + CloseableHttpAsyncClient client = getHttpAsyncClient(); + client.start(); + uriBuilder.setCustomQuery(request.getQueryString()); + HttpGet get = null; + + try { + get = new HttpGet(uriBuilder.build()); + return client.execute(get, null); + } catch (URISyntaxException e) { + throw new IOException(e); } - - /** - * Creates a tracker that will send {@link PiwikRequest}s to the specified - * Tracking HTTP API endpoint via the provided proxy - * - * @param hostUrl url endpoint to send requests to. Usually in the format - * http://your-piwik-domain.tld/piwik.php. - * @param proxyHost url endpoint for the proxy - * @param proxyPort proxy server port number - */ - public PiwikTracker(String hostUrl, String proxyHost, int proxyPort){ - this(hostUrl, proxyHost, proxyPort, DEFAULT_TIMEOUT); + } + + /** + * Send multiple requests in a single HTTP call. More efficient than sending + * several individual requests. + * + * @param requests the requests to send + * @return the response from these requests + * @throws IOException thrown if there was a problem with this connection + * @deprecated use sendBulkRequestAsync instead + */ + @Deprecated + public HttpResponse sendBulkRequest(Iterable requests) throws IOException { + return sendBulkRequest(requests, null); + } + + /** + * Send multiple requests in a single HTTP call. More efficient than sending + * several individual requests. + * + * @param requests the requests to send + * @return future with response from these requests + * @throws IOException thrown if there was a problem with this connection + */ + public Future sendBulkRequestAsync(Iterable requests) throws IOException { + return sendBulkRequestAsync(requests, null); + } + + /** + * Send multiple requests in a single HTTP call. More efficient than sending + * several individual requests. Specify the AuthToken if parameters that require + * an auth token is used. + * + * @param requests the requests to send + * @param authToken specify if any of the parameters use require AuthToken + * @return the response from these requests + * @throws IOException thrown if there was a problem with this connection + * @deprecated use sendBulkRequestAsync instead + */ + @Deprecated + public HttpResponse sendBulkRequest(Iterable requests, String authToken) throws IOException { + if (authToken != null && authToken.length() != PiwikRequest.AUTH_TOKEN_LENGTH) { + throw new IllegalArgumentException(authToken + " is not " + PiwikRequest.AUTH_TOKEN_LENGTH + " characters long."); } - public PiwikTracker(String hostUrl, String proxyHost, int proxyPort, int timeout){ - uriBuilder = new URIBuilder(URI.create(hostUrl)); - this.proxyHost = proxyHost; - this.proxyPort = proxyPort; - this.timeout = timeout; - } + JsonObjectBuilder ob = Json.createObjectBuilder(); + JsonArrayBuilder ab = Json.createArrayBuilder(); - /** - * Send a request. - * @param request request to send - * @return the response from this request - * @throws IOException thrown if there was a problem with this connection - * @deprecated use sendRequestAsync instead - */ - @Deprecated - public HttpResponse sendRequest(PiwikRequest request) throws IOException{ - HttpClient client = getHttpClient(); - uriBuilder.setCustomQuery(request.getQueryString()); - HttpGet get = null; - - try { - get = new HttpGet(uriBuilder.build()); - return client.execute(get); - } catch (URISyntaxException e) { - throw new IOException(e); - } + for (PiwikRequest request : requests) { + ab.add("?" + request.getQueryString()); } - /** - * Send a request. - * @param request request to send - * @return future with response from this request - * @throws IOException thrown if there was a problem with this connection - */ - public Future sendRequestAsync(PiwikRequest request) throws IOException{ - CloseableHttpAsyncClient client = getHttpAsyncClient(); - client.start(); - uriBuilder.setCustomQuery(request.getQueryString()); - HttpGet get = null; - - try { - get = new HttpGet(uriBuilder.build()); - return client.execute(get,null); - } catch (URISyntaxException e) { - throw new IOException(e); - } + ob.add(REQUESTS, ab); + + if (authToken != null) { + ob.add(AUTH_TOKEN, authToken); } - /** - * Send multiple requests in a single HTTP call. More efficient than sending - * several individual requests. - * @param requests the requests to send - * @return the response from these requests - * @throws IOException thrown if there was a problem with this connection - * @deprecated use sendBulkRequestAsync instead - */ - @Deprecated - public HttpResponse sendBulkRequest(Iterable requests) throws IOException{ - return sendBulkRequest(requests, null); + HttpClient client = getHttpClient(); + HttpPost post = null; + + try { + post = new HttpPost(uriBuilder.build()); + post.setEntity(new StringEntity(ob.build().toString(), + ContentType.APPLICATION_JSON)); + return client.execute(post); + } catch (URISyntaxException e) { + throw new IOException(e); } + } + + /** + * Send multiple requests in a single HTTP call. More efficient than sending + * several individual requests. Specify the AuthToken if parameters that require + * an auth token is used. + * + * @param requests the requests to send + * @param authToken specify if any of the parameters use require AuthToken + * @return the response from these requests + * @throws IOException thrown if there was a problem with this connection + */ + public Future sendBulkRequestAsync(Iterable requests, String authToken) throws IOException { + if (authToken != null && authToken.length() != PiwikRequest.AUTH_TOKEN_LENGTH) { + throw new IllegalArgumentException(authToken + " is not " + PiwikRequest.AUTH_TOKEN_LENGTH + " characters long."); + } + + JsonObjectBuilder ob = Json.createObjectBuilder(); + JsonArrayBuilder ab = Json.createArrayBuilder(); - /** - * Send multiple requests in a single HTTP call. More efficient than sending - * several individual requests. - * @param requests the requests to send - * @return future with response from these requests - * @throws IOException thrown if there was a problem with this connection - */ - public Future sendBulkRequestAsync(Iterable requests) throws IOException{ - return sendBulkRequestAsync(requests, null); + for (PiwikRequest request : requests) { + ab.add("?" + request.getQueryString()); } - /** - * Send multiple requests in a single HTTP call. More efficient than sending - * several individual requests. Specify the AuthToken if parameters that require - * an auth token is used. - * @param requests the requests to send - * @param authToken specify if any of the parameters use require AuthToken - * @return the response from these requests - * @throws IOException thrown if there was a problem with this connection - * @deprecated use sendBulkRequestAsync instead - */ - @Deprecated - public HttpResponse sendBulkRequest(Iterable requests, String authToken) throws IOException{ - if (authToken != null && authToken.length() != PiwikRequest.AUTH_TOKEN_LENGTH){ - throw new IllegalArgumentException(authToken+" is not "+PiwikRequest.AUTH_TOKEN_LENGTH+" characters long."); - } - - JsonObjectBuilder ob = Json.createObjectBuilder(); - JsonArrayBuilder ab = Json.createArrayBuilder(); - - for (PiwikRequest request : requests){ - ab.add("?"+request.getQueryString()); - } - - ob.add(REQUESTS, ab); - - if (authToken != null){ - ob.add(AUTH_TOKEN, authToken); - } - - HttpClient client = getHttpClient(); - HttpPost post = null; - - try { - post = new HttpPost(uriBuilder.build()); - post.setEntity(new StringEntity(ob.build().toString(), - ContentType.APPLICATION_JSON)); - return client.execute(post); - } catch (URISyntaxException e) { - throw new IOException(e); - } + ob.add(REQUESTS, ab); + + if (authToken != null) { + ob.add(AUTH_TOKEN, authToken); } - /** - * Send multiple requests in a single HTTP call. More efficient than sending - * several individual requests. Specify the AuthToken if parameters that require - * an auth token is used. - * @param requests the requests to send - * @param authToken specify if any of the parameters use require AuthToken - * @return the response from these requests - * @throws IOException thrown if there was a problem with this connection - */ - public Future sendBulkRequestAsync(Iterable requests, String authToken) throws IOException{ - if (authToken != null && authToken.length() != PiwikRequest.AUTH_TOKEN_LENGTH){ - throw new IllegalArgumentException(authToken+" is not "+PiwikRequest.AUTH_TOKEN_LENGTH+" characters long."); - } - - JsonObjectBuilder ob = Json.createObjectBuilder(); - JsonArrayBuilder ab = Json.createArrayBuilder(); - - for (PiwikRequest request : requests){ - ab.add("?"+request.getQueryString()); - } - - ob.add(REQUESTS, ab); - - if (authToken != null){ - ob.add(AUTH_TOKEN, authToken); - } - - CloseableHttpAsyncClient client = getHttpAsyncClient(); - client.start(); - HttpPost post = null; - - try { - post = new HttpPost(uriBuilder.build()); - post.setEntity(new StringEntity(ob.build().toString(), - ContentType.APPLICATION_JSON)); - return client.execute(post,null); - } catch (URISyntaxException e) { - throw new IOException(e); - } + CloseableHttpAsyncClient client = getHttpAsyncClient(); + client.start(); + HttpPost post = null; + + try { + post = new HttpPost(uriBuilder.build()); + post.setEntity(new StringEntity(ob.build().toString(), + ContentType.APPLICATION_JSON)); + return client.execute(post, null); + } catch (URISyntaxException e) { + throw new IOException(e); } + } + + /** + * Get a HTTP client. With proxy if a proxy is provided in the constructor. + * + * @return a HTTP client + */ + protected HttpClient getHttpClient() { - /** - * Get a HTTP client. With proxy if a proxy is provided in the constructor. - * @return a HTTP client - */ - protected HttpClient getHttpClient(){ + HttpClientBuilder builder = HttpClientBuilder.create(); - HttpClientBuilder builder = HttpClientBuilder.create(); + if (proxyHost != null && proxyPort != 0) { + HttpHost proxy = new HttpHost(proxyHost, proxyPort); + DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy); + builder.setRoutePlanner(routePlanner); + } - if(proxyHost != null && proxyPort != 0) { - HttpHost proxy = new HttpHost(proxyHost, proxyPort); - DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy); - builder.setRoutePlanner(routePlanner); - } + RequestConfig.Builder config = RequestConfig.custom() + .setConnectTimeout(timeout) + .setConnectionRequestTimeout(timeout) + .setSocketTimeout(timeout); - RequestConfig.Builder config = RequestConfig.custom() - .setConnectTimeout(timeout) - .setConnectionRequestTimeout(timeout) - .setSocketTimeout(timeout); + builder.setDefaultRequestConfig(config.build()); - builder.setDefaultRequestConfig(config.build()); + return builder.build(); + } - return builder.build(); + /** + * Get an async HTTP client. With proxy if a proxy is provided in the constructor. + * + * @return an async HTTP client + */ + protected CloseableHttpAsyncClient getHttpAsyncClient() { + + if (asyncClient != null) { + return asyncClient; } - /** - * Get an async HTTP client. With proxy if a proxy is provided in the constructor. - * @return an async HTTP client - */ - protected CloseableHttpAsyncClient getHttpAsyncClient(){ - - if(asyncClient != null) { - return asyncClient; - } - - HttpAsyncClientBuilder builder = HttpAsyncClientBuilder.create(); - - if(proxyHost != null && proxyPort != 0) { - HttpHost proxy = new HttpHost(proxyHost, proxyPort); - DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy); - builder.setRoutePlanner(routePlanner); - } - - RequestConfig.Builder config = RequestConfig.custom() - .setConnectTimeout(timeout) - .setConnectionRequestTimeout(timeout) - .setSocketTimeout(timeout); - - builder.setDefaultRequestConfig(config.build()); - - asyncClient = builder.build(); - - return asyncClient; + HttpAsyncClientBuilder builder = HttpAsyncClientBuilder.create(); + + if (proxyHost != null && proxyPort != 0) { + HttpHost proxy = new HttpHost(proxyHost, proxyPort); + DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy); + builder.setRoutePlanner(routePlanner); } + + RequestConfig.Builder config = RequestConfig.custom() + .setConnectTimeout(timeout) + .setConnectionRequestTimeout(timeout) + .setSocketTimeout(timeout); + + builder.setDefaultRequestConfig(config.build()); + + asyncClient = builder.build(); + + return asyncClient; + } } diff --git a/src/test/java/org/piwik/java/tracking/CustomVariableListTest.java b/src/test/java/org/piwik/java/tracking/CustomVariableListTest.java index 861400f2..76b71600 100644 --- a/src/test/java/org/piwik/java/tracking/CustomVariableListTest.java +++ b/src/test/java/org/piwik/java/tracking/CustomVariableListTest.java @@ -5,89 +5,87 @@ */ package org.piwik.java.tracking; +import org.junit.Before; +import org.junit.Test; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import org.junit.Before; -import org.junit.Test; /** - * * @author Katie */ public class CustomVariableListTest { - private CustomVariableList cvl; - - @Before - public void setUp() { - cvl = new CustomVariableList(); - } + private CustomVariableList cvl; - @Test - public void testAdd_CustomVariable() { - CustomVariable a = new CustomVariable("a", "b"); - CustomVariable b = new CustomVariable("c", "d"); - CustomVariable c = new CustomVariable("a", "e"); - CustomVariable d = new CustomVariable("a", "f"); - - assertTrue(cvl.isEmpty()); - cvl.add(a); - assertFalse(cvl.isEmpty()); - assertEquals("b", cvl.get("a")); - assertEquals(a, cvl.get(1)); - assertEquals("{\"1\":[\"a\",\"b\"]}", cvl.toString()); - - cvl.add(b); - assertEquals("d", cvl.get("c")); - assertEquals(b, cvl.get(2)); - assertEquals("{\"1\":[\"a\",\"b\"],\"2\":[\"c\",\"d\"]}", cvl.toString()); - - cvl.add(c, 5); - assertEquals("b", cvl.get("a")); - assertEquals(c, cvl.get(5)); - assertNull(cvl.get(3)); - assertEquals("{\"1\":[\"a\",\"b\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"e\"]}", cvl.toString()); - - cvl.add(d); - assertEquals("f", cvl.get("a")); - assertEquals(d, cvl.get(1)); - assertEquals(d, cvl.get(5)); - assertEquals("{\"1\":[\"a\",\"f\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"f\"]}", cvl.toString()); - - cvl.remove("a"); - assertNull(cvl.get("a")); - assertNull(cvl.get(1)); - assertNull(cvl.get(5)); - assertEquals("{\"2\":[\"c\",\"d\"]}", cvl.toString()); + @Before + public void setUp() { + cvl = new CustomVariableList(); + } - cvl.remove(2); - assertNull(cvl.get("c")); - assertNull(cvl.get(2)); - assertTrue(cvl.isEmpty()); - assertEquals("{}", cvl.toString()); - } - - @Test - public void testAddCustomVariableIndexLessThan1(){ - try{ - cvl.add(new CustomVariable("a", "b"), 0); - fail("Exception should have been throw."); - } - catch(IllegalArgumentException e){ - assertEquals("Index must be greater than 0.", e.getLocalizedMessage()); - } + @Test + public void testAdd_CustomVariable() { + CustomVariable a = new CustomVariable("a", "b"); + CustomVariable b = new CustomVariable("c", "d"); + CustomVariable c = new CustomVariable("a", "e"); + CustomVariable d = new CustomVariable("a", "f"); + + assertTrue(cvl.isEmpty()); + cvl.add(a); + assertFalse(cvl.isEmpty()); + assertEquals("b", cvl.get("a")); + assertEquals(a, cvl.get(1)); + assertEquals("{\"1\":[\"a\",\"b\"]}", cvl.toString()); + + cvl.add(b); + assertEquals("d", cvl.get("c")); + assertEquals(b, cvl.get(2)); + assertEquals("{\"1\":[\"a\",\"b\"],\"2\":[\"c\",\"d\"]}", cvl.toString()); + + cvl.add(c, 5); + assertEquals("b", cvl.get("a")); + assertEquals(c, cvl.get(5)); + assertNull(cvl.get(3)); + assertEquals("{\"1\":[\"a\",\"b\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"e\"]}", cvl.toString()); + + cvl.add(d); + assertEquals("f", cvl.get("a")); + assertEquals(d, cvl.get(1)); + assertEquals(d, cvl.get(5)); + assertEquals("{\"1\":[\"a\",\"f\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"f\"]}", cvl.toString()); + + cvl.remove("a"); + assertNull(cvl.get("a")); + assertNull(cvl.get(1)); + assertNull(cvl.get(5)); + assertEquals("{\"2\":[\"c\",\"d\"]}", cvl.toString()); + + cvl.remove(2); + assertNull(cvl.get("c")); + assertNull(cvl.get(2)); + assertTrue(cvl.isEmpty()); + assertEquals("{}", cvl.toString()); + } + + @Test + public void testAddCustomVariableIndexLessThan1() { + try { + cvl.add(new CustomVariable("a", "b"), 0); + fail("Exception should have been throw."); + } catch (IllegalArgumentException e) { + assertEquals("Index must be greater than 0.", e.getLocalizedMessage()); } - - @Test - public void testGetCustomVariableIntegerLessThan1(){ - try{ - cvl.get(0); - fail("Exception should have been throw."); - } - catch(IllegalArgumentException e){ - assertEquals("Index must be greater than 0.", e.getLocalizedMessage()); - } + } + + @Test + public void testGetCustomVariableIntegerLessThan1() { + try { + cvl.get(0); + fail("Exception should have been throw."); + } catch (IllegalArgumentException e) { + assertEquals("Index must be greater than 0.", e.getLocalizedMessage()); } + } } diff --git a/src/test/java/org/piwik/java/tracking/CustomVariableTest.java b/src/test/java/org/piwik/java/tracking/CustomVariableTest.java index 28b4e475..4223eaed 100644 --- a/src/test/java/org/piwik/java/tracking/CustomVariableTest.java +++ b/src/test/java/org/piwik/java/tracking/CustomVariableTest.java @@ -7,48 +7,48 @@ import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.*; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; /** - * * @author Katie */ public class CustomVariableTest { - private CustomVariable customVariable; - - @Before - public void setUp() { - customVariable = new CustomVariable("key", "value"); - } - - @Test - public void testConstructorNullKey(){ - try{ - new CustomVariable(null, null); - fail("Exception should have been throw."); - } - catch(NullPointerException e){ - assertEquals("Key cannot be null.", e.getLocalizedMessage()); - } - } - @Test - public void testConstructorNullValue(){ - try{ - new CustomVariable("key", null); - fail("Exception should have been throw."); - } - catch(NullPointerException e){ - assertEquals("Value cannot be null.", e.getLocalizedMessage()); - } + private CustomVariable customVariable; + + @Before + public void setUp() { + customVariable = new CustomVariable("key", "value"); + } + + @Test + public void testConstructorNullKey() { + try { + new CustomVariable(null, null); + fail("Exception should have been throw."); + } catch (NullPointerException e) { + assertEquals("Key cannot be null.", e.getLocalizedMessage()); } + } - @Test - public void testGetKey() { - assertEquals("key", customVariable.getKey()); + @Test + public void testConstructorNullValue() { + try { + new CustomVariable("key", null); + fail("Exception should have been throw."); + } catch (NullPointerException e) { + assertEquals("Value cannot be null.", e.getLocalizedMessage()); } + } + + @Test + public void testGetKey() { + assertEquals("key", customVariable.getKey()); + } - @Test - public void testGetValue() { - assertEquals("value", customVariable.getValue()); - } + @Test + public void testGetValue() { + assertEquals("value", customVariable.getValue()); + } } diff --git a/src/test/java/org/piwik/java/tracking/EcommerceItemTest.java b/src/test/java/org/piwik/java/tracking/EcommerceItemTest.java index 4359920b..52dbb9be 100644 --- a/src/test/java/org/piwik/java/tracking/EcommerceItemTest.java +++ b/src/test/java/org/piwik/java/tracking/EcommerceItemTest.java @@ -1,126 +1,127 @@ -/* +/* * Piwik Java Tracker - * + * * @link https://github.com/piwik/piwik-java-tracker * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause */ package org.piwik.java.tracking; -import javax.json.JsonValue.ValueType; import org.junit.After; import org.junit.AfterClass; -import static org.junit.Assert.assertEquals; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import javax.json.JsonValue.ValueType; + +import static org.junit.Assert.assertEquals; + /** - * * @author brettcsorba */ -public class EcommerceItemTest{ - EcommerceItem ecommerceItem; - - public EcommerceItemTest(){ - } - - @BeforeClass - public static void setUpClass(){ - } - - @AfterClass - public static void tearDownClass(){ - } - - @Before - public void setUp(){ - ecommerceItem = new EcommerceItem(null, null, null, null, null); - } - - @After - public void tearDown(){ - } - - /** - * Test of constructor, of class EcommerceItem. - */ - @Test - public void testConstructor(){ - EcommerceItem ecommerceItem = new EcommerceItem("sku", "name", "category", 1.0, 1); - - assertEquals("sku", ecommerceItem.getSku()); - assertEquals("name", ecommerceItem.getName()); - assertEquals("category", ecommerceItem.getCategory()); - assertEquals(new Double(1.0), ecommerceItem.getPrice()); - assertEquals(new Integer(1), ecommerceItem.getQuantity()); - } - - /** - * Test of getSku method, of class EcommerceItem. - */ - @Test - public void testGetSku(){ - ecommerceItem.setSku("sku"); - - assertEquals("sku", ecommerceItem.getSku()); - } - - /** - * Test of getName method, of class EcommerceItem. - */ - @Test - public void testGetName(){ - ecommerceItem.setName("name"); - - assertEquals("name", ecommerceItem.getName()); - } - - /** - * Test of getCategory method, of class EcommerceItem. - */ - @Test - public void testGetCategory(){ - ecommerceItem.setCategory("category"); - - assertEquals("category", ecommerceItem.getCategory()); - } - - /** - * Test of getPrice method, of class EcommerceItem. - */ - @Test - public void testGetPrice(){ - ecommerceItem.setPrice(1.0); - - assertEquals(new Double(1.0), ecommerceItem.getPrice()); - } - - /** - * Test of getQuantity method, of class EcommerceItem. - */ - @Test - public void testGetQuantity(){ - ecommerceItem.setQuantity(1); - - assertEquals(new Integer(1), ecommerceItem.getQuantity()); - } - - /** - * Test of getValueType method, of class EcommerceItem. - */ - @Test - public void testGetValueType(){ - assertEquals(ValueType.ARRAY, ecommerceItem.getValueType()); - } - - /** - * Test of toString method, of class EcommerceItem. - */ - @Test - public void testToString(){ - ecommerceItem = new EcommerceItem("sku", "name", "category", 1.0, 1); - - assertEquals("[\"sku\",\"name\",\"category\",1.0,1]", ecommerceItem.toString()); - } - +public class EcommerceItemTest { + EcommerceItem ecommerceItem; + + public EcommerceItemTest() { + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + ecommerceItem = new EcommerceItem(null, null, null, null, null); + } + + @After + public void tearDown() { + } + + /** + * Test of constructor, of class EcommerceItem. + */ + @Test + public void testConstructor() { + EcommerceItem ecommerceItem = new EcommerceItem("sku", "name", "category", 1.0, 1); + + assertEquals("sku", ecommerceItem.getSku()); + assertEquals("name", ecommerceItem.getName()); + assertEquals("category", ecommerceItem.getCategory()); + assertEquals(new Double(1.0), ecommerceItem.getPrice()); + assertEquals(new Integer(1), ecommerceItem.getQuantity()); + } + + /** + * Test of getSku method, of class EcommerceItem. + */ + @Test + public void testGetSku() { + ecommerceItem.setSku("sku"); + + assertEquals("sku", ecommerceItem.getSku()); + } + + /** + * Test of getName method, of class EcommerceItem. + */ + @Test + public void testGetName() { + ecommerceItem.setName("name"); + + assertEquals("name", ecommerceItem.getName()); + } + + /** + * Test of getCategory method, of class EcommerceItem. + */ + @Test + public void testGetCategory() { + ecommerceItem.setCategory("category"); + + assertEquals("category", ecommerceItem.getCategory()); + } + + /** + * Test of getPrice method, of class EcommerceItem. + */ + @Test + public void testGetPrice() { + ecommerceItem.setPrice(1.0); + + assertEquals(new Double(1.0), ecommerceItem.getPrice()); + } + + /** + * Test of getQuantity method, of class EcommerceItem. + */ + @Test + public void testGetQuantity() { + ecommerceItem.setQuantity(1); + + assertEquals(new Integer(1), ecommerceItem.getQuantity()); + } + + /** + * Test of getValueType method, of class EcommerceItem. + */ + @Test + public void testGetValueType() { + assertEquals(ValueType.ARRAY, ecommerceItem.getValueType()); + } + + /** + * Test of toString method, of class EcommerceItem. + */ + @Test + public void testToString() { + ecommerceItem = new EcommerceItem("sku", "name", "category", 1.0, 1); + + assertEquals("[\"sku\",\"name\",\"category\",1.0,1]", ecommerceItem.toString()); + } + } diff --git a/src/test/java/org/piwik/java/tracking/PiwikDateTest.java b/src/test/java/org/piwik/java/tracking/PiwikDateTest.java index 704f937e..1025b5eb 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikDateTest.java +++ b/src/test/java/org/piwik/java/tracking/PiwikDateTest.java @@ -1,57 +1,55 @@ -/* +/* * Piwik Java Tracker - * + * * @link https://github.com/piwik/piwik-java-tracker * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause */ package org.piwik.java.tracking; +import org.junit.Test; + import java.util.TimeZone; -import org.junit.After; -import org.junit.AfterClass; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; /** - * * @author brettcsorba */ -public class PiwikDateTest{ - /** - * Test of constructor, of class PiwikDate. - */ - @Test - public void testConstructor0(){ - PiwikDate date = new PiwikDate(); - - assertNotNull(date); - } - @Test - public void testConstructor1(){ - PiwikDate date = new PiwikDate(1433186085092L); - - assertNotNull(date); - - assertEquals("2015-06-01 19:14:45", date.toString()); - - date = new PiwikDate(1467437553000L); - - assertEquals("2016-07-02 05:32:33", date.toString()); - } - - /** - * Test of setTimeZone method, of class PiwikDate. - */ - @Test - public void testSetTimeZone(){ - PiwikDate date = new PiwikDate(1433186085092L); - - date.setTimeZone(TimeZone.getTimeZone("America/New_York")); - - assertEquals("2015-06-01 15:14:45", date.toString()); - } - +public class PiwikDateTest { + /** + * Test of constructor, of class PiwikDate. + */ + @Test + public void testConstructor0() { + PiwikDate date = new PiwikDate(); + + assertNotNull(date); + } + + @Test + public void testConstructor1() { + PiwikDate date = new PiwikDate(1433186085092L); + + assertNotNull(date); + + assertEquals("2015-06-01 19:14:45", date.toString()); + + date = new PiwikDate(1467437553000L); + + assertEquals("2016-07-02 05:32:33", date.toString()); + } + + /** + * Test of setTimeZone method, of class PiwikDate. + */ + @Test + public void testSetTimeZone() { + PiwikDate date = new PiwikDate(1433186085092L); + + date.setTimeZone(TimeZone.getTimeZone("America/New_York")); + + assertEquals("2015-06-01 15:14:45", date.toString()); + } + } diff --git a/src/test/java/org/piwik/java/tracking/PiwikJsonArrayTest.java b/src/test/java/org/piwik/java/tracking/PiwikJsonArrayTest.java index a3d9187a..bed3d3b1 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikJsonArrayTest.java +++ b/src/test/java/org/piwik/java/tracking/PiwikJsonArrayTest.java @@ -1,72 +1,73 @@ -/* +/* * Piwik Java Tracker - * + * * @link https://github.com/piwik/piwik-java-tracker * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause */ package org.piwik.java.tracking; -import javax.json.JsonValue; import org.junit.After; import org.junit.AfterClass; -import static org.junit.Assert.assertEquals; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; + +import javax.json.JsonValue; + +import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; /** - * * @author brettcsorba */ -public class PiwikJsonArrayTest{ - PiwikJsonArray array; - - public PiwikJsonArrayTest(){ - } - - @BeforeClass - public static void setUpClass(){ - } - - @AfterClass - public static void tearDownClass(){ - } - - @Before - public void setUp(){ - array = new PiwikJsonArray(); - } - - @After - public void tearDown(){ - } +public class PiwikJsonArrayTest { + PiwikJsonArray array; + + public PiwikJsonArrayTest() { + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + array = new PiwikJsonArray(); + } + + @After + public void tearDown() { + } + + /** + * Test of get method, of class PiwikJsonArray. + */ + @Test + public void testAddGetSet() { + JsonValue value = mock(JsonValue.class); + JsonValue value2 = mock(JsonValue.class); + + array.add(value); + assertEquals(value, array.get(0)); + + array.set(0, value2); + assertEquals(value2, array.get(0)); + } + + /** + * Test of toString method, of class PiwikJsonArray. + */ + @Test + public void testToString() { + array.add(JsonValue.TRUE); + array.add(new EcommerceItem("a", "b", "c", 1.0, 2)); + array.add(JsonValue.FALSE); - /** - * Test of get method, of class PiwikJsonArray. - */ - @Test - public void testAddGetSet(){ - JsonValue value = mock(JsonValue.class); - JsonValue value2 = mock(JsonValue.class); - - array.add(value); - assertEquals(value, array.get(0)); - - array.set(0, value2); - assertEquals(value2, array.get(0)); - } + assertEquals("[true,[\"a\",\"b\",\"c\",1.0,2],false]", array.toString()); + } - /** - * Test of toString method, of class PiwikJsonArray. - */ - @Test - public void testToString(){ - array.add(JsonValue.TRUE); - array.add(new EcommerceItem("a", "b", "c", 1.0, 2)); - array.add(JsonValue.FALSE); - - assertEquals("[true,[\"a\",\"b\",\"c\",1.0,2],false]", array.toString()); - } - } diff --git a/src/test/java/org/piwik/java/tracking/PiwikJsonObjectTest.java b/src/test/java/org/piwik/java/tracking/PiwikJsonObjectTest.java index 0a840fce..358be490 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikJsonObjectTest.java +++ b/src/test/java/org/piwik/java/tracking/PiwikJsonObjectTest.java @@ -1,6 +1,6 @@ -/* +/* * Piwik Java Tracker - * + * * @link https://github.com/piwik/piwik-java-tracker * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause */ @@ -8,69 +8,69 @@ import org.junit.After; import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; /** - * * @author brettcsorba */ -public class PiwikJsonObjectTest{ - PiwikJsonObject obj; - - public PiwikJsonObjectTest(){ - } - - @BeforeClass - public static void setUpClass(){ - } - - @AfterClass - public static void tearDownClass(){ - } - - @Before - public void setUp(){ - obj = new PiwikJsonObject(); - } - - @After - public void tearDown(){ - } +public class PiwikJsonObjectTest { + PiwikJsonObject obj; + + public PiwikJsonObjectTest() { + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + obj = new PiwikJsonObject(); + } + + @After + public void tearDown() { + } + + /** + * Test of get method, of class PiwikJsonObject. + */ + @Test + public void testMethods() { + assertTrue(obj.isEmpty()); + assertEquals(0, obj.size()); + assertNull(obj.put("key", "value")); + assertFalse(obj.isEmpty()); + assertEquals(1, obj.size()); + assertEquals("value", obj.get("key")); + assertEquals("value", obj.remove("key")); + assertNull(obj.remove("key")); + assertTrue(obj.isEmpty()); + assertEquals(0, obj.size()); + } + + /** + * Test of toString method, of class PiwikJsonObject. + */ + @Test + public void testToString() { + obj.put("key", "value"); + obj.put("key2", "value2"); + obj.put("key3", "value3"); + obj.remove("key2"); - /** - * Test of get method, of class PiwikJsonObject. - */ - @Test - public void testMethods(){ - assertTrue(obj.isEmpty()); - assertEquals(0, obj.size()); - assertNull(obj.put("key", "value")); - assertFalse(obj.isEmpty()); - assertEquals(1, obj.size()); - assertEquals("value", obj.get("key")); - assertEquals("value", obj.remove("key")); - assertNull(obj.remove("key")); - assertTrue(obj.isEmpty()); - assertEquals(0, obj.size()); - } + assertEquals("{\"1\":[\"key\",\"value\"],\"2\":[\"key3\",\"value3\"]}", obj.toString()); + } - /** - * Test of toString method, of class PiwikJsonObject. - */ - @Test - public void testToString(){ - obj.put("key", "value"); - obj.put("key2", "value2"); - obj.put("key3", "value3"); - obj.remove("key2"); - - assertEquals("{\"1\":[\"key\",\"value\"],\"2\":[\"key3\",\"value3\"]}", obj.toString()); - } - } diff --git a/src/test/java/org/piwik/java/tracking/PiwikLocaleTest.java b/src/test/java/org/piwik/java/tracking/PiwikLocaleTest.java index e2d720da..b14c4943 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikLocaleTest.java +++ b/src/test/java/org/piwik/java/tracking/PiwikLocaleTest.java @@ -1,69 +1,70 @@ -/* +/* * Piwik Java Tracker - * + * * @link https://github.com/piwik/piwik-java-tracker * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause */ package org.piwik.java.tracking; -import java.util.Locale; import org.junit.After; import org.junit.AfterClass; -import static org.junit.Assert.assertEquals; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import java.util.Locale; + +import static org.junit.Assert.assertEquals; + /** - * * @author brettcsorba */ -public class PiwikLocaleTest{ - PiwikLocale locale; - - public PiwikLocaleTest(){ - } - - @BeforeClass - public static void setUpClass(){ - } - - @AfterClass - public static void tearDownClass(){ - } - - @Before - public void setUp(){ - locale = new PiwikLocale(Locale.US); - } - - @After - public void tearDown(){ - } +public class PiwikLocaleTest { + PiwikLocale locale; + + public PiwikLocaleTest() { + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + locale = new PiwikLocale(Locale.US); + } + + @After + public void tearDown() { + } + + /** + * Test of getLocale method, of class PiwikLocale. + */ + @Test + public void testConstructor() { + assertEquals(Locale.US, locale.getLocale()); + } - /** - * Test of getLocale method, of class PiwikLocale. - */ - @Test - public void testConstructor(){ - assertEquals(Locale.US, locale.getLocale()); - } + /** + * Test of setLocale method, of class PiwikLocale. + */ + @Test + public void testLocale() { + locale.setLocale(Locale.GERMANY); + assertEquals(Locale.GERMANY, locale.getLocale()); + } - /** - * Test of setLocale method, of class PiwikLocale. - */ - @Test - public void testLocale(){ - locale.setLocale(Locale.GERMANY); - assertEquals(Locale.GERMANY, locale.getLocale()); - } + /** + * Test of toString method, of class PiwikLocale. + */ + @Test + public void testToString() { + assertEquals("us", locale.toString()); + } - /** - * Test of toString method, of class PiwikLocale. - */ - @Test - public void testToString(){ - assertEquals("us", locale.toString()); - } - } diff --git a/src/test/java/org/piwik/java/tracking/PiwikRequestTest.java b/src/test/java/org/piwik/java/tracking/PiwikRequestTest.java index e599028a..6009f7b5 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikRequestTest.java +++ b/src/test/java/org/piwik/java/tracking/PiwikRequestTest.java @@ -1,1571 +1,1589 @@ -/* +/* * Piwik Java Tracker - * + * * @link https://github.com/piwik/piwik-java-tracker * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause */ package org.piwik.java.tracking; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import javax.xml.bind.TypeConstraintException; import java.net.URL; import java.nio.charset.Charset; import java.util.List; import java.util.Locale; -import javax.xml.bind.TypeConstraintException; -import org.junit.After; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import org.junit.Before; -import org.junit.Test; /** - * * @author brettcsorba */ -public class PiwikRequestTest{ - private PiwikRequest request; - - @Before - public void setUp() throws Exception{ - request = new PiwikRequest(3, new URL("http://test.com")); - } - - @After - public void tearDown(){ - } - - @Test - public void testConstructor() throws Exception{ - request = new PiwikRequest(3, new URL("http://test.com")); - assertEquals(Integer.valueOf(3), request.getSiteId()); - assertTrue(request.getRequired()); - assertEquals(new URL("http://test.com"), request.getActionUrl()); - assertNotNull(request.getVisitorId()); - assertNotNull(request.getRandomValue()); - assertEquals("1", request.getApiVersion()); - assertFalse(request.getResponseAsImage()); - } - - /** - * Test of getActionName method, of class PiwikRequest. - */ - @Test - public void testActionName(){ - request.setActionName("action"); - assertEquals("action", request.getActionName()); - request.setActionName(null); - assertNull(request.getActionName()); - } - - /** - * Test of getActionTime method, of class PiwikRequest. - */ - @Test - public void testActionTime(){ - request.setActionTime(1000L); - assertEquals(Long.valueOf(1000L), request.getActionTime()); - } - - /** - * Test of getActionUrl method, of class PiwikRequest. - */ - @Test - public void testActionUrl() throws Exception{ - request.setActionUrl(null); - assertNull(request.getActionUrl()); - assertNull(request.getActionUrlAsString()); - - URL url = new URL("http://action.com"); - request.setActionUrl(url); - assertEquals(url, request.getActionUrl()); - try{ - request.getActionUrlAsString(); - fail("Exception should have been thrown."); - } - catch(TypeConstraintException e){ - assertEquals("The stored Action URL is a URL, not a String. Use \"getActionUrl\" instead.", e.getLocalizedMessage()); - } - - request.setActionUrlWithString(null); - assertNull(request.getActionUrl()); - assertNull(request.getActionUrlAsString()); - - request.setActionUrlWithString("actionUrl"); - assertEquals("actionUrl", request.getActionUrlAsString()); - try{ - request.getActionUrl(); - fail("Exception should have been thrown."); - } - catch(TypeConstraintException e){ - assertEquals("The stored Action URL is a String, not a URL. Use \"getActionUrlAsString\" instead.", e.getLocalizedMessage()); - } - } - - /** - * Test of getApiVersion method, of class PiwikRequest. - */ - @Test - public void testApiVersion(){ - request.setApiVersion("2"); - assertEquals("2", request.getApiVersion()); - } - - /** - * Test of getAuthToken method, of class PiwikRequest. - */ - @Test - public void testAuthTokenTT(){ - try{ - request.setAuthToken("1234"); - fail("Exception should have been thrown."); - } - catch(IllegalArgumentException e){ - assertEquals("1234 is not 32 characters long.", e.getLocalizedMessage()); - } - } - @Test - public void testAuthTokenTF(){ - request.setAuthToken("12345678901234567890123456789012"); - assertEquals("12345678901234567890123456789012", request.getAuthToken()); - } - @Test - public void testAuthTokenF(){ - request.setAuthToken("12345678901234567890123456789012"); - request.setAuthToken(null); - assertNull(request.getAuthToken()); - } - - /** - * Test of verifyAuthTokenSet method, of class PiwikRequest. - */ - @Test - public void testVerifyAuthTokenSet(){ - try{ - request.verifyAuthTokenSet(); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("AuthToken must be set before this value can be set.", e.getLocalizedMessage()); - } - } - - /** - * Test of getCampaignKeyword method, of class PiwikRequest. - */ - @Test - public void testCampaignKeyword(){ - request.setCampaignKeyword("keyword"); - assertEquals("keyword", request.getCampaignKeyword()); - } - - /** - * Test of getCampaignName method, of class PiwikRequest. - */ - @Test - public void testCampaignName(){ - request.setCampaignName("name"); - assertEquals("name", request.getCampaignName()); - } - - /** - * Test of getCharacterSet method, of class PiwikRequest. - */ - @Test - public void testCharacterSet(){ - Charset charset = Charset.defaultCharset(); - request.setCharacterSet(charset); - assertEquals(charset, request.getCharacterSet()); - } - - /** - * Test of getContentInteraction method, of class PiwikRequest. - */ - @Test - public void testContentInteraction(){ - request.setContentInteraction("interaction"); - assertEquals("interaction", request.getContentInteraction()); - } - - /** - * Test of getContentName method, of class PiwikRequest. - */ - @Test - public void testContentName(){ - request.setContentName("name"); - assertEquals("name", request.getContentName()); - } - - /** - * Test of getContentPiece method, of class PiwikRequest. - */ - @Test - public void testContentPiece(){ - request.setContentPiece("piece"); - assertEquals("piece", request.getContentPiece()); - } - - /** - * Test of getContentTarget method, of class PiwikRequest. - */ - @Test - public void testContentTarget() throws Exception{ - URL url = new URL("http://target.com"); - request.setContentTarget(url); - assertEquals(url, request.getContentTarget()); - - try{ - request.getContentTargetAsString(); - fail("Exception should have been thrown."); - } - catch(TypeConstraintException e){ - assertEquals("The stored Content Target is a URL, not a String. Use \"getContentTarget\" instead.", e.getLocalizedMessage()); - } - - request.setContentTargetWithString("contentTarget"); - assertEquals("contentTarget", request.getContentTargetAsString()); - - try{ - request.getContentTarget(); - fail("Exception should have been thrown."); - } - catch(TypeConstraintException e){ - assertEquals("The stored Content Target is a String, not a URL. Use \"getContentTargetAsString\" instead.", e.getLocalizedMessage()); - } - } - - /** - * Test of getCurrentHour method, of class PiwikRequest. - */ - @Test - public void testCurrentHour(){ - request.setCurrentHour(1); - assertEquals(Integer.valueOf(1), request.getCurrentHour()); - } - - /** - * Test of getCurrentMinute method, of class PiwikRequest. - */ - @Test - public void testCurrentMinute(){ - request.setCurrentMinute(2); - assertEquals(Integer.valueOf(2), request.getCurrentMinute()); - } - - /** - * Test of getCurrentSecond method, of class PiwikRequest. - */ - @Test - public void testCurrentSecond(){ - request.setCurrentSecond(3); - assertEquals(Integer.valueOf(3), request.getCurrentSecond()); - } - - /** - * Test of getCustomTrackingParameter method, of class PiwikRequest. - */ - @Test - public void testGetCustomTrackingParameter_T(){ - try{ - request.getCustomTrackingParameter(null); - fail("Exception should have been thrown."); - } - catch(NullPointerException e){ - assertEquals("Key cannot be null.", e.getLocalizedMessage()); - } - } - @Test - public void testGetCustomTrackingParameter_FT(){ - assertTrue(request.getCustomTrackingParameter("key").isEmpty()); - } - @Test - public void testSetCustomTrackingParameter_T(){ - try{ - request.setCustomTrackingParameter(null, null); - fail("Exception should have been thrown."); - } - catch(NullPointerException e){ - assertEquals("Key cannot be null.", e.getLocalizedMessage()); - } - } - @Test - public void testSetCustomTrackingParameter_F(){ - request.setCustomTrackingParameter("key", "value"); - List l = request.getCustomTrackingParameter("key"); - assertEquals(1, l.size()); - assertEquals("value", l.get(0)); - - request.setCustomTrackingParameter("key", "value2"); - l = request.getCustomTrackingParameter("key"); - assertEquals(1, l.size()); - assertEquals("value2", l.get(0)); - - request.setCustomTrackingParameter("key", null); - l = request.getCustomTrackingParameter("key"); - assertTrue(l.isEmpty()); - } - @Test - public void testAddCustomTrackingParameter_T(){ - try{ - request.addCustomTrackingParameter(null, null); - fail("Exception should have been thrown."); - } - catch(NullPointerException e){ - assertEquals("Key cannot be null.", e.getLocalizedMessage()); - } - } - @Test - public void testAddCustomTrackingParameter_FT(){ - try{ - request.addCustomTrackingParameter("key", null); - fail("Exception should have been thrown."); - } - catch(NullPointerException e){ - assertEquals("Cannot add a null custom tracking parameter.", e.getLocalizedMessage()); - } - } - @Test - public void testAddCustomTrackingParameter_FF(){ - request.addCustomTrackingParameter("key", "value"); - List l = request.getCustomTrackingParameter("key"); - assertEquals(1, l.size()); - assertEquals("value", l.get(0)); - - request.addCustomTrackingParameter("key", "value2"); - l = request.getCustomTrackingParameter("key"); - assertEquals(2, l.size()); - assertTrue(l.contains("value")); - assertTrue(l.contains("value2")); - } - @Test - public void testClearCustomTrackingParameter(){ - request.setCustomTrackingParameter("key", "value"); - request.clearCustomTrackingParameter(); - List l = request.getCustomTrackingParameter("key"); - assertTrue(l.isEmpty()); - } - - /** - * Test of getDeviceResolution method, of class PiwikRequest. - */ - @Test - public void testDeviceResolution(){ - request.setDeviceResolution("1x2"); - assertEquals("1x2", request.getDeviceResolution()); - } - - /** - * Test of getDownloadUrl method, of class PiwikRequest. - */ - @Test - public void testDownloadUrl() throws Exception{ - URL url = new URL("http://download.com"); - request.setDownloadUrl(url); - assertEquals(url, request.getDownloadUrl()); - - try{ - request.getDownloadUrlAsString(); - fail("Exception should have been thrown."); - } - catch(TypeConstraintException e){ - assertEquals("The stored Download URL is a URL, not a String. Use \"getDownloadUrl\" instead.", e.getLocalizedMessage()); - } - - request.setDownloadUrlWithString("downloadUrl"); - assertEquals("downloadUrl", request.getDownloadUrlAsString()); - - try{ - request.getDownloadUrl(); - fail("Exception should have been thrown."); - } - catch(TypeConstraintException e){ - assertEquals("The stored Download URL is a String, not a URL. Use \"getDownloadUrlAsString\" instead.", e.getLocalizedMessage()); - } - } - - /** - * Test of enableEcommerce method, of class PiwikRequest. - */ - @Test - public void testEnableEcommerce(){ - request.enableEcommerce(); - assertEquals(Integer.valueOf(0), request.getGoalId()); - } - - /** - * Test of verifyEcommerceEnabled method, of class PiwikRequest. - */ - @Test - public void testVerifyEcommerceEnabledT(){ - try{ - request.verifyEcommerceEnabled(); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); - } - } - @Test - public void testVerifyEcommerceEnabledFT(){ - try{ - request.setGoalId(1); - request.verifyEcommerceEnabled(); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); - } - } - @Test - public void testVerifyEcommerceEnabledFF(){ - request.enableEcommerce(); - request.verifyEcommerceEnabled(); - } - - /** - * Test of verifyEcommerceState method, of class PiwikRequest. - */ - @Test - public void testVerifyEcommerceStateE(){ - try{ - request.verifyEcommerceState(); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); - } - } - @Test - public void testVerifyEcommerceStateT(){ - try{ - request.enableEcommerce(); - request.verifyEcommerceState(); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("EcommerceId must be set before this value can be set.", - e.getLocalizedMessage()); - } - } - @Test - public void testVerifyEcommerceStateFT(){ - try{ - request.enableEcommerce(); - request.setEcommerceId("1"); - request.verifyEcommerceState(); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("EcommerceRevenue must be set before this value can be set.", - e.getLocalizedMessage()); - } - } - @Test - public void testVerifyEcommerceStateFF(){ - request.enableEcommerce(); - request.setEcommerceId("1"); - request.setEcommerceRevenue(2.0); - request.verifyEcommerceState(); - } - - /** - * Test of getEcommerceDiscount method, of class PiwikRequest. - */ - @Test - public void testEcommerceDiscountT(){ - request.enableEcommerce(); - request.setEcommerceId("1"); - request.setEcommerceRevenue(2.0); - request.setEcommerceDiscount(1.0); - - assertEquals(Double.valueOf(1.0), request.getEcommerceDiscount()); - } - @Test - public void testEcommerceDiscountTE(){ - try{ - request.setEcommerceDiscount(1.0); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); - } - } - @Test - public void testEcommerceDiscountF(){ - request.setEcommerceDiscount(null); - - assertNull(request.getEcommerceDiscount()); - } - - /** - * Test of getEcommerceId method, of class PiwikRequest. - */ - @Test - public void testEcommerceIdT(){ - request.enableEcommerce(); - request.setEcommerceId("1"); - - assertEquals("1", request.getEcommerceId()); - } - @Test - public void testEcommerceIdTE(){ - try{ - request.setEcommerceId("1"); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); - } - } - @Test - public void testEcommerceIdF(){ - request.setEcommerceId(null); - - assertNull(request.getEcommerceId()); - } - - /** - * Test of getEcommerceItem method, of class PiwikRequest. - */ - @Test - public void testEcommerceItemE(){ - try{ - EcommerceItem item = new EcommerceItem("sku", "name", "category", 1.0, 2); - request.addEcommerceItem(item); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); - } - } - @Test - public void testEcommerceItemE2(){ - try{ - request.enableEcommerce(); - request.setEcommerceId("1"); - request.setEcommerceRevenue(2.0); - request.addEcommerceItem(null); - fail("Exception should have been thrown."); - } - catch(NullPointerException e){ - assertEquals("Value cannot be null.", e.getLocalizedMessage()); - } - } - @Test - public void testEcommerceItem(){ - assertNull(request.getEcommerceItem(0)); - - EcommerceItem item = new EcommerceItem("sku", "name", "category", 1.0, 2); - request.enableEcommerce(); - request.setEcommerceId("1"); - request.setEcommerceRevenue(2.0); - request.addEcommerceItem(item); - - assertEquals(item, request.getEcommerceItem(0)); - - request.clearEcommerceItems(); - assertNull(request.getEcommerceItem(0)); - } - - /** - * Test of getEcommerceLastOrderTimestamp method, of class PiwikRequest. - */ - @Test - public void testEcommerceLastOrderTimestampT(){ - request.enableEcommerce(); - request.setEcommerceId("1"); - request.setEcommerceRevenue(2.0); - request.setEcommerceLastOrderTimestamp(1000L); - - assertEquals(Long.valueOf(1000L), request.getEcommerceLastOrderTimestamp()); - } - @Test - public void testEcommerceLastOrderTimestampTE(){ - try{ - request.setEcommerceLastOrderTimestamp(1000L); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); - } - } - @Test - public void testEcommerceLastOrderTimestampF(){ - request.setEcommerceLastOrderTimestamp(null); - - assertNull(request.getEcommerceLastOrderTimestamp()); - } - - /** - * Test of getEcommerceRevenue method, of class PiwikRequest. - */ - @Test - public void testEcommerceRevenueT(){ - request.enableEcommerce(); - request.setEcommerceId("1"); - request.setEcommerceRevenue(20.0); - - assertEquals(Double.valueOf(20.0), request.getEcommerceRevenue()); - } - @Test - public void testEcommerceRevenueTE(){ - try{ - request.setEcommerceRevenue(20.0); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); - } - } - @Test - public void testEcommerceRevenueF(){ - request.setEcommerceRevenue(null); - - assertNull(request.getEcommerceRevenue()); - } - - /** - * Test of getEcommerceShippingCost method, of class PiwikRequest. - */ - @Test - public void testEcommerceShippingCostT(){ - request.enableEcommerce(); - request.setEcommerceId("1"); - request.setEcommerceRevenue(2.0); - request.setEcommerceShippingCost(20.0); - - assertEquals(Double.valueOf(20.0), request.getEcommerceShippingCost()); - } - @Test - public void testEcommerceShippingCostTE(){ - try{ - request.setEcommerceShippingCost(20.0); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); - } - } - @Test - public void testEcommerceShippingCostF(){ - request.setEcommerceShippingCost(null); - - assertNull(request.getEcommerceShippingCost()); - } - - /** - * Test of getEcommerceSubtotal method, of class PiwikRequest. - */ - @Test - public void testEcommerceSubtotalT(){ - request.enableEcommerce(); - request.setEcommerceId("1"); - request.setEcommerceRevenue(2.0); - request.setEcommerceSubtotal(20.0); - - assertEquals(Double.valueOf(20.0), request.getEcommerceSubtotal()); - } - @Test - public void testEcommerceSubtotalTE(){ - try{ - request.setEcommerceSubtotal(20.0); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); - } - } - @Test - public void testEcommerceSubtotalF(){ - request.setEcommerceSubtotal(null); - - assertNull(request.getEcommerceSubtotal()); - } - - /** - * Test of getEcommerceTax method, of class PiwikRequest. - */ - @Test - public void testEcommerceTaxT(){ - request.enableEcommerce(); - request.setEcommerceId("1"); - request.setEcommerceRevenue(2.0); - request.setEcommerceTax(20.0); - - assertEquals(Double.valueOf(20.0), request.getEcommerceTax()); - } - @Test - public void testEcommerceTaxTE(){ - try{ - request.setEcommerceTax(20.0); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); - } - } - @Test - public void testEcommerceTaxF(){ - request.setEcommerceTax(null); - - assertNull(request.getEcommerceTax()); - } - - /** - * Test of getEventAction method, of class PiwikRequest. - */ - @Test - public void testEventAction(){ - request.setEventAction("action"); - assertEquals("action", request.getEventAction()); - request.setEventAction(null); - assertNull(request.getEventAction()); - } - @Test - public void testEventActionException(){ - try{ - request.setEventAction(""); - fail("Exception should have been thrown"); - } - catch(IllegalArgumentException e){ - assertEquals("Value cannot be empty.", e.getLocalizedMessage()); - } - } - - /** - * Test of getEventCategory method, of class PiwikRequest. - */ - @Test - public void testEventCategory(){ - request.setEventCategory("category"); - assertEquals("category", request.getEventCategory()); - } - - /** - * Test of getEventName method, of class PiwikRequest. - */ - @Test - public void testEventName(){ - request.setEventName("name"); - assertEquals("name", request.getEventName()); - } - - - /** - * Test of getEventValue method, of class PiwikRequest. - */ - @Test - public void testEventValue(){ - request.setEventValue(1); - assertEquals(1, request.getEventValue()); - } - - /** - * Test of getGoalId method, of class PiwikRequest. - */ - @Test - public void testGoalId(){ - request.setGoalId(1); - assertEquals(Integer.valueOf(1), request.getGoalId()); - } - - /** - * Test of getGoalRevenue method, of class PiwikRequest. - */ - @Test - public void testGoalRevenueTT(){ - try{ - request.setGoalRevenue(20.0); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("GoalId must be set before GoalRevenue can be set.", - e.getLocalizedMessage()); - } - } - @Test - public void testGoalRevenueTF(){ - request.setGoalId(1); - request.setGoalRevenue(20.0); - - assertEquals(Double.valueOf(20.0), request.getGoalRevenue()); - } - @Test - public void testGoalRevenueF(){ - request.setGoalRevenue(null); - - assertNull(request.getGoalRevenue()); - } - - /** - * Test of getHeaderAcceptLanguage method, of class PiwikRequest. - */ - @Test - public void testHeaderAcceptLanguage(){ - request.setHeaderAcceptLanguage("language"); - assertEquals("language", request.getHeaderAcceptLanguage()); - } - - /** - * Test of getHeaderUserAgent method, of class PiwikRequest. - */ - @Test - public void testHeaderUserAgent(){ - request.setHeaderUserAgent("agent"); - assertEquals("agent", request.getHeaderUserAgent()); - } - - /** - * Test of getNewVisit method, of class PiwikRequest. - */ - @Test - public void testNewVisit(){ - request.setNewVisit(true); - assertEquals(true, request.getNewVisit()); - request.setNewVisit(null); - assertNull(request.getNewVisit()); - } - - /** - * Test of getOutlinkUrl method, of class PiwikRequest. - */ - @Test - public void testOutlinkUrl() throws Exception{ - URL url = new URL("http://outlink.com"); - request.setOutlinkUrl(url); - assertEquals(url, request.getOutlinkUrl()); - - try{ - request.getOutlinkUrlAsString(); - fail("Exception should have been thrown."); - } - catch(TypeConstraintException e){ - assertEquals("The stored Outlink URL is a URL, not a String. Use \"getOutlinkUrl\" instead.", e.getLocalizedMessage()); - } - - request.setOutlinkUrlWithString("outlinkUrl"); - assertEquals("outlinkUrl", request.getOutlinkUrlAsString()); - - try{ - request.getOutlinkUrl(); - fail("Exception should have been thrown."); - } - catch(TypeConstraintException e){ - assertEquals("The stored Outlink URL is a String, not a URL. Use \"getOutlinkUrlAsString\" instead.", e.getLocalizedMessage()); - } - } - - /** - * Test of getPageCustomVariable method, of class PiwikRequest. - */ - @Test - public void testPageCustomVariableStringStringE(){ - try{ - request.setPageCustomVariable(null, null); - fail("Exception should have been thrown"); - } - catch(NullPointerException e){ - assertEquals("Key cannot be null.", e.getLocalizedMessage()); - } - } - @Test - public void testPageCustomVariableStringStringE2(){ - try{ - request.setPageCustomVariable(null, "pageVal"); - fail("Exception should have been thrown"); - } - catch(NullPointerException e){ - assertEquals("Key cannot be null.", e.getLocalizedMessage()); - } - } - @Test - public void testPageCustomVariableStringStringE3(){ - try{ - request.getPageCustomVariable(null); - fail("Exception should have been thrown"); - } - catch(NullPointerException e){ - assertEquals("Key cannot be null.", e.getLocalizedMessage()); - } - } - @Test - public void testPageCustomVariableStringString(){ - assertNull(request.getPageCustomVariable("pageKey")); - request.setPageCustomVariable("pageKey", "pageVal"); - assertEquals("pageVal", request.getPageCustomVariable("pageKey")); - request.setPageCustomVariable("pageKey", null); - assertNull(request.getPageCustomVariable("pageKey")); - request.setPageCustomVariable("pageKey", "pageVal"); - assertEquals("pageVal", request.getPageCustomVariable("pageKey")); - } - @Test - public void testPageCustomVariableCustomVariable(){ - assertNull(request.getPageCustomVariable(1)); - CustomVariable cv = new CustomVariable("pageKey", "pageVal"); - request.setPageCustomVariable(cv, 1); - assertEquals(cv, request.getPageCustomVariable(1)); - request.setPageCustomVariable(null, 1); - assertNull(request.getPageCustomVariable(1)); - request.setPageCustomVariable(cv, 2); - assertEquals(cv, request.getPageCustomVariable(2)); - } - - /** - * Test of getPluginDirector method, of class PiwikRequest. - */ - @Test - public void testPluginDirector(){ - request.setPluginDirector(true); - assertEquals(true, request.getPluginDirector()); - } - - /** - * Test of getPluginFlash method, of class PiwikRequest. - */ - @Test - public void testPluginFlash(){ - request.setPluginFlash(true); - assertEquals(true, request.getPluginFlash()); - } - - /** - * Test of getPluginGears method, of class PiwikRequest. - */ - @Test - public void testPluginGears(){ - request.setPluginGears(true); - assertEquals(true, request.getPluginGears()); - } - - /** - * Test of getPluginJava method, of class PiwikRequest. - */ - @Test - public void testPluginJava(){ - request.setPluginJava(true); - assertEquals(true, request.getPluginJava()); - } - - /** - * Test of getPluginPDF method, of class PiwikRequest. - */ - @Test - public void testPluginPDF(){ - request.setPluginPDF(true); - assertEquals(true, request.getPluginPDF()); - } - - /** - * Test of getPluginQuicktime method, of class PiwikRequest. - */ - @Test - public void testPluginQuicktime(){ - request.setPluginQuicktime(true); - assertEquals(true, request.getPluginQuicktime()); - } - - /** - * Test of getPluginRealPlayer method, of class PiwikRequest. - */ - @Test - public void testPluginRealPlayer(){ - request.setPluginRealPlayer(true); - assertEquals(true, request.getPluginRealPlayer()); - } - - /** - * Test of getPluginSilverlight method, of class PiwikRequest. - */ - @Test - public void testPluginSilverlight(){ - request.setPluginSilverlight(true); - assertEquals(true, request.getPluginSilverlight()); - } - - /** - * Test of getPluginWindowsMedia method, of class PiwikRequest. - */ - @Test - public void testPluginWindowsMedia(){ - request.setPluginWindowsMedia(true); - assertEquals(true, request.getPluginWindowsMedia()); - } - - /** - * Test of getRandomValue method, of class PiwikRequest. - */ - @Test - public void testRandomValue(){ - request.setRandomValue("value"); - assertEquals("value", request.getRandomValue()); - } - - /** - * Test of setReferrerUrl method, of class PiwikRequest. - */ - @Test - public void testReferrerUrl() throws Exception{ - URL url = new URL("http://referrer.com"); - request.setReferrerUrl(url); - assertEquals(url, request.getReferrerUrl()); - - try{ - request.getReferrerUrlAsString(); - fail("Exception should have been thrown."); - } - catch(TypeConstraintException e){ - assertEquals("The stored Referrer URL is a URL, not a String. Use \"getReferrerUrl\" instead.", e.getLocalizedMessage()); - } - - request.setReferrerUrlWithString("referrerUrl"); - assertEquals("referrerUrl", request.getReferrerUrlAsString()); - - try{ - request.getReferrerUrl(); - fail("Exception should have been thrown."); - } - catch(TypeConstraintException e){ - assertEquals("The stored Referrer URL is a String, not a URL. Use \"getReferrerUrlAsString\" instead.", e.getLocalizedMessage()); - } - } - - /** - * Test of getRequestDatetime method, of class PiwikRequest. - */ - @Test - public void testRequestDatetimeTTT(){ - request.setAuthToken("12345678901234567890123456789012"); - PiwikDate date = new PiwikDate(1000L); - request.setRequestDatetime(date); - - assertEquals(date, request.getRequestDatetime()); - } - @Test - public void testRequestDatetimeTTF(){ - try{ - PiwikDate date = new PiwikDate(1000L); - request.setRequestDatetime(date); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("Because you are trying to set RequestDatetime for a time greater than 4 hours ago, AuthToken must be set first.", - e.getLocalizedMessage()); - } - } - @Test - public void testRequestDatetimeTF(){ - PiwikDate date = new PiwikDate(); - request.setRequestDatetime(date); - assertEquals(date, request.getRequestDatetime()); - } - @Test - public void testRequestDatetimeF(){ - PiwikDate date = new PiwikDate(); - request.setRequestDatetime(date); - request.setRequestDatetime(null); - assertNull(request.getRequestDatetime()); - } - - /** - * Test of getRequired method, of class PiwikRequest. - */ - @Test - public void testRequired(){ - request.setRequired(false); - assertEquals(false, request.getRequired()); - } - - /** - * Test of getResponseAsImage method, of class PiwikRequest. - */ - @Test - public void testResponseAsImage(){ - request.setResponseAsImage(true); - assertEquals(true, request.getResponseAsImage()); - } - - /** - * Test of getSearchCategory method, of class PiwikRequest. - */ - @Test - public void testSearchCategoryTT(){ - try{ - request.setSearchCategory("category"); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("SearchQuery must be set before SearchCategory can be set.", - e.getLocalizedMessage()); - } - } - @Test - public void testSearchCategoryTF(){ - request.setSearchQuery("query"); - request.setSearchCategory("category"); - assertEquals("category", request.getSearchCategory()); - } - @Test - public void testSearchCategoryF(){ - request.setSearchCategory(null); - assertNull(request.getSearchCategory()); - } - - /** - * Test of getSearchQuery method, of class PiwikRequest. - */ - @Test - public void testSearchQuery(){ - request.setSearchQuery("query"); - assertEquals("query", request.getSearchQuery()); - } - - /** - * Test of getSearchResultsCount method, of class PiwikRequest. - */ - @Test - public void testSearchResultsCountTT(){ - try{ - request.setSearchResultsCount(100L); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("SearchQuery must be set before SearchResultsCount can be set.", - e.getLocalizedMessage()); - } - } - @Test - public void testSearchResultsCountTF(){ - request.setSearchQuery("query"); - request.setSearchResultsCount(100L); - assertEquals(Long.valueOf(100L), request.getSearchResultsCount()); - } - @Test - public void testSearchResultsCountF(){ - request.setSearchResultsCount(null); - assertNull(request.getSearchResultsCount()); - } - - /** - * Test of getSiteId method, of class PiwikRequest. - */ - @Test - public void testSiteId(){ - request.setSiteId(2); - assertEquals(Integer.valueOf(2), request.getSiteId()); - } - - /** - * Test of setTrackBotRequest method, of class PiwikRequest. - */ - @Test - public void testTrackBotRequests(){ - request.setTrackBotRequests(true); - assertEquals(true, request.getTrackBotRequests()); - } - - - /** - * Test of getUserrCustomVariable method, of class PiwikRequest. - */ - @Test - public void testUserCustomVariableStringString(){ - request.setUserCustomVariable("userKey", "userValue"); - assertEquals("userValue", request.getUserCustomVariable("userKey")); - } - @Test - public void testVisitCustomVariableCustomVariable(){ - request.setRandomValue("random"); - request.setVisitorId("1234567890123456"); - - assertNull(request.getVisitCustomVariable(1)); - CustomVariable cv = new CustomVariable("visitKey", "visitVal"); - request.setVisitCustomVariable(cv, 1); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_cvar={\"1\":[\"visitKey\",\"visitVal\"]}&_id=1234567890123456&url=http://test.com", request.getQueryString()); - - request.setUserCustomVariable("key", "val"); - assertEquals(cv, request.getVisitCustomVariable(1)); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_cvar={\"1\":[\"visitKey\",\"visitVal\"],\"2\":[\"key\",\"val\"]}&_id=1234567890123456&url=http://test.com", request.getQueryString()); - - request.setVisitCustomVariable(null, 1); - assertNull(request.getVisitCustomVariable(1)); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_cvar={\"2\":[\"key\",\"val\"]}&_id=1234567890123456&url=http://test.com", request.getQueryString()); - - request.setVisitCustomVariable(cv, 2); - assertEquals(cv, request.getVisitCustomVariable(2)); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_cvar={\"2\":[\"visitKey\",\"visitVal\"]}&_id=1234567890123456&url=http://test.com", request.getQueryString()); - - request.setUserCustomVariable("visitKey", null); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com", request.getQueryString()); - } - - /** - * Test of getUserId method, of class PiwikRequest. - */ - @Test - public void testUserId(){ - request.setUserId("id"); - assertEquals("id", request.getUserId()); - } - - /** - * Test of getVisitorCity method, of class PiwikRequest. - */ - @Test - public void testVisitorCityT(){ - request.setAuthToken("12345678901234567890123456789012"); - request.setVisitorCity("city"); - assertEquals("city", request.getVisitorCity()); - } - @Test - public void testVisitorCityTE(){ - try{ - request.setVisitorCity("city"); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); - } - } - @Test - public void testVisitorCityF(){ - request.setVisitorCity(null); - assertNull(request.getVisitorCity()); - } - - /** - * Test of getVisitorCountry method, of class PiwikRequest. - */ - @Test - public void testVisitorCountryT(){ - PiwikLocale country = new PiwikLocale(Locale.US); - request.setAuthToken("12345678901234567890123456789012"); - request.setVisitorCountry(country); - - assertEquals(country, request.getVisitorCountry()); - } - @Test - public void testVisitorCountryTE(){ - try{ - PiwikLocale country = new PiwikLocale(Locale.US); - request.setVisitorCountry(country); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); - } - } - @Test - public void testVisitorCountryF(){ - request.setVisitorCountry(null); - - assertNull(request.getVisitorCountry()); - } - - /** - * Test of getVisitorCustomId method, of class PiwikRequest. - */ - @Test - public void testVisitorCustomTT(){ - try{ - request.setVisitorCustomId("1"); - fail("Exception should have been thrown."); - } - catch(IllegalArgumentException e){ - assertEquals("1 is not 16 characters long.", - e.getLocalizedMessage()); - } - } - @Test - public void testVisitorCustomTFT(){ - try{ - request.setVisitorCustomId("1234567890abcdeg"); - fail("Exception should have been thrown."); - } - catch(IllegalArgumentException e){ - assertEquals("1234567890abcdeg is not a hexadecimal string.", - e.getLocalizedMessage()); - } - } - @Test - public void testVisitorCustomIdTFF(){ - request.setVisitorCustomId("1234567890abcdef"); - assertEquals("1234567890abcdef", request.getVisitorCustomId()); - } - @Test - public void testVisitorCustomIdF(){ - request.setVisitorCustomId("1234567890abcdef"); - request.setVisitorCustomId(null); - assertNull(request.getVisitorCustomId()); - } - - /** - * Test of getVisitorFirstVisitTimestamp method, of class PiwikRequest. - */ - @Test - public void testVisitorFirstVisitTimestamp(){ - request.setVisitorFirstVisitTimestamp(1000L); - assertEquals(Long.valueOf(1000L), request.getVisitorFirstVisitTimestamp()); - } - - /** - * Test of getVisitorId method, of class PiwikRequest. - */ - @Test - public void testVisitorIdTT(){ - try{ - request.setVisitorId("1"); - fail("Exception should have been thrown."); - } - catch(IllegalArgumentException e){ - assertEquals("1 is not 16 characters long.", - e.getLocalizedMessage()); - } - } - @Test - public void testVisitorIdTFT(){ - try{ - request.setVisitorId("1234567890abcdeg"); - fail("Exception should have been thrown."); - } - catch(IllegalArgumentException e){ - assertEquals("1234567890abcdeg is not a hexadecimal string.", - e.getLocalizedMessage()); - } - } - @Test - public void testVisitorIdTFF(){ - request.setVisitorId("1234567890abcdef"); - assertEquals("1234567890abcdef", request.getVisitorId()); - } - @Test - public void testVisitorIdF(){ - request.setVisitorId("1234567890abcdef"); - request.setVisitorId(null); - assertNull(request.getVisitorId()); - } - - /** - * Test of getVisitorIp method, of class PiwikRequest. - */ - @Test - public void testVisitorIpT(){ - request.setAuthToken("12345678901234567890123456789012"); - request.setVisitorIp("ip"); - assertEquals("ip", request.getVisitorIp()); - } - @Test - public void testVisitorIpTE(){ - try{ - request.setVisitorIp("ip"); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); - } - } - @Test - public void testVisitorIpF(){ - request.setVisitorIp(null); - assertNull(request.getVisitorIp()); - } - - /** - * Test of getVisitorLatitude method, of class PiwikRequest. - */ - @Test - public void testVisitorLatitudeT(){ - request.setAuthToken("12345678901234567890123456789012"); - request.setVisitorLatitude(10.5); - assertEquals(Double.valueOf(10.5), request.getVisitorLatitude()); - } - @Test - public void testVisitorLatitudeTE(){ - try{ - request.setVisitorLatitude(10.5); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); - } - } - @Test - public void testVisitorLatitudeF(){ - request.setVisitorLatitude(null); - assertNull(request.getVisitorLatitude()); - } - - /** - * Test of getVisitorLongitude method, of class PiwikRequest. - */ - @Test - public void testVisitorLongitudeT(){ - request.setAuthToken("12345678901234567890123456789012"); - request.setVisitorLongitude(20.5); - assertEquals(Double.valueOf(20.5), request.getVisitorLongitude()); - } - @Test - public void testVisitorLongitudeTE(){ - try{ - request.setVisitorLongitude(20.5); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); - } - } - @Test - public void testVisitorLongitudeF(){ - request.setVisitorLongitude(null); - assertNull(request.getVisitorLongitude()); - } - - /** - * Test of getVisitorPreviousVisitTimestamp method, of class PiwikRequest. - */ - @Test - public void testVisitorPreviousVisitTimestamp(){ - request.setVisitorPreviousVisitTimestamp(1000L); - assertEquals(Long.valueOf(1000L), request.getVisitorPreviousVisitTimestamp()); - } - - /** - * Test of getVisitorRegion method, of class PiwikRequest. - */ - @Test - public void testVisitorRegionT(){ - request.setAuthToken("12345678901234567890123456789012"); - request.setVisitorRegion("region"); - - assertEquals("region", request.getVisitorRegion()); - } - @Test - public void testGetVisitorRegionTE(){ - try{ - request.setVisitorRegion("region"); - fail("Exception should have been thrown."); - } - catch(IllegalStateException e){ - assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); - } - } - @Test - public void testVisitorRegionF(){ - request.setVisitorRegion(null); - - assertNull(request.getVisitorRegion()); - } - - /** - * Test of getVisitorVisitCount method, of class PiwikRequest. - */ - @Test - public void testVisitorVisitCount(){ - request.setVisitorVisitCount(100); - assertEquals(Integer.valueOf(100), request.getVisitorVisitCount()); - } - - /** - * Test of getQueryString method, of class PiwikRequest. - */ - @Test - public void testGetQueryString(){ - request.setRandomValue("random"); - request.setVisitorId("1234567890123456"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com", request.getQueryString()); - request.setPageCustomVariable("key", "val"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&cvar={\"1\":[\"key\",\"val\"]}&_id=1234567890123456&url=http://test.com", - request.getQueryString()); - request.setPageCustomVariable("key", null); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com", request.getQueryString()); - request.addCustomTrackingParameter("key", "test"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com&key=test", request.getQueryString()); - request.addCustomTrackingParameter("key", "test2"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com&key=test&key=test2", request.getQueryString()); - request.setCustomTrackingParameter("key2", "test3"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com&key2=test3&key=test&key=test2", request.getQueryString()); - request.setCustomTrackingParameter("key", "test4"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com&key2=test3&key=test4", request.getQueryString()); - request.setRandomValue(null); - request.setSiteId(null); - request.setRequired(null); - request.setApiVersion(null); - request.setResponseAsImage(null); - request.setVisitorId(null); - request.setActionUrl(null); - assertEquals("key2=test3&key=test4", request.getQueryString()); - request.clearCustomTrackingParameter(); - assertEquals("", request.getQueryString()); - } - @Test - public void testGetQueryString2(){ - request.setActionUrlWithString("http://test.com"); - request.setRandomValue("random"); - request.setVisitorId("1234567890123456"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com", request.getQueryString()); - } - - /** - * Test of getUrlEncodedQueryString method, of class PiwikRequest. - */ - @Test - public void testGetUrlEncodedQueryString(){ - request.setRandomValue("random"); - request.setVisitorId("1234567890123456"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); - request.addCustomTrackingParameter("ke/y", "te:st"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com&ke%2Fy=te%3Ast", request.getUrlEncodedQueryString()); - request.addCustomTrackingParameter("ke/y", "te:st2"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2", request.getUrlEncodedQueryString()); - request.setCustomTrackingParameter("ke/y2", "te:st3"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2&ke%2Fy2=te%3Ast3", request.getUrlEncodedQueryString()); - request.setCustomTrackingParameter("ke/y", "te:st4"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com&ke%2Fy=te%3Ast4&ke%2Fy2=te%3Ast3", request.getUrlEncodedQueryString()); - request.setRandomValue(null); - request.setSiteId(null); - request.setRequired(null); - request.setApiVersion(null); - request.setResponseAsImage(null); - request.setVisitorId(null); - request.setActionUrl(null); - assertEquals("ke%2Fy=te%3Ast4&ke%2Fy2=te%3Ast3", request.getUrlEncodedQueryString()); - request.clearCustomTrackingParameter(); - assertEquals("", request.getUrlEncodedQueryString()); - } - @Test - public void testGetUrlEncodedQueryString2(){ - request.setActionUrlWithString("http://test.com"); - request.setRandomValue("random"); - request.setVisitorId("1234567890123456"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); - } - - /** - * Test of getRandomHexString method, of class PiwikRequest. - */ - @Test - public void testGetRandomHexString(){ - String s = PiwikRequest.getRandomHexString(10); - - assertEquals(10, s.length()); - Long.parseLong(s, 16); - } +public class PiwikRequestTest { + private PiwikRequest request; + + @Before + public void setUp() throws Exception { + request = new PiwikRequest(3, new URL("http://test.com")); + } + + @After + public void tearDown() { + } + + @Test + public void testConstructor() throws Exception { + request = new PiwikRequest(3, new URL("http://test.com")); + assertEquals(Integer.valueOf(3), request.getSiteId()); + assertTrue(request.getRequired()); + assertEquals(new URL("http://test.com"), request.getActionUrl()); + assertNotNull(request.getVisitorId()); + assertNotNull(request.getRandomValue()); + assertEquals("1", request.getApiVersion()); + assertFalse(request.getResponseAsImage()); + } + + /** + * Test of getActionName method, of class PiwikRequest. + */ + @Test + public void testActionName() { + request.setActionName("action"); + assertEquals("action", request.getActionName()); + request.setActionName(null); + assertNull(request.getActionName()); + } + + /** + * Test of getActionTime method, of class PiwikRequest. + */ + @Test + public void testActionTime() { + request.setActionTime(1000L); + assertEquals(Long.valueOf(1000L), request.getActionTime()); + } + + /** + * Test of getActionUrl method, of class PiwikRequest. + */ + @Test + public void testActionUrl() throws Exception { + request.setActionUrl(null); + assertNull(request.getActionUrl()); + assertNull(request.getActionUrlAsString()); + + URL url = new URL("http://action.com"); + request.setActionUrl(url); + assertEquals(url, request.getActionUrl()); + try { + request.getActionUrlAsString(); + fail("Exception should have been thrown."); + } catch (TypeConstraintException e) { + assertEquals("The stored Action URL is a URL, not a String. Use \"getActionUrl\" instead.", e.getLocalizedMessage()); + } + + request.setActionUrlWithString(null); + assertNull(request.getActionUrl()); + assertNull(request.getActionUrlAsString()); + + request.setActionUrlWithString("actionUrl"); + assertEquals("actionUrl", request.getActionUrlAsString()); + try { + request.getActionUrl(); + fail("Exception should have been thrown."); + } catch (TypeConstraintException e) { + assertEquals("The stored Action URL is a String, not a URL. Use \"getActionUrlAsString\" instead.", e.getLocalizedMessage()); + } + } + + /** + * Test of getApiVersion method, of class PiwikRequest. + */ + @Test + public void testApiVersion() { + request.setApiVersion("2"); + assertEquals("2", request.getApiVersion()); + } + + /** + * Test of getAuthToken method, of class PiwikRequest. + */ + @Test + public void testAuthTokenTT() { + try { + request.setAuthToken("1234"); + fail("Exception should have been thrown."); + } catch (IllegalArgumentException e) { + assertEquals("1234 is not 32 characters long.", e.getLocalizedMessage()); + } + } + + @Test + public void testAuthTokenTF() { + request.setAuthToken("12345678901234567890123456789012"); + assertEquals("12345678901234567890123456789012", request.getAuthToken()); + } + + @Test + public void testAuthTokenF() { + request.setAuthToken("12345678901234567890123456789012"); + request.setAuthToken(null); + assertNull(request.getAuthToken()); + } + + /** + * Test of verifyAuthTokenSet method, of class PiwikRequest. + */ + @Test + public void testVerifyAuthTokenSet() { + try { + request.verifyAuthTokenSet(); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("AuthToken must be set before this value can be set.", e.getLocalizedMessage()); + } + } + + /** + * Test of getCampaignKeyword method, of class PiwikRequest. + */ + @Test + public void testCampaignKeyword() { + request.setCampaignKeyword("keyword"); + assertEquals("keyword", request.getCampaignKeyword()); + } + + /** + * Test of getCampaignName method, of class PiwikRequest. + */ + @Test + public void testCampaignName() { + request.setCampaignName("name"); + assertEquals("name", request.getCampaignName()); + } + + /** + * Test of getCharacterSet method, of class PiwikRequest. + */ + @Test + public void testCharacterSet() { + Charset charset = Charset.defaultCharset(); + request.setCharacterSet(charset); + assertEquals(charset, request.getCharacterSet()); + } + + /** + * Test of getContentInteraction method, of class PiwikRequest. + */ + @Test + public void testContentInteraction() { + request.setContentInteraction("interaction"); + assertEquals("interaction", request.getContentInteraction()); + } + + /** + * Test of getContentName method, of class PiwikRequest. + */ + @Test + public void testContentName() { + request.setContentName("name"); + assertEquals("name", request.getContentName()); + } + + /** + * Test of getContentPiece method, of class PiwikRequest. + */ + @Test + public void testContentPiece() { + request.setContentPiece("piece"); + assertEquals("piece", request.getContentPiece()); + } + + /** + * Test of getContentTarget method, of class PiwikRequest. + */ + @Test + public void testContentTarget() throws Exception { + URL url = new URL("http://target.com"); + request.setContentTarget(url); + assertEquals(url, request.getContentTarget()); + + try { + request.getContentTargetAsString(); + fail("Exception should have been thrown."); + } catch (TypeConstraintException e) { + assertEquals("The stored Content Target is a URL, not a String. Use \"getContentTarget\" instead.", e.getLocalizedMessage()); + } + + request.setContentTargetWithString("contentTarget"); + assertEquals("contentTarget", request.getContentTargetAsString()); + + try { + request.getContentTarget(); + fail("Exception should have been thrown."); + } catch (TypeConstraintException e) { + assertEquals("The stored Content Target is a String, not a URL. Use \"getContentTargetAsString\" instead.", e.getLocalizedMessage()); + } + } + + /** + * Test of getCurrentHour method, of class PiwikRequest. + */ + @Test + public void testCurrentHour() { + request.setCurrentHour(1); + assertEquals(Integer.valueOf(1), request.getCurrentHour()); + } + + /** + * Test of getCurrentMinute method, of class PiwikRequest. + */ + @Test + public void testCurrentMinute() { + request.setCurrentMinute(2); + assertEquals(Integer.valueOf(2), request.getCurrentMinute()); + } + + /** + * Test of getCurrentSecond method, of class PiwikRequest. + */ + @Test + public void testCurrentSecond() { + request.setCurrentSecond(3); + assertEquals(Integer.valueOf(3), request.getCurrentSecond()); + } + + /** + * Test of getCustomTrackingParameter method, of class PiwikRequest. + */ + @Test + public void testGetCustomTrackingParameter_T() { + try { + request.getCustomTrackingParameter(null); + fail("Exception should have been thrown."); + } catch (NullPointerException e) { + assertEquals("Key cannot be null.", e.getLocalizedMessage()); + } + } + + @Test + public void testGetCustomTrackingParameter_FT() { + assertTrue(request.getCustomTrackingParameter("key").isEmpty()); + } + + @Test + public void testSetCustomTrackingParameter_T() { + try { + request.setCustomTrackingParameter(null, null); + fail("Exception should have been thrown."); + } catch (NullPointerException e) { + assertEquals("Key cannot be null.", e.getLocalizedMessage()); + } + } + + @Test + public void testSetCustomTrackingParameter_F() { + request.setCustomTrackingParameter("key", "value"); + List l = request.getCustomTrackingParameter("key"); + assertEquals(1, l.size()); + assertEquals("value", l.get(0)); + + request.setCustomTrackingParameter("key", "value2"); + l = request.getCustomTrackingParameter("key"); + assertEquals(1, l.size()); + assertEquals("value2", l.get(0)); + + request.setCustomTrackingParameter("key", null); + l = request.getCustomTrackingParameter("key"); + assertTrue(l.isEmpty()); + } + + @Test + public void testAddCustomTrackingParameter_T() { + try { + request.addCustomTrackingParameter(null, null); + fail("Exception should have been thrown."); + } catch (NullPointerException e) { + assertEquals("Key cannot be null.", e.getLocalizedMessage()); + } + } + + @Test + public void testAddCustomTrackingParameter_FT() { + try { + request.addCustomTrackingParameter("key", null); + fail("Exception should have been thrown."); + } catch (NullPointerException e) { + assertEquals("Cannot add a null custom tracking parameter.", e.getLocalizedMessage()); + } + } + + @Test + public void testAddCustomTrackingParameter_FF() { + request.addCustomTrackingParameter("key", "value"); + List l = request.getCustomTrackingParameter("key"); + assertEquals(1, l.size()); + assertEquals("value", l.get(0)); + + request.addCustomTrackingParameter("key", "value2"); + l = request.getCustomTrackingParameter("key"); + assertEquals(2, l.size()); + assertTrue(l.contains("value")); + assertTrue(l.contains("value2")); + } + + @Test + public void testClearCustomTrackingParameter() { + request.setCustomTrackingParameter("key", "value"); + request.clearCustomTrackingParameter(); + List l = request.getCustomTrackingParameter("key"); + assertTrue(l.isEmpty()); + } + + /** + * Test of getDeviceResolution method, of class PiwikRequest. + */ + @Test + public void testDeviceResolution() { + request.setDeviceResolution("1x2"); + assertEquals("1x2", request.getDeviceResolution()); + } + + /** + * Test of getDownloadUrl method, of class PiwikRequest. + */ + @Test + public void testDownloadUrl() throws Exception { + URL url = new URL("http://download.com"); + request.setDownloadUrl(url); + assertEquals(url, request.getDownloadUrl()); + + try { + request.getDownloadUrlAsString(); + fail("Exception should have been thrown."); + } catch (TypeConstraintException e) { + assertEquals("The stored Download URL is a URL, not a String. Use \"getDownloadUrl\" instead.", e.getLocalizedMessage()); + } + + request.setDownloadUrlWithString("downloadUrl"); + assertEquals("downloadUrl", request.getDownloadUrlAsString()); + + try { + request.getDownloadUrl(); + fail("Exception should have been thrown."); + } catch (TypeConstraintException e) { + assertEquals("The stored Download URL is a String, not a URL. Use \"getDownloadUrlAsString\" instead.", e.getLocalizedMessage()); + } + } + + /** + * Test of enableEcommerce method, of class PiwikRequest. + */ + @Test + public void testEnableEcommerce() { + request.enableEcommerce(); + assertEquals(Integer.valueOf(0), request.getGoalId()); + } + + /** + * Test of verifyEcommerceEnabled method, of class PiwikRequest. + */ + @Test + public void testVerifyEcommerceEnabledT() { + try { + request.verifyEcommerceEnabled(); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", + e.getLocalizedMessage()); + } + } + + @Test + public void testVerifyEcommerceEnabledFT() { + try { + request.setGoalId(1); + request.verifyEcommerceEnabled(); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", + e.getLocalizedMessage()); + } + } + + @Test + public void testVerifyEcommerceEnabledFF() { + request.enableEcommerce(); + request.verifyEcommerceEnabled(); + } + + /** + * Test of verifyEcommerceState method, of class PiwikRequest. + */ + @Test + public void testVerifyEcommerceStateE() { + try { + request.verifyEcommerceState(); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", + e.getLocalizedMessage()); + } + } + + @Test + public void testVerifyEcommerceStateT() { + try { + request.enableEcommerce(); + request.verifyEcommerceState(); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("EcommerceId must be set before this value can be set.", + e.getLocalizedMessage()); + } + } + + @Test + public void testVerifyEcommerceStateFT() { + try { + request.enableEcommerce(); + request.setEcommerceId("1"); + request.verifyEcommerceState(); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("EcommerceRevenue must be set before this value can be set.", + e.getLocalizedMessage()); + } + } + + @Test + public void testVerifyEcommerceStateFF() { + request.enableEcommerce(); + request.setEcommerceId("1"); + request.setEcommerceRevenue(2.0); + request.verifyEcommerceState(); + } + + /** + * Test of getEcommerceDiscount method, of class PiwikRequest. + */ + @Test + public void testEcommerceDiscountT() { + request.enableEcommerce(); + request.setEcommerceId("1"); + request.setEcommerceRevenue(2.0); + request.setEcommerceDiscount(1.0); + + assertEquals(Double.valueOf(1.0), request.getEcommerceDiscount()); + } + + @Test + public void testEcommerceDiscountTE() { + try { + request.setEcommerceDiscount(1.0); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", + e.getLocalizedMessage()); + } + } + + @Test + public void testEcommerceDiscountF() { + request.setEcommerceDiscount(null); + + assertNull(request.getEcommerceDiscount()); + } + + /** + * Test of getEcommerceId method, of class PiwikRequest. + */ + @Test + public void testEcommerceIdT() { + request.enableEcommerce(); + request.setEcommerceId("1"); + + assertEquals("1", request.getEcommerceId()); + } + + @Test + public void testEcommerceIdTE() { + try { + request.setEcommerceId("1"); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", + e.getLocalizedMessage()); + } + } + + @Test + public void testEcommerceIdF() { + request.setEcommerceId(null); + + assertNull(request.getEcommerceId()); + } + + /** + * Test of getEcommerceItem method, of class PiwikRequest. + */ + @Test + public void testEcommerceItemE() { + try { + EcommerceItem item = new EcommerceItem("sku", "name", "category", 1.0, 2); + request.addEcommerceItem(item); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", + e.getLocalizedMessage()); + } + } + + @Test + public void testEcommerceItemE2() { + try { + request.enableEcommerce(); + request.setEcommerceId("1"); + request.setEcommerceRevenue(2.0); + request.addEcommerceItem(null); + fail("Exception should have been thrown."); + } catch (NullPointerException e) { + assertEquals("Value cannot be null.", e.getLocalizedMessage()); + } + } + + @Test + public void testEcommerceItem() { + assertNull(request.getEcommerceItem(0)); + + EcommerceItem item = new EcommerceItem("sku", "name", "category", 1.0, 2); + request.enableEcommerce(); + request.setEcommerceId("1"); + request.setEcommerceRevenue(2.0); + request.addEcommerceItem(item); + + assertEquals(item, request.getEcommerceItem(0)); + + request.clearEcommerceItems(); + assertNull(request.getEcommerceItem(0)); + } + + /** + * Test of getEcommerceLastOrderTimestamp method, of class PiwikRequest. + */ + @Test + public void testEcommerceLastOrderTimestampT() { + request.enableEcommerce(); + request.setEcommerceId("1"); + request.setEcommerceRevenue(2.0); + request.setEcommerceLastOrderTimestamp(1000L); + + assertEquals(Long.valueOf(1000L), request.getEcommerceLastOrderTimestamp()); + } + + @Test + public void testEcommerceLastOrderTimestampTE() { + try { + request.setEcommerceLastOrderTimestamp(1000L); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", + e.getLocalizedMessage()); + } + } + + @Test + public void testEcommerceLastOrderTimestampF() { + request.setEcommerceLastOrderTimestamp(null); + + assertNull(request.getEcommerceLastOrderTimestamp()); + } + + /** + * Test of getEcommerceRevenue method, of class PiwikRequest. + */ + @Test + public void testEcommerceRevenueT() { + request.enableEcommerce(); + request.setEcommerceId("1"); + request.setEcommerceRevenue(20.0); + + assertEquals(Double.valueOf(20.0), request.getEcommerceRevenue()); + } + + @Test + public void testEcommerceRevenueTE() { + try { + request.setEcommerceRevenue(20.0); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", + e.getLocalizedMessage()); + } + } + + @Test + public void testEcommerceRevenueF() { + request.setEcommerceRevenue(null); + + assertNull(request.getEcommerceRevenue()); + } + + /** + * Test of getEcommerceShippingCost method, of class PiwikRequest. + */ + @Test + public void testEcommerceShippingCostT() { + request.enableEcommerce(); + request.setEcommerceId("1"); + request.setEcommerceRevenue(2.0); + request.setEcommerceShippingCost(20.0); + + assertEquals(Double.valueOf(20.0), request.getEcommerceShippingCost()); + } + + @Test + public void testEcommerceShippingCostTE() { + try { + request.setEcommerceShippingCost(20.0); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", + e.getLocalizedMessage()); + } + } + + @Test + public void testEcommerceShippingCostF() { + request.setEcommerceShippingCost(null); + + assertNull(request.getEcommerceShippingCost()); + } + + /** + * Test of getEcommerceSubtotal method, of class PiwikRequest. + */ + @Test + public void testEcommerceSubtotalT() { + request.enableEcommerce(); + request.setEcommerceId("1"); + request.setEcommerceRevenue(2.0); + request.setEcommerceSubtotal(20.0); + + assertEquals(Double.valueOf(20.0), request.getEcommerceSubtotal()); + } + + @Test + public void testEcommerceSubtotalTE() { + try { + request.setEcommerceSubtotal(20.0); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", + e.getLocalizedMessage()); + } + } + + @Test + public void testEcommerceSubtotalF() { + request.setEcommerceSubtotal(null); + + assertNull(request.getEcommerceSubtotal()); + } + + /** + * Test of getEcommerceTax method, of class PiwikRequest. + */ + @Test + public void testEcommerceTaxT() { + request.enableEcommerce(); + request.setEcommerceId("1"); + request.setEcommerceRevenue(2.0); + request.setEcommerceTax(20.0); + + assertEquals(Double.valueOf(20.0), request.getEcommerceTax()); + } + + @Test + public void testEcommerceTaxTE() { + try { + request.setEcommerceTax(20.0); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", + e.getLocalizedMessage()); + } + } + + @Test + public void testEcommerceTaxF() { + request.setEcommerceTax(null); + + assertNull(request.getEcommerceTax()); + } + + /** + * Test of getEventAction method, of class PiwikRequest. + */ + @Test + public void testEventAction() { + request.setEventAction("action"); + assertEquals("action", request.getEventAction()); + request.setEventAction(null); + assertNull(request.getEventAction()); + } + + @Test + public void testEventActionException() { + try { + request.setEventAction(""); + fail("Exception should have been thrown"); + } catch (IllegalArgumentException e) { + assertEquals("Value cannot be empty.", e.getLocalizedMessage()); + } + } + + /** + * Test of getEventCategory method, of class PiwikRequest. + */ + @Test + public void testEventCategory() { + request.setEventCategory("category"); + assertEquals("category", request.getEventCategory()); + } + + /** + * Test of getEventName method, of class PiwikRequest. + */ + @Test + public void testEventName() { + request.setEventName("name"); + assertEquals("name", request.getEventName()); + } + + + /** + * Test of getEventValue method, of class PiwikRequest. + */ + @Test + public void testEventValue() { + request.setEventValue(1); + assertEquals(1, request.getEventValue()); + } + + /** + * Test of getGoalId method, of class PiwikRequest. + */ + @Test + public void testGoalId() { + request.setGoalId(1); + assertEquals(Integer.valueOf(1), request.getGoalId()); + } + + /** + * Test of getGoalRevenue method, of class PiwikRequest. + */ + @Test + public void testGoalRevenueTT() { + try { + request.setGoalRevenue(20.0); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("GoalId must be set before GoalRevenue can be set.", + e.getLocalizedMessage()); + } + } + + @Test + public void testGoalRevenueTF() { + request.setGoalId(1); + request.setGoalRevenue(20.0); + + assertEquals(Double.valueOf(20.0), request.getGoalRevenue()); + } + + @Test + public void testGoalRevenueF() { + request.setGoalRevenue(null); + + assertNull(request.getGoalRevenue()); + } + + /** + * Test of getHeaderAcceptLanguage method, of class PiwikRequest. + */ + @Test + public void testHeaderAcceptLanguage() { + request.setHeaderAcceptLanguage("language"); + assertEquals("language", request.getHeaderAcceptLanguage()); + } + + /** + * Test of getHeaderUserAgent method, of class PiwikRequest. + */ + @Test + public void testHeaderUserAgent() { + request.setHeaderUserAgent("agent"); + assertEquals("agent", request.getHeaderUserAgent()); + } + + /** + * Test of getNewVisit method, of class PiwikRequest. + */ + @Test + public void testNewVisit() { + request.setNewVisit(true); + assertEquals(true, request.getNewVisit()); + request.setNewVisit(null); + assertNull(request.getNewVisit()); + } + + /** + * Test of getOutlinkUrl method, of class PiwikRequest. + */ + @Test + public void testOutlinkUrl() throws Exception { + URL url = new URL("http://outlink.com"); + request.setOutlinkUrl(url); + assertEquals(url, request.getOutlinkUrl()); + + try { + request.getOutlinkUrlAsString(); + fail("Exception should have been thrown."); + } catch (TypeConstraintException e) { + assertEquals("The stored Outlink URL is a URL, not a String. Use \"getOutlinkUrl\" instead.", e.getLocalizedMessage()); + } + + request.setOutlinkUrlWithString("outlinkUrl"); + assertEquals("outlinkUrl", request.getOutlinkUrlAsString()); + + try { + request.getOutlinkUrl(); + fail("Exception should have been thrown."); + } catch (TypeConstraintException e) { + assertEquals("The stored Outlink URL is a String, not a URL. Use \"getOutlinkUrlAsString\" instead.", e.getLocalizedMessage()); + } + } + + /** + * Test of getPageCustomVariable method, of class PiwikRequest. + */ + @Test + public void testPageCustomVariableStringStringE() { + try { + request.setPageCustomVariable(null, null); + fail("Exception should have been thrown"); + } catch (NullPointerException e) { + assertEquals("Key cannot be null.", e.getLocalizedMessage()); + } + } + + @Test + public void testPageCustomVariableStringStringE2() { + try { + request.setPageCustomVariable(null, "pageVal"); + fail("Exception should have been thrown"); + } catch (NullPointerException e) { + assertEquals("Key cannot be null.", e.getLocalizedMessage()); + } + } + + @Test + public void testPageCustomVariableStringStringE3() { + try { + request.getPageCustomVariable(null); + fail("Exception should have been thrown"); + } catch (NullPointerException e) { + assertEquals("Key cannot be null.", e.getLocalizedMessage()); + } + } + + @Test + public void testPageCustomVariableStringString() { + assertNull(request.getPageCustomVariable("pageKey")); + request.setPageCustomVariable("pageKey", "pageVal"); + assertEquals("pageVal", request.getPageCustomVariable("pageKey")); + request.setPageCustomVariable("pageKey", null); + assertNull(request.getPageCustomVariable("pageKey")); + request.setPageCustomVariable("pageKey", "pageVal"); + assertEquals("pageVal", request.getPageCustomVariable("pageKey")); + } + + @Test + public void testPageCustomVariableCustomVariable() { + assertNull(request.getPageCustomVariable(1)); + CustomVariable cv = new CustomVariable("pageKey", "pageVal"); + request.setPageCustomVariable(cv, 1); + assertEquals(cv, request.getPageCustomVariable(1)); + request.setPageCustomVariable(null, 1); + assertNull(request.getPageCustomVariable(1)); + request.setPageCustomVariable(cv, 2); + assertEquals(cv, request.getPageCustomVariable(2)); + } + + /** + * Test of getPluginDirector method, of class PiwikRequest. + */ + @Test + public void testPluginDirector() { + request.setPluginDirector(true); + assertEquals(true, request.getPluginDirector()); + } + + /** + * Test of getPluginFlash method, of class PiwikRequest. + */ + @Test + public void testPluginFlash() { + request.setPluginFlash(true); + assertEquals(true, request.getPluginFlash()); + } + + /** + * Test of getPluginGears method, of class PiwikRequest. + */ + @Test + public void testPluginGears() { + request.setPluginGears(true); + assertEquals(true, request.getPluginGears()); + } + + /** + * Test of getPluginJava method, of class PiwikRequest. + */ + @Test + public void testPluginJava() { + request.setPluginJava(true); + assertEquals(true, request.getPluginJava()); + } + + /** + * Test of getPluginPDF method, of class PiwikRequest. + */ + @Test + public void testPluginPDF() { + request.setPluginPDF(true); + assertEquals(true, request.getPluginPDF()); + } + + /** + * Test of getPluginQuicktime method, of class PiwikRequest. + */ + @Test + public void testPluginQuicktime() { + request.setPluginQuicktime(true); + assertEquals(true, request.getPluginQuicktime()); + } + + /** + * Test of getPluginRealPlayer method, of class PiwikRequest. + */ + @Test + public void testPluginRealPlayer() { + request.setPluginRealPlayer(true); + assertEquals(true, request.getPluginRealPlayer()); + } + + /** + * Test of getPluginSilverlight method, of class PiwikRequest. + */ + @Test + public void testPluginSilverlight() { + request.setPluginSilverlight(true); + assertEquals(true, request.getPluginSilverlight()); + } + + /** + * Test of getPluginWindowsMedia method, of class PiwikRequest. + */ + @Test + public void testPluginWindowsMedia() { + request.setPluginWindowsMedia(true); + assertEquals(true, request.getPluginWindowsMedia()); + } + + /** + * Test of getRandomValue method, of class PiwikRequest. + */ + @Test + public void testRandomValue() { + request.setRandomValue("value"); + assertEquals("value", request.getRandomValue()); + } + + /** + * Test of setReferrerUrl method, of class PiwikRequest. + */ + @Test + public void testReferrerUrl() throws Exception { + URL url = new URL("http://referrer.com"); + request.setReferrerUrl(url); + assertEquals(url, request.getReferrerUrl()); + + try { + request.getReferrerUrlAsString(); + fail("Exception should have been thrown."); + } catch (TypeConstraintException e) { + assertEquals("The stored Referrer URL is a URL, not a String. Use \"getReferrerUrl\" instead.", e.getLocalizedMessage()); + } + + request.setReferrerUrlWithString("referrerUrl"); + assertEquals("referrerUrl", request.getReferrerUrlAsString()); + + try { + request.getReferrerUrl(); + fail("Exception should have been thrown."); + } catch (TypeConstraintException e) { + assertEquals("The stored Referrer URL is a String, not a URL. Use \"getReferrerUrlAsString\" instead.", e.getLocalizedMessage()); + } + } + + /** + * Test of getRequestDatetime method, of class PiwikRequest. + */ + @Test + public void testRequestDatetimeTTT() { + request.setAuthToken("12345678901234567890123456789012"); + PiwikDate date = new PiwikDate(1000L); + request.setRequestDatetime(date); + + assertEquals(date, request.getRequestDatetime()); + } + + @Test + public void testRequestDatetimeTTF() { + try { + PiwikDate date = new PiwikDate(1000L); + request.setRequestDatetime(date); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("Because you are trying to set RequestDatetime for a time greater than 4 hours ago, AuthToken must be set first.", + e.getLocalizedMessage()); + } + } + + @Test + public void testRequestDatetimeTF() { + PiwikDate date = new PiwikDate(); + request.setRequestDatetime(date); + assertEquals(date, request.getRequestDatetime()); + } + + @Test + public void testRequestDatetimeF() { + PiwikDate date = new PiwikDate(); + request.setRequestDatetime(date); + request.setRequestDatetime(null); + assertNull(request.getRequestDatetime()); + } + + /** + * Test of getRequired method, of class PiwikRequest. + */ + @Test + public void testRequired() { + request.setRequired(false); + assertEquals(false, request.getRequired()); + } + + /** + * Test of getResponseAsImage method, of class PiwikRequest. + */ + @Test + public void testResponseAsImage() { + request.setResponseAsImage(true); + assertEquals(true, request.getResponseAsImage()); + } + + /** + * Test of getSearchCategory method, of class PiwikRequest. + */ + @Test + public void testSearchCategoryTT() { + try { + request.setSearchCategory("category"); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("SearchQuery must be set before SearchCategory can be set.", + e.getLocalizedMessage()); + } + } + + @Test + public void testSearchCategoryTF() { + request.setSearchQuery("query"); + request.setSearchCategory("category"); + assertEquals("category", request.getSearchCategory()); + } + + @Test + public void testSearchCategoryF() { + request.setSearchCategory(null); + assertNull(request.getSearchCategory()); + } + + /** + * Test of getSearchQuery method, of class PiwikRequest. + */ + @Test + public void testSearchQuery() { + request.setSearchQuery("query"); + assertEquals("query", request.getSearchQuery()); + } + + /** + * Test of getSearchResultsCount method, of class PiwikRequest. + */ + @Test + public void testSearchResultsCountTT() { + try { + request.setSearchResultsCount(100L); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("SearchQuery must be set before SearchResultsCount can be set.", + e.getLocalizedMessage()); + } + } + + @Test + public void testSearchResultsCountTF() { + request.setSearchQuery("query"); + request.setSearchResultsCount(100L); + assertEquals(Long.valueOf(100L), request.getSearchResultsCount()); + } + + @Test + public void testSearchResultsCountF() { + request.setSearchResultsCount(null); + assertNull(request.getSearchResultsCount()); + } + + /** + * Test of getSiteId method, of class PiwikRequest. + */ + @Test + public void testSiteId() { + request.setSiteId(2); + assertEquals(Integer.valueOf(2), request.getSiteId()); + } + + /** + * Test of setTrackBotRequest method, of class PiwikRequest. + */ + @Test + public void testTrackBotRequests() { + request.setTrackBotRequests(true); + assertEquals(true, request.getTrackBotRequests()); + } + + + /** + * Test of getUserrCustomVariable method, of class PiwikRequest. + */ + @Test + public void testUserCustomVariableStringString() { + request.setUserCustomVariable("userKey", "userValue"); + assertEquals("userValue", request.getUserCustomVariable("userKey")); + } + + @Test + public void testVisitCustomVariableCustomVariable() { + request.setRandomValue("random"); + request.setVisitorId("1234567890123456"); + + assertNull(request.getVisitCustomVariable(1)); + CustomVariable cv = new CustomVariable("visitKey", "visitVal"); + request.setVisitCustomVariable(cv, 1); + assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_cvar={\"1\":[\"visitKey\",\"visitVal\"]}&_id=1234567890123456&url=http://test.com", request.getQueryString()); + + request.setUserCustomVariable("key", "val"); + assertEquals(cv, request.getVisitCustomVariable(1)); + assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_cvar={\"1\":[\"visitKey\",\"visitVal\"],\"2\":[\"key\",\"val\"]}&_id=1234567890123456&url=http://test.com", request.getQueryString()); + + request.setVisitCustomVariable(null, 1); + assertNull(request.getVisitCustomVariable(1)); + assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_cvar={\"2\":[\"key\",\"val\"]}&_id=1234567890123456&url=http://test.com", request.getQueryString()); + + request.setVisitCustomVariable(cv, 2); + assertEquals(cv, request.getVisitCustomVariable(2)); + assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_cvar={\"2\":[\"visitKey\",\"visitVal\"]}&_id=1234567890123456&url=http://test.com", request.getQueryString()); + + request.setUserCustomVariable("visitKey", null); + assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com", request.getQueryString()); + } + + /** + * Test of getUserId method, of class PiwikRequest. + */ + @Test + public void testUserId() { + request.setUserId("id"); + assertEquals("id", request.getUserId()); + } + + /** + * Test of getVisitorCity method, of class PiwikRequest. + */ + @Test + public void testVisitorCityT() { + request.setAuthToken("12345678901234567890123456789012"); + request.setVisitorCity("city"); + assertEquals("city", request.getVisitorCity()); + } + + @Test + public void testVisitorCityTE() { + try { + request.setVisitorCity("city"); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("AuthToken must be set before this value can be set.", + e.getLocalizedMessage()); + } + } + + @Test + public void testVisitorCityF() { + request.setVisitorCity(null); + assertNull(request.getVisitorCity()); + } + + /** + * Test of getVisitorCountry method, of class PiwikRequest. + */ + @Test + public void testVisitorCountryT() { + PiwikLocale country = new PiwikLocale(Locale.US); + request.setAuthToken("12345678901234567890123456789012"); + request.setVisitorCountry(country); + + assertEquals(country, request.getVisitorCountry()); + } + + @Test + public void testVisitorCountryTE() { + try { + PiwikLocale country = new PiwikLocale(Locale.US); + request.setVisitorCountry(country); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("AuthToken must be set before this value can be set.", + e.getLocalizedMessage()); + } + } + + @Test + public void testVisitorCountryF() { + request.setVisitorCountry(null); + + assertNull(request.getVisitorCountry()); + } + + /** + * Test of getVisitorCustomId method, of class PiwikRequest. + */ + @Test + public void testVisitorCustomTT() { + try { + request.setVisitorCustomId("1"); + fail("Exception should have been thrown."); + } catch (IllegalArgumentException e) { + assertEquals("1 is not 16 characters long.", + e.getLocalizedMessage()); + } + } + + @Test + public void testVisitorCustomTFT() { + try { + request.setVisitorCustomId("1234567890abcdeg"); + fail("Exception should have been thrown."); + } catch (IllegalArgumentException e) { + assertEquals("1234567890abcdeg is not a hexadecimal string.", + e.getLocalizedMessage()); + } + } + + @Test + public void testVisitorCustomIdTFF() { + request.setVisitorCustomId("1234567890abcdef"); + assertEquals("1234567890abcdef", request.getVisitorCustomId()); + } + + @Test + public void testVisitorCustomIdF() { + request.setVisitorCustomId("1234567890abcdef"); + request.setVisitorCustomId(null); + assertNull(request.getVisitorCustomId()); + } + + /** + * Test of getVisitorFirstVisitTimestamp method, of class PiwikRequest. + */ + @Test + public void testVisitorFirstVisitTimestamp() { + request.setVisitorFirstVisitTimestamp(1000L); + assertEquals(Long.valueOf(1000L), request.getVisitorFirstVisitTimestamp()); + } + + /** + * Test of getVisitorId method, of class PiwikRequest. + */ + @Test + public void testVisitorIdTT() { + try { + request.setVisitorId("1"); + fail("Exception should have been thrown."); + } catch (IllegalArgumentException e) { + assertEquals("1 is not 16 characters long.", + e.getLocalizedMessage()); + } + } + + @Test + public void testVisitorIdTFT() { + try { + request.setVisitorId("1234567890abcdeg"); + fail("Exception should have been thrown."); + } catch (IllegalArgumentException e) { + assertEquals("1234567890abcdeg is not a hexadecimal string.", + e.getLocalizedMessage()); + } + } + + @Test + public void testVisitorIdTFF() { + request.setVisitorId("1234567890abcdef"); + assertEquals("1234567890abcdef", request.getVisitorId()); + } + + @Test + public void testVisitorIdF() { + request.setVisitorId("1234567890abcdef"); + request.setVisitorId(null); + assertNull(request.getVisitorId()); + } + + /** + * Test of getVisitorIp method, of class PiwikRequest. + */ + @Test + public void testVisitorIpT() { + request.setAuthToken("12345678901234567890123456789012"); + request.setVisitorIp("ip"); + assertEquals("ip", request.getVisitorIp()); + } + + @Test + public void testVisitorIpTE() { + try { + request.setVisitorIp("ip"); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("AuthToken must be set before this value can be set.", + e.getLocalizedMessage()); + } + } + + @Test + public void testVisitorIpF() { + request.setVisitorIp(null); + assertNull(request.getVisitorIp()); + } + + /** + * Test of getVisitorLatitude method, of class PiwikRequest. + */ + @Test + public void testVisitorLatitudeT() { + request.setAuthToken("12345678901234567890123456789012"); + request.setVisitorLatitude(10.5); + assertEquals(Double.valueOf(10.5), request.getVisitorLatitude()); + } + + @Test + public void testVisitorLatitudeTE() { + try { + request.setVisitorLatitude(10.5); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("AuthToken must be set before this value can be set.", + e.getLocalizedMessage()); + } + } + + @Test + public void testVisitorLatitudeF() { + request.setVisitorLatitude(null); + assertNull(request.getVisitorLatitude()); + } + + /** + * Test of getVisitorLongitude method, of class PiwikRequest. + */ + @Test + public void testVisitorLongitudeT() { + request.setAuthToken("12345678901234567890123456789012"); + request.setVisitorLongitude(20.5); + assertEquals(Double.valueOf(20.5), request.getVisitorLongitude()); + } + + @Test + public void testVisitorLongitudeTE() { + try { + request.setVisitorLongitude(20.5); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("AuthToken must be set before this value can be set.", + e.getLocalizedMessage()); + } + } + + @Test + public void testVisitorLongitudeF() { + request.setVisitorLongitude(null); + assertNull(request.getVisitorLongitude()); + } + + /** + * Test of getVisitorPreviousVisitTimestamp method, of class PiwikRequest. + */ + @Test + public void testVisitorPreviousVisitTimestamp() { + request.setVisitorPreviousVisitTimestamp(1000L); + assertEquals(Long.valueOf(1000L), request.getVisitorPreviousVisitTimestamp()); + } + + /** + * Test of getVisitorRegion method, of class PiwikRequest. + */ + @Test + public void testVisitorRegionT() { + request.setAuthToken("12345678901234567890123456789012"); + request.setVisitorRegion("region"); + + assertEquals("region", request.getVisitorRegion()); + } + + @Test + public void testGetVisitorRegionTE() { + try { + request.setVisitorRegion("region"); + fail("Exception should have been thrown."); + } catch (IllegalStateException e) { + assertEquals("AuthToken must be set before this value can be set.", + e.getLocalizedMessage()); + } + } + + @Test + public void testVisitorRegionF() { + request.setVisitorRegion(null); + + assertNull(request.getVisitorRegion()); + } + + /** + * Test of getVisitorVisitCount method, of class PiwikRequest. + */ + @Test + public void testVisitorVisitCount() { + request.setVisitorVisitCount(100); + assertEquals(Integer.valueOf(100), request.getVisitorVisitCount()); + } + + /** + * Test of getQueryString method, of class PiwikRequest. + */ + @Test + public void testGetQueryString() { + request.setRandomValue("random"); + request.setVisitorId("1234567890123456"); + assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com", request.getQueryString()); + request.setPageCustomVariable("key", "val"); + assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&cvar={\"1\":[\"key\",\"val\"]}&_id=1234567890123456&url=http://test.com", + request.getQueryString()); + request.setPageCustomVariable("key", null); + assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com", request.getQueryString()); + request.addCustomTrackingParameter("key", "test"); + assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com&key=test", request.getQueryString()); + request.addCustomTrackingParameter("key", "test2"); + assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com&key=test&key=test2", request.getQueryString()); + request.setCustomTrackingParameter("key2", "test3"); + assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com&key2=test3&key=test&key=test2", request.getQueryString()); + request.setCustomTrackingParameter("key", "test4"); + assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com&key2=test3&key=test4", request.getQueryString()); + request.setRandomValue(null); + request.setSiteId(null); + request.setRequired(null); + request.setApiVersion(null); + request.setResponseAsImage(null); + request.setVisitorId(null); + request.setActionUrl(null); + assertEquals("key2=test3&key=test4", request.getQueryString()); + request.clearCustomTrackingParameter(); + assertEquals("", request.getQueryString()); + } + + @Test + public void testGetQueryString2() { + request.setActionUrlWithString("http://test.com"); + request.setRandomValue("random"); + request.setVisitorId("1234567890123456"); + assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com", request.getQueryString()); + } + + /** + * Test of getUrlEncodedQueryString method, of class PiwikRequest. + */ + @Test + public void testGetUrlEncodedQueryString() { + request.setRandomValue("random"); + request.setVisitorId("1234567890123456"); + assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); + request.addCustomTrackingParameter("ke/y", "te:st"); + assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com&ke%2Fy=te%3Ast", request.getUrlEncodedQueryString()); + request.addCustomTrackingParameter("ke/y", "te:st2"); + assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2", request.getUrlEncodedQueryString()); + request.setCustomTrackingParameter("ke/y2", "te:st3"); + assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2&ke%2Fy2=te%3Ast3", request.getUrlEncodedQueryString()); + request.setCustomTrackingParameter("ke/y", "te:st4"); + assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com&ke%2Fy=te%3Ast4&ke%2Fy2=te%3Ast3", request.getUrlEncodedQueryString()); + request.setRandomValue(null); + request.setSiteId(null); + request.setRequired(null); + request.setApiVersion(null); + request.setResponseAsImage(null); + request.setVisitorId(null); + request.setActionUrl(null); + assertEquals("ke%2Fy=te%3Ast4&ke%2Fy2=te%3Ast3", request.getUrlEncodedQueryString()); + request.clearCustomTrackingParameter(); + assertEquals("", request.getUrlEncodedQueryString()); + } + + @Test + public void testGetUrlEncodedQueryString2() { + request.setActionUrlWithString("http://test.com"); + request.setRandomValue("random"); + request.setVisitorId("1234567890123456"); + assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); + } + + /** + * Test of getRandomHexString method, of class PiwikRequest. + */ + @Test + public void testGetRandomHexString() { + String s = PiwikRequest.getRandomHexString(10); + + assertEquals(10, s.length()); + Long.parseLong(s, 16); + } } diff --git a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java b/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java index e4589287..e812fe20 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java +++ b/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java @@ -15,7 +15,11 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.util.EntityUtils; -import org.junit.*; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; import org.mockito.ArgumentMatcher; import java.io.IOException; @@ -28,342 +32,346 @@ import java.util.List; import java.util.concurrent.Future; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Matchers.argThat; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; /** * @author brettcsorba */ public class PiwikTrackerTest { - // https://stackoverflow.com/a/3732328 - static class Handler implements HttpHandler { - @Override - public void handle(HttpExchange exchange) throws IOException { - String response = "OK"; - exchange.sendResponseHeaders(200, response.length()); - OutputStream os = exchange.getResponseBody(); - os.write(response.getBytes()); - os.close(); - } + // https://stackoverflow.com/a/3732328 + static class Handler implements HttpHandler { + @Override + public void handle(HttpExchange exchange) throws IOException { + String response = "OK"; + exchange.sendResponseHeaders(200, response.length()); + OutputStream os = exchange.getResponseBody(); + os.write(response.getBytes()); + os.close(); } - - PiwikTracker piwikTracker; - PiwikTracker localTracker; - HttpServer server; - - public PiwikTrackerTest() { + } + + PiwikTracker piwikTracker; + PiwikTracker localTracker; + HttpServer server; + + public PiwikTrackerTest() { + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + // test with mocks + piwikTracker = spy(new PiwikTracker("http://test.com")); + + // test with local server + localTracker = new PiwikTracker("http://localhost:8001/test"); + try { + server = HttpServer.create(new InetSocketAddress(8001), 0); + server.createContext("/test", new Handler()); + server.setExecutor(null); // creates a default executor + server.start(); + } catch (IOException ex) { } - - @BeforeClass - public static void setUpClass() { + } + + @After + public void tearDown() { + server.stop(0); + } + + /** + * Test of addParameter method, of class PiwikTracker. + */ + @Test + public void testAddParameter() { + } + + /** + * Test of sendRequest method, of class PiwikTracker. + */ + @Test + public void testSendRequest() throws Exception { + PiwikRequest request = mock(PiwikRequest.class); + HttpClient client = mock(HttpClient.class); + HttpResponse response = mock(HttpResponse.class); + + doReturn(client).when(piwikTracker).getHttpClient(); + doReturn("query").when(request).getQueryString(); + doReturn(response).when(client) + .execute(argThat(new CorrectGetRequest("http://test.com?query"))); + + assertEquals(response, piwikTracker.sendRequest(request)); + } + + /** + * Test of sendRequestAsync method, of class PiwikTracker. + */ + @Test + public void testSendRequestAsync() throws Exception { + PiwikRequest request = mock(PiwikRequest.class); + CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); + HttpResponse response = mock(HttpResponse.class); + Future future = mock(Future.class); + + doReturn(client).when(piwikTracker).getHttpAsyncClient(); + doReturn("query").when(request).getQueryString(); + doReturn(response).when(future).get(); + doReturn(true).when(future).isDone(); + doReturn(future).when(client) + .execute(argThat(new CorrectGetRequest("http://test.com?query")), any()); + + assertEquals(response, piwikTracker.sendRequestAsync(request).get()); + } + + /** + * Test sync API with local server + */ + @Test + public void testWithLocalServer() throws Exception { + // one + PiwikRequest request = new PiwikRequest(3, new URL("http://test.com")); + HttpResponse response = localTracker.sendRequest(request); + String msg = EntityUtils.toString(response.getEntity()); + assertEquals("OK", msg); + + // bulk + List requests = Collections.singletonList(request); + HttpResponse responseBulk = localTracker.sendBulkRequest(requests); + String msgBulk = EntityUtils.toString(responseBulk.getEntity()); + assertEquals("OK", msgBulk); + } + + /** + * Test async API with local server + */ + @Test + public void testWithLocalServerAsync() throws Exception { + // one + PiwikRequest request = new PiwikRequest(3, new URL("http://test.com")); + HttpResponse response = localTracker.sendRequestAsync(request).get(); + String msg = EntityUtils.toString(response.getEntity()); + assertEquals("OK", msg); + + // bulk + List requests = Collections.singletonList(request); + HttpResponse responseBulk = localTracker.sendBulkRequestAsync(requests).get(); + String msgBulk = EntityUtils.toString(responseBulk.getEntity()); + assertEquals("OK", msgBulk); + } + + static class CorrectGetRequest implements ArgumentMatcher { + String url; + + public CorrectGetRequest(String url) { + this.url = url; } - @AfterClass - public static void tearDownClass() { + @Override + public boolean matches(HttpGet get) { + return url.equals(get.getURI().toString()); } - - @Before - public void setUp() { - // test with mocks - piwikTracker = spy(new PiwikTracker("http://test.com")); - - // test with local server - localTracker = new PiwikTracker("http://localhost:8001/test"); - try { - server = HttpServer.create(new InetSocketAddress(8001), 0); - server.createContext("/test", new Handler()); - server.setExecutor(null); // creates a default executor - server.start(); - } catch (IOException ex) { - } + } + + /** + * Test of sendBulkRequest method, of class PiwikTracker. + */ + @Test + public void testSendBulkRequest_Iterable() throws Exception { + List requests = new ArrayList<>(); + HttpResponse response = mock(HttpResponse.class); + + doReturn(response).when(piwikTracker).sendBulkRequest(requests, null); + + assertEquals(response, piwikTracker.sendBulkRequest(requests)); + } + + /** + * Test of sendBulkRequest method, of class PiwikTracker. + */ + @Test + public void testSendBulkRequest_Iterable_StringTT() throws Exception { + try { + List requests = new ArrayList<>(); + HttpClient client = mock(HttpClient.class); + PiwikRequest request = mock(PiwikRequest.class); + + doReturn("query").when(request).getQueryString(); + requests.add(request); + doReturn(client).when(piwikTracker).getHttpClient(); + + piwikTracker.sendBulkRequest(requests, "1"); + fail("Exception should have been thrown."); + } catch (IllegalArgumentException e) { + assertEquals("1 is not 32 characters long.", e.getLocalizedMessage()); } - - @After - public void tearDown() { - server.stop(0); + } + + @Test + public void testSendBulkRequest_Iterable_StringFF() throws Exception { + List requests = new ArrayList<>(); + HttpClient client = mock(HttpClient.class); + PiwikRequest request = mock(PiwikRequest.class); + HttpResponse response = mock(HttpResponse.class); + + doReturn("query").when(request).getQueryString(); + requests.add(request); + doReturn(client).when(piwikTracker).getHttpClient(); + doReturn(response).when(client).execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"]}"))); + + assertEquals(response, piwikTracker.sendBulkRequest(requests, null)); + } + + @Test + public void testSendBulkRequest_Iterable_StringFT() throws Exception { + List requests = new ArrayList<>(); + HttpClient client = mock(HttpClient.class); + PiwikRequest request = mock(PiwikRequest.class); + HttpResponse response = mock(HttpResponse.class); + + doReturn("query").when(request).getQueryString(); + requests.add(request); + doReturn(client).when(piwikTracker).getHttpClient(); + doReturn(response).when(client) + .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"],\"token_auth\":\"12345678901234567890123456789012\"}"))); + + assertEquals(response, piwikTracker.sendBulkRequest(requests, "12345678901234567890123456789012")); + } + + /** + * Test of sendBulkRequestAsync method, of class PiwikTracker. + */ + @Test + public void testSendBulkRequestAsync_Iterable() throws Exception { + List requests = new ArrayList<>(); + HttpResponse response = mock(HttpResponse.class); + Future future = mock(Future.class); + doReturn(response).when(future).get(); + doReturn(true).when(future).isDone(); + + doReturn(future).when(piwikTracker).sendBulkRequestAsync(requests, null); + + assertEquals(response, piwikTracker.sendBulkRequestAsync(requests).get()); + } + + /** + * Test of sendBulkRequestAsync method, of class PiwikTracker. + */ + @Test + public void testSendBulkRequestAsync_Iterable_StringTT() throws Exception { + try { + List requests = new ArrayList<>(); + CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); + PiwikRequest request = mock(PiwikRequest.class); + + doReturn("query").when(request).getQueryString(); + requests.add(request); + doReturn(client).when(piwikTracker).getHttpAsyncClient(); + + piwikTracker.sendBulkRequestAsync(requests, "1"); + fail("Exception should have been thrown."); + } catch (IllegalArgumentException e) { + assertEquals("1 is not 32 characters long.", e.getLocalizedMessage()); } - - /** - * Test of addParameter method, of class PiwikTracker. - */ - @Test - public void testAddParameter() { + } + + @Test + public void testSendBulkRequestAsync_Iterable_StringFF() throws Exception { + List requests = new ArrayList<>(); + CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); + PiwikRequest request = mock(PiwikRequest.class); + HttpResponse response = mock(HttpResponse.class); + Future future = mock(Future.class); + doReturn(response).when(future).get(); + doReturn(true).when(future).isDone(); + + doReturn("query").when(request).getQueryString(); + requests.add(request); + doReturn(client).when(piwikTracker).getHttpAsyncClient(); + doReturn(future).when(client) + .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"]}")), any()); + + assertEquals(response, piwikTracker.sendBulkRequestAsync(requests, null).get()); + } + + @Test + public void testSendBulkRequestAsync_Iterable_StringFT() throws Exception { + List requests = new ArrayList<>(); + CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); + PiwikRequest request = mock(PiwikRequest.class); + HttpResponse response = mock(HttpResponse.class); + Future future = mock(Future.class); + doReturn(response).when(future).get(); + doReturn(true).when(future).isDone(); + + doReturn("query").when(request).getQueryString(); + requests.add(request); + doReturn(client).when(piwikTracker).getHttpAsyncClient(); + doReturn(future).when(client) + .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"],\"token_auth\":\"12345678901234567890123456789012\"}")), any()); + + assertEquals(response, piwikTracker.sendBulkRequestAsync(requests, "12345678901234567890123456789012").get()); + } + + static class CorrectPostRequest implements ArgumentMatcher { + String body; + + public CorrectPostRequest(String body) { + this.body = body; } - /** - * Test of sendRequest method, of class PiwikTracker. - */ - @Test - public void testSendRequest() throws Exception { - PiwikRequest request = mock(PiwikRequest.class); - HttpClient client = mock(HttpClient.class); - HttpResponse response = mock(HttpResponse.class); - - doReturn(client).when(piwikTracker).getHttpClient(); - doReturn("query").when(request).getQueryString(); - doReturn(response).when(client) - .execute(argThat(new CorrectGetRequest("http://test.com?query"))); - - assertEquals(response, piwikTracker.sendRequest(request)); - } - - /** - * Test of sendRequestAsync method, of class PiwikTracker. - */ - @Test - public void testSendRequestAsync() throws Exception { - PiwikRequest request = mock(PiwikRequest.class); - CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); - HttpResponse response = mock(HttpResponse.class); - Future future = mock(Future.class); - - doReturn(client).when(piwikTracker).getHttpAsyncClient(); - doReturn("query").when(request).getQueryString(); - doReturn(response).when(future).get(); - doReturn(true).when(future).isDone(); - doReturn(future).when(client) - .execute(argThat(new CorrectGetRequest("http://test.com?query")), any()); - - assertEquals(response, piwikTracker.sendRequestAsync(request).get()); - } - - /** - * Test sync API with local server - */ - @Test - public void testWithLocalServer() throws Exception { - // one - PiwikRequest request = new PiwikRequest(3, new URL("http://test.com")); - HttpResponse response = localTracker.sendRequest(request); - String msg = EntityUtils.toString(response.getEntity()); - assertEquals("OK", msg); - - // bulk - List requests = Collections.singletonList(request); - HttpResponse responseBulk = localTracker.sendBulkRequest(requests); - String msgBulk = EntityUtils.toString(responseBulk.getEntity()); - assertEquals("OK", msgBulk); - } - - /** - * Test async API with local server - */ - @Test - public void testWithLocalServerAsync() throws Exception { - // one - PiwikRequest request = new PiwikRequest(3, new URL("http://test.com")); - HttpResponse response = localTracker.sendRequestAsync(request).get(); - String msg = EntityUtils.toString(response.getEntity()); - assertEquals("OK", msg); - - // bulk - List requests = Collections.singletonList(request); - HttpResponse responseBulk = localTracker.sendBulkRequestAsync(requests).get(); - String msgBulk = EntityUtils.toString(responseBulk.getEntity()); - assertEquals("OK", msgBulk); - } - - static class CorrectGetRequest implements ArgumentMatcher { - String url; - - public CorrectGetRequest(String url) { - this.url = url; - } - - @Override - public boolean matches(HttpGet get) { - return url.equals(get.getURI().toString()); - } - } - - /** - * Test of sendBulkRequest method, of class PiwikTracker. - */ - @Test - public void testSendBulkRequest_Iterable() throws Exception { - List requests = new ArrayList<>(); - HttpResponse response = mock(HttpResponse.class); - - doReturn(response).when(piwikTracker).sendBulkRequest(requests, null); - - assertEquals(response, piwikTracker.sendBulkRequest(requests)); - } - - /** - * Test of sendBulkRequest method, of class PiwikTracker. - */ - @Test - public void testSendBulkRequest_Iterable_StringTT() throws Exception { - try { - List requests = new ArrayList<>(); - HttpClient client = mock(HttpClient.class); - PiwikRequest request = mock(PiwikRequest.class); - - doReturn("query").when(request).getQueryString(); - requests.add(request); - doReturn(client).when(piwikTracker).getHttpClient(); - - piwikTracker.sendBulkRequest(requests, "1"); - fail("Exception should have been thrown."); - } catch (IllegalArgumentException e) { - assertEquals("1 is not 32 characters long.", e.getLocalizedMessage()); - } - } - - @Test - public void testSendBulkRequest_Iterable_StringFF() throws Exception { - List requests = new ArrayList<>(); - HttpClient client = mock(HttpClient.class); - PiwikRequest request = mock(PiwikRequest.class); - HttpResponse response = mock(HttpResponse.class); - - doReturn("query").when(request).getQueryString(); - requests.add(request); - doReturn(client).when(piwikTracker).getHttpClient(); - doReturn(response).when(client).execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"]}"))); - - assertEquals(response, piwikTracker.sendBulkRequest(requests, null)); - } - - @Test - public void testSendBulkRequest_Iterable_StringFT() throws Exception { - List requests = new ArrayList<>(); - HttpClient client = mock(HttpClient.class); - PiwikRequest request = mock(PiwikRequest.class); - HttpResponse response = mock(HttpResponse.class); - - doReturn("query").when(request).getQueryString(); - requests.add(request); - doReturn(client).when(piwikTracker).getHttpClient(); - doReturn(response).when(client) - .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"],\"token_auth\":\"12345678901234567890123456789012\"}"))); - - assertEquals(response, piwikTracker.sendBulkRequest(requests, "12345678901234567890123456789012")); - } - - /** - * Test of sendBulkRequestAsync method, of class PiwikTracker. - */ - @Test - public void testSendBulkRequestAsync_Iterable() throws Exception { - List requests = new ArrayList<>(); - HttpResponse response = mock(HttpResponse.class); - Future future = mock(Future.class); - doReturn(response).when(future).get(); - doReturn(true).when(future).isDone(); - - doReturn(future).when(piwikTracker).sendBulkRequestAsync(requests, null); - - assertEquals(response, piwikTracker.sendBulkRequestAsync(requests).get()); - } - - /** - * Test of sendBulkRequestAsync method, of class PiwikTracker. - */ - @Test - public void testSendBulkRequestAsync_Iterable_StringTT() throws Exception { - try { - List requests = new ArrayList<>(); - CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); - PiwikRequest request = mock(PiwikRequest.class); - - doReturn("query").when(request).getQueryString(); - requests.add(request); - doReturn(client).when(piwikTracker).getHttpAsyncClient(); - - piwikTracker.sendBulkRequestAsync(requests, "1"); - fail("Exception should have been thrown."); - } catch (IllegalArgumentException e) { - assertEquals("1 is not 32 characters long.", e.getLocalizedMessage()); - } - } - - @Test - public void testSendBulkRequestAsync_Iterable_StringFF() throws Exception { - List requests = new ArrayList<>(); - CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); - PiwikRequest request = mock(PiwikRequest.class); - HttpResponse response = mock(HttpResponse.class); - Future future = mock(Future.class); - doReturn(response).when(future).get(); - doReturn(true).when(future).isDone(); - - doReturn("query").when(request).getQueryString(); - requests.add(request); - doReturn(client).when(piwikTracker).getHttpAsyncClient(); - doReturn(future).when(client) - .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"]}")), any()); - - assertEquals(response, piwikTracker.sendBulkRequestAsync(requests, null).get()); - } - - @Test - public void testSendBulkRequestAsync_Iterable_StringFT() throws Exception { - List requests = new ArrayList<>(); - CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); - PiwikRequest request = mock(PiwikRequest.class); - HttpResponse response = mock(HttpResponse.class); - Future future = mock(Future.class); - doReturn(response).when(future).get(); - doReturn(true).when(future).isDone(); - - doReturn("query").when(request).getQueryString(); - requests.add(request); - doReturn(client).when(piwikTracker).getHttpAsyncClient(); - doReturn(future).when(client) - .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"],\"token_auth\":\"12345678901234567890123456789012\"}")), any()); - - assertEquals(response, piwikTracker.sendBulkRequestAsync(requests, "12345678901234567890123456789012").get()); - } - - static class CorrectPostRequest implements ArgumentMatcher { - String body; - - public CorrectPostRequest(String body) { - this.body = body; - } - - @Override - public boolean matches(HttpPost post) { - try { - InputStream bais = post.getEntity().getContent(); - byte[] bytes = new byte[bais.available()]; - bais.read(bytes); - String str = new String(bytes); - return body.equals(str); - } catch (IOException e) { - fail("Exception should not have been throw."); - } - return false; - } - } - - /** - * Test of getHttpClient method, of class PiwikTracker. - */ - @Test - public void testGetHttpClient() { - assertNotNull(piwikTracker.getHttpClient()); - } - - /** - * Test of getHttpAsyncClient method, of class PiwikTracker. - */ - @Test - public void testGetHttpAsyncClient() { - assertNotNull(piwikTracker.getHttpAsyncClient()); - } - - /** - * Test of getHttpClient method, of class PiwikTracker, with proxy. - */ - @Test - public void testGetHttpClientWithProxy() { - piwikTracker = new PiwikTracker("http://test.com", "http://proxy", 8080); - HttpClient httpClient = piwikTracker.getHttpClient(); - - assertNotNull(httpClient); + @Override + public boolean matches(HttpPost post) { + try { + InputStream bais = post.getEntity().getContent(); + byte[] bytes = new byte[bais.available()]; + bais.read(bytes); + String str = new String(bytes); + return body.equals(str); + } catch (IOException e) { + fail("Exception should not have been throw."); + } + return false; } + } + + /** + * Test of getHttpClient method, of class PiwikTracker. + */ + @Test + public void testGetHttpClient() { + assertNotNull(piwikTracker.getHttpClient()); + } + + /** + * Test of getHttpAsyncClient method, of class PiwikTracker. + */ + @Test + public void testGetHttpAsyncClient() { + assertNotNull(piwikTracker.getHttpAsyncClient()); + } + + /** + * Test of getHttpClient method, of class PiwikTracker, with proxy. + */ + @Test + public void testGetHttpClientWithProxy() { + piwikTracker = new PiwikTracker("http://test.com", "http://proxy", 8080); + HttpClient httpClient = piwikTracker.getHttpClient(); + + assertNotNull(httpClient); + } } From aef5994b78682f94e283a0b90b5dbdefe318224a Mon Sep 17 00:00:00 2001 From: Thomas Lutz Date: Sun, 27 Dec 2020 02:17:47 +0100 Subject: [PATCH 027/467] Revert README code formatting --- README.md | 96 +++++++++++++++---------------------------------------- 1 file changed, 26 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index 1ac02a3b..3e0b8d19 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,7 @@ Matomo Java Tracker ================ [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/matomo-java-tracker/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/matomo-java-tracker) - ## Code Status - [![Build Status](https://travis-ci.org/matomo-org/matomo-java-tracker.svg?branch=master)](https://travis-ci.org/matomo-org/matomo-java-tracker) [![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/matomo-org/matomo-java-tracker.svg)](https://isitmaintained.com/project/matomo-org/matomo-java-tracker "Average time to resolve an issue") [![Percentage of issues still open](https://isitmaintained.com/badge/open/matomo-org/matomo-java-tracker.svg)](https://isitmaintained.com/project/matomo-org/matomo-java-tracker "Percentage of issues still open") @@ -12,141 +10,99 @@ Matomo Java Tracker Official Java implementation of the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api). ## Javadoc - -The Javadoc for this project is hosted as a Github page for this repo. The latest Javadoc can be -found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/HEAD/index.html). Javadoc for the latest and all -releases can be found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/index.html). +The Javadoc for this project is hosted as a Github page for this repo. The latest Javadoc can be found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/HEAD/index.html). Javadoc for the latest and all releases can be found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/index.html). ## Using this API - ### Create a Request - -Each PiwikRequest represents an action the user has taken that you want tracked by your Piwik server. Create a -PiwikRequest through - +Each PiwikRequest represents an action the user has taken that you want tracked by your Piwik server. Create a PiwikRequest through ```java -PiwikRequest request=new PiwikRequest(siteId,actionUrl); +PiwikRequest request = new PiwikRequest(siteId, actionUrl); ``` The following parameters are also enabled by default: ```java -required=true; - visitorId=random 16character hex string; - randomValue=random 20character hex string; - apiVersion=1; - responseAsImage=false; +required = true; +visitorId = random 16 character hex string; +randomValue = random 20 character hex string; +apiVersion = 1; +responseAsImage = false; ``` Overwrite these properties as desired. -Note that if you want to be able to track campaigns using Referrers > Campaigns, you must add the correct -URL parameters to your actionUrl. For example, - +Note that if you want to be able to track campaigns using Referrers > Campaigns, you must add the correct URL parameters to your actionUrl. For example, ```java -URL actionUrl=new URL("http://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore"); +URL actionUrl = new URL("http://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore"); ``` - See [Tracking Campaigns](https://matomo.org/docs/tracking-campaigns/) for more information. -All HTTP query parameters denoted on -the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate -getters and setters. See PiwikRequest.java for the mappings of the parameters to their corresponding -Java getters/setters. +All HTTP query parameters denoted on the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate getters and setters. See PiwikRequest.java for the mappings of the parameters to their corresponding Java getters/setters. Some parameters are dependent on the state of other parameters: -EcommerceEnabled must be called before the following parameters are set: EcommerceId and -EcommerceRevenue. +EcommerceEnabled must be called before the following parameters are set: EcommerceId and EcommerceRevenue. -EcommerceId and EcommerceRevenue must be set before the following parameters are -set: EcommerceDiscount, EcommerceItem, EcommerceLastOrderTimestamp, -EcommerceShippingCost, EcommerceSubtotal, and EcommerceTax. +EcommerceId and EcommerceRevenue must be set before the following parameters are set: EcommerceDiscount, EcommerceItem, EcommerceLastOrderTimestamp, EcommerceShippingCost, EcommerceSubtotal, and EcommerceTax. -AuthToken must be set before the following parameters are set: VisitorCity, -VisitorCountry, VisitorIp, VisitorLatitude, VisitorLongitude, and VisitorRegion -. +AuthToken must be set before the following parameters are set: VisitorCity, VisitorCountry, VisitorIp, VisitorLatitude, VisitorLongitude, and VisitorRegion. ### Sending Requests - Create a PiwikTracker through - ```java -PiwikTracker tracker=new PiwikTracker(hostUrl); +PiwikTracker tracker = new PiwikTracker(hostUrl); ``` - -where hostUrl is the url endpoint of the Piwik server. Usually in the -format http://your-piwik-domain.tld/piwik.php. +where hostUrl is the url endpoint of the Piwik server. Usually in the format http://your-piwik-domain.tld/piwik.php. To send a single request, call - ```java -HttpResponse response=tracker.sendRequest(request); +HttpResponse response = tracker.sendRequest(request); ``` -If you have multiple requests to wish to track, it may be more efficient to send them in a single HTTP call. To do this, -send a bulk request. Place your requests in an Iterable data structure and call - +If you have multiple requests to wish to track, it may be more efficient to send them in a single HTTP call. To do this, send a bulk request. Place your requests in an Iterable data structure and call ```java -HttpResponse response=tracker.sendBulkRequest(requests); +HttpResponse response = tracker.sendBulkRequest(requests); ``` - -If some of the parameters that you've specified in the bulk request require AuthToken to be set, this can also be set in -the bulk request through - +If some of the parameters that you've specified in the bulk request require AuthToken to be set, this can also be set in the bulk request through ```java -HttpResponse response=tracker.sendBulkRequest(requests,authToken); +HttpResponse response = tracker.sendBulkRequest(requests, authToken); ``` - ## Install - This project can be tested and built by calling - ```shell mvn package ``` - The built jars and javadoc can be found in target Test this project using - ```shell mvn test ``` -This project also supports [Pitest](http://pitest.org/) mutation testing. This report can be generated by calling - +This project also supports [Pitest](http://pitest.org/) mutation testing. This report can be generated by calling ```shell mvn org.pitest:pitest-maven:mutationCoverage ``` - -and will produce an html report at target/pit-reports/YYYYMMDDHHMI +and will produce an HTML report at target/pit-reports/YYYYMMDDHHMI Clean this project using - ```shell mvn clean ``` ## Contribute - -Have a fantastic feature idea? Spot a bug? We would absolutely love for you to contribute to this project! Please feel -free to: +Have a fantastic feature idea? Spot a bug? We would absolutely love for you to contribute to this project! Please feel free to: * Fork this project * Create a feature branch from the dev branch * Write awesome code that does awesome things * Write awesome test to test your awesome code -* Verify that everything is working as it should by running mvn test. If everything passes, you may - want to make sure that your tests are covering everything you think they are! Run mvn org.pitest: - pitest-maven:mutationCoverage to find out! +* Verify that everything is working as it should by running mvn test. If everything passes, you may want to make sure that your tests are covering everything you think they are! Run mvn org.pitest:pitest-maven:mutationCoverage to find out! * Commit this code to your repository * Submit a pull request from your branch to our dev branch and let us know why you made the changes you did * We'll take a look at your request and work to get it integrated with the repo! ## License - This software is released under the BSD 3-Clause license. See [LICENSE](LICENSE). ## Copyright - -Copyright (c) 2015 General Electric Company. All rights reserved. +Copyright (c) 2015 General Electric Company. All rights reserved. \ No newline at end of file From de0c78e80b5bfe817753f91e7a6183eddf5d8230 Mon Sep 17 00:00:00 2001 From: Thomas Lutz Date: Mon, 5 Apr 2021 11:46:58 +0200 Subject: [PATCH 028/467] Update dependencies: junit, mockito --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0aa7c2b3..858dd87b 100644 --- a/pom.xml +++ b/pom.xml @@ -170,13 +170,13 @@ org.mockito mockito-core - 3.6.28 + 3.8.0 test junit junit - 4.13.1 + 4.13.2 test From 72b926fe2c8bf5fb0d9c36cacdefdb0761da57cd Mon Sep 17 00:00:00 2001 From: Thomas Lutz Date: Mon, 5 Apr 2021 12:06:25 +0200 Subject: [PATCH 029/467] Prepare 1.6 Release: Remove -SNAPSHOT from version in pom.xml --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 858dd87b..ca79cca5 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker - 1.6-SNAPSHOT + 1.6 jar Matomo Java Tracker From 2e384aa7ac0863750ac7b4a66f68b6cfc7281711 Mon Sep 17 00:00:00 2001 From: Thomas Lutz Date: Mon, 5 Apr 2021 12:12:45 +0200 Subject: [PATCH 030/467] Bump version to 1.7-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ca79cca5..5ee8dce6 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker - 1.6 + 1.7-SNAPSHOT jar Matomo Java Tracker From 24f8d3712056f3d3b0b5dde21feba455e2484810 Mon Sep 17 00:00:00 2001 From: Norbert Becker Date: Tue, 4 May 2021 17:26:50 +0200 Subject: [PATCH 031/467] Add Eclipse project files to .gitignore --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index bdf8136c..74c79135 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,8 @@ hs_err_pid* # IDEA files *.iml -.idea \ No newline at end of file +.idea + +# Eclipse files +.classpath +.project From 3e8323a2dfc157780a6548a13a6d1b5730662c53 Mon Sep 17 00:00:00 2001 From: Norbert Becker Date: Tue, 4 May 2021 17:27:32 +0200 Subject: [PATCH 032/467] Redesign PiwikTracker for using AsyncHttpClient singletons --- .../java/tracking/HttpClientFactory.java | 104 ++++++++++++++++ .../org/piwik/java/tracking/PiwikTracker.java | 111 ++++++------------ 2 files changed, 137 insertions(+), 78 deletions(-) create mode 100644 src/main/java/org/piwik/java/tracking/HttpClientFactory.java diff --git a/src/main/java/org/piwik/java/tracking/HttpClientFactory.java b/src/main/java/org/piwik/java/tracking/HttpClientFactory.java new file mode 100644 index 00000000..73c5f196 --- /dev/null +++ b/src/main/java/org/piwik/java/tracking/HttpClientFactory.java @@ -0,0 +1,104 @@ +package org.piwik.java.tracking; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import org.apache.http.HttpHost; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.DefaultProxyRoutePlanner; +import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; +import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; + +/** + * Internal factory for providing instances of HTTP clients. + * Especially {@linkAsyncHttpClient} instances are intended to be global resources that share the same lifecycle as the application. + * For details see Apache documentation. + * + * @author norbertroamsys + */ +class HttpClientFactory { + + /** + * Internal key class for caching {@link CloseableHttpAsyncClient} instances. + */ + private static final class KeyEntry { + + private final String proxyHost; + private final int proxyPort, timeout; + + public KeyEntry(final String proxyHost, final int proxyPort, final int timeout) { + this.proxyHost = proxyHost; + this.proxyPort = proxyPort; + this.timeout = timeout; + } + + @Override + public int hashCode() { + return Objects.hash(proxyHost, proxyPort, timeout); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } else if (obj == null) { + return false; + } else if (obj instanceof KeyEntry) { + final KeyEntry other = (KeyEntry) obj; + return Objects.equals(proxyHost, other.proxyHost) && proxyPort == other.proxyPort && timeout == other.timeout; + } else { + return false; + } + } + } + + private static final Map ASYNC_INSTANCES = new HashMap<>(); + + /** + * Factory for getting a synchronous client by proxy and timeout configuration. + * The clients will be created on each call. + * + * @param proxyHost the proxy host + * @param proxyPort the proxy port + * @param timeout the timeout + * @return the created client + */ + public static HttpClient getInstanceFor(final String proxyHost, final int proxyPort, final int timeout) { + return HttpClientBuilder.create().setRoutePlanner(createRoutePlanner(proxyHost, proxyPort)).setDefaultRequestConfig(createRequestConfig(timeout)).build(); + } + + /** + * Factory for getting a asynchronous client by proxy and timeout configuration. + * The clients will be created and cached as a singleton instance. + * + * @param proxyHost the proxy host + * @param proxyPort the proxy port + * @param timeout the timeout + * @return the created client + */ + public static synchronized CloseableHttpAsyncClient getAsyncInstanceFor(final String proxyHost, final int proxyPort, final int timeout) { + return ASYNC_INSTANCES.computeIfAbsent(new KeyEntry(proxyHost, proxyPort, timeout), key -> + HttpAsyncClientBuilder.create().setRoutePlanner(createRoutePlanner(key.proxyHost, key.proxyPort)).setDefaultRequestConfig(createRequestConfig(key.timeout)).build()); + } + + private static DefaultProxyRoutePlanner createRoutePlanner(final String proxyHost, final int proxyPort) { + if (proxyHost != null && proxyPort != 0) { + final HttpHost proxy = new HttpHost(proxyHost, proxyPort); + final DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy); + return routePlanner; + } else { + return null; + } + } + + private static RequestConfig createRequestConfig(final int timeout) { + final RequestConfig.Builder config = RequestConfig.custom() + .setConnectTimeout(timeout) + .setConnectionRequestTimeout(timeout) + .setSocketTimeout(timeout); + return config.build(); + } + +} diff --git a/src/main/java/org/piwik/java/tracking/PiwikTracker.java b/src/main/java/org/piwik/java/tracking/PiwikTracker.java index 7b892036..57857562 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikTracker.java +++ b/src/main/java/org/piwik/java/tracking/PiwikTracker.java @@ -6,27 +6,21 @@ */ package org.piwik.java.tracking; -import org.apache.http.HttpHost; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.concurrent.Future; +import javax.json.Json; +import javax.json.JsonArrayBuilder; +import javax.json.JsonObjectBuilder; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; -import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URIBuilder; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.DefaultProxyRoutePlanner; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; -import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; - -import javax.json.Json; -import javax.json.JsonArrayBuilder; -import javax.json.JsonObjectBuilder; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.concurrent.Future; /** * A class that sends {@link PiwikRequest}s to a specified Piwik server. @@ -41,7 +35,6 @@ public class PiwikTracker { private final int timeout; private final String proxyHost; private final int proxyPort; - private CloseableHttpAsyncClient asyncClient = null; /** * Creates a tracker that will send {@link PiwikRequest}s to the specified @@ -50,7 +43,7 @@ public class PiwikTracker { * @param hostUrl url endpoint to send requests to. Usually in the format * http://your-piwik-domain.tld/piwik.php. */ - public PiwikTracker(String hostUrl) { + public PiwikTracker(final String hostUrl) { this(hostUrl, DEFAULT_TIMEOUT); } @@ -62,7 +55,7 @@ public PiwikTracker(String hostUrl) { * http://your-piwik-domain.tld/piwik.php. * @param timeout the timeout of the sent request in milliseconds */ - public PiwikTracker(String hostUrl, int timeout) { + public PiwikTracker(final String hostUrl, final int timeout) { uriBuilder = new URIBuilder(URI.create(hostUrl)); this.timeout = timeout; this.proxyHost = null; @@ -78,11 +71,11 @@ public PiwikTracker(String hostUrl, int timeout) { * @param proxyHost url endpoint for the proxy * @param proxyPort proxy server port number */ - public PiwikTracker(String hostUrl, String proxyHost, int proxyPort) { + public PiwikTracker(final String hostUrl, final String proxyHost, final int proxyPort) { this(hostUrl, proxyHost, proxyPort, DEFAULT_TIMEOUT); } - public PiwikTracker(String hostUrl, String proxyHost, int proxyPort, int timeout) { + public PiwikTracker(final String hostUrl, final String proxyHost, final int proxyPort, final int timeout) { uriBuilder = new URIBuilder(URI.create(hostUrl)); this.proxyHost = proxyHost; this.proxyPort = proxyPort; @@ -98,15 +91,15 @@ public PiwikTracker(String hostUrl, String proxyHost, int proxyPort, int timeout * @deprecated use sendRequestAsync instead */ @Deprecated - public HttpResponse sendRequest(PiwikRequest request) throws IOException { - HttpClient client = getHttpClient(); + public HttpResponse sendRequest(final PiwikRequest request) throws IOException { + final HttpClient client = getHttpClient(); uriBuilder.setCustomQuery(request.getQueryString()); HttpGet get = null; try { get = new HttpGet(uriBuilder.build()); return client.execute(get); - } catch (URISyntaxException e) { + } catch (final URISyntaxException e) { throw new IOException(e); } } @@ -118,8 +111,8 @@ public HttpResponse sendRequest(PiwikRequest request) throws IOException { * @return future with response from this request * @throws IOException thrown if there was a problem with this connection */ - public Future sendRequestAsync(PiwikRequest request) throws IOException { - CloseableHttpAsyncClient client = getHttpAsyncClient(); + public Future sendRequestAsync(final PiwikRequest request) throws IOException { + final CloseableHttpAsyncClient client = getHttpAsyncClient(); client.start(); uriBuilder.setCustomQuery(request.getQueryString()); HttpGet get = null; @@ -127,7 +120,7 @@ public Future sendRequestAsync(PiwikRequest request) throws IOExce try { get = new HttpGet(uriBuilder.build()); return client.execute(get, null); - } catch (URISyntaxException e) { + } catch (final URISyntaxException e) { throw new IOException(e); } } @@ -142,7 +135,7 @@ public Future sendRequestAsync(PiwikRequest request) throws IOExce * @deprecated use sendBulkRequestAsync instead */ @Deprecated - public HttpResponse sendBulkRequest(Iterable requests) throws IOException { + public HttpResponse sendBulkRequest(final Iterable requests) throws IOException { return sendBulkRequest(requests, null); } @@ -154,7 +147,7 @@ public HttpResponse sendBulkRequest(Iterable requests) throws IOEx * @return future with response from these requests * @throws IOException thrown if there was a problem with this connection */ - public Future sendBulkRequestAsync(Iterable requests) throws IOException { + public Future sendBulkRequestAsync(final Iterable requests) throws IOException { return sendBulkRequestAsync(requests, null); } @@ -170,15 +163,15 @@ public Future sendBulkRequestAsync(Iterable requests * @deprecated use sendBulkRequestAsync instead */ @Deprecated - public HttpResponse sendBulkRequest(Iterable requests, String authToken) throws IOException { + public HttpResponse sendBulkRequest(final Iterable requests, final String authToken) throws IOException { if (authToken != null && authToken.length() != PiwikRequest.AUTH_TOKEN_LENGTH) { throw new IllegalArgumentException(authToken + " is not " + PiwikRequest.AUTH_TOKEN_LENGTH + " characters long."); } - JsonObjectBuilder ob = Json.createObjectBuilder(); - JsonArrayBuilder ab = Json.createArrayBuilder(); + final JsonObjectBuilder ob = Json.createObjectBuilder(); + final JsonArrayBuilder ab = Json.createArrayBuilder(); - for (PiwikRequest request : requests) { + for (final PiwikRequest request : requests) { ab.add("?" + request.getQueryString()); } @@ -188,7 +181,7 @@ public HttpResponse sendBulkRequest(Iterable requests, String auth ob.add(AUTH_TOKEN, authToken); } - HttpClient client = getHttpClient(); + final HttpClient client = getHttpClient(); HttpPost post = null; try { @@ -196,7 +189,7 @@ public HttpResponse sendBulkRequest(Iterable requests, String auth post.setEntity(new StringEntity(ob.build().toString(), ContentType.APPLICATION_JSON)); return client.execute(post); - } catch (URISyntaxException e) { + } catch (final URISyntaxException e) { throw new IOException(e); } } @@ -211,15 +204,15 @@ public HttpResponse sendBulkRequest(Iterable requests, String auth * @return the response from these requests * @throws IOException thrown if there was a problem with this connection */ - public Future sendBulkRequestAsync(Iterable requests, String authToken) throws IOException { + public Future sendBulkRequestAsync(final Iterable requests, final String authToken) throws IOException { if (authToken != null && authToken.length() != PiwikRequest.AUTH_TOKEN_LENGTH) { throw new IllegalArgumentException(authToken + " is not " + PiwikRequest.AUTH_TOKEN_LENGTH + " characters long."); } - JsonObjectBuilder ob = Json.createObjectBuilder(); - JsonArrayBuilder ab = Json.createArrayBuilder(); + final JsonObjectBuilder ob = Json.createObjectBuilder(); + final JsonArrayBuilder ab = Json.createArrayBuilder(); - for (PiwikRequest request : requests) { + for (final PiwikRequest request : requests) { ab.add("?" + request.getQueryString()); } @@ -229,7 +222,7 @@ public Future sendBulkRequestAsync(Iterable requests ob.add(AUTH_TOKEN, authToken); } - CloseableHttpAsyncClient client = getHttpAsyncClient(); + final CloseableHttpAsyncClient client = getHttpAsyncClient(); client.start(); HttpPost post = null; @@ -238,7 +231,7 @@ public Future sendBulkRequestAsync(Iterable requests post.setEntity(new StringEntity(ob.build().toString(), ContentType.APPLICATION_JSON)); return client.execute(post, null); - } catch (URISyntaxException e) { + } catch (final URISyntaxException e) { throw new IOException(e); } } @@ -249,23 +242,7 @@ public Future sendBulkRequestAsync(Iterable requests * @return a HTTP client */ protected HttpClient getHttpClient() { - - HttpClientBuilder builder = HttpClientBuilder.create(); - - if (proxyHost != null && proxyPort != 0) { - HttpHost proxy = new HttpHost(proxyHost, proxyPort); - DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy); - builder.setRoutePlanner(routePlanner); - } - - RequestConfig.Builder config = RequestConfig.custom() - .setConnectTimeout(timeout) - .setConnectionRequestTimeout(timeout) - .setSocketTimeout(timeout); - - builder.setDefaultRequestConfig(config.build()); - - return builder.build(); + return HttpClientFactory.getInstanceFor(proxyHost, proxyPort, timeout); } /** @@ -274,28 +251,6 @@ protected HttpClient getHttpClient() { * @return an async HTTP client */ protected CloseableHttpAsyncClient getHttpAsyncClient() { - - if (asyncClient != null) { - return asyncClient; - } - - HttpAsyncClientBuilder builder = HttpAsyncClientBuilder.create(); - - if (proxyHost != null && proxyPort != 0) { - HttpHost proxy = new HttpHost(proxyHost, proxyPort); - DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy); - builder.setRoutePlanner(routePlanner); - } - - RequestConfig.Builder config = RequestConfig.custom() - .setConnectTimeout(timeout) - .setConnectionRequestTimeout(timeout) - .setSocketTimeout(timeout); - - builder.setDefaultRequestConfig(config.build()); - - asyncClient = builder.build(); - - return asyncClient; + return HttpClientFactory.getAsyncInstanceFor(proxyHost, proxyPort, timeout); } } From 11e0e578df9bb7f721a23d786a5faff7f12e408e Mon Sep 17 00:00:00 2001 From: Thomas Steur Date: Thu, 17 Jun 2021 09:56:50 +1200 Subject: [PATCH 033/467] Overwrite global PR template Otherwise it would be using this PR template : https://github.com/matomo-org/.github/blob/main/.github/pull_request_template.md --- .github/pull_request_template.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1 @@ + From 35a34d80b265421235a695e420ed4b4dbc1eed2f Mon Sep 17 00:00:00 2001 From: Thomas Lutz Date: Thu, 24 Jun 2021 16:58:48 +0200 Subject: [PATCH 034/467] Prepare 1.7 Release: Remove -SNAPSHOT from version in pom.xml --- README.md | 2 +- pom.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3e0b8d19..b916ff05 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Create a PiwikTracker through ```java PiwikTracker tracker = new PiwikTracker(hostUrl); ``` -where hostUrl is the url endpoint of the Piwik server. Usually in the format http://your-piwik-domain.tld/piwik.php. +where hostUrl is the url endpoint of the Piwik server. Usually in the format http://your-piwik-domain.tld/piwik.php. To send a single request, call ```java diff --git a/pom.xml b/pom.xml index 5ee8dce6..ce90f8ef 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker - 1.7-SNAPSHOT + 1.7 jar Matomo Java Tracker @@ -170,7 +170,7 @@ org.mockito mockito-core - 3.8.0 + 3.11.2 test From 537e8672643070252658dcd1fb3b04afc2254580 Mon Sep 17 00:00:00 2001 From: Samuli Saarinen Date: Mon, 16 Aug 2021 10:51:54 +0300 Subject: [PATCH 035/467] Support FutureCallbacks in async tracking requests Resolves #55 --- README.md | 2 +- .../org/piwik/java/tracking/PiwikTracker.java | 47 +++++++++++++- .../piwik/java/tracking/PiwikTrackerTest.java | 63 +++++++++++++++++-- 3 files changed, 102 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index b916ff05..ec5b610c 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ mvn clean Have a fantastic feature idea? Spot a bug? We would absolutely love for you to contribute to this project! Please feel free to: * Fork this project -* Create a feature branch from the dev branch +* Create a feature branch from the master branch * Write awesome code that does awesome things * Write awesome test to test your awesome code * Verify that everything is working as it should by running mvn test. If everything passes, you may want to make sure that your tests are covering everything you think they are! Run mvn org.pitest:pitest-maven:mutationCoverage to find out! diff --git a/src/main/java/org/piwik/java/tracking/PiwikTracker.java b/src/main/java/org/piwik/java/tracking/PiwikTracker.java index 57857562..a0ec1201 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikTracker.java +++ b/src/main/java/org/piwik/java/tracking/PiwikTracker.java @@ -18,6 +18,7 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URIBuilder; +import org.apache.http.concurrent.FutureCallback; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; @@ -112,6 +113,17 @@ public HttpResponse sendRequest(final PiwikRequest request) throws IOException { * @throws IOException thrown if there was a problem with this connection */ public Future sendRequestAsync(final PiwikRequest request) throws IOException { + return sendRequestAsync(request, null); + } + /** + * Send a request. + * + * @param request request to send + * @param callback callback that gets executed when response arrives + * @return future with response from this request + * @throws IOException thrown if there was a problem with this connection + */ + public Future sendRequestAsync(final PiwikRequest request, FutureCallback callback) throws IOException { final CloseableHttpAsyncClient client = getHttpAsyncClient(); client.start(); uriBuilder.setCustomQuery(request.getQueryString()); @@ -119,7 +131,7 @@ public Future sendRequestAsync(final PiwikRequest request) throws try { get = new HttpGet(uriBuilder.build()); - return client.execute(get, null); + return client.execute(get, callback); } catch (final URISyntaxException e) { throw new IOException(e); } @@ -148,7 +160,20 @@ public HttpResponse sendBulkRequest(final Iterable requests) throw * @throws IOException thrown if there was a problem with this connection */ public Future sendBulkRequestAsync(final Iterable requests) throws IOException { - return sendBulkRequestAsync(requests, null); + return sendBulkRequestAsync(requests, null, null); + } + + /** + * Send multiple requests in a single HTTP call. More efficient than sending + * several individual requests. + * + * @param requests the requests to send + * @param callback callback that gets executed when response arrives + * @return future with response from these requests + * @throws IOException thrown if there was a problem with this connection + */ + public Future sendBulkRequestAsync(final Iterable requests, FutureCallback callback) throws IOException { + return sendBulkRequestAsync(requests, null, callback); } /** @@ -205,6 +230,22 @@ public HttpResponse sendBulkRequest(final Iterable requests, final * @throws IOException thrown if there was a problem with this connection */ public Future sendBulkRequestAsync(final Iterable requests, final String authToken) throws IOException { + return sendBulkRequestAsync(requests, authToken, null); + } + + /** + * Send multiple requests in a single HTTP call. More efficient than sending + * several individual requests. Specify the AuthToken if parameters that require + * an auth token is used. + * + * @param requests the requests to send + * @param authToken specify if any of the parameters use require AuthToken + * @param callback callback that gets executed when response arrives + * @return the response from these requests + * @throws IOException thrown if there was a problem with this connection + */ + public Future sendBulkRequestAsync(final Iterable requests, final String authToken, + FutureCallback callback) throws IOException { if (authToken != null && authToken.length() != PiwikRequest.AUTH_TOKEN_LENGTH) { throw new IllegalArgumentException(authToken + " is not " + PiwikRequest.AUTH_TOKEN_LENGTH + " characters long."); } @@ -230,7 +271,7 @@ public Future sendBulkRequestAsync(final Iterable re post = new HttpPost(uriBuilder.build()); post.setEntity(new StringEntity(ob.build().toString(), ContentType.APPLICATION_JSON)); - return client.execute(post, null); + return client.execute(post, callback); } catch (final URISyntaxException e) { throw new IOException(e); } diff --git a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java b/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java index e812fe20..0cdaa39b 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java +++ b/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java @@ -13,6 +13,7 @@ import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; +import org.apache.http.concurrent.FutureCallback; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.util.EntityUtils; import org.junit.After; @@ -30,11 +31,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.concurrent.Future; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.doReturn; @@ -173,6 +173,57 @@ public void testWithLocalServerAsync() throws Exception { assertEquals("OK", msgBulk); } + /** + * Test async API with local server + */ + @Test + public void testWithLocalServerAsyncCallback() throws Exception { + CountDownLatch latch = new CountDownLatch(2); + BlockingQueue responses = new LinkedBlockingQueue<>(); + BlockingQueue exceptions = new LinkedBlockingQueue<>(); + AtomicInteger cancelled = new AtomicInteger(); + + FutureCallback cb = new FutureCallback() { + + @Override + public void completed(HttpResponse httpResponse) { + responses.add(httpResponse); + latch.countDown(); + } + + @Override + public void failed(Exception e) { + exceptions.add(e); + latch.countDown(); + } + + @Override + public void cancelled() { + cancelled.incrementAndGet(); + latch.countDown(); + + } + }; + + // one + PiwikRequest request = new PiwikRequest(3, new URL("http://test.com")); + Future respFuture = localTracker.sendRequestAsync(request, cb); + // bulk + List requests = Collections.singletonList(request); + Future bulkFuture = localTracker.sendBulkRequestAsync(requests, cb); + + assertTrue("Responses not received", latch.await(100, TimeUnit.MILLISECONDS)); + assertEquals("Not expecting cancelled responses", 0, cancelled.get()); + assertEquals("Not expecting exceptions", exceptions.size(), 0); + assertTrue("Single response future not done", respFuture.isDone()); + assertTrue("Bulk response future not done", bulkFuture.isDone()); + HttpResponse response = responses.poll(1, TimeUnit.MILLISECONDS); + assertEquals("OK", EntityUtils.toString(response.getEntity())); + + HttpResponse bulkResponse = responses.poll(1, TimeUnit.MILLISECONDS); + assertEquals("OK", EntityUtils.toString(bulkResponse.getEntity())); + } + static class CorrectGetRequest implements ArgumentMatcher { String url; @@ -262,7 +313,7 @@ public void testSendBulkRequestAsync_Iterable() throws Exception { doReturn(response).when(future).get(); doReturn(true).when(future).isDone(); - doReturn(future).when(piwikTracker).sendBulkRequestAsync(requests, null); + doReturn(future).when(piwikTracker).sendBulkRequestAsync(requests); assertEquals(response, piwikTracker.sendBulkRequestAsync(requests).get()); } @@ -304,7 +355,7 @@ public void testSendBulkRequestAsync_Iterable_StringFF() throws Exception { doReturn(future).when(client) .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"]}")), any()); - assertEquals(response, piwikTracker.sendBulkRequestAsync(requests, null).get()); + assertEquals(response, piwikTracker.sendBulkRequestAsync(requests).get()); } @Test From 14e80fcb8226f6d2443e77948186c4aaaaaa7b1e Mon Sep 17 00:00:00 2001 From: Thomas Lutz Date: Tue, 31 Aug 2021 14:19:20 +0200 Subject: [PATCH 036/467] Bump version to 1.8-SNAPSHOT, update mockito-core to 3.12.4 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ce90f8ef..af8e2527 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker - 1.7 + 1.8-SNAPSHOT jar Matomo Java Tracker @@ -170,7 +170,7 @@ org.mockito mockito-core - 3.11.2 + 3.12.4 test From 17e6c46a6d6836f61e59e30b423846c1f1ccc30e Mon Sep 17 00:00:00 2001 From: Stefan Giehl Date: Sun, 14 Nov 2021 17:25:52 +0100 Subject: [PATCH 037/467] Fixes typo in developerConnection property of pom.xml --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index af8e2527..449a31b5 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ scm:git@github.com:matomo-org/matomo-java-tracker.git - scm:git@github.com:pmatomo-org/matomo-java-tracker.git + scm:git@github.com:matomo-org/matomo-java-tracker.git git@github.com:matomo-org/matomo-java-tracker.git From c96d6300a8971f0a4c3c408a8d6ea0720bfd665c Mon Sep 17 00:00:00 2001 From: Starker3 Date: Fri, 8 Jul 2022 16:29:29 +1200 Subject: [PATCH 038/467] Update # FAQ Links --- src/main/java/org/piwik/java/tracking/PiwikRequest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/piwik/java/tracking/PiwikRequest.java b/src/main/java/org/piwik/java/tracking/PiwikRequest.java index 1a07283a..d1d32440 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikRequest.java +++ b/src/main/java/org/piwik/java/tracking/PiwikRequest.java @@ -145,7 +145,7 @@ public String getActionName() { /** * Set the title of the action being tracked. It is possible to - * use slashes / + * use slashes / * to set one or several categories for this action. * For example, Help / Feedback * will create the Action Feedback in the category Help. @@ -949,7 +949,7 @@ public String getHeaderAcceptLanguage() { /** * Set an override value for the Accept-Language HTTP header * field. This value is used to detect the visitor's country if - * GeoIP is not enabled. + * GeoIP is not enabled. * * @param acceptLangage the Accept-Language HTTP header to set. A null value will remove this parameter */ @@ -1317,7 +1317,7 @@ public PiwikDate getRequestDatetime() { * Set the datetime of the request (normally the current time is used). * This can be used to record visits and page views in the past. The datetime * must be sent in UTC timezone. Note: if you record data in the past, you will - * need to force Piwik to re-process + * need to force Piwik to re-process * reports for the past dates. If you set the Request Datetime to a datetime * older than four hours then Auth Token must be set. If you set * Request Datetime with a datetime in the last four hours then you From 27a14b2ae95944bd00d9df7734d35671394d9fb9 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 1 Aug 2022 15:45:38 +0200 Subject: [PATCH 039/467] Add Dependabot file --- .github/dependabot.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..04ceb6e8 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "weekly" From a43a4ccccdc8159c627fccb0b264096f91579eab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 13:46:11 +0000 Subject: [PATCH 040/467] Bump maven-compiler-plugin from 3.8.1 to 3.10.1 Bumps [maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.8.1 to 3.10.1. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.8.1...maven-compiler-plugin-3.10.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 449a31b5..e138a87e 100644 --- a/pom.xml +++ b/pom.xml @@ -57,7 +57,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.10.1 1.8 1.8 From 684d13437aac3df95749cd2722eaddd6196eba83 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 13:46:13 +0000 Subject: [PATCH 041/467] Bump nexus-staging-maven-plugin from 1.6.8 to 1.6.13 Bumps nexus-staging-maven-plugin from 1.6.8 to 1.6.13. --- updated-dependencies: - dependency-name: org.sonatype.plugins:nexus-staging-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 449a31b5..adce7c0b 100644 --- a/pom.xml +++ b/pom.xml @@ -140,7 +140,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.8 + 1.6.13 true ossrh From dfd8797982e5c2c9cc132312f36b8c2b66e3f4d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 13:46:17 +0000 Subject: [PATCH 042/467] Bump maven-gpg-plugin from 1.6 to 3.0.1 Bumps [maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 1.6 to 3.0.1. - [Release notes](https://github.com/apache/maven-gpg-plugin/releases) - [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-1.6...maven-gpg-plugin-3.0.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-gpg-plugin dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 449a31b5..dd141747 100644 --- a/pom.xml +++ b/pom.xml @@ -126,7 +126,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 + 3.0.1 sign-artifacts From 2cb3ee457089da252696309134e96abe60f089db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 13:46:22 +0000 Subject: [PATCH 043/467] Bump jacoco-maven-plugin from 0.8.6 to 0.8.8 Bumps [jacoco-maven-plugin](https://github.com/jacoco/jacoco) from 0.8.6 to 0.8.8. - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.6...v0.8.8) --- updated-dependencies: - dependency-name: org.jacoco:jacoco-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 449a31b5..6704feae 100644 --- a/pom.xml +++ b/pom.xml @@ -113,7 +113,7 @@ org.jacoco jacoco-maven-plugin - 0.8.6 + 0.8.8 prepare-agent From 81d3a04e648c4a8f0dfc3f42f313d4580a81e05f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 13:46:28 +0000 Subject: [PATCH 044/467] Bump maven-javadoc-plugin from 3.2.0 to 3.4.0 Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.2.0 to 3.4.0. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.2.0...maven-javadoc-plugin-3.4.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 449a31b5..fb611866 100644 --- a/pom.xml +++ b/pom.xml @@ -79,7 +79,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.2.0 + 3.4.0 From 37b950d43ecc6ee164e73a2a9d07f54905f24d22 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 13:46:29 +0000 Subject: [PATCH 045/467] Bump mockito-core from 3.12.4 to 4.6.1 Bumps [mockito-core](https://github.com/mockito/mockito) from 3.12.4 to 4.6.1. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.12.4...v4.6.1) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 449a31b5..5788ceee 100644 --- a/pom.xml +++ b/pom.xml @@ -170,7 +170,7 @@ org.mockito mockito-core - 3.12.4 + 4.6.1 test From 258ca5897ff5b2df1de77b7553e209b5967d9e11 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 13:47:48 +0000 Subject: [PATCH 046/467] Bump httpasyncclient from 4.1.4 to 4.1.5 Bumps httpasyncclient from 4.1.4 to 4.1.5. --- updated-dependencies: - dependency-name: org.apache.httpcomponents:httpasyncclient dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 449a31b5..43e01de0 100644 --- a/pom.xml +++ b/pom.xml @@ -160,7 +160,7 @@ org.apache.httpcomponents httpasyncclient - 4.1.4 + 4.1.5 org.glassfish From ef75cb1dced0afcef26f183cdd503a45a7c98ba8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 13:48:34 +0000 Subject: [PATCH 047/467] Bump jaxb-api from 2.3.0 to 2.3.1 Bumps [jaxb-api](https://github.com/javaee/jaxb-spec) from 2.3.0 to 2.3.1. - [Release notes](https://github.com/javaee/jaxb-spec/releases) - [Commits](https://github.com/javaee/jaxb-spec/compare/2.3.0...2.3.1) --- updated-dependencies: - dependency-name: javax.xml.bind:jaxb-api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 449a31b5..675d71b1 100644 --- a/pom.xml +++ b/pom.xml @@ -57,7 +57,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.10.1 1.8 1.8 @@ -79,7 +79,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.2.0 + 3.4.0 @@ -113,7 +113,7 @@ org.jacoco jacoco-maven-plugin - 0.8.6 + 0.8.8 prepare-agent @@ -126,7 +126,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 + 3.0.1 sign-artifacts @@ -140,7 +140,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.8 + 1.6.13 true ossrh @@ -160,7 +160,7 @@ org.apache.httpcomponents httpasyncclient - 4.1.4 + 4.1.5 org.glassfish @@ -170,7 +170,7 @@ org.mockito mockito-core - 3.12.4 + 4.6.1 test @@ -182,7 +182,7 @@ javax.xml.bind jaxb-api - 2.3.0 + 2.3.1
From fcc188d1a40899b6ee0b362c3174b128b1f17f3d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 13:48:39 +0000 Subject: [PATCH 048/467] Bump pitest-maven from 1.5.2 to 1.9.3 Bumps [pitest-maven](https://github.com/hcoles/pitest) from 1.5.2 to 1.9.3. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/pitest-parent-1.5.2...1.9.3) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 449a31b5..1b4de100 100644 --- a/pom.xml +++ b/pom.xml @@ -57,7 +57,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.10.1 1.8 1.8 @@ -79,7 +79,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.2.0 + 3.4.0 @@ -95,7 +95,7 @@ org.pitest pitest-maven - 1.5.2 + 1.9.3 org.piwik.java.tracking* @@ -113,7 +113,7 @@ org.jacoco jacoco-maven-plugin - 0.8.6 + 0.8.8 prepare-agent @@ -126,7 +126,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 + 3.0.1 sign-artifacts @@ -140,7 +140,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.8 + 1.6.13 true ossrh @@ -160,7 +160,7 @@ org.apache.httpcomponents httpasyncclient - 4.1.4 + 4.1.5 org.glassfish @@ -170,7 +170,7 @@ org.mockito mockito-core - 3.12.4 + 4.6.1 test From 4e211c6f9a82056d049698f7c016f6feb6669375 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 1 Aug 2022 15:57:21 +0200 Subject: [PATCH 049/467] Updates deprecated method in test --- src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java b/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java index 0cdaa39b..f31979cd 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java +++ b/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java @@ -36,7 +36,7 @@ import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Matchers.argThat; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; From 34fa424f1e772123200ca18416c78ebfd0591cbb Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 1 Aug 2022 17:11:50 +0200 Subject: [PATCH 050/467] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ec5b610c..952cedda 100644 --- a/README.md +++ b/README.md @@ -55,16 +55,16 @@ where hostUrl is the url endpoint of the Piwik server. Usually in the format ht To send a single request, call ```java -HttpResponse response = tracker.sendRequest(request); +Future response = tracker.sendRequestAsync(request); ``` If you have multiple requests to wish to track, it may be more efficient to send them in a single HTTP call. To do this, send a bulk request. Place your requests in an Iterable data structure and call ```java -HttpResponse response = tracker.sendBulkRequest(requests); +Future response = tracker.sendBulkRequestAsync(requests); ``` If some of the parameters that you've specified in the bulk request require AuthToken to be set, this can also be set in the bulk request through ```java -HttpResponse response = tracker.sendBulkRequest(requests, authToken); +Future response = tracker.sendBulkRequestAsync(requests, authToken); ``` ## Install This project can be tested and built by calling @@ -105,4 +105,4 @@ Have a fantastic feature idea? Spot a bug? We would absolutely love for you to This software is released under the BSD 3-Clause license. See [LICENSE](LICENSE). ## Copyright -Copyright (c) 2015 General Electric Company. All rights reserved. \ No newline at end of file +Copyright (c) 2015 General Electric Company. All rights reserved. From 3846c868e63c0a252f324eb172fab91a1153d75f Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 2 Aug 2022 14:39:01 +0200 Subject: [PATCH 051/467] Add request builder --- .editorconfig | 9 + README.md | 105 +- pom.xml | 34 +- .../matomo/java/tracking/CustomVariable.java | 35 + .../matomo/java/tracking/CustomVariables.java | 86 + .../matomo/java/tracking/EcommerceItem.java | 31 + .../matomo/java/tracking/EcommerceItems.java | 52 + .../java/tracking/HttpClientFactory.java | 89 + .../java/tracking/InvalidUrlException.java | 8 + .../matomo/java/tracking/MatomoBoolean.java | 29 + .../org/matomo/java/tracking/MatomoDate.java | 77 + .../matomo/java/tracking/MatomoLocale.java | 35 + .../matomo/java/tracking/MatomoRequest.java | 2102 ++++++++++++++++ .../java/tracking/MatomoRequestBuilder.java | 680 ++++++ .../matomo/java/tracking/MatomoTracker.java | 271 +++ .../matomo/java/tracking/QueryParameters.java | 32 + .../piwik/java/tracking/CustomVariable.java | 49 +- .../java/tracking/CustomVariableList.java | 97 - .../piwik/java/tracking/EcommerceItem.java | 164 +- .../java/tracking/HttpClientFactory.java | 104 - .../org/piwik/java/tracking/PiwikDate.java | 57 +- .../piwik/java/tracking/PiwikJsonArray.java | 72 - .../piwik/java/tracking/PiwikJsonObject.java | 117 - .../org/piwik/java/tracking/PiwikLocale.java | 50 +- .../org/piwik/java/tracking/PiwikRequest.java | 2110 +---------------- .../org/piwik/java/tracking/PiwikTracker.java | 285 +-- .../java/tracking/CustomVariableTest.java | 6 +- .../java/tracking/CustomVariablesTest.java | 85 + .../java/tracking/EcommerceItemTest.java | 27 +- .../java/tracking/EcommerceItemsTest.java | 18 + .../tracking/MatomoRequestBuilderTest.java | 48 + .../java/tracking/PiwikDateTest.java | 9 +- .../java/tracking/PiwikLocaleTest.java | 7 +- .../java/tracking/PiwikRequestTest.java | 214 +- .../java/tracking/PiwikTrackerTest.java | 51 +- .../java/tracking/CustomVariableListTest.java | 91 - .../java/tracking/PiwikJsonArrayTest.java | 73 - .../java/tracking/PiwikJsonObjectTest.java | 76 - 38 files changed, 3991 insertions(+), 3494 deletions(-) create mode 100644 .editorconfig create mode 100644 src/main/java/org/matomo/java/tracking/CustomVariable.java create mode 100644 src/main/java/org/matomo/java/tracking/CustomVariables.java create mode 100644 src/main/java/org/matomo/java/tracking/EcommerceItem.java create mode 100644 src/main/java/org/matomo/java/tracking/EcommerceItems.java create mode 100644 src/main/java/org/matomo/java/tracking/HttpClientFactory.java create mode 100644 src/main/java/org/matomo/java/tracking/InvalidUrlException.java create mode 100644 src/main/java/org/matomo/java/tracking/MatomoBoolean.java create mode 100644 src/main/java/org/matomo/java/tracking/MatomoDate.java create mode 100644 src/main/java/org/matomo/java/tracking/MatomoLocale.java create mode 100644 src/main/java/org/matomo/java/tracking/MatomoRequest.java create mode 100644 src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java create mode 100644 src/main/java/org/matomo/java/tracking/MatomoTracker.java create mode 100644 src/main/java/org/matomo/java/tracking/QueryParameters.java delete mode 100644 src/main/java/org/piwik/java/tracking/CustomVariableList.java delete mode 100644 src/main/java/org/piwik/java/tracking/HttpClientFactory.java delete mode 100644 src/main/java/org/piwik/java/tracking/PiwikJsonArray.java delete mode 100644 src/main/java/org/piwik/java/tracking/PiwikJsonObject.java rename src/test/java/org/{piwik => matomo}/java/tracking/CustomVariableTest.java (84%) create mode 100644 src/test/java/org/matomo/java/tracking/CustomVariablesTest.java rename src/test/java/org/{piwik => matomo}/java/tracking/EcommerceItemTest.java (75%) create mode 100644 src/test/java/org/matomo/java/tracking/EcommerceItemsTest.java create mode 100644 src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java rename src/test/java/org/{piwik => matomo}/java/tracking/PiwikDateTest.java (79%) rename src/test/java/org/{piwik => matomo}/java/tracking/PiwikLocaleTest.java (83%) rename src/test/java/org/{piwik => matomo}/java/tracking/PiwikRequestTest.java (82%) rename src/test/java/org/{piwik => matomo}/java/tracking/PiwikTrackerTest.java (86%) delete mode 100644 src/test/java/org/piwik/java/tracking/CustomVariableListTest.java delete mode 100644 src/test/java/org/piwik/java/tracking/PiwikJsonArrayTest.java delete mode 100644 src/test/java/org/piwik/java/tracking/PiwikJsonObjectTest.java diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..c6c8b362 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/README.md b/README.md index 952cedda..175813f9 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ -Matomo Java Tracker -================ +# Matomo Java Tracker [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/matomo-java-tracker/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/matomo-java-tracker) -## Code Status [![Build Status](https://travis-ci.org/matomo-org/matomo-java-tracker.svg?branch=master)](https://travis-ci.org/matomo-org/matomo-java-tracker) [![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/matomo-org/matomo-java-tracker.svg)](https://isitmaintained.com/project/matomo-org/matomo-java-tracker "Average time to resolve an issue") [![Percentage of issues still open](https://isitmaintained.com/badge/open/matomo-org/matomo-java-tracker.svg)](https://isitmaintained.com/project/matomo-org/matomo-java-tracker "Percentage of issues still open") @@ -10,99 +8,144 @@ Matomo Java Tracker Official Java implementation of the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api). ## Javadoc -The Javadoc for this project is hosted as a Github page for this repo. The latest Javadoc can be found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/HEAD/index.html). Javadoc for the latest and all releases can be found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/index.html). + +The Javadoc for this project is hosted as a Github page for this repo. The latest Javadoc can be +found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/HEAD/index.html). Javadoc for the latest and all +releases can be found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/index.html). ## Using this API + ### Create a Request -Each PiwikRequest represents an action the user has taken that you want tracked by your Piwik server. Create a PiwikRequest through -```java -PiwikRequest request = new PiwikRequest(siteId, actionUrl); -``` -The following parameters are also enabled by default: +Each MatomoRequest represents an action the user has taken that you want tracked by your Matomo server. Create a +MatomoRequest through ```java -required = true; -visitorId = random 16 character hex string; -randomValue = random 20 character hex string; -apiVersion = 1; -responseAsImage = false; +MatomoRequest request=MatomoRequest.builder() + .siteId(42) + .actionUrl("https://www.mydomain.com/some/site") + .actionName("Signup") + .build(); ``` +Per default every request has the following default parameters: + +| Parameter Name | Default Value | +|------------------|--------------------------------| +| required | true | +| visitorId | random 16 character hex string | +| randomValue | random 20 character hex string | +| apiVersion | 1 | +| responseAsImage | false | + Overwrite these properties as desired. -Note that if you want to be able to track campaigns using Referrers > Campaigns, you must add the correct URL parameters to your actionUrl. For example, +Note that if you want to be able to track campaigns using Referrers > Campaigns, you must add the correct +URL parameters to your actionUrl. For example, + ```java -URL actionUrl = new URL("http://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore"); +URL actionUrl=new URL("http://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore"); ``` + See [Tracking Campaigns](https://matomo.org/docs/tracking-campaigns/) for more information. -All HTTP query parameters denoted on the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate getters and setters. See PiwikRequest.java for the mappings of the parameters to their corresponding Java getters/setters. +All HTTP query parameters denoted on +the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate +getters and setters. See MatomoRequest.java for the mappings of the parameters to their corresponding +Java getters/setters. Some parameters are dependent on the state of other parameters: -EcommerceEnabled must be called before the following parameters are set: EcommerceId and EcommerceRevenue. +EcommerceEnabled must be called before the following parameters are set: EcommerceId and +EcommerceRevenue. -EcommerceId and EcommerceRevenue must be set before the following parameters are set: EcommerceDiscount, EcommerceItem, EcommerceLastOrderTimestamp, EcommerceShippingCost, EcommerceSubtotal, and EcommerceTax. +EcommerceId and EcommerceRevenue must be set before the following parameters are +set: EcommerceDiscount, EcommerceItem, EcommerceLastOrderTimestamp, +EcommerceShippingCost, EcommerceSubtotal, and EcommerceTax. -AuthToken must be set before the following parameters are set: VisitorCity, VisitorCountry, VisitorIp, VisitorLatitude, VisitorLongitude, and VisitorRegion. +AuthToken must be set before the following parameters are set: VisitorCity, +VisitorCountry, VisitorIp, VisitorLatitude, VisitorLongitude, and VisitorRegion +. ### Sending Requests -Create a PiwikTracker through + +Create a MatomoTracker through + ```java -PiwikTracker tracker = new PiwikTracker(hostUrl); +MatomoTracker tracker=new MatomoTracker(hostUrl); ``` -where hostUrl is the url endpoint of the Piwik server. Usually in the format http://your-piwik-domain.tld/piwik.php. + +where hostUrl is the url endpoint of the Matomo server. Usually in the format http://your-matomo-domain.tld/matomo.php. To send a single request, call + ```java -Future response = tracker.sendRequestAsync(request); +Future response=tracker.sendRequestAsync(request); ``` -If you have multiple requests to wish to track, it may be more efficient to send them in a single HTTP call. To do this, send a bulk request. Place your requests in an Iterable data structure and call +If you have multiple requests to wish to track, it may be more efficient to send them in a single HTTP call. To do this, +send a bulk request. Place your requests in an Iterable data structure and call + ```java -Future response = tracker.sendBulkRequestAsync(requests); +Future response=tracker.sendBulkRequestAsync(requests); ``` -If some of the parameters that you've specified in the bulk request require AuthToken to be set, this can also be set in the bulk request through + +If some of the parameters that you've specified in the bulk request require AuthToken to be set, this can also be set in +the bulk request through + ```java -Future response = tracker.sendBulkRequestAsync(requests, authToken); +Future response=tracker.sendBulkRequestAsync(requests,authToken); ``` + ## Install + This project can be tested and built by calling + ```shell mvn package ``` + The built jars and javadoc can be found in target Test this project using + ```shell mvn test ``` -This project also supports [Pitest](http://pitest.org/) mutation testing. This report can be generated by calling +This project also supports [Pitest](http://pitest.org/) mutation testing. This report can be generated by calling + ```shell mvn org.pitest:pitest-maven:mutationCoverage ``` + and will produce an HTML report at target/pit-reports/YYYYMMDDHHMI Clean this project using + ```shell mvn clean ``` ## Contribute -Have a fantastic feature idea? Spot a bug? We would absolutely love for you to contribute to this project! Please feel free to: + +Have a fantastic feature idea? Spot a bug? We would absolutely love for you to contribute to this project! Please feel +free to: * Fork this project * Create a feature branch from the master branch * Write awesome code that does awesome things * Write awesome test to test your awesome code -* Verify that everything is working as it should by running mvn test. If everything passes, you may want to make sure that your tests are covering everything you think they are! Run mvn org.pitest:pitest-maven:mutationCoverage to find out! +* Verify that everything is working as it should by running mvn test. If everything passes, you may + want to make sure that your tests are covering everything you think they are! Run mvn org.pitest: + pitest-maven:mutationCoverage to find out! * Commit this code to your repository * Submit a pull request from your branch to our dev branch and let us know why you made the changes you did * We'll take a look at your request and work to get it integrated with the repo! ## License + This software is released under the BSD 3-Clause license. See [LICENSE](LICENSE). ## Copyright + Copyright (c) 2015 General Electric Company. All rights reserved. diff --git a/pom.xml b/pom.xml index bcdb4b66..c32db240 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker - 1.8-SNAPSHOT + 2.0-SNAPSHOT jar Matomo Java Tracker @@ -98,10 +98,10 @@ 1.9.3 - org.piwik.java.tracking* + org.matomo.java.tracking* - org.piwik.java.tracking* + org.matomo.java.tracking* @@ -152,20 +152,25 @@ - - org.apache.httpcomponents - httpclient - 4.5.13 - org.apache.httpcomponents httpasyncclient 4.1.5 - org.glassfish - javax.json - 1.1.4 + com.google.guava + guava + 31.1-jre + + + com.fasterxml.jackson.core + jackson-databind + 2.13.3 + + + org.slf4j + slf4j-api + 1.7.36 org.mockito @@ -180,9 +185,10 @@ test - javax.xml.bind - jaxb-api - 2.3.1 + org.projectlombok + lombok + 1.18.24 + provided diff --git a/src/main/java/org/matomo/java/tracking/CustomVariable.java b/src/main/java/org/matomo/java/tracking/CustomVariable.java new file mode 100644 index 00000000..2d52e4d4 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/CustomVariable.java @@ -0,0 +1,35 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ +package org.matomo.java.tracking; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NonNull; +import lombok.ToString; +import lombok.experimental.FieldDefaults; + +/** + * A user defined custom variable. + * + * @author brettcsorba + */ +@Getter +@FieldDefaults(level = AccessLevel.PRIVATE) +@AllArgsConstructor +@ToString +@EqualsAndHashCode +public class CustomVariable { + + @NonNull + String key; + + @NonNull + String value; + +} diff --git a/src/main/java/org/matomo/java/tracking/CustomVariables.java b/src/main/java/org/matomo/java/tracking/CustomVariables.java new file mode 100644 index 00000000..0bbe94df --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/CustomVariables.java @@ -0,0 +1,86 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ +package org.matomo.java.tracking; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.EqualsAndHashCode; +import lombok.NonNull; + +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; + +/** + * @author brettcsorba + */ +@EqualsAndHashCode +class CustomVariables { + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private final Map variables = new HashMap<>(); + + void add(@NonNull CustomVariable variable) { + boolean found = false; + for (Map.Entry entry : variables.entrySet()) { + CustomVariable customVariable = entry.getValue(); + if (customVariable.getKey().equals(variable.getKey())) { + variables.put(entry.getKey(), variable); + found = true; + } + } + if (!found) { + int i = 1; + while (variables.putIfAbsent(i, variable) != null) { + i++; + } + } + } + + void add(@NonNull CustomVariable cv, int index) { + if (index <= 0) { + throw new IllegalArgumentException("Index must be greater than 0."); + } + variables.put(index, cv); + } + + @Nullable + CustomVariable get(int index) { + if (index <= 0) { + throw new IllegalArgumentException("Index must be greater than 0."); + } + return variables.get(index); + } + + @Nullable + String get(@NonNull String key) { + return variables.values().stream().filter(variable -> variable.getKey().equals(key)).findFirst().map(CustomVariable::getValue).orElse(null); + } + + void remove(int index) { + variables.remove(index); + } + + void remove(@NonNull String key) { + variables.entrySet().removeIf(entry -> entry.getValue().getKey().equals(key)); + } + + boolean isEmpty() { + return variables.isEmpty(); + } + + @Override + public String toString() { + ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); + for (Map.Entry entry : variables.entrySet()) { + CustomVariable variable = entry.getValue(); + objectNode.putArray(entry.getKey().toString()).add(variable.getKey()).add(variable.getValue()); + } + return objectNode.toString(); + } +} diff --git a/src/main/java/org/matomo/java/tracking/EcommerceItem.java b/src/main/java/org/matomo/java/tracking/EcommerceItem.java new file mode 100644 index 00000000..f7b5bf23 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/EcommerceItem.java @@ -0,0 +1,31 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ +package org.matomo.java.tracking; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +/** + * Represents an item in an ecommerce order. + * + * @author brettcsorba + */ +@Setter +@Getter +@AllArgsConstructor +@Builder +public class EcommerceItem { + + private String sku; + private String name; + private String category; + private Double price; + private Integer quantity; + +} diff --git a/src/main/java/org/matomo/java/tracking/EcommerceItems.java b/src/main/java/org/matomo/java/tracking/EcommerceItems.java new file mode 100644 index 00000000..9f435535 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/EcommerceItems.java @@ -0,0 +1,52 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ +package org.matomo.java.tracking; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import lombok.EqualsAndHashCode; +import lombok.NonNull; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.List; + +@EqualsAndHashCode +class EcommerceItems { + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private final List ecommerceItems = new ArrayList<>(); + + public void add(@NonNull EcommerceItem ecommerceItem) { + ecommerceItems.add(ecommerceItem); + } + + @Nonnull + public EcommerceItem get(int index) { + return ecommerceItems.get(index); + } + + public EcommerceItem set(int index, EcommerceItem element) { + return ecommerceItems.set(index, element); + } + + @Override + public String toString() { + ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode(); + for (EcommerceItem ecommerceItem : ecommerceItems) { + arrayNode.add(OBJECT_MAPPER.createArrayNode() + .add(ecommerceItem.getSku()) + .add(ecommerceItem.getName()) + .add(ecommerceItem.getCategory()) + .add(ecommerceItem.getPrice()) + .add(ecommerceItem.getQuantity()) + ); + } + return arrayNode.toString(); + } +} diff --git a/src/main/java/org/matomo/java/tracking/HttpClientFactory.java b/src/main/java/org/matomo/java/tracking/HttpClientFactory.java new file mode 100644 index 00000000..1ae896d8 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/HttpClientFactory.java @@ -0,0 +1,89 @@ +package org.matomo.java.tracking; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import org.apache.http.HttpHost; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.DefaultProxyRoutePlanner; +import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; +import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; + +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; + +/** + * Internal factory for providing instances of HTTP clients. + * Especially {@linkAsyncHttpClient} instances are intended to be global resources that share the same lifecycle as the application. + * For details see Apache documentation. + * + * @author norbertroamsys + */ +final class HttpClientFactory { + + private HttpClientFactory() { + // utility + } + + /** + * Internal key class for caching {@link CloseableHttpAsyncClient} instances. + */ + @EqualsAndHashCode + @AllArgsConstructor + private static final class KeyEntry { + + private final String proxyHost; + private final int proxyPort; + private final int timeout; + + } + + private static final Map ASYNC_INSTANCES = new HashMap<>(); + + /** + * Factory for getting a synchronous client by proxy and timeout configuration. + * The clients will be created on each call. + * + * @param proxyHost the proxy host + * @param proxyPort the proxy port + * @param timeout the timeout + * @return the created client + */ + public static HttpClient getInstanceFor(final String proxyHost, final int proxyPort, final int timeout) { + return HttpClientBuilder.create().setRoutePlanner(createRoutePlanner(proxyHost, proxyPort)).setDefaultRequestConfig(createRequestConfig(timeout)).build(); + } + + /** + * Factory for getting a asynchronous client by proxy and timeout configuration. + * The clients will be created and cached as a singleton instance. + * + * @param proxyHost the proxy host + * @param proxyPort the proxy port + * @param timeout the timeout + * @return the created client + */ + public static synchronized CloseableHttpAsyncClient getAsyncInstanceFor(final String proxyHost, final int proxyPort, final int timeout) { + return ASYNC_INSTANCES.computeIfAbsent(new KeyEntry(proxyHost, proxyPort, timeout), key -> + HttpAsyncClientBuilder.create().setRoutePlanner(createRoutePlanner(key.proxyHost, key.proxyPort)).setDefaultRequestConfig(createRequestConfig(key.timeout)).build()); + } + + @Nullable + private static DefaultProxyRoutePlanner createRoutePlanner(final String proxyHost, final int proxyPort) { + if (proxyHost != null && proxyPort != 0) { + final HttpHost proxy = new HttpHost(proxyHost, proxyPort); + return new DefaultProxyRoutePlanner(proxy); + } + return null; + } + + private static RequestConfig createRequestConfig(final int timeout) { + final RequestConfig.Builder config = RequestConfig.custom() + .setConnectTimeout(timeout) + .setConnectionRequestTimeout(timeout) + .setSocketTimeout(timeout); + return config.build(); + } + +} diff --git a/src/main/java/org/matomo/java/tracking/InvalidUrlException.java b/src/main/java/org/matomo/java/tracking/InvalidUrlException.java new file mode 100644 index 00000000..01d4fbcb --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/InvalidUrlException.java @@ -0,0 +1,8 @@ +package org.matomo.java.tracking; + +public class InvalidUrlException extends RuntimeException { + + public InvalidUrlException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/org/matomo/java/tracking/MatomoBoolean.java b/src/main/java/org/matomo/java/tracking/MatomoBoolean.java new file mode 100644 index 00000000..faa3ee92 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/MatomoBoolean.java @@ -0,0 +1,29 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ +package org.matomo.java.tracking; + +import lombok.Value; + +/** + * Object representing a locale required by some Matomo query parameters. + * + * @author brettcsorba + */ +@Value +public class MatomoBoolean { + boolean value; + + /** + * Returns the locale's lowercase country code. + * + * @return the locale's lowercase country code + */ + @Override + public String toString() { + return value ? "1" : "0"; + } +} diff --git a/src/main/java/org/matomo/java/tracking/MatomoDate.java b/src/main/java/org/matomo/java/tracking/MatomoDate.java new file mode 100644 index 00000000..25706998 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/MatomoDate.java @@ -0,0 +1,77 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ +package org.matomo.java.tracking; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + +/** + * A datetime object that will return the datetime in the format {@code yyyy-MM-dd hh:mm:ss}. + * + * @author brettcsorba + */ +public class MatomoDate { + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH); + + private ZonedDateTime zonedDateTime; + + /** + * Allocates a Date object and initializes it so that it represents the time + * at which it was allocated, measured to the nearest millisecond. + */ + public MatomoDate() { + zonedDateTime = ZonedDateTime.now(ZoneOffset.UTC); + } + + /** + * Allocates a Date object and initializes it to represent the specified number + * of milliseconds since the standard base time known as "the epoch", namely + * January 1, 1970, 00:00:00 GMT. + * + * @param epochMilli the milliseconds since January 1, 1970, 00:00:00 GMT. + */ + public MatomoDate(long epochMilli) { + zonedDateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(epochMilli), ZoneOffset.UTC); + } + + /** + * Sets the time zone of the String that will be returned by {@link #toString()}. + * Defaults to UTC. + * + * @param zone the TimeZone to set + */ + public void setTimeZone(ZoneId zone) { + zonedDateTime = zonedDateTime.withZoneSameInstant(zone); + } + + /** + * Converts this MatomoDate object to a String of the form:
+ *
+ * {@code yyyy-MM-dd hh:mm:ss}. + * + * @return a string representation of this MatomoDate + */ + @Override + public String toString() { + return DATE_TIME_FORMATTER.format(zonedDateTime); + } + + /** + * Converts this datetime to the number of milliseconds from the epoch + * of 1970-01-01T00:00:00Z. + * + * @return the number of milliseconds since the epoch of 1970-01-01T00:00:00Z + * @throws ArithmeticException if numeric overflow occurs + */ + public long getTime() { + return zonedDateTime.toInstant().toEpochMilli(); + } +} diff --git a/src/main/java/org/matomo/java/tracking/MatomoLocale.java b/src/main/java/org/matomo/java/tracking/MatomoLocale.java new file mode 100644 index 00000000..a561a17a --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/MatomoLocale.java @@ -0,0 +1,35 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ +package org.matomo.java.tracking; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +import java.util.Locale; + +/** + * Object representing a locale required by some Matomo query parameters. + * + * @author brettcsorba + */ +@Setter +@Getter +@AllArgsConstructor +public class MatomoLocale { + private Locale locale; + + /** + * Returns the locale's lowercase country code. + * + * @return the locale's lowercase country code + */ + @Override + public String toString() { + return locale.getCountry().toLowerCase(Locale.ENGLISH); + } +} diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/src/main/java/org/matomo/java/tracking/MatomoRequest.java new file mode 100644 index 00000000..2d73c0bb --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -0,0 +1,2102 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ +package org.matomo.java.tracking; + +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Multimap; +import com.google.common.io.BaseEncoding; +import lombok.NonNull; +import lombok.ToString; +import org.apache.http.client.utils.URIBuilder; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.Charset; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * A class that implements the + * Matomo Tracking HTTP API. These requests can be sent using {@link MatomoTracker}. + * + * @author brettcsorba + */ +@ToString +public class MatomoRequest { + public static final int ID_LENGTH = 16; + public static final int AUTH_TOKEN_LENGTH = 32; + + private static final String ACTION_NAME = "action_name"; + private static final String ACTION_TIME = "gt_ms"; + private static final String ACTION_URL = "url"; + private static final String API_VERSION = "apiv"; + private static final String AUTH_TOKEN = "token_auth"; + private static final String CAMPAIGN_KEYWORD = "_rck"; + private static final String CAMPAIGN_NAME = "_rcn"; + private static final String CHARACTER_SET = "cs"; + private static final String CONTENT_INTERACTION = "c_i"; + private static final String CONTENT_NAME = "c_n"; + private static final String CONTENT_PIECE = "c_p"; + private static final String CONTENT_TARGET = "c_t"; + private static final String CURRENT_HOUR = "h"; + private static final String CURRENT_MINUTE = "m"; + private static final String CURRENT_SECOND = "s"; + private static final String DEVICE_RESOLUTION = "res"; + private static final String DOWNLOAD_URL = "download"; + private static final String ECOMMERCE_DISCOUNT = "ec_dt"; + private static final String ECOMMERCE_ID = "ec_id"; + private static final String ECOMMERCE_ITEMS = "ec_items"; + private static final String ECOMMERCE_LAST_ORDER_TIMESTAMP = "_ects"; + private static final String ECOMMERCE_REVENUE = "revenue"; + private static final String ECOMMERCE_SHIPPING_COST = "ec_sh"; + private static final String ECOMMERCE_SUBTOTAL = "ec_st"; + private static final String ECOMMERCE_TAX = "ec_tx"; + private static final String EVENT_ACTION = "e_a"; + private static final String EVENT_CATEGORY = "e_c"; + private static final String EVENT_NAME = "e_n"; + private static final String EVENT_VALUE = "e_v"; + private static final String HEADER_ACCEPT_LANGUAGE = "lang"; + private static final String GOAL_ID = "idgoal"; + private static final String GOAL_REVENUE = "revenue"; + private static final String HEADER_USER_AGENT = "ua"; + private static final String NEW_VISIT = "new_visit"; + private static final String OUTLINK_URL = "link"; + private static final String PAGE_CUSTOM_VARIABLE = "cvar"; + private static final String PLUGIN_DIRECTOR = "dir"; + private static final String PLUGIN_FLASH = "fla"; + private static final String PLUGIN_GEARS = "gears"; + private static final String PLUGIN_JAVA = "java"; + private static final String PLUGIN_PDF = "pdf"; + private static final String PLUGIN_QUICKTIME = "qt"; + private static final String PLUGIN_REAL_PLAYER = "realp"; + private static final String PLUGIN_SILVERLIGHT = "ag"; + private static final String PLUGIN_WINDOWS_MEDIA = "wma"; + private static final String RANDOM_VALUE = "rand"; + private static final String REFERRER_URL = "urlref"; + private static final String REQUEST_DATETIME = "cdt"; + private static final String REQUIRED = "rec"; + private static final String RESPONSE_AS_IMAGE = "send_image"; + private static final String SEARCH_CATEGORY = "search_cat"; + private static final String SEARCH_QUERY = "search"; + private static final String SEARCH_RESULTS_COUNT = "search_count"; + private static final String SITE_ID = "idsite"; + private static final String TRACK_BOT_REQUESTS = "bots"; + private static final String VISIT_CUSTOM_VARIABLE = "_cvar"; + private static final String USER_ID = "uid"; + private static final String VISITOR_CITY = "city"; + private static final String VISITOR_COUNTRY = "country"; + private static final String VISITOR_CUSTOM_ID = "cid"; + private static final String VISITOR_FIRST_VISIT_TIMESTAMP = "_idts"; + private static final String VISITOR_ID = "_id"; + private static final String VISITOR_IP = "cip"; + private static final String VISITOR_LATITUDE = "lat"; + private static final String VISITOR_LONGITUDE = "long"; + private static final String VISITOR_PREVIOUS_VISIT_TIMESTAMP = "_viewts"; + private static final String VISITOR_REGION = "region"; + private static final String VISITOR_VISIT_COUNT = "_idvc"; + + private static final int RANDOM_VALUE_LENGTH = 20; + private static final long REQUEST_DATETIME_AUTH_LIMIT = 14400000L; + private static final Pattern VISITOR_ID_PATTERN = Pattern.compile("[0-9A-Fa-f]+"); + + private final Multimap parameters = LinkedHashMultimap.create(8, 1); + + private final Set customTrackingParameterNames = new HashSet<>(2); + + /** + * Create a new request from the id of the site being tracked and the full + * url for the current action. This constructor also sets: + *
+   * {@code
+   * Required = true
+   * Visior Id = random 16 character hex string
+   * Random Value = random 20 character hex string
+   * API version = 1
+   * Response as Image = false
+   * }
+   * 
+ * Overwrite these values yourself as desired. + * + * @param siteId the id of the website we're tracking a visit/action for + * @param actionUrl the full URL for the current action + */ + public MatomoRequest(int siteId, URL actionUrl) { + setParameter(SITE_ID, siteId); + setBooleanParameter(REQUIRED, true); + setParameter(ACTION_URL, actionUrl); + setParameter(VISITOR_ID, getRandomHexString(ID_LENGTH)); + setParameter(RANDOM_VALUE, getRandomHexString(RANDOM_VALUE_LENGTH)); + setParameter(API_VERSION, "1"); + setBooleanParameter(RESPONSE_AS_IMAGE, false); + } + + public static MatomoRequestBuilder builder() { + return new MatomoRequestBuilder(); + } + + /** + * Get the title of the action being tracked + * + * @return the title of the action being tracked + */ + @Nullable + public String getActionName() { + return castOrNull(ACTION_NAME); + } + + /** + * Set the title of the action being tracked. It is possible to + * use slashes / + * to set one or several categories for this action. + * For example, Help / Feedback + * will create the Action Feedback in the category Help. + * + * @param actionName the title of the action to set. A null value will remove this parameter + */ + public void setActionName(String actionName) { + setParameter(ACTION_NAME, actionName); + } + + /** + * Get the amount of time it took the server to generate this action, in milliseconds. + * + * @return the amount of time + */ + @Nullable + public Long getActionTime() { + return castOrNull(ACTION_TIME); + } + + /** + * Set the amount of time it took the server to generate this action, in milliseconds. + * This value is used to process the + * Page speed report + * Avg. generation time column in the Page URL and Page Title reports, + * as well as a site wide running average of the speed of your server. + * + * @param actionTime the amount of time to set. A null value will remove this parameter + */ + public void setActionTime(Long actionTime) { + setParameter(ACTION_TIME, actionTime); + } + + /** + * Get the full URL for the current action. + * + * @return the full URL + */ + @Nullable + public URL getActionUrl() { + return castOrNull(ACTION_URL); + } + + /** + * Get the full URL for the current action. + * + * @return the full URL + */ + @Nullable + public String getActionUrlAsString() { + return getUrlParameter(ACTION_URL); + } + + + /** + * Set the full URL for the current action. + * + * @param actionUrl the full URL to set. A null value will remove this parameter + */ + public void setActionUrl(URL actionUrl) { + setParameter(ACTION_URL, actionUrl); + } + + /** + * Set the full URL for the current action. + * + * @param actionUrl the full URL to set. A null value will remove this parameter + */ + public void setActionUrlWithString(String actionUrl) { + setUrlParameter(ACTION_URL, actionUrl); + } + + /** + * Get the api version + * + * @return the api version + */ + @Nullable + public String getApiVersion() { + return castOrNull(API_VERSION); + } + + /** + * Set the api version to use (currently always set to 1) + * + * @param apiVersion the api version to set. A null value will remove this parameter + */ + public void setApiVersion(String apiVersion) { + setParameter(API_VERSION, apiVersion); + } + + /** + * Get the authorization key. + * + * @return the authorization key + */ + @Nullable + public String getAuthToken() { + return castOrNull(AUTH_TOKEN); + } + + /** + * Set the {@value #AUTH_TOKEN_LENGTH} character authorization key used to authenticate the API request. + * + * @param authToken the authorization key to set. A null value will remove this parameter + */ + public void setAuthToken(String authToken) { + if (authToken != null && authToken.length() != AUTH_TOKEN_LENGTH) { + throw new IllegalArgumentException(authToken + " is not " + AUTH_TOKEN_LENGTH + " characters long."); + } + setParameter(AUTH_TOKEN, authToken); + } + + /** + * Verifies that AuthToken has been set for this request. Will throw an + * {@link IllegalStateException} if not. + */ + public void verifyAuthTokenSet() { + if (getAuthToken() == null) { + throw new IllegalStateException("AuthToken must be set before this value can be set."); + } + } + + /** + * Get the campaign keyword + * + * @return the campaign keyword + */ + @Nullable + public String getCampaignKeyword() { + return castOrNull(CAMPAIGN_KEYWORD); + } + + /** + * Set the Campaign Keyword (see + * Tracking Campaigns). + * Used to populate the Referrers > Campaigns report (clicking on a + * campaign loads all keywords for this campaign). Note: this parameter + * will only be used for the first pageview of a visit. + * + * @param campaignKeyword the campaign keyword to set. A null value will remove this parameter + */ + public void setCampaignKeyword(String campaignKeyword) { + setParameter(CAMPAIGN_KEYWORD, campaignKeyword); + } + + /** + * Get the campaign name + * + * @return the campaign name + */ + @Nullable + public String getCampaignName() { + return castOrNull(CAMPAIGN_NAME); + } + + /** + * Set the Campaign Name (see + * Tracking Campaigns). + * Used to populate the Referrers > Campaigns report. Note: this parameter + * will only be used for the first pageview of a visit. + * + * @param campaignName the campaign name to set. A null value will remove this parameter + */ + public void setCampaignName(String campaignName) { + setParameter(CAMPAIGN_NAME, campaignName); + } + + /** + * Get the charset of the page being tracked + * + * @return the charset + */ + @Nullable + public Charset getCharacterSet() { + return castOrNull(CHARACTER_SET); + } + + /** + * The charset of the page being tracked. Specify the charset if the data + * you send to Matomo is encoded in a different character set than the default + * utf-8. + * + * @param characterSet the charset to set. A null value will remove this parameter + */ + public void setCharacterSet(Charset characterSet) { + setParameter(CHARACTER_SET, characterSet); + } + + /** + * Get the name of the interaction with the content + * + * @return the name of the interaction + */ + @Nullable + public String getContentInteraction() { + return castOrNull(CONTENT_INTERACTION); + } + + /** + * Set the name of the interaction with the content. For instance a 'click'. + * + * @param contentInteraction the name of the interaction to set. A null value will remove this parameter + */ + public void setContentInteraction(String contentInteraction) { + setParameter(CONTENT_INTERACTION, contentInteraction); + } + + /** + * Get the name of the content + * + * @return the name + */ + @Nullable + public String getContentName() { + return castOrNull(CONTENT_NAME); + } + + /** + * Set the name of the content. For instance 'Ad Foo Bar'. + * + * @param contentName the name to set. A null value will remove this parameter + */ + public void setContentName(String contentName) { + setParameter(CONTENT_NAME, contentName); + } + + /** + * Get the content piece. + * + * @return the content piece. + */ + @Nullable + public String getContentPiece() { + return castOrNull(CONTENT_PIECE); + } + + /** + * Set the actual content piece. For instance the path to an image, video, audio, any text. + * + * @param contentPiece the content piece to set. A null value will remove this parameter + */ + public void setContentPiece(String contentPiece) { + setParameter(CONTENT_PIECE, contentPiece); + } + + /** + * Get the content target + * + * @return the target + */ + @Nullable + public URL getContentTarget() { + return castOrNull(CONTENT_TARGET); + } + + /** + * Get the content target + * + * @return the target + */ + @Nullable + public String getContentTargetAsString() { + return getUrlParameter(CONTENT_TARGET); + } + + /** + * Set the target of the content. For instance the URL of a landing page. + * + * @param contentTarget the target to set. A null value will remove this parameter + */ + public void setContentTarget(URL contentTarget) { + setParameter(CONTENT_TARGET, contentTarget); + } + + /** + * Set the target of the content. For instance the URL of a landing page. + * + * @param contentTarget the target to set. A null value will remove this parameter + */ + public void setContentTargetWithString(String contentTarget) { + setUrlParameter(CONTENT_TARGET, contentTarget); + } + + /** + * Get the current hour. + * + * @return the current hour + */ + @Nullable + public Integer getCurrentHour() { + return castOrNull(CURRENT_HOUR); + } + + /** + * Set the current hour (local time). + * + * @param currentHour the hour to set. A null value will remove this parameter + */ + public void setCurrentHour(Integer currentHour) { + setParameter(CURRENT_HOUR, currentHour); + } + + /** + * Get the current minute. + * + * @return the current minute + */ + @Nullable + public Integer getCurrentMinute() { + return castOrNull(CURRENT_MINUTE); + } + + /** + * Set the current minute (local time). + * + * @param currentMinute the minute to set. A null value will remove this parameter + */ + public void setCurrentMinute(Integer currentMinute) { + setParameter(CURRENT_MINUTE, currentMinute); + } + + /** + * Get the current second + * + * @return the current second + */ + @Nullable + public Integer getCurrentSecond() { + return castOrNull(CURRENT_SECOND); + } + + /** + * Set the current second (local time). + * + * @param currentSecond the second to set. A null value will remove this parameter + */ + public void setCurrentSecond(Integer currentSecond) { + setParameter(CURRENT_SECOND, currentSecond); + } + + /** + * Gets the list of objects currently stored at the specified custom tracking + * parameter. An empty list will be returned if there are no objects set at + * that key. + * + * @param key the key of the parameter whose list of objects to get. Cannot be null + * @return the list of objects currently stored at the specified key + */ + public List getCustomTrackingParameter(@NonNull String key) { + return new ArrayList<>(parameters.get(key)); + } + + /** + * Set a custom tracking parameter whose toString() value will be sent to + * the Matomo server. These parameters are stored separately from named Matomo + * parameters, meaning it is not possible to overwrite or clear named Matomo + * parameters with this method. A custom parameter that has the same name + * as a named Matomo parameter will be sent in addition to that named parameter. + * + * @param key the parameter's key. Cannot be null + * @param value the parameter's value. Removes the parameter if null + */ + public void setCustomTrackingParameter(@NonNull String key, @Nullable T value) { + customTrackingParameterNames.add(key); + setParameter(key, value); + } + + /** + * Add a custom tracking parameter to the specified key. This allows users + * to have multiple parameters with the same name and different values, + * commonly used during situations where list parameters are needed + * + * @param key the parameter's key. Cannot be null + * @param value the parameter's value. Cannot be null + */ + public void addCustomTrackingParameter(@NonNull String key, @NonNull Object value) { + customTrackingParameterNames.add(key); + addParameter(key, value); + } + + /** + * Removes all custom tracking parameters + */ + public void clearCustomTrackingParameter() { + for (String customTrackingParameterName : customTrackingParameterNames) { + setParameter(customTrackingParameterName, null); + } + } + + /** + * Get the resolution of the device + * + * @return the resolution + */ + @Nullable + public String getDeviceResolution() { + return castOrNull(DEVICE_RESOLUTION); + } + + /** + * Set the resolution of the device the visitor is using, eg 1280x1024. + * + * @param deviceResolution the resolution to set. A null value will remove this parameter + */ + public void setDeviceResolution(String deviceResolution) { + setParameter(DEVICE_RESOLUTION, deviceResolution); + } + + /** + * Get the url of a file the user had downloaded + * + * @return the url + */ + @Nullable + public URL getDownloadUrl() { + return castOrNull(DOWNLOAD_URL); + } + + /** + * Get the url of a file the user had downloaded + * + * @return the url + */ + @Nullable + public String getDownloadUrlAsString() { + return getUrlParameter(DOWNLOAD_URL); + } + + /** + * Set the url of a file the user has downloaded. Used for tracking downloads. + * We recommend to also set the url parameter to this same value. + * + * @param downloadUrl the url to set. A null value will remove this parameter + */ + public void setDownloadUrl(URL downloadUrl) { + setParameter(DOWNLOAD_URL, downloadUrl); + } + + /** + * Set the url of a file the user has downloaded. Used for tracking downloads. + * We recommend to also set the url parameter to this same value. + * + * @param downloadUrl the url to set. A null value will remove this parameter + */ + public void setDownloadUrlWithString(String downloadUrl) { + setUrlParameter(DOWNLOAD_URL, downloadUrl); + } + + /** + * Sets idgoal=0 in the request to track an ecommerce interaction: + * cart update or an ecommerce order. + */ + public void enableEcommerce() { + setGoalId(0); + } + + /** + * Verifies that Ecommerce has been enabled for the request. Will throw an + * {@link IllegalStateException} if not. + */ + public void verifyEcommerceEnabled() { + if (getGoalId() == null || getGoalId() != 0) { + throw new IllegalStateException("GoalId must be \"0\". Try calling enableEcommerce first before calling this method."); + } + } + + /** + * Verifies that Ecommerce has been enabled and that Ecommerce Id and + * Ecommerce Revenue have been set for the request. Will throw an + * {@link IllegalStateException} if not. + */ + public void verifyEcommerceState() { + verifyEcommerceEnabled(); + if (getEcommerceId() == null) { + throw new IllegalStateException("EcommerceId must be set before this value can be set."); + } + if (getEcommerceRevenue() == null) { + throw new IllegalStateException("EcommerceRevenue must be set before this value can be set."); + } + } + + /** + * Get the discount offered. + * + * @return the discount + */ + @Nullable + public Double getEcommerceDiscount() { + return castOrNull(ECOMMERCE_DISCOUNT); + } + + /** + * Set the discount offered. Ecommerce must be enabled, and EcommerceId and + * EcommerceRevenue must first be set. + * + * @param discount the discount to set. A null value will remove this parameter + */ + public void setEcommerceDiscount(Double discount) { + if (discount != null) { + verifyEcommerceState(); + } + setParameter(ECOMMERCE_DISCOUNT, discount); + } + + /** + * Get the id of this order. + * + * @return the id + */ + @Nullable + public String getEcommerceId() { + return castOrNull(ECOMMERCE_ID); + } + + /** + * Set the unique string identifier for the ecommerce order (required when + * tracking an ecommerce order). Ecommerce must be enabled. + * + * @param id the id to set. A null value will remove this parameter + */ + public void setEcommerceId(String id) { + if (id != null) { + verifyEcommerceEnabled(); + } + setParameter(ECOMMERCE_ID, id); + } + + /** + * Get the {@link EcommerceItem} at the specified index + * + * @param index the index of the {@link EcommerceItem} to return + * @return the {@link EcommerceItem} at the specified index + */ + @Nullable + public EcommerceItem getEcommerceItem(int index) { + EcommerceItems ecommerceItems = castOrNull(ECOMMERCE_ITEMS); + if (ecommerceItems == null) { + return null; + } + return ecommerceItems.get(index); + } + + /** + * Add an {@link EcommerceItem} to this order. Ecommerce must be enabled, + * and EcommerceId and EcommerceRevenue must first be set. + * + * @param item the {@link EcommerceItem} to add. Cannot be null + */ + public void addEcommerceItem(@NonNull EcommerceItem item) { + verifyEcommerceState(); + EcommerceItems ecommerceItems = castOrNull(ECOMMERCE_ITEMS); + if (ecommerceItems == null) { + ecommerceItems = new EcommerceItems(); + setParameter(ECOMMERCE_ITEMS, ecommerceItems); + } + ecommerceItems.add(item); + } + + /** + * Clears all {@link EcommerceItem} from this order. + */ + public void clearEcommerceItems() { + setParameter(ECOMMERCE_ITEMS, null); + } + + /** + * Get the timestamp of the customer's last ecommerce order + * + * @return the timestamp + */ + @Nullable + public Long getEcommerceLastOrderTimestamp() { + return castOrNull(ECOMMERCE_LAST_ORDER_TIMESTAMP); + } + + /** + * Set the UNUX timestamp of this customer's last ecommerce order. This value + * is used to process the "Days since last order" report. Ecommerce must be + * enabled, and EcommerceId and EcommerceRevenue must first be set. + * + * @param timestamp the timestamp to set. A null value will remove this parameter + */ + public void setEcommerceLastOrderTimestamp(Long timestamp) { + if (timestamp != null) { + verifyEcommerceState(); + } + setParameter(ECOMMERCE_LAST_ORDER_TIMESTAMP, timestamp); + } + + /** + * Get the grand total of the ecommerce order. + * + * @return the grand total + */ + @Nullable + public Double getEcommerceRevenue() { + return castOrNull(ECOMMERCE_REVENUE); + } + + /** + * Set the grand total of the ecommerce order (required when tracking an + * ecommerce order). Ecommerce must be enabled. + * + * @param revenue the grand total to set. A null value will remove this parameter + */ + public void setEcommerceRevenue(Double revenue) { + if (revenue != null) { + verifyEcommerceEnabled(); + } + setParameter(ECOMMERCE_REVENUE, revenue); + } + + /** + * Get the shipping cost of the ecommerce order. + * + * @return the shipping cost + */ + @Nullable + public Double getEcommerceShippingCost() { + return castOrNull(ECOMMERCE_SHIPPING_COST); + } + + /** + * Set the shipping cost of the ecommerce order. Ecommerce must be enabled, + * and EcommerceId and EcommerceRevenue must first be set. + * + * @param shippingCost the shipping cost to set. A null value will remove this parameter + */ + public void setEcommerceShippingCost(Double shippingCost) { + if (shippingCost != null) { + verifyEcommerceState(); + } + setParameter(ECOMMERCE_SHIPPING_COST, shippingCost); + } + + /** + * Get the subtotal of the ecommerce order; excludes shipping. + * + * @return the subtotal + */ + @Nullable + public Double getEcommerceSubtotal() { + return castOrNull(ECOMMERCE_SUBTOTAL); + } + + /** + * Set the subtotal of the ecommerce order; excludes shipping. Ecommerce + * must be enabled and EcommerceId and EcommerceRevenue must first be set. + * + * @param subtotal the subtotal to set. A null value will remove this parameter + */ + public void setEcommerceSubtotal(Double subtotal) { + if (subtotal != null) { + verifyEcommerceState(); + } + setParameter(ECOMMERCE_SUBTOTAL, subtotal); + } + + /** + * Get the tax amount of the ecommerce order. + * + * @return the tax amount + */ + @Nullable + public Double getEcommerceTax() { + return castOrNull(ECOMMERCE_TAX); + } + + /** + * Set the tax amount of the ecommerce order. Ecommerce must be enabled, and + * EcommerceId and EcommerceRevenue must first be set. + * + * @param tax the tax amount to set. A null value will remove this parameter + */ + public void setEcommerceTax(Double tax) { + if (tax != null) { + verifyEcommerceState(); + } + setParameter(ECOMMERCE_TAX, tax); + } + + /** + * Get the event action. + * + * @return the event action + */ + @Nullable + public String getEventAction() { + return castOrNull(EVENT_ACTION); + } + + /** + * Set the event action. Must not be empty. (eg. Play, Pause, Duration, + * Add Playlist, Downloaded, Clicked...). + * + * @param eventAction the event action to set. A null value will remove this parameter + */ + public void setEventAction(String eventAction) { + setNonEmptyStringParameter(EVENT_ACTION, eventAction); + } + + /** + * Get the event category. + * + * @return the event category + */ + @Nullable + public String getEventCategory() { + return castOrNull(EVENT_CATEGORY); + } + + /** + * Set the event category. Must not be empty. (eg. Videos, Music, Games...). + * + * @param eventCategory the event category to set. A null value will remove this parameter + */ + public void setEventCategory(String eventCategory) { + setNonEmptyStringParameter(EVENT_CATEGORY, eventCategory); + } + + /** + * Get the event name. + * + * @return the event name + */ + @Nullable + public String getEventName() { + return castOrNull(EVENT_NAME); + } + + /** + * Set the event name. (eg. a Movie name, or Song name, or File name...). + * + * @param eventName the event name to set. A null value will remove this parameter + */ + public void setEventName(String eventName) { + setParameter(EVENT_NAME, eventName); + } + + /** + * Get the event value. + * + * @return the event value + */ + @Nullable + public Number getEventValue() { + return castOrNull(EVENT_VALUE); + } + + /** + * Set the event value. Must be a float or integer value (numeric), not a string. + * + * @param eventValue the event value to set. A null value will remove this parameter + */ + public void setEventValue(Number eventValue) { + setParameter(EVENT_VALUE, eventValue); + } + + /** + * Get the goal id + * + * @return the goal id + */ + @Nullable + public Integer getGoalId() { + return castOrNull(GOAL_ID); + } + + /** + * Set the goal id. If specified, the tracking request will trigger a + * conversion for the goal of the website being tracked with this id. + * + * @param goalId the goal id to set. A null value will remove this parameter + */ + public void setGoalId(Integer goalId) { + setParameter(GOAL_ID, goalId); + } + + /** + * Get the goal revenue. + * + * @return the goal revenue + */ + @Nullable + public Double getGoalRevenue() { + return castOrNull(GOAL_REVENUE); + } + + /** + * Set a monetary value that was generated as revenue by this goal conversion. + * Only used if idgoal is specified in the request. + * + * @param goalRevenue the goal revenue to set. A null value will remove this parameter + */ + public void setGoalRevenue(Double goalRevenue) { + if (goalRevenue != null && getGoalId() == null) { + throw new IllegalStateException("GoalId must be set before GoalRevenue can be set."); + } + setParameter(GOAL_REVENUE, goalRevenue); + } + + /** + * Get the Accept-Language HTTP header + * + * @return the Accept-Language HTTP header + */ + @Nullable + public String getHeaderAcceptLanguage() { + return castOrNull(HEADER_ACCEPT_LANGUAGE); + } + + /** + * Set an override value for the Accept-Language HTTP header + * field. This value is used to detect the visitor's country if + * GeoIP is not enabled. + * + * @param acceptLangage the Accept-Language HTTP header to set. A null value will remove this parameter + */ + public void setHeaderAcceptLanguage(String acceptLangage) { + setParameter(HEADER_ACCEPT_LANGUAGE, acceptLangage); + } + + /** + * Get the User-Agent HTTP header + * + * @return the User-Agent HTTP header + */ + @Nullable + public String getHeaderUserAgent() { + return castOrNull(HEADER_USER_AGENT); + } + + /** + * Set an override value for the User-Agent HTTP header field. + * The user agent is used to detect the operating system and browser used. + * + * @param userAgent the User-Agent HTTP header tos et + */ + public void setHeaderUserAgent(String userAgent) { + setParameter(HEADER_USER_AGENT, userAgent); + } + + /** + * Get if this request will force a new visit. + * + * @return true if this request will force a new visit + */ + @Nullable + public Boolean getNewVisit() { + return getBooleanParameter(NEW_VISIT); + } + + /** + * If set to true, will force a new visit to be created for this action. + * + * @param newVisit if this request will force a new visit + */ + public void setNewVisit(Boolean newVisit) { + setBooleanParameter(NEW_VISIT, newVisit); + } + + /** + * Get the outlink url + * + * @return the outlink url + */ + @Nullable + public URL getOutlinkUrl() { + return castOrNull(OUTLINK_URL); + } + + /** + * Get the outlink url + * + * @return the outlink url + */ + @Nullable + public String getOutlinkUrlAsString() { + return getUrlParameter(OUTLINK_URL); + } + + /** + * Set an external URL the user has opened. Used for tracking outlink clicks. + * We recommend to also set the url parameter to this same value. + * + * @param outlinkUrl the outlink url to set. A null value will remove this parameter + */ + public void setOutlinkUrl(URL outlinkUrl) { + setParameter(OUTLINK_URL, outlinkUrl); + } + + /** + * Set an external URL the user has opened. Used for tracking outlink clicks. + * We recommend to also set the url parameter to this same value. + * + * @param outlinkUrl the outlink url to set. A null value will remove this parameter + */ + public void setOutlinkUrlWithString(String outlinkUrl) { + setUrlParameter(OUTLINK_URL, outlinkUrl); + } + + /** + * Get the page custom variable at the specified key. + * + * @param key the key of the variable to get + * @return the variable at the specified key, null if key is not present + * @deprecated Use the {@link #getPageCustomVariable(int)} method instead. + */ + @Nullable + @Deprecated + public String getPageCustomVariable(String key) { + return getCustomVariable(PAGE_CUSTOM_VARIABLE, key); + } + + /** + * Get the page custom variable at the specified index. + * + * @param index the index of the variable to get. Must be greater than 0 + * @return the variable at the specified key, null if nothing at this index + */ + @Nullable + public CustomVariable getPageCustomVariable(int index) { + return getCustomVariable(PAGE_CUSTOM_VARIABLE, index); + } + + /** + * Set a page custom variable with the specified key and value at the first available index. + * All page custom variables with this key will be overwritten or deleted + * + * @param key the key of the variable to set + * @param value the value of the variable to set at the specified key. A null value will remove this custom variable + * @deprecated Use the {@link #setPageCustomVariable(CustomVariable, int)} method instead. + */ + @Deprecated + public void setPageCustomVariable(String key, String value) { + if (value == null) { + removeCustomVariable(PAGE_CUSTOM_VARIABLE, key); + } else { + setCustomVariable(PAGE_CUSTOM_VARIABLE, new CustomVariable(key, value), null); + } + } + + /** + * Set a page custom variable at the specified index. + * + * @param customVariable the CustomVariable to set. A null value will remove the CustomVariable at the specified index + * @param index the index of he CustomVariable to set + */ + public void setPageCustomVariable(CustomVariable customVariable, int index) { + setCustomVariable(PAGE_CUSTOM_VARIABLE, customVariable, index); + } + + /** + * Check if the visitor has the Director plugin. + * + * @return true if visitor has the Director plugin + */ + @Nullable + public Boolean getPluginDirector() { + return getBooleanParameter(PLUGIN_DIRECTOR); + } + + /** + * Set if the visitor has the Director plugin. + * + * @param director true if the visitor has the Director plugin + */ + public void setPluginDirector(Boolean director) { + setBooleanParameter(PLUGIN_DIRECTOR, director); + } + + /** + * Check if the visitor has the Flash plugin. + * + * @return true if the visitor has the Flash plugin + */ + @Nullable + public Boolean getPluginFlash() { + return getBooleanParameter(PLUGIN_FLASH); + } + + /** + * Set if the visitor has the Flash plugin. + * + * @param flash true if the visitor has the Flash plugin + */ + @Nullable + public void setPluginFlash(Boolean flash) { + setBooleanParameter(PLUGIN_FLASH, flash); + } + + /** + * Check if the visitor has the Gears plugin. + * + * @return true if the visitor has the Gears plugin + */ + @Nullable + public Boolean getPluginGears() { + return getBooleanParameter(PLUGIN_GEARS); + } + + /** + * Set if the visitor has the Gears plugin. + * + * @param gears true if the visitor has the Gears plugin + */ + public void setPluginGears(Boolean gears) { + setBooleanParameter(PLUGIN_GEARS, gears); + } + + /** + * Check if the visitor has the Java plugin. + * + * @return true if the visitor has the Java plugin + */ + @Nullable + public Boolean getPluginJava() { + return getBooleanParameter(PLUGIN_JAVA); + } + + /** + * Set if the visitor has the Java plugin. + * + * @param java true if the visitor has the Java plugin + */ + public void setPluginJava(Boolean java) { + setBooleanParameter(PLUGIN_JAVA, java); + } + + /** + * Check if the visitor has the PDF plugin. + * + * @return true if the visitor has the PDF plugin + */ + @Nullable + public Boolean getPluginPDF() { + return getBooleanParameter(PLUGIN_PDF); + } + + /** + * Set if the visitor has the PDF plugin. + * + * @param pdf true if the visitor has the PDF plugin + */ + public void setPluginPDF(Boolean pdf) { + setBooleanParameter(PLUGIN_PDF, pdf); + } + + /** + * Check if the visitor has the Quicktime plugin. + * + * @return true if the visitor has the Quicktime plugin + */ + @Nullable + public Boolean getPluginQuicktime() { + return getBooleanParameter(PLUGIN_QUICKTIME); + } + + /** + * Set if the visitor has the Quicktime plugin. + * + * @param quicktime true if the visitor has the Quicktime plugin + */ + public void setPluginQuicktime(Boolean quicktime) { + setBooleanParameter(PLUGIN_QUICKTIME, quicktime); + } + + /** + * Check if the visitor has the RealPlayer plugin. + * + * @return true if the visitor has the RealPlayer plugin + */ + @Nullable + public Boolean getPluginRealPlayer() { + return getBooleanParameter(PLUGIN_REAL_PLAYER); + } + + /** + * Set if the visitor has the RealPlayer plugin. + * + * @param realPlayer true if the visitor has the RealPlayer plugin + */ + public void setPluginRealPlayer(Boolean realPlayer) { + setBooleanParameter(PLUGIN_REAL_PLAYER, realPlayer); + } + + /** + * Check if the visitor has the Silverlight plugin. + * + * @return true if the visitor has the Silverlight plugin + */ + @Nullable + public Boolean getPluginSilverlight() { + return getBooleanParameter(PLUGIN_SILVERLIGHT); + } + + /** + * Set if the visitor has the Silverlight plugin. + * + * @param silverlight true if the visitor has the Silverlight plugin + */ + public void setPluginSilverlight(Boolean silverlight) { + setBooleanParameter(PLUGIN_SILVERLIGHT, silverlight); + } + + /** + * Check if the visitor has the Windows Media plugin. + * + * @return true if the visitor has the Windows Media plugin + */ + @Nullable + public Boolean getPluginWindowsMedia() { + return getBooleanParameter(PLUGIN_WINDOWS_MEDIA); + } + + /** + * Set if the visitor has the Windows Media plugin. + * + * @param windowsMedia true if the visitor has the Windows Media plugin + */ + public void setPluginWindowsMedia(Boolean windowsMedia) { + setBooleanParameter(PLUGIN_WINDOWS_MEDIA, windowsMedia); + } + + /** + * Get the random value for this request + * + * @return the random value + */ + @Nullable + public String getRandomValue() { + return castOrNull(RANDOM_VALUE); + } + + /** + * Set a random value that is generated before each request. Using it helps + * avoid the tracking request being cached by the browser or a proxy. + * + * @param randomValue the random value to set. A null value will remove this parameter + */ + public void setRandomValue(String randomValue) { + setParameter(RANDOM_VALUE, randomValue); + } + + /** + * Get the referrer url + * + * @return the referrer url + */ + @Nullable + public URL getReferrerUrl() { + return castOrNull(REFERRER_URL); + } + + /** + * Get the referrer url + * + * @return the referrer url + */ + @Nullable + public String getReferrerUrlAsString() { + return getUrlParameter(REFERRER_URL); + } + + /** + * Set the full HTTP Referrer URL. This value is used to determine how someone + * got to your website (ie, through a website, search engine or campaign). + * + * @param referrerUrl the referrer url to set. A null value will remove this parameter + */ + public void setReferrerUrl(URL referrerUrl) { + setParameter(REFERRER_URL, referrerUrl); + } + + /** + * Set the full HTTP Referrer URL. This value is used to determine how someone + * got to your website (ie, through a website, search engine or campaign). + * + * @param referrerUrl the referrer url to set. A null value will remove this parameter + */ + public void setReferrerUrlWithString(String referrerUrl) { + setUrlParameter(REFERRER_URL, referrerUrl); + } + + /** + * Get the datetime of the request + * + * @return the datetime of the request + */ + @Nullable + public MatomoDate getRequestDatetime() { + return castOrNull(REQUEST_DATETIME); + } + + /** + * Set the datetime of the request (normally the current time is used). + * This can be used to record visits and page views in the past. The datetime + * must be sent in UTC timezone. Note: if you record data in the past, you will + * need to force Matomo to re-process + * reports for the past dates. If you set the Request Datetime to a datetime + * older than four hours then Auth Token must be set. If you set + * Request Datetime with a datetime in the last four hours then you + * don't need to pass Auth Token. + * + * @param datetime the datetime of the request to set. A null value will remove this parameter + */ + public void setRequestDatetime(MatomoDate datetime) { + if (datetime != null && new Date().getTime() - datetime.getTime() > REQUEST_DATETIME_AUTH_LIMIT && getAuthToken() == null) { + throw new IllegalStateException("Because you are trying to set RequestDatetime for a time greater than 4 hours ago, AuthToken must be set first."); + } + setParameter(REQUEST_DATETIME, datetime); + } + + /** + * Get if this request will be tracked. + * + * @return true if request will be tracked + */ + @Nullable + public Boolean getRequired() { + return getBooleanParameter(REQUIRED); + } + + /** + * Set if this request will be tracked by the Matomo server. + * + * @param required true if request will be tracked + */ + public void setRequired(Boolean required) { + setBooleanParameter(REQUIRED, required); + } + + /** + * Get if the response will be an image. + * + * @return true if the response will be an an image + */ + @Nullable + public Boolean getResponseAsImage() { + return getBooleanParameter(RESPONSE_AS_IMAGE); + } + + /** + * Set if the response will be an image. If set to false, Matomo will respond + * with a HTTP 204 response code instead of a GIF image. This improves performance + * and can fix errors if images are not allowed to be obtained directly + * (eg Chrome Apps). Available since Matomo 2.10.0. + * + * @param responseAsImage true if the response will be an image + */ + public void setResponseAsImage(Boolean responseAsImage) { + setBooleanParameter(RESPONSE_AS_IMAGE, responseAsImage); + } + + /** + * Get the search category + * + * @return the search category + */ + @Nullable + public String getSearchCategory() { + return castOrNull(SEARCH_CATEGORY); + } + + /** + * Specify a search category with this parameter. SearchQuery must first be + * set. + * + * @param searchCategory the search category to set. A null value will remove this parameter + */ + public void setSearchCategory(String searchCategory) { + if (searchCategory != null && getSearchQuery() == null) { + throw new IllegalStateException("SearchQuery must be set before SearchCategory can be set."); + } + setParameter(SEARCH_CATEGORY, searchCategory); + } + + /** + * Get the search query. + * + * @return the search query + */ + @Nullable + public String getSearchQuery() { + return castOrNull(SEARCH_QUERY); + } + + /** + * Set the search query. When specified, the request will not be tracked as + * a normal pageview but will instead be tracked as a Site Search request. + * + * @param searchQuery the search query to set. A null value will remove this parameter + */ + public void setSearchQuery(String searchQuery) { + setParameter(SEARCH_QUERY, searchQuery); + } + + /** + * Get the search results count. + * + * @return the search results count + */ + @Nullable + public Long getSearchResultsCount() { + return castOrNull(SEARCH_RESULTS_COUNT); + } + + /** + * We recommend to set the + * search count to the number of search results displayed on the results page. + * When keywords are tracked with {@code Search Results Count=0} they will appear in + * the "No Result Search Keyword" report. SearchQuery must first be set. + * + * @param searchResultsCount the search results count to set. A null value will remove this parameter + */ + public void setSearchResultsCount(Long searchResultsCount) { + if (searchResultsCount != null && getSearchQuery() == null) { + throw new IllegalStateException("SearchQuery must be set before SearchResultsCount can be set."); + } + setParameter(SEARCH_RESULTS_COUNT, searchResultsCount); + } + + /** + * Get the id of the website we're tracking. + * + * @return the id of the website + */ + @Nullable + public Integer getSiteId() { + return castOrNull(SITE_ID); + } + + /** + * Set the ID of the website we're tracking a visit/action for. + * + * @param siteId the id of the website to set. A null value will remove this parameter + */ + public void setSiteId(Integer siteId) { + setParameter(SITE_ID, siteId); + } + + /** + * Set if bot requests should be tracked + * + * @return true if bot requests should be tracked + */ + @Nullable + public Boolean getTrackBotRequests() { + return getBooleanParameter(TRACK_BOT_REQUESTS); + } + + /** + * By default Matomo does not track bots. If you use the Tracking Java API, + * you may be interested in tracking bot requests. To enable Bot Tracking in + * Matomo, set Track Bot Requests to true. + * + * @param trackBotRequests true if bot requests should be tracked + */ + public void setTrackBotRequests(Boolean trackBotRequests) { + setBooleanParameter(TRACK_BOT_REQUESTS, trackBotRequests); + } + + /** + * Get the visit custom variable at the specified key. + * + * @param key the key of the variable to get + * @return the variable at the specified key, null if key is not present + * @deprecated Use the {@link #getVisitCustomVariable(int)} method instead. + */ + @Nullable + @Deprecated + public String getUserCustomVariable(String key) { + return getCustomVariable(VISIT_CUSTOM_VARIABLE, key); + } + + /** + * Get the visit custom variable at the specified index. + * + * @param index the index of the variable to get + * @return the variable at the specified index, null if nothing at this index + */ + @Nullable + public CustomVariable getVisitCustomVariable(int index) { + return getCustomVariable(VISIT_CUSTOM_VARIABLE, index); + } + + /** + * Set a visit custom variable with the specified key and value at the first available index. + * All visit custom variables with this key will be overwritten or deleted + * + * @param key the key of the variable to set + * @param value the value of the variable to set at the specified key. A null value will remove this parameter + * @deprecated Use the {@link #setVisitCustomVariable(CustomVariable, int)} method instead. + */ + @Deprecated + public void setUserCustomVariable(String key, String value) { + if (value == null) { + removeCustomVariable(VISIT_CUSTOM_VARIABLE, key); + } else { + setCustomVariable(VISIT_CUSTOM_VARIABLE, new CustomVariable(key, value), null); + } + } + + /** + * Set a user custom variable at the specified key. + * + * @param customVariable the CustomVariable to set. A null value will remove the custom variable at the specified index + * @param index the index to set the customVariable at. + */ + public void setVisitCustomVariable(CustomVariable customVariable, int index) { + setCustomVariable(VISIT_CUSTOM_VARIABLE, customVariable, index); + } + + /** + * Get the user id for this request. + * + * @return the user id + */ + @Nullable + public String getUserId() { + return castOrNull(USER_ID); + } + + /** + * Set the user id for this request. + * User id is any non empty unique string identifying the user (such as an email + * address or a username). To access this value, users must be logged-in in your + * system so you can fetch this user id from your system, and pass it to Matomo. + * The user id appears in the visitor log, the Visitor profile, and you can + * Segment + * reports for one or several user ids. When specified, the user id will be + * "enforced". This means that if there is no recent visit with this user id, + * a new one will be created. If a visit is found in the last 30 minutes with + * your specified user id, then the new action will be recorded to this existing visit. + * + * @param userId the user id to set. A null value will remove this parameter + */ + public void setUserId(String userId) { + setNonEmptyStringParameter(USER_ID, userId); + } + + /** + * Get the visitor's city. + * + * @return the visitor's city + */ + @Nullable + public String getVisitorCity() { + return castOrNull(VISITOR_CITY); + } + + /** + * Set an override value for the city. The name of the city the visitor is + * located in, eg, Tokyo. AuthToken must first be set. + * + * @param city the visitor's city to set. A null value will remove this parameter + */ + public void setVisitorCity(String city) { + if (city != null) { + verifyAuthTokenSet(); + } + setParameter(VISITOR_CITY, city); + } + + /** + * Get the visitor's country. + * + * @return the visitor's country + */ + @Nullable + public MatomoLocale getVisitorCountry() { + return castOrNull(VISITOR_COUNTRY); + } + + /** + * Set an override value for the country. AuthToken must first be set. + * + * @param country the visitor's country to set. A null value will remove this parameter + */ + public void setVisitorCountry(MatomoLocale country) { + if (country != null) { + verifyAuthTokenSet(); + } + setParameter(VISITOR_COUNTRY, country); + } + + /** + * Get the visitor's custom id. + * + * @return the visitor's custom id + */ + @Nullable + public String getVisitorCustomId() { + return castOrNull(VISITOR_CUSTOM_ID); + } + + /** + * Set a custom visitor ID for this request. You must set this value to exactly + * a {@value #ID_LENGTH} character hexadecimal string (containing only characters 01234567890abcdefABCDEF). + * We recommended to set the UserId rather than the VisitorCustomId. + * + * @param visitorCustomId the visitor's custom id to set. A null value will remove this parameter + */ + public void setVisitorCustomId(String visitorCustomId) { + if (visitorCustomId != null) { + if (visitorCustomId.length() != ID_LENGTH) { + throw new IllegalArgumentException(visitorCustomId + " is not " + ID_LENGTH + " characters long."); + } + // Verify visitorID is a 16 character hexadecimal string + if (!VISITOR_ID_PATTERN.matcher(visitorCustomId).matches()) { + throw new IllegalArgumentException(visitorCustomId + " is not a hexadecimal string."); + } + } + setParameter(VISITOR_CUSTOM_ID, visitorCustomId); + } + + /** + * Get the timestamp of the visitor's first visit. + * + * @return the timestamp of the visitor's first visit + */ + @Nullable + public Long getVisitorFirstVisitTimestamp() { + return castOrNull(VISITOR_FIRST_VISIT_TIMESTAMP); + } + + /** + * Set the UNIX timestamp of this visitor's first visit. This could be set + * to the date where the user first started using your software/app, or when + * he/she created an account. This parameter is used to populate the + * Goals > Days to Conversion report. + * + * @param timestamp the timestamp of the visitor's first visit to set. A null value will remove this parameter + */ + public void setVisitorFirstVisitTimestamp(Long timestamp) { + setParameter(VISITOR_FIRST_VISIT_TIMESTAMP, timestamp); + } + + /** + * Get the visitor's id. + * + * @return the visitor's id + */ + @Nullable + public String getVisitorId() { + return castOrNull(VISITOR_ID); + } + + /** + * Set the unique visitor ID, must be a {@value #ID_LENGTH} characters hexadecimal string. + * Every unique visitor must be assigned a different ID and this ID must not + * change after it is assigned. If this value is not set Matomo will still + * track visits, but the unique visitors metric might be less accurate. + * + * @param visitorId the visitor id to set. A null value will remove this parameter + */ + public void setVisitorId(String visitorId) { + if (visitorId != null) { + if (visitorId.length() != ID_LENGTH) { + throw new IllegalArgumentException(visitorId + " is not " + ID_LENGTH + " characters long."); + } + // Verify visitorID is a 16 character hexadecimal string + if (!VISITOR_ID_PATTERN.matcher(visitorId).matches()) { + throw new IllegalArgumentException(visitorId + " is not a hexadecimal string."); + } + } + setParameter(VISITOR_ID, visitorId); + } + + /** + * Get the visitor's ip. + * + * @return the visitor's ip + */ + @Nullable + public String getVisitorIp() { + return castOrNull(VISITOR_IP); + } + + /** + * Set the override value for the visitor IP (both IPv4 and IPv6 notations + * supported). AuthToken must first be set. + * + * @param visitorIp the visitor's ip to set. A null value will remove this parameter + */ + public void setVisitorIp(String visitorIp) { + if (visitorIp != null) { + verifyAuthTokenSet(); + } + setParameter(VISITOR_IP, visitorIp); + } + + /** + * Get the visitor's latitude. + * + * @return the visitor's latitude + */ + @Nullable + public Double getVisitorLatitude() { + return castOrNull(VISITOR_LATITUDE); + } + + /** + * Set an override value for the visitor's latitude, eg 22.456. AuthToken + * must first be set. + * + * @param latitude the visitor's latitude to set. A null value will remove this parameter + */ + public void setVisitorLatitude(Double latitude) { + if (latitude != null) { + verifyAuthTokenSet(); + } + setParameter(VISITOR_LATITUDE, latitude); + } + + /** + * Get the visitor's longitude. + * + * @return the visitor's longitude + */ + @Nullable + public Double getVisitorLongitude() { + return castOrNull(VISITOR_LONGITUDE); + } + + /** + * Set an override value for the visitor's longitude, eg 22.456. AuthToken + * must first be set. + * + * @param longitude the visitor's longitude to set. A null value will remove this parameter + */ + public void setVisitorLongitude(Double longitude) { + if (longitude != null) { + verifyAuthTokenSet(); + } + setParameter(VISITOR_LONGITUDE, longitude); + } + + /** + * Get the timestamp of the visitor's previous visit. + * + * @return the timestamp of the visitor's previous visit + */ + @Nullable + public Long getVisitorPreviousVisitTimestamp() { + return castOrNull(VISITOR_PREVIOUS_VISIT_TIMESTAMP); + } + + /** + * Set the UNIX timestamp of this visitor's previous visit. This parameter + * is used to populate the report + * Visitors > Engagement > Visits by days since last visit. + * + * @param timestamp the timestamp of the visitor's previous visit to set. A null value will remove this parameter + */ + public void setVisitorPreviousVisitTimestamp(Long timestamp) { + setParameter(VISITOR_PREVIOUS_VISIT_TIMESTAMP, timestamp); + } + + /** + * Get the visitor's region. + * + * @return the visitor's region + */ + @Nullable + public String getVisitorRegion() { + return castOrNull(VISITOR_REGION); + } + + /** + * Set an override value for the region. Should be set to the two letter + * region code as defined by + * MaxMind's GeoIP databases. + * See here + * for a list of them for every country (the region codes are located in the + * second column, to the left of the region name and to the right of the country + * code). + * + * @param region the visitor's region to set. A null value will remove this parameter + */ + public void setVisitorRegion(String region) { + if (region != null) { + verifyAuthTokenSet(); + } + setParameter(VISITOR_REGION, region); + } + + /** + * Get the count of visits for this visitor. + * + * @return the count of visits for this visitor + */ + @Nullable + public Integer getVisitorVisitCount() { + return castOrNull(VISITOR_VISIT_COUNT); + } + + /** + * Set the current count of visits for this visitor. To set this value correctly, + * it would be required to store the value for each visitor in your application + * (using sessions or persisting in a database). Then you would manually increment + * the counts by one on each new visit or "session", depending on how you choose + * to define a visit. This value is used to populate the report + * Visitors > Engagement > Visits by visit number. + * + * @param visitorVisitCount the count of visits for this visitor to set. A null value will remove this parameter + */ + public void setVisitorVisitCount(Integer visitorVisitCount) { + setParameter(VISITOR_VISIT_COUNT, visitorVisitCount); + } + + public Map> getParameters() { + return parameters.asMap(); + } + + /** + * Get the query string represented by this object. + * + * @return the query string represented by this object + * @deprecated Use {@link URIBuilder} in conjunction with {@link #getParameters()} and {@link QueryParameters#fromMap(Map)} ()} instead + */ + @Nonnull + @Deprecated + public String getQueryString() { + return parameters.entries().stream().map(parameter -> parameter.getKey() + '=' + parameter.getValue().toString()).collect(Collectors.joining("&")); + } + + /** + * Get the url encoded query string represented by this object. + * + * @return the url encoded query string represented by this object + * @deprecated Use {@link URIBuilder} in conjunction with {@link #getParameters()} and {@link QueryParameters#fromMap(Map)} ()} instead + */ + @Nonnull + @Deprecated + public String getUrlEncodedQueryString() { + String queryString = new URIBuilder().setParameters(QueryParameters.fromMap(getParameters())).toString(); + if (queryString.isEmpty()) { + return ""; + } + return queryString.substring(1); + } + + /** + * Get a random hexadecimal string of a specified length. + * + * @param length length of the string to produce + * @return a random string consisting only of hexadecimal characters + */ + @Nonnull + public static String getRandomHexString(int length) { + byte[] bytes = new byte[length / 2]; + new SecureRandom().nextBytes(bytes); + return BaseEncoding.base16().lowerCase().encode(bytes); + } + + /** + * Set a stored parameter. + * + * @param key the parameter's key + * @param value the parameter's value. Removes the parameter if null + */ + public void setParameter(@NonNull String key, @Nullable Object value) { + parameters.removeAll(key); + if (value != null) { + addParameter(key, value); + } + } + + /** + * Add more values to the given parameter + * + * @param key the parameter's key. Must not be null + * @param value the parameter's value. Must not be null + */ + public void addParameter(@NonNull String key, @NonNull Object value) { + parameters.put(key, value); + } + + + /** + * Get a stored parameter that and cast it if present + * + * @param key the parameter's key. Must not be null + * @return the stored parameter's value casted to the requested type or null if no value is present + */ + @Nullable + private T castOrNull(@NonNull String key) { + Collection values = parameters.get(key); + if (values.isEmpty()) { + return null; + } + return (T) values.iterator().next(); + } + + /** + * Set a stored parameter and verify it is a non-empty string. + * + * @param key the parameter's key + * @param value the parameter's value. Cannot be the empty. Removes the parameter if null + * string + */ + private void setNonEmptyStringParameter(@NonNull String key, String value) { + if (value != null && value.trim().isEmpty()) { + throw new IllegalArgumentException("Value cannot be empty."); + } + setParameter(key, value); + } + + /** + * Get a stored parameter that is a boolean. + * + * @param key the parameter's key + * @return the stored parameter's value + */ + @Nullable + private Boolean getBooleanParameter(@NonNull String key) { + MatomoBoolean matomoBoolean = castOrNull(key); + if (matomoBoolean == null) { + return null; + } + return matomoBoolean.isValue(); + } + + /** + * Set a stored parameter that is a boolean. + * + * @param key the parameter's key + * @param value the parameter's value. Removes the parameter if null + */ + private void setBooleanParameter(@NonNull String key, @Nullable Boolean value) { + if (value == null) { + setParameter(key, null); + } else { + setParameter(key, new MatomoBoolean(value)); + } + } + + /** + * Get a stored parameter that is a URL. + * + * @param key the parameter's key + * @return the stored parameter's value + */ + @Nullable + private String getUrlParameter(@NonNull String key) { + URL url = castOrNull(key); + if (url == null) { + return null; + } + return url.toString(); + } + + /** + * Set a stored parameter that is a valid URL. It will be syntactically checked + * + * @param key the parameter's key + * @param url the parameter's value. Removes the parameter if null + * @throws InvalidUrlException if the URL is invalid + */ + private void setUrlParameter(@NonNull String key, @Nullable String url) { + if (url == null) { + setParameter(key, null); + } else { + try { + setParameter(key, new URL(url)); + } catch (MalformedURLException e) { + throw new InvalidUrlException(e); + } + } + + } + + /** + * Get a value that is stored in a json object at the specified parameter. + * + * @param parameter the parameter to retrieve the json object from + * @param index the index of the value. + * @return the value at the specified index + */ + @Nullable + private CustomVariable getCustomVariable(@NonNull String parameter, int index) { + CustomVariables customVariables = castOrNull(parameter); + if (customVariables == null) { + return null; + } + return customVariables.get(index); + } + + @Nullable + private String getCustomVariable(@NonNull String parameter, @NonNull String key) { + CustomVariables customVariables = castOrNull(parameter); + if (customVariables == null) { + return null; + } + return customVariables.get(key); + } + + /** + * Store a value in a json object at the specified parameter. + * + * @param parameter the parameter to store the json object at + * @param parameter the key of the value. Cannot be null + * @param customVariable the value. Removes the parameter if null + */ + private void setCustomVariable(@NonNull String parameter, @Nullable CustomVariable customVariable, Integer index) { + + if (customVariable == null && index == null) { + throw new IllegalArgumentException("Either custom variable or index must be set"); + } + CustomVariables customVariables = castOrNull(parameter); + if (customVariables == null) { + customVariables = new CustomVariables(); + setParameter(parameter, customVariables); + } + if (customVariable == null) { + customVariables.remove(index); + if (customVariables.isEmpty()) { + setParameter(parameter, null); + } + } else if (index == null) { + customVariables.add(customVariable); + } else { + customVariables.add(customVariable, index); + } + } + + private void removeCustomVariable(@NonNull String parameter, @NonNull String key) { + CustomVariables customVariables = castOrNull(parameter); + if (customVariables != null) { + customVariables.remove(key); + if (customVariables.isEmpty()) { + setParameter(parameter, null); + } + } + } + +} diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java b/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java new file mode 100644 index 00000000..70b60ad3 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java @@ -0,0 +1,680 @@ +package org.matomo.java.tracking; + +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.Charset; +import java.util.List; +import java.util.Map; + +public class MatomoRequestBuilder { + + private int siteId; + + private URL actionUrl; + + private String actionName; + private Long actionTime; + private String apiVersion; + private String authToken; + private String campaignKeyword; + private String campaignName; + private Charset characterSet; + private String contentInteraction; + private String contentName; + private String contentPiece; + private URL contentTarget; + private Integer currentHour; + private Integer currentMinute; + private Integer currentSecond; + private String deviceResolution; + private URL downloadUrl; + private Double ecommerceDiscount; + private String ecommerceId; + private Long ecommerceLastOrderTimestamp; + private Double ecommerceRevenue; + private Double ecommerceShippingCost; + private Double ecommerceSubtotal; + private Double ecommerceTax; + private String eventAction; + private String eventCategory; + private String eventName; + private Number eventValue; + private Integer goalId; + private Double goalRevenue; + private String headerAcceptLanguage; + private String headerUserAgent; + private Boolean newVisit; + private URL outlinkUrl; + private Boolean pluginDirector; + private Boolean pluginFlash; + private Boolean pluginGears; + private Boolean pluginJava; + private Boolean pluginPDF; + private Boolean pluginQuicktime; + private Boolean pluginRealPlayer; + private Boolean pluginSilverlight; + private Boolean pluginWindowsMedia; + private String randomValue; + private URL referrerUrl; + private MatomoDate requestDatetime; + private Boolean required; + private Boolean responseAsImage; + private String searchCategory; + private String searchQuery; + private Long searchResultsCount; + private Boolean trackBotRequests; + private String userId; + private String visitorCity; + private MatomoLocale visitorCountry; + private String visitorCustomId; + private Long visitorFirstVisitTimestamp; + private String visitorId; + private String visitorIp; + private Double visitorLatitude; + private Double visitorLongitude; + private Long visitorPreviousVisitTimestamp; + private String visitorRegion; + private Integer visitorVisitCount; + + private List visitCustomVariables; + + private List pageCustomVariables; + private Map customTrackingParameters; + + public MatomoRequestBuilder siteId(int siteId) { + this.siteId = siteId; + return this; + } + + public MatomoRequestBuilder actionUrl(URL actionUrl) { + this.actionUrl = actionUrl; + return this; + } + + public MatomoRequestBuilder actionUrl(String actionUrl) { + try { + this.actionUrl = new URL(actionUrl); + } catch (MalformedURLException e) { + throw new InvalidUrlException(e); + } + return this; + } + + public MatomoRequestBuilder actionName(String actionName) { + this.actionName = actionName; + return this; + } + + public MatomoRequestBuilder actionTime(Long actionTime) { + this.actionTime = actionTime; + return this; + } + + public MatomoRequestBuilder apiVersion(String apiVersion) { + this.apiVersion = apiVersion; + return this; + } + + public MatomoRequestBuilder authToken(String authToken) { + this.authToken = authToken; + return this; + } + + public MatomoRequestBuilder campaignKeyword(String campaignKeyword) { + this.campaignKeyword = campaignKeyword; + return this; + } + + public MatomoRequestBuilder campaignName(String campaignName) { + this.campaignName = campaignName; + return this; + } + + public MatomoRequestBuilder characterSet(Charset characterSet) { + this.characterSet = characterSet; + return this; + } + + public MatomoRequestBuilder contentInteraction(String contentInteraction) { + this.contentInteraction = contentInteraction; + return this; + } + + public MatomoRequestBuilder contentName(String contentName) { + this.contentName = contentName; + return this; + } + + public MatomoRequestBuilder contentPiece(String contentPiece) { + this.contentPiece = contentPiece; + return this; + } + + public MatomoRequestBuilder contentTarget(URL contentTarget) { + this.contentTarget = contentTarget; + return this; + } + + public MatomoRequestBuilder contentTarget(String contentTarget) { + try { + this.contentTarget = new URL(contentTarget); + } catch (MalformedURLException e) { + throw new InvalidUrlException(e); + } + return this; + } + + public MatomoRequestBuilder currentHour(Integer currentHour) { + this.currentHour = currentHour; + return this; + } + + public MatomoRequestBuilder currentMinute(Integer currentMinute) { + this.currentMinute = currentMinute; + return this; + } + + public MatomoRequestBuilder currentSecond(Integer currentSecond) { + this.currentSecond = currentSecond; + return this; + } + + public MatomoRequestBuilder deviceResolution(String deviceResolution) { + this.deviceResolution = deviceResolution; + return this; + } + + public MatomoRequestBuilder downloadUrl(URL downloadUrl) { + this.downloadUrl = downloadUrl; + return this; + } + + public MatomoRequestBuilder downloadUrl(String downloadUrl) { + try { + this.downloadUrl = new URL(downloadUrl); + } catch (MalformedURLException e) { + throw new InvalidUrlException(e); + } + return this; + } + + public MatomoRequestBuilder ecommerceDiscount(Double ecommerceDiscount) { + this.ecommerceDiscount = ecommerceDiscount; + return this; + } + + public MatomoRequestBuilder ecommerceId(String ecommerceId) { + this.ecommerceId = ecommerceId; + return this; + } + + public MatomoRequestBuilder ecommerceLastOrderTimestamp(Long ecommerceLastOrderTimestamp) { + this.ecommerceLastOrderTimestamp = ecommerceLastOrderTimestamp; + return this; + } + + public MatomoRequestBuilder ecommerceRevenue(Double ecommerceRevenue) { + this.ecommerceRevenue = ecommerceRevenue; + return this; + } + + public MatomoRequestBuilder ecommerceShippingCost(Double ecommerceShippingCost) { + this.ecommerceShippingCost = ecommerceShippingCost; + return this; + } + + public MatomoRequestBuilder ecommerceSubtotal(Double ecommerceSubtotal) { + this.ecommerceSubtotal = ecommerceSubtotal; + return this; + } + + public MatomoRequestBuilder ecommerceTax(Double ecommerceTax) { + this.ecommerceTax = ecommerceTax; + return this; + } + + public MatomoRequestBuilder eventAction(String eventAction) { + this.eventAction = eventAction; + return this; + } + + public MatomoRequestBuilder eventCategory(String eventCategory) { + this.eventCategory = eventCategory; + return this; + } + + public MatomoRequestBuilder eventName(String eventName) { + this.eventName = eventName; + return this; + } + + public MatomoRequestBuilder eventValue(Number eventValue) { + this.eventValue = eventValue; + return this; + } + + public MatomoRequestBuilder goalId(Integer goalId) { + this.goalId = goalId; + return this; + } + + public MatomoRequestBuilder goalRevenue(Double goalRevenue) { + this.goalRevenue = goalRevenue; + return this; + } + + public MatomoRequestBuilder headerAcceptLanguage(String headerAcceptLanguage) { + this.headerAcceptLanguage = headerAcceptLanguage; + return this; + } + + public MatomoRequestBuilder headerUserAgent(String headerUserAgent) { + this.headerUserAgent = headerUserAgent; + return this; + } + + public MatomoRequestBuilder newVisit(Boolean newVisit) { + this.newVisit = newVisit; + return this; + } + + public MatomoRequestBuilder outlinkUrl(URL outlinkUrl) { + this.outlinkUrl = outlinkUrl; + return this; + } + + public MatomoRequestBuilder outlinkUrl(String outlinkUrl) { + try { + this.outlinkUrl = new URL(outlinkUrl); + } catch (MalformedURLException e) { + throw new InvalidUrlException(e); + } + return this; + } + + public MatomoRequestBuilder pluginDirector(Boolean pluginDirector) { + this.pluginDirector = pluginDirector; + return this; + } + + public MatomoRequestBuilder pluginFlash(Boolean pluginFlash) { + this.pluginFlash = pluginFlash; + return this; + } + + public MatomoRequestBuilder pluginGears(Boolean pluginGears) { + this.pluginGears = pluginGears; + return this; + } + + public MatomoRequestBuilder pluginJava(Boolean pluginJava) { + this.pluginJava = pluginJava; + return this; + } + + public MatomoRequestBuilder pluginPDF(Boolean pluginPDF) { + this.pluginPDF = pluginPDF; + return this; + } + + public MatomoRequestBuilder pluginQuicktime(Boolean pluginQuicktime) { + this.pluginQuicktime = pluginQuicktime; + return this; + } + + public MatomoRequestBuilder pluginRealPlayer(Boolean pluginRealPlayer) { + this.pluginRealPlayer = pluginRealPlayer; + return this; + } + + public MatomoRequestBuilder pluginSilverlight(Boolean pluginSilverlight) { + this.pluginSilverlight = pluginSilverlight; + return this; + } + + public MatomoRequestBuilder pluginWindowsMedia(Boolean pluginWindowsMedia) { + this.pluginWindowsMedia = pluginWindowsMedia; + return this; + } + + public MatomoRequestBuilder randomValue(String randomValue) { + this.randomValue = randomValue; + return this; + } + + public MatomoRequestBuilder referrerUrl(URL referrerUrl) { + this.referrerUrl = referrerUrl; + return this; + } + + public MatomoRequestBuilder referrerUrl(String referrerUrl) { + try { + this.referrerUrl = new URL(referrerUrl); + } catch (MalformedURLException e) { + throw new InvalidUrlException(e); + } + return this; + } + + public MatomoRequestBuilder requestDatetime(MatomoDate requestDatetime) { + this.requestDatetime = requestDatetime; + return this; + } + + public MatomoRequestBuilder required(Boolean required) { + this.required = required; + return this; + } + + public MatomoRequestBuilder responseAsImage(Boolean responseAsImage) { + this.responseAsImage = responseAsImage; + return this; + } + + public MatomoRequestBuilder searchCategory(String searchCategory) { + this.searchCategory = searchCategory; + return this; + } + + public MatomoRequestBuilder searchQuery(String searchQuery) { + this.searchQuery = searchQuery; + return this; + } + + public MatomoRequestBuilder searchResultsCount(Long searchResultsCount) { + this.searchResultsCount = searchResultsCount; + return this; + } + + public MatomoRequestBuilder trackBotRequests(Boolean trackBotRequests) { + this.trackBotRequests = trackBotRequests; + return this; + } + + public MatomoRequestBuilder userId(String userId) { + this.userId = userId; + return this; + } + + public MatomoRequestBuilder visitorCity(String visitorCity) { + this.visitorCity = visitorCity; + return this; + } + + public MatomoRequestBuilder visitorCountry(MatomoLocale visitorCountry) { + this.visitorCountry = visitorCountry; + return this; + } + + public MatomoRequestBuilder visitorCustomId(String visitorCustomId) { + this.visitorCustomId = visitorCustomId; + return this; + } + + public MatomoRequestBuilder visitorFirstVisitTimestamp(Long visitorFirstVisitTimestamp) { + this.visitorFirstVisitTimestamp = visitorFirstVisitTimestamp; + return this; + } + + public MatomoRequestBuilder visitorId(String visitorId) { + this.visitorId = visitorId; + return this; + } + + public MatomoRequestBuilder visitorIp(String visitorIp) { + this.visitorIp = visitorIp; + return this; + } + + public MatomoRequestBuilder visitorLatitude(Double visitorLatitude) { + this.visitorLatitude = visitorLatitude; + return this; + } + + public MatomoRequestBuilder visitorLongitude(Double visitorLongitude) { + this.visitorLongitude = visitorLongitude; + return this; + } + + public MatomoRequestBuilder visitorPreviousVisitTimestamp(Long visitorPreviousVisitTimestamp) { + this.visitorPreviousVisitTimestamp = visitorPreviousVisitTimestamp; + return this; + } + + public MatomoRequestBuilder visitorRegion(String visitorRegion) { + this.visitorRegion = visitorRegion; + return this; + } + + public MatomoRequestBuilder visitorVisitCount(Integer visitorVisitCount) { + this.visitorVisitCount = visitorVisitCount; + return this; + } + + public MatomoRequestBuilder visitCustomVariables(List visitCustomVariables) { + this.visitCustomVariables = visitCustomVariables; + return this; + } + + public MatomoRequestBuilder pageCustomVariables(List pageCustomVariables) { + this.pageCustomVariables = pageCustomVariables; + return this; + } + + public MatomoRequestBuilder customTrackingParameters(Map customTrackingParameters) { + this.customTrackingParameters = customTrackingParameters; + return this; + } + + public MatomoRequest build() { + MatomoRequest matomoRequest = new MatomoRequest(siteId, actionUrl); + if (actionName != null) { + matomoRequest.setActionName(actionName); + } + if (actionTime != null) { + matomoRequest.setActionTime(actionTime); + } + if (apiVersion != null) { + matomoRequest.setApiVersion(apiVersion); + } + if (authToken != null) { + matomoRequest.setAuthToken(authToken); + } + if (campaignKeyword != null) { + matomoRequest.setCampaignKeyword(campaignKeyword); + } + if (campaignName != null) { + matomoRequest.setCampaignName(campaignName); + } + if (characterSet != null) { + matomoRequest.setCharacterSet(characterSet); + } + if (contentInteraction != null) { + matomoRequest.setContentInteraction(contentInteraction); + } + if (contentName != null) { + matomoRequest.setContentName(contentName); + } + if (contentPiece != null) { + matomoRequest.setContentPiece(contentPiece); + } + if (contentTarget != null) { + matomoRequest.setContentTarget(contentTarget); + } + if (currentHour != null) { + matomoRequest.setCurrentHour(currentHour); + } + if (currentMinute != null) { + matomoRequest.setCurrentMinute(currentMinute); + } + if (currentSecond != null) { + matomoRequest.setCurrentSecond(currentSecond); + } + if (customTrackingParameters != null) { + for (Map.Entry customTrackingParameter : customTrackingParameters.entrySet()) { + matomoRequest.addCustomTrackingParameter(customTrackingParameter.getKey(), customTrackingParameter.getValue()); + } + } + if (deviceResolution != null) { + matomoRequest.setDeviceResolution(deviceResolution); + } + if (downloadUrl != null) { + matomoRequest.setDownloadUrl(downloadUrl); + } + if (ecommerceDiscount != null) { + matomoRequest.setEcommerceDiscount(ecommerceDiscount); + } + if (ecommerceId != null) { + matomoRequest.setEcommerceId(ecommerceId); + } + if (ecommerceLastOrderTimestamp != null) { + matomoRequest.setEcommerceLastOrderTimestamp(ecommerceLastOrderTimestamp); + } + if (ecommerceRevenue != null) { + matomoRequest.setEcommerceRevenue(ecommerceRevenue); + } + if (ecommerceShippingCost != null) { + matomoRequest.setEcommerceShippingCost(ecommerceShippingCost); + } + if (ecommerceSubtotal != null) { + matomoRequest.setEcommerceSubtotal(ecommerceSubtotal); + } + if (ecommerceTax != null) { + matomoRequest.setEcommerceTax(ecommerceTax); + } + if (eventAction != null) { + matomoRequest.setEventAction(eventAction); + } + if (eventCategory != null) { + matomoRequest.setEventCategory(eventCategory); + } + if (eventName != null) { + matomoRequest.setEventName(eventName); + } + if (eventValue != null) { + matomoRequest.setEventValue(eventValue); + } + if (goalId != null) { + matomoRequest.setGoalId(goalId); + } + if (goalRevenue != null) { + matomoRequest.setGoalRevenue(goalRevenue); + } + if (headerAcceptLanguage != null) { + matomoRequest.setHeaderAcceptLanguage(headerAcceptLanguage); + } + if (headerUserAgent != null) { + matomoRequest.setHeaderUserAgent(headerUserAgent); + } + if (newVisit != null) { + matomoRequest.setNewVisit(newVisit); + } + if (outlinkUrl != null) { + matomoRequest.setOutlinkUrl(outlinkUrl); + } + if (pageCustomVariables != null) { + for (int i = 0; i < pageCustomVariables.size(); i++) { + CustomVariable pageCustomVariable = pageCustomVariables.get(i); + matomoRequest.setPageCustomVariable(pageCustomVariable, i + 1); + } + } + if (pluginDirector != null) { + matomoRequest.setPluginDirector(pluginDirector); + } + if (pluginFlash != null) { + matomoRequest.setPluginFlash(pluginFlash); + } + if (pluginGears != null) { + matomoRequest.setPluginGears(pluginGears); + } + if (pluginJava != null) { + matomoRequest.setPluginJava(pluginJava); + } + if (pluginPDF != null) { + matomoRequest.setPluginPDF(pluginPDF); + } + if (pluginQuicktime != null) { + matomoRequest.setPluginQuicktime(pluginQuicktime); + } + if (pluginRealPlayer != null) { + matomoRequest.setPluginRealPlayer(pluginRealPlayer); + } + if (pluginSilverlight != null) { + matomoRequest.setPluginSilverlight(pluginSilverlight); + } + if (pluginWindowsMedia != null) { + matomoRequest.setPluginWindowsMedia(pluginWindowsMedia); + } + if (randomValue != null) { + matomoRequest.setRandomValue(randomValue); + } + if (referrerUrl != null) { + matomoRequest.setReferrerUrl(referrerUrl); + } + if (requestDatetime != null) { + matomoRequest.setRequestDatetime(requestDatetime); + } + if (required != null) { + matomoRequest.setRequired(required); + } + if (responseAsImage != null) { + matomoRequest.setResponseAsImage(responseAsImage); + } + if (searchCategory != null) { + matomoRequest.setSearchCategory(searchCategory); + } + if (searchQuery != null) { + matomoRequest.setSearchQuery(searchQuery); + } + if (searchResultsCount != null) { + matomoRequest.setSearchResultsCount(searchResultsCount); + } + if (trackBotRequests != null) { + matomoRequest.setTrackBotRequests(trackBotRequests); + } + if (visitCustomVariables != null) { + for (int i = 0; i < visitCustomVariables.size(); i++) { + CustomVariable visitCustomVariable = visitCustomVariables.get(i); + matomoRequest.setVisitCustomVariable(visitCustomVariable, i + 1); + } + } + if (userId != null) { + matomoRequest.setUserId(userId); + } + if (visitorCity != null) { + matomoRequest.setVisitorCity(visitorCity); + } + if (visitorCountry != null) { + matomoRequest.setVisitorCountry(visitorCountry); + } + if (visitorCustomId != null) { + matomoRequest.setVisitorCustomId(visitorCustomId); + } + if (visitorFirstVisitTimestamp != null) { + matomoRequest.setVisitorFirstVisitTimestamp(visitorFirstVisitTimestamp); + } + if (visitorId != null) { + matomoRequest.setVisitorId(visitorId); + } + if (visitorIp != null) { + matomoRequest.setVisitorIp(visitorIp); + } + if (visitorLatitude != null) { + matomoRequest.setVisitorLatitude(visitorLatitude); + } + if (visitorLongitude != null) { + matomoRequest.setVisitorLongitude(visitorLongitude); + } + if (visitorPreviousVisitTimestamp != null) { + matomoRequest.setVisitorPreviousVisitTimestamp(visitorPreviousVisitTimestamp); + } + if (visitorRegion != null) { + matomoRequest.setVisitorRegion(visitorRegion); + } + if (visitorVisitCount != null) { + matomoRequest.setVisitorVisitCount(visitorVisitCount); + } + return matomoRequest; + } + +} diff --git a/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/src/main/java/org/matomo/java/tracking/MatomoTracker.java new file mode 100644 index 00000000..4b0d8882 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -0,0 +1,271 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ +package org.matomo.java.tracking; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.concurrent.FutureCallback; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.concurrent.Future; + +/** + * A class that sends {@link MatomoRequest}s to a specified Matomo server. + * + * @author brettcsorba + */ +@Slf4j +public class MatomoTracker { + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private static final String AUTH_TOKEN = "token_auth"; + private static final String REQUESTS = "requests"; + private static final int DEFAULT_TIMEOUT = 5000; + private final URI hostUrl; + private final int timeout; + private final String proxyHost; + private final int proxyPort; + + /** + * Creates a tracker that will send {@link MatomoRequest}s to the specified + * Tracking HTTP API endpoint. + * + * @param hostUrl url endpoint to send requests to. Usually in the format + * http://your-matomo-domain.tld/matomo.php. + */ + public MatomoTracker(final String hostUrl) { + this(hostUrl, DEFAULT_TIMEOUT); + } + + /** + * Creates a tracker that will send {@link MatomoRequest}s to the specified + * Tracking HTTP API endpoint. + * + * @param hostUrl url endpoint to send requests to. Usually in the format + * http://your-matomo-domain.tld/matomo.php. + * @param timeout the timeout of the sent request in milliseconds + */ + public MatomoTracker(final String hostUrl, final int timeout) { + this(hostUrl, null, 0, timeout); + } + + public MatomoTracker(final String hostUrl, final String proxyHost, final int proxyPort, final int timeout) { + this.hostUrl = URI.create(hostUrl); + this.proxyHost = proxyHost; + this.proxyPort = proxyPort; + this.timeout = timeout; + } + + /** + * Creates a tracker that will send {@link MatomoRequest}s to the specified + * Tracking HTTP API endpoint via the provided proxy + * + * @param hostUrl url endpoint to send requests to. Usually in the format + * http://your-matomo-domain.tld/matomo.php. + * @param proxyHost url endpoint for the proxy + * @param proxyPort proxy server port number + */ + public MatomoTracker(final String hostUrl, final String proxyHost, final int proxyPort) { + this(hostUrl, proxyHost, proxyPort, DEFAULT_TIMEOUT); + } + + /** + * Send a request. + * + * @param request request to send + * @return the response from this request + * @throws IOException thrown if there was a problem with this connection + * @deprecated use sendRequestAsync instead + */ + @Deprecated + public HttpResponse sendRequest(final MatomoRequest request) throws IOException { + final HttpClient client = getHttpClient(); + try { + HttpUriRequest get = new HttpGet(new URIBuilder(hostUrl).addParameters(QueryParameters.fromMap(request.getParameters())).build()); + log.debug("Sending request via GET: {}", request); + return client.execute(get); + } catch (final URISyntaxException e) { + throw new IOException(e); + } + } + + /** + * Get a HTTP client. With proxy if a proxy is provided in the constructor. + * + * @return a HTTP client + */ + protected HttpClient getHttpClient() { + return HttpClientFactory.getInstanceFor(proxyHost, proxyPort, timeout); + } + + /** + * Send a request. + * + * @param request request to send + * @return future with response from this request + * @throws IOException thrown if there was a problem with this connection + */ + public Future sendRequestAsync(final MatomoRequest request) throws IOException { + return sendRequestAsync(request, null); + } + + /** + * Send a request. + * + * @param request request to send + * @param callback callback that gets executed when response arrives + * @return future with response from this request + * @throws IOException thrown if there was a problem with this connection + */ + public Future sendRequestAsync(final MatomoRequest request, FutureCallback callback) throws IOException { + final CloseableHttpAsyncClient client = getHttpAsyncClient(); + client.start(); + + try { + HttpUriRequest get = new HttpGet(new URIBuilder(hostUrl).addParameters(QueryParameters.fromMap(request.getParameters())).build()); + log.debug("Sending async request via GET: {}", request); + return client.execute(get, callback); + } catch (final URISyntaxException e) { + throw new IOException(e); + } + } + + /** + * Get an async HTTP client. With proxy if a proxy is provided in the constructor. + * + * @return an async HTTP client + */ + protected CloseableHttpAsyncClient getHttpAsyncClient() { + return HttpClientFactory.getAsyncInstanceFor(proxyHost, proxyPort, timeout); + } + + /** + * Send multiple requests in a single HTTP call. More efficient than sending + * several individual requests. + * + * @param requests the requests to send + * @return the response from these requests + * @throws IOException thrown if there was a problem with this connection + * @deprecated use sendBulkRequestAsync instead + */ + @Deprecated + public HttpResponse sendBulkRequest(final Iterable requests) throws IOException { + return sendBulkRequest(requests, null); + } + + /** + * Send multiple requests in a single HTTP call. More efficient than sending + * several individual requests. Specify the AuthToken if parameters that require + * an auth token is used. + * + * @param requests the requests to send + * @param authToken specify if any of the parameters use require AuthToken + * @return the response from these requests + * @throws IOException thrown if there was a problem with this connection + * @deprecated use sendBulkRequestAsync instead + */ + @Deprecated + public HttpResponse sendBulkRequest(final Iterable requests, final String authToken) throws IOException { + if (authToken != null && authToken.length() != MatomoRequest.AUTH_TOKEN_LENGTH) { + throw new IllegalArgumentException(authToken + " is not " + MatomoRequest.AUTH_TOKEN_LENGTH + " characters long."); + } + + HttpPost post = buildPost(requests, authToken); + final HttpClient client = getHttpClient(); + log.debug("Sending requests via POST: {}", requests); + return client.execute(post); + } + + private HttpPost buildPost(Iterable requests, String authToken) { + ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); + ArrayNode requestsNode = objectNode.putArray(REQUESTS); + for (final MatomoRequest request : requests) { + requestsNode.add(new URIBuilder().addParameters(QueryParameters.fromMap(request.getParameters())).toString()); + } + if (authToken != null) { + objectNode.put(AUTH_TOKEN, authToken); + } + HttpPost post = new HttpPost(hostUrl); + post.setEntity(new StringEntity(objectNode.toString(), ContentType.APPLICATION_JSON)); + return post; + } + + /** + * Send multiple requests in a single HTTP call. More efficient than sending + * several individual requests. + * + * @param requests the requests to send + * @return future with response from these requests + * @throws IOException thrown if there was a problem with this connection + */ + public Future sendBulkRequestAsync(final Iterable requests) throws IOException { + return sendBulkRequestAsync(requests, null, null); + } + + /** + * Send multiple requests in a single HTTP call. More efficient than sending + * several individual requests. Specify the AuthToken if parameters that require + * an auth token is used. + * + * @param requests the requests to send + * @param authToken specify if any of the parameters use require AuthToken + * @param callback callback that gets executed when response arrives + * @return the response from these requests + * @throws IOException thrown if there was a problem with this connection + */ + public Future sendBulkRequestAsync(final Iterable requests, final String authToken, FutureCallback callback) throws IOException { + if (authToken != null && authToken.length() != MatomoRequest.AUTH_TOKEN_LENGTH) { + throw new IllegalArgumentException(authToken + " is not " + MatomoRequest.AUTH_TOKEN_LENGTH + " characters long."); + } + HttpPost post = buildPost(requests, authToken); + final CloseableHttpAsyncClient client = getHttpAsyncClient(); + client.start(); + log.debug("Sending async requests via POST: {}", requests); + return client.execute(post, callback); + } + + /** + * Send multiple requests in a single HTTP call. More efficient than sending + * several individual requests. + * + * @param requests the requests to send + * @param callback callback that gets executed when response arrives + * @return future with response from these requests + * @throws IOException thrown if there was a problem with this connection + */ + public Future sendBulkRequestAsync(final Iterable requests, FutureCallback callback) throws IOException { + return sendBulkRequestAsync(requests, null, callback); + } + + /** + * Send multiple requests in a single HTTP call. More efficient than sending + * several individual requests. Specify the AuthToken if parameters that require + * an auth token is used. + * + * @param requests the requests to send + * @param authToken specify if any of the parameters use require AuthToken + * @return the response from these requests + * @throws IOException thrown if there was a problem with this connection + */ + public Future sendBulkRequestAsync(final Iterable requests, final String authToken) throws IOException { + return sendBulkRequestAsync(requests, authToken, null); + } +} diff --git a/src/main/java/org/matomo/java/tracking/QueryParameters.java b/src/main/java/org/matomo/java/tracking/QueryParameters.java new file mode 100644 index 00000000..2e1f94c3 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/QueryParameters.java @@ -0,0 +1,32 @@ +package org.matomo.java.tracking; + +import lombok.NonNull; +import org.apache.http.NameValuePair; +import org.apache.http.message.BasicNameValuePair; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +public final class QueryParameters { + + private QueryParameters() { + // utility + } + + @Nonnull + public static List fromMap(@NonNull Map> map) { + List queryParameters = new ArrayList<>(); + for (Map.Entry> entries : map.entrySet()) { + for (Object value : entries.getValue()) { + queryParameters.add(new BasicNameValuePair(entries.getKey(), value.toString())); + } + } + queryParameters.sort(Comparator.comparing(NameValuePair::getName)); + return queryParameters; + } + +} diff --git a/src/main/java/org/piwik/java/tracking/CustomVariable.java b/src/main/java/org/piwik/java/tracking/CustomVariable.java index 392b7473..48b3813a 100644 --- a/src/main/java/org/piwik/java/tracking/CustomVariable.java +++ b/src/main/java/org/piwik/java/tracking/CustomVariable.java @@ -1,52 +1,25 @@ /* - * Piwik Java Tracker + * Matomo Java Tracker * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ package org.piwik.java.tracking; +import org.matomo.java.tracking.MatomoRequest; + /** - * A user defined custom variable. - * * @author brettcsorba + * @deprecated Use {@link org.matomo.java.tracking.CustomVariable} instead. */ -public final class CustomVariable { - private final String key; - private final String value; +@Deprecated +public class CustomVariable extends org.matomo.java.tracking.CustomVariable { /** - * Create a new CustomVariable - * - * @param key the key of this CustomVariable - * @param value the value of this CustomVariable + * @deprecated Use {@link MatomoRequest} instead. */ + @Deprecated public CustomVariable(String key, String value) { - if (key == null) { - throw new NullPointerException("Key cannot be null."); - } - if (value == null) { - throw new NullPointerException("Value cannot be null."); - } - this.key = key; - this.value = value; - } - - /** - * Get the key of this CustomVariable - * - * @return the key of this CustomVariable - */ - public String getKey() { - return key; - } - - /** - * Get the value of this CustomVariable - * - * @return the value of this CustomVariable - */ - public String getValue() { - return value; + super(key, value); } } diff --git a/src/main/java/org/piwik/java/tracking/CustomVariableList.java b/src/main/java/org/piwik/java/tracking/CustomVariableList.java deleted file mode 100644 index a8ade45f..00000000 --- a/src/main/java/org/piwik/java/tracking/CustomVariableList.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Piwik Java Tracker - * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause - */ -package org.piwik.java.tracking; - -import javax.json.Json; -import javax.json.JsonArrayBuilder; -import javax.json.JsonObjectBuilder; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; - -/** - * @author brettcsorba - */ -class CustomVariableList { - private final Map map = new HashMap<>(); - - void add(CustomVariable cv) { - boolean found = false; - for (Entry entry : map.entrySet()) { - if (entry.getValue().getKey().equals(cv.getKey())) { - map.put(entry.getKey(), cv); - found = true; - } - } - if (!found) { - int i = 1; - while (map.containsKey(i)) { - ++i; - } - - map.put(i, cv); - } - } - - void add(CustomVariable cv, int index) { - if (index <= 0) { - throw new IllegalArgumentException("Index must be greater than 0."); - } - map.put(index, cv); - } - - CustomVariable get(int index) { - if (index <= 0) { - throw new IllegalArgumentException("Index must be greater than 0."); - } - return map.get(index); - } - - String get(String key) { - Iterator> i = map.entrySet().iterator(); - while (i.hasNext()) { - CustomVariable value = i.next().getValue(); - if (value.getKey().equals(key)) { - return value.getValue(); - } - } - return null; - } - - void remove(int index) { - map.remove(index); - } - - void remove(String key) { - Iterator> i = map.entrySet().iterator(); - while (i.hasNext()) { - Entry entry = i.next(); - if (entry.getValue().getKey().equals(key)) { - i.remove(); - } - } - } - - boolean isEmpty() { - return map.isEmpty(); - } - - @Override - public String toString() { - JsonObjectBuilder ob = Json.createObjectBuilder(); - - for (Entry entry : map.entrySet()) { - JsonArrayBuilder ab = Json.createArrayBuilder(); - ab.add(entry.getValue().getKey()); - ab.add(entry.getValue().getValue()); - ob.add(entry.getKey().toString(), ab); - } - - return ob.build().toString(); - } -} diff --git a/src/main/java/org/piwik/java/tracking/EcommerceItem.java b/src/main/java/org/piwik/java/tracking/EcommerceItem.java index 616d2e63..65b21446 100644 --- a/src/main/java/org/piwik/java/tracking/EcommerceItem.java +++ b/src/main/java/org/piwik/java/tracking/EcommerceItem.java @@ -1,169 +1,25 @@ /* - * Piwik Java Tracker + * Matomo Java Tracker * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ package org.piwik.java.tracking; -import javax.json.Json; -import javax.json.JsonArray; -import javax.json.JsonArrayBuilder; -import javax.json.JsonValue; +import org.matomo.java.tracking.MatomoRequest; /** - * Represents an item in an ecommerce order. - * * @author brettcsorba + * @deprecated Use {@link org.matomo.java.tracking.EcommerceItem} instead. */ -public class EcommerceItem implements JsonValue { - private String sku; - private String name; - private String category; - private Double price; - private Integer quantity; +@Deprecated +public class EcommerceItem extends org.matomo.java.tracking.EcommerceItem { /** - * Construct an EcommerceItem from its sku, name, category, price, and - * quantity of the order. - * - * @param sku the item's sku - * @param name the item's name - * @param category the item's category - * @param price the item's price - * @param quantity the quantity of this item in this order + * @deprecated Use {@link MatomoRequest} instead. */ + @Deprecated public EcommerceItem(String sku, String name, String category, Double price, Integer quantity) { - this.sku = sku; - this.name = name; - this.category = category; - this.price = price; - this.quantity = quantity; - } - - /** - * Get an item's sku. - * - * @return the item's sku - */ - public String getSku() { - return sku; - } - - /** - * Set an item's sku. - * - * @param sku the sku to set - */ - public void setSku(String sku) { - this.sku = sku; - } - - /** - * Get an item's name. - * - * @return the item's name - */ - public String getName() { - return name; - } - - /** - * Set an item's name. - * - * @param name the name to set - */ - public void setName(String name) { - this.name = name; - } - - /** - * Get an item's category. - * - * @return an item's category - */ - public String getCategory() { - return category; - } - - /** - * Set an item's category. - * - * @param category the category to set - */ - public void setCategory(String category) { - this.category = category; - } - - /** - * Get an item's price. - * - * @return an item's price - */ - public Double getPrice() { - return price; - } - - /** - * Set an item's price. - * - * @param price the price to set - */ - public void setPrice(Double price) { - this.price = price; - } - - /** - * Get the quantity of this item in this order. - * - * @return the quantity of this item in the order - */ - public Integer getQuantity() { - return quantity; - } - - /** - * Set the quantity of this item in this order - * - * @param quantity the quantity of this item to set - */ - public void setQuantity(Integer quantity) { - this.quantity = quantity; - } - - /** - * Get the JSON value type of EcommerceItem. - * - * @return ValueType.ARRAY - */ - @Override - public ValueType getValueType() { - return ValueType.ARRAY; - } - - /** - * Returns the value of this EcommerceItem as a JSON Array string. - * - * @return this as a JSON array string - */ - @Override - public String toString() { - return toJsonArray().toString(); - } - - /** - * Returns the value of this EcommerceItem as a JsonArray. - * - * @return this as a JsonArray - */ - JsonArray toJsonArray() { - JsonArrayBuilder ab = Json.createArrayBuilder(); - ab.add(sku); - ab.add(name); - ab.add(category); - ab.add(price); - ab.add(quantity); - - return ab.build(); + super(sku, name, category, price, quantity); } } diff --git a/src/main/java/org/piwik/java/tracking/HttpClientFactory.java b/src/main/java/org/piwik/java/tracking/HttpClientFactory.java deleted file mode 100644 index 73c5f196..00000000 --- a/src/main/java/org/piwik/java/tracking/HttpClientFactory.java +++ /dev/null @@ -1,104 +0,0 @@ -package org.piwik.java.tracking; - -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import org.apache.http.HttpHost; -import org.apache.http.client.HttpClient; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.DefaultProxyRoutePlanner; -import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; -import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; - -/** - * Internal factory for providing instances of HTTP clients. - * Especially {@linkAsyncHttpClient} instances are intended to be global resources that share the same lifecycle as the application. - * For details see Apache documentation. - * - * @author norbertroamsys - */ -class HttpClientFactory { - - /** - * Internal key class for caching {@link CloseableHttpAsyncClient} instances. - */ - private static final class KeyEntry { - - private final String proxyHost; - private final int proxyPort, timeout; - - public KeyEntry(final String proxyHost, final int proxyPort, final int timeout) { - this.proxyHost = proxyHost; - this.proxyPort = proxyPort; - this.timeout = timeout; - } - - @Override - public int hashCode() { - return Objects.hash(proxyHost, proxyPort, timeout); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } else if (obj == null) { - return false; - } else if (obj instanceof KeyEntry) { - final KeyEntry other = (KeyEntry) obj; - return Objects.equals(proxyHost, other.proxyHost) && proxyPort == other.proxyPort && timeout == other.timeout; - } else { - return false; - } - } - } - - private static final Map ASYNC_INSTANCES = new HashMap<>(); - - /** - * Factory for getting a synchronous client by proxy and timeout configuration. - * The clients will be created on each call. - * - * @param proxyHost the proxy host - * @param proxyPort the proxy port - * @param timeout the timeout - * @return the created client - */ - public static HttpClient getInstanceFor(final String proxyHost, final int proxyPort, final int timeout) { - return HttpClientBuilder.create().setRoutePlanner(createRoutePlanner(proxyHost, proxyPort)).setDefaultRequestConfig(createRequestConfig(timeout)).build(); - } - - /** - * Factory for getting a asynchronous client by proxy and timeout configuration. - * The clients will be created and cached as a singleton instance. - * - * @param proxyHost the proxy host - * @param proxyPort the proxy port - * @param timeout the timeout - * @return the created client - */ - public static synchronized CloseableHttpAsyncClient getAsyncInstanceFor(final String proxyHost, final int proxyPort, final int timeout) { - return ASYNC_INSTANCES.computeIfAbsent(new KeyEntry(proxyHost, proxyPort, timeout), key -> - HttpAsyncClientBuilder.create().setRoutePlanner(createRoutePlanner(key.proxyHost, key.proxyPort)).setDefaultRequestConfig(createRequestConfig(key.timeout)).build()); - } - - private static DefaultProxyRoutePlanner createRoutePlanner(final String proxyHost, final int proxyPort) { - if (proxyHost != null && proxyPort != 0) { - final HttpHost proxy = new HttpHost(proxyHost, proxyPort); - final DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy); - return routePlanner; - } else { - return null; - } - } - - private static RequestConfig createRequestConfig(final int timeout) { - final RequestConfig.Builder config = RequestConfig.custom() - .setConnectTimeout(timeout) - .setConnectionRequestTimeout(timeout) - .setSocketTimeout(timeout); - return config.build(); - } - -} diff --git a/src/main/java/org/piwik/java/tracking/PiwikDate.java b/src/main/java/org/piwik/java/tracking/PiwikDate.java index 66fe885c..6105bd8d 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikDate.java +++ b/src/main/java/org/piwik/java/tracking/PiwikDate.java @@ -1,65 +1,46 @@ /* - * Piwik Java Tracker + * Matomo Java Tracker * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ package org.piwik.java.tracking; -import java.text.SimpleDateFormat; -import java.util.Date; +import org.matomo.java.tracking.MatomoDate; + +import java.time.ZoneId; import java.util.TimeZone; /** - * A datetime object that will return the datetime in the format {@code yyyy-MM-dd hh:mm:ss}. - * * @author brettcsorba + * @deprecated Use {@link MatomoDate} instead. */ -public class PiwikDate extends Date { - SimpleDateFormat format; +@Deprecated +public class PiwikDate extends MatomoDate { /** - * Allocates a Date object and initializes it so that it represents the time - * at which it was allocated, measured to the nearest millisecond. + * @author brettcsorba + * @deprecated Use {@link MatomoDate} instead. */ public PiwikDate() { super(); - format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - format.setTimeZone(TimeZone.getTimeZone("UTC")); } /** - * Allocates a Date object and initializes it to represent the specified number - * of milliseconds since the standard base time known as "the epoch", namely - * January 1, 1970, 00:00:00 GMT. - * - * @param date the milliseconds since January 1, 1970, 00:00:00 GMT. + * @author brettcsorba + * @deprecated Use {@link MatomoDate} instead. */ - public PiwikDate(long date) { - super(date); - format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - format.setTimeZone(TimeZone.getTimeZone("UTC")); + public PiwikDate(long epochMilli) { + super(epochMilli); } /** - * Sets the time zone of the String that will be returned by {@link #toString()}. - * Defaults to UTC. - * - * @param zone the TimeZone to set + * @author brettcsorba + * @deprecated Use {@link MatomoDate#setTimeZone(ZoneId)} instead. */ + @Deprecated public void setTimeZone(TimeZone zone) { - format.setTimeZone(zone); + setTimeZone(zone.toZoneId()); } - /** - * Converts this PiwikDate object to a String of the form:
- *
- * {@code yyyy-MM-dd hh:mm:ss}. - * - * @return a string representation of this PiwikDate - */ - @Override - public String toString() { - return format.format(this); - } } diff --git a/src/main/java/org/piwik/java/tracking/PiwikJsonArray.java b/src/main/java/org/piwik/java/tracking/PiwikJsonArray.java deleted file mode 100644 index bdf9e6ee..00000000 --- a/src/main/java/org/piwik/java/tracking/PiwikJsonArray.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Piwik Java Tracker - * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause - */ -package org.piwik.java.tracking; - -import javax.json.Json; -import javax.json.JsonArrayBuilder; -import javax.json.JsonValue; -import java.util.ArrayList; -import java.util.List; - -/** - * Object representing a JSON array required by some Piwik query parameters. - * - * @author brettcsorba - */ -public class PiwikJsonArray { - List list = new ArrayList<>(); - - /** - * Get the value stored at the specified index. - * - * @param index the index of the value to return - * @return the value stored at the specified index - */ - public JsonValue get(int index) { - return list.get(index); - } - - /** - * Add a value to the end of this array. - * - * @param value value to add to the end of the array - */ - public void add(JsonValue value) { - list.add(value); - } - - /** - * Set the value at the specified index to the specified value. - * - * @param index the index of the value to set - * @param value the value to set at the specified index - */ - public void set(int index, JsonValue value) { - list.set(index, value); - } - - /** - * Returns a JSON encoded array string representing this object. - * - * @return returns the current array as a JSON encode string - */ - @Override - public String toString() { - JsonArrayBuilder ab = Json.createArrayBuilder(); - - for (int x = 0; x < list.size(); ++x) { - JsonValue value = list.get(x); - if (value instanceof EcommerceItem) { - ab.add(((EcommerceItem) value).toJsonArray()); - } else { - ab.add(value); - } - } - - return ab.build().toString(); - } -} diff --git a/src/main/java/org/piwik/java/tracking/PiwikJsonObject.java b/src/main/java/org/piwik/java/tracking/PiwikJsonObject.java deleted file mode 100644 index 0b83a651..00000000 --- a/src/main/java/org/piwik/java/tracking/PiwikJsonObject.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Piwik Java Tracker - * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause - */ -package org.piwik.java.tracking; - -import javax.json.Json; -import javax.json.JsonArrayBuilder; -import javax.json.JsonObjectBuilder; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Map.Entry; - -/** - * Object representing the custom variable array required by some - * Piwik query parameters. An array is represented by an object that has - * the index of the entry as the key (1 indexed) and a 2 entry array representing - * a custom key and custom value as the value. - * - * @author brettcsorba - */ -@Deprecated -public class PiwikJsonObject { - Map map = new LinkedHashMap<>(); - - /** - * Gets the custom value stored at this custom key. - * - * @param key key used to lookup value - * @return value stored at specified key, null if not present - */ - public String get(String key) { - return map.get(key); - } - - /** - * Returns true if this object contains no custom key-value pairs. - * - * @return true if this object contains no custom key-value pairs - */ - public boolean isEmpty() { - return size() == 0; - } - - /** - * Puts a custom value at this custom key. - * - * @param key key to store value at - * @param value value to store at specified key - * @return previous value stored at key if present, null otherwise - */ - public String put(String key, String value) { - return map.put(key, value); - } - - /** - * Removes the custom value stored at this custom key. - * - * @param key key used to lookup value to remove - * @return the value that was removed, null if no value was there to remove - */ - public String remove(String key) { - return map.remove(key); - } - - /** - * Returns the number of custom key-value pairs. - * - * @return the number of custom key-value pairs - */ - public int size() { - return map.size(); - } - - /** - * Produces the JSON string representing this object.
- *
- * For example, if the following values were put into the object
- *
- * {@code ("key1", "value1") } and {@code ("key2", "value2") }
- *
- * {@code {"1": ["key1", "value1"], "2": ["key2": "value2"]} }
- *
- * would be produced. The produced JSON will be ordered according to the - * order the values were put in. Removing an object will cause the values - * to backfill accordingly.
- *
- * For example, if the following values were put into the object
- *
- * {@code ("key1", "value1")}, {@code ("key2", "value2")}, and {@code ("key3", "value3")}
- *
- * and {@code ("key2", "value2") } was then removed, then
- *
- * {@code {"1": ["key1", "value1"], "2": ["key3": "value3"]} }
- *
- * would be produced. - *
- * - * @return the JSON string representation of this object - */ - @Override - public String toString() { - JsonObjectBuilder ob = Json.createObjectBuilder(); - - int x = 1; - for (Entry entry : map.entrySet()) { - JsonArrayBuilder ab = Json.createArrayBuilder(); - ab.add(entry.getKey()); - ab.add(entry.getValue()); - ob.add(Integer.toString(x++), ab); - } - - return ob.build().toString(); - } -} diff --git a/src/main/java/org/piwik/java/tracking/PiwikLocale.java b/src/main/java/org/piwik/java/tracking/PiwikLocale.java index a573026a..686bbfd7 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikLocale.java +++ b/src/main/java/org/piwik/java/tracking/PiwikLocale.java @@ -1,55 +1,27 @@ /* - * Piwik Java Tracker + * Matomo Java Tracker * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ package org.piwik.java.tracking; +import org.matomo.java.tracking.MatomoLocale; + import java.util.Locale; /** - * Object representing a locale required by some Piwik query parameters. - * * @author brettcsorba + * @deprecated Use {@link org.matomo.java.tracking.MatomoLocale} instead. */ -public class PiwikLocale { - private Locale locale; +@Deprecated +public class PiwikLocale extends MatomoLocale { /** - * Create this PiwikLocale from a Locale. - * - * @param locale the locale to create this object from + * @deprecated Use {@link MatomoLocale} instead. */ + @Deprecated public PiwikLocale(Locale locale) { - this.locale = locale; - } - - /** - * Gets the locale. - * - * @return the locale - */ - public Locale getLocale() { - return locale; - } - - /** - * Sets the locale. - * - * @param locale the locale to set - */ - public void setLocale(Locale locale) { - this.locale = locale; - } - - /** - * Returns the locale's lowercase country code. - * - * @return the locale's lowercase country code - */ - @Override - public String toString() { - return locale.getCountry().toLowerCase(); + super(locale); } } diff --git a/src/main/java/org/piwik/java/tracking/PiwikRequest.java b/src/main/java/org/piwik/java/tracking/PiwikRequest.java index d1d32440..b7ad9181 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikRequest.java +++ b/src/main/java/org/piwik/java/tracking/PiwikRequest.java @@ -1,2115 +1,27 @@ /* - * Piwik Java Tracker + * Matomo Java Tracker * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ package org.piwik.java.tracking; -import javax.json.JsonValue; -import javax.xml.bind.DatatypeConverter; -import javax.xml.bind.TypeConstraintException; -import java.io.UnsupportedEncodingException; +import org.matomo.java.tracking.MatomoRequest; + import java.net.URL; -import java.net.URLEncoder; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Random; /** - * A class that implements the - * Piwik Tracking HTTP API. These requests can be sent using {@link PiwikTracker}. - * * @author brettcsorba + * @deprecated Use {@link MatomoRequest} instead. */ -public class PiwikRequest { - public static final int ID_LENGTH = 16; - public static final int AUTH_TOKEN_LENGTH = 32; - - private static final String ACTION_NAME = "action_name"; - private static final String ACTION_TIME = "gt_ms"; - private static final String ACTION_URL = "url"; - private static final String API_VERSION = "apiv"; - private static final String AUTH_TOKEN = "token_auth"; - private static final String CAMPAIGN_KEYWORD = "_rck"; - private static final String CAMPAIGN_NAME = "_rcn"; - private static final String CHARACTER_SET = "cs"; - private static final String CONTENT_INTERACTION = "c_i"; - private static final String CONTENT_NAME = "c_n"; - private static final String CONTENT_PIECE = "c_p"; - private static final String CONTENT_TARGET = "c_t"; - private static final String CURRENT_HOUR = "h"; - private static final String CURRENT_MINUTE = "m"; - private static final String CURRENT_SECOND = "s"; - private static final String DEVICE_RESOLUTION = "res"; - private static final String DOWNLOAD_URL = "download"; - private static final String ECOMMERCE_DISCOUNT = "ec_dt"; - private static final String ECOMMERCE_ID = "ec_id"; - private static final String ECOMMERCE_ITEMS = "ec_items"; - private static final String ECOMMERCE_LAST_ORDER_TIMESTAMP = "_ects"; - private static final String ECOMMERCE_REVENUE = "revenue"; - private static final String ECOMMERCE_SHIPPING_COST = "ec_sh"; - private static final String ECOMMERCE_SUBTOTAL = "ec_st"; - private static final String ECOMMERCE_TAX = "ec_tx"; - private static final String EVENT_ACTION = "e_a"; - private static final String EVENT_CATEGORY = "e_c"; - private static final String EVENT_NAME = "e_n"; - private static final String EVENT_VALUE = "e_v"; - private static final String HEADER_ACCEPT_LANGUAGE = "lang"; - private static final String GOAL_ID = "idgoal"; - private static final String GOAL_REVENUE = "revenue"; - private static final String HEADER_USER_AGENT = "ua"; - private static final String NEW_VISIT = "new_visit"; - private static final String OUTLINK_URL = "link"; - private static final String PAGE_CUSTOM_VARIABLE = "cvar"; - private static final String PLUGIN_DIRECTOR = "dir"; - private static final String PLUGIN_FLASH = "fla"; - private static final String PLUGIN_GEARS = "gears"; - private static final String PLUGIN_JAVA = "java"; - private static final String PLUGIN_PDF = "pdf"; - private static final String PLUGIN_QUICKTIME = "qt"; - private static final String PLUGIN_REAL_PLAYER = "realp"; - private static final String PLUGIN_SILVERLIGHT = "ag"; - private static final String PLUGIN_WINDOWS_MEDIA = "wma"; - private static final String RANDOM_VALUE = "rand"; - private static final String REFERRER_URL = "urlref"; - private static final String REQUEST_DATETIME = "cdt"; - private static final String REQUIRED = "rec"; - private static final String RESPONSE_AS_IMAGE = "send_image"; - private static final String SEARCH_CATEGORY = "search_cat"; - private static final String SEARCH_QUERY = "search"; - private static final String SEARCH_RESULTS_COUNT = "search_count"; - private static final String SITE_ID = "idsite"; - private static final String TRACK_BOT_REQUESTS = "bots"; - private static final String VISIT_CUSTOM_VARIABLE = "_cvar"; - private static final String USER_ID = "uid"; - private static final String VISITOR_CITY = "city"; - private static final String VISITOR_COUNTRY = "country"; - private static final String VISITOR_CUSTOM_ID = "cid"; - private static final String VISITOR_FIRST_VISIT_TIMESTAMP = "_idts"; - private static final String VISITOR_ID = "_id"; - private static final String VISITOR_IP = "cip"; - private static final String VISITOR_LATITUDE = "lat"; - private static final String VISITOR_LONGITUDE = "long"; - private static final String VISITOR_PREVIOUS_VISIT_TIMESTAMP = "_viewts"; - private static final String VISITOR_REGION = "region"; - private static final String VISITOR_VISIT_COUNT = "_idvc"; - - private static final int RANDOM_VALUE_LENGTH = 20; - private static final long REQUEST_DATETIME_AUTH_LIMIT = 14400000L; - - private final Map parameters = new HashMap<>(); - private final Map customTrackingParameters = new HashMap<>(); +@Deprecated +public class PiwikRequest extends MatomoRequest { /** - * Create a new request from the id of the site being tracked and the full - * url for the current action. This constructor also sets: - *
-   * {@code
-   * Required = true
-   * Visior Id = random 16 character hex string
-   * Random Value = random 20 character hex string
-   * API version = 1
-   * Response as Image = false
-   * }
-   * 
- * Overwrite these values yourself as desired. - * - * @param siteId the id of the website we're tracking a visit/action for - * @param actionUrl the full URL for the current action + * @deprecated Use {@link MatomoRequest} instead. */ + @Deprecated public PiwikRequest(Integer siteId, URL actionUrl) { - setParameter(SITE_ID, siteId); - setBooleanParameter(REQUIRED, true); - setParameter(ACTION_URL, actionUrl); - setParameter(VISITOR_ID, getRandomHexString(ID_LENGTH)); - setParameter(RANDOM_VALUE, getRandomHexString(RANDOM_VALUE_LENGTH)); - setParameter(API_VERSION, "1"); - setBooleanParameter(RESPONSE_AS_IMAGE, false); - } - - /** - * Get the title of the action being tracked - * - * @return the title of the action being tracked - */ - - public String getActionName() { - return (String) getParameter(ACTION_NAME); - } - - /** - * Set the title of the action being tracked. It is possible to - * use slashes / - * to set one or several categories for this action. - * For example, Help / Feedback - * will create the Action Feedback in the category Help. - * - * @param actionName the title of the action to set. A null value will remove this parameter - */ - public void setActionName(String actionName) { - setParameter(ACTION_NAME, actionName); - } - - /** - * Get the amount of time it took the server to generate this action, in milliseconds. - * - * @return the amount of time - */ - public Long getActionTime() { - return (Long) getParameter(ACTION_TIME); - } - - /** - * Set the amount of time it took the server to generate this action, in milliseconds. - * This value is used to process the - * Page speed report - * Avg. generation time column in the Page URL and Page Title reports, - * as well as a site wide running average of the speed of your server. - * - * @param actionTime the amount of time to set. A null value will remove this parameter - */ - public void setActionTime(Long actionTime) { - setParameter(ACTION_TIME, actionTime); - } - - /** - * Get the full URL for the current action. - * - * @return the full URL - * @throws TypeConstraintException if the stored Action URL is a string - */ - public URL getActionUrl() { - return returnAsUrl(ACTION_URL, "Action URL", "getActionUrlAsString"); - } - - /** - * Get the full URL for the current action. - * - * @return the full URL - * @throws TypeConstraintException if the stored Action URL is a URL - */ - public String getActionUrlAsString() { - return returnAsString(ACTION_URL, "Action URL", "getActionUrl"); - } - - /** - * Set the full URL for the current action. - * - * @param actionUrl the full URL to set. A null value will remove this parameter - */ - public void setActionUrl(URL actionUrl) { - setParameter(ACTION_URL, actionUrl); - } - - /** - * Set the full URL for the current action. - * - * @param actionUrl the full URL to set. A null value will remove this parameter - */ - public void setActionUrlWithString(String actionUrl) { - setParameter(ACTION_URL, actionUrl); - } - - /** - * Get the api version - * - * @return the api version - */ - public String getApiVersion() { - return (String) getParameter(API_VERSION); - } - - /** - * Set the api version to use (currently always set to 1) - * - * @param apiVersion the api version to set. A null value will remove this parameter - */ - public void setApiVersion(String apiVersion) { - setParameter(API_VERSION, apiVersion); - } - - /** - * Get the authorization key. - * - * @return the authorization key - */ - public String getAuthToken() { - return (String) getParameter(AUTH_TOKEN); - } - - /** - * Set the {@value #AUTH_TOKEN_LENGTH} character authorization key used to authenticate the API request. - * - * @param authToken the authorization key to set. A null value will remove this parameter - */ - public void setAuthToken(String authToken) { - if (authToken != null && authToken.length() != AUTH_TOKEN_LENGTH) { - throw new IllegalArgumentException(authToken + " is not " + AUTH_TOKEN_LENGTH + " characters long."); - } - setParameter(AUTH_TOKEN, authToken); - } - - /** - * Verifies that AuthToken has been set for this request. Will throw an - * {@link IllegalStateException} if not. - */ - public void verifyAuthTokenSet() { - if (getAuthToken() == null) { - throw new IllegalStateException("AuthToken must be set before this value can be set."); - } - } - - /** - * Get the campaign keyword - * - * @return the campaign keyword - */ - public String getCampaignKeyword() { - return (String) getParameter(CAMPAIGN_KEYWORD); - } - - /** - * Set the Campaign Keyword (see - * Tracking Campaigns). - * Used to populate the Referrers > Campaigns report (clicking on a - * campaign loads all keywords for this campaign). Note: this parameter - * will only be used for the first pageview of a visit. - * - * @param campaignKeyword the campaign keyword to set. A null value will remove this parameter - */ - public void setCampaignKeyword(String campaignKeyword) { - setParameter(CAMPAIGN_KEYWORD, campaignKeyword); - } - - /** - * Get the campaign name - * - * @return the campaign name - */ - public String getCampaignName() { - return (String) getParameter(CAMPAIGN_NAME); - } - - /** - * Set the Campaign Name (see - * Tracking Campaigns). - * Used to populate the Referrers > Campaigns report. Note: this parameter - * will only be used for the first pageview of a visit. - * - * @param campaignName the campaign name to set. A null value will remove this parameter - */ - public void setCampaignName(String campaignName) { - setParameter(CAMPAIGN_NAME, campaignName); - } - - /** - * Get the charset of the page being tracked - * - * @return the charset - */ - public Charset getCharacterSet() { - return (Charset) getParameter(CHARACTER_SET); - } - - /** - * The charset of the page being tracked. Specify the charset if the data - * you send to Piwik is encoded in a different character set than the default - * utf-8. - * - * @param characterSet the charset to set. A null value will remove this parameter - */ - public void setCharacterSet(Charset characterSet) { - setParameter(CHARACTER_SET, characterSet); - } - - /** - * Get the name of the interaction with the content - * - * @return the name of the interaction - */ - public String getContentInteraction() { - return (String) getParameter(CONTENT_INTERACTION); - } - - /** - * Set the name of the interaction with the content. For instance a 'click'. - * - * @param contentInteraction the name of the interaction to set. A null value will remove this parameter - */ - public void setContentInteraction(String contentInteraction) { - setParameter(CONTENT_INTERACTION, contentInteraction); - } - - /** - * Get the name of the content - * - * @return the name - */ - public String getContentName() { - return (String) getParameter(CONTENT_NAME); - } - - /** - * Set the name of the content. For instance 'Ad Foo Bar'. - * - * @param contentName the name to set. A null value will remove this parameter - */ - public void setContentName(String contentName) { - setParameter(CONTENT_NAME, contentName); - } - - /** - * Get the content piece. - * - * @return the content piece. - */ - public String getContentPiece() { - return (String) getParameter(CONTENT_PIECE); - } - - /** - * Set the actual content piece. For instance the path to an image, video, audio, any text. - * - * @param contentPiece the content piece to set. A null value will remove this parameter - */ - public void setContentPiece(String contentPiece) { - setParameter(CONTENT_PIECE, contentPiece); - } - - /** - * Get the content target - * - * @return the target - * @throws TypeConstraintException if the stored Content Target is a string - */ - public URL getContentTarget() { - return returnAsUrl(CONTENT_TARGET, "Content Target", "getContentTargetAsString"); - } - - /** - * Get the content target - * - * @return the target - * @throws TypeConstraintException if the stored Content Target is a URL - */ - public String getContentTargetAsString() { - return returnAsString(CONTENT_TARGET, "Content Target", "getContentTarget"); - } - - /** - * Set the target of the content. For instance the URL of a landing page. - * - * @param contentTarget the target to set. A null value will remove this parameter - */ - public void setContentTarget(URL contentTarget) { - setParameter(CONTENT_TARGET, contentTarget); - } - - /** - * Set the target of the content. For instance the URL of a landing page. - * - * @param contentTarget the target to set. A null value will remove this parameter - */ - public void setContentTargetWithString(String contentTarget) { - setParameter(CONTENT_TARGET, contentTarget); - } - - /** - * Get the current hour. - * - * @return the current hour - */ - public Integer getCurrentHour() { - return (Integer) getParameter(CURRENT_HOUR); - } - - /** - * Set the current hour (local time). - * - * @param currentHour the hour to set. A null value will remove this parameter - */ - public void setCurrentHour(Integer currentHour) { - setParameter(CURRENT_HOUR, currentHour); - } - - /** - * Get the current minute. - * - * @return the current minute - */ - public Integer getCurrentMinute() { - return (Integer) getParameter(CURRENT_MINUTE); - } - - /** - * Set the current minute (local time). - * - * @param currentMinute the minute to set. A null value will remove this parameter - */ - public void setCurrentMinute(Integer currentMinute) { - setParameter(CURRENT_MINUTE, currentMinute); - } - - /** - * Get the current second - * - * @return the current second - */ - public Integer getCurrentSecond() { - return (Integer) getParameter(CURRENT_SECOND); - } - - /** - * Set the current second (local time). - * - * @param currentSecond the second to set. A null value will remove this parameter - */ - public void setCurrentSecond(Integer currentSecond) { - setParameter(CURRENT_SECOND, currentSecond); - } - - /** - * Gets the list of objects currently stored at the specified custom tracking - * parameter. An empty list will be returned if there are no objects set at - * that key. - * - * @param key the key of the parameter whose list of objects to get. Cannot be null - * @return the list of objects currently stored at the specified key - */ - public List getCustomTrackingParameter(String key) { - if (key == null) { - throw new NullPointerException("Key cannot be null."); - } - List l = customTrackingParameters.get(key); - if (l == null) { - return new ArrayList(0); - } - return new ArrayList(l); - } - - /** - * Set a custom tracking parameter whose toString() value will be sent to - * the Piwik server. These parameters are stored separately from named Piwik - * parameters, meaning it is not possible to overwrite or clear named Piwik - * parameters with this method. A custom parameter that has the same name - * as a named Piwik parameter will be sent in addition to that named parameter. - * - * @param key the parameter's key. Cannot be null - * @param value the parameter's value. Removes the parameter if null - */ - public void setCustomTrackingParameter(String key, Object value) { - if (key == null) { - throw new NullPointerException("Key cannot be null."); - } - if (value == null) { - customTrackingParameters.remove(key); - } else { - List l = new ArrayList(); - l.add(value); - customTrackingParameters.put(key, l); - } - } - - /** - * Add a custom tracking parameter to the specified key. This allows users - * to have multiple parameters with the same name and different values, - * commonly used during situations where list parameters are needed - * - * @param key the parameter's key. Cannot be null - * @param value the parameter's value. Cannot be null - */ - public void addCustomTrackingParameter(String key, Object value) { - if (key == null) { - throw new NullPointerException("Key cannot be null."); - } - if (value == null) { - throw new NullPointerException("Cannot add a null custom tracking parameter."); - } else { - List l = customTrackingParameters.get(key); - if (l == null) { - l = new ArrayList(); - customTrackingParameters.put(key, l); - } - l.add(value); - } - } - - /** - * Removes all custom tracking parameters - */ - public void clearCustomTrackingParameter() { - customTrackingParameters.clear(); - } - - /** - * Get the resolution of the device - * - * @return the resolution - */ - public String getDeviceResolution() { - return (String) getParameter(DEVICE_RESOLUTION); - } - - /** - * Set the resolution of the device the visitor is using, eg 1280x1024. - * - * @param deviceResolution the resolution to set. A null value will remove this parameter - */ - public void setDeviceResolution(String deviceResolution) { - setParameter(DEVICE_RESOLUTION, deviceResolution); - } - - /** - * Get the url of a file the user had downloaded - * - * @return the url - * @throws TypeConstraintException if the stored Download URL is a String - */ - public URL getDownloadUrl() { - return returnAsUrl(DOWNLOAD_URL, "Download URL", "getDownloadUrlAsString"); - } - - /** - * Get the url of a file the user had downloaded - * - * @return the url - * @throws TypeConstraintException if the stored Download URL is a URL - */ - public String getDownloadUrlAsString() { - return returnAsString(DOWNLOAD_URL, "Download URL", "getDownloadUrl"); - } - - /** - * Set the url of a file the user has downloaded. Used for tracking downloads. - * We recommend to also set the url parameter to this same value. - * - * @param downloadUrl the url to set. A null value will remove this parameter - */ - public void setDownloadUrl(URL downloadUrl) { - setParameter(DOWNLOAD_URL, downloadUrl); - } - - /** - * Set the url of a file the user has downloaded. Used for tracking downloads. - * We recommend to also set the url parameter to this same value. - * - * @param downloadUrl the url to set. A null value will remove this parameter - */ - public void setDownloadUrlWithString(String downloadUrl) { - setParameter(DOWNLOAD_URL, downloadUrl); - } - - /** - * Sets idgoal=0 in the request to track an ecommerce interaction: - * cart update or an ecommerce order. - */ - public void enableEcommerce() { - setGoalId(0); - } - - /** - * Verifies that Ecommerce has been enabled for the request. Will throw an - * {@link IllegalStateException} if not. - */ - public void verifyEcommerceEnabled() { - if (getGoalId() == null || getGoalId() != 0) { - throw new IllegalStateException("GoalId must be \"0\". Try calling enableEcommerce first before calling this method."); - } - } - - /** - * Verifies that Ecommerce has been enabled and that Ecommerce Id and - * Ecommerce Revenue have been set for the request. Will throw an - * {@link IllegalStateException} if not. - */ - public void verifyEcommerceState() { - verifyEcommerceEnabled(); - if (getEcommerceId() == null) { - throw new IllegalStateException("EcommerceId must be set before this value can be set."); - } - if (getEcommerceRevenue() == null) { - throw new IllegalStateException("EcommerceRevenue must be set before this value can be set."); - } - } - - /** - * Get the discount offered. - * - * @return the discount - */ - public Double getEcommerceDiscount() { - return (Double) getParameter(ECOMMERCE_DISCOUNT); - } - - /** - * Set the discount offered. Ecommerce must be enabled, and EcommerceId and - * EcommerceRevenue must first be set. - * - * @param discount the discount to set. A null value will remove this parameter - */ - public void setEcommerceDiscount(Double discount) { - if (discount != null) { - verifyEcommerceState(); - } - setParameter(ECOMMERCE_DISCOUNT, discount); - } - - /** - * Get the id of this order. - * - * @return the id - */ - public String getEcommerceId() { - return (String) getParameter(ECOMMERCE_ID); - } - - /** - * Set the unique string identifier for the ecommerce order (required when - * tracking an ecommerce order). Ecommerce must be enabled. - * - * @param id the id to set. A null value will remove this parameter - */ - public void setEcommerceId(String id) { - if (id != null) { - verifyEcommerceEnabled(); - } - setParameter(ECOMMERCE_ID, id); - } - - /** - * Get the {@link EcommerceItem} at the specified index - * - * @param index the index of the {@link EcommerceItem} to return - * @return the {@link EcommerceItem} at the specified index - */ - public EcommerceItem getEcommerceItem(int index) { - return (EcommerceItem) getFromJsonArray(ECOMMERCE_ITEMS, index); - } - - /** - * Add an {@link EcommerceItem} to this order. Ecommerce must be enabled, - * and EcommerceId and EcommerceRevenue must first be set. - * - * @param item the {@link EcommerceItem} to add. Cannot be null - */ - public void addEcommerceItem(EcommerceItem item) { - if (item != null) { - verifyEcommerceState(); - } - addToJsonArray(ECOMMERCE_ITEMS, item); - } - - /** - * Clears all {@link EcommerceItem} from this order. - */ - public void clearEcommerceItems() { - removeJsonArray(ECOMMERCE_ITEMS); - } - - /** - * Get the timestamp of the customer's last ecommerce order - * - * @return the timestamp - */ - public Long getEcommerceLastOrderTimestamp() { - return (Long) getParameter(ECOMMERCE_LAST_ORDER_TIMESTAMP); - } - - /** - * Set the UNUX timestamp of this customer's last ecommerce order. This value - * is used to process the "Days since last order" report. Ecommerce must be - * enabled, and EcommerceId and EcommerceRevenue must first be set. - * - * @param timestamp the timestamp to set. A null value will remove this parameter - */ - public void setEcommerceLastOrderTimestamp(Long timestamp) { - if (timestamp != null) { - verifyEcommerceState(); - } - setParameter(ECOMMERCE_LAST_ORDER_TIMESTAMP, timestamp); - } - - /** - * Get the grand total of the ecommerce order. - * - * @return the grand total - */ - public Double getEcommerceRevenue() { - return (Double) getParameter(ECOMMERCE_REVENUE); - } - - /** - * Set the grand total of the ecommerce order (required when tracking an - * ecommerce order). Ecommerce must be enabled. - * - * @param revenue the grand total to set. A null value will remove this parameter - */ - public void setEcommerceRevenue(Double revenue) { - if (revenue != null) { - verifyEcommerceEnabled(); - } - setParameter(ECOMMERCE_REVENUE, revenue); - } - - /** - * Get the shipping cost of the ecommerce order. - * - * @return the shipping cost - */ - public Double getEcommerceShippingCost() { - return (Double) getParameter(ECOMMERCE_SHIPPING_COST); - } - - /** - * Set the shipping cost of the ecommerce order. Ecommerce must be enabled, - * and EcommerceId and EcommerceRevenue must first be set. - * - * @param shippingCost the shipping cost to set. A null value will remove this parameter - */ - public void setEcommerceShippingCost(Double shippingCost) { - if (shippingCost != null) { - verifyEcommerceState(); - } - setParameter(ECOMMERCE_SHIPPING_COST, shippingCost); - } - - /** - * Get the subtotal of the ecommerce order; excludes shipping. - * - * @return the subtotal - */ - public Double getEcommerceSubtotal() { - return (Double) getParameter(ECOMMERCE_SUBTOTAL); - } - - /** - * Set the subtotal of the ecommerce order; excludes shipping. Ecommerce - * must be enabled and EcommerceId and EcommerceRevenue must first be set. - * - * @param subtotal the subtotal to set. A null value will remove this parameter - */ - public void setEcommerceSubtotal(Double subtotal) { - if (subtotal != null) { - verifyEcommerceState(); - } - setParameter(ECOMMERCE_SUBTOTAL, subtotal); - } - - /** - * Get the tax amount of the ecommerce order. - * - * @return the tax amount - */ - public Double getEcommerceTax() { - return (Double) getParameter(ECOMMERCE_TAX); - } - - /** - * Set the tax amount of the ecommerce order. Ecommerce must be enabled, and - * EcommerceId and EcommerceRevenue must first be set. - * - * @param tax the tax amount to set. A null value will remove this parameter - */ - public void setEcommerceTax(Double tax) { - if (tax != null) { - verifyEcommerceState(); - } - setParameter(ECOMMERCE_TAX, tax); - } - - /** - * Get the event action. - * - * @return the event action - */ - public String getEventAction() { - return getNonEmptyStringParameter(EVENT_ACTION); - } - - /** - * Set the event action. Must not be empty. (eg. Play, Pause, Duration, - * Add Playlist, Downloaded, Clicked...). - * - * @param eventAction the event action to set. A null value will remove this parameter - */ - public void setEventAction(String eventAction) { - setNonEmptyStringParameter(EVENT_ACTION, eventAction); - } - - /** - * Get the event category. - * - * @return the event category - */ - public String getEventCategory() { - return getNonEmptyStringParameter(EVENT_CATEGORY); - } - - /** - * Set the event category. Must not be empty. (eg. Videos, Music, Games...). - * - * @param eventCategory the event category to set. A null value will remove this parameter - */ - public void setEventCategory(String eventCategory) { - setNonEmptyStringParameter(EVENT_CATEGORY, eventCategory); - } - - /** - * Get the event name. - * - * @return the event name - */ - public String getEventName() { - return (String) getParameter(EVENT_NAME); - } - - /** - * Set the event name. (eg. a Movie name, or Song name, or File name...). - * - * @param eventName the event name to set. A null value will remove this parameter - */ - public void setEventName(String eventName) { - setParameter(EVENT_NAME, eventName); - } - - /** - * Get the event value. - * - * @return the event value - */ - public Number getEventValue() { - return (Number) getParameter(EVENT_VALUE); - } - - /** - * Set the event value. Must be a float or integer value (numeric), not a string. - * - * @param eventValue the event value to set. A null value will remove this parameter - */ - public void setEventValue(Number eventValue) { - setParameter(EVENT_VALUE, eventValue); - } - - /** - * Get the goal id - * - * @return the goal id - */ - public Integer getGoalId() { - return (Integer) getParameter(GOAL_ID); - } - - /** - * Set the goal id. If specified, the tracking request will trigger a - * conversion for the goal of the website being tracked with this id. - * - * @param goalId the goal id to set. A null value will remove this parameter - */ - public void setGoalId(Integer goalId) { - setParameter(GOAL_ID, goalId); - } - - /** - * Get the goal revenue. - * - * @return the goal revenue - */ - public Double getGoalRevenue() { - return (Double) getParameter(GOAL_REVENUE); - } - - /** - * Set a monetary value that was generated as revenue by this goal conversion. - * Only used if idgoal is specified in the request. - * - * @param goalRevenue the goal revenue to set. A null value will remove this parameter - */ - public void setGoalRevenue(Double goalRevenue) { - if (goalRevenue != null && getGoalId() == null) { - throw new IllegalStateException("GoalId must be set before GoalRevenue can be set."); - } - setParameter(GOAL_REVENUE, goalRevenue); - } - - /** - * Get the Accept-Language HTTP header - * - * @return the Accept-Language HTTP header - */ - public String getHeaderAcceptLanguage() { - return (String) getParameter(HEADER_ACCEPT_LANGUAGE); - } - - /** - * Set an override value for the Accept-Language HTTP header - * field. This value is used to detect the visitor's country if - * GeoIP is not enabled. - * - * @param acceptLangage the Accept-Language HTTP header to set. A null value will remove this parameter - */ - public void setHeaderAcceptLanguage(String acceptLangage) { - setParameter(HEADER_ACCEPT_LANGUAGE, acceptLangage); - } - - /** - * Get the User-Agent HTTP header - * - * @return the User-Agent HTTP header - */ - public String getHeaderUserAgent() { - return (String) getParameter(HEADER_USER_AGENT); - } - - /** - * Set an override value for the User-Agent HTTP header field. - * The user agent is used to detect the operating system and browser used. - * - * @param userAgent the User-Agent HTTP header tos et - */ - public void setHeaderUserAgent(String userAgent) { - setParameter(HEADER_USER_AGENT, userAgent); - } - - /** - * Get if this request will force a new visit. - * - * @return true if this request will force a new visit - */ - public Boolean getNewVisit() { - return getBooleanParameter(NEW_VISIT); - } - - /** - * If set to true, will force a new visit to be created for this action. - * - * @param newVisit if this request will force a new visit - */ - public void setNewVisit(Boolean newVisit) { - setBooleanParameter(NEW_VISIT, newVisit); - } - - /** - * Get the outlink url - * - * @return the outlink url - * @throws TypeConstraintException if the stored Outlink URL is a String - */ - public URL getOutlinkUrl() { - return returnAsUrl(OUTLINK_URL, "Outlink URL", "getOutlinkUrlAsString"); - } - - /** - * Get the outlink url - * - * @return the outlink url - * @throws TypeConstraintException if the stored Outlink URL is a URL - */ - public String getOutlinkUrlAsString() { - return returnAsString(OUTLINK_URL, "Outlink URL", "getOutlinkUrl"); - } - - /** - * Set an external URL the user has opened. Used for tracking outlink clicks. - * We recommend to also set the url parameter to this same value. - * - * @param outlinkUrl the outlink url to set. A null value will remove this parameter - */ - public void setOutlinkUrl(URL outlinkUrl) { - setParameter(OUTLINK_URL, outlinkUrl); - } - - /** - * Set an external URL the user has opened. Used for tracking outlink clicks. - * We recommend to also set the url parameter to this same value. - * - * @param outlinkUrl the outlink url to set. A null value will remove this parameter - */ - public void setOutlinkUrlWithString(String outlinkUrl) { - setParameter(OUTLINK_URL, outlinkUrl); - } - - /** - * Get the page custom variable at the specified key. - * - * @param key the key of the variable to get - * @return the variable at the specified key, null if key is not present - * @deprecated Use the {@link #getPageCustomVariable(int)} method instead. - */ - @Deprecated - public String getPageCustomVariable(String key) { - return getCustomVariable(PAGE_CUSTOM_VARIABLE, key); - } - - /** - * Get the page custom variable at the specified index. - * - * @param index the index of the variable to get. Must be greater than 0 - * @return the variable at the specified key, null if nothing at this index - */ - public CustomVariable getPageCustomVariable(int index) { - return getCustomVariable(PAGE_CUSTOM_VARIABLE, index); - } - - /** - * Set a page custom variable with the specified key and value at the first available index. - * All page custom variables with this key will be overwritten or deleted - * - * @param key the key of the variable to set - * @param value the value of the variable to set at the specified key. A null value will remove this custom variable - * @deprecated Use the {@link #setPageCustomVariable(CustomVariable, int)} method instead. - */ - @Deprecated - public void setPageCustomVariable(String key, String value) { - if (value == null) { - removeCustomVariable(PAGE_CUSTOM_VARIABLE, key); - } else { - setCustomVariable(PAGE_CUSTOM_VARIABLE, new CustomVariable(key, value), null); - } - } - - /** - * Set a page custom variable at the specified index. - * - * @param customVariable the CustomVariable to set. A null value will remove the CustomVariable at the specified index - * @param index the index of he CustomVariable to set - */ - public void setPageCustomVariable(CustomVariable customVariable, int index) { - setCustomVariable(PAGE_CUSTOM_VARIABLE, customVariable, index); - } - - /** - * Check if the visitor has the Director plugin. - * - * @return true if visitor has the Director plugin - */ - public Boolean getPluginDirector() { - return getBooleanParameter(PLUGIN_DIRECTOR); - } - - /** - * Set if the visitor has the Director plugin. - * - * @param director true if the visitor has the Director plugin - */ - public void setPluginDirector(Boolean director) { - setBooleanParameter(PLUGIN_DIRECTOR, director); - } - - /** - * Check if the visitor has the Flash plugin. - * - * @return true if the visitor has the Flash plugin - */ - public Boolean getPluginFlash() { - return getBooleanParameter(PLUGIN_FLASH); - } - - /** - * Set if the visitor has the Flash plugin. - * - * @param flash true if the visitor has the Flash plugin - */ - public void setPluginFlash(Boolean flash) { - setBooleanParameter(PLUGIN_FLASH, flash); - } - - /** - * Check if the visitor has the Gears plugin. - * - * @return true if the visitor has the Gears plugin - */ - public Boolean getPluginGears() { - return getBooleanParameter(PLUGIN_GEARS); - } - - /** - * Set if the visitor has the Gears plugin. - * - * @param gears true if the visitor has the Gears plugin - */ - public void setPluginGears(Boolean gears) { - setBooleanParameter(PLUGIN_GEARS, gears); - } - - /** - * Check if the visitor has the Java plugin. - * - * @return true if the visitor has the Java plugin - */ - public Boolean getPluginJava() { - return getBooleanParameter(PLUGIN_JAVA); - } - - /** - * Set if the visitor has the Java plugin. - * - * @param java true if the visitor has the Java plugin - */ - public void setPluginJava(Boolean java) { - setBooleanParameter(PLUGIN_JAVA, java); - } - - /** - * Check if the visitor has the PDF plugin. - * - * @return true if the visitor has the PDF plugin - */ - public Boolean getPluginPDF() { - return getBooleanParameter(PLUGIN_PDF); - } - - /** - * Set if the visitor has the PDF plugin. - * - * @param pdf true if the visitor has the PDF plugin - */ - public void setPluginPDF(Boolean pdf) { - setBooleanParameter(PLUGIN_PDF, pdf); - } - - /** - * Check if the visitor has the Quicktime plugin. - * - * @return true if the visitor has the Quicktime plugin - */ - public Boolean getPluginQuicktime() { - return getBooleanParameter(PLUGIN_QUICKTIME); - } - - /** - * Set if the visitor has the Quicktime plugin. - * - * @param quicktime true if the visitor has the Quicktime plugin - */ - public void setPluginQuicktime(Boolean quicktime) { - setBooleanParameter(PLUGIN_QUICKTIME, quicktime); - } - - /** - * Check if the visitor has the RealPlayer plugin. - * - * @return true if the visitor has the RealPlayer plugin - */ - public Boolean getPluginRealPlayer() { - return getBooleanParameter(PLUGIN_REAL_PLAYER); - } - - /** - * Set if the visitor has the RealPlayer plugin. - * - * @param realPlayer true if the visitor has the RealPlayer plugin - */ - public void setPluginRealPlayer(Boolean realPlayer) { - setBooleanParameter(PLUGIN_REAL_PLAYER, realPlayer); - } - - /** - * Check if the visitor has the Silverlight plugin. - * - * @return true if the visitor has the Silverlight plugin - */ - public Boolean getPluginSilverlight() { - return getBooleanParameter(PLUGIN_SILVERLIGHT); - } - - /** - * Set if the visitor has the Silverlight plugin. - * - * @param silverlight true if the visitor has the Silverlight plugin - */ - public void setPluginSilverlight(Boolean silverlight) { - setBooleanParameter(PLUGIN_SILVERLIGHT, silverlight); - } - - /** - * Check if the visitor has the Windows Media plugin. - * - * @return true if the visitor has the Windows Media plugin - */ - public Boolean getPluginWindowsMedia() { - return getBooleanParameter(PLUGIN_WINDOWS_MEDIA); - } - - /** - * Set if the visitor has the Windows Media plugin. - * - * @param windowsMedia true if the visitor has the Windows Media plugin - */ - public void setPluginWindowsMedia(Boolean windowsMedia) { - setBooleanParameter(PLUGIN_WINDOWS_MEDIA, windowsMedia); - } - - /** - * Get the random value for this request - * - * @return the random value - */ - public String getRandomValue() { - return (String) getParameter(RANDOM_VALUE); - } - - /** - * Set a random value that is generated before each request. Using it helps - * avoid the tracking request being cached by the browser or a proxy. - * - * @param randomValue the random value to set. A null value will remove this parameter - */ - public void setRandomValue(String randomValue) { - setParameter(RANDOM_VALUE, randomValue); - } - - /** - * Get the referrer url - * - * @return the referrer url - * @throws TypeConstraintException if the stored Referrer URL is a String - */ - public URL getReferrerUrl() { - return returnAsUrl(REFERRER_URL, "Referrer URL", "getReferrerUrlAsString"); - } - - /** - * Get the referrer url - * - * @return the referrer url - * @throws TypeConstraintException if the stored Referrer URL is a URL - */ - public String getReferrerUrlAsString() { - return returnAsString(REFERRER_URL, "Referrer URL", "getReferrerUrl"); - } - - /** - * Set the full HTTP Referrer URL. This value is used to determine how someone - * got to your website (ie, through a website, search engine or campaign). - * - * @param referrerUrl the referrer url to set. A null value will remove this parameter - */ - public void setReferrerUrl(URL referrerUrl) { - setParameter(REFERRER_URL, referrerUrl); - } - - /** - * Set the full HTTP Referrer URL. This value is used to determine how someone - * got to your website (ie, through a website, search engine or campaign). - * - * @param referrerUrl the referrer url to set. A null value will remove this parameter - */ - public void setReferrerUrlWithString(String referrerUrl) { - setParameter(REFERRER_URL, referrerUrl); - } - - /** - * Get the datetime of the request - * - * @return the datetime of the request - */ - public PiwikDate getRequestDatetime() { - return (PiwikDate) getParameter(REQUEST_DATETIME); - } - - /** - * Set the datetime of the request (normally the current time is used). - * This can be used to record visits and page views in the past. The datetime - * must be sent in UTC timezone. Note: if you record data in the past, you will - * need to force Piwik to re-process - * reports for the past dates. If you set the Request Datetime to a datetime - * older than four hours then Auth Token must be set. If you set - * Request Datetime with a datetime in the last four hours then you - * don't need to pass Auth Token. - * - * @param datetime the datetime of the request to set. A null value will remove this parameter - */ - public void setRequestDatetime(PiwikDate datetime) { - if (datetime != null && new Date().getTime() - datetime.getTime() > REQUEST_DATETIME_AUTH_LIMIT && getAuthToken() == null) { - throw new IllegalStateException("Because you are trying to set RequestDatetime for a time greater than 4 hours ago, AuthToken must be set first."); - } - setParameter(REQUEST_DATETIME, datetime); - } - - /** - * Get if this request will be tracked. - * - * @return true if request will be tracked - */ - public Boolean getRequired() { - return getBooleanParameter(REQUIRED); - } - - /** - * Set if this request will be tracked by the Piwik server. - * - * @param required true if request will be tracked - */ - public void setRequired(Boolean required) { - setBooleanParameter(REQUIRED, required); - } - - /** - * Get if the response will be an image. - * - * @return true if the response will be an an image - */ - public Boolean getResponseAsImage() { - return getBooleanParameter(RESPONSE_AS_IMAGE); - } - - /** - * Set if the response will be an image. If set to false, Piwik will respond - * with a HTTP 204 response code instead of a GIF image. This improves performance - * and can fix errors if images are not allowed to be obtained directly - * (eg Chrome Apps). Available since Piwik 2.10.0. - * - * @param responseAsImage true if the response will be an image - */ - public void setResponseAsImage(Boolean responseAsImage) { - setBooleanParameter(RESPONSE_AS_IMAGE, responseAsImage); - } - - /** - * Get the search category - * - * @return the search category - */ - public String getSearchCategory() { - return (String) getParameter(SEARCH_CATEGORY); - } - - /** - * Specify a search category with this parameter. SearchQuery must first be - * set. - * - * @param searchCategory the search category to set. A null value will remove this parameter - */ - public void setSearchCategory(String searchCategory) { - if (searchCategory != null && getSearchQuery() == null) { - throw new IllegalStateException("SearchQuery must be set before SearchCategory can be set."); - } - setParameter(SEARCH_CATEGORY, searchCategory); - } - - /** - * Get the search query. - * - * @return the search query - */ - public String getSearchQuery() { - return (String) getParameter(SEARCH_QUERY); - } - - /** - * Set the search query. When specified, the request will not be tracked as - * a normal pageview but will instead be tracked as a Site Search request. - * - * @param searchQuery the search query to set. A null value will remove this parameter - */ - public void setSearchQuery(String searchQuery) { - setParameter(SEARCH_QUERY, searchQuery); - } - - /** - * Get the search results count. - * - * @return the search results count - */ - public Long getSearchResultsCount() { - return (Long) getParameter(SEARCH_RESULTS_COUNT); - } - - /** - * We recommend to set the - * search count to the number of search results displayed on the results page. - * When keywords are tracked with {@code Search Results Count=0} they will appear in - * the "No Result Search Keyword" report. SearchQuery must first be set. - * - * @param searchResultsCount the search results count to set. A null value will remove this parameter - */ - public void setSearchResultsCount(Long searchResultsCount) { - if (searchResultsCount != null && getSearchQuery() == null) { - throw new IllegalStateException("SearchQuery must be set before SearchResultsCount can be set."); - } - setParameter(SEARCH_RESULTS_COUNT, searchResultsCount); - } - - /** - * Get the id of the website we're tracking. - * - * @return the id of the website - */ - public Integer getSiteId() { - return (Integer) getParameter(SITE_ID); - } - - /** - * Set the ID of the website we're tracking a visit/action for. - * - * @param siteId the id of the website to set. A null value will remove this parameter - */ - public void setSiteId(Integer siteId) { - setParameter(SITE_ID, siteId); - } - - /** - * Set if bot requests should be tracked - * - * @return true if bot requests should be tracked - */ - public Boolean getTrackBotRequests() { - return getBooleanParameter(TRACK_BOT_REQUESTS); - } - - /** - * By default Piwik does not track bots. If you use the Tracking Java API, - * you may be interested in tracking bot requests. To enable Bot Tracking in - * Piwik, set Track Bot Requests to true. - * - * @param trackBotRequests true if bot requests should be tracked - */ - public void setTrackBotRequests(Boolean trackBotRequests) { - setBooleanParameter(TRACK_BOT_REQUESTS, trackBotRequests); - } - - /** - * Get the visit custom variable at the specified key. - * - * @param key the key of the variable to get - * @return the variable at the specified key, null if key is not present - * @deprecated Use the {@link #getVisitCustomVariable(int)} method instead. - */ - @Deprecated - public String getUserCustomVariable(String key) { - return getCustomVariable(VISIT_CUSTOM_VARIABLE, key); - } - - /** - * Get the visit custom variable at the specified index. - * - * @param index the index of the variable to get - * @return the variable at the specified index, null if nothing at this index - */ - public CustomVariable getVisitCustomVariable(int index) { - return getCustomVariable(VISIT_CUSTOM_VARIABLE, index); - } - - /** - * Set a visit custom variable with the specified key and value at the first available index. - * All visit custom variables with this key will be overwritten or deleted - * - * @param key the key of the variable to set - * @param value the value of the variable to set at the specified key. A null value will remove this parameter - * @deprecated Use the {@link #setVisitCustomVariable(CustomVariable, int)} method instead. - */ - @Deprecated - public void setUserCustomVariable(String key, String value) { - if (value == null) { - removeCustomVariable(VISIT_CUSTOM_VARIABLE, key); - } else { - setCustomVariable(VISIT_CUSTOM_VARIABLE, new CustomVariable(key, value), null); - } - } - - /** - * Set a user custom variable at the specified key. - * - * @param customVariable the CustomVariable to set. A null value will remove the custom variable at the specified index - * @param index the index to set the customVariable at. - */ - public void setVisitCustomVariable(CustomVariable customVariable, int index) { - setCustomVariable(VISIT_CUSTOM_VARIABLE, customVariable, index); - } - - /** - * Get the user id for this request. - * - * @return the user id - */ - public String getUserId() { - return (String) getParameter(USER_ID); - } - - /** - * Set the user id for this request. - * User id is any non empty unique string identifying the user (such as an email - * address or a username). To access this value, users must be logged-in in your - * system so you can fetch this user id from your system, and pass it to Piwik. - * The user id appears in the visitor log, the Visitor profile, and you can - * Segment - * reports for one or several user ids. When specified, the user id will be - * "enforced". This means that if there is no recent visit with this user id, - * a new one will be created. If a visit is found in the last 30 minutes with - * your specified user id, then the new action will be recorded to this existing visit. - * - * @param userId the user id to set. A null value will remove this parameter - */ - public void setUserId(String userId) { - setNonEmptyStringParameter(USER_ID, userId); - } - - /** - * Get the visitor's city. - * - * @return the visitor's city - */ - public String getVisitorCity() { - return (String) getParameter(VISITOR_CITY); - } - - /** - * Set an override value for the city. The name of the city the visitor is - * located in, eg, Tokyo. AuthToken must first be set. - * - * @param city the visitor's city to set. A null value will remove this parameter - */ - public void setVisitorCity(String city) { - if (city != null) { - verifyAuthTokenSet(); - } - setParameter(VISITOR_CITY, city); - } - - /** - * Get the visitor's country. - * - * @return the visitor's country - */ - public PiwikLocale getVisitorCountry() { - return (PiwikLocale) getParameter(VISITOR_COUNTRY); - } - - /** - * Set an override value for the country. AuthToken must first be set. - * - * @param country the visitor's country to set. A null value will remove this parameter - */ - public void setVisitorCountry(PiwikLocale country) { - if (country != null) { - verifyAuthTokenSet(); - } - setParameter(VISITOR_COUNTRY, country); - } - - /** - * Get the visitor's custom id. - * - * @return the visitor's custom id - */ - public String getVisitorCustomId() { - return (String) getParameter(VISITOR_CUSTOM_ID); - } - - /** - * Set a custom visitor ID for this request. You must set this value to exactly - * a {@value #ID_LENGTH} character hexadecimal string (containing only characters 01234567890abcdefABCDEF). - * We recommended to set the UserId rather than the VisitorCustomId. - * - * @param visitorCustomId the visitor's custom id to set. A null value will remove this parameter - */ - public void setVisitorCustomId(String visitorCustomId) { - if (visitorCustomId != null) { - if (visitorCustomId.length() != ID_LENGTH) { - throw new IllegalArgumentException(visitorCustomId + " is not " + ID_LENGTH + " characters long."); - } - // Verify visitorID is a 16 character hexadecimal string - else if (!visitorCustomId.matches("[0-9A-Fa-f]+")) { - throw new IllegalArgumentException(visitorCustomId + " is not a hexadecimal string."); - } - } - setParameter(VISITOR_CUSTOM_ID, visitorCustomId); - } - - /** - * Get the timestamp of the visitor's first visit. - * - * @return the timestamp of the visitor's first visit - */ - public Long getVisitorFirstVisitTimestamp() { - return (Long) getParameter(VISITOR_FIRST_VISIT_TIMESTAMP); - } - - /** - * Set the UNIX timestamp of this visitor's first visit. This could be set - * to the date where the user first started using your software/app, or when - * he/she created an account. This parameter is used to populate the - * Goals > Days to Conversion report. - * - * @param timestamp the timestamp of the visitor's first visit to set. A null value will remove this parameter - */ - public void setVisitorFirstVisitTimestamp(Long timestamp) { - setParameter(VISITOR_FIRST_VISIT_TIMESTAMP, timestamp); - } - - /** - * Get the visitor's id. - * - * @return the visitor's id - */ - public String getVisitorId() { - return (String) getParameter(VISITOR_ID); - } - - /** - * Set the unique visitor ID, must be a {@value #ID_LENGTH} characters hexadecimal string. - * Every unique visitor must be assigned a different ID and this ID must not - * change after it is assigned. If this value is not set Piwik will still - * track visits, but the unique visitors metric might be less accurate. - * - * @param visitorId the visitor id to set. A null value will remove this parameter - */ - public void setVisitorId(String visitorId) { - if (visitorId != null) { - if (visitorId.length() != ID_LENGTH) { - throw new IllegalArgumentException(visitorId + " is not " + ID_LENGTH + " characters long."); - } - // Verify visitorID is a 16 character hexadecimal string - else if (!visitorId.matches("[0-9A-Fa-f]+")) { - throw new IllegalArgumentException(visitorId + " is not a hexadecimal string."); - } - } - setParameter(VISITOR_ID, visitorId); - } - - /** - * Get the visitor's ip. - * - * @return the visitor's ip - */ - public String getVisitorIp() { - return (String) getParameter(VISITOR_IP); - } - - /** - * Set the override value for the visitor IP (both IPv4 and IPv6 notations - * supported). AuthToken must first be set. - * - * @param visitorIp the visitor's ip to set. A null value will remove this parameter - */ - public void setVisitorIp(String visitorIp) { - if (visitorIp != null) { - verifyAuthTokenSet(); - } - setParameter(VISITOR_IP, visitorIp); - } - - /** - * Get the visitor's latitude. - * - * @return the visitor's latitude - */ - public Double getVisitorLatitude() { - return (Double) getParameter(VISITOR_LATITUDE); - } - - /** - * Set an override value for the visitor's latitude, eg 22.456. AuthToken - * must first be set. - * - * @param latitude the visitor's latitude to set. A null value will remove this parameter - */ - public void setVisitorLatitude(Double latitude) { - if (latitude != null) { - verifyAuthTokenSet(); - } - setParameter(VISITOR_LATITUDE, latitude); - } - - /** - * Get the visitor's longitude. - * - * @return the visitor's longitude - */ - public Double getVisitorLongitude() { - return (Double) getParameter(VISITOR_LONGITUDE); - } - - /** - * Set an override value for the visitor's longitude, eg 22.456. AuthToken - * must first be set. - * - * @param longitude the visitor's longitude to set. A null value will remove this parameter - */ - public void setVisitorLongitude(Double longitude) { - if (longitude != null) { - verifyAuthTokenSet(); - } - setParameter(VISITOR_LONGITUDE, longitude); - } - - /** - * Get the timestamp of the visitor's previous visit. - * - * @return the timestamp of the visitor's previous visit - */ - public Long getVisitorPreviousVisitTimestamp() { - return (Long) getParameter(VISITOR_PREVIOUS_VISIT_TIMESTAMP); - } - - /** - * Set the UNIX timestamp of this visitor's previous visit. This parameter - * is used to populate the report - * Visitors > Engagement > Visits by days since last visit. - * - * @param timestamp the timestamp of the visitor's previous visit to set. A null value will remove this parameter - */ - public void setVisitorPreviousVisitTimestamp(Long timestamp) { - setParameter(VISITOR_PREVIOUS_VISIT_TIMESTAMP, timestamp); - } - - /** - * Get the visitor's region. - * - * @return the visitor's region - */ - public String getVisitorRegion() { - return (String) getParameter(VISITOR_REGION); - } - - /** - * Set an override value for the region. Should be set to the two letter - * region code as defined by - * MaxMind's GeoIP databases. - * See here - * for a list of them for every country (the region codes are located in the - * second column, to the left of the region name and to the right of the country - * code). - * - * @param region the visitor's region to set. A null value will remove this parameter - */ - public void setVisitorRegion(String region) { - if (region != null) { - verifyAuthTokenSet(); - } - setParameter(VISITOR_REGION, region); - } - - /** - * Get the count of visits for this visitor. - * - * @return the count of visits for this visitor - */ - public Integer getVisitorVisitCount() { - return (Integer) getParameter(VISITOR_VISIT_COUNT); - } - - /** - * Set the current count of visits for this visitor. To set this value correctly, - * it would be required to store the value for each visitor in your application - * (using sessions or persisting in a database). Then you would manually increment - * the counts by one on each new visit or "session", depending on how you choose - * to define a visit. This value is used to populate the report - * Visitors > Engagement > Visits by visit number. - * - * @param visitorVisitCount the count of visits for this visitor to set. A null value will remove this parameter - */ - public void setVisitorVisitCount(Integer visitorVisitCount) { - setParameter(VISITOR_VISIT_COUNT, visitorVisitCount); - } - - /** - * Get the query string represented by this object. - * - * @return the query string represented by this object - */ - - public String getQueryString() { - StringBuilder sb = new StringBuilder(); - for (Entry parameter : parameters.entrySet()) { - if (sb.length() > 0) { - sb.append("&"); - } - sb.append(parameter.getKey()); - sb.append("="); - sb.append(parameter.getValue().toString()); - } - for (Entry customTrackingParameter : customTrackingParameters.entrySet()) { - for (Object o : customTrackingParameter.getValue()) { - if (sb.length() > 0) { - sb.append("&"); - } - sb.append(customTrackingParameter.getKey()); - sb.append("="); - sb.append(o.toString()); - } - } - - return sb.toString(); - } - - /** - * Get the url encoded query string represented by this object. - * - * @return the url encoded query string represented by this object - */ - public String getUrlEncodedQueryString() { - StringBuilder sb = new StringBuilder(); - for (Entry parameter : parameters.entrySet()) { - if (sb.length() > 0) { - sb.append("&"); - } - try { - StringBuilder sb2 = new StringBuilder(); - sb2.append(parameter.getKey()); - sb2.append("="); - sb2.append(URLEncoder.encode(parameter.getValue().toString(), "UTF-8")); - sb.append(sb2); - } catch (UnsupportedEncodingException e) { - System.err.println(e.getMessage()); - } - } - for (Entry customTrackingParameter : customTrackingParameters.entrySet()) { - for (Object o : customTrackingParameter.getValue()) { - if (sb.length() > 0) { - sb.append("&"); - } - try { - StringBuilder sb2 = new StringBuilder(); - sb2.append(URLEncoder.encode(customTrackingParameter.getKey(), "UTF-8")); - sb2.append("="); - sb2.append(URLEncoder.encode(o.toString(), "UTF-8")); - sb.append(sb2); - } catch (UnsupportedEncodingException e) { - System.err.println(e.getMessage()); - } - } - } - - return sb.toString(); - } - - /** - * Get a random hexadecimal string of a specified length. - * - * @param length length of the string to produce - * @return a random string consisting only of hexadecimal characters - */ - public static String getRandomHexString(int length) { - byte[] bytes = new byte[length / 2]; - new Random().nextBytes(bytes); - return DatatypeConverter.printHexBinary(bytes); - } - - /** - * Get a stored parameter. - * - * @param key the parameter's key - * @return the stored parameter's value - */ - private Object getParameter(String key) { - return parameters.get(key); - } - - /** - * Set a stored parameter. - * - * @param key the parameter's key - * @param value the parameter's value. Removes the parameter if null - */ - private void setParameter(String key, Object value) { - if (value == null) { - parameters.remove(key); - } else { - parameters.put(key, value); - } - } - - /** - * Get a stored parameter that is a non-empty string. - * - * @param key the parameter's key - * @return the stored parameter's value - */ - private String getNonEmptyStringParameter(String key) { - return (String) parameters.get(key); - } - - /** - * Set a stored parameter and verify it is a non-empty string. - * - * @param key the parameter's key - * @param value the parameter's value. Cannot be the empty. Removes the parameter if null - * string - */ - private void setNonEmptyStringParameter(String key, String value) { - if (value == null) { - parameters.remove(key); - } else if (value.length() == 0) { - throw new IllegalArgumentException("Value cannot be empty."); - } else { - parameters.put(key, value); - } - } - - /** - * Get a stored parameter that is a boolean. - * - * @param key the parameter's key - * @return the stored parameter's value - */ - private Boolean getBooleanParameter(String key) { - Integer i = (Integer) parameters.get(key); - if (i == null) { - return null; - } - return i.equals(1); - } - - /** - * Set a stored parameter that is a boolean. This value will be stored as "1" - * for true and "0" for false. - * - * @param key the parameter's key - * @param value the parameter's value. Removes the parameter if null - */ - private void setBooleanParameter(String key, Boolean value) { - if (value == null) { - parameters.remove(key); - } else if (value) { - parameters.put(key, 1); - } else { - parameters.put(key, 0); - } - } - - /** - * Get a value that is stored in a json object at the specified parameter. - * - * @param parameter the parameter to retrieve the json object from - * @param key the key of the value. Cannot be null - * @return the value - */ - private CustomVariable getCustomVariable(String parameter, int index) { - CustomVariableList cvl = (CustomVariableList) parameters.get(parameter); - if (cvl == null) { - return null; - } - - return cvl.get(index); - } - - private String getCustomVariable(String parameter, String key) { - if (key == null) { - throw new NullPointerException("Key cannot be null."); - } - - CustomVariableList cvl = (CustomVariableList) parameters.get(parameter); - if (cvl == null) { - return null; - } - - return cvl.get(key); - } - - /** - * Store a value in a json object at the specified parameter. - * - * @param parameter the parameter to store the json object at - * @param key the key of the value. Cannot be null - * @param value the value. Removes the parameter if null - */ - private void setCustomVariable(String parameter, CustomVariable customVariable, Integer index) { - CustomVariableList cvl = (CustomVariableList) parameters.get(parameter); - if (cvl == null) { - cvl = new CustomVariableList(); - parameters.put(parameter, cvl); - } - - if (customVariable == null) { - cvl.remove(index); - if (cvl.isEmpty()) { - parameters.remove(parameter); - } - } else if (index == null) { - cvl.add(customVariable); - } else { - cvl.add(customVariable, index); - } - } - - private void removeCustomVariable(String parameter, String key) { - if (key == null) { - throw new NullPointerException("Key cannot be null."); - } - CustomVariableList cvl = (CustomVariableList) parameters.get(parameter); - if (cvl != null) { - cvl.remove(key); - if (cvl.isEmpty()) { - parameters.remove(parameter); - } - } - } - - /** - * Get the value at the specified index from the json array at the specified - * parameter. - * - * @param key the key of the json array to access - * @param index the index of the value in the json array - * @return the value at the index in the json array - */ - private JsonValue getFromJsonArray(String key, int index) { - PiwikJsonArray a = (PiwikJsonArray) parameters.get(key); - if (a == null) { - return null; - } - - return a.get(index); - } - - /** - * Add a value to the json array at the specified parameter - * - * @param key the key of the json array to add to - * @param value the value to add. Cannot be null - */ - private void addToJsonArray(String key, JsonValue value) { - if (value == null) { - throw new NullPointerException("Value cannot be null."); - } - - PiwikJsonArray a = (PiwikJsonArray) parameters.get(key); - if (a == null) { - a = new PiwikJsonArray(); - parameters.put(key, a); - } - a.add(value); - } - - /** - * Removes the json array at the specified parameter - * - * @param key the key of the json array to remove - */ - private void removeJsonArray(String key) { - parameters.remove(key); - } - - private URL returnAsUrl(String parameter, String name, String altMethod) { - Object obj = getParameter(parameter); - if (obj == null) { - return null; - } - if (obj instanceof URL) { - return (URL) obj; - } - throw new TypeConstraintException("The stored " + name + - " is a String, not a URL. Use \"" + - altMethod + "\" instead."); - } - - private String returnAsString(String parameter, String name, String altMethod) { - Object obj = getParameter(parameter); - if (obj == null) { - return null; - } - if (obj instanceof String) { - return (String) obj; - } - throw new TypeConstraintException("The stored " + name + - " is a URL, not a String. Use \"" + - altMethod + "\" instead."); + super(siteId, actionUrl); } } diff --git a/src/main/java/org/piwik/java/tracking/PiwikTracker.java b/src/main/java/org/piwik/java/tracking/PiwikTracker.java index a0ec1201..4e91e0b0 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikTracker.java +++ b/src/main/java/org/piwik/java/tracking/PiwikTracker.java @@ -1,297 +1,50 @@ /* - * Piwik Java Tracker + * Matomo Java Tracker * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ package org.piwik.java.tracking; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.concurrent.Future; -import javax.json.Json; -import javax.json.JsonArrayBuilder; -import javax.json.JsonObjectBuilder; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.utils.URIBuilder; -import org.apache.http.concurrent.FutureCallback; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; +import org.matomo.java.tracking.MatomoTracker; /** - * A class that sends {@link PiwikRequest}s to a specified Piwik server. - * * @author brettcsorba + * @deprecated Use {@link MatomoTracker} instead. */ -public class PiwikTracker { - private static final String AUTH_TOKEN = "token_auth"; - private static final String REQUESTS = "requests"; - private static final int DEFAULT_TIMEOUT = 5000; - private final URIBuilder uriBuilder; - private final int timeout; - private final String proxyHost; - private final int proxyPort; +@Deprecated +public class PiwikTracker extends MatomoTracker { /** - * Creates a tracker that will send {@link PiwikRequest}s to the specified - * Tracking HTTP API endpoint. - * - * @param hostUrl url endpoint to send requests to. Usually in the format - * http://your-piwik-domain.tld/piwik.php. + * @deprecated Use {@link MatomoTracker} instead. */ + @Deprecated public PiwikTracker(final String hostUrl) { - this(hostUrl, DEFAULT_TIMEOUT); - } - - /** - * Creates a tracker that will send {@link PiwikRequest}s to the specified - * Tracking HTTP API endpoint. - * - * @param hostUrl url endpoint to send requests to. Usually in the format - * http://your-piwik-domain.tld/piwik.php. - * @param timeout the timeout of the sent request in milliseconds - */ - public PiwikTracker(final String hostUrl, final int timeout) { - uriBuilder = new URIBuilder(URI.create(hostUrl)); - this.timeout = timeout; - this.proxyHost = null; - this.proxyPort = 0; - } - - /** - * Creates a tracker that will send {@link PiwikRequest}s to the specified - * Tracking HTTP API endpoint via the provided proxy - * - * @param hostUrl url endpoint to send requests to. Usually in the format - * http://your-piwik-domain.tld/piwik.php. - * @param proxyHost url endpoint for the proxy - * @param proxyPort proxy server port number - */ - public PiwikTracker(final String hostUrl, final String proxyHost, final int proxyPort) { - this(hostUrl, proxyHost, proxyPort, DEFAULT_TIMEOUT); - } - - public PiwikTracker(final String hostUrl, final String proxyHost, final int proxyPort, final int timeout) { - uriBuilder = new URIBuilder(URI.create(hostUrl)); - this.proxyHost = proxyHost; - this.proxyPort = proxyPort; - this.timeout = timeout; + super(hostUrl); } /** - * Send a request. - * - * @param request request to send - * @return the response from this request - * @throws IOException thrown if there was a problem with this connection - * @deprecated use sendRequestAsync instead + * @deprecated Use {@link MatomoTracker} instead. */ @Deprecated - public HttpResponse sendRequest(final PiwikRequest request) throws IOException { - final HttpClient client = getHttpClient(); - uriBuilder.setCustomQuery(request.getQueryString()); - HttpGet get = null; - - try { - get = new HttpGet(uriBuilder.build()); - return client.execute(get); - } catch (final URISyntaxException e) { - throw new IOException(e); - } - } - - /** - * Send a request. - * - * @param request request to send - * @return future with response from this request - * @throws IOException thrown if there was a problem with this connection - */ - public Future sendRequestAsync(final PiwikRequest request) throws IOException { - return sendRequestAsync(request, null); - } - /** - * Send a request. - * - * @param request request to send - * @param callback callback that gets executed when response arrives - * @return future with response from this request - * @throws IOException thrown if there was a problem with this connection - */ - public Future sendRequestAsync(final PiwikRequest request, FutureCallback callback) throws IOException { - final CloseableHttpAsyncClient client = getHttpAsyncClient(); - client.start(); - uriBuilder.setCustomQuery(request.getQueryString()); - HttpGet get = null; - - try { - get = new HttpGet(uriBuilder.build()); - return client.execute(get, callback); - } catch (final URISyntaxException e) { - throw new IOException(e); - } + public PiwikTracker(final String hostUrl, final int timeout) { + super(hostUrl, timeout); } /** - * Send multiple requests in a single HTTP call. More efficient than sending - * several individual requests. - * - * @param requests the requests to send - * @return the response from these requests - * @throws IOException thrown if there was a problem with this connection - * @deprecated use sendBulkRequestAsync instead + * @deprecated Use {@link MatomoTracker} instead. */ @Deprecated - public HttpResponse sendBulkRequest(final Iterable requests) throws IOException { - return sendBulkRequest(requests, null); - } - - /** - * Send multiple requests in a single HTTP call. More efficient than sending - * several individual requests. - * - * @param requests the requests to send - * @return future with response from these requests - * @throws IOException thrown if there was a problem with this connection - */ - public Future sendBulkRequestAsync(final Iterable requests) throws IOException { - return sendBulkRequestAsync(requests, null, null); - } - - /** - * Send multiple requests in a single HTTP call. More efficient than sending - * several individual requests. - * - * @param requests the requests to send - * @param callback callback that gets executed when response arrives - * @return future with response from these requests - * @throws IOException thrown if there was a problem with this connection - */ - public Future sendBulkRequestAsync(final Iterable requests, FutureCallback callback) throws IOException { - return sendBulkRequestAsync(requests, null, callback); + public PiwikTracker(final String hostUrl, final String proxyHost, final int proxyPort) { + super(hostUrl, proxyHost, proxyPort); } /** - * Send multiple requests in a single HTTP call. More efficient than sending - * several individual requests. Specify the AuthToken if parameters that require - * an auth token is used. - * - * @param requests the requests to send - * @param authToken specify if any of the parameters use require AuthToken - * @return the response from these requests - * @throws IOException thrown if there was a problem with this connection - * @deprecated use sendBulkRequestAsync instead + * @deprecated Use {@link MatomoTracker} instead. */ @Deprecated - public HttpResponse sendBulkRequest(final Iterable requests, final String authToken) throws IOException { - if (authToken != null && authToken.length() != PiwikRequest.AUTH_TOKEN_LENGTH) { - throw new IllegalArgumentException(authToken + " is not " + PiwikRequest.AUTH_TOKEN_LENGTH + " characters long."); - } - - final JsonObjectBuilder ob = Json.createObjectBuilder(); - final JsonArrayBuilder ab = Json.createArrayBuilder(); - - for (final PiwikRequest request : requests) { - ab.add("?" + request.getQueryString()); - } - - ob.add(REQUESTS, ab); - - if (authToken != null) { - ob.add(AUTH_TOKEN, authToken); - } - - final HttpClient client = getHttpClient(); - HttpPost post = null; - - try { - post = new HttpPost(uriBuilder.build()); - post.setEntity(new StringEntity(ob.build().toString(), - ContentType.APPLICATION_JSON)); - return client.execute(post); - } catch (final URISyntaxException e) { - throw new IOException(e); - } - } - - /** - * Send multiple requests in a single HTTP call. More efficient than sending - * several individual requests. Specify the AuthToken if parameters that require - * an auth token is used. - * - * @param requests the requests to send - * @param authToken specify if any of the parameters use require AuthToken - * @return the response from these requests - * @throws IOException thrown if there was a problem with this connection - */ - public Future sendBulkRequestAsync(final Iterable requests, final String authToken) throws IOException { - return sendBulkRequestAsync(requests, authToken, null); - } - - /** - * Send multiple requests in a single HTTP call. More efficient than sending - * several individual requests. Specify the AuthToken if parameters that require - * an auth token is used. - * - * @param requests the requests to send - * @param authToken specify if any of the parameters use require AuthToken - * @param callback callback that gets executed when response arrives - * @return the response from these requests - * @throws IOException thrown if there was a problem with this connection - */ - public Future sendBulkRequestAsync(final Iterable requests, final String authToken, - FutureCallback callback) throws IOException { - if (authToken != null && authToken.length() != PiwikRequest.AUTH_TOKEN_LENGTH) { - throw new IllegalArgumentException(authToken + " is not " + PiwikRequest.AUTH_TOKEN_LENGTH + " characters long."); - } - - final JsonObjectBuilder ob = Json.createObjectBuilder(); - final JsonArrayBuilder ab = Json.createArrayBuilder(); - - for (final PiwikRequest request : requests) { - ab.add("?" + request.getQueryString()); - } - - ob.add(REQUESTS, ab); - - if (authToken != null) { - ob.add(AUTH_TOKEN, authToken); - } - - final CloseableHttpAsyncClient client = getHttpAsyncClient(); - client.start(); - HttpPost post = null; - - try { - post = new HttpPost(uriBuilder.build()); - post.setEntity(new StringEntity(ob.build().toString(), - ContentType.APPLICATION_JSON)); - return client.execute(post, callback); - } catch (final URISyntaxException e) { - throw new IOException(e); - } - } - - /** - * Get a HTTP client. With proxy if a proxy is provided in the constructor. - * - * @return a HTTP client - */ - protected HttpClient getHttpClient() { - return HttpClientFactory.getInstanceFor(proxyHost, proxyPort, timeout); + public PiwikTracker(final String hostUrl, final String proxyHost, final int proxyPort, final int timeout) { + super(hostUrl, proxyHost, proxyPort, timeout); } - /** - * Get an async HTTP client. With proxy if a proxy is provided in the constructor. - * - * @return an async HTTP client - */ - protected CloseableHttpAsyncClient getHttpAsyncClient() { - return HttpClientFactory.getAsyncInstanceFor(proxyHost, proxyPort, timeout); - } } diff --git a/src/test/java/org/piwik/java/tracking/CustomVariableTest.java b/src/test/java/org/matomo/java/tracking/CustomVariableTest.java similarity index 84% rename from src/test/java/org/piwik/java/tracking/CustomVariableTest.java rename to src/test/java/org/matomo/java/tracking/CustomVariableTest.java index 4223eaed..9818bc8d 100644 --- a/src/test/java/org/piwik/java/tracking/CustomVariableTest.java +++ b/src/test/java/org/matomo/java/tracking/CustomVariableTest.java @@ -3,7 +3,7 @@ * To change this template file, choose Tools | Templates * and open the template in the editor. */ -package org.piwik.java.tracking; +package org.matomo.java.tracking; import org.junit.Before; import org.junit.Test; @@ -28,7 +28,7 @@ public void testConstructorNullKey() { new CustomVariable(null, null); fail("Exception should have been throw."); } catch (NullPointerException e) { - assertEquals("Key cannot be null.", e.getLocalizedMessage()); + assertEquals("key is marked non-null but is null", e.getLocalizedMessage()); } } @@ -38,7 +38,7 @@ public void testConstructorNullValue() { new CustomVariable("key", null); fail("Exception should have been throw."); } catch (NullPointerException e) { - assertEquals("Value cannot be null.", e.getLocalizedMessage()); + assertEquals("value is marked non-null but is null", e.getLocalizedMessage()); } } diff --git a/src/test/java/org/matomo/java/tracking/CustomVariablesTest.java b/src/test/java/org/matomo/java/tracking/CustomVariablesTest.java new file mode 100644 index 00000000..bdc4e8d1 --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/CustomVariablesTest.java @@ -0,0 +1,85 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.matomo.java.tracking; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * @author Katie + */ +public class CustomVariablesTest { + private final CustomVariables customVariables = new CustomVariables(); + + @Test + public void testAdd_CustomVariable() { + CustomVariable a = new CustomVariable("a", "b"); + CustomVariable b = new CustomVariable("c", "d"); + CustomVariable c = new CustomVariable("a", "e"); + CustomVariable d = new CustomVariable("a", "f"); + + assertTrue(customVariables.isEmpty()); + customVariables.add(a); + assertFalse(customVariables.isEmpty()); + assertEquals("b", customVariables.get("a")); + assertEquals(a, customVariables.get(1)); + assertEquals("{\"1\":[\"a\",\"b\"]}", customVariables.toString()); + + customVariables.add(b); + assertEquals("d", customVariables.get("c")); + assertEquals(b, customVariables.get(2)); + assertEquals("{\"1\":[\"a\",\"b\"],\"2\":[\"c\",\"d\"]}", customVariables.toString()); + + customVariables.add(c, 5); + assertEquals("b", customVariables.get("a")); + assertEquals(c, customVariables.get(5)); + assertNull(customVariables.get(3)); + assertEquals("{\"1\":[\"a\",\"b\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"e\"]}", customVariables.toString()); + + customVariables.add(d); + assertEquals("f", customVariables.get("a")); + assertEquals(d, customVariables.get(1)); + assertEquals(d, customVariables.get(5)); + assertEquals("{\"1\":[\"a\",\"f\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"f\"]}", customVariables.toString()); + + customVariables.remove("a"); + assertNull(customVariables.get("a")); + assertNull(customVariables.get(1)); + assertNull(customVariables.get(5)); + assertEquals("{\"2\":[\"c\",\"d\"]}", customVariables.toString()); + + customVariables.remove(2); + assertNull(customVariables.get("c")); + assertNull(customVariables.get(2)); + assertTrue(customVariables.isEmpty()); + assertEquals("{}", customVariables.toString()); + } + + @Test + public void testAddCustomVariableIndexLessThan1() { + try { + customVariables.add(new CustomVariable("a", "b"), 0); + fail("Exception should have been throw."); + } catch (IllegalArgumentException e) { + assertEquals("Index must be greater than 0.", e.getLocalizedMessage()); + } + } + + @Test + public void testGetCustomVariableIntegerLessThan1() { + try { + customVariables.get(0); + fail("Exception should have been throw."); + } catch (IllegalArgumentException e) { + assertEquals("Index must be greater than 0.", e.getLocalizedMessage()); + } + } +} diff --git a/src/test/java/org/piwik/java/tracking/EcommerceItemTest.java b/src/test/java/org/matomo/java/tracking/EcommerceItemTest.java similarity index 75% rename from src/test/java/org/piwik/java/tracking/EcommerceItemTest.java rename to src/test/java/org/matomo/java/tracking/EcommerceItemTest.java index 52dbb9be..986fb305 100644 --- a/src/test/java/org/piwik/java/tracking/EcommerceItemTest.java +++ b/src/test/java/org/matomo/java/tracking/EcommerceItemTest.java @@ -1,10 +1,10 @@ /* - * Piwik Java Tracker + * Matomo Java Tracker * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ -package org.piwik.java.tracking; +package org.matomo.java.tracking; import org.junit.After; import org.junit.AfterClass; @@ -12,8 +12,6 @@ import org.junit.BeforeClass; import org.junit.Test; -import javax.json.JsonValue.ValueType; - import static org.junit.Assert.assertEquals; /** @@ -106,22 +104,5 @@ public void testGetQuantity() { assertEquals(new Integer(1), ecommerceItem.getQuantity()); } - /** - * Test of getValueType method, of class EcommerceItem. - */ - @Test - public void testGetValueType() { - assertEquals(ValueType.ARRAY, ecommerceItem.getValueType()); - } - - /** - * Test of toString method, of class EcommerceItem. - */ - @Test - public void testToString() { - ecommerceItem = new EcommerceItem("sku", "name", "category", 1.0, 1); - - assertEquals("[\"sku\",\"name\",\"category\",1.0,1]", ecommerceItem.toString()); - } } diff --git a/src/test/java/org/matomo/java/tracking/EcommerceItemsTest.java b/src/test/java/org/matomo/java/tracking/EcommerceItemsTest.java new file mode 100644 index 00000000..ee76b7ce --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/EcommerceItemsTest.java @@ -0,0 +1,18 @@ +package org.matomo.java.tracking; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class EcommerceItemsTest { + + @Test + public void formatsJson() { + + EcommerceItems ecommerceItems = new EcommerceItems(); + ecommerceItems.add(new EcommerceItem("sku", "name", "category", 1.0, 1)); + + assertEquals("[[\"sku\",\"name\",\"category\",1.0,1]]", ecommerceItems.toString()); + + } +} diff --git a/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java b/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java new file mode 100644 index 00000000..1583aea7 --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java @@ -0,0 +1,48 @@ +package org.matomo.java.tracking; + +import org.junit.Test; + +import java.net.URL; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class MatomoRequestBuilderTest { + + @Test + public void buildsRequest() throws Exception { + + CustomVariable customVariable = new CustomVariable("pageCustomVariableName", "pageCustomVariableValue"); + MatomoRequest matomoRequest = MatomoRequest.builder() + .siteId(42) + .actionName("ACTION_NAME") + .actionUrl("https://www.your-domain.tld/some/page?query=foo") + .referrerUrl("https://referrer.com") + .customTrackingParameters(Collections.singletonMap("trackingParameterName", "trackingParameterValue")) + .pageCustomVariables(Collections.singletonList(customVariable)) + .visitCustomVariables(Collections.singletonList(customVariable)) + .build(); + + Map> parameters = matomoRequest.getParameters(); + assertThat(parameters.get("idsite"), hasItem((Object) 42)); + assertThat(parameters.get("action_name"), hasItem((Object) "ACTION_NAME")); + assertThat(parameters.get("apiv"), hasItem((Object) "1")); + assertThat(parameters.get("url"), hasItem((Object) new URL("https://www.your-domain.tld/some/page?query=foo"))); + assertThat(parameters.get("_id").isEmpty(), is(false)); + assertThat(parameters.get("rand").isEmpty(), is(false)); + assertThat(parameters.get("send_image"), hasItem((Object) new MatomoBoolean(false))); + assertThat(parameters.get("rec"), hasItem((Object) new MatomoBoolean(true))); + assertThat(parameters.get("urlref"), hasItem((Object) new URL("https://referrer.com"))); + assertThat(parameters.get("trackingParameterName"), hasItem((Object) "trackingParameterValue")); + CustomVariables customVariables = new CustomVariables(); + customVariables.add(customVariable); + assertThat(parameters.get("cvar"), hasItem((Object) customVariables)); + assertThat(parameters.get("_cvar"), hasItem((Object) customVariables)); + + } + +} diff --git a/src/test/java/org/piwik/java/tracking/PiwikDateTest.java b/src/test/java/org/matomo/java/tracking/PiwikDateTest.java similarity index 79% rename from src/test/java/org/piwik/java/tracking/PiwikDateTest.java rename to src/test/java/org/matomo/java/tracking/PiwikDateTest.java index 1025b5eb..0ba201d5 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikDateTest.java +++ b/src/test/java/org/matomo/java/tracking/PiwikDateTest.java @@ -1,12 +1,13 @@ /* - * Piwik Java Tracker + * Matomo Java Tracker * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ -package org.piwik.java.tracking; +package org.matomo.java.tracking; import org.junit.Test; +import org.piwik.java.tracking.PiwikDate; import java.util.TimeZone; diff --git a/src/test/java/org/piwik/java/tracking/PiwikLocaleTest.java b/src/test/java/org/matomo/java/tracking/PiwikLocaleTest.java similarity index 83% rename from src/test/java/org/piwik/java/tracking/PiwikLocaleTest.java rename to src/test/java/org/matomo/java/tracking/PiwikLocaleTest.java index b14c4943..1931e5e6 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikLocaleTest.java +++ b/src/test/java/org/matomo/java/tracking/PiwikLocaleTest.java @@ -1,16 +1,17 @@ /* * Piwik Java Tracker * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ -package org.piwik.java.tracking; +package org.matomo.java.tracking; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.piwik.java.tracking.PiwikLocale; import java.util.Locale; diff --git a/src/test/java/org/piwik/java/tracking/PiwikRequestTest.java b/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java similarity index 82% rename from src/test/java/org/piwik/java/tracking/PiwikRequestTest.java rename to src/test/java/org/matomo/java/tracking/PiwikRequestTest.java index 6009f7b5..b9cc3f8f 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikRequestTest.java +++ b/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java @@ -1,16 +1,18 @@ /* * Piwik Java Tracker * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ -package org.piwik.java.tracking; +package org.matomo.java.tracking; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.piwik.java.tracking.PiwikDate; +import org.piwik.java.tracking.PiwikLocale; +import org.piwik.java.tracking.PiwikRequest; -import javax.xml.bind.TypeConstraintException; import java.net.URL; import java.nio.charset.Charset; import java.util.List; @@ -82,25 +84,15 @@ public void testActionUrl() throws Exception { URL url = new URL("http://action.com"); request.setActionUrl(url); assertEquals(url, request.getActionUrl()); - try { - request.getActionUrlAsString(); - fail("Exception should have been thrown."); - } catch (TypeConstraintException e) { - assertEquals("The stored Action URL is a URL, not a String. Use \"getActionUrl\" instead.", e.getLocalizedMessage()); - } + assertEquals("http://action.com", request.getActionUrlAsString()); request.setActionUrlWithString(null); assertNull(request.getActionUrl()); assertNull(request.getActionUrlAsString()); - request.setActionUrlWithString("actionUrl"); - assertEquals("actionUrl", request.getActionUrlAsString()); - try { - request.getActionUrl(); - fail("Exception should have been thrown."); - } catch (TypeConstraintException e) { - assertEquals("The stored Action URL is a String, not a URL. Use \"getActionUrlAsString\" instead.", e.getLocalizedMessage()); - } + request.setActionUrlWithString("http://actionstring.com"); + assertEquals("http://actionstring.com", request.getActionUrlAsString()); + assertEquals(new URL("http://actionstring.com"), request.getActionUrl()); } /** @@ -214,23 +206,12 @@ public void testContentTarget() throws Exception { URL url = new URL("http://target.com"); request.setContentTarget(url); assertEquals(url, request.getContentTarget()); + assertEquals("http://target.com", request.getContentTargetAsString()); - try { - request.getContentTargetAsString(); - fail("Exception should have been thrown."); - } catch (TypeConstraintException e) { - assertEquals("The stored Content Target is a URL, not a String. Use \"getContentTarget\" instead.", e.getLocalizedMessage()); - } + request.setContentTargetWithString("http://targetstring.com"); + assertEquals("http://targetstring.com", request.getContentTargetAsString()); + assertEquals(new URL("http://targetstring.com"), request.getContentTarget()); - request.setContentTargetWithString("contentTarget"); - assertEquals("contentTarget", request.getContentTargetAsString()); - - try { - request.getContentTarget(); - fail("Exception should have been thrown."); - } catch (TypeConstraintException e) { - assertEquals("The stored Content Target is a String, not a URL. Use \"getContentTargetAsString\" instead.", e.getLocalizedMessage()); - } } /** @@ -269,7 +250,7 @@ public void testGetCustomTrackingParameter_T() { request.getCustomTrackingParameter(null); fail("Exception should have been thrown."); } catch (NullPointerException e) { - assertEquals("Key cannot be null.", e.getLocalizedMessage()); + assertEquals("key is marked non-null but is null", e.getLocalizedMessage()); } } @@ -284,7 +265,7 @@ public void testSetCustomTrackingParameter_T() { request.setCustomTrackingParameter(null, null); fail("Exception should have been thrown."); } catch (NullPointerException e) { - assertEquals("Key cannot be null.", e.getLocalizedMessage()); + assertEquals("key is marked non-null but is null", e.getLocalizedMessage()); } } @@ -311,7 +292,7 @@ public void testAddCustomTrackingParameter_T() { request.addCustomTrackingParameter(null, null); fail("Exception should have been thrown."); } catch (NullPointerException e) { - assertEquals("Key cannot be null.", e.getLocalizedMessage()); + assertEquals("key is marked non-null but is null", e.getLocalizedMessage()); } } @@ -321,7 +302,7 @@ public void testAddCustomTrackingParameter_FT() { request.addCustomTrackingParameter("key", null); fail("Exception should have been thrown."); } catch (NullPointerException e) { - assertEquals("Cannot add a null custom tracking parameter.", e.getLocalizedMessage()); + assertEquals("value is marked non-null but is null", e.getLocalizedMessage()); } } @@ -364,23 +345,12 @@ public void testDownloadUrl() throws Exception { URL url = new URL("http://download.com"); request.setDownloadUrl(url); assertEquals(url, request.getDownloadUrl()); + assertEquals("http://download.com", request.getDownloadUrlAsString()); - try { - request.getDownloadUrlAsString(); - fail("Exception should have been thrown."); - } catch (TypeConstraintException e) { - assertEquals("The stored Download URL is a URL, not a String. Use \"getDownloadUrl\" instead.", e.getLocalizedMessage()); - } - - request.setDownloadUrlWithString("downloadUrl"); - assertEquals("downloadUrl", request.getDownloadUrlAsString()); + request.setDownloadUrlWithString("http://downloadstring.com"); + assertEquals("http://downloadstring.com", request.getDownloadUrlAsString()); + assertEquals(new URL("http://downloadstring.com"), request.getDownloadUrl()); - try { - request.getDownloadUrl(); - fail("Exception should have been thrown."); - } catch (TypeConstraintException e) { - assertEquals("The stored Download URL is a String, not a URL. Use \"getDownloadUrlAsString\" instead.", e.getLocalizedMessage()); - } } /** @@ -402,7 +372,7 @@ public void testVerifyEcommerceEnabledT() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -414,7 +384,7 @@ public void testVerifyEcommerceEnabledFT() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -434,7 +404,7 @@ public void testVerifyEcommerceStateE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -446,7 +416,7 @@ public void testVerifyEcommerceStateT() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("EcommerceId must be set before this value can be set.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -459,7 +429,7 @@ public void testVerifyEcommerceStateFT() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("EcommerceRevenue must be set before this value can be set.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -491,7 +461,7 @@ public void testEcommerceDiscountTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -520,7 +490,7 @@ public void testEcommerceIdTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -542,7 +512,7 @@ public void testEcommerceItemE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -555,7 +525,7 @@ public void testEcommerceItemE2() { request.addEcommerceItem(null); fail("Exception should have been thrown."); } catch (NullPointerException e) { - assertEquals("Value cannot be null.", e.getLocalizedMessage()); + assertEquals("item is marked non-null but is null", e.getLocalizedMessage()); } } @@ -595,7 +565,7 @@ public void testEcommerceLastOrderTimestampTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -625,7 +595,7 @@ public void testEcommerceRevenueTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -656,7 +626,7 @@ public void testEcommerceShippingCostTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -687,7 +657,7 @@ public void testEcommerceSubtotalTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -718,7 +688,7 @@ public void testEcommerceTaxTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -797,7 +767,7 @@ public void testGoalRevenueTT() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be set before GoalRevenue can be set.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -853,23 +823,12 @@ public void testOutlinkUrl() throws Exception { URL url = new URL("http://outlink.com"); request.setOutlinkUrl(url); assertEquals(url, request.getOutlinkUrl()); + assertEquals("http://outlink.com", request.getOutlinkUrlAsString()); - try { - request.getOutlinkUrlAsString(); - fail("Exception should have been thrown."); - } catch (TypeConstraintException e) { - assertEquals("The stored Outlink URL is a URL, not a String. Use \"getOutlinkUrl\" instead.", e.getLocalizedMessage()); - } - - request.setOutlinkUrlWithString("outlinkUrl"); - assertEquals("outlinkUrl", request.getOutlinkUrlAsString()); + request.setOutlinkUrlWithString("http://outlinkstring.com"); + assertEquals("http://outlinkstring.com", request.getOutlinkUrlAsString()); + assertEquals(new URL("http://outlinkstring.com"), request.getOutlinkUrl()); - try { - request.getOutlinkUrl(); - fail("Exception should have been thrown."); - } catch (TypeConstraintException e) { - assertEquals("The stored Outlink URL is a String, not a URL. Use \"getOutlinkUrlAsString\" instead.", e.getLocalizedMessage()); - } } /** @@ -881,7 +840,7 @@ public void testPageCustomVariableStringStringE() { request.setPageCustomVariable(null, null); fail("Exception should have been thrown"); } catch (NullPointerException e) { - assertEquals("Key cannot be null.", e.getLocalizedMessage()); + assertEquals("key is marked non-null but is null", e.getLocalizedMessage()); } } @@ -891,7 +850,7 @@ public void testPageCustomVariableStringStringE2() { request.setPageCustomVariable(null, "pageVal"); fail("Exception should have been thrown"); } catch (NullPointerException e) { - assertEquals("Key cannot be null.", e.getLocalizedMessage()); + assertEquals("key is marked non-null but is null", e.getLocalizedMessage()); } } @@ -901,7 +860,7 @@ public void testPageCustomVariableStringStringE3() { request.getPageCustomVariable(null); fail("Exception should have been thrown"); } catch (NullPointerException e) { - assertEquals("Key cannot be null.", e.getLocalizedMessage()); + assertEquals("key is marked non-null but is null", e.getLocalizedMessage()); } } @@ -1026,23 +985,12 @@ public void testReferrerUrl() throws Exception { URL url = new URL("http://referrer.com"); request.setReferrerUrl(url); assertEquals(url, request.getReferrerUrl()); + assertEquals("http://referrer.com", request.getReferrerUrlAsString()); - try { - request.getReferrerUrlAsString(); - fail("Exception should have been thrown."); - } catch (TypeConstraintException e) { - assertEquals("The stored Referrer URL is a URL, not a String. Use \"getReferrerUrl\" instead.", e.getLocalizedMessage()); - } - - request.setReferrerUrlWithString("referrerUrl"); - assertEquals("referrerUrl", request.getReferrerUrlAsString()); + request.setReferrerUrlWithString("http://referrerstring.com"); + assertEquals("http://referrerstring.com", request.getReferrerUrlAsString()); + assertEquals(new URL("http://referrerstring.com"), request.getReferrerUrl()); - try { - request.getReferrerUrl(); - fail("Exception should have been thrown."); - } catch (TypeConstraintException e) { - assertEquals("The stored Referrer URL is a String, not a URL. Use \"getReferrerUrlAsString\" instead.", e.getLocalizedMessage()); - } } /** @@ -1065,7 +1013,7 @@ public void testRequestDatetimeTTF() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("Because you are trying to set RequestDatetime for a time greater than 4 hours ago, AuthToken must be set first.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1112,7 +1060,7 @@ public void testSearchCategoryTT() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("SearchQuery must be set before SearchCategory can be set.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1148,7 +1096,7 @@ public void testSearchResultsCountTT() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("SearchQuery must be set before SearchResultsCount can be set.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1201,22 +1149,22 @@ public void testVisitCustomVariableCustomVariable() { assertNull(request.getVisitCustomVariable(1)); CustomVariable cv = new CustomVariable("visitKey", "visitVal"); request.setVisitCustomVariable(cv, 1); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_cvar={\"1\":[\"visitKey\",\"visitVal\"]}&_id=1234567890123456&url=http://test.com", request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&_cvar={\"1\":[\"visitKey\",\"visitVal\"]}", request.getQueryString()); request.setUserCustomVariable("key", "val"); assertEquals(cv, request.getVisitCustomVariable(1)); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_cvar={\"1\":[\"visitKey\",\"visitVal\"],\"2\":[\"key\",\"val\"]}&_id=1234567890123456&url=http://test.com", request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&_cvar={\"1\":[\"visitKey\",\"visitVal\"],\"2\":[\"key\",\"val\"]}", request.getQueryString()); request.setVisitCustomVariable(null, 1); assertNull(request.getVisitCustomVariable(1)); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_cvar={\"2\":[\"key\",\"val\"]}&_id=1234567890123456&url=http://test.com", request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&_cvar={\"2\":[\"key\",\"val\"]}", request.getQueryString()); request.setVisitCustomVariable(cv, 2); assertEquals(cv, request.getVisitCustomVariable(2)); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_cvar={\"2\":[\"visitKey\",\"visitVal\"]}&_id=1234567890123456&url=http://test.com", request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&_cvar={\"2\":[\"visitKey\",\"visitVal\"]}", request.getQueryString()); request.setUserCustomVariable("visitKey", null); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com", request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456", request.getQueryString()); } /** @@ -1245,7 +1193,7 @@ public void testVisitorCityTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1275,7 +1223,7 @@ public void testVisitorCountryTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1296,7 +1244,7 @@ public void testVisitorCustomTT() { fail("Exception should have been thrown."); } catch (IllegalArgumentException e) { assertEquals("1 is not 16 characters long.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1307,7 +1255,7 @@ public void testVisitorCustomTFT() { fail("Exception should have been thrown."); } catch (IllegalArgumentException e) { assertEquals("1234567890abcdeg is not a hexadecimal string.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1343,7 +1291,7 @@ public void testVisitorIdTT() { fail("Exception should have been thrown."); } catch (IllegalArgumentException e) { assertEquals("1 is not 16 characters long.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1354,7 +1302,7 @@ public void testVisitorIdTFT() { fail("Exception should have been thrown."); } catch (IllegalArgumentException e) { assertEquals("1234567890abcdeg is not a hexadecimal string.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1388,7 +1336,7 @@ public void testVisitorIpTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1415,7 +1363,7 @@ public void testVisitorLatitudeTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1442,7 +1390,7 @@ public void testVisitorLongitudeTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1479,7 +1427,7 @@ public void testGetVisitorRegionTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1506,20 +1454,20 @@ public void testVisitorVisitCount() { public void testGetQueryString() { request.setRandomValue("random"); request.setVisitorId("1234567890123456"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com", request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456", request.getQueryString()); request.setPageCustomVariable("key", "val"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&cvar={\"1\":[\"key\",\"val\"]}&_id=1234567890123456&url=http://test.com", - request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&cvar={\"1\":[\"key\",\"val\"]}", + request.getQueryString()); request.setPageCustomVariable("key", null); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com", request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456", request.getQueryString()); request.addCustomTrackingParameter("key", "test"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com&key=test", request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&key=test", request.getQueryString()); request.addCustomTrackingParameter("key", "test2"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com&key=test&key=test2", request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&key=test&key=test2", request.getQueryString()); request.setCustomTrackingParameter("key2", "test3"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com&key2=test3&key=test&key=test2", request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&key=test&key=test2&key2=test3", request.getQueryString()); request.setCustomTrackingParameter("key", "test4"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com&key2=test3&key=test4", request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&key2=test3&key=test4", request.getQueryString()); request.setRandomValue(null); request.setSiteId(null); request.setRequired(null); @@ -1537,7 +1485,7 @@ public void testGetQueryString2() { request.setActionUrlWithString("http://test.com"); request.setRandomValue("random"); request.setVisitorId("1234567890123456"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com", request.getQueryString()); + assertEquals("idsite=3&rec=1&apiv=1&send_image=0&url=http://test.com&rand=random&_id=1234567890123456", request.getQueryString()); } /** @@ -1547,15 +1495,15 @@ public void testGetQueryString2() { public void testGetUrlEncodedQueryString() { request.setRandomValue("random"); request.setVisitorId("1234567890123456"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); + assertEquals("_id=1234567890123456&apiv=1&idsite=3&rand=random&rec=1&send_image=0&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); request.addCustomTrackingParameter("ke/y", "te:st"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com&ke%2Fy=te%3Ast", request.getUrlEncodedQueryString()); + assertEquals("_id=1234567890123456&apiv=1&idsite=3&ke%2Fy=te%3Ast&rand=random&rec=1&send_image=0&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); request.addCustomTrackingParameter("ke/y", "te:st2"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2", request.getUrlEncodedQueryString()); + assertEquals("_id=1234567890123456&apiv=1&idsite=3&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2&rand=random&rec=1&send_image=0&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); request.setCustomTrackingParameter("ke/y2", "te:st3"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2&ke%2Fy2=te%3Ast3", request.getUrlEncodedQueryString()); + assertEquals("_id=1234567890123456&apiv=1&idsite=3&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2&ke%2Fy2=te%3Ast3&rand=random&rec=1&send_image=0&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); request.setCustomTrackingParameter("ke/y", "te:st4"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com&ke%2Fy=te%3Ast4&ke%2Fy2=te%3Ast3", request.getUrlEncodedQueryString()); + assertEquals("_id=1234567890123456&apiv=1&idsite=3&ke%2Fy=te%3Ast4&ke%2Fy2=te%3Ast3&rand=random&rec=1&send_image=0&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); request.setRandomValue(null); request.setSiteId(null); request.setRequired(null); @@ -1573,7 +1521,7 @@ public void testGetUrlEncodedQueryString2() { request.setActionUrlWithString("http://test.com"); request.setRandomValue("random"); request.setVisitorId("1234567890123456"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); + assertEquals("_id=1234567890123456&apiv=1&idsite=3&rand=random&rec=1&send_image=0&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); } /** diff --git a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java b/src/test/java/org/matomo/java/tracking/PiwikTrackerTest.java similarity index 86% rename from src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java rename to src/test/java/org/matomo/java/tracking/PiwikTrackerTest.java index f31979cd..e94354c0 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java +++ b/src/test/java/org/matomo/java/tracking/PiwikTrackerTest.java @@ -1,10 +1,10 @@ /* * Piwik Java Tracker * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ -package org.piwik.java.tracking; +package org.matomo.java.tracking; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; @@ -22,6 +22,8 @@ import org.junit.BeforeClass; import org.junit.Test; import org.mockito.ArgumentMatcher; +import org.piwik.java.tracking.PiwikRequest; +import org.piwik.java.tracking.PiwikTracker; import java.io.IOException; import java.io.InputStream; @@ -29,12 +31,21 @@ import java.net.InetSocketAddress; import java.net.URL; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.concurrent.*; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.doReturn; @@ -45,6 +56,8 @@ * @author brettcsorba */ public class PiwikTrackerTest { + private static final Map> PARAMETERS = Collections.singletonMap("parameterName", Collections.singleton("parameterValue")); + // https://stackoverflow.com/a/3732328 static class Handler implements HttpHandler { @Override @@ -110,9 +123,9 @@ public void testSendRequest() throws Exception { HttpResponse response = mock(HttpResponse.class); doReturn(client).when(piwikTracker).getHttpClient(); - doReturn("query").when(request).getQueryString(); + doReturn(PARAMETERS).when(request).getParameters(); doReturn(response).when(client) - .execute(argThat(new CorrectGetRequest("http://test.com?query"))); + .execute(argThat(new CorrectGetRequest("http://test.com?parameterName=parameterValue"))); assertEquals(response, piwikTracker.sendRequest(request)); } @@ -128,11 +141,11 @@ public void testSendRequestAsync() throws Exception { Future future = mock(Future.class); doReturn(client).when(piwikTracker).getHttpAsyncClient(); - doReturn("query").when(request).getQueryString(); + doReturn(PARAMETERS).when(request).getParameters(); doReturn(response).when(future).get(); doReturn(true).when(future).isDone(); doReturn(future).when(client) - .execute(argThat(new CorrectGetRequest("http://test.com?query")), any()); + .execute(argThat(new CorrectGetRequest("http://test.com?parameterName=parameterValue")), any()); assertEquals(response, piwikTracker.sendRequestAsync(request).get()); } @@ -260,7 +273,7 @@ public void testSendBulkRequest_Iterable_StringTT() throws Exception { HttpClient client = mock(HttpClient.class); PiwikRequest request = mock(PiwikRequest.class); - doReturn("query").when(request).getQueryString(); + doReturn(PARAMETERS).when(request).getParameters(); requests.add(request); doReturn(client).when(piwikTracker).getHttpClient(); @@ -278,10 +291,10 @@ public void testSendBulkRequest_Iterable_StringFF() throws Exception { PiwikRequest request = mock(PiwikRequest.class); HttpResponse response = mock(HttpResponse.class); - doReturn("query").when(request).getQueryString(); + doReturn(PARAMETERS).when(request).getParameters(); requests.add(request); doReturn(client).when(piwikTracker).getHttpClient(); - doReturn(response).when(client).execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"]}"))); + doReturn(response).when(client).execute(argThat(new CorrectPostRequest("{\"requests\":[\"?parameterName=parameterValue\"]}"))); assertEquals(response, piwikTracker.sendBulkRequest(requests, null)); } @@ -293,11 +306,11 @@ public void testSendBulkRequest_Iterable_StringFT() throws Exception { PiwikRequest request = mock(PiwikRequest.class); HttpResponse response = mock(HttpResponse.class); - doReturn("query").when(request).getQueryString(); + doReturn(PARAMETERS).when(request).getParameters(); requests.add(request); doReturn(client).when(piwikTracker).getHttpClient(); doReturn(response).when(client) - .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"],\"token_auth\":\"12345678901234567890123456789012\"}"))); + .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?parameterName=parameterValue\"],\"token_auth\":\"12345678901234567890123456789012\"}"))); assertEquals(response, piwikTracker.sendBulkRequest(requests, "12345678901234567890123456789012")); } @@ -328,7 +341,7 @@ public void testSendBulkRequestAsync_Iterable_StringTT() throws Exception { CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); PiwikRequest request = mock(PiwikRequest.class); - doReturn("query").when(request).getQueryString(); + doReturn(PARAMETERS).when(request).getParameters(); requests.add(request); doReturn(client).when(piwikTracker).getHttpAsyncClient(); @@ -349,11 +362,11 @@ public void testSendBulkRequestAsync_Iterable_StringFF() throws Exception { doReturn(response).when(future).get(); doReturn(true).when(future).isDone(); - doReturn("query").when(request).getQueryString(); + doReturn(PARAMETERS).when(request).getParameters(); requests.add(request); doReturn(client).when(piwikTracker).getHttpAsyncClient(); doReturn(future).when(client) - .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"]}")), any()); + .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?parameterName=parameterValue\"]}")), any()); assertEquals(response, piwikTracker.sendBulkRequestAsync(requests).get()); } @@ -368,11 +381,11 @@ public void testSendBulkRequestAsync_Iterable_StringFT() throws Exception { doReturn(response).when(future).get(); doReturn(true).when(future).isDone(); - doReturn("query").when(request).getQueryString(); + doReturn(PARAMETERS).when(request).getParameters(); requests.add(request); doReturn(client).when(piwikTracker).getHttpAsyncClient(); doReturn(future).when(client) - .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"],\"token_auth\":\"12345678901234567890123456789012\"}")), any()); + .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?parameterName=parameterValue\"],\"token_auth\":\"12345678901234567890123456789012\"}")), any()); assertEquals(response, piwikTracker.sendBulkRequestAsync(requests, "12345678901234567890123456789012").get()); } diff --git a/src/test/java/org/piwik/java/tracking/CustomVariableListTest.java b/src/test/java/org/piwik/java/tracking/CustomVariableListTest.java deleted file mode 100644 index 76b71600..00000000 --- a/src/test/java/org/piwik/java/tracking/CustomVariableListTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.piwik.java.tracking; - -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -/** - * @author Katie - */ -public class CustomVariableListTest { - private CustomVariableList cvl; - - @Before - public void setUp() { - cvl = new CustomVariableList(); - } - - @Test - public void testAdd_CustomVariable() { - CustomVariable a = new CustomVariable("a", "b"); - CustomVariable b = new CustomVariable("c", "d"); - CustomVariable c = new CustomVariable("a", "e"); - CustomVariable d = new CustomVariable("a", "f"); - - assertTrue(cvl.isEmpty()); - cvl.add(a); - assertFalse(cvl.isEmpty()); - assertEquals("b", cvl.get("a")); - assertEquals(a, cvl.get(1)); - assertEquals("{\"1\":[\"a\",\"b\"]}", cvl.toString()); - - cvl.add(b); - assertEquals("d", cvl.get("c")); - assertEquals(b, cvl.get(2)); - assertEquals("{\"1\":[\"a\",\"b\"],\"2\":[\"c\",\"d\"]}", cvl.toString()); - - cvl.add(c, 5); - assertEquals("b", cvl.get("a")); - assertEquals(c, cvl.get(5)); - assertNull(cvl.get(3)); - assertEquals("{\"1\":[\"a\",\"b\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"e\"]}", cvl.toString()); - - cvl.add(d); - assertEquals("f", cvl.get("a")); - assertEquals(d, cvl.get(1)); - assertEquals(d, cvl.get(5)); - assertEquals("{\"1\":[\"a\",\"f\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"f\"]}", cvl.toString()); - - cvl.remove("a"); - assertNull(cvl.get("a")); - assertNull(cvl.get(1)); - assertNull(cvl.get(5)); - assertEquals("{\"2\":[\"c\",\"d\"]}", cvl.toString()); - - cvl.remove(2); - assertNull(cvl.get("c")); - assertNull(cvl.get(2)); - assertTrue(cvl.isEmpty()); - assertEquals("{}", cvl.toString()); - } - - @Test - public void testAddCustomVariableIndexLessThan1() { - try { - cvl.add(new CustomVariable("a", "b"), 0); - fail("Exception should have been throw."); - } catch (IllegalArgumentException e) { - assertEquals("Index must be greater than 0.", e.getLocalizedMessage()); - } - } - - @Test - public void testGetCustomVariableIntegerLessThan1() { - try { - cvl.get(0); - fail("Exception should have been throw."); - } catch (IllegalArgumentException e) { - assertEquals("Index must be greater than 0.", e.getLocalizedMessage()); - } - } -} diff --git a/src/test/java/org/piwik/java/tracking/PiwikJsonArrayTest.java b/src/test/java/org/piwik/java/tracking/PiwikJsonArrayTest.java deleted file mode 100644 index bed3d3b1..00000000 --- a/src/test/java/org/piwik/java/tracking/PiwikJsonArrayTest.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Piwik Java Tracker - * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause - */ -package org.piwik.java.tracking; - -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -import javax.json.JsonValue; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; - -/** - * @author brettcsorba - */ -public class PiwikJsonArrayTest { - PiwikJsonArray array; - - public PiwikJsonArrayTest() { - } - - @BeforeClass - public static void setUpClass() { - } - - @AfterClass - public static void tearDownClass() { - } - - @Before - public void setUp() { - array = new PiwikJsonArray(); - } - - @After - public void tearDown() { - } - - /** - * Test of get method, of class PiwikJsonArray. - */ - @Test - public void testAddGetSet() { - JsonValue value = mock(JsonValue.class); - JsonValue value2 = mock(JsonValue.class); - - array.add(value); - assertEquals(value, array.get(0)); - - array.set(0, value2); - assertEquals(value2, array.get(0)); - } - - /** - * Test of toString method, of class PiwikJsonArray. - */ - @Test - public void testToString() { - array.add(JsonValue.TRUE); - array.add(new EcommerceItem("a", "b", "c", 1.0, 2)); - array.add(JsonValue.FALSE); - - assertEquals("[true,[\"a\",\"b\",\"c\",1.0,2],false]", array.toString()); - } - -} diff --git a/src/test/java/org/piwik/java/tracking/PiwikJsonObjectTest.java b/src/test/java/org/piwik/java/tracking/PiwikJsonObjectTest.java deleted file mode 100644 index 358be490..00000000 --- a/src/test/java/org/piwik/java/tracking/PiwikJsonObjectTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Piwik Java Tracker - * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause - */ -package org.piwik.java.tracking; - -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -/** - * @author brettcsorba - */ -public class PiwikJsonObjectTest { - PiwikJsonObject obj; - - public PiwikJsonObjectTest() { - } - - @BeforeClass - public static void setUpClass() { - } - - @AfterClass - public static void tearDownClass() { - } - - @Before - public void setUp() { - obj = new PiwikJsonObject(); - } - - @After - public void tearDown() { - } - - /** - * Test of get method, of class PiwikJsonObject. - */ - @Test - public void testMethods() { - assertTrue(obj.isEmpty()); - assertEquals(0, obj.size()); - assertNull(obj.put("key", "value")); - assertFalse(obj.isEmpty()); - assertEquals(1, obj.size()); - assertEquals("value", obj.get("key")); - assertEquals("value", obj.remove("key")); - assertNull(obj.remove("key")); - assertTrue(obj.isEmpty()); - assertEquals(0, obj.size()); - } - - /** - * Test of toString method, of class PiwikJsonObject. - */ - @Test - public void testToString() { - obj.put("key", "value"); - obj.put("key2", "value2"); - obj.put("key3", "value3"); - obj.remove("key2"); - - assertEquals("{\"1\":[\"key\",\"value\"],\"2\":[\"key3\",\"value3\"]}", obj.toString()); - } - -} From b644fa386ec86314a18ed76fc2fca3069bf1a521 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 2 Aug 2022 15:48:01 +0200 Subject: [PATCH 052/467] Fixes #58 --- pom.xml | 9 +- .../matomo/java/tracking/MatomoRequest.java | 161 ++++++++++++------ .../java/tracking/MatomoRequestBuilder.java | 67 ++------ .../org/piwik/java/tracking/PiwikRequest.java | 5 +- .../tracking/MatomoRequestBuilderTest.java | 21 ++- .../java/tracking/PiwikRequestTest.java | 6 +- 6 files changed, 139 insertions(+), 130 deletions(-) diff --git a/pom.xml b/pom.xml index c32db240..59f84176 100644 --- a/pom.xml +++ b/pom.xml @@ -50,6 +50,7 @@ UTF-8 + 1.7.36 @@ -170,7 +171,7 @@ org.slf4j slf4j-api - 1.7.36 + ${slf4j.version} org.mockito @@ -184,6 +185,12 @@ 4.13.2 test + + org.slf4j + slf4j-simple + ${slf4j.version} + test + org.projectlombok lombok diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/src/main/java/org/matomo/java/tracking/MatomoRequest.java index 2d73c0bb..b8006720 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -134,7 +134,7 @@ public class MatomoRequest { * @param siteId the id of the website we're tracking a visit/action for * @param actionUrl the full URL for the current action */ - public MatomoRequest(int siteId, URL actionUrl) { + public MatomoRequest(int siteId, String actionUrl) { setParameter(SITE_ID, siteId); setBooleanParameter(REQUIRED, true); setParameter(ACTION_URL, actionUrl); @@ -198,10 +198,11 @@ public void setActionTime(Long actionTime) { * Get the full URL for the current action. * * @return the full URL + * @deprecated Please use {@link #getActionUrlAsString} */ @Nullable public URL getActionUrl() { - return castOrNull(ACTION_URL); + return castToUrlOrNull(ACTION_URL); } /** @@ -209,18 +210,30 @@ public URL getActionUrl() { * * @return the full URL */ + @Deprecated @Nullable public String getActionUrlAsString() { - return getUrlParameter(ACTION_URL); + return castOrNull(ACTION_URL); } + /** + * Set the full URL for the current action. + * + * @param actionUrl the full URL to set. A null value will remove this parameter + * @deprecated Please use {@link #setActionUrl(String)} + */ + @Deprecated + public void setActionUrl(@NonNull URL actionUrl) { + setActionUrl(actionUrl.toString()); + } + /** * Set the full URL for the current action. * * @param actionUrl the full URL to set. A null value will remove this parameter */ - public void setActionUrl(URL actionUrl) { + public void setActionUrl(String actionUrl) { setParameter(ACTION_URL, actionUrl); } @@ -228,9 +241,11 @@ public void setActionUrl(URL actionUrl) { * Set the full URL for the current action. * * @param actionUrl the full URL to set. A null value will remove this parameter + * @deprecated Please use {@link #setActionUrl(String)} */ + @Deprecated public void setActionUrlWithString(String actionUrl) { - setUrlParameter(ACTION_URL, actionUrl); + setActionUrl(actionUrl); } /** @@ -414,7 +429,20 @@ public void setContentPiece(String contentPiece) { */ @Nullable public URL getContentTarget() { - return castOrNull(CONTENT_TARGET); + return castToUrlOrNull(CONTENT_TARGET); + } + + @Nullable + private URL castToUrlOrNull(@NonNull String key) { + String url = castOrNull(key); + if (url == null) { + return null; + } + try { + return new URL(url); + } catch (MalformedURLException e) { + throw new InvalidUrlException(e); + } } /** @@ -424,15 +452,26 @@ public URL getContentTarget() { */ @Nullable public String getContentTargetAsString() { - return getUrlParameter(CONTENT_TARGET); + return castOrNull(CONTENT_TARGET); } /** * Set the target of the content. For instance the URL of a landing page. * * @param contentTarget the target to set. A null value will remove this parameter + * @deprecated Please use {@link #setContentTarget(String)} */ - public void setContentTarget(URL contentTarget) { + @Deprecated + public void setContentTarget(@NonNull URL contentTarget) { + setContentTarget(contentTarget.toString()); + } + + /** + * Set the target of the content. For instance the URL of a landing page. + * + * @param contentTarget the target to set. A null value will remove this parameter + */ + public void setContentTarget(String contentTarget) { setParameter(CONTENT_TARGET, contentTarget); } @@ -440,9 +479,11 @@ public void setContentTarget(URL contentTarget) { * Set the target of the content. For instance the URL of a landing page. * * @param contentTarget the target to set. A null value will remove this parameter + * @deprecated Please use {@link #setContentTarget(String)} */ + @Deprecated public void setContentTargetWithString(String contentTarget) { - setUrlParameter(CONTENT_TARGET, contentTarget); + setContentTarget(contentTarget); } /** @@ -577,7 +618,7 @@ public void setDeviceResolution(String deviceResolution) { */ @Nullable public URL getDownloadUrl() { - return castOrNull(DOWNLOAD_URL); + return castToUrlOrNull(DOWNLOAD_URL); } /** @@ -587,7 +628,19 @@ public URL getDownloadUrl() { */ @Nullable public String getDownloadUrlAsString() { - return getUrlParameter(DOWNLOAD_URL); + return castOrNull(DOWNLOAD_URL); + } + + /** + * Set the url of a file the user has downloaded. Used for tracking downloads. + * We recommend to also set the url parameter to this same value. + * + * @param downloadUrl the url to set. A null value will remove this parameter + * @deprecated Please use {@link #setDownloadUrl(String)} + */ + @Deprecated + public void setDownloadUrl(@NonNull URL downloadUrl) { + setDownloadUrl(downloadUrl.toString()); } /** @@ -596,7 +649,7 @@ public String getDownloadUrlAsString() { * * @param downloadUrl the url to set. A null value will remove this parameter */ - public void setDownloadUrl(URL downloadUrl) { + public void setDownloadUrl(String downloadUrl) { setParameter(DOWNLOAD_URL, downloadUrl); } @@ -605,9 +658,11 @@ public void setDownloadUrl(URL downloadUrl) { * We recommend to also set the url parameter to this same value. * * @param downloadUrl the url to set. A null value will remove this parameter + * @deprecated Please use {@link #setDownloadUrl(String)} */ + @Deprecated public void setDownloadUrlWithString(String downloadUrl) { - setUrlParameter(DOWNLOAD_URL, downloadUrl); + setDownloadUrl(downloadUrl); } /** @@ -1030,7 +1085,7 @@ public void setNewVisit(Boolean newVisit) { */ @Nullable public URL getOutlinkUrl() { - return castOrNull(OUTLINK_URL); + return castToUrlOrNull(OUTLINK_URL); } /** @@ -1040,16 +1095,29 @@ public URL getOutlinkUrl() { */ @Nullable public String getOutlinkUrlAsString() { - return getUrlParameter(OUTLINK_URL); + return castOrNull(OUTLINK_URL); + } + + /** + * Set an external URL the user has opened. Used for tracking outlink clicks. + * We recommend to also set the url parameter to this same value. + * + * @param outlinkUrl the outlink url to set. A null value will remove this parameter + * @deprecated Please use {@link #setOutlinkUrl(String)} + */ + @Deprecated + public void setOutlinkUrl(@NonNull URL outlinkUrl) { + setOutlinkUrl(outlinkUrl.toString()); } + /** * Set an external URL the user has opened. Used for tracking outlink clicks. * We recommend to also set the url parameter to this same value. * * @param outlinkUrl the outlink url to set. A null value will remove this parameter */ - public void setOutlinkUrl(URL outlinkUrl) { + public void setOutlinkUrl(String outlinkUrl) { setParameter(OUTLINK_URL, outlinkUrl); } @@ -1058,9 +1126,11 @@ public void setOutlinkUrl(URL outlinkUrl) { * We recommend to also set the url parameter to this same value. * * @param outlinkUrl the outlink url to set. A null value will remove this parameter + * @deprecated Please use {@link #setOutlinkUrl(String)} */ + @Deprecated public void setOutlinkUrlWithString(String outlinkUrl) { - setUrlParameter(OUTLINK_URL, outlinkUrl); + setOutlinkUrl(outlinkUrl); } /** @@ -1313,7 +1383,7 @@ public void setRandomValue(String randomValue) { */ @Nullable public URL getReferrerUrl() { - return castOrNull(REFERRER_URL); + return castToUrlOrNull(REFERRER_URL); } /** @@ -1323,7 +1393,7 @@ public URL getReferrerUrl() { */ @Nullable public String getReferrerUrlAsString() { - return getUrlParameter(REFERRER_URL); + return castOrNull(REFERRER_URL); } /** @@ -1331,8 +1401,20 @@ public String getReferrerUrlAsString() { * got to your website (ie, through a website, search engine or campaign). * * @param referrerUrl the referrer url to set. A null value will remove this parameter + * @deprecated Please use {@link #setReferrerUrl(String)} */ - public void setReferrerUrl(URL referrerUrl) { + @Deprecated + public void setReferrerUrl(@NonNull URL referrerUrl) { + setReferrerUrl(referrerUrl.toString()); + } + + /** + * Set the full HTTP Referrer URL. This value is used to determine how someone + * got to your website (ie, through a website, search engine or campaign). + * + * @param referrerUrl the referrer url to set. A null value will remove this parameter + */ + public void setReferrerUrl(String referrerUrl) { setParameter(REFERRER_URL, referrerUrl); } @@ -1341,9 +1423,11 @@ public void setReferrerUrl(URL referrerUrl) { * got to your website (ie, through a website, search engine or campaign). * * @param referrerUrl the referrer url to set. A null value will remove this parameter + * @deprecated Please use {@link #setReferrerUrl(String)} */ + @Deprecated public void setReferrerUrlWithString(String referrerUrl) { - setUrlParameter(REFERRER_URL, referrerUrl); + setReferrerUrl(referrerUrl); } /** @@ -2000,41 +2084,6 @@ private void setBooleanParameter(@NonNull String key, @Nullable Boolean value) { } } - /** - * Get a stored parameter that is a URL. - * - * @param key the parameter's key - * @return the stored parameter's value - */ - @Nullable - private String getUrlParameter(@NonNull String key) { - URL url = castOrNull(key); - if (url == null) { - return null; - } - return url.toString(); - } - - /** - * Set a stored parameter that is a valid URL. It will be syntactically checked - * - * @param key the parameter's key - * @param url the parameter's value. Removes the parameter if null - * @throws InvalidUrlException if the URL is invalid - */ - private void setUrlParameter(@NonNull String key, @Nullable String url) { - if (url == null) { - setParameter(key, null); - } else { - try { - setParameter(key, new URL(url)); - } catch (MalformedURLException e) { - throw new InvalidUrlException(e); - } - } - - } - /** * Get a value that is stored in a json object at the specified parameter. * diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java b/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java index 70b60ad3..39866f62 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java +++ b/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java @@ -1,7 +1,5 @@ package org.matomo.java.tracking; -import java.net.MalformedURLException; -import java.net.URL; import java.nio.charset.Charset; import java.util.List; import java.util.Map; @@ -10,7 +8,7 @@ public class MatomoRequestBuilder { private int siteId; - private URL actionUrl; + private String actionUrl; private String actionName; private Long actionTime; @@ -22,12 +20,12 @@ public class MatomoRequestBuilder { private String contentInteraction; private String contentName; private String contentPiece; - private URL contentTarget; + private String contentTarget; private Integer currentHour; private Integer currentMinute; private Integer currentSecond; private String deviceResolution; - private URL downloadUrl; + private String downloadUrl; private Double ecommerceDiscount; private String ecommerceId; private Long ecommerceLastOrderTimestamp; @@ -44,7 +42,7 @@ public class MatomoRequestBuilder { private String headerAcceptLanguage; private String headerUserAgent; private Boolean newVisit; - private URL outlinkUrl; + private String outlinkUrl; private Boolean pluginDirector; private Boolean pluginFlash; private Boolean pluginGears; @@ -55,7 +53,7 @@ public class MatomoRequestBuilder { private Boolean pluginSilverlight; private Boolean pluginWindowsMedia; private String randomValue; - private URL referrerUrl; + private String referrerUrl; private MatomoDate requestDatetime; private Boolean required; private Boolean responseAsImage; @@ -86,17 +84,8 @@ public MatomoRequestBuilder siteId(int siteId) { return this; } - public MatomoRequestBuilder actionUrl(URL actionUrl) { - this.actionUrl = actionUrl; - return this; - } - public MatomoRequestBuilder actionUrl(String actionUrl) { - try { - this.actionUrl = new URL(actionUrl); - } catch (MalformedURLException e) { - throw new InvalidUrlException(e); - } + this.actionUrl = actionUrl; return this; } @@ -150,17 +139,8 @@ public MatomoRequestBuilder contentPiece(String contentPiece) { return this; } - public MatomoRequestBuilder contentTarget(URL contentTarget) { - this.contentTarget = contentTarget; - return this; - } - public MatomoRequestBuilder contentTarget(String contentTarget) { - try { - this.contentTarget = new URL(contentTarget); - } catch (MalformedURLException e) { - throw new InvalidUrlException(e); - } + this.contentTarget = contentTarget; return this; } @@ -184,17 +164,8 @@ public MatomoRequestBuilder deviceResolution(String deviceResolution) { return this; } - public MatomoRequestBuilder downloadUrl(URL downloadUrl) { - this.downloadUrl = downloadUrl; - return this; - } - public MatomoRequestBuilder downloadUrl(String downloadUrl) { - try { - this.downloadUrl = new URL(downloadUrl); - } catch (MalformedURLException e) { - throw new InvalidUrlException(e); - } + this.downloadUrl = downloadUrl; return this; } @@ -278,17 +249,8 @@ public MatomoRequestBuilder newVisit(Boolean newVisit) { return this; } - public MatomoRequestBuilder outlinkUrl(URL outlinkUrl) { - this.outlinkUrl = outlinkUrl; - return this; - } - public MatomoRequestBuilder outlinkUrl(String outlinkUrl) { - try { - this.outlinkUrl = new URL(outlinkUrl); - } catch (MalformedURLException e) { - throw new InvalidUrlException(e); - } + this.outlinkUrl = outlinkUrl; return this; } @@ -342,17 +304,8 @@ public MatomoRequestBuilder randomValue(String randomValue) { return this; } - public MatomoRequestBuilder referrerUrl(URL referrerUrl) { - this.referrerUrl = referrerUrl; - return this; - } - public MatomoRequestBuilder referrerUrl(String referrerUrl) { - try { - this.referrerUrl = new URL(referrerUrl); - } catch (MalformedURLException e) { - throw new InvalidUrlException(e); - } + this.referrerUrl = referrerUrl; return this; } diff --git a/src/main/java/org/piwik/java/tracking/PiwikRequest.java b/src/main/java/org/piwik/java/tracking/PiwikRequest.java index b7ad9181..bab72b01 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikRequest.java +++ b/src/main/java/org/piwik/java/tracking/PiwikRequest.java @@ -6,6 +6,7 @@ */ package org.piwik.java.tracking; +import lombok.NonNull; import org.matomo.java.tracking.MatomoRequest; import java.net.URL; @@ -21,7 +22,7 @@ public class PiwikRequest extends MatomoRequest { * @deprecated Use {@link MatomoRequest} instead. */ @Deprecated - public PiwikRequest(Integer siteId, URL actionUrl) { - super(siteId, actionUrl); + public PiwikRequest(int siteId, @NonNull URL actionUrl) { + super(siteId, actionUrl.toString()); } } diff --git a/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java b/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java index 1583aea7..e7d61f5f 100644 --- a/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java +++ b/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java @@ -2,7 +2,6 @@ import org.junit.Test; -import java.net.URL; import java.util.Collection; import java.util.Collections; import java.util.Map; @@ -28,20 +27,20 @@ public void buildsRequest() throws Exception { .build(); Map> parameters = matomoRequest.getParameters(); - assertThat(parameters.get("idsite"), hasItem((Object) 42)); - assertThat(parameters.get("action_name"), hasItem((Object) "ACTION_NAME")); - assertThat(parameters.get("apiv"), hasItem((Object) "1")); - assertThat(parameters.get("url"), hasItem((Object) new URL("https://www.your-domain.tld/some/page?query=foo"))); + assertThat(parameters.get("idsite"), hasItem(42)); + assertThat(parameters.get("action_name"), hasItem("ACTION_NAME")); + assertThat(parameters.get("apiv"), hasItem("1")); + assertThat(parameters.get("url"), hasItem("https://www.your-domain.tld/some/page?query=foo")); assertThat(parameters.get("_id").isEmpty(), is(false)); assertThat(parameters.get("rand").isEmpty(), is(false)); - assertThat(parameters.get("send_image"), hasItem((Object) new MatomoBoolean(false))); - assertThat(parameters.get("rec"), hasItem((Object) new MatomoBoolean(true))); - assertThat(parameters.get("urlref"), hasItem((Object) new URL("https://referrer.com"))); - assertThat(parameters.get("trackingParameterName"), hasItem((Object) "trackingParameterValue")); + assertThat(parameters.get("send_image"), hasItem(new MatomoBoolean(false))); + assertThat(parameters.get("rec"), hasItem(new MatomoBoolean(true))); + assertThat(parameters.get("urlref"), hasItem("https://referrer.com")); + assertThat(parameters.get("trackingParameterName"), hasItem("trackingParameterValue")); CustomVariables customVariables = new CustomVariables(); customVariables.add(customVariable); - assertThat(parameters.get("cvar"), hasItem((Object) customVariables)); - assertThat(parameters.get("_cvar"), hasItem((Object) customVariables)); + assertThat(parameters.get("cvar"), hasItem(customVariables)); + assertThat(parameters.get("_cvar"), hasItem(customVariables)); } diff --git a/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java b/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java index b9cc3f8f..e50fc017 100644 --- a/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java +++ b/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java @@ -77,7 +77,7 @@ public void testActionTime() { */ @Test public void testActionUrl() throws Exception { - request.setActionUrl(null); + request.setActionUrl((String) null); assertNull(request.getActionUrl()); assertNull(request.getActionUrlAsString()); @@ -1474,7 +1474,7 @@ public void testGetQueryString() { request.setApiVersion(null); request.setResponseAsImage(null); request.setVisitorId(null); - request.setActionUrl(null); + request.setActionUrl((String) null); assertEquals("key2=test3&key=test4", request.getQueryString()); request.clearCustomTrackingParameter(); assertEquals("", request.getQueryString()); @@ -1510,7 +1510,7 @@ public void testGetUrlEncodedQueryString() { request.setApiVersion(null); request.setResponseAsImage(null); request.setVisitorId(null); - request.setActionUrl(null); + request.setActionUrl((String) null); assertEquals("ke%2Fy=te%3Ast4&ke%2Fy2=te%3Ast3", request.getUrlEncodedQueryString()); request.clearCustomTrackingParameter(); assertEquals("", request.getUrlEncodedQueryString()); From 26a767f189f25b498f77b4a73927ff33e43f01fb Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 2 Aug 2022 15:59:57 +0200 Subject: [PATCH 053/467] Fixes #41 --- .../matomo/java/tracking/MatomoRequest.java | 21 +++++++++++++++++++ .../java/tracking/MatomoRequestBuilder.java | 10 +++++++++ .../tracking/MatomoRequestBuilderTest.java | 4 +++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/src/main/java/org/matomo/java/tracking/MatomoRequest.java index b8006720..d2d1df29 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -55,6 +55,8 @@ public class MatomoRequest { private static final String CURRENT_HOUR = "h"; private static final String CURRENT_MINUTE = "m"; private static final String CURRENT_SECOND = "s"; + + private static final String CUSTOM_ACTION = "ca"; private static final String DEVICE_RESOLUTION = "res"; private static final String DOWNLOAD_URL = "download"; private static final String ECOMMERCE_DISCOUNT = "ec_dt"; @@ -543,6 +545,25 @@ public void setCurrentSecond(Integer currentSecond) { setParameter(CURRENT_SECOND, currentSecond); } + /** + * Get the custom action + * + * @return the custom action + */ + @Nullable + public Boolean getCustomAction() { + return getBooleanParameter(CUSTOM_ACTION); + } + + /** + * Set the custom action + * + * @param customAction the second to set. A null value will remove this parameter + */ + public void setCustomAction(Boolean customAction) { + setBooleanParameter(CUSTOM_ACTION, customAction); + } + /** * Gets the list of objects currently stored at the specified custom tracking * parameter. An empty list will be returned if there are no objects set at diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java b/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java index 39866f62..712a0be9 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java +++ b/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java @@ -24,6 +24,8 @@ public class MatomoRequestBuilder { private Integer currentHour; private Integer currentMinute; private Integer currentSecond; + + private Boolean customAction; private String deviceResolution; private String downloadUrl; private Double ecommerceDiscount; @@ -159,6 +161,11 @@ public MatomoRequestBuilder currentSecond(Integer currentSecond) { return this; } + public MatomoRequestBuilder customAction(Boolean customAction) { + this.customAction = customAction; + return this; + } + public MatomoRequestBuilder deviceResolution(String deviceResolution) { this.deviceResolution = deviceResolution; return this; @@ -463,6 +470,9 @@ public MatomoRequest build() { if (currentSecond != null) { matomoRequest.setCurrentSecond(currentSecond); } + if (customAction != null) { + matomoRequest.setCustomAction(customAction); + } if (customTrackingParameters != null) { for (Map.Entry customTrackingParameter : customTrackingParameters.entrySet()) { matomoRequest.addCustomTrackingParameter(customTrackingParameter.getKey(), customTrackingParameter.getValue()); diff --git a/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java b/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java index e7d61f5f..9d2804ed 100644 --- a/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java +++ b/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java @@ -13,7 +13,7 @@ public class MatomoRequestBuilderTest { @Test - public void buildsRequest() throws Exception { + public void buildsRequest() { CustomVariable customVariable = new CustomVariable("pageCustomVariableName", "pageCustomVariableValue"); MatomoRequest matomoRequest = MatomoRequest.builder() @@ -24,6 +24,7 @@ public void buildsRequest() throws Exception { .customTrackingParameters(Collections.singletonMap("trackingParameterName", "trackingParameterValue")) .pageCustomVariables(Collections.singletonList(customVariable)) .visitCustomVariables(Collections.singletonList(customVariable)) + .customAction(true) .build(); Map> parameters = matomoRequest.getParameters(); @@ -41,6 +42,7 @@ public void buildsRequest() throws Exception { customVariables.add(customVariable); assertThat(parameters.get("cvar"), hasItem(customVariables)); assertThat(parameters.get("_cvar"), hasItem(customVariables)); + assertThat(parameters.get("ca"), hasItem(new MatomoBoolean(true))); } From 3d90390db1bb605541759fba48b4be89cf357335 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 8 Aug 2022 14:46:49 +0200 Subject: [PATCH 054/467] Removes unused method --- src/main/java/org/matomo/java/tracking/EcommerceItems.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/org/matomo/java/tracking/EcommerceItems.java b/src/main/java/org/matomo/java/tracking/EcommerceItems.java index 9f435535..a27ae2f2 100644 --- a/src/main/java/org/matomo/java/tracking/EcommerceItems.java +++ b/src/main/java/org/matomo/java/tracking/EcommerceItems.java @@ -31,10 +31,6 @@ public EcommerceItem get(int index) { return ecommerceItems.get(index); } - public EcommerceItem set(int index, EcommerceItem element) { - return ecommerceItems.set(index, element); - } - @Override public String toString() { ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode(); From 88a4730673c35254c02e3761af3959184d9cbd93 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 8 Aug 2022 15:44:08 +0200 Subject: [PATCH 055/467] Removes unused exception --- src/main/java/org/matomo/java/tracking/MatomoTracker.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/src/main/java/org/matomo/java/tracking/MatomoTracker.java index 4b0d8882..9d2ef7ae 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoTracker.java +++ b/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -229,9 +229,8 @@ public Future sendBulkRequestAsync(final Iterable sendBulkRequestAsync(final Iterable requests, final String authToken, FutureCallback callback) throws IOException { + public Future sendBulkRequestAsync(final Iterable requests, final String authToken, FutureCallback callback) { if (authToken != null && authToken.length() != MatomoRequest.AUTH_TOKEN_LENGTH) { throw new IllegalArgumentException(authToken + " is not " + MatomoRequest.AUTH_TOKEN_LENGTH + " characters long."); } From 4800dc67a003deb6729c2b9d765a63279de41be1 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 8 Aug 2022 15:44:17 +0200 Subject: [PATCH 056/467] Enhance README --- README.md | 251 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 218 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 175813f9..7b325aa1 100644 --- a/README.md +++ b/README.md @@ -15,17 +15,48 @@ releases can be found [here](https://matomo-org.github.io/matomo-java-tracker/ja ## Using this API +### Add library to your build + +Add a dependency on Matomo Java Tracker using Maven: + +```xml + + + org.piwik.java.tracking + matomo-java-tracker + 2.0 + +``` + +or Gradle: + +```groovy +dependencies { + implementation("org.piwik.java.tracking:matomo-java-tracker:2.0") +} +``` + ### Create a Request Each MatomoRequest represents an action the user has taken that you want tracked by your Matomo server. Create a MatomoRequest through ```java -MatomoRequest request=MatomoRequest.builder() - .siteId(42) - .actionUrl("https://www.mydomain.com/some/site") - .actionName("Signup") - .build(); + +import org.matomo.java.tracking.MatomoRequest; + +public class YourImplementation { + + public void yourMethod() { + MatomoRequest request = MatomoRequest.builder() + .siteId(42) + .actionUrl("https://www.mydomain.com/signup") + .actionName("Signup") + .build(); + } + +} + ``` Per default every request has the following default parameters: @@ -40,76 +71,230 @@ Per default every request has the following default parameters: Overwrite these properties as desired. -Note that if you want to be able to track campaigns using Referrers > Campaigns, you must add the correct +Note that if you want to be able to track campaigns using *Referrers > Campaigns*, you must add the correct URL parameters to your actionUrl. For example, ```java -URL actionUrl=new URL("http://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore"); + +package example; + +import org.matomo.java.tracking.MatomoRequest; + +public class YourImplementation { + + public void yourMethod() { + + MatomoRequest request = MatomoRequest.builder() + .siteId(42) + .actionUrl("http://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore") // include the query parameters to the url + .actionName("LearnMore") + .build(); + } + +} ``` See [Tracking Campaigns](https://matomo.org/docs/tracking-campaigns/) for more information. All HTTP query parameters denoted on the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate -getters and setters. See MatomoRequest.java for the mappings of the parameters to their corresponding +getters and setters. See _MatomoRequest.java_ for the mappings of the parameters to their corresponding Java getters/setters. Some parameters are dependent on the state of other parameters: -EcommerceEnabled must be called before the following parameters are set: EcommerceId and -EcommerceRevenue. +_EcommerceEnabled_ must be called before the following parameters are set: *EcommerceId* and * +EcommerceRevenue*. -EcommerceId and EcommerceRevenue must be set before the following parameters are -set: EcommerceDiscount, EcommerceItem, EcommerceLastOrderTimestamp, -EcommerceShippingCost, EcommerceSubtotal, and EcommerceTax. +_EcommerceId_ and _EcommerceRevenue_ must be set before the following parameters are +set: *EcommerceDiscount*, *EcommerceItem*, *EcommerceLastOrderTimestamp*, * +EcommerceShippingCost*, *EcommerceSubtotal*, and *EcommerceTax*. -AuthToken must be set before the following parameters are set: VisitorCity, -VisitorCountry, VisitorIp, VisitorLatitude, VisitorLongitude, and VisitorRegion +_AuthToken_ must be set before the following parameters are set: *VisitorCity*, * +VisitorCountry*, *VisitorIp*, *VisitorLatitude*, *VisitorLongitude*, and *VisitorRegion* . ### Sending Requests -Create a MatomoTracker through +Create a MatomoTracker using the constructor ```java -MatomoTracker tracker=new MatomoTracker(hostUrl); +package example; + +import org.matomo.java.tracking.MatomoTracker; + +public class YourImplementation { + + public void yourMethod() { + + MatomoTracker tracker = new MatomoTracker("https://your-matomo-domain.tld/matomo.php"); + + } + +} ``` -where hostUrl is the url endpoint of the Matomo server. Usually in the format http://your-matomo-domain.tld/matomo.php. +using the Matomo Endpoint URL as the first parameter. To send a single request, call ```java -Future response=tracker.sendRequestAsync(request); +package example; + +import org.apache.http.HttpResponse; +import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoTracker; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +public class YourImplementation { + + public void yourMethod() { + + MatomoRequest request = MatomoRequest.builder().siteId(42).actionUrl("https://www.mydomain.com/some/page").actionName("Signup").build(); + + MatomoTracker tracker = new MatomoTracker("https://your-matomo-domain.tld/matomo.php"); + try { + Future response = tracker.sendRequestAsync(request); + // usually not needed: + HttpResponse httpResponse = response.get(); + int statusCode = httpResponse.getStatusLine().getStatusCode(); + if (statusCode > 399) { + // problem + } + } catch (IOException e) { + throw new UncheckedIOException("Could not send request to Matomo", e); + } catch (ExecutionException | InterruptedException e) { + throw new RuntimeException("Error while getting response", e); + } + + } + +} ``` If you have multiple requests to wish to track, it may be more efficient to send them in a single HTTP call. To do this, -send a bulk request. Place your requests in an Iterable data structure and call +send a bulk request. Place your requests in an _Iterable_ data structure and call ```java -Future response=tracker.sendBulkRequestAsync(requests); +package example; + +import org.apache.http.HttpResponse; +import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoRequestBuilder; +import org.matomo.java.tracking.MatomoTracker; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +public class YourImplementation { + + public void yourMethod() { + + Collection requests = new ArrayList<>(); + MatomoRequestBuilder builder = MatomoRequest.builder().siteId(42); + requests.add(builder.actionUrl("https://www.mydomain.com/some/page").actionName("Some Page").build()); + requests.add(builder.actionUrl("https://www.mydomain.com/another/page").actionName("Another Page").build()); + + MatomoTracker tracker = new MatomoTracker("https://your-matomo-domain.tld/matomo.php"); + try { + Future response = tracker.sendBulkRequestAsync(requests); + // usually not needed: + HttpResponse httpResponse = response.get(); + int statusCode = httpResponse.getStatusLine().getStatusCode(); + if (statusCode > 399) { + // problem + } + } catch (IOException e) { + throw new UncheckedIOException("Could not send request to Matomo", e); + } catch (ExecutionException | InterruptedException e) { + throw new RuntimeException("Error while getting response", e); + } + + } + +} + ``` If some of the parameters that you've specified in the bulk request require AuthToken to be set, this can also be set in the bulk request through ```java -Future response=tracker.sendBulkRequestAsync(requests,authToken); +package example; + +import org.apache.http.HttpResponse; +import org.matomo.java.tracking.MatomoLocale; +import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoRequestBuilder; +import org.matomo.java.tracking.MatomoTracker; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Locale; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +public class YourImplementation { + + public void yourMethod() { + + Collection requests = new ArrayList<>(); + MatomoRequestBuilder builder = MatomoRequest.builder().siteId(42); + requests.add(builder.actionUrl("https://www.mydomain.com/some/page").actionName("Some Page").build()); + requests.add(builder.actionUrl("https://www.mydomain.com/another/page").actionName("Another Page").visitorCountry(new MatomoLocale(Locale.GERMANY)).build()); + + MatomoTracker tracker = new MatomoTracker("https://your-matomo-domain.tld/matomo.php"); + try { + Future response = tracker.sendBulkRequestAsync(requests, "33dc3f2536d3025974cccb4b4d2d98f4"); // second parameter is authentication token need for country override + // usually not needed: + HttpResponse httpResponse = response.get(); + int statusCode = httpResponse.getStatusLine().getStatusCode(); + if (statusCode > 399) { + // problem + } + } catch (IOException e) { + throw new UncheckedIOException("Could not send request to Matomo", e); + } catch (ExecutionException | InterruptedException e) { + throw new RuntimeException("Error while getting response", e); + } + + } + +} + + ``` -## Install +## Building + +You need a GPG signing key on your machine. Please follow these +instructions: https://docs.github.com/en/authentication/managing-commit-signature-verification/generating-a-new-gpg-key This project can be tested and built by calling ```shell -mvn package +mvn install ``` -The built jars and javadoc can be found in target +The built jars and javadoc can be found in `target`. By using the install Maven goal, the snapshot +version can be used using your local Maven repository for testing purposes, e.g. -Test this project using +```xml -```shell -mvn test + + org.piwik.java.tracking + matomo-java-tracker + 2.1-SNAPSHOT + ``` This project also supports [Pitest](http://pitest.org/) mutation testing. This report can be generated by calling @@ -118,7 +303,7 @@ This project also supports [Pitest](http://pitest.org/) mutation testing. This r mvn org.pitest:pitest-maven:mutationCoverage ``` -and will produce an HTML report at target/pit-reports/YYYYMMDDHHMI +and will produce an HTML report at `target/pit-reports/YYYYMMDDHHMI` Clean this project using @@ -132,12 +317,12 @@ Have a fantastic feature idea? Spot a bug? We would absolutely love for you to c free to: * Fork this project -* Create a feature branch from the master branch +* Create a feature branch from the _master_ branch * Write awesome code that does awesome things * Write awesome test to test your awesome code -* Verify that everything is working as it should by running mvn test. If everything passes, you may - want to make sure that your tests are covering everything you think they are! Run mvn org.pitest: - pitest-maven:mutationCoverage to find out! +* Verify that everything is working as it should by running _mvn test_. If everything passes, you may + want to make sure that your tests are covering everything you think they are! + Run `mvn org.pitest:pitest-maven:mutationCoverage` to find out! * Commit this code to your repository * Submit a pull request from your branch to our dev branch and let us know why you made the changes you did * We'll take a look at your request and work to get it integrated with the repo! From 3579e46cc6d32e0b0715f7a53a715acf00a68a8d Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 8 Aug 2022 16:10:14 +0200 Subject: [PATCH 057/467] Use unchecked exceptions --- .../matomo/java/tracking/MatomoException.java | 10 +++ .../matomo/java/tracking/MatomoTracker.java | 72 +++++++++++-------- 2 files changed, 51 insertions(+), 31 deletions(-) create mode 100644 src/main/java/org/matomo/java/tracking/MatomoException.java diff --git a/src/main/java/org/matomo/java/tracking/MatomoException.java b/src/main/java/org/matomo/java/tracking/MatomoException.java new file mode 100644 index 00000000..39ea492e --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/MatomoException.java @@ -0,0 +1,10 @@ +package org.matomo.java.tracking; + +public class MatomoException extends RuntimeException { + + private static final long serialVersionUID = 4592083764365938934L; + + public MatomoException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/src/main/java/org/matomo/java/tracking/MatomoTracker.java index 9d2ef7ae..4ac30954 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoTracker.java +++ b/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -9,6 +9,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.NonNull; import lombok.extern.slf4j.Slf4j; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; @@ -21,6 +22,8 @@ import org.apache.http.entity.StringEntity; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; @@ -49,9 +52,9 @@ public class MatomoTracker { * Tracking HTTP API endpoint. * * @param hostUrl url endpoint to send requests to. Usually in the format - * http://your-matomo-domain.tld/matomo.php. + * http://your-matomo-domain.tld/matomo.php. Must not be null */ - public MatomoTracker(final String hostUrl) { + public MatomoTracker(@NonNull final String hostUrl) { this(hostUrl, DEFAULT_TIMEOUT); } @@ -63,11 +66,11 @@ public MatomoTracker(final String hostUrl) { * http://your-matomo-domain.tld/matomo.php. * @param timeout the timeout of the sent request in milliseconds */ - public MatomoTracker(final String hostUrl, final int timeout) { + public MatomoTracker(@NonNull final String hostUrl, final int timeout) { this(hostUrl, null, 0, timeout); } - public MatomoTracker(final String hostUrl, final String proxyHost, final int proxyPort, final int timeout) { + public MatomoTracker(@NonNull final String hostUrl, @Nullable final String proxyHost, final int proxyPort, final int timeout) { this.hostUrl = URI.create(hostUrl); this.proxyHost = proxyHost; this.proxyPort = proxyPort; @@ -83,27 +86,36 @@ public MatomoTracker(final String hostUrl, final String proxyHost, final int pro * @param proxyHost url endpoint for the proxy * @param proxyPort proxy server port number */ - public MatomoTracker(final String hostUrl, final String proxyHost, final int proxyPort) { + public MatomoTracker(@NonNull final String hostUrl, @Nullable final String proxyHost, final int proxyPort) { this(hostUrl, proxyHost, proxyPort, DEFAULT_TIMEOUT); } /** - * Send a request. + * Sends a tracking request to Matomo * - * @param request request to send + * @param request request to send. must not be null * @return the response from this request * @throws IOException thrown if there was a problem with this connection * @deprecated use sendRequestAsync instead */ @Deprecated - public HttpResponse sendRequest(final MatomoRequest request) throws IOException { + public HttpResponse sendRequest(@NonNull final MatomoRequest request) { final HttpClient client = getHttpClient(); + HttpUriRequest get = createGetRequest(request); + log.debug("Sending request via GET: {}", request); try { - HttpUriRequest get = new HttpGet(new URIBuilder(hostUrl).addParameters(QueryParameters.fromMap(request.getParameters())).build()); - log.debug("Sending request via GET: {}", request); return client.execute(get); - } catch (final URISyntaxException e) { - throw new IOException(e); + } catch (IOException e) { + throw new MatomoException("Could not send request to Matomo", e); + } + } + + @Nonnull + private HttpUriRequest createGetRequest(@NonNull MatomoRequest request) { + try { + return new HttpGet(new URIBuilder(hostUrl).addParameters(QueryParameters.fromMap(request.getParameters())).build()); + } catch (URISyntaxException e) { + throw new InvalidUrlException(e); } } @@ -123,7 +135,7 @@ protected HttpClient getHttpClient() { * @return future with response from this request * @throws IOException thrown if there was a problem with this connection */ - public Future sendRequestAsync(final MatomoRequest request) throws IOException { + public Future sendRequestAsync(@NonNull final MatomoRequest request) { return sendRequestAsync(request, null); } @@ -135,17 +147,12 @@ public Future sendRequestAsync(final MatomoRequest request) throws * @return future with response from this request * @throws IOException thrown if there was a problem with this connection */ - public Future sendRequestAsync(final MatomoRequest request, FutureCallback callback) throws IOException { + public Future sendRequestAsync(@NonNull final MatomoRequest request, @Nullable FutureCallback callback) { final CloseableHttpAsyncClient client = getHttpAsyncClient(); client.start(); - - try { - HttpUriRequest get = new HttpGet(new URIBuilder(hostUrl).addParameters(QueryParameters.fromMap(request.getParameters())).build()); - log.debug("Sending async request via GET: {}", request); - return client.execute(get, callback); - } catch (final URISyntaxException e) { - throw new IOException(e); - } + HttpUriRequest get = createGetRequest(request); + log.debug("Sending async request via GET: {}", request); + return client.execute(get, callback); } /** @@ -167,7 +174,7 @@ protected CloseableHttpAsyncClient getHttpAsyncClient() { * @deprecated use sendBulkRequestAsync instead */ @Deprecated - public HttpResponse sendBulkRequest(final Iterable requests) throws IOException { + public HttpResponse sendBulkRequest(@NonNull final Iterable requests) { return sendBulkRequest(requests, null); } @@ -183,18 +190,21 @@ public HttpResponse sendBulkRequest(final Iterable requ * @deprecated use sendBulkRequestAsync instead */ @Deprecated - public HttpResponse sendBulkRequest(final Iterable requests, final String authToken) throws IOException { + public HttpResponse sendBulkRequest(@NonNull final Iterable requests, @Nullable final String authToken) { if (authToken != null && authToken.length() != MatomoRequest.AUTH_TOKEN_LENGTH) { throw new IllegalArgumentException(authToken + " is not " + MatomoRequest.AUTH_TOKEN_LENGTH + " characters long."); } - HttpPost post = buildPost(requests, authToken); final HttpClient client = getHttpClient(); log.debug("Sending requests via POST: {}", requests); - return client.execute(post); + try { + return client.execute(post); + } catch (IOException e) { + throw new MatomoException("Could not send bulk request", e); + } } - private HttpPost buildPost(Iterable requests, String authToken) { + private HttpPost buildPost(@NonNull Iterable requests, @Nullable String authToken) { ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); ArrayNode requestsNode = objectNode.putArray(REQUESTS); for (final MatomoRequest request : requests) { @@ -216,7 +226,7 @@ private HttpPost buildPost(Iterable requests, String au * @return future with response from these requests * @throws IOException thrown if there was a problem with this connection */ - public Future sendBulkRequestAsync(final Iterable requests) throws IOException { + public Future sendBulkRequestAsync(@NonNull final Iterable requests) { return sendBulkRequestAsync(requests, null, null); } @@ -230,7 +240,7 @@ public Future sendBulkRequestAsync(final Iterable sendBulkRequestAsync(final Iterable requests, final String authToken, FutureCallback callback) { + public Future sendBulkRequestAsync(@NonNull final Iterable requests, @Nullable final String authToken, @Nullable FutureCallback callback) { if (authToken != null && authToken.length() != MatomoRequest.AUTH_TOKEN_LENGTH) { throw new IllegalArgumentException(authToken + " is not " + MatomoRequest.AUTH_TOKEN_LENGTH + " characters long."); } @@ -250,7 +260,7 @@ public Future sendBulkRequestAsync(final Iterable sendBulkRequestAsync(final Iterable requests, FutureCallback callback) throws IOException { + public Future sendBulkRequestAsync(@NonNull final Iterable requests, @Nullable FutureCallback callback) { return sendBulkRequestAsync(requests, null, callback); } @@ -264,7 +274,7 @@ public Future sendBulkRequestAsync(final Iterable sendBulkRequestAsync(final Iterable requests, final String authToken) throws IOException { + public Future sendBulkRequestAsync(@NonNull final Iterable requests, @Nullable final String authToken) { return sendBulkRequestAsync(requests, authToken, null); } } From c41ef4e5d85d353897839b3d3f679d07360f38b3 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 8 Aug 2022 16:11:24 +0200 Subject: [PATCH 058/467] Remove unnecessary throws declarations --- .../java/org/matomo/java/tracking/PiwikTrackerTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/matomo/java/tracking/PiwikTrackerTest.java b/src/test/java/org/matomo/java/tracking/PiwikTrackerTest.java index e94354c0..b37a9ebc 100644 --- a/src/test/java/org/matomo/java/tracking/PiwikTrackerTest.java +++ b/src/test/java/org/matomo/java/tracking/PiwikTrackerTest.java @@ -254,7 +254,7 @@ public boolean matches(HttpGet get) { * Test of sendBulkRequest method, of class PiwikTracker. */ @Test - public void testSendBulkRequest_Iterable() throws Exception { + public void testSendBulkRequest_Iterable() { List requests = new ArrayList<>(); HttpResponse response = mock(HttpResponse.class); @@ -267,7 +267,7 @@ public void testSendBulkRequest_Iterable() throws Exception { * Test of sendBulkRequest method, of class PiwikTracker. */ @Test - public void testSendBulkRequest_Iterable_StringTT() throws Exception { + public void testSendBulkRequest_Iterable_StringTT() { try { List requests = new ArrayList<>(); HttpClient client = mock(HttpClient.class); @@ -335,7 +335,7 @@ public void testSendBulkRequestAsync_Iterable() throws Exception { * Test of sendBulkRequestAsync method, of class PiwikTracker. */ @Test - public void testSendBulkRequestAsync_Iterable_StringTT() throws Exception { + public void testSendBulkRequestAsync_Iterable_StringTT() { try { List requests = new ArrayList<>(); CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); From 8b456c9b7cb3c7280a2c190bb9e43c389b856df1 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 8 Aug 2022 16:15:53 +0200 Subject: [PATCH 059/467] Removes invalid Javadoc --- .../java/org/matomo/java/tracking/HttpClientFactory.java | 2 +- src/main/java/org/matomo/java/tracking/MatomoRequest.java | 2 +- src/main/java/org/matomo/java/tracking/MatomoTracker.java | 8 -------- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/matomo/java/tracking/HttpClientFactory.java b/src/main/java/org/matomo/java/tracking/HttpClientFactory.java index 1ae896d8..86dca94f 100644 --- a/src/main/java/org/matomo/java/tracking/HttpClientFactory.java +++ b/src/main/java/org/matomo/java/tracking/HttpClientFactory.java @@ -16,7 +16,7 @@ /** * Internal factory for providing instances of HTTP clients. - * Especially {@linkAsyncHttpClient} instances are intended to be global resources that share the same lifecycle as the application. + * Especially {@link org.apache.http.nio.client.HttpAsyncClient} instances are intended to be global resources that share the same lifecycle as the application. * For details see Apache documentation. * * @author norbertroamsys diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/src/main/java/org/matomo/java/tracking/MatomoRequest.java index d2d1df29..f3681161 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -2134,8 +2134,8 @@ private String getCustomVariable(@NonNull String parameter, @NonNull String key) * Store a value in a json object at the specified parameter. * * @param parameter the parameter to store the json object at - * @param parameter the key of the value. Cannot be null * @param customVariable the value. Removes the parameter if null + * @param index the custom variable index */ private void setCustomVariable(@NonNull String parameter, @Nullable CustomVariable customVariable, Integer index) { diff --git a/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/src/main/java/org/matomo/java/tracking/MatomoTracker.java index 4ac30954..d5995beb 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoTracker.java +++ b/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -95,7 +95,6 @@ public MatomoTracker(@NonNull final String hostUrl, @Nullable final String proxy * * @param request request to send. must not be null * @return the response from this request - * @throws IOException thrown if there was a problem with this connection * @deprecated use sendRequestAsync instead */ @Deprecated @@ -133,7 +132,6 @@ protected HttpClient getHttpClient() { * * @param request request to send * @return future with response from this request - * @throws IOException thrown if there was a problem with this connection */ public Future sendRequestAsync(@NonNull final MatomoRequest request) { return sendRequestAsync(request, null); @@ -145,7 +143,6 @@ public Future sendRequestAsync(@NonNull final MatomoRequest reques * @param request request to send * @param callback callback that gets executed when response arrives * @return future with response from this request - * @throws IOException thrown if there was a problem with this connection */ public Future sendRequestAsync(@NonNull final MatomoRequest request, @Nullable FutureCallback callback) { final CloseableHttpAsyncClient client = getHttpAsyncClient(); @@ -170,7 +167,6 @@ protected CloseableHttpAsyncClient getHttpAsyncClient() { * * @param requests the requests to send * @return the response from these requests - * @throws IOException thrown if there was a problem with this connection * @deprecated use sendBulkRequestAsync instead */ @Deprecated @@ -186,7 +182,6 @@ public HttpResponse sendBulkRequest(@NonNull final Iterable requests, * * @param requests the requests to send * @return future with response from these requests - * @throws IOException thrown if there was a problem with this connection */ public Future sendBulkRequestAsync(@NonNull final Iterable requests) { return sendBulkRequestAsync(requests, null, null); @@ -258,7 +252,6 @@ public Future sendBulkRequestAsync(@NonNull final Iterable sendBulkRequestAsync(@NonNull final Iterable requests, @Nullable FutureCallback callback) { return sendBulkRequestAsync(requests, null, callback); @@ -272,7 +265,6 @@ public Future sendBulkRequestAsync(@NonNull final Iterable sendBulkRequestAsync(@NonNull final Iterable requests, @Nullable final String authToken) { return sendBulkRequestAsync(requests, authToken, null); From 6ad425058fc1025284075222f61fab5d2862fd08 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Aug 2022 21:02:32 +0000 Subject: [PATCH 060/467] Bump pitest-maven from 1.9.3 to 1.9.4 Bumps [pitest-maven](https://github.com/hcoles/pitest) from 1.9.3 to 1.9.4. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.9.3...1.9.4) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bcdb4b66..5d481bc2 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,7 @@ org.pitest pitest-maven - 1.9.3 + 1.9.4 org.piwik.java.tracking* From 01c52f6d754d0e790c5c1cd84ffec0c7edbfaacc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 21:02:01 +0000 Subject: [PATCH 061/467] Bump mockito-core from 4.6.1 to 4.7.0 Bumps [mockito-core](https://github.com/mockito/mockito) from 4.6.1 to 4.7.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.6.1...v4.7.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5491bc53..1d857d82 100644 --- a/pom.xml +++ b/pom.xml @@ -176,7 +176,7 @@ org.mockito mockito-core - 4.6.1 + 4.7.0 test From b7739945cce84902de6cb6ece8fa99746ecc39e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 21:02:05 +0000 Subject: [PATCH 062/467] Bump maven-javadoc-plugin from 3.4.0 to 3.4.1 Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.4.0 to 3.4.1. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.4.0...maven-javadoc-plugin-3.4.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5491bc53..be9edc40 100644 --- a/pom.xml +++ b/pom.xml @@ -80,7 +80,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.4.0 + 3.4.1 From aa7973d1bcc23edb3863798543b759d1bb2c921e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Aug 2022 21:03:23 +0000 Subject: [PATCH 063/467] Bump slf4j.version from 1.7.36 to 2.0.0 Bumps `slf4j.version` from 1.7.36 to 2.0.0. Updates `slf4j-api` from 1.7.36 to 2.0.0 - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_1.7.36...v_2.0.0) Updates `slf4j-simple` from 1.7.36 to 2.0.0 - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_1.7.36...v_2.0.0) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-major - dependency-name: org.slf4j:slf4j-simple dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1841e6cc..84585c94 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ UTF-8 - 1.7.36 + 2.0.0 From 0b6a6dbe2f88c076804a1943c172742a92ee3cf6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Aug 2022 21:03:29 +0000 Subject: [PATCH 064/467] Bump pitest-maven from 1.9.4 to 1.9.5 Bumps [pitest-maven](https://github.com/hcoles/pitest) from 1.9.4 to 1.9.5. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.9.4...1.9.5) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1841e6cc..3da450ee 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.pitest pitest-maven - 1.9.4 + 1.9.5 org.matomo.java.tracking* From c2ab45eed33d729ec4d118366be3e69ea3562a1e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Sep 2022 21:03:37 +0000 Subject: [PATCH 065/467] Bump jackson-databind from 2.13.3 to 2.13.4 Bumps [jackson-databind](https://github.com/FasterXML/jackson) from 2.13.3 to 2.13.4. - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a7603003..b566ea8e 100644 --- a/pom.xml +++ b/pom.xml @@ -166,7 +166,7 @@ com.fasterxml.jackson.core jackson-databind - 2.13.3 + 2.13.4 org.slf4j From 5432d84c458b6a70dc82cc1c014439a8623d4e42 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Sep 2022 21:12:00 +0000 Subject: [PATCH 066/467] Bump mockito-core from 4.7.0 to 4.8.0 Bumps [mockito-core](https://github.com/mockito/mockito) from 4.7.0 to 4.8.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.7.0...v4.8.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b566ea8e..e38341b5 100644 --- a/pom.xml +++ b/pom.xml @@ -176,7 +176,7 @@ org.mockito mockito-core - 4.7.0 + 4.8.0 test From e287204e18ee85866ee0e91f48e1fa8ee0008397 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Sep 2022 21:02:12 +0000 Subject: [PATCH 067/467] Bump slf4j.version from 2.0.0 to 2.0.1 Bumps `slf4j.version` from 2.0.0 to 2.0.1. Updates `slf4j-api` from 2.0.0 to 2.0.1 - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.0...v_2.0.1) Updates `slf4j-simple` from 2.0.0 to 2.0.1 - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.0...v_2.0.1) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.slf4j:slf4j-simple dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e38341b5..ec6752e5 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ UTF-8 - 2.0.0 + 2.0.1 From a2bb7f8667f807241232704bb85360d6327b33ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Sep 2022 21:02:46 +0000 Subject: [PATCH 068/467] Bump slf4j.version from 2.0.1 to 2.0.2 Bumps `slf4j.version` from 2.0.1 to 2.0.2. Updates `slf4j-api` from 2.0.1 to 2.0.2 - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.1...v_2.0.2) Updates `slf4j-simple` from 2.0.1 to 2.0.2 - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.1...v_2.0.2) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.slf4j:slf4j-simple dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ec6752e5..ae686b49 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ UTF-8 - 2.0.1 + 2.0.2 From 8d1da10f7808e520cb45d696b5f18d0dd0ca8186 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Oct 2022 21:03:26 +0000 Subject: [PATCH 069/467] Bump slf4j.version from 2.0.2 to 2.0.3 Bumps `slf4j.version` from 2.0.2 to 2.0.3. Updates `slf4j-api` from 2.0.2 to 2.0.3 - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.2...v_2.0.3) Updates `slf4j-simple` from 2.0.2 to 2.0.3 - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.2...v_2.0.3) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.slf4j:slf4j-simple dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ae686b49..d9a0203a 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ UTF-8 - 2.0.2 + 2.0.3 From 565c9387201e823650821112605046fab5320190 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Oct 2022 21:03:30 +0000 Subject: [PATCH 070/467] Bump pitest-maven from 1.9.5 to 1.9.6 Bumps [pitest-maven](https://github.com/hcoles/pitest) from 1.9.5 to 1.9.6. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.9.5...1.9.6) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ae686b49..b6206c6b 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.pitest pitest-maven - 1.9.5 + 1.9.6 org.matomo.java.tracking* From bf6aab8c57c44ab2d7a79fa6d94ca29ca83d58c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Oct 2022 21:02:22 +0000 Subject: [PATCH 071/467] Bump pitest-maven from 1.9.6 to 1.9.8 Bumps [pitest-maven](https://github.com/hcoles/pitest) from 1.9.6 to 1.9.8. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.9.6...1.9.8) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c5464cc0..9e9cb2c7 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.pitest pitest-maven - 1.9.6 + 1.9.8 org.matomo.java.tracking* From b950fe9df883fd23ce7bd83b4868fe0003fb22d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Oct 2022 21:02:04 +0000 Subject: [PATCH 072/467] Bump jackson-databind from 2.13.4 to 2.13.4.2 Bumps [jackson-databind](https://github.com/FasterXML/jackson) from 2.13.4 to 2.13.4.2. - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9e9cb2c7..99fd60cd 100644 --- a/pom.xml +++ b/pom.xml @@ -166,7 +166,7 @@ com.fasterxml.jackson.core jackson-databind - 2.13.4 + 2.13.4.2 org.slf4j From ccb95ae2701697e42ccce0abe5eac0b647bf50cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Oct 2022 21:02:58 +0000 Subject: [PATCH 073/467] Bump mockito-core from 4.8.0 to 4.8.1 Bumps [mockito-core](https://github.com/mockito/mockito) from 4.8.0 to 4.8.1. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.8.0...v4.8.1) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 99fd60cd..af02ccfb 100644 --- a/pom.xml +++ b/pom.xml @@ -176,7 +176,7 @@ org.mockito mockito-core - 4.8.0 + 4.8.1 test From 18bade0b758286701ffe099a10f71bf8c1c4d9f6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Oct 2022 21:02:20 +0000 Subject: [PATCH 074/467] Bump pitest-maven from 1.9.8 to 1.9.9 Bumps [pitest-maven](https://github.com/hcoles/pitest) from 1.9.8 to 1.9.9. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.9.8...1.9.9) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index af02ccfb..27abbc83 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.pitest pitest-maven - 1.9.8 + 1.9.9 org.matomo.java.tracking* From bf34db58bea415bd5f62a6891e8df28ad6aedbe0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Nov 2022 21:02:16 +0000 Subject: [PATCH 075/467] Bump jackson-databind from 2.13.4.2 to 2.14.0 Bumps [jackson-databind](https://github.com/FasterXML/jackson) from 2.13.4.2 to 2.14.0. - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 27abbc83..d295c3cf 100644 --- a/pom.xml +++ b/pom.xml @@ -166,7 +166,7 @@ com.fasterxml.jackson.core jackson-databind - 2.13.4.2 + 2.14.0 org.slf4j From 03ec06dbb27fd1c86d4d0fbc98a91830ae291fa9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 21:00:38 +0000 Subject: [PATCH 076/467] Bump pitest-maven from 1.9.9 to 1.9.10 Bumps [pitest-maven](https://github.com/hcoles/pitest) from 1.9.9 to 1.9.10. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.9.9...1.9.10) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d295c3cf..ba8f4c00 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.pitest pitest-maven - 1.9.9 + 1.9.10 org.matomo.java.tracking* From fda2441f8b7b398c1cafdb525e9ac42530cd9380 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 21:00:43 +0000 Subject: [PATCH 077/467] Bump mockito-core from 4.8.1 to 4.9.0 Bumps [mockito-core](https://github.com/mockito/mockito) from 4.8.1 to 4.9.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.8.1...v4.9.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d295c3cf..64d264e0 100644 --- a/pom.xml +++ b/pom.xml @@ -176,7 +176,7 @@ org.mockito mockito-core - 4.8.1 + 4.9.0 test From 5dad85679523052b6520d32e54a255d5d63f0146 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Nov 2022 21:03:39 +0000 Subject: [PATCH 078/467] Bump pitest-maven from 1.9.10 to 1.9.11 Bumps [pitest-maven](https://github.com/hcoles/pitest) from 1.9.10 to 1.9.11. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.9.10...1.9.11) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0956e271..dfb65b6d 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.pitest pitest-maven - 1.9.10 + 1.9.11 org.matomo.java.tracking* From 51a51ec405ad22347eb06933c8e85f36f557910a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Nov 2022 21:03:47 +0000 Subject: [PATCH 079/467] Bump slf4j.version from 2.0.3 to 2.0.4 Bumps `slf4j.version` from 2.0.3 to 2.0.4. Updates `slf4j-api` from 2.0.3 to 2.0.4 - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.3...v_2.0.4) Updates `slf4j-simple` from 2.0.3 to 2.0.4 - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.3...v_2.0.4) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.slf4j:slf4j-simple dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0956e271..a470e32d 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ UTF-8 - 2.0.3 + 2.0.4 From 30a75271b9fb625ab423eaf5c4371cd645131d49 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Nov 2022 21:00:50 +0000 Subject: [PATCH 080/467] Bump slf4j.version from 2.0.4 to 2.0.5 Bumps `slf4j.version` from 2.0.4 to 2.0.5. Updates `slf4j-api` from 2.0.4 to 2.0.5 - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.4...v_2.0.5) Updates `slf4j-simple` from 2.0.4 to 2.0.5 - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.4...v_2.0.5) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.slf4j:slf4j-simple dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9c752bf5..201bc4f4 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ UTF-8 - 2.0.4 + 2.0.5 From b533839ec0eb05e1ae785255d819eedca9cabdb4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Nov 2022 21:00:53 +0000 Subject: [PATCH 081/467] Bump jackson-databind from 2.14.0 to 2.14.1 Bumps [jackson-databind](https://github.com/FasterXML/jackson) from 2.14.0 to 2.14.1. - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9c752bf5..a78e6c6b 100644 --- a/pom.xml +++ b/pom.xml @@ -166,7 +166,7 @@ com.fasterxml.jackson.core jackson-databind - 2.14.0 + 2.14.1 org.slf4j From 74d3dd02a0d5341d86994a78d6e9373d56e7c415 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Dec 2022 21:00:37 +0000 Subject: [PATCH 082/467] Bump pitest-maven from 1.9.11 to 1.10.3 Bumps [pitest-maven](https://github.com/hcoles/pitest) from 1.9.11 to 1.10.3. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.9.11...1.10.3) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c41f6686..379c6fc1 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.pitest pitest-maven - 1.9.11 + 1.10.3 org.matomo.java.tracking* From b204b4ed16457ed0f8536244639698a135816f33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Dec 2022 21:00:34 +0000 Subject: [PATCH 083/467] Bump slf4j.version from 2.0.5 to 2.0.6 Bumps `slf4j.version` from 2.0.5 to 2.0.6. Updates `slf4j-api` from 2.0.5 to 2.0.6 - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.5...v_2.0.6) Updates `slf4j-simple` from 2.0.5 to 2.0.6 - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.5...v_2.0.6) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.slf4j:slf4j-simple dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 379c6fc1..dfaedfea 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ UTF-8 - 2.0.5 + 2.0.6 From cc736ef46f49104c03ed0f8f7d7ae0dc49456a74 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Dec 2022 21:00:40 +0000 Subject: [PATCH 084/467] Bump mockito-core from 4.9.0 to 4.10.0 Bumps [mockito-core](https://github.com/mockito/mockito) from 4.9.0 to 4.10.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.9.0...v4.10.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 379c6fc1..ccd7aa30 100644 --- a/pom.xml +++ b/pom.xml @@ -176,7 +176,7 @@ org.mockito mockito-core - 4.9.0 + 4.10.0 test From 3efefd65d48f9be3b3bd74a04eee861a028dc7cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Jan 2023 21:00:27 +0000 Subject: [PATCH 085/467] Bump mockito-core from 4.10.0 to 4.11.0 Bumps [mockito-core](https://github.com/mockito/mockito) from 4.10.0 to 4.11.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.10.0...v4.11.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b4e2b0b8..1533e155 100644 --- a/pom.xml +++ b/pom.xml @@ -176,7 +176,7 @@ org.mockito mockito-core - 4.10.0 + 4.11.0 test From 341ee1fbb29a45896c16d9f221646274f8787229 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Jan 2023 21:00:49 +0000 Subject: [PATCH 086/467] Bump pitest-maven from 1.10.3 to 1.10.4 Bumps [pitest-maven](https://github.com/hcoles/pitest) from 1.10.3 to 1.10.4. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.10.3...1.10.4) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1533e155..46869060 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.pitest pitest-maven - 1.10.3 + 1.10.4 org.matomo.java.tracking* From b3c59ef1808436ccc2b32dad9a2c043eed0042e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Jan 2023 21:00:38 +0000 Subject: [PATCH 087/467] Bump pitest-maven from 1.10.4 to 1.11.0 Bumps [pitest-maven](https://github.com/hcoles/pitest) from 1.10.4 to 1.11.0. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.10.4...1.11.0) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 46869060..e7180e3e 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.pitest pitest-maven - 1.10.4 + 1.11.0 org.matomo.java.tracking* From ddbd4eb0468c13d2e7e55eabbfe3c7edc5ee1bae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Jan 2023 21:00:40 +0000 Subject: [PATCH 088/467] Bump jackson-databind from 2.14.1 to 2.14.2 Bumps [jackson-databind](https://github.com/FasterXML/jackson) from 2.14.1 to 2.14.2. - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 46869060..becc0ebb 100644 --- a/pom.xml +++ b/pom.xml @@ -166,7 +166,7 @@ com.fasterxml.jackson.core jackson-databind - 2.14.1 + 2.14.2 org.slf4j From c7e07e40b3bbffab4ff6cffa90ade4c8f6050634 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 21:00:51 +0000 Subject: [PATCH 089/467] Bump lombok from 1.18.24 to 1.18.26 Bumps [lombok](https://github.com/projectlombok/lombok) from 1.18.24 to 1.18.26. - [Release notes](https://github.com/projectlombok/lombok/releases) - [Changelog](https://github.com/projectlombok/lombok/blob/master/doc/changelog.markdown) - [Commits](https://github.com/projectlombok/lombok/compare/v1.18.24...v1.18.26) --- updated-dependencies: - dependency-name: org.projectlombok:lombok dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 46869060..c0f75467 100644 --- a/pom.xml +++ b/pom.xml @@ -194,7 +194,7 @@ org.projectlombok lombok - 1.18.24 + 1.18.26 provided From b793bc23f5a6c24c58954ded885a06a96d96f14c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Feb 2023 21:56:25 +0000 Subject: [PATCH 090/467] Bump maven-javadoc-plugin from 3.4.1 to 3.5.0 Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.4.1 to 3.5.0. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.4.1...maven-javadoc-plugin-3.5.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5b8056e9..68fd6e62 100644 --- a/pom.xml +++ b/pom.xml @@ -80,7 +80,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.4.1 + 3.5.0 From 8364cb38bd0c955f9276c7ac7fa6434444e66dcf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Feb 2023 21:20:41 +0000 Subject: [PATCH 091/467] Bump maven-compiler-plugin from 3.10.1 to 3.11.0 Bumps [maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.10.1 to 3.11.0. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.10.1...maven-compiler-plugin-3.11.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 68fd6e62..1007743b 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.10.1 + 3.11.0 1.8 1.8 From 5a9a414c457463350b4cd6e2d46f2ab0a419c95d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Feb 2023 21:20:47 +0000 Subject: [PATCH 092/467] Bump pitest-maven from 1.11.0 to 1.11.3 Bumps [pitest-maven](https://github.com/hcoles/pitest) from 1.11.0 to 1.11.3. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.11.0...1.11.3) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 68fd6e62..3431cfdf 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.pitest pitest-maven - 1.11.0 + 1.11.3 org.matomo.java.tracking* From 82c8a1fc5b20611231dcc1687a81d2519eded0c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Mar 2023 21:56:41 +0000 Subject: [PATCH 093/467] Bump pitest-maven from 1.11.3 to 1.11.4 Bumps [pitest-maven](https://github.com/hcoles/pitest) from 1.11.3 to 1.11.4. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.11.3...1.11.4) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bd219609..ea336994 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.pitest pitest-maven - 1.11.3 + 1.11.4 org.matomo.java.tracking* From 9c9df5c9fc80f57283e742f77ca52dd743925cce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 21:56:32 +0000 Subject: [PATCH 094/467] Bump pitest-maven from 1.11.4 to 1.11.5 Bumps [pitest-maven](https://github.com/hcoles/pitest) from 1.11.4 to 1.11.5. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.11.4...1.11.5) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ea336994..8616bdaa 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.pitest pitest-maven - 1.11.4 + 1.11.5 org.matomo.java.tracking* From 08aa542c5b899f94d5dae35d767f2f44e57957eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 21:56:37 +0000 Subject: [PATCH 095/467] Bump slf4j.version from 2.0.6 to 2.0.7 Bumps `slf4j.version` from 2.0.6 to 2.0.7. Updates `slf4j-api` from 2.0.6 to 2.0.7 - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/commits) Updates `slf4j-simple` from 2.0.6 to 2.0.7 - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/commits) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.slf4j:slf4j-simple dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ea336994..c54edd75 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ UTF-8 - 2.0.6 + 2.0.7 From 20d9147593ec5c0560889eace7def19a00d3c185 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Mar 2023 21:56:36 +0000 Subject: [PATCH 096/467] Bump pitest-maven from 1.11.5 to 1.11.7 Bumps [pitest-maven](https://github.com/hcoles/pitest) from 1.11.5 to 1.11.7. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.11.5...1.11.7) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f26a4f1f..da34b2e5 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.pitest pitest-maven - 1.11.5 + 1.11.7 org.matomo.java.tracking* From fa8a3b5fd799cbfababea2744df4cec9e94644f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 22:02:51 +0000 Subject: [PATCH 097/467] Bump jacoco-maven-plugin from 0.8.8 to 0.8.9 Bumps [jacoco-maven-plugin](https://github.com/jacoco/jacoco) from 0.8.8 to 0.8.9. - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.8...v0.8.9) --- updated-dependencies: - dependency-name: org.jacoco:jacoco-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index da34b2e5..182a9a58 100644 --- a/pom.xml +++ b/pom.xml @@ -114,7 +114,7 @@ org.jacoco jacoco-maven-plugin - 0.8.8 + 0.8.9 prepare-agent From 4765c0a006b117d47749f0b42f77d1ea9abc547f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Apr 2023 21:56:38 +0000 Subject: [PATCH 098/467] Bump pitest-maven from 1.11.7 to 1.12.0 Bumps [pitest-maven](https://github.com/hcoles/pitest) from 1.11.7 to 1.12.0. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.11.7...1.12.0) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 182a9a58..146bfacf 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.pitest pitest-maven - 1.11.7 + 1.12.0 org.matomo.java.tracking* From 8a665d65899b1ac931ea03a41ba8a29c85f686cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Apr 2023 21:57:23 +0000 Subject: [PATCH 099/467] Bump pitest-maven from 1.12.0 to 1.13.0 Bumps [pitest-maven](https://github.com/hcoles/pitest) from 1.12.0 to 1.13.0. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.12.0...1.13.0) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 146bfacf..b4d7d1fa 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.pitest pitest-maven - 1.12.0 + 1.13.0 org.matomo.java.tracking* From 0259c30e39c0210cbc98885b20977542ceb5e0b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Apr 2023 21:57:28 +0000 Subject: [PATCH 100/467] Bump jackson-databind from 2.14.2 to 2.15.0 Bumps [jackson-databind](https://github.com/FasterXML/jackson) from 2.14.2 to 2.15.0. - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 146bfacf..102d0ca7 100644 --- a/pom.xml +++ b/pom.xml @@ -166,7 +166,7 @@ com.fasterxml.jackson.core jackson-databind - 2.14.2 + 2.15.0 org.slf4j From 43a2043cdf35ebbdc5a37a2bff274ef894d4da7c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 21:57:19 +0000 Subject: [PATCH 101/467] Bump jacoco-maven-plugin from 0.8.9 to 0.8.10 Bumps [jacoco-maven-plugin](https://github.com/jacoco/jacoco) from 0.8.9 to 0.8.10. - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.9...v0.8.10) --- updated-dependencies: - dependency-name: org.jacoco:jacoco-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bb3ed0a1..d1a7282d 100644 --- a/pom.xml +++ b/pom.xml @@ -114,7 +114,7 @@ org.jacoco jacoco-maven-plugin - 0.8.9 + 0.8.10 prepare-agent From 480695d11a24f1a07516e5790ae237614bdb9662 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 21:56:30 +0000 Subject: [PATCH 102/467] Bump pitest-maven from 1.13.0 to 1.13.1 Bumps [pitest-maven](https://github.com/hcoles/pitest) from 1.13.0 to 1.13.1. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.13.0...1.13.1) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d1a7282d..9a648107 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.pitest pitest-maven - 1.13.0 + 1.13.1 org.matomo.java.tracking* From 543dc06410c66adc5ba97ee9d6f17dd63381ba67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 21:56:33 +0000 Subject: [PATCH 103/467] Bump maven-gpg-plugin from 3.0.1 to 3.1.0 Bumps [maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 3.0.1 to 3.1.0. - [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.0.1...maven-gpg-plugin-3.1.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-gpg-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d1a7282d..72ad7ddf 100644 --- a/pom.xml +++ b/pom.xml @@ -127,7 +127,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.0.1 + 3.1.0 sign-artifacts From 3620f9e4ebbce6e608b7c2e2ef0ded98a84ddca9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 May 2023 21:56:31 +0000 Subject: [PATCH 104/467] Bump maven-source-plugin from 3.2.1 to 3.3.0 Bumps [maven-source-plugin](https://github.com/apache/maven-source-plugin) from 3.2.1 to 3.3.0. - [Commits](https://github.com/apache/maven-source-plugin/compare/maven-source-plugin-3.2.1...maven-source-plugin-3.3.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-source-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 526a673b..f9ab2206 100644 --- a/pom.xml +++ b/pom.xml @@ -67,7 +67,7 @@ org.apache.maven.plugins maven-source-plugin - 3.2.1 + 3.3.0 attach-sources From 6b15a87e3869b3ada5394bd994b518c0750dc227 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 May 2023 21:56:33 +0000 Subject: [PATCH 105/467] Bump jackson-databind from 2.15.0 to 2.15.1 Bumps [jackson-databind](https://github.com/FasterXML/jackson) from 2.15.0 to 2.15.1. - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 526a673b..d3c11c3d 100644 --- a/pom.xml +++ b/pom.xml @@ -166,7 +166,7 @@ com.fasterxml.jackson.core jackson-databind - 2.15.0 + 2.15.1 org.slf4j From d0b294416cb2265fb91fb2235a738479040c63e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 May 2023 21:56:39 +0000 Subject: [PATCH 106/467] Bump pitest-maven from 1.13.1 to 1.14.0 Bumps [pitest-maven](https://github.com/hcoles/pitest) from 1.13.1 to 1.14.0. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.13.1...1.14.0) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 526a673b..6efdc5bd 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.pitest pitest-maven - 1.13.1 + 1.14.0 org.matomo.java.tracking* From c8e7441fae00d5aba6c0ee674997acc0fb8da1f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 May 2023 21:56:29 +0000 Subject: [PATCH 107/467] Bump pitest-maven from 1.14.0 to 1.14.1 Bumps [pitest-maven](https://github.com/hcoles/pitest) from 1.14.0 to 1.14.1. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.14.0...1.14.1) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b799f708..c68a3bc0 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.pitest pitest-maven - 1.14.0 + 1.14.1 org.matomo.java.tracking* From 89533bb16d9b92076ffa549f60b0e54c68215ced Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 May 2023 21:56:32 +0000 Subject: [PATCH 108/467] Bump guava from 31.1-jre to 32.0.0-jre Bumps [guava](https://github.com/google/guava) from 31.1-jre to 32.0.0-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) --- updated-dependencies: - dependency-name: com.google.guava:guava dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b799f708..39893090 100644 --- a/pom.xml +++ b/pom.xml @@ -161,7 +161,7 @@ com.google.guava guava - 31.1-jre + 32.0.0-jre com.fasterxml.jackson.core From 02fbf1665b5ebcd712c4cbdac2fb7afa576dbd1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 May 2023 21:56:34 +0000 Subject: [PATCH 109/467] Bump lombok from 1.18.26 to 1.18.28 Bumps [lombok](https://github.com/projectlombok/lombok) from 1.18.26 to 1.18.28. - [Release notes](https://github.com/projectlombok/lombok/releases) - [Changelog](https://github.com/projectlombok/lombok/blob/master/doc/changelog.markdown) - [Commits](https://github.com/projectlombok/lombok/compare/v1.18.26...v1.18.28) --- updated-dependencies: - dependency-name: org.projectlombok:lombok dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b799f708..787d10bc 100644 --- a/pom.xml +++ b/pom.xml @@ -194,7 +194,7 @@ org.projectlombok lombok - 1.18.26 + 1.18.28 provided From 1eaa7d6de74de8b67fcc4dc64003e9af7602dc1d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jun 2023 21:56:28 +0000 Subject: [PATCH 110/467] Bump jackson-databind from 2.15.1 to 2.15.2 Bumps [jackson-databind](https://github.com/FasterXML/jackson) from 2.15.1 to 2.15.2. - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6881d598..7bc1b411 100644 --- a/pom.xml +++ b/pom.xml @@ -166,7 +166,7 @@ com.fasterxml.jackson.core jackson-databind - 2.15.1 + 2.15.2 org.slf4j From 59a1e1e841acc722432690d4e8a783db72e9c7e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 21:56:27 +0000 Subject: [PATCH 111/467] Bump guava from 32.0.0-jre to 32.0.1-jre Bumps [guava](https://github.com/google/guava) from 32.0.0-jre to 32.0.1-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) --- updated-dependencies: - dependency-name: com.google.guava:guava dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7bc1b411..e4e8efbc 100644 --- a/pom.xml +++ b/pom.xml @@ -161,7 +161,7 @@ com.google.guava guava - 32.0.0-jre + 32.0.1-jre com.fasterxml.jackson.core From db037cada5a4711d2def95c48839552e5cf73f93 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jun 2023 21:56:41 +0000 Subject: [PATCH 112/467] Bump pitest-maven from 1.14.1 to 1.14.2 Bumps [pitest-maven](https://github.com/hcoles/pitest) from 1.14.1 to 1.14.2. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.14.1...1.14.2) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e4e8efbc..10eb41d7 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.pitest pitest-maven - 1.14.1 + 1.14.2 org.matomo.java.tracking* From ef1a418e733dec48881794d469e02b8643db3855 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jul 2023 21:46:58 +0000 Subject: [PATCH 113/467] Bump guava from 32.0.1-jre to 32.1.1-jre Bumps [guava](https://github.com/google/guava) from 32.0.1-jre to 32.1.1-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) --- updated-dependencies: - dependency-name: com.google.guava:guava dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 10eb41d7..d4dcfbf3 100644 --- a/pom.xml +++ b/pom.xml @@ -161,7 +161,7 @@ com.google.guava guava - 32.0.1-jre + 32.1.1-jre com.fasterxml.jackson.core From a5eae9a940e266f53e606d902d6d0394a697b18c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 21:15:21 +0000 Subject: [PATCH 114/467] Bump com.google.guava:guava from 32.1.1-jre to 32.1.2-jre Bumps [com.google.guava:guava](https://github.com/google/guava) from 32.1.1-jre to 32.1.2-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) --- updated-dependencies: - dependency-name: com.google.guava:guava dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d4dcfbf3..781be199 100644 --- a/pom.xml +++ b/pom.xml @@ -161,7 +161,7 @@ com.google.guava guava - 32.1.1-jre + 32.1.2-jre com.fasterxml.jackson.core From e8920e80e2bc67552e0099998f151edc6ec10646 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 21:16:39 +0000 Subject: [PATCH 115/467] Bump org.pitest:pitest-maven from 1.14.2 to 1.14.3 Bumps [org.pitest:pitest-maven](https://github.com/hcoles/pitest) from 1.14.2 to 1.14.3. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.14.2...1.14.3) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 781be199..da68dbd5 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.pitest pitest-maven - 1.14.2 + 1.14.3 org.matomo.java.tracking* From f7afe5dd28b420e40c839f47af125b249ebc49dc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 21:28:10 +0000 Subject: [PATCH 116/467] Bump org.pitest:pitest-maven from 1.14.3 to 1.14.4 Bumps [org.pitest:pitest-maven](https://github.com/hcoles/pitest) from 1.14.3 to 1.14.4. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.14.3...1.14.4) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index da68dbd5..fc8b3757 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.pitest pitest-maven - 1.14.3 + 1.14.4 org.matomo.java.tracking* From 0415e4292e799f9b15b7e21a2bcc946a034bcaff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 21:21:48 +0000 Subject: [PATCH 117/467] Bump slf4j.version from 2.0.7 to 2.0.9 Bumps `slf4j.version` from 2.0.7 to 2.0.9. Updates `org.slf4j:slf4j-api` from 2.0.7 to 2.0.9 Updates `org.slf4j:slf4j-simple` from 2.0.7 to 2.0.9 --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.slf4j:slf4j-simple dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fc8b3757..522bbecc 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ UTF-8 - 2.0.7 + 2.0.9 From 9384f646961b2cf8d59de596bd8962e19b43c3a0 Mon Sep 17 00:00:00 2001 From: Sarah Date: Wed, 6 Sep 2023 13:41:22 +0200 Subject: [PATCH 118/467] fix: set search query before search category --- .../java/org/matomo/java/tracking/MatomoRequestBuilder.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java b/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java index 712a0be9..747791a4 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java +++ b/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java @@ -583,12 +583,12 @@ public MatomoRequest build() { if (responseAsImage != null) { matomoRequest.setResponseAsImage(responseAsImage); } - if (searchCategory != null) { - matomoRequest.setSearchCategory(searchCategory); - } if (searchQuery != null) { matomoRequest.setSearchQuery(searchQuery); } + if (searchCategory != null) { + matomoRequest.setSearchCategory(searchCategory); + } if (searchResultsCount != null) { matomoRequest.setSearchResultsCount(searchResultsCount); } From 1e58dfb5ae11232ae7443dfcce8f2945ca9e7bf6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 21:27:38 +0000 Subject: [PATCH 119/467] build(deps): bump org.apache.maven.plugins:maven-javadoc-plugin Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.5.0 to 3.6.0. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.5.0...maven-javadoc-plugin-3.6.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 522bbecc..4b3ba9d4 100644 --- a/pom.xml +++ b/pom.xml @@ -80,7 +80,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.5.0 + 3.6.0 From 09f9532ffdd3bcdb3ca6f87bc497e2e98d02ddf5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 21:00:43 +0000 Subject: [PATCH 120/467] build(deps): bump org.pitest:pitest-maven from 1.14.4 to 1.15.0 Bumps [org.pitest:pitest-maven](https://github.com/hcoles/pitest) from 1.14.4 to 1.15.0. - [Release notes](https://github.com/hcoles/pitest/releases) - [Commits](https://github.com/hcoles/pitest/compare/1.14.4...1.15.0) --- updated-dependencies: - dependency-name: org.pitest:pitest-maven dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4b3ba9d4..64b39d0c 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.pitest pitest-maven - 1.14.4 + 1.15.0 org.matomo.java.tracking* From bb15dab5a278ba0d3a2768b36a74b12707a9dcda Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 21:00:49 +0000 Subject: [PATCH 121/467] build(deps): bump org.projectlombok:lombok from 1.18.28 to 1.18.30 Bumps [org.projectlombok:lombok](https://github.com/projectlombok/lombok) from 1.18.28 to 1.18.30. - [Changelog](https://github.com/projectlombok/lombok/blob/master/doc/changelog.markdown) - [Commits](https://github.com/projectlombok/lombok/compare/v1.18.28...v1.18.30) --- updated-dependencies: - dependency-name: org.projectlombok:lombok dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4b3ba9d4..26afc01a 100644 --- a/pom.xml +++ b/pom.xml @@ -194,7 +194,7 @@ org.projectlombok lombok - 1.18.28 + 1.18.30 provided From 012da17b772a1f2034ea5cbd0214652edf0314d7 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 10 Oct 2023 11:27:49 +0200 Subject: [PATCH 122/467] Set Maven SCM URLs --- pom.xml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 358f1824..0e4d8d8e 100644 --- a/pom.xml +++ b/pom.xml @@ -29,12 +29,19 @@ Thomas Lutz thomaslutz.de@gmail.com + + dheid + Daniel Heid + mail@daniel-heid.de + Freelancer + https://www.daniel-heid.de/ + - scm:git@github.com:matomo-org/matomo-java-tracker.git - scm:git@github.com:matomo-org/matomo-java-tracker.git - git@github.com:matomo-org/matomo-java-tracker.git + scm:git:ssh://git@github.com/matomo-org/matomo-java-tracker.git + scm:git:ssh://git@github.com/matomo-org/matomo-java-tracker.git + https://github.com/matomo-org/matomo-java-tracker From b679ac386138233a3d378625e66b211923e667a3 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 10 Oct 2023 11:29:26 +0200 Subject: [PATCH 123/467] [maven-release-plugin] prepare release matomo-java-tracker-2.1 --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 0e4d8d8e..34118f9d 100644 --- a/pom.xml +++ b/pom.xml @@ -1,10 +1,9 @@ - + 4.0.0 org.piwik.java.tracking matomo-java-tracker - 2.0-SNAPSHOT + 2.1 jar Matomo Java Tracker @@ -42,6 +41,7 @@ scm:git:ssh://git@github.com/matomo-org/matomo-java-tracker.git scm:git:ssh://git@github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker + matomo-java-tracker-2.1 From 2c9aa2c6b7b7d2f478f9a0d4b80b4447d42b30cf Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 10 Oct 2023 11:29:30 +0200 Subject: [PATCH 124/467] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 34118f9d..f538462b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker - 2.1 + 2.2-SNAPSHOT jar Matomo Java Tracker @@ -41,7 +41,7 @@ scm:git:ssh://git@github.com/matomo-org/matomo-java-tracker.git scm:git:ssh://git@github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - matomo-java-tracker-2.1 + HEAD From e8521b1064c3eecdbb61bd38bf43954d73460db4 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 10 Oct 2023 16:27:49 +0200 Subject: [PATCH 125/467] Introduce Semantic Versioning --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f538462b..b75d9149 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker - 2.2-SNAPSHOT + 2.2.0-SNAPSHOT jar Matomo Java Tracker From 9c1ffd38635c4b6fafa09f2fd1247da8f32918cb Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 10 Oct 2023 20:48:21 +0200 Subject: [PATCH 126/467] #140 Use GitHub Actions --- .github/ISSUE_TEMPLATE/bug_report.md | 20 ++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++ .github/dependabot.yml | 14 ++- .github/pull_request_template.md | 10 ++ .github/release-drafter.yml | 81 ++++++++++++++ .github/workflows/build.yml | 19 ++++ .github/workflows/codeql-analysis.yml | 35 ++++++ .github/workflows/release.yml | 36 ++++++ .travis.yml | 7 -- CODE_OF_CONDUCT.md | 128 ++++++++++++++++++++++ CONTRIBUTING.md | 92 ++++++++++++++++ SECURITY.md | 16 +++ 12 files changed, 468 insertions(+), 10 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/release-drafter.yml create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/codeql-analysis.yml create mode 100644 .github/workflows/release.yml delete mode 100644 .travis.yml create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 SECURITY.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..adbb253e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,20 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[BUG]" +labels: bug +assignees: dheid + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**Code snippets** +Code to reproduce the behavior + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..d5a45c1a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[REQUEST]" +labels: enhancement +assignees: dheid + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 04ceb6e8..cd85bf2d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,6 +1,14 @@ version: 2 updates: - - package-ecosystem: "maven" - directory: "/" + - package-ecosystem: maven + directory: / schedule: - interval: "weekly" + interval: weekly + assignees: + - dheid + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + assignees: + - dheid diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 8b137891..42d2b4f0 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1 +1,11 @@ + +- [ ] Ensure that the pull request title represents the desired changelog entry +- [ ] Please describe what you did +- [ ] Link to relevant issues in GitHub +- [ ] Link to relevant pull requests, esp. upstream and downstream changes +- [ ] Ensure you have provided tests - that demonstrates feature works or fixes the issue + + diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 00000000..d43ca132 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,81 @@ +name-template: 'v$RESOLVED_VERSION 🌈' +tag-template: 'v$RESOLVED_VERSION' + +categories: + - title: 💥 Breaking changes + labels: + - breaking + - title: 🚨 Removed + labels: + - removed + - title: 🎉 Major features and improvements + labels: + - major-enhancement + - major-rfe + - title: 🐛 Major bug fixes + labels: + - major-bug + - title: ⚠️ Deprecated + labels: + - deprecated + - title: 🚀 New features and improvements + labels: + - enhancement + - feature + - rfe + - title: 🐛 Bug fixes + labels: + - bug + - fix + - bugfix + - regression + - regression-fix + - title: 🌐 Localization and translation + labels: + - localization + - title: 👷 Changes for developers + labels: + - developer + - title: 📝 Documentation updates + labels: + - documentation + - title: 👻 Maintenance + labels: + - chore + - internal + - maintenance + - title: 🚦 Tests + labels: + - test + - tests + - title: ✍ Other changes + - title: 📦 Dependency updates + labels: + - dependencies + collapse-after: 15 + +exclude-labels: + - reverted + - no-changelog + - skip-changelog + - invalid + +template: | + ## Changes + + $CHANGES + +autolabeler: + - label: 'documentation' + files: + - '*.md' + branch: + - '/docs{0,1}\/.+/' + - label: 'bug' + branch: + - '/fix\/.+/' + title: + - '/fix/i' + - label: 'enhancement' + branch: + - '/feature\/.+/' diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..83541909 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,19 @@ +name: Java CI with Maven + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'temurin' + cache: maven + - run: mvn -B verify jacoco:report coveralls:report diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000..4991ec03 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,35 @@ +name: "CodeQL" + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + schedule: + - cron: '27 11 * * 5' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'java' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..9902e44b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,36 @@ +name: Release with Maven +on: + workflow_dispatch: +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'temurin' + cache: maven + server-id: ossrh + server-username: OSSRH_USERNAME + server-password: OSSRH_PASSWORD + gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} + gpg-passphrase: GPG_PASSPHRASE + - run: | + git config user.email "mail@daniel-heid.de" + git config user.name "Daniel Heid" + - id: version + run: | + VERSION=$( mvn -B help:evaluate -Dexpression=project.version -q -DforceStdout ) + echo "::set-output name=version::${VERSION#-SNAPSHOT}" + - run: mvn -B release:prepare release:perform + env: + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + - uses: release-drafter/release-drafter@v5 + with: + version: ${{ steps.version.outputs.version }} + publish: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 13a50d11..00000000 --- a/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -install: mvn install -DskipTests=true -Dgpg.skip=true -jdk: - - openjdk8 -language: java -after_success: - - mvn clean test jacoco:report coveralls:report - \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..298acc40 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +mail@daniel-heid.de. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..d90529cd --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,92 @@ +# Contributing + +When contributing to this repository, please first discuss the change you wish to make via issue, +email, or any other method with the owners of this repository before making a change. + +Please note we have a code of conduct, please follow it in all your interactions with the project. + +## Pull Request Process + +1. Ensure any install or build dependencies are removed before the end of the layer when doing a + build. +2. Update the README.md with details of changes to the interface, this includes new environment + variables, exposed ports, useful file locations and container parameters. +3. Increase the version numbers in any examples files and the README.md to the new version that this + Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). +4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you + do not have permission to do that, you may request the second reviewer to merge it for you. + +## Code of Conduct + +### Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +### Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +### Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +### Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +### Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at mail@daniel-heid.de. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +### Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..5379a4ed --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,16 @@ +# Security Policy + +## Supported Versions + +The following versions of this library are +currently being supported with security updates. + +| Version | Supported | +|---------|--------------------| +| 2.x | :white_check_mark: | + +## Reporting a Vulnerability + +If you found a security vulerability please don't hesitate to send me a message, +open a new [discussion](https://github.com/matomo-org/matomo-java-tracker/discussions) or +open a new [issue](https://github.com/matomo-org/matomo-java-tracker/issues). From 8a12cde21387de7e71d0cc9bb8ca46f71604affb Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 10 Oct 2023 20:50:57 +0200 Subject: [PATCH 127/467] Update version in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7b325aa1..0782d6b8 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Add a dependency on Matomo Java Tracker using Maven: org.piwik.java.tracking matomo-java-tracker - 2.0 + 2.1 ``` @@ -32,7 +32,7 @@ or Gradle: ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker:2.0") + implementation("org.piwik.java.tracking:matomo-java-tracker:2.1") } ``` From 502858ef69492a6dfba24f7e6d7e8e02a81feba0 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 10 Oct 2023 21:03:11 +0200 Subject: [PATCH 128/467] #140 Sign artifact only during release --- pom.xml | 149 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 82 insertions(+), 67 deletions(-) diff --git a/pom.xml b/pom.xml index b75d9149..4767e6fe 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 org.piwik.java.tracking @@ -61,15 +62,88 @@ + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 1.8 + 1.8 + + + + org.pitest + pitest-maven + 1.15.0 + + + org.matomo.java.tracking* + + + org.matomo.java.tracking* + + + + + org.eluder.coveralls + coveralls-maven-plugin + 4.3.0 + + + org.jacoco + jacoco-maven-plugin + 0.8.10 + + + prepare-agent + + prepare-agent + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 3.1.0 + + + sign-artifacts + verify + + sign + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.13 + true + + ossrh + https://oss.sonatype.org/ + false + + + + org.apache.maven.plugins - maven-compiler-plugin - 3.11.0 - - 1.8 - 1.8 - + maven-failsafe-plugin + 3.1.2 + + + + integration-test + verify + + + org.apache.maven.plugins @@ -79,7 +153,7 @@ attach-sources - jar + jar-no-fork @@ -88,9 +162,6 @@ org.apache.maven.plugins maven-javadoc-plugin 3.6.0 - - - attach-javadocs @@ -100,62 +171,6 @@ - - org.pitest - pitest-maven - 1.15.0 - - - org.matomo.java.tracking* - - - org.matomo.java.tracking* - - - - - org.eluder.coveralls - coveralls-maven-plugin - 4.3.0 - - - org.jacoco - jacoco-maven-plugin - 0.8.10 - - - prepare-agent - - prepare-agent - - - - - - org.apache.maven.plugins - maven-gpg-plugin - 3.1.0 - - - sign-artifacts - verify - - sign - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.13 - true - - ossrh - https://oss.sonatype.org/ - false - - From 47f20834a193e62ded518e1e1bbcd56292459c6c Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 10 Oct 2023 21:06:07 +0200 Subject: [PATCH 129/467] #140 Remove Coveralls --- .github/workflows/build.yml | 2 +- pom.xml | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 83541909..502d4acc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,4 +16,4 @@ jobs: java-version: '8' distribution: 'temurin' cache: maven - - run: mvn -B verify jacoco:report coveralls:report + - run: mvn -B verify jacoco:report diff --git a/pom.xml b/pom.xml index 4767e6fe..996d3717 100644 --- a/pom.xml +++ b/pom.xml @@ -86,11 +86,6 @@ - - org.eluder.coveralls - coveralls-maven-plugin - 4.3.0 - org.jacoco jacoco-maven-plugin From 1276f8c3a5bf543242ad1b00a47f9872f9e2726c Mon Sep 17 00:00:00 2001 From: ARUS2023 <145485937+ARUS2023@users.noreply.github.com> Date: Tue, 10 Oct 2023 14:14:21 -0500 Subject: [PATCH 130/467] Remove 8 unnecessary stubbings in PiwikTrackerTest.java (#138) --- .../java/org/matomo/java/tracking/PiwikTrackerTest.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/test/java/org/matomo/java/tracking/PiwikTrackerTest.java b/src/test/java/org/matomo/java/tracking/PiwikTrackerTest.java index b37a9ebc..6c1dcc12 100644 --- a/src/test/java/org/matomo/java/tracking/PiwikTrackerTest.java +++ b/src/test/java/org/matomo/java/tracking/PiwikTrackerTest.java @@ -143,7 +143,6 @@ public void testSendRequestAsync() throws Exception { doReturn(client).when(piwikTracker).getHttpAsyncClient(); doReturn(PARAMETERS).when(request).getParameters(); doReturn(response).when(future).get(); - doReturn(true).when(future).isDone(); doReturn(future).when(client) .execute(argThat(new CorrectGetRequest("http://test.com?parameterName=parameterValue")), any()); @@ -273,9 +272,7 @@ public void testSendBulkRequest_Iterable_StringTT() { HttpClient client = mock(HttpClient.class); PiwikRequest request = mock(PiwikRequest.class); - doReturn(PARAMETERS).when(request).getParameters(); requests.add(request); - doReturn(client).when(piwikTracker).getHttpClient(); piwikTracker.sendBulkRequest(requests, "1"); fail("Exception should have been thrown."); @@ -324,7 +321,6 @@ public void testSendBulkRequestAsync_Iterable() throws Exception { HttpResponse response = mock(HttpResponse.class); Future future = mock(Future.class); doReturn(response).when(future).get(); - doReturn(true).when(future).isDone(); doReturn(future).when(piwikTracker).sendBulkRequestAsync(requests); @@ -341,9 +337,7 @@ public void testSendBulkRequestAsync_Iterable_StringTT() { CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); PiwikRequest request = mock(PiwikRequest.class); - doReturn(PARAMETERS).when(request).getParameters(); requests.add(request); - doReturn(client).when(piwikTracker).getHttpAsyncClient(); piwikTracker.sendBulkRequestAsync(requests, "1"); fail("Exception should have been thrown."); @@ -360,7 +354,6 @@ public void testSendBulkRequestAsync_Iterable_StringFF() throws Exception { HttpResponse response = mock(HttpResponse.class); Future future = mock(Future.class); doReturn(response).when(future).get(); - doReturn(true).when(future).isDone(); doReturn(PARAMETERS).when(request).getParameters(); requests.add(request); @@ -379,7 +372,6 @@ public void testSendBulkRequestAsync_Iterable_StringFT() throws Exception { HttpResponse response = mock(HttpResponse.class); Future future = mock(Future.class); doReturn(response).when(future).get(); - doReturn(true).when(future).isDone(); doReturn(PARAMETERS).when(request).getParameters(); requests.add(request); From 6d4fe77b87ea6575f4801d3ea0c8738b24a061ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Oct 2023 21:15:14 +0200 Subject: [PATCH 131/467] build(deps): bump org.pitest:pitest-maven from 1.15.0 to 1.15.1 (#142) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 996d3717..011ee0dd 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ org.pitest pitest-maven - 1.15.0 + 1.15.1 org.matomo.java.tracking* From 65d10deff0515eea433f3dcf7ae1a53ecf307d22 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Oct 2023 09:16:06 +0200 Subject: [PATCH 132/467] build(deps): bump com.fasterxml.jackson.core:jackson-databind from 2.15.2 to 2.15.3 (#146) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 011ee0dd..6fca26d3 100644 --- a/pom.xml +++ b/pom.xml @@ -183,7 +183,7 @@ com.fasterxml.jackson.core jackson-databind - 2.15.2 + 2.15.3 org.slf4j From f230e35cf92d4acc83739c2daf299837210a9ad6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Oct 2023 09:16:16 +0200 Subject: [PATCH 133/467] build(deps): bump com.google.guava:guava from 32.1.2-jre to 32.1.3-jre (#144) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6fca26d3..7f748f7d 100644 --- a/pom.xml +++ b/pom.xml @@ -178,7 +178,7 @@ com.google.guava guava - 32.1.2-jre + 32.1.3-jre com.fasterxml.jackson.core From 27caf553d954c565989f3bde394870dde4264295 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Oct 2023 09:16:26 +0200 Subject: [PATCH 134/467] build(deps): bump org.jacoco:jacoco-maven-plugin from 0.8.10 to 0.8.11 (#145) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7f748f7d..be52e3bc 100644 --- a/pom.xml +++ b/pom.xml @@ -89,7 +89,7 @@ org.jacoco jacoco-maven-plugin - 0.8.10 + 0.8.11 prepare-agent From d81e32b41b56d4b46978444306bdad49dbc5e6bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Oct 2023 07:10:34 +0200 Subject: [PATCH 135/467] build(deps): bump org.apache.maven.plugins:maven-failsafe-plugin from 3.1.2 to 3.2.1 (#147) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index be52e3bc..233978da 100644 --- a/pom.xml +++ b/pom.xml @@ -130,7 +130,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.1.2 + 3.2.1 From d033b5e74045a9b057ee017d1c9847764b4316b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Oct 2023 23:07:12 +0100 Subject: [PATCH 136/467] build(deps): bump org.pitest:pitest-maven from 1.15.1 to 1.15.2 (#148) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 233978da..992a9dce 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ org.pitest pitest-maven - 1.15.1 + 1.15.2 org.matomo.java.tracking* From cbb808f56a82827ad06d68bc539b0c3d09ab26e8 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Thu, 2 Nov 2023 22:25:01 +0100 Subject: [PATCH 137/467] Major refactoring (#65) --- .github/workflows/build.yml | 22 +- .github/workflows/codeql-analysis.yml | 20 +- .github/workflows/release.yml | 58 +- CODE_OF_CONDUCT.md | 2 +- CONTRIBUTING.md | 1 + README.md | 110 +- checkstyle.xml | 358 +++ pom.xml | 248 +- src/main/java/lombok.config | 1 + .../org/matomo/java/tracking/AuthToken.java | 43 + .../matomo/java/tracking/CustomVariable.java | 31 +- .../matomo/java/tracking/CustomVariables.java | 86 - .../java/tracking/DaemonThreadFactory.java | 24 + .../matomo/java/tracking/EcommerceItem.java | 35 +- .../matomo/java/tracking/EcommerceItems.java | 48 - .../java/tracking/HttpClientFactory.java | 89 - .../java/tracking/InvalidUrlException.java | 12 +- .../matomo/java/tracking/MatomoBoolean.java | 29 - .../org/matomo/java/tracking/MatomoDate.java | 23 +- .../matomo/java/tracking/MatomoException.java | 17 +- .../matomo/java/tracking/MatomoLocale.java | 27 +- .../matomo/java/tracking/MatomoRequest.java | 2324 ++++------------- .../java/tracking/MatomoRequestBuilder.java | 643 ----- .../matomo/java/tracking/MatomoTracker.java | 255 +- .../java/tracking/ProxyAuthenticator.java | 35 + .../matomo/java/tracking/QueryCreator.java | 144 + .../matomo/java/tracking/QueryParameters.java | 32 - .../java/tracking/RequestValidator.java | 55 + .../java/org/matomo/java/tracking/Sender.java | 219 ++ .../java/tracking/TrackerConfiguration.java | 118 + .../java/tracking/TrackingParameter.java | 23 + .../tracking/TrackingParameterMethod.java | 37 + .../matomo/java/tracking/package-info.java | 14 + .../tracking/parameters/AcceptLanguage.java | 68 + .../java/tracking/parameters/Country.java | 112 + .../tracking/parameters/CustomVariable.java | 57 + .../tracking/parameters/CustomVariables.java | 151 ++ .../tracking/parameters/DeviceResolution.java | 51 + .../tracking/parameters/EcommerceItem.java | 41 + .../tracking/parameters/EcommerceItems.java | 42 + .../matomo/java/tracking/parameters/Hex.java | 24 + .../java/tracking/parameters/RandomValue.java | 55 + .../java/tracking/parameters/UniqueId.java | 55 + .../java/tracking/parameters/VisitorId.java | 98 + .../tracking/parameters/package-info.java | 9 + .../piwik/java/tracking/CustomVariable.java | 17 +- .../piwik/java/tracking/EcommerceItem.java | 19 +- .../org/piwik/java/tracking/PiwikDate.java | 26 +- .../org/piwik/java/tracking/PiwikLocale.java | 15 +- .../org/piwik/java/tracking/PiwikRequest.java | 16 +- .../org/piwik/java/tracking/PiwikTracker.java | 30 +- .../org/piwik/java/tracking/package-info.java | 6 + .../matomo/java/tracking/AuthTokenTest.java | 68 + .../java/tracking/CustomVariableTest.java | 57 +- .../java/tracking/CustomVariablesTest.java | 85 - .../tracking/DaemonThreadFactoryTest.java | 37 + .../java/tracking/EcommerceItemTest.java | 86 +- .../java/tracking/EcommerceItemsTest.java | 18 - .../tracking/InvalidUrlExceptionTest.java | 18 + .../java/tracking/MatomoExceptionTest.java | 25 + .../java/tracking/MatomoLocaleTest.java | 23 + .../tracking/MatomoRequestBuilderTest.java | 70 +- .../java/tracking/MatomoRequestTest.java | 95 + .../matomo/java/tracking/MatomoTrackerIT.java | 526 ++++ .../matomo/java/tracking/PiwikDateTest.java | 46 +- .../matomo/java/tracking/PiwikLocaleTest.java | 57 +- .../java/tracking/PiwikRequestTest.java | 1156 ++------ .../matomo/java/tracking/PiwikTrackerIT.java | 253 ++ .../java/tracking/PiwikTrackerTest.java | 433 --- .../java/tracking/ProxyAuthenticatorTest.java | 52 + .../java/tracking/QueryCreatorTest.java | 363 +++ .../java/tracking/RequestValidatorTest.java | 162 ++ .../org/matomo/java/tracking/SenderIT.java | 96 + .../tracking/TrackerConfigurationTest.java | 61 + .../tracking/TrackingParameterMethodTest.java | 50 + .../parameters/AcceptLanguageTest.java | 46 + .../java/tracking/parameters/CountryTest.java | 165 ++ .../parameters/CustomVariableTest.java | 69 + .../parameters/CustomVariablesTest.java | 170 ++ .../parameters/DeviceResolutionTest.java | 42 + .../parameters/EcommerceItemsTest.java | 17 + .../tracking/parameters/UniqueIdTest.java | 28 + .../tracking/parameters/VisitorIdTest.java | 164 ++ .../java/tracking/CustomVariableTest.java | 17 + .../java/tracking/EcommerceItemTest.java | 19 + 85 files changed, 5974 insertions(+), 4675 deletions(-) create mode 100644 checkstyle.xml create mode 100644 src/main/java/lombok.config create mode 100644 src/main/java/org/matomo/java/tracking/AuthToken.java delete mode 100644 src/main/java/org/matomo/java/tracking/CustomVariables.java create mode 100644 src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java delete mode 100644 src/main/java/org/matomo/java/tracking/EcommerceItems.java delete mode 100644 src/main/java/org/matomo/java/tracking/HttpClientFactory.java delete mode 100644 src/main/java/org/matomo/java/tracking/MatomoBoolean.java delete mode 100644 src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java create mode 100644 src/main/java/org/matomo/java/tracking/ProxyAuthenticator.java create mode 100644 src/main/java/org/matomo/java/tracking/QueryCreator.java delete mode 100644 src/main/java/org/matomo/java/tracking/QueryParameters.java create mode 100644 src/main/java/org/matomo/java/tracking/RequestValidator.java create mode 100644 src/main/java/org/matomo/java/tracking/Sender.java create mode 100644 src/main/java/org/matomo/java/tracking/TrackerConfiguration.java create mode 100644 src/main/java/org/matomo/java/tracking/TrackingParameter.java create mode 100644 src/main/java/org/matomo/java/tracking/TrackingParameterMethod.java create mode 100644 src/main/java/org/matomo/java/tracking/package-info.java create mode 100644 src/main/java/org/matomo/java/tracking/parameters/AcceptLanguage.java create mode 100644 src/main/java/org/matomo/java/tracking/parameters/Country.java create mode 100644 src/main/java/org/matomo/java/tracking/parameters/CustomVariable.java create mode 100644 src/main/java/org/matomo/java/tracking/parameters/CustomVariables.java create mode 100644 src/main/java/org/matomo/java/tracking/parameters/DeviceResolution.java create mode 100644 src/main/java/org/matomo/java/tracking/parameters/EcommerceItem.java create mode 100644 src/main/java/org/matomo/java/tracking/parameters/EcommerceItems.java create mode 100644 src/main/java/org/matomo/java/tracking/parameters/Hex.java create mode 100644 src/main/java/org/matomo/java/tracking/parameters/RandomValue.java create mode 100644 src/main/java/org/matomo/java/tracking/parameters/UniqueId.java create mode 100644 src/main/java/org/matomo/java/tracking/parameters/VisitorId.java create mode 100644 src/main/java/org/matomo/java/tracking/parameters/package-info.java create mode 100644 src/main/java/org/piwik/java/tracking/package-info.java create mode 100644 src/test/java/org/matomo/java/tracking/AuthTokenTest.java delete mode 100644 src/test/java/org/matomo/java/tracking/CustomVariablesTest.java create mode 100644 src/test/java/org/matomo/java/tracking/DaemonThreadFactoryTest.java delete mode 100644 src/test/java/org/matomo/java/tracking/EcommerceItemsTest.java create mode 100644 src/test/java/org/matomo/java/tracking/InvalidUrlExceptionTest.java create mode 100644 src/test/java/org/matomo/java/tracking/MatomoExceptionTest.java create mode 100644 src/test/java/org/matomo/java/tracking/MatomoLocaleTest.java create mode 100644 src/test/java/org/matomo/java/tracking/MatomoRequestTest.java create mode 100644 src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java create mode 100644 src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java delete mode 100644 src/test/java/org/matomo/java/tracking/PiwikTrackerTest.java create mode 100644 src/test/java/org/matomo/java/tracking/ProxyAuthenticatorTest.java create mode 100644 src/test/java/org/matomo/java/tracking/QueryCreatorTest.java create mode 100644 src/test/java/org/matomo/java/tracking/RequestValidatorTest.java create mode 100644 src/test/java/org/matomo/java/tracking/SenderIT.java create mode 100644 src/test/java/org/matomo/java/tracking/TrackerConfigurationTest.java create mode 100644 src/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java create mode 100644 src/test/java/org/matomo/java/tracking/parameters/AcceptLanguageTest.java create mode 100644 src/test/java/org/matomo/java/tracking/parameters/CountryTest.java create mode 100644 src/test/java/org/matomo/java/tracking/parameters/CustomVariableTest.java create mode 100644 src/test/java/org/matomo/java/tracking/parameters/CustomVariablesTest.java create mode 100644 src/test/java/org/matomo/java/tracking/parameters/DeviceResolutionTest.java create mode 100644 src/test/java/org/matomo/java/tracking/parameters/EcommerceItemsTest.java create mode 100644 src/test/java/org/matomo/java/tracking/parameters/UniqueIdTest.java create mode 100644 src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java create mode 100644 src/test/java/org/piwik/java/tracking/CustomVariableTest.java create mode 100644 src/test/java/org/piwik/java/tracking/EcommerceItemTest.java diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 502d4acc..7abf6b97 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,11 +9,19 @@ on: jobs: build: runs-on: ubuntu-latest + permissions: + pull-requests: write steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v3 - with: - java-version: '8' - distribution: 'temurin' - cache: maven - - run: mvn -B verify jacoco:report + - uses: actions/checkout@v4 + - uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'temurin' + cache: maven + - run: mvn -B verify + - uses: madrapps/jacoco-report@v1.6.1 + with: + paths: ${{ github.workspace }}/target/site/jacoco/jacoco.xml + token: ${{ secrets.GITHUB_TOKEN }} + min-coverage-overall: 80 + min-coverage-changed-files: 80 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4991ec03..45a7fb5d 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -23,13 +23,13 @@ jobs: language: [ 'java' ] steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + - name: Checkout repository + uses: actions/checkout@v4 + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9902e44b..e1bd90b8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,32 +5,32 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v3 - with: - java-version: '8' - distribution: 'temurin' - cache: maven - server-id: ossrh - server-username: OSSRH_USERNAME - server-password: OSSRH_PASSWORD - gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} - gpg-passphrase: GPG_PASSPHRASE - - run: | - git config user.email "mail@daniel-heid.de" - git config user.name "Daniel Heid" - - id: version - run: | - VERSION=$( mvn -B help:evaluate -Dexpression=project.version -q -DforceStdout ) - echo "::set-output name=version::${VERSION#-SNAPSHOT}" - - run: mvn -B release:prepare release:perform - env: - OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} - OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} - GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - - uses: release-drafter/release-drafter@v5 - with: - version: ${{ steps.version.outputs.version }} - publish: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: actions/checkout@v4 + - uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'temurin' + cache: maven + server-id: ossrh + server-username: OSSRH_USERNAME + server-password: OSSRH_PASSWORD + gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} + gpg-passphrase: GPG_PASSPHRASE + - run: | + git config user.email "matomo-java-tracker@daniel-heid.de" + git config user.name "Matomo Java Tracker" + - id: version + run: | + VERSION=$( mvn -B help:evaluate -Dexpression=project.version -q -DforceStdout ) + echo "::set-output name=version::${VERSION#-SNAPSHOT}" + - run: mvn -B release:prepare release:perform + env: + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + - uses: release-drafter/release-drafter@v5 + with: + version: ${{ steps.version.outputs.version }} + publish: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 298acc40..494c2390 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an +standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d90529cd..1c208038 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -89,4 +89,5 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage], versi available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org + [version]: http://contributor-covenant.org/version/1/4/ diff --git a/README.md b/README.md index 0782d6b8..232366e1 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Official Java implementation of the [Matomo Tracking HTTP API](https://developer ## Javadoc -The Javadoc for this project is hosted as a Github page for this repo. The latest Javadoc can be +The Javadoc for this project is hosted as a GitHub page for this repo. The latest Javadoc can be found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/HEAD/index.html). Javadoc for the latest and all releases can be found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/index.html). @@ -61,13 +61,13 @@ public class YourImplementation { Per default every request has the following default parameters: -| Parameter Name | Default Value | -|------------------|--------------------------------| -| required | true | -| visitorId | random 16 character hex string | -| randomValue | random 20 character hex string | -| apiVersion | 1 | -| responseAsImage | false | +| Parameter Name | Default Value | +|-----------------|--------------------------------| +| required | true | +| visitorId | random 16 character hex string | +| randomValue | random 20 character hex string | +| apiVersion | 1 | +| responseAsImage | false | Overwrite these properties as desired. @@ -86,7 +86,8 @@ public class YourImplementation { MatomoRequest request = MatomoRequest.builder() .siteId(42) - .actionUrl("http://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore") // include the query parameters to the url + .actionUrl( + "http://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore") // include the query parameters to the url .actionName("LearnMore") .build(); } @@ -153,7 +154,8 @@ public class YourImplementation { public void yourMethod() { - MatomoRequest request = MatomoRequest.builder().siteId(42).actionUrl("https://www.mydomain.com/some/page").actionName("Signup").build(); + MatomoRequest request = + MatomoRequest.builder().siteId(42).actionUrl("https://www.mydomain.com/some/page").actionName("Signup").build(); MatomoTracker tracker = new MatomoTracker("https://your-matomo-domain.tld/matomo.php"); try { @@ -183,7 +185,6 @@ package example; import org.apache.http.HttpResponse; import org.matomo.java.tracking.MatomoRequest; -import org.matomo.java.tracking.MatomoRequestBuilder; import org.matomo.java.tracking.MatomoTracker; import java.io.IOException; @@ -232,7 +233,6 @@ package example; import org.apache.http.HttpResponse; import org.matomo.java.tracking.MatomoLocale; import org.matomo.java.tracking.MatomoRequest; -import org.matomo.java.tracking.MatomoRequestBuilder; import org.matomo.java.tracking.MatomoTracker; import java.io.IOException; @@ -250,11 +250,15 @@ public class YourImplementation { Collection requests = new ArrayList<>(); MatomoRequestBuilder builder = MatomoRequest.builder().siteId(42); requests.add(builder.actionUrl("https://www.mydomain.com/some/page").actionName("Some Page").build()); - requests.add(builder.actionUrl("https://www.mydomain.com/another/page").actionName("Another Page").visitorCountry(new MatomoLocale(Locale.GERMANY)).build()); + requests.add(builder.actionUrl("https://www.mydomain.com/another/page").actionName("Another Page") + .visitorCountry(new MatomoLocale(Locale.GERMANY)).build()); MatomoTracker tracker = new MatomoTracker("https://your-matomo-domain.tld/matomo.php"); try { - Future response = tracker.sendBulkRequestAsync(requests, "33dc3f2536d3025974cccb4b4d2d98f4"); // second parameter is authentication token need for country override + Future response = tracker.sendBulkRequestAsync( + requests, + "33dc3f2536d3025974cccb4b4d2d98f4" + ); // second parameter is authentication token need for country override // usually not needed: HttpResponse httpResponse = response.get(); int statusCode = httpResponse.getStatusLine().getStatusCode(); @@ -274,6 +278,80 @@ public class YourImplementation { ``` +## Migration from Version 2 to 3 + +We improved this library by adding the dimension parameter and removing outdated parameters in Matomo version 5, +removing some dependencies (that even contained vulnerabilities) and increasing maintainability. Sadly this includes the +following breaking changes: + +### Removals + +* The parameter `actionTime` (`gt_ms`) is no longer supported by Matomo 5 and was removed. +* Many methods marked as deprecated in version 2 were removed. Please see the + former [Javadoc](https://matomo-org.github.io/matomo-java-tracker/javadoc/HEAD/index.html) of version 2 to get the + deprecated methods. +* We removed the vulnerable dependency to the Apache HTTP client. Callbacks are no longer of type `FutureCallback`, but + `Consumer` instead. +* The `send...` methods of `MatomoTracker` no longer return a value (usually Matomo always returns an HTTP 204 response + without a body). If the request fails, an exception will be thrown. +* Since there are several ways on how to set the auth token, `verifyAuthTokenSet` was removed. Just check yourself, + whether your auth token is null. However, the tracker checks, whether an auth token is either set by parameter, by + request or per configuration. +* Due to a major refactoring on how the queries are created, we no longer use a large map instead of concrete attributes + to collect the Matomo parameters. Therefore `getParameters()` of class `MatomoRequest` no longer exists. Please use + getters and setters instead. +* The methods `verifyEcommerceEnabled()` and `verifyEcommerceState()` were removed from `MatomoRequest`. The request + will be validated prior to sending and not during construction. +* `getRandomHexString` was removed. Use `RandomValue.random()` or `VisitorId.random()` instead. + +## Type Changes and Renaming + +* `requestDatetime`, `visitorPreviousVisitTimestamp`, `visitorFirstVisitTimestamp`, `ecommerceLastOrderTimestamp` are + now of type `Instant`. You can use `Instant.ofEpochSecond()` to create + them from epoch seconds. +* `requestDatetime` was renamed to `requestTimestamp` due to setter collision and downwards compatibility +* `goalRevenue` is the same parameter as `ecommerceRevenue` and was removed to prevent duplication. + Use `ecommerceRevenue` instead. +* `setEventValue` requires a double parameter now +* `setEcommerceLastOrderTimestamp` requires an `Instant` parameter now +* `headerAcceptLanguage` is now of type `AcceptLanguage`. You can build it easily + using `AcceptLanguage.fromHeader("de")` +* `visitorCountry` is now of type `Country`. You can build it easily using `AcceptLanguage.fromCode("fr")` +* `deviceResolution` is now of type `DeviceResolution`. You can build it easily + using `DeviceResolution.builder.width(...).height(...).build()`. To easy the migration, we added a constructor + method `DeviceResolution.fromString()` that accepts inputs of kind _width_x_height_, e.g. `100x200` +* `pageViewId` is now of type `UniqueId`. You can build it easily using `UniqueId.random()` +* `randomValue` is now of type `RandomValue`. You can build it easily using `RandomValue.random()`. However, if you + really + want to insert a custom string here, use `RandomValue.fromString()` construction method. +* URL was removed due to performance and complicated exception handling and problems with parsing of complex + URLs. `actionUrl`, `referrerUrl`, `outlinkUrl`, `contentTarget` and `downloadUrl` are now strings. +* `getCustomTrackingParameter()` of `MatomoRequest` returns an unmodifiable list now. +* Instead of `IllegalStateException` the tracker throws `MatomoException` +* In former versions the goal id had always to be zero or null. You can now define higher numbers than zero. +* For more type changes see the sections below. + +### Visitor ID + +* `visitorId` and `visitorCustomId` are now of type `VisitorId`. You can build them easily + using `VisitorId.fromHash(...)`. +* You can use `VisitorId.fromHex()` to create a `VisitorId` from a string that contains only hexadecimal characters. +* VisitorId.fromHex() now supports less than 16 hexadecimal characters. If the string is shorter than 16 characters, + the remaining characters will be filled with zeros. + +### Custom Variables + +* According to Matomo, custom variables should no longer be used. Please use dimensions instead. Dimension support has + been introduced. +* `CustomVariable` is now in package `org.matomo.java.tracking.parameters`. +* `customTrackingParameters` in `MatomoRequestBuilder` requires a `Map>` instead + of `Map` now +* `pageCustomVariables` and `visitCustomVariables` are now of type `CustomVariables` instead of collections. Create them + with `CustomVariables.builder().variable(customVariable)` +* `setPageCustomVariable` and `getPageCustomVariable` now longer accept a string as an index. Please use integers + instead. +* Custom variables will now be sent URL encoded + ## Building You need a GPG signing key on your machine. Please follow these @@ -285,8 +363,8 @@ This project can be tested and built by calling mvn install ``` -The built jars and javadoc can be found in `target`. By using the install Maven goal, the snapshot -version can be used using your local Maven repository for testing purposes, e.g. +The built jars and javadoc can be found in `target`. By using the Maven goal `install, the snapshot +version can be used in your local Maven repository for testing purposes, e.g. ```xml diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 00000000..c71071b6 --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,358 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 992a9dce..b4818b1d 100644 --- a/pom.xml +++ b/pom.xml @@ -1,10 +1,10 @@ - 4.0.0 org.piwik.java.tracking matomo-java-tracker - 2.2.0-SNAPSHOT + 3.0.0-rc1-SNAPSHOT jar Matomo Java Tracker @@ -58,6 +58,10 @@ UTF-8 + UTF-8 + UTF-8 + 1.8 + 1.8 2.0.9 @@ -68,36 +72,46 @@ org.apache.maven.plugins maven-compiler-plugin 3.11.0 - - 1.8 - 1.8 - - org.pitest - pitest-maven - 1.15.2 - - - org.matomo.java.tracking* - - - org.matomo.java.tracking* - - + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.4.5 - org.jacoco - jacoco-maven-plugin - 0.8.11 - - - prepare-agent - - prepare-agent - - - + org.apache.maven.plugins + maven-clean-plugin + 3.3.2 + + + org.apache.maven.plugins + maven-deploy-plugin + 3.1.1 + + + org.apache.maven.plugins + maven-install-plugin + 3.1.1 + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.1 + + + org.apache.maven.plugins + maven-site-plugin + 3.12.1 + + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.1 org.apache.maven.plugins @@ -121,7 +135,7 @@ ossrh https://oss.sonatype.org/ - false + true @@ -157,6 +171,10 @@ org.apache.maven.plugins maven-javadoc-plugin 3.6.0 + + true + all,-missing,-reference + attach-javadocs @@ -166,24 +184,143 @@ + + org.apache.maven.plugins + maven-enforcer-plugin + 3.4.1 + + + enforce-maven + + enforce + + + + + 3.2.5 + + + + + + + + org.pitest + pitest-maven + 1.15.2 + + + org.matomo.java.tracking* + + + org.matomo.java.tracking* + + + + + org.jacoco + jacoco-maven-plugin + 0.8.10 + + + prepare-agent + + prepare-agent + + + + report + + report + + + + check + + check + + + + + CLASS + + + LINE + COVEREDRATIO + 0.9 + + + BRANCH + COVEREDRATIO + 0.5 + + + + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.3.1 + + warning + checkstyle.xml + true + + + + + validate + + + check + + + + + + com.github.spotbugs + spotbugs-maven-plugin + 4.7.3.6 + + + org.owasp + dependency-check-maven + 8.4.2 + + true + + + + + release + + + + org.apache.maven.plugins + maven-gpg-plugin + + + org.sonatype.plugins + nexus-staging-maven-plugin + + + + + + - org.apache.httpcomponents - httpasyncclient - 4.1.5 - - - com.google.guava - guava - 32.1.3-jre - - - com.fasterxml.jackson.core - jackson-databind - 2.15.3 + org.jetbrains + annotations + 24.0.1 + provided org.slf4j @@ -191,15 +328,21 @@ ${slf4j.version} - org.mockito - mockito-core - 4.11.0 + org.projectlombok + lombok + 1.18.30 + provided + + + org.junit.jupiter + junit-jupiter + 5.10.0 test - junit - junit - 4.13.2 + org.assertj + assertj-core + 3.24.2 test @@ -209,10 +352,11 @@ test - org.projectlombok - lombok - 1.18.30 - provided + com.github.tomakehurst + wiremock + 2.27.2 + test + diff --git a/src/main/java/lombok.config b/src/main/java/lombok.config new file mode 100644 index 00000000..7a21e880 --- /dev/null +++ b/src/main/java/lombok.config @@ -0,0 +1 @@ +lombok.addLombokGeneratedAnnotation = true diff --git a/src/main/java/org/matomo/java/tracking/AuthToken.java b/src/main/java/org/matomo/java/tracking/AuthToken.java new file mode 100644 index 00000000..1c2f1ecb --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/AuthToken.java @@ -0,0 +1,43 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking; + +import org.jetbrains.annotations.Nullable; + +final class AuthToken { + + private AuthToken() { + // utility + } + + @Nullable + static String determineAuthToken( + @Nullable String overrideAuthToken, + @Nullable Iterable requests, + @Nullable TrackerConfiguration trackerConfiguration + ) { + if (isNotBlank(overrideAuthToken)) { + return overrideAuthToken; + } + if (requests != null) { + for (MatomoRequest request : requests) { + if (request != null && isNotBlank(request.getAuthToken())) { + return request.getAuthToken(); + } + } + } + if (trackerConfiguration != null && isNotBlank(trackerConfiguration.getDefaultAuthToken())) { + return trackerConfiguration.getDefaultAuthToken(); + } + return null; + } + + private static boolean isNotBlank(@Nullable String str) { + return str != null && !str.isEmpty() && !str.trim().isEmpty(); + } +} diff --git a/src/main/java/org/matomo/java/tracking/CustomVariable.java b/src/main/java/org/matomo/java/tracking/CustomVariable.java index 2d52e4d4..ad148b46 100644 --- a/src/main/java/org/matomo/java/tracking/CustomVariable.java +++ b/src/main/java/org/matomo/java/tracking/CustomVariable.java @@ -4,32 +4,27 @@ * @link https://github.com/matomo/matomo-java-tracker * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ + package org.matomo.java.tracking; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.EqualsAndHashCode; -import lombok.Getter; import lombok.NonNull; -import lombok.ToString; -import lombok.experimental.FieldDefaults; /** * A user defined custom variable. * * @author brettcsorba + * @deprecated Use {@link org.matomo.java.tracking.parameters.EcommerceItem} instead. */ -@Getter -@FieldDefaults(level = AccessLevel.PRIVATE) -@AllArgsConstructor -@ToString -@EqualsAndHashCode -public class CustomVariable { - - @NonNull - String key; - - @NonNull - String value; +@Deprecated +public class CustomVariable extends org.matomo.java.tracking.parameters.CustomVariable { + /** + * Instantiates a new custom variable. + * + * @param key the key of the custom variable (required) + * @param value the value of the custom variable (required) + */ + public CustomVariable(@NonNull String key, @NonNull String value) { + super(key, value); + } } diff --git a/src/main/java/org/matomo/java/tracking/CustomVariables.java b/src/main/java/org/matomo/java/tracking/CustomVariables.java deleted file mode 100644 index 0bbe94df..00000000 --- a/src/main/java/org/matomo/java/tracking/CustomVariables.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Matomo Java Tracker - * - * @link https://github.com/matomo/matomo-java-tracker - * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause - */ -package org.matomo.java.tracking; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import lombok.EqualsAndHashCode; -import lombok.NonNull; - -import javax.annotation.Nullable; -import java.util.HashMap; -import java.util.Map; - -/** - * @author brettcsorba - */ -@EqualsAndHashCode -class CustomVariables { - - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - - private final Map variables = new HashMap<>(); - - void add(@NonNull CustomVariable variable) { - boolean found = false; - for (Map.Entry entry : variables.entrySet()) { - CustomVariable customVariable = entry.getValue(); - if (customVariable.getKey().equals(variable.getKey())) { - variables.put(entry.getKey(), variable); - found = true; - } - } - if (!found) { - int i = 1; - while (variables.putIfAbsent(i, variable) != null) { - i++; - } - } - } - - void add(@NonNull CustomVariable cv, int index) { - if (index <= 0) { - throw new IllegalArgumentException("Index must be greater than 0."); - } - variables.put(index, cv); - } - - @Nullable - CustomVariable get(int index) { - if (index <= 0) { - throw new IllegalArgumentException("Index must be greater than 0."); - } - return variables.get(index); - } - - @Nullable - String get(@NonNull String key) { - return variables.values().stream().filter(variable -> variable.getKey().equals(key)).findFirst().map(CustomVariable::getValue).orElse(null); - } - - void remove(int index) { - variables.remove(index); - } - - void remove(@NonNull String key) { - variables.entrySet().removeIf(entry -> entry.getValue().getKey().equals(key)); - } - - boolean isEmpty() { - return variables.isEmpty(); - } - - @Override - public String toString() { - ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); - for (Map.Entry entry : variables.entrySet()) { - CustomVariable variable = entry.getValue(); - objectNode.putArray(entry.getKey().toString()).add(variable.getKey()).add(variable.getValue()); - } - return objectNode.toString(); - } -} diff --git a/src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java b/src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java new file mode 100644 index 00000000..2bd4df9d --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java @@ -0,0 +1,24 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking; + +import org.jetbrains.annotations.Nullable; + +import java.util.concurrent.ThreadFactory; + +class DaemonThreadFactory implements ThreadFactory { + + @Override + public Thread newThread(@Nullable Runnable runnable) { + Thread thread = new Thread(runnable); + thread.setDaemon(true); + thread.setName("MatomoJavaTracker"); + return thread; + } + +} diff --git a/src/main/java/org/matomo/java/tracking/EcommerceItem.java b/src/main/java/org/matomo/java/tracking/EcommerceItem.java index f7b5bf23..e7df1ed0 100644 --- a/src/main/java/org/matomo/java/tracking/EcommerceItem.java +++ b/src/main/java/org/matomo/java/tracking/EcommerceItem.java @@ -4,28 +4,33 @@ * @link https://github.com/matomo/matomo-java-tracker * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ + package org.matomo.java.tracking; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.Setter; /** - * Represents an item in an ecommerce order. + * A user defined custom variable. * * @author brettcsorba + * @deprecated Use {@link org.matomo.java.tracking.parameters.EcommerceItem} instead. */ -@Setter -@Getter -@AllArgsConstructor -@Builder -public class EcommerceItem { +@Deprecated +public class EcommerceItem extends org.matomo.java.tracking.parameters.EcommerceItem { - private String sku; - private String name; - private String category; - private Double price; - private Integer quantity; + /** + * Instantiates a new ecommerce item. + * + * @param sku the sku (Stock Keeping Unit) of the item + * @param name the name of the item + * @param category the category of the item + * @param price the price of the item + * @param quantity the quantity of the item + */ + public EcommerceItem( + String sku, String name, String category, + Double price, Integer quantity + ) { + super(sku, name, category, price, quantity); + } } diff --git a/src/main/java/org/matomo/java/tracking/EcommerceItems.java b/src/main/java/org/matomo/java/tracking/EcommerceItems.java deleted file mode 100644 index a27ae2f2..00000000 --- a/src/main/java/org/matomo/java/tracking/EcommerceItems.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Matomo Java Tracker - * - * @link https://github.com/matomo/matomo-java-tracker - * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause - */ -package org.matomo.java.tracking; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; -import lombok.EqualsAndHashCode; -import lombok.NonNull; - -import javax.annotation.Nonnull; -import java.util.ArrayList; -import java.util.List; - -@EqualsAndHashCode -class EcommerceItems { - - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - - private final List ecommerceItems = new ArrayList<>(); - - public void add(@NonNull EcommerceItem ecommerceItem) { - ecommerceItems.add(ecommerceItem); - } - - @Nonnull - public EcommerceItem get(int index) { - return ecommerceItems.get(index); - } - - @Override - public String toString() { - ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode(); - for (EcommerceItem ecommerceItem : ecommerceItems) { - arrayNode.add(OBJECT_MAPPER.createArrayNode() - .add(ecommerceItem.getSku()) - .add(ecommerceItem.getName()) - .add(ecommerceItem.getCategory()) - .add(ecommerceItem.getPrice()) - .add(ecommerceItem.getQuantity()) - ); - } - return arrayNode.toString(); - } -} diff --git a/src/main/java/org/matomo/java/tracking/HttpClientFactory.java b/src/main/java/org/matomo/java/tracking/HttpClientFactory.java deleted file mode 100644 index 86dca94f..00000000 --- a/src/main/java/org/matomo/java/tracking/HttpClientFactory.java +++ /dev/null @@ -1,89 +0,0 @@ -package org.matomo.java.tracking; - -import lombok.AllArgsConstructor; -import lombok.EqualsAndHashCode; -import org.apache.http.HttpHost; -import org.apache.http.client.HttpClient; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.DefaultProxyRoutePlanner; -import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; -import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; - -import javax.annotation.Nullable; -import java.util.HashMap; -import java.util.Map; - -/** - * Internal factory for providing instances of HTTP clients. - * Especially {@link org.apache.http.nio.client.HttpAsyncClient} instances are intended to be global resources that share the same lifecycle as the application. - * For details see Apache documentation. - * - * @author norbertroamsys - */ -final class HttpClientFactory { - - private HttpClientFactory() { - // utility - } - - /** - * Internal key class for caching {@link CloseableHttpAsyncClient} instances. - */ - @EqualsAndHashCode - @AllArgsConstructor - private static final class KeyEntry { - - private final String proxyHost; - private final int proxyPort; - private final int timeout; - - } - - private static final Map ASYNC_INSTANCES = new HashMap<>(); - - /** - * Factory for getting a synchronous client by proxy and timeout configuration. - * The clients will be created on each call. - * - * @param proxyHost the proxy host - * @param proxyPort the proxy port - * @param timeout the timeout - * @return the created client - */ - public static HttpClient getInstanceFor(final String proxyHost, final int proxyPort, final int timeout) { - return HttpClientBuilder.create().setRoutePlanner(createRoutePlanner(proxyHost, proxyPort)).setDefaultRequestConfig(createRequestConfig(timeout)).build(); - } - - /** - * Factory for getting a asynchronous client by proxy and timeout configuration. - * The clients will be created and cached as a singleton instance. - * - * @param proxyHost the proxy host - * @param proxyPort the proxy port - * @param timeout the timeout - * @return the created client - */ - public static synchronized CloseableHttpAsyncClient getAsyncInstanceFor(final String proxyHost, final int proxyPort, final int timeout) { - return ASYNC_INSTANCES.computeIfAbsent(new KeyEntry(proxyHost, proxyPort, timeout), key -> - HttpAsyncClientBuilder.create().setRoutePlanner(createRoutePlanner(key.proxyHost, key.proxyPort)).setDefaultRequestConfig(createRequestConfig(key.timeout)).build()); - } - - @Nullable - private static DefaultProxyRoutePlanner createRoutePlanner(final String proxyHost, final int proxyPort) { - if (proxyHost != null && proxyPort != 0) { - final HttpHost proxy = new HttpHost(proxyHost, proxyPort); - return new DefaultProxyRoutePlanner(proxy); - } - return null; - } - - private static RequestConfig createRequestConfig(final int timeout) { - final RequestConfig.Builder config = RequestConfig.custom() - .setConnectTimeout(timeout) - .setConnectionRequestTimeout(timeout) - .setSocketTimeout(timeout); - return config.build(); - } - -} diff --git a/src/main/java/org/matomo/java/tracking/InvalidUrlException.java b/src/main/java/org/matomo/java/tracking/InvalidUrlException.java index 01d4fbcb..0b243ab0 100644 --- a/src/main/java/org/matomo/java/tracking/InvalidUrlException.java +++ b/src/main/java/org/matomo/java/tracking/InvalidUrlException.java @@ -1,8 +1,18 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + package org.matomo.java.tracking; +/** + * Thrown when an invalid URL is passed to the tracker. + */ public class InvalidUrlException extends RuntimeException { - public InvalidUrlException(Throwable cause) { + InvalidUrlException(Throwable cause) { super(cause); } } diff --git a/src/main/java/org/matomo/java/tracking/MatomoBoolean.java b/src/main/java/org/matomo/java/tracking/MatomoBoolean.java deleted file mode 100644 index faa3ee92..00000000 --- a/src/main/java/org/matomo/java/tracking/MatomoBoolean.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Matomo Java Tracker - * - * @link https://github.com/matomo/matomo-java-tracker - * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause - */ -package org.matomo.java.tracking; - -import lombok.Value; - -/** - * Object representing a locale required by some Matomo query parameters. - * - * @author brettcsorba - */ -@Value -public class MatomoBoolean { - boolean value; - - /** - * Returns the locale's lowercase country code. - * - * @return the locale's lowercase country code - */ - @Override - public String toString() { - return value ? "1" : "0"; - } -} diff --git a/src/main/java/org/matomo/java/tracking/MatomoDate.java b/src/main/java/org/matomo/java/tracking/MatomoDate.java index 25706998..9c02e400 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoDate.java +++ b/src/main/java/org/matomo/java/tracking/MatomoDate.java @@ -4,22 +4,25 @@ * @link https://github.com/matomo/matomo-java-tracker * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ + package org.matomo.java.tracking; +import lombok.Getter; + import java.time.Instant; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Locale; /** * A datetime object that will return the datetime in the format {@code yyyy-MM-dd hh:mm:ss}. * * @author brettcsorba + * @deprecated Please use {@link Instant} */ +@Deprecated +@Getter public class MatomoDate { - private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH); private ZonedDateTime zonedDateTime; @@ -27,6 +30,7 @@ public class MatomoDate { * Allocates a Date object and initializes it so that it represents the time * at which it was allocated, measured to the nearest millisecond. */ + @Deprecated public MatomoDate() { zonedDateTime = ZonedDateTime.now(ZoneOffset.UTC); } @@ -38,6 +42,7 @@ public MatomoDate() { * * @param epochMilli the milliseconds since January 1, 1970, 00:00:00 GMT. */ + @Deprecated public MatomoDate(long epochMilli) { zonedDateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(epochMilli), ZoneOffset.UTC); } @@ -52,18 +57,6 @@ public void setTimeZone(ZoneId zone) { zonedDateTime = zonedDateTime.withZoneSameInstant(zone); } - /** - * Converts this MatomoDate object to a String of the form:
- *
- * {@code yyyy-MM-dd hh:mm:ss}. - * - * @return a string representation of this MatomoDate - */ - @Override - public String toString() { - return DATE_TIME_FORMATTER.format(zonedDateTime); - } - /** * Converts this datetime to the number of milliseconds from the epoch * of 1970-01-01T00:00:00Z. diff --git a/src/main/java/org/matomo/java/tracking/MatomoException.java b/src/main/java/org/matomo/java/tracking/MatomoException.java index 39ea492e..1f94a08f 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoException.java +++ b/src/main/java/org/matomo/java/tracking/MatomoException.java @@ -1,10 +1,25 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + package org.matomo.java.tracking; +/** + * Thrown when an error occurs while communicating with the Matomo server or when the request is invalid. + */ public class MatomoException extends RuntimeException { private static final long serialVersionUID = 4592083764365938934L; - public MatomoException(String message, Throwable cause) { + MatomoException(String message) { + super(message); + } + + MatomoException(String message, Throwable cause) { super(message, cause); } + } diff --git a/src/main/java/org/matomo/java/tracking/MatomoLocale.java b/src/main/java/org/matomo/java/tracking/MatomoLocale.java index a561a17a..f889e443 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoLocale.java +++ b/src/main/java/org/matomo/java/tracking/MatomoLocale.java @@ -4,32 +4,37 @@ * @link https://github.com/matomo/matomo-java-tracker * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ + package org.matomo.java.tracking; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.Setter; +import static java.util.Objects.requireNonNull; import java.util.Locale; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.matomo.java.tracking.parameters.Country; /** * Object representing a locale required by some Matomo query parameters. * * @author brettcsorba + * @deprecated Use {@link Country} instead */ @Setter @Getter -@AllArgsConstructor -public class MatomoLocale { - private Locale locale; +@Deprecated +public class MatomoLocale extends Country { /** - * Returns the locale's lowercase country code. + * Constructs a new MatomoLocale. * - * @return the locale's lowercase country code + * @param locale The locale to get the country code from + * @deprecated Please use {@link Country} */ - @Override - public String toString() { - return locale.getCountry().toLowerCase(Locale.ENGLISH); + @Deprecated + public MatomoLocale(@NotNull Locale locale) { + super(requireNonNull(locale, "Locale must not be null")); } + } diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/src/main/java/org/matomo/java/tracking/MatomoRequest.java index f3681161..46570479 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -4,30 +4,39 @@ * @link https://github.com/matomo/matomo-java-tracker * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ + package org.matomo.java.tracking; -import com.google.common.collect.LinkedHashMultimap; -import com.google.common.collect.Multimap; -import com.google.common.io.BaseEncoding; -import lombok.NonNull; -import lombok.ToString; -import org.apache.http.client.utils.URIBuilder; +import static java.util.Objects.requireNonNull; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.net.MalformedURLException; -import java.net.URL; import java.nio.charset.Charset; -import java.security.SecureRandom; +import java.time.Instant; import java.util.ArrayList; import java.util.Collection; -import java.util.Date; -import java.util.HashSet; +import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.regex.Pattern; -import java.util.stream.Collectors; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Builder.Default; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.Setter; +import lombok.ToString; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.matomo.java.tracking.parameters.AcceptLanguage; +import org.matomo.java.tracking.parameters.Country; +import org.matomo.java.tracking.parameters.CustomVariable; +import org.matomo.java.tracking.parameters.CustomVariables; +import org.matomo.java.tracking.parameters.DeviceResolution; +import org.matomo.java.tracking.parameters.EcommerceItem; +import org.matomo.java.tracking.parameters.EcommerceItems; +import org.matomo.java.tracking.parameters.RandomValue; +import org.matomo.java.tracking.parameters.UniqueId; +import org.matomo.java.tracking.parameters.VisitorId; /** * A class that implements the @@ -35,1123 +44,671 @@ * * @author brettcsorba */ +@Builder +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor @ToString public class MatomoRequest { - public static final int ID_LENGTH = 16; - public static final int AUTH_TOKEN_LENGTH = 32; - - private static final String ACTION_NAME = "action_name"; - private static final String ACTION_TIME = "gt_ms"; - private static final String ACTION_URL = "url"; - private static final String API_VERSION = "apiv"; - private static final String AUTH_TOKEN = "token_auth"; - private static final String CAMPAIGN_KEYWORD = "_rck"; - private static final String CAMPAIGN_NAME = "_rcn"; - private static final String CHARACTER_SET = "cs"; - private static final String CONTENT_INTERACTION = "c_i"; - private static final String CONTENT_NAME = "c_n"; - private static final String CONTENT_PIECE = "c_p"; - private static final String CONTENT_TARGET = "c_t"; - private static final String CURRENT_HOUR = "h"; - private static final String CURRENT_MINUTE = "m"; - private static final String CURRENT_SECOND = "s"; - - private static final String CUSTOM_ACTION = "ca"; - private static final String DEVICE_RESOLUTION = "res"; - private static final String DOWNLOAD_URL = "download"; - private static final String ECOMMERCE_DISCOUNT = "ec_dt"; - private static final String ECOMMERCE_ID = "ec_id"; - private static final String ECOMMERCE_ITEMS = "ec_items"; - private static final String ECOMMERCE_LAST_ORDER_TIMESTAMP = "_ects"; - private static final String ECOMMERCE_REVENUE = "revenue"; - private static final String ECOMMERCE_SHIPPING_COST = "ec_sh"; - private static final String ECOMMERCE_SUBTOTAL = "ec_st"; - private static final String ECOMMERCE_TAX = "ec_tx"; - private static final String EVENT_ACTION = "e_a"; - private static final String EVENT_CATEGORY = "e_c"; - private static final String EVENT_NAME = "e_n"; - private static final String EVENT_VALUE = "e_v"; - private static final String HEADER_ACCEPT_LANGUAGE = "lang"; - private static final String GOAL_ID = "idgoal"; - private static final String GOAL_REVENUE = "revenue"; - private static final String HEADER_USER_AGENT = "ua"; - private static final String NEW_VISIT = "new_visit"; - private static final String OUTLINK_URL = "link"; - private static final String PAGE_CUSTOM_VARIABLE = "cvar"; - private static final String PLUGIN_DIRECTOR = "dir"; - private static final String PLUGIN_FLASH = "fla"; - private static final String PLUGIN_GEARS = "gears"; - private static final String PLUGIN_JAVA = "java"; - private static final String PLUGIN_PDF = "pdf"; - private static final String PLUGIN_QUICKTIME = "qt"; - private static final String PLUGIN_REAL_PLAYER = "realp"; - private static final String PLUGIN_SILVERLIGHT = "ag"; - private static final String PLUGIN_WINDOWS_MEDIA = "wma"; - private static final String RANDOM_VALUE = "rand"; - private static final String REFERRER_URL = "urlref"; - private static final String REQUEST_DATETIME = "cdt"; - private static final String REQUIRED = "rec"; - private static final String RESPONSE_AS_IMAGE = "send_image"; - private static final String SEARCH_CATEGORY = "search_cat"; - private static final String SEARCH_QUERY = "search"; - private static final String SEARCH_RESULTS_COUNT = "search_count"; - private static final String SITE_ID = "idsite"; - private static final String TRACK_BOT_REQUESTS = "bots"; - private static final String VISIT_CUSTOM_VARIABLE = "_cvar"; - private static final String USER_ID = "uid"; - private static final String VISITOR_CITY = "city"; - private static final String VISITOR_COUNTRY = "country"; - private static final String VISITOR_CUSTOM_ID = "cid"; - private static final String VISITOR_FIRST_VISIT_TIMESTAMP = "_idts"; - private static final String VISITOR_ID = "_id"; - private static final String VISITOR_IP = "cip"; - private static final String VISITOR_LATITUDE = "lat"; - private static final String VISITOR_LONGITUDE = "long"; - private static final String VISITOR_PREVIOUS_VISIT_TIMESTAMP = "_viewts"; - private static final String VISITOR_REGION = "region"; - private static final String VISITOR_VISIT_COUNT = "_idvc"; - - private static final int RANDOM_VALUE_LENGTH = 20; - private static final long REQUEST_DATETIME_AUTH_LIMIT = 14400000L; - private static final Pattern VISITOR_ID_PATTERN = Pattern.compile("[0-9A-Fa-f]+"); - - private final Multimap parameters = LinkedHashMultimap.create(8, 1); - - private final Set customTrackingParameterNames = new HashSet<>(2); /** - * Create a new request from the id of the site being tracked and the full - * url for the current action. This constructor also sets: - *
-   * {@code
-   * Required = true
-   * Visior Id = random 16 character hex string
-   * Random Value = random 20 character hex string
-   * API version = 1
-   * Response as Image = false
-   * }
-   * 
- * Overwrite these values yourself as desired. - * - * @param siteId the id of the website we're tracking a visit/action for - * @param actionUrl the full URL for the current action + * The ID of the website we're tracking a visit/action for. Only needed, if no default site id is configured */ - public MatomoRequest(int siteId, String actionUrl) { - setParameter(SITE_ID, siteId); - setBooleanParameter(REQUIRED, true); - setParameter(ACTION_URL, actionUrl); - setParameter(VISITOR_ID, getRandomHexString(ID_LENGTH)); - setParameter(RANDOM_VALUE, getRandomHexString(RANDOM_VALUE_LENGTH)); - setParameter(API_VERSION, "1"); - setBooleanParameter(RESPONSE_AS_IMAGE, false); - } + @TrackingParameter(name = "rec") + @Default + private Boolean required = true; - public static MatomoRequestBuilder builder() { - return new MatomoRequestBuilder(); - } + /** + * The ID of the website we're tracking a visit/action for. Only needed, if no default site id is configured + */ + @TrackingParameter(name = "idsite") + private Integer siteId; /** - * Get the title of the action being tracked - * - * @return the title of the action being tracked + * The title of the action being tracked. For page tracks this is used as page title. If enabled in your installation + * you may use the category tree structure in this field. For example, "game / register new user" would then create a + * group "game" and add the item "register new user" in it. */ - @Nullable - public String getActionName() { - return castOrNull(ACTION_NAME); - } + @TrackingParameter(name = "action_name") + private String actionName; /** - * Set the title of the action being tracked. It is possible to - *
use slashes / - * to set one or several categories for this action. - * For example, Help / Feedback - * will create the Action Feedback in the category Help. - * - * @param actionName the title of the action to set. A null value will remove this parameter + * The full URL for the current action. */ - public void setActionName(String actionName) { - setParameter(ACTION_NAME, actionName); - } + @TrackingParameter(name = "url") + private String actionUrl; /** - * Get the amount of time it took the server to generate this action, in milliseconds. - * - * @return the amount of time + * Defines the API version to use (default: 1). */ - @Nullable - public Long getActionTime() { - return castOrNull(ACTION_TIME); - } + @TrackingParameter(name = "apiv") + @Default + private String apiVersion = "1"; /** - * Set the amount of time it took the server to generate this action, in milliseconds. - * This value is used to process the - * Page speed report - * Avg. generation time column in the Page URL and Page Title reports, - * as well as a site wide running average of the speed of your server. - * - * @param actionTime the amount of time to set. A null value will remove this parameter + * The unique visitor ID. See {@link VisitorId} */ - public void setActionTime(Long actionTime) { - setParameter(ACTION_TIME, actionTime); - } + @TrackingParameter(name = "_id") + @Default + private VisitorId visitorId = VisitorId.random(); /** - * Get the full URL for the current action. - * - * @return the full URL - * @deprecated Please use {@link #getActionUrlAsString} + * The full HTTP Referrer URL. This value is used to determine how someone got to your website (ie, through a website, + * search engine or campaign) */ - @Nullable - public URL getActionUrl() { - return castToUrlOrNull(ACTION_URL); - } + @TrackingParameter(name = "urlref") + private String referrerUrl; /** - * Get the full URL for the current action. - * - * @return the full URL + * Custom variables are custom name-value pairs that you can assign to your visitors (or page views). */ - @Deprecated - @Nullable - public String getActionUrlAsString() { - return castOrNull(ACTION_URL); - } + @TrackingParameter(name = "_cvar") + private CustomVariables visitCustomVariables; + /** + * The current count of visits for this visitor. To set this value correctly, it would be required to store the value + * for each visitor in your application (using sessions or persisting in a database). Then you would manually + * increment the counts by one on each new visit or "session", depending on how you choose to define a visit. + */ + @TrackingParameter(name = "_idvc") + private Integer visitorVisitCount; /** - * Set the full URL for the current action. - * - * @param actionUrl the full URL to set. A null value will remove this parameter - * @deprecated Please use {@link #setActionUrl(String)} + * The UNIX timestamp of this visitor's previous visit. This parameter is used to populate the report Visitors > + * Engagement > Visits by days since last visit. */ - @Deprecated - public void setActionUrl(@NonNull URL actionUrl) { - setActionUrl(actionUrl.toString()); - } + @TrackingParameter(name = "_viewts") + private Instant visitorPreviousVisitTimestamp; /** - * Set the full URL for the current action. - * - * @param actionUrl the full URL to set. A null value will remove this parameter + * The UNIX timestamp of this visitor's first visit. This could be set to the date where the user first started using + * your software/app, or when he/she created an account. */ - public void setActionUrl(String actionUrl) { - setParameter(ACTION_URL, actionUrl); - } + @TrackingParameter(name = "_idts") + private Instant visitorFirstVisitTimestamp; /** - * Set the full URL for the current action. - * - * @param actionUrl the full URL to set. A null value will remove this parameter - * @deprecated Please use {@link #setActionUrl(String)} + * The campaign name. This parameter will only be used for the first pageview of a visit. */ - @Deprecated - public void setActionUrlWithString(String actionUrl) { - setActionUrl(actionUrl); - } + @TrackingParameter(name = "_rcn") + private String campaignName; /** - * Get the api version - * - * @return the api version + * The campaign keyword (see + * Tracking Campaigns). Used to populate the Referrers > Campaigns report (clicking on a + * campaign loads all keywords for this campaign). This parameter will only be used for the first pageview of a visit. */ - @Nullable - public String getApiVersion() { - return castOrNull(API_VERSION); - } + @TrackingParameter(name = "_rck") + private String campaignKeyword; /** - * Set the api version to use (currently always set to 1) - * - * @param apiVersion the api version to set. A null value will remove this parameter + * The resolution of the device the visitor is using. */ - public void setApiVersion(String apiVersion) { - setParameter(API_VERSION, apiVersion); - } + @TrackingParameter(name = "res") + private DeviceResolution deviceResolution; /** - * Get the authorization key. - * - * @return the authorization key + * The current hour (local time). */ - @Nullable - public String getAuthToken() { - return castOrNull(AUTH_TOKEN); - } + @TrackingParameter(name = "h") + private Integer currentHour; /** - * Set the {@value #AUTH_TOKEN_LENGTH} character authorization key used to authenticate the API request. - * - * @param authToken the authorization key to set. A null value will remove this parameter + * The current minute (local time). */ - public void setAuthToken(String authToken) { - if (authToken != null && authToken.length() != AUTH_TOKEN_LENGTH) { - throw new IllegalArgumentException(authToken + " is not " + AUTH_TOKEN_LENGTH + " characters long."); - } - setParameter(AUTH_TOKEN, authToken); - } + @TrackingParameter(name = "m") + private Integer currentMinute; /** - * Verifies that AuthToken has been set for this request. Will throw an - * {@link IllegalStateException} if not. + * The current second (local time). */ - public void verifyAuthTokenSet() { - if (getAuthToken() == null) { - throw new IllegalStateException("AuthToken must be set before this value can be set."); - } - } + @TrackingParameter(name = "s") + private Integer currentSecond; /** - * Get the campaign keyword - * - * @return the campaign keyword + * Does the visitor use the Adobe Flash Plugin. */ - @Nullable - public String getCampaignKeyword() { - return castOrNull(CAMPAIGN_KEYWORD); - } + @TrackingParameter(name = "fla") + private Boolean pluginFlash; /** - * Set the Campaign Keyword (see - * Tracking Campaigns). - * Used to populate the Referrers > Campaigns report (clicking on a - * campaign loads all keywords for this campaign). Note: this parameter - * will only be used for the first pageview of a visit. - * - * @param campaignKeyword the campaign keyword to set. A null value will remove this parameter + * Does the visitor use the Java plugin. */ - public void setCampaignKeyword(String campaignKeyword) { - setParameter(CAMPAIGN_KEYWORD, campaignKeyword); - } + @TrackingParameter(name = "java") + private Boolean pluginJava; /** - * Get the campaign name - * - * @return the campaign name + * Does the visitor use Director plugin. */ - @Nullable - public String getCampaignName() { - return castOrNull(CAMPAIGN_NAME); - } + @TrackingParameter(name = "dir") + private Boolean pluginDirector; /** - * Set the Campaign Name (see - * Tracking Campaigns). - * Used to populate the Referrers > Campaigns report. Note: this parameter - * will only be used for the first pageview of a visit. - * - * @param campaignName the campaign name to set. A null value will remove this parameter + * Does the visitor use Quicktime plugin. */ - public void setCampaignName(String campaignName) { - setParameter(CAMPAIGN_NAME, campaignName); - } + @TrackingParameter(name = "qt") + private Boolean pluginQuicktime; /** - * Get the charset of the page being tracked - * - * @return the charset + * Does the visitor use Realplayer plugin. */ - @Nullable - public Charset getCharacterSet() { - return castOrNull(CHARACTER_SET); - } + @TrackingParameter(name = "realp") + private Boolean pluginRealPlayer; /** - * The charset of the page being tracked. Specify the charset if the data - * you send to Matomo is encoded in a different character set than the default - * utf-8. - * - * @param characterSet the charset to set. A null value will remove this parameter + * Does the visitor use a PDF plugin. */ - public void setCharacterSet(Charset characterSet) { - setParameter(CHARACTER_SET, characterSet); - } + @TrackingParameter(name = "pdf") + private Boolean pluginPDF; /** - * Get the name of the interaction with the content - * - * @return the name of the interaction + * Does the visitor use a Windows Media plugin. */ - @Nullable - public String getContentInteraction() { - return castOrNull(CONTENT_INTERACTION); - } + @TrackingParameter(name = "wma") + private Boolean pluginWindowsMedia; /** - * Set the name of the interaction with the content. For instance a 'click'. - * - * @param contentInteraction the name of the interaction to set. A null value will remove this parameter + * Does the visitor use a Gears plugin. */ - public void setContentInteraction(String contentInteraction) { - setParameter(CONTENT_INTERACTION, contentInteraction); - } + @TrackingParameter(name = "gears") + private Boolean pluginGears; /** - * Get the name of the content - * - * @return the name + * Does the visitor use a Silverlight plugin. */ - @Nullable - public String getContentName() { - return castOrNull(CONTENT_NAME); - } + @TrackingParameter(name = "ag") + private Boolean pluginSilverlight; /** - * Set the name of the content. For instance 'Ad Foo Bar'. - * - * @param contentName the name to set. A null value will remove this parameter + * Does the visitor's client is known to support cookies. */ - public void setContentName(String contentName) { - setParameter(CONTENT_NAME, contentName); - } + @TrackingParameter(name = "cookie") + private Boolean supportsCookies; /** - * Get the content piece. - * - * @return the content piece. + * An override value for the User-Agent HTTP header field. */ - @Nullable - public String getContentPiece() { - return castOrNull(CONTENT_PIECE); - } + @TrackingParameter(name = "ua") + private String headerUserAgent; /** - * Set the actual content piece. For instance the path to an image, video, audio, any text. - * - * @param contentPiece the content piece to set. A null value will remove this parameter + * An override value for the Accept-Language HTTP header field. This value is used to detect the visitor's country if + * GeoIP is not enabled. */ - public void setContentPiece(String contentPiece) { - setParameter(CONTENT_PIECE, contentPiece); - } + @TrackingParameter(name = "lang") + private AcceptLanguage headerAcceptLanguage; /** - * Get the content target - * - * @return the target + * Defines the User ID for this request. User ID is any non-empty unique string identifying the user (such as an email + * address or a username). When specified, the User ID will be "enforced". This means that if there is no recent + * visit with this User ID, a new one will be created. If a visit is found in the last 30 minutes with your specified + * User ID, then the new action will be recorded to this existing visit. */ - @Nullable - public URL getContentTarget() { - return castToUrlOrNull(CONTENT_TARGET); - } + @TrackingParameter(name = "uid") + private String userId; - @Nullable - private URL castToUrlOrNull(@NonNull String key) { - String url = castOrNull(key); - if (url == null) { - return null; - } - try { - return new URL(url); - } catch (MalformedURLException e) { - throw new InvalidUrlException(e); - } - } + /** + * defines the visitor ID for this request. + */ + @TrackingParameter(name = "cid") + private VisitorId visitorCustomId; /** - * Get the content target - * - * @return the target + * will force a new visit to be created for this action. */ - @Nullable - public String getContentTargetAsString() { - return castOrNull(CONTENT_TARGET); - } + @TrackingParameter(name = "new_visit") + private Boolean newVisit; /** - * Set the target of the content. For instance the URL of a landing page. - * - * @param contentTarget the target to set. A null value will remove this parameter - * @deprecated Please use {@link #setContentTarget(String)} + * Custom variables are custom name-value pairs that you can assign to your visitors (or page views). */ - @Deprecated - public void setContentTarget(@NonNull URL contentTarget) { - setContentTarget(contentTarget.toString()); - } + @TrackingParameter(name = "cvar") + private CustomVariables pageCustomVariables; /** - * Set the target of the content. For instance the URL of a landing page. - * - * @param contentTarget the target to set. A null value will remove this parameter + * An external URL the user has opened. Used for tracking outlink clicks. We recommend to also set the url parameter + * to this same value. */ - public void setContentTarget(String contentTarget) { - setParameter(CONTENT_TARGET, contentTarget); - } + @TrackingParameter(name = "link") + private String outlinkUrl; /** - * Set the target of the content. For instance the URL of a landing page. - * - * @param contentTarget the target to set. A null value will remove this parameter - * @deprecated Please use {@link #setContentTarget(String)} + * URL of a file the user has downloaded. Used for tracking downloads. We recommend to also set the url parameter to + * this same value. */ - @Deprecated - public void setContentTargetWithString(String contentTarget) { - setContentTarget(contentTarget); - } + @TrackingParameter(name = "download") + private String downloadUrl; /** - * Get the current hour. - * - * @return the current hour + * The Site Search keyword. When specified, the request will not be tracked as a normal pageview but will instead be + * tracked as a Site Search request */ - @Nullable - public Integer getCurrentHour() { - return castOrNull(CURRENT_HOUR); - } + @TrackingParameter(name = "search") + private String searchQuery; /** - * Set the current hour (local time). - * - * @param currentHour the hour to set. A null value will remove this parameter + * When search is specified, you can optionally specify a search category with this parameter. */ - public void setCurrentHour(Integer currentHour) { - setParameter(CURRENT_HOUR, currentHour); - } + @TrackingParameter(name = "search_cat") + private String searchCategory; /** - * Get the current minute. - * - * @return the current minute + * When search is specified, we also recommend setting the search_count to the number of search results displayed on + * the results page. When keywords are tracked with &search_count=0 they will appear in the "No Result Search Keyword" + * report. */ - @Nullable - public Integer getCurrentMinute() { - return castOrNull(CURRENT_MINUTE); - } + @TrackingParameter(name = "search_count") + private Long searchResultsCount; /** - * Set the current minute (local time). - * - * @param currentMinute the minute to set. A null value will remove this parameter + * Accepts a six character unique ID that identifies which actions were performed on a specific page view. When a page + * was viewed, all following tracking requests (such as events) during that page view should use the same pageview ID. + * Once another page was viewed a new unique ID should be generated. Use [0-9a-Z] as possible characters for the + * unique ID. */ - public void setCurrentMinute(Integer currentMinute) { - setParameter(CURRENT_MINUTE, currentMinute); - } + @TrackingParameter(name = "pv_id") + private UniqueId pageViewId; /** - * Get the current second - * - * @return the current second + * If specified, the tracking request will trigger a conversion for the goal of the website being tracked with this + * ID. */ - @Nullable - public Integer getCurrentSecond() { - return castOrNull(CURRENT_SECOND); - } + @TrackingParameter(name = "idgoal") + private Integer goalId; /** - * Set the current second (local time). - * - * @param currentSecond the second to set. A null value will remove this parameter + * The grand total for the ecommerce order (required when tracking an ecommerce order). */ - public void setCurrentSecond(Integer currentSecond) { - setParameter(CURRENT_SECOND, currentSecond); - } + @TrackingParameter(name = "revenue") + private Double ecommerceRevenue; /** - * Get the custom action - * - * @return the custom action + * The charset of the page being tracked. Specify the charset if the data you send to Matomo is encoded in a different + * character set than the default utf-8 */ - @Nullable - public Boolean getCustomAction() { - return getBooleanParameter(CUSTOM_ACTION); - } + @TrackingParameter(name = "cs") + private Charset characterSet; /** - * Set the custom action - * - * @param customAction the second to set. A null value will remove this parameter + * can be optionally sent along any tracking request that isn't a page view. For example, it can be sent together with + * an event tracking request. The advantage being that should you ever disable the event plugin, then the event + * tracking requests will be ignored vs if the parameter is not set, a page view would be tracked even though it isn't + * a page view. */ - public void setCustomAction(Boolean customAction) { - setBooleanParameter(CUSTOM_ACTION, customAction); - } + @TrackingParameter(name = "ca") + private Boolean customAction; /** - * Gets the list of objects currently stored at the specified custom tracking - * parameter. An empty list will be returned if there are no objects set at - * that key. - * - * @param key the key of the parameter whose list of objects to get. Cannot be null - * @return the list of objects currently stored at the specified key + * How long it took to connect to server. */ - public List getCustomTrackingParameter(@NonNull String key) { - return new ArrayList<>(parameters.get(key)); - } + @TrackingParameter(name = "pf_net") + private Long networkTime; /** - * Set a custom tracking parameter whose toString() value will be sent to - * the Matomo server. These parameters are stored separately from named Matomo - * parameters, meaning it is not possible to overwrite or clear named Matomo - * parameters with this method. A custom parameter that has the same name - * as a named Matomo parameter will be sent in addition to that named parameter. - * - * @param key the parameter's key. Cannot be null - * @param value the parameter's value. Removes the parameter if null + * How long it took the server to generate page. */ - public void setCustomTrackingParameter(@NonNull String key, @Nullable T value) { - customTrackingParameterNames.add(key); - setParameter(key, value); - } + @TrackingParameter(name = "pf_srv") + private Long serverTime; /** - * Add a custom tracking parameter to the specified key. This allows users - * to have multiple parameters with the same name and different values, - * commonly used during situations where list parameters are needed - * - * @param key the parameter's key. Cannot be null - * @param value the parameter's value. Cannot be null + * How long it takes the browser to download the response from the server. */ - public void addCustomTrackingParameter(@NonNull String key, @NonNull Object value) { - customTrackingParameterNames.add(key); - addParameter(key, value); - } + @TrackingParameter(name = "pf_tfr") + private Long transferTime; /** - * Removes all custom tracking parameters + * How long the browser spends loading the webpage after the response was fully received until the user can start + * interacting with it. */ - public void clearCustomTrackingParameter() { - for (String customTrackingParameterName : customTrackingParameterNames) { - setParameter(customTrackingParameterName, null); - } - } + @TrackingParameter(name = "pf_dm1") + private Long domProcessingTime; /** - * Get the resolution of the device - * - * @return the resolution + * How long it takes for the browser to load media and execute any Javascript code listening for the DOMContentLoaded + * event. */ - @Nullable - public String getDeviceResolution() { - return castOrNull(DEVICE_RESOLUTION); - } + @TrackingParameter(name = "pf_dm2") + private Long domCompletionTime; /** - * Set the resolution of the device the visitor is using, eg 1280x1024. - * - * @param deviceResolution the resolution to set. A null value will remove this parameter + * How long it takes the browser to execute Javascript code waiting for the window.load event. */ - public void setDeviceResolution(String deviceResolution) { - setParameter(DEVICE_RESOLUTION, deviceResolution); - } + @TrackingParameter(name = "pf_onl") + private Long onloadTime; /** - * Get the url of a file the user had downloaded - * - * @return the url + * eg. Videos, Music, Games... */ - @Nullable - public URL getDownloadUrl() { - return castToUrlOrNull(DOWNLOAD_URL); - } + @TrackingParameter(name = "e_c") + private String eventCategory; /** - * Get the url of a file the user had downloaded - * - * @return the url + * e.g. Play, Pause, Duration, Add Playlist, Downloaded, Clicked... */ - @Nullable - public String getDownloadUrlAsString() { - return castOrNull(DOWNLOAD_URL); - } + @TrackingParameter(name = "e_a") + private String eventAction; /** - * Set the url of a file the user has downloaded. Used for tracking downloads. - * We recommend to also set the url parameter to this same value. - * - * @param downloadUrl the url to set. A null value will remove this parameter - * @deprecated Please use {@link #setDownloadUrl(String)} + * e.g. a Movie name, or Song name, or File name... */ - @Deprecated - public void setDownloadUrl(@NonNull URL downloadUrl) { - setDownloadUrl(downloadUrl.toString()); - } + @TrackingParameter(name = "e_n") + private String eventName; /** - * Set the url of a file the user has downloaded. Used for tracking downloads. - * We recommend to also set the url parameter to this same value. - * - * @param downloadUrl the url to set. A null value will remove this parameter + * Some numeric value that represents the event value. */ - public void setDownloadUrl(String downloadUrl) { - setParameter(DOWNLOAD_URL, downloadUrl); - } + @TrackingParameter(name = "e_n") + private Double eventValue; /** - * Set the url of a file the user has downloaded. Used for tracking downloads. - * We recommend to also set the url parameter to this same value. - * - * @param downloadUrl the url to set. A null value will remove this parameter - * @deprecated Please use {@link #setDownloadUrl(String)} + * The name of the content. For instance 'Ad Foo Bar' */ - @Deprecated - public void setDownloadUrlWithString(String downloadUrl) { - setDownloadUrl(downloadUrl); - } + @TrackingParameter(name = "c_n") + private String contentName; /** - * Sets idgoal=0 in the request to track an ecommerce interaction: - * cart update or an ecommerce order. + * The actual content piece. For instance the path to an image, video, audio, any text */ - public void enableEcommerce() { - setGoalId(0); - } + @TrackingParameter(name = "c_p") + private String contentPiece; /** - * Verifies that Ecommerce has been enabled for the request. Will throw an - * {@link IllegalStateException} if not. + * The target of the content. For instance the URL of a landing page */ - public void verifyEcommerceEnabled() { - if (getGoalId() == null || getGoalId() != 0) { - throw new IllegalStateException("GoalId must be \"0\". Try calling enableEcommerce first before calling this method."); - } - } + @TrackingParameter(name = "c_t") + private String contentTarget; /** - * Verifies that Ecommerce has been enabled and that Ecommerce Id and - * Ecommerce Revenue have been set for the request. Will throw an - * {@link IllegalStateException} if not. + * The name of the interaction with the content. For instance a 'click' */ - public void verifyEcommerceState() { - verifyEcommerceEnabled(); - if (getEcommerceId() == null) { - throw new IllegalStateException("EcommerceId must be set before this value can be set."); - } - if (getEcommerceRevenue() == null) { - throw new IllegalStateException("EcommerceRevenue must be set before this value can be set."); - } - } + @TrackingParameter(name = "c_i") + private String contentInteraction; /** - * Get the discount offered. - * - * @return the discount + * he unique string identifier for the ecommerce order (required when tracking an ecommerce order). you must set + * &idgoal=0 in the request to track an ecommerce interaction: cart update or an ecommerce order. */ - @Nullable - public Double getEcommerceDiscount() { - return castOrNull(ECOMMERCE_DISCOUNT); - } + @TrackingParameter(name = "ec_id") + private String ecommerceId; /** - * Set the discount offered. Ecommerce must be enabled, and EcommerceId and - * EcommerceRevenue must first be set. - * - * @param discount the discount to set. A null value will remove this parameter + * Items in the Ecommerce order. */ - public void setEcommerceDiscount(Double discount) { - if (discount != null) { - verifyEcommerceState(); - } - setParameter(ECOMMERCE_DISCOUNT, discount); - } + @TrackingParameter(name = "ec_items") + private EcommerceItems ecommerceItems; /** - * Get the id of this order. - * - * @return the id + * The subtotal of the order; excludes shipping. */ - @Nullable - public String getEcommerceId() { - return castOrNull(ECOMMERCE_ID); - } + @TrackingParameter(name = "ec_st") + private Double ecommerceSubtotal; /** - * Set the unique string identifier for the ecommerce order (required when - * tracking an ecommerce order). Ecommerce must be enabled. - * - * @param id the id to set. A null value will remove this parameter + * Tax amount of the order. */ - public void setEcommerceId(String id) { - if (id != null) { - verifyEcommerceEnabled(); - } - setParameter(ECOMMERCE_ID, id); - } + @TrackingParameter(name = "ec_tx") + private Double ecommerceTax; /** - * Get the {@link EcommerceItem} at the specified index - * - * @param index the index of the {@link EcommerceItem} to return - * @return the {@link EcommerceItem} at the specified index + * Shipping cost of the order. */ - @Nullable - public EcommerceItem getEcommerceItem(int index) { - EcommerceItems ecommerceItems = castOrNull(ECOMMERCE_ITEMS); - if (ecommerceItems == null) { - return null; - } - return ecommerceItems.get(index); - } + @TrackingParameter(name = "ec_sh") + private Double ecommerceShippingCost; /** - * Add an {@link EcommerceItem} to this order. Ecommerce must be enabled, - * and EcommerceId and EcommerceRevenue must first be set. - * - * @param item the {@link EcommerceItem} to add. Cannot be null + * Discount offered. */ - public void addEcommerceItem(@NonNull EcommerceItem item) { - verifyEcommerceState(); - EcommerceItems ecommerceItems = castOrNull(ECOMMERCE_ITEMS); - if (ecommerceItems == null) { - ecommerceItems = new EcommerceItems(); - setParameter(ECOMMERCE_ITEMS, ecommerceItems); - } - ecommerceItems.add(item); - } + @TrackingParameter(name = "ec_dt") + private Double ecommerceDiscount; /** - * Clears all {@link EcommerceItem} from this order. + * The UNIX timestamp of this customer's last ecommerce order. This value is used to process the "Days since last + * order" report. */ - public void clearEcommerceItems() { - setParameter(ECOMMERCE_ITEMS, null); - } + @TrackingParameter(name = "_ects") + private Instant ecommerceLastOrderTimestamp; /** - * Get the timestamp of the customer's last ecommerce order - * - * @return the timestamp + * 32 character authorization key used to authenticate the API request. We recommend to create a user specifically for + * accessing the Tracking API, and give the user only write permission on the website(s). */ - @Nullable - public Long getEcommerceLastOrderTimestamp() { - return castOrNull(ECOMMERCE_LAST_ORDER_TIMESTAMP); - } + @TrackingParameter(name = "token_auth", regex = "[a-z0-9]{32}") + private String authToken; + /** - * Set the UNUX timestamp of this customer's last ecommerce order. This value - * is used to process the "Days since last order" report. Ecommerce must be - * enabled, and EcommerceId and EcommerceRevenue must first be set. - * - * @param timestamp the timestamp to set. A null value will remove this parameter + * Override value for the visitor IP (both IPv4 and IPv6 notations supported). */ - public void setEcommerceLastOrderTimestamp(Long timestamp) { - if (timestamp != null) { - verifyEcommerceState(); - } - setParameter(ECOMMERCE_LAST_ORDER_TIMESTAMP, timestamp); - } + @TrackingParameter(name = "cip") + private String visitorIp; /** - * Get the grand total of the ecommerce order. - * - * @return the grand total + * Override for the datetime of the request (normally the current time is used). This can be used to record visits and + * page views in the past. */ - @Nullable - public Double getEcommerceRevenue() { - return castOrNull(ECOMMERCE_REVENUE); - } + @TrackingParameter(name = "cdt") + private Instant requestTimestamp; /** - * Set the grand total of the ecommerce order (required when tracking an - * ecommerce order). Ecommerce must be enabled. - * - * @param revenue the grand total to set. A null value will remove this parameter + * An override value for the country. Must be a two-letter ISO 3166 Alpha-2 country code. */ - public void setEcommerceRevenue(Double revenue) { - if (revenue != null) { - verifyEcommerceEnabled(); - } - setParameter(ECOMMERCE_REVENUE, revenue); - } + @TrackingParameter(name = "country") + private Country visitorCountry; /** - * Get the shipping cost of the ecommerce order. - * - * @return the shipping cost + * An override value for the region. Should be set to a ISO 3166-2 region code, which are used by MaxMind's and + * DB-IP's GeoIP2 databases. See here for a list of them for every country. */ - @Nullable - public Double getEcommerceShippingCost() { - return castOrNull(ECOMMERCE_SHIPPING_COST); - } + @TrackingParameter(name = "region") + private String visitorRegion; /** - * Set the shipping cost of the ecommerce order. Ecommerce must be enabled, - * and EcommerceId and EcommerceRevenue must first be set. - * - * @param shippingCost the shipping cost to set. A null value will remove this parameter + * An override value for the city. The name of the city the visitor is located in, eg, Tokyo. */ - public void setEcommerceShippingCost(Double shippingCost) { - if (shippingCost != null) { - verifyEcommerceState(); - } - setParameter(ECOMMERCE_SHIPPING_COST, shippingCost); - } + @TrackingParameter(name = "city") + private String visitorCity; /** - * Get the subtotal of the ecommerce order; excludes shipping. - * - * @return the subtotal + * An override value for the visitor's latitude, eg 22.456. */ - @Nullable - public Double getEcommerceSubtotal() { - return castOrNull(ECOMMERCE_SUBTOTAL); - } + @TrackingParameter(name = "lat") + private Double visitorLatitude; /** - * Set the subtotal of the ecommerce order; excludes shipping. Ecommerce - * must be enabled and EcommerceId and EcommerceRevenue must first be set. - * - * @param subtotal the subtotal to set. A null value will remove this parameter + * An override value for the visitor's longitude, eg 22.456. */ - public void setEcommerceSubtotal(Double subtotal) { - if (subtotal != null) { - verifyEcommerceState(); - } - setParameter(ECOMMERCE_SUBTOTAL, subtotal); - } + @TrackingParameter(name = "long") + private Double visitorLongitude; /** - * Get the tax amount of the ecommerce order. - * - * @return the tax amount + * When set to false, the queued tracking handler won't be used and instead the tracking request will be executed + * directly. This can be useful when you need to debug a tracking problem or want to test that the tracking works in + * general. */ - @Nullable - public Double getEcommerceTax() { - return castOrNull(ECOMMERCE_TAX); - } + @TrackingParameter(name = "queuedtracking") + private Boolean queuedTracking; /** - * Set the tax amount of the ecommerce order. Ecommerce must be enabled, and - * EcommerceId and EcommerceRevenue must first be set. + * If set to 0 (send_image=0) Matomo will respond with an HTTP 204 response code instead of a GIF image. This improves + * performance and can fix errors if images are not allowed to be obtained directly (e.g. Chrome Apps). Available + * since Matomo 2.10.0 * - * @param tax the tax amount to set. A null value will remove this parameter + *

Default is {@code false} */ - public void setEcommerceTax(Double tax) { - if (tax != null) { - verifyEcommerceState(); - } - setParameter(ECOMMERCE_TAX, tax); - } - - /** - * Get the event action. - * - * @return the event action - */ - @Nullable - public String getEventAction() { - return castOrNull(EVENT_ACTION); - } - - /** - * Set the event action. Must not be empty. (eg. Play, Pause, Duration, - * Add Playlist, Downloaded, Clicked...). - * - * @param eventAction the event action to set. A null value will remove this parameter - */ - public void setEventAction(String eventAction) { - setNonEmptyStringParameter(EVENT_ACTION, eventAction); - } - - /** - * Get the event category. - * - * @return the event category - */ - @Nullable - public String getEventCategory() { - return castOrNull(EVENT_CATEGORY); - } - - /** - * Set the event category. Must not be empty. (eg. Videos, Music, Games...). - * - * @param eventCategory the event category to set. A null value will remove this parameter - */ - public void setEventCategory(String eventCategory) { - setNonEmptyStringParameter(EVENT_CATEGORY, eventCategory); - } + @TrackingParameter(name = "send_image") + @Default + private Boolean responseAsImage = false; /** - * Get the event name. - * - * @return the event name + * If set to true, the request will be a Heartbeat request which will not track any new activity (such as a new visit, + * new action or new goal). The heartbeat request will only update the visit's total time to provide accurate "Visit + * duration" metric when this parameter is set. It won't record any other data. This means by sending an additional + * tracking request when the user leaves your site or app with &ping=1, you fix the issue where the time spent of the + * last page visited is reported as 0 seconds. */ - @Nullable - public String getEventName() { - return castOrNull(EVENT_NAME); - } + @TrackingParameter(name = "ping") + private Boolean ping; /** - * Set the event name. (eg. a Movie name, or Song name, or File name...). - * - * @param eventName the event name to set. A null value will remove this parameter + * By default, Matomo does not track bots. If you use the Tracking HTTP API directly, you may be interested in + * tracking bot requests. */ - public void setEventName(String eventName) { - setParameter(EVENT_NAME, eventName); - } + @TrackingParameter(name = "bots") + private Boolean trackBotRequests; - /** - * Get the event value. - * - * @return the event value - */ - @Nullable - public Number getEventValue() { - return castOrNull(EVENT_VALUE); - } /** - * Set the event value. Must be a float or integer value (numeric), not a string. - * - * @param eventValue the event value to set. A null value will remove this parameter + * Meant to hold a random value that is generated before each request. Using it helps avoid the tracking request + * being cached by the browser or a proxy. */ - public void setEventValue(Number eventValue) { - setParameter(EVENT_VALUE, eventValue); - } + @TrackingParameter(name = "rand") + @Default + private RandomValue randomValue = RandomValue.random(); - /** - * Get the goal id - * - * @return the goal id - */ - @Nullable - public Integer getGoalId() { - return castOrNull(GOAL_ID); - } + private Iterable dimensions; - /** - * Set the goal id. If specified, the tracking request will trigger a - * conversion for the goal of the website being tracked with this id. - * - * @param goalId the goal id to set. A null value will remove this parameter - */ - public void setGoalId(Integer goalId) { - setParameter(GOAL_ID, goalId); - } + private Map> customTrackingParameters; /** - * Get the goal revenue. + * Create a new request from the id of the site being tracked and the full + * url for the current action. This constructor also sets: + *
+   * {@code
+   * Required = true
+   * Visior Id = random 16 character hex string
+   * Random Value = random 20 character hex string
+   * API version = 1
+   * Response as Image = false
+   * }
+   * 
+ * Overwrite these values yourself as desired. * - * @return the goal revenue + * @param siteId the id of the website we're tracking a visit/action for + * @param actionUrl the full URL for the current action + * @deprecated Please use {@link MatomoRequest#builder()} */ - @Nullable - public Double getGoalRevenue() { - return castOrNull(GOAL_REVENUE); + @Deprecated + public MatomoRequest(int siteId, String actionUrl) { + this.siteId = siteId; + this.actionUrl = actionUrl; + required = true; + visitorId = VisitorId.random(); + randomValue = RandomValue.random(); + apiVersion = "1"; + responseAsImage = false; } /** - * Set a monetary value that was generated as revenue by this goal conversion. - * Only used if idgoal is specified in the request. + * Gets the list of objects currently stored at the specified custom tracking + * parameter. An empty list will be returned if there are no objects set at + * that key. * - * @param goalRevenue the goal revenue to set. A null value will remove this parameter + * @param key the key of the parameter whose list of objects to get. Cannot be null + * @return the list of objects currently stored at the specified key */ - public void setGoalRevenue(Double goalRevenue) { - if (goalRevenue != null && getGoalId() == null) { - throw new IllegalStateException("GoalId must be set before GoalRevenue can be set."); + public List getCustomTrackingParameter(@NonNull String key) { + if (customTrackingParameters == null || customTrackingParameters.isEmpty()) { + return Collections.emptyList(); } - setParameter(GOAL_REVENUE, goalRevenue); - } - - /** - * Get the Accept-Language HTTP header - * - * @return the Accept-Language HTTP header - */ - @Nullable - public String getHeaderAcceptLanguage() { - return castOrNull(HEADER_ACCEPT_LANGUAGE); - } - - /** - * Set an override value for the Accept-Language HTTP header - * field. This value is used to detect the visitor's country if - * GeoIP is not enabled. - * - * @param acceptLangage the Accept-Language HTTP header to set. A null value will remove this parameter - */ - public void setHeaderAcceptLanguage(String acceptLangage) { - setParameter(HEADER_ACCEPT_LANGUAGE, acceptLangage); + Collection parameterValues = customTrackingParameters.get(key); + if (parameterValues == null || parameterValues.isEmpty()) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(new ArrayList<>(parameterValues)); } /** - * Get the User-Agent HTTP header + * Set a custom tracking parameter whose toString() value will be sent to + * the Matomo server. These parameters are stored separately from named Matomo + * parameters, meaning it is not possible to overwrite or clear named Matomo + * parameters with this method. A custom parameter that has the same name + * as a named Matomo parameter will be sent in addition to that named parameter. * - * @return the User-Agent HTTP header + * @param key the parameter's key. Cannot be null + * @param value the parameter's value. Removes the parameter if null */ - @Nullable - public String getHeaderUserAgent() { - return castOrNull(HEADER_USER_AGENT); - } + public void setCustomTrackingParameter(@NonNull String key, @Nullable Object value) { - /** - * Set an override value for the User-Agent HTTP header field. - * The user agent is used to detect the operating system and browser used. - * - * @param userAgent the User-Agent HTTP header tos et - */ - public void setHeaderUserAgent(String userAgent) { - setParameter(HEADER_USER_AGENT, userAgent); + if (value == null) { + if (customTrackingParameters != null) { + customTrackingParameters.remove(key); + } + } else { + if (customTrackingParameters == null) { + customTrackingParameters = new LinkedHashMap<>(); + } + Collection values = customTrackingParameters.computeIfAbsent(key, k -> new ArrayList<>()); + values.clear(); + values.add(value); + } } /** - * Get if this request will force a new visit. + * Add a custom tracking parameter to the specified key. This allows users + * to have multiple parameters with the same name and different values, + * commonly used during situations where list parameters are needed * - * @return true if this request will force a new visit + * @param key the parameter's key. Cannot be null + * @param value the parameter's value. Cannot be null */ - @Nullable - public Boolean getNewVisit() { - return getBooleanParameter(NEW_VISIT); + public void addCustomTrackingParameter(@NonNull String key, @NonNull Object value) { + if (customTrackingParameters == null) { + customTrackingParameters = new LinkedHashMap<>(); + } + customTrackingParameters.computeIfAbsent(key, k -> new ArrayList<>()).add(value); } /** - * If set to true, will force a new visit to be created for this action. - * - * @param newVisit if this request will force a new visit + * Removes all custom tracking parameters. */ - public void setNewVisit(Boolean newVisit) { - setBooleanParameter(NEW_VISIT, newVisit); + public void clearCustomTrackingParameter() { + customTrackingParameters.clear(); } /** - * Get the outlink url + * Sets idgoal=0 in the request to track an ecommerce interaction: + * cart update or an ecommerce order. * - * @return the outlink url + * @deprecated Please use {@link MatomoRequest#setGoalId(Integer)} instead */ - @Nullable - public URL getOutlinkUrl() { - return castToUrlOrNull(OUTLINK_URL); + @Deprecated + public void enableEcommerce() { + setGoalId(0); } /** - * Get the outlink url + * Get the {@link EcommerceItem} at the specified index. * - * @return the outlink url + * @param index the index of the {@link EcommerceItem} to return + * @return the {@link EcommerceItem} at the specified index */ @Nullable - public String getOutlinkUrlAsString() { - return castOrNull(OUTLINK_URL); - } - - /** - * Set an external URL the user has opened. Used for tracking outlink clicks. - * We recommend to also set the url parameter to this same value. - * - * @param outlinkUrl the outlink url to set. A null value will remove this parameter - * @deprecated Please use {@link #setOutlinkUrl(String)} - */ - @Deprecated - public void setOutlinkUrl(@NonNull URL outlinkUrl) { - setOutlinkUrl(outlinkUrl.toString()); + public EcommerceItem getEcommerceItem(int index) { + if (ecommerceItems == null || ecommerceItems.isEmpty()) { + return null; + } + return ecommerceItems.get(index); } - /** - * Set an external URL the user has opened. Used for tracking outlink clicks. - * We recommend to also set the url parameter to this same value. + * Add an {@link EcommerceItem} to this order. Ecommerce must be enabled, + * and EcommerceId and EcommerceRevenue must first be set. * - * @param outlinkUrl the outlink url to set. A null value will remove this parameter + * @param item the {@link EcommerceItem} to add. Cannot be null */ - public void setOutlinkUrl(String outlinkUrl) { - setParameter(OUTLINK_URL, outlinkUrl); + public void addEcommerceItem(@NonNull EcommerceItem item) { + if (ecommerceItems == null) { + ecommerceItems = new EcommerceItems(); + } + ecommerceItems.add(item); } /** - * Set an external URL the user has opened. Used for tracking outlink clicks. - * We recommend to also set the url parameter to this same value. - * - * @param outlinkUrl the outlink url to set. A null value will remove this parameter - * @deprecated Please use {@link #setOutlinkUrl(String)} + * Clears all {@link EcommerceItem} from this order. */ - @Deprecated - public void setOutlinkUrlWithString(String outlinkUrl) { - setOutlinkUrl(outlinkUrl); + public void clearEcommerceItems() { + ecommerceItems.clear(); } /** @@ -1164,7 +721,10 @@ public void setOutlinkUrlWithString(String outlinkUrl) { @Nullable @Deprecated public String getPageCustomVariable(String key) { - return getCustomVariable(PAGE_CUSTOM_VARIABLE, key); + if (pageCustomVariables == null) { + return null; + } + return pageCustomVariables.get(key); } /** @@ -1172,10 +732,20 @@ public String getPageCustomVariable(String key) { * * @param index the index of the variable to get. Must be greater than 0 * @return the variable at the specified key, null if nothing at this index + * @deprecated Use {@link MatomoRequest#getPageCustomVariables()} instead */ + @Deprecated @Nullable public CustomVariable getPageCustomVariable(int index) { - return getCustomVariable(PAGE_CUSTOM_VARIABLE, index); + return getCustomVariable(pageCustomVariables, index); + } + + @Nullable + private static CustomVariable getCustomVariable(CustomVariables customVariables, int index) { + if (customVariables == null) { + return null; + } + return customVariables.get(index); } /** @@ -1184,989 +754,161 @@ public CustomVariable getPageCustomVariable(int index) { * * @param key the key of the variable to set * @param value the value of the variable to set at the specified key. A null value will remove this custom variable - * @deprecated Use the {@link #setPageCustomVariable(CustomVariable, int)} method instead. + * @deprecated Use {@link MatomoRequest#getPageCustomVariables()} instead */ @Deprecated - public void setPageCustomVariable(String key, String value) { + public void setPageCustomVariable(@NotNull String key, @Nullable String value) { + requireNonNull(key, "Key must not be null"); if (value == null) { - removeCustomVariable(PAGE_CUSTOM_VARIABLE, key); + if (pageCustomVariables == null) { + return; + } + pageCustomVariables.remove(key); } else { - setCustomVariable(PAGE_CUSTOM_VARIABLE, new CustomVariable(key, value), null); + CustomVariable variable = new CustomVariable(key, value); + if (pageCustomVariables == null) { + pageCustomVariables = new CustomVariables(); + } + pageCustomVariables.add(variable); } } /** * Set a page custom variable at the specified index. * - * @param customVariable the CustomVariable to set. A null value will remove the CustomVariable at the specified index + * @param customVariable the CustomVariable to set. A null value will remove the CustomVariable at the specified + * index * @param index the index of he CustomVariable to set + * @deprecated Use {@link #getPageCustomVariables()} instead */ - public void setPageCustomVariable(CustomVariable customVariable, int index) { - setCustomVariable(PAGE_CUSTOM_VARIABLE, customVariable, index); - } - - /** - * Check if the visitor has the Director plugin. - * - * @return true if visitor has the Director plugin - */ - @Nullable - public Boolean getPluginDirector() { - return getBooleanParameter(PLUGIN_DIRECTOR); - } - - /** - * Set if the visitor has the Director plugin. - * - * @param director true if the visitor has the Director plugin - */ - public void setPluginDirector(Boolean director) { - setBooleanParameter(PLUGIN_DIRECTOR, director); - } - - /** - * Check if the visitor has the Flash plugin. - * - * @return true if the visitor has the Flash plugin - */ - @Nullable - public Boolean getPluginFlash() { - return getBooleanParameter(PLUGIN_FLASH); - } - - /** - * Set if the visitor has the Flash plugin. - * - * @param flash true if the visitor has the Flash plugin - */ - @Nullable - public void setPluginFlash(Boolean flash) { - setBooleanParameter(PLUGIN_FLASH, flash); - } - - /** - * Check if the visitor has the Gears plugin. - * - * @return true if the visitor has the Gears plugin - */ - @Nullable - public Boolean getPluginGears() { - return getBooleanParameter(PLUGIN_GEARS); - } - - /** - * Set if the visitor has the Gears plugin. - * - * @param gears true if the visitor has the Gears plugin - */ - public void setPluginGears(Boolean gears) { - setBooleanParameter(PLUGIN_GEARS, gears); - } - - /** - * Check if the visitor has the Java plugin. - * - * @return true if the visitor has the Java plugin - */ - @Nullable - public Boolean getPluginJava() { - return getBooleanParameter(PLUGIN_JAVA); + @Deprecated + public void setPageCustomVariable(@Nullable CustomVariable customVariable, int index) { + if (pageCustomVariables == null) { + if (customVariable == null) { + return; + } + pageCustomVariables = new CustomVariables(); + } + setCustomVariable(pageCustomVariables, customVariable, index); } - /** - * Set if the visitor has the Java plugin. - * - * @param java true if the visitor has the Java plugin - */ - public void setPluginJava(Boolean java) { - setBooleanParameter(PLUGIN_JAVA, java); + private static void setCustomVariable( + CustomVariables customVariables, @Nullable CustomVariable customVariable, + int index + ) { + if (customVariable == null) { + customVariables.remove(index); + } else { + customVariables.add(customVariable, index); + } } /** - * Check if the visitor has the PDF plugin. + * Get the datetime of the request. * - * @return true if the visitor has the PDF plugin + * @return the datetime of the request + * @deprecated Use {@link #getRequestTimestamp()} instead */ + @Deprecated @Nullable - public Boolean getPluginPDF() { - return getBooleanParameter(PLUGIN_PDF); - } - - /** - * Set if the visitor has the PDF plugin. - * - * @param pdf true if the visitor has the PDF plugin - */ - public void setPluginPDF(Boolean pdf) { - setBooleanParameter(PLUGIN_PDF, pdf); + public MatomoDate getRequestDatetime() { + return requestTimestamp == null ? null : new MatomoDate(requestTimestamp.toEpochMilli()); } /** - * Check if the visitor has the Quicktime plugin. + * Set the datetime of the request (normally the current time is used). + * This can be used to record visits and page views in the past. The datetime + * must be sent in UTC timezone. Note: if you record data in the past, you will + * need to force Matomo to re-process + * reports for the past dates. If you set the Request Datetime to a datetime + * older than four hours then Auth Token must be set. If you set + * Request Datetime with a datetime in the last four hours then you + * don't need to pass Auth Token. * - * @return true if the visitor has the Quicktime plugin + * @param matomoDate the datetime of the request to set. A null value will remove this parameter + * @deprecated Use {@link #setRequestTimestamp(Instant)} instead */ - @Nullable - public Boolean getPluginQuicktime() { - return getBooleanParameter(PLUGIN_QUICKTIME); + @Deprecated + public void setRequestDatetime(MatomoDate matomoDate) { + if (matomoDate == null) { + requestTimestamp = null; + } else { + setRequestTimestamp(matomoDate.getZonedDateTime().toInstant()); + } } - /** - * Set if the visitor has the Quicktime plugin. - * - * @param quicktime true if the visitor has the Quicktime plugin - */ - public void setPluginQuicktime(Boolean quicktime) { - setBooleanParameter(PLUGIN_QUICKTIME, quicktime); - } /** - * Check if the visitor has the RealPlayer plugin. + * Get the visit custom variable at the specified key. * - * @return true if the visitor has the RealPlayer plugin + * @param key the key of the variable to get + * @return the variable at the specified key, null if key is not present + * @deprecated Use the {@link #getVisitCustomVariable(int)} method instead. */ @Nullable - public Boolean getPluginRealPlayer() { - return getBooleanParameter(PLUGIN_REAL_PLAYER); - } - - /** - * Set if the visitor has the RealPlayer plugin. - * - * @param realPlayer true if the visitor has the RealPlayer plugin - */ - public void setPluginRealPlayer(Boolean realPlayer) { - setBooleanParameter(PLUGIN_REAL_PLAYER, realPlayer); + @Deprecated + public String getUserCustomVariable(String key) { + if (visitCustomVariables == null) { + return null; + } + return visitCustomVariables.get(key); } /** - * Check if the visitor has the Silverlight plugin. + * Get the visit custom variable at the specified index. * - * @return true if the visitor has the Silverlight plugin + * @param index the index of the variable to get + * @return the variable at the specified index, null if nothing at this index + * @deprecated Use {@link #getVisitCustomVariables()} instead */ @Nullable - public Boolean getPluginSilverlight() { - return getBooleanParameter(PLUGIN_SILVERLIGHT); + @Deprecated + public CustomVariable getVisitCustomVariable(int index) { + return getCustomVariable(visitCustomVariables, index); } /** - * Set if the visitor has the Silverlight plugin. + * Set a visit custom variable with the specified key and value at the first available index. + * All visit custom variables with this key will be overwritten or deleted * - * @param silverlight true if the visitor has the Silverlight plugin + * @param key the key of the variable to set + * @param value the value of the variable to set at the specified key. A null value will remove this parameter + * @deprecated Use {@link #setVisitCustomVariables(CustomVariables)} instead */ - public void setPluginSilverlight(Boolean silverlight) { - setBooleanParameter(PLUGIN_SILVERLIGHT, silverlight); + @Deprecated + public void setUserCustomVariable(@NotNull String key, @Nullable String value) { + requireNonNull(key, "Key must not be null"); + if (value == null) { + if (visitCustomVariables == null) { + return; + } + visitCustomVariables.remove(key); + } else { + CustomVariable variable = new CustomVariable(key, value); + if (visitCustomVariables == null) { + visitCustomVariables = new CustomVariables(); + } + visitCustomVariables.add(variable); + } } /** - * Check if the visitor has the Windows Media plugin. + * Set a user custom variable at the specified key. * - * @return true if the visitor has the Windows Media plugin + * @param customVariable the CustomVariable to set. A null value will remove the custom variable at the specified + * index + * @param index the index to set the customVariable at. + * @deprecated Use {@link #setVisitCustomVariables(CustomVariables)} instead */ - @Nullable - public Boolean getPluginWindowsMedia() { - return getBooleanParameter(PLUGIN_WINDOWS_MEDIA); - } - - /** - * Set if the visitor has the Windows Media plugin. - * - * @param windowsMedia true if the visitor has the Windows Media plugin - */ - public void setPluginWindowsMedia(Boolean windowsMedia) { - setBooleanParameter(PLUGIN_WINDOWS_MEDIA, windowsMedia); - } - - /** - * Get the random value for this request - * - * @return the random value - */ - @Nullable - public String getRandomValue() { - return castOrNull(RANDOM_VALUE); - } - - /** - * Set a random value that is generated before each request. Using it helps - * avoid the tracking request being cached by the browser or a proxy. - * - * @param randomValue the random value to set. A null value will remove this parameter - */ - public void setRandomValue(String randomValue) { - setParameter(RANDOM_VALUE, randomValue); - } - - /** - * Get the referrer url - * - * @return the referrer url - */ - @Nullable - public URL getReferrerUrl() { - return castToUrlOrNull(REFERRER_URL); - } - - /** - * Get the referrer url - * - * @return the referrer url - */ - @Nullable - public String getReferrerUrlAsString() { - return castOrNull(REFERRER_URL); - } - - /** - * Set the full HTTP Referrer URL. This value is used to determine how someone - * got to your website (ie, through a website, search engine or campaign). - * - * @param referrerUrl the referrer url to set. A null value will remove this parameter - * @deprecated Please use {@link #setReferrerUrl(String)} - */ - @Deprecated - public void setReferrerUrl(@NonNull URL referrerUrl) { - setReferrerUrl(referrerUrl.toString()); - } - - /** - * Set the full HTTP Referrer URL. This value is used to determine how someone - * got to your website (ie, through a website, search engine or campaign). - * - * @param referrerUrl the referrer url to set. A null value will remove this parameter - */ - public void setReferrerUrl(String referrerUrl) { - setParameter(REFERRER_URL, referrerUrl); - } - - /** - * Set the full HTTP Referrer URL. This value is used to determine how someone - * got to your website (ie, through a website, search engine or campaign). - * - * @param referrerUrl the referrer url to set. A null value will remove this parameter - * @deprecated Please use {@link #setReferrerUrl(String)} - */ - @Deprecated - public void setReferrerUrlWithString(String referrerUrl) { - setReferrerUrl(referrerUrl); - } - - /** - * Get the datetime of the request - * - * @return the datetime of the request - */ - @Nullable - public MatomoDate getRequestDatetime() { - return castOrNull(REQUEST_DATETIME); - } - - /** - * Set the datetime of the request (normally the current time is used). - * This can be used to record visits and page views in the past. The datetime - * must be sent in UTC timezone. Note: if you record data in the past, you will - * need to force Matomo to re-process - * reports for the past dates. If you set the Request Datetime to a datetime - * older than four hours then Auth Token must be set. If you set - * Request Datetime with a datetime in the last four hours then you - * don't need to pass Auth Token. - * - * @param datetime the datetime of the request to set. A null value will remove this parameter - */ - public void setRequestDatetime(MatomoDate datetime) { - if (datetime != null && new Date().getTime() - datetime.getTime() > REQUEST_DATETIME_AUTH_LIMIT && getAuthToken() == null) { - throw new IllegalStateException("Because you are trying to set RequestDatetime for a time greater than 4 hours ago, AuthToken must be set first."); - } - setParameter(REQUEST_DATETIME, datetime); - } - - /** - * Get if this request will be tracked. - * - * @return true if request will be tracked - */ - @Nullable - public Boolean getRequired() { - return getBooleanParameter(REQUIRED); - } - - /** - * Set if this request will be tracked by the Matomo server. - * - * @param required true if request will be tracked - */ - public void setRequired(Boolean required) { - setBooleanParameter(REQUIRED, required); - } - - /** - * Get if the response will be an image. - * - * @return true if the response will be an an image - */ - @Nullable - public Boolean getResponseAsImage() { - return getBooleanParameter(RESPONSE_AS_IMAGE); - } - - /** - * Set if the response will be an image. If set to false, Matomo will respond - * with a HTTP 204 response code instead of a GIF image. This improves performance - * and can fix errors if images are not allowed to be obtained directly - * (eg Chrome Apps). Available since Matomo 2.10.0. - * - * @param responseAsImage true if the response will be an image - */ - public void setResponseAsImage(Boolean responseAsImage) { - setBooleanParameter(RESPONSE_AS_IMAGE, responseAsImage); - } - - /** - * Get the search category - * - * @return the search category - */ - @Nullable - public String getSearchCategory() { - return castOrNull(SEARCH_CATEGORY); - } - - /** - * Specify a search category with this parameter. SearchQuery must first be - * set. - * - * @param searchCategory the search category to set. A null value will remove this parameter - */ - public void setSearchCategory(String searchCategory) { - if (searchCategory != null && getSearchQuery() == null) { - throw new IllegalStateException("SearchQuery must be set before SearchCategory can be set."); - } - setParameter(SEARCH_CATEGORY, searchCategory); - } - - /** - * Get the search query. - * - * @return the search query - */ - @Nullable - public String getSearchQuery() { - return castOrNull(SEARCH_QUERY); - } - - /** - * Set the search query. When specified, the request will not be tracked as - * a normal pageview but will instead be tracked as a Site Search request. - * - * @param searchQuery the search query to set. A null value will remove this parameter - */ - public void setSearchQuery(String searchQuery) { - setParameter(SEARCH_QUERY, searchQuery); - } - - /** - * Get the search results count. - * - * @return the search results count - */ - @Nullable - public Long getSearchResultsCount() { - return castOrNull(SEARCH_RESULTS_COUNT); - } - - /** - * We recommend to set the - * search count to the number of search results displayed on the results page. - * When keywords are tracked with {@code Search Results Count=0} they will appear in - * the "No Result Search Keyword" report. SearchQuery must first be set. - * - * @param searchResultsCount the search results count to set. A null value will remove this parameter - */ - public void setSearchResultsCount(Long searchResultsCount) { - if (searchResultsCount != null && getSearchQuery() == null) { - throw new IllegalStateException("SearchQuery must be set before SearchResultsCount can be set."); - } - setParameter(SEARCH_RESULTS_COUNT, searchResultsCount); - } - - /** - * Get the id of the website we're tracking. - * - * @return the id of the website - */ - @Nullable - public Integer getSiteId() { - return castOrNull(SITE_ID); - } - - /** - * Set the ID of the website we're tracking a visit/action for. - * - * @param siteId the id of the website to set. A null value will remove this parameter - */ - public void setSiteId(Integer siteId) { - setParameter(SITE_ID, siteId); - } - - /** - * Set if bot requests should be tracked - * - * @return true if bot requests should be tracked - */ - @Nullable - public Boolean getTrackBotRequests() { - return getBooleanParameter(TRACK_BOT_REQUESTS); - } - - /** - * By default Matomo does not track bots. If you use the Tracking Java API, - * you may be interested in tracking bot requests. To enable Bot Tracking in - * Matomo, set Track Bot Requests to true. - * - * @param trackBotRequests true if bot requests should be tracked - */ - public void setTrackBotRequests(Boolean trackBotRequests) { - setBooleanParameter(TRACK_BOT_REQUESTS, trackBotRequests); - } - - /** - * Get the visit custom variable at the specified key. - * - * @param key the key of the variable to get - * @return the variable at the specified key, null if key is not present - * @deprecated Use the {@link #getVisitCustomVariable(int)} method instead. - */ - @Nullable - @Deprecated - public String getUserCustomVariable(String key) { - return getCustomVariable(VISIT_CUSTOM_VARIABLE, key); - } - - /** - * Get the visit custom variable at the specified index. - * - * @param index the index of the variable to get - * @return the variable at the specified index, null if nothing at this index - */ - @Nullable - public CustomVariable getVisitCustomVariable(int index) { - return getCustomVariable(VISIT_CUSTOM_VARIABLE, index); - } - - /** - * Set a visit custom variable with the specified key and value at the first available index. - * All visit custom variables with this key will be overwritten or deleted - * - * @param key the key of the variable to set - * @param value the value of the variable to set at the specified key. A null value will remove this parameter - * @deprecated Use the {@link #setVisitCustomVariable(CustomVariable, int)} method instead. - */ - @Deprecated - public void setUserCustomVariable(String key, String value) { - if (value == null) { - removeCustomVariable(VISIT_CUSTOM_VARIABLE, key); - } else { - setCustomVariable(VISIT_CUSTOM_VARIABLE, new CustomVariable(key, value), null); - } - } - - /** - * Set a user custom variable at the specified key. - * - * @param customVariable the CustomVariable to set. A null value will remove the custom variable at the specified index - * @param index the index to set the customVariable at. - */ - public void setVisitCustomVariable(CustomVariable customVariable, int index) { - setCustomVariable(VISIT_CUSTOM_VARIABLE, customVariable, index); - } - - /** - * Get the user id for this request. - * - * @return the user id - */ - @Nullable - public String getUserId() { - return castOrNull(USER_ID); - } - - /** - * Set the user id for this request. - * User id is any non empty unique string identifying the user (such as an email - * address or a username). To access this value, users must be logged-in in your - * system so you can fetch this user id from your system, and pass it to Matomo. - * The user id appears in the visitor log, the Visitor profile, and you can - * Segment - * reports for one or several user ids. When specified, the user id will be - * "enforced". This means that if there is no recent visit with this user id, - * a new one will be created. If a visit is found in the last 30 minutes with - * your specified user id, then the new action will be recorded to this existing visit. - * - * @param userId the user id to set. A null value will remove this parameter - */ - public void setUserId(String userId) { - setNonEmptyStringParameter(USER_ID, userId); - } - - /** - * Get the visitor's city. - * - * @return the visitor's city - */ - @Nullable - public String getVisitorCity() { - return castOrNull(VISITOR_CITY); - } - - /** - * Set an override value for the city. The name of the city the visitor is - * located in, eg, Tokyo. AuthToken must first be set. - * - * @param city the visitor's city to set. A null value will remove this parameter - */ - public void setVisitorCity(String city) { - if (city != null) { - verifyAuthTokenSet(); - } - setParameter(VISITOR_CITY, city); - } - - /** - * Get the visitor's country. - * - * @return the visitor's country - */ - @Nullable - public MatomoLocale getVisitorCountry() { - return castOrNull(VISITOR_COUNTRY); - } - - /** - * Set an override value for the country. AuthToken must first be set. - * - * @param country the visitor's country to set. A null value will remove this parameter - */ - public void setVisitorCountry(MatomoLocale country) { - if (country != null) { - verifyAuthTokenSet(); - } - setParameter(VISITOR_COUNTRY, country); - } - - /** - * Get the visitor's custom id. - * - * @return the visitor's custom id - */ - @Nullable - public String getVisitorCustomId() { - return castOrNull(VISITOR_CUSTOM_ID); - } - - /** - * Set a custom visitor ID for this request. You must set this value to exactly - * a {@value #ID_LENGTH} character hexadecimal string (containing only characters 01234567890abcdefABCDEF). - * We recommended to set the UserId rather than the VisitorCustomId. - * - * @param visitorCustomId the visitor's custom id to set. A null value will remove this parameter - */ - public void setVisitorCustomId(String visitorCustomId) { - if (visitorCustomId != null) { - if (visitorCustomId.length() != ID_LENGTH) { - throw new IllegalArgumentException(visitorCustomId + " is not " + ID_LENGTH + " characters long."); - } - // Verify visitorID is a 16 character hexadecimal string - if (!VISITOR_ID_PATTERN.matcher(visitorCustomId).matches()) { - throw new IllegalArgumentException(visitorCustomId + " is not a hexadecimal string."); - } - } - setParameter(VISITOR_CUSTOM_ID, visitorCustomId); - } - - /** - * Get the timestamp of the visitor's first visit. - * - * @return the timestamp of the visitor's first visit - */ - @Nullable - public Long getVisitorFirstVisitTimestamp() { - return castOrNull(VISITOR_FIRST_VISIT_TIMESTAMP); - } - - /** - * Set the UNIX timestamp of this visitor's first visit. This could be set - * to the date where the user first started using your software/app, or when - * he/she created an account. This parameter is used to populate the - * Goals > Days to Conversion report. - * - * @param timestamp the timestamp of the visitor's first visit to set. A null value will remove this parameter - */ - public void setVisitorFirstVisitTimestamp(Long timestamp) { - setParameter(VISITOR_FIRST_VISIT_TIMESTAMP, timestamp); - } - - /** - * Get the visitor's id. - * - * @return the visitor's id - */ - @Nullable - public String getVisitorId() { - return castOrNull(VISITOR_ID); - } - - /** - * Set the unique visitor ID, must be a {@value #ID_LENGTH} characters hexadecimal string. - * Every unique visitor must be assigned a different ID and this ID must not - * change after it is assigned. If this value is not set Matomo will still - * track visits, but the unique visitors metric might be less accurate. - * - * @param visitorId the visitor id to set. A null value will remove this parameter - */ - public void setVisitorId(String visitorId) { - if (visitorId != null) { - if (visitorId.length() != ID_LENGTH) { - throw new IllegalArgumentException(visitorId + " is not " + ID_LENGTH + " characters long."); - } - // Verify visitorID is a 16 character hexadecimal string - if (!VISITOR_ID_PATTERN.matcher(visitorId).matches()) { - throw new IllegalArgumentException(visitorId + " is not a hexadecimal string."); - } - } - setParameter(VISITOR_ID, visitorId); - } - - /** - * Get the visitor's ip. - * - * @return the visitor's ip - */ - @Nullable - public String getVisitorIp() { - return castOrNull(VISITOR_IP); - } - - /** - * Set the override value for the visitor IP (both IPv4 and IPv6 notations - * supported). AuthToken must first be set. - * - * @param visitorIp the visitor's ip to set. A null value will remove this parameter - */ - public void setVisitorIp(String visitorIp) { - if (visitorIp != null) { - verifyAuthTokenSet(); - } - setParameter(VISITOR_IP, visitorIp); - } - - /** - * Get the visitor's latitude. - * - * @return the visitor's latitude - */ - @Nullable - public Double getVisitorLatitude() { - return castOrNull(VISITOR_LATITUDE); - } - - /** - * Set an override value for the visitor's latitude, eg 22.456. AuthToken - * must first be set. - * - * @param latitude the visitor's latitude to set. A null value will remove this parameter - */ - public void setVisitorLatitude(Double latitude) { - if (latitude != null) { - verifyAuthTokenSet(); - } - setParameter(VISITOR_LATITUDE, latitude); - } - - /** - * Get the visitor's longitude. - * - * @return the visitor's longitude - */ - @Nullable - public Double getVisitorLongitude() { - return castOrNull(VISITOR_LONGITUDE); - } - - /** - * Set an override value for the visitor's longitude, eg 22.456. AuthToken - * must first be set. - * - * @param longitude the visitor's longitude to set. A null value will remove this parameter - */ - public void setVisitorLongitude(Double longitude) { - if (longitude != null) { - verifyAuthTokenSet(); - } - setParameter(VISITOR_LONGITUDE, longitude); - } - - /** - * Get the timestamp of the visitor's previous visit. - * - * @return the timestamp of the visitor's previous visit - */ - @Nullable - public Long getVisitorPreviousVisitTimestamp() { - return castOrNull(VISITOR_PREVIOUS_VISIT_TIMESTAMP); - } - - /** - * Set the UNIX timestamp of this visitor's previous visit. This parameter - * is used to populate the report - * Visitors > Engagement > Visits by days since last visit. - * - * @param timestamp the timestamp of the visitor's previous visit to set. A null value will remove this parameter - */ - public void setVisitorPreviousVisitTimestamp(Long timestamp) { - setParameter(VISITOR_PREVIOUS_VISIT_TIMESTAMP, timestamp); - } - - /** - * Get the visitor's region. - * - * @return the visitor's region - */ - @Nullable - public String getVisitorRegion() { - return castOrNull(VISITOR_REGION); - } - - /** - * Set an override value for the region. Should be set to the two letter - * region code as defined by - * MaxMind's GeoIP databases. - * See here - * for a list of them for every country (the region codes are located in the - * second column, to the left of the region name and to the right of the country - * code). - * - * @param region the visitor's region to set. A null value will remove this parameter - */ - public void setVisitorRegion(String region) { - if (region != null) { - verifyAuthTokenSet(); - } - setParameter(VISITOR_REGION, region); - } - - /** - * Get the count of visits for this visitor. - * - * @return the count of visits for this visitor - */ - @Nullable - public Integer getVisitorVisitCount() { - return castOrNull(VISITOR_VISIT_COUNT); - } - - /** - * Set the current count of visits for this visitor. To set this value correctly, - * it would be required to store the value for each visitor in your application - * (using sessions or persisting in a database). Then you would manually increment - * the counts by one on each new visit or "session", depending on how you choose - * to define a visit. This value is used to populate the report - * Visitors > Engagement > Visits by visit number. - * - * @param visitorVisitCount the count of visits for this visitor to set. A null value will remove this parameter - */ - public void setVisitorVisitCount(Integer visitorVisitCount) { - setParameter(VISITOR_VISIT_COUNT, visitorVisitCount); - } - - public Map> getParameters() { - return parameters.asMap(); - } - - /** - * Get the query string represented by this object. - * - * @return the query string represented by this object - * @deprecated Use {@link URIBuilder} in conjunction with {@link #getParameters()} and {@link QueryParameters#fromMap(Map)} ()} instead - */ - @Nonnull @Deprecated - public String getQueryString() { - return parameters.entries().stream().map(parameter -> parameter.getKey() + '=' + parameter.getValue().toString()).collect(Collectors.joining("&")); - } - - /** - * Get the url encoded query string represented by this object. - * - * @return the url encoded query string represented by this object - * @deprecated Use {@link URIBuilder} in conjunction with {@link #getParameters()} and {@link QueryParameters#fromMap(Map)} ()} instead - */ - @Nonnull - @Deprecated - public String getUrlEncodedQueryString() { - String queryString = new URIBuilder().setParameters(QueryParameters.fromMap(getParameters())).toString(); - if (queryString.isEmpty()) { - return ""; - } - return queryString.substring(1); - } - - /** - * Get a random hexadecimal string of a specified length. - * - * @param length length of the string to produce - * @return a random string consisting only of hexadecimal characters - */ - @Nonnull - public static String getRandomHexString(int length) { - byte[] bytes = new byte[length / 2]; - new SecureRandom().nextBytes(bytes); - return BaseEncoding.base16().lowerCase().encode(bytes); - } - - /** - * Set a stored parameter. - * - * @param key the parameter's key - * @param value the parameter's value. Removes the parameter if null - */ - public void setParameter(@NonNull String key, @Nullable Object value) { - parameters.removeAll(key); - if (value != null) { - addParameter(key, value); - } - } - - /** - * Add more values to the given parameter - * - * @param key the parameter's key. Must not be null - * @param value the parameter's value. Must not be null - */ - public void addParameter(@NonNull String key, @NonNull Object value) { - parameters.put(key, value); - } - - - /** - * Get a stored parameter that and cast it if present - * - * @param key the parameter's key. Must not be null - * @return the stored parameter's value casted to the requested type or null if no value is present - */ - @Nullable - private T castOrNull(@NonNull String key) { - Collection values = parameters.get(key); - if (values.isEmpty()) { - return null; - } - return (T) values.iterator().next(); - } - - /** - * Set a stored parameter and verify it is a non-empty string. - * - * @param key the parameter's key - * @param value the parameter's value. Cannot be the empty. Removes the parameter if null - * string - */ - private void setNonEmptyStringParameter(@NonNull String key, String value) { - if (value != null && value.trim().isEmpty()) { - throw new IllegalArgumentException("Value cannot be empty."); - } - setParameter(key, value); - } - - /** - * Get a stored parameter that is a boolean. - * - * @param key the parameter's key - * @return the stored parameter's value - */ - @Nullable - private Boolean getBooleanParameter(@NonNull String key) { - MatomoBoolean matomoBoolean = castOrNull(key); - if (matomoBoolean == null) { - return null; - } - return matomoBoolean.isValue(); - } - - /** - * Set a stored parameter that is a boolean. - * - * @param key the parameter's key - * @param value the parameter's value. Removes the parameter if null - */ - private void setBooleanParameter(@NonNull String key, @Nullable Boolean value) { - if (value == null) { - setParameter(key, null); - } else { - setParameter(key, new MatomoBoolean(value)); - } - } - - /** - * Get a value that is stored in a json object at the specified parameter. - * - * @param parameter the parameter to retrieve the json object from - * @param index the index of the value. - * @return the value at the specified index - */ - @Nullable - private CustomVariable getCustomVariable(@NonNull String parameter, int index) { - CustomVariables customVariables = castOrNull(parameter); - if (customVariables == null) { - return null; - } - return customVariables.get(index); - } - - @Nullable - private String getCustomVariable(@NonNull String parameter, @NonNull String key) { - CustomVariables customVariables = castOrNull(parameter); - if (customVariables == null) { - return null; - } - return customVariables.get(key); - } - - /** - * Store a value in a json object at the specified parameter. - * - * @param parameter the parameter to store the json object at - * @param customVariable the value. Removes the parameter if null - * @param index the custom variable index - */ - private void setCustomVariable(@NonNull String parameter, @Nullable CustomVariable customVariable, Integer index) { - - if (customVariable == null && index == null) { - throw new IllegalArgumentException("Either custom variable or index must be set"); - } - CustomVariables customVariables = castOrNull(parameter); - if (customVariables == null) { - customVariables = new CustomVariables(); - setParameter(parameter, customVariables); - } - if (customVariable == null) { - customVariables.remove(index); - if (customVariables.isEmpty()) { - setParameter(parameter, null); - } - } else if (index == null) { - customVariables.add(customVariable); - } else { - customVariables.add(customVariable, index); - } - } - - private void removeCustomVariable(@NonNull String parameter, @NonNull String key) { - CustomVariables customVariables = castOrNull(parameter); - if (customVariables != null) { - customVariables.remove(key); - if (customVariables.isEmpty()) { - setParameter(parameter, null); + public void setVisitCustomVariable(@Nullable CustomVariable customVariable, int index) { + if (visitCustomVariables == null) { + if (customVariable == null) { + return; } + visitCustomVariables = new CustomVariables(); } + setCustomVariable(visitCustomVariables, customVariable, index); } } diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java b/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java deleted file mode 100644 index 747791a4..00000000 --- a/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java +++ /dev/null @@ -1,643 +0,0 @@ -package org.matomo.java.tracking; - -import java.nio.charset.Charset; -import java.util.List; -import java.util.Map; - -public class MatomoRequestBuilder { - - private int siteId; - - private String actionUrl; - - private String actionName; - private Long actionTime; - private String apiVersion; - private String authToken; - private String campaignKeyword; - private String campaignName; - private Charset characterSet; - private String contentInteraction; - private String contentName; - private String contentPiece; - private String contentTarget; - private Integer currentHour; - private Integer currentMinute; - private Integer currentSecond; - - private Boolean customAction; - private String deviceResolution; - private String downloadUrl; - private Double ecommerceDiscount; - private String ecommerceId; - private Long ecommerceLastOrderTimestamp; - private Double ecommerceRevenue; - private Double ecommerceShippingCost; - private Double ecommerceSubtotal; - private Double ecommerceTax; - private String eventAction; - private String eventCategory; - private String eventName; - private Number eventValue; - private Integer goalId; - private Double goalRevenue; - private String headerAcceptLanguage; - private String headerUserAgent; - private Boolean newVisit; - private String outlinkUrl; - private Boolean pluginDirector; - private Boolean pluginFlash; - private Boolean pluginGears; - private Boolean pluginJava; - private Boolean pluginPDF; - private Boolean pluginQuicktime; - private Boolean pluginRealPlayer; - private Boolean pluginSilverlight; - private Boolean pluginWindowsMedia; - private String randomValue; - private String referrerUrl; - private MatomoDate requestDatetime; - private Boolean required; - private Boolean responseAsImage; - private String searchCategory; - private String searchQuery; - private Long searchResultsCount; - private Boolean trackBotRequests; - private String userId; - private String visitorCity; - private MatomoLocale visitorCountry; - private String visitorCustomId; - private Long visitorFirstVisitTimestamp; - private String visitorId; - private String visitorIp; - private Double visitorLatitude; - private Double visitorLongitude; - private Long visitorPreviousVisitTimestamp; - private String visitorRegion; - private Integer visitorVisitCount; - - private List visitCustomVariables; - - private List pageCustomVariables; - private Map customTrackingParameters; - - public MatomoRequestBuilder siteId(int siteId) { - this.siteId = siteId; - return this; - } - - public MatomoRequestBuilder actionUrl(String actionUrl) { - this.actionUrl = actionUrl; - return this; - } - - public MatomoRequestBuilder actionName(String actionName) { - this.actionName = actionName; - return this; - } - - public MatomoRequestBuilder actionTime(Long actionTime) { - this.actionTime = actionTime; - return this; - } - - public MatomoRequestBuilder apiVersion(String apiVersion) { - this.apiVersion = apiVersion; - return this; - } - - public MatomoRequestBuilder authToken(String authToken) { - this.authToken = authToken; - return this; - } - - public MatomoRequestBuilder campaignKeyword(String campaignKeyword) { - this.campaignKeyword = campaignKeyword; - return this; - } - - public MatomoRequestBuilder campaignName(String campaignName) { - this.campaignName = campaignName; - return this; - } - - public MatomoRequestBuilder characterSet(Charset characterSet) { - this.characterSet = characterSet; - return this; - } - - public MatomoRequestBuilder contentInteraction(String contentInteraction) { - this.contentInteraction = contentInteraction; - return this; - } - - public MatomoRequestBuilder contentName(String contentName) { - this.contentName = contentName; - return this; - } - - public MatomoRequestBuilder contentPiece(String contentPiece) { - this.contentPiece = contentPiece; - return this; - } - - public MatomoRequestBuilder contentTarget(String contentTarget) { - this.contentTarget = contentTarget; - return this; - } - - public MatomoRequestBuilder currentHour(Integer currentHour) { - this.currentHour = currentHour; - return this; - } - - public MatomoRequestBuilder currentMinute(Integer currentMinute) { - this.currentMinute = currentMinute; - return this; - } - - public MatomoRequestBuilder currentSecond(Integer currentSecond) { - this.currentSecond = currentSecond; - return this; - } - - public MatomoRequestBuilder customAction(Boolean customAction) { - this.customAction = customAction; - return this; - } - - public MatomoRequestBuilder deviceResolution(String deviceResolution) { - this.deviceResolution = deviceResolution; - return this; - } - - public MatomoRequestBuilder downloadUrl(String downloadUrl) { - this.downloadUrl = downloadUrl; - return this; - } - - public MatomoRequestBuilder ecommerceDiscount(Double ecommerceDiscount) { - this.ecommerceDiscount = ecommerceDiscount; - return this; - } - - public MatomoRequestBuilder ecommerceId(String ecommerceId) { - this.ecommerceId = ecommerceId; - return this; - } - - public MatomoRequestBuilder ecommerceLastOrderTimestamp(Long ecommerceLastOrderTimestamp) { - this.ecommerceLastOrderTimestamp = ecommerceLastOrderTimestamp; - return this; - } - - public MatomoRequestBuilder ecommerceRevenue(Double ecommerceRevenue) { - this.ecommerceRevenue = ecommerceRevenue; - return this; - } - - public MatomoRequestBuilder ecommerceShippingCost(Double ecommerceShippingCost) { - this.ecommerceShippingCost = ecommerceShippingCost; - return this; - } - - public MatomoRequestBuilder ecommerceSubtotal(Double ecommerceSubtotal) { - this.ecommerceSubtotal = ecommerceSubtotal; - return this; - } - - public MatomoRequestBuilder ecommerceTax(Double ecommerceTax) { - this.ecommerceTax = ecommerceTax; - return this; - } - - public MatomoRequestBuilder eventAction(String eventAction) { - this.eventAction = eventAction; - return this; - } - - public MatomoRequestBuilder eventCategory(String eventCategory) { - this.eventCategory = eventCategory; - return this; - } - - public MatomoRequestBuilder eventName(String eventName) { - this.eventName = eventName; - return this; - } - - public MatomoRequestBuilder eventValue(Number eventValue) { - this.eventValue = eventValue; - return this; - } - - public MatomoRequestBuilder goalId(Integer goalId) { - this.goalId = goalId; - return this; - } - - public MatomoRequestBuilder goalRevenue(Double goalRevenue) { - this.goalRevenue = goalRevenue; - return this; - } - - public MatomoRequestBuilder headerAcceptLanguage(String headerAcceptLanguage) { - this.headerAcceptLanguage = headerAcceptLanguage; - return this; - } - - public MatomoRequestBuilder headerUserAgent(String headerUserAgent) { - this.headerUserAgent = headerUserAgent; - return this; - } - - public MatomoRequestBuilder newVisit(Boolean newVisit) { - this.newVisit = newVisit; - return this; - } - - public MatomoRequestBuilder outlinkUrl(String outlinkUrl) { - this.outlinkUrl = outlinkUrl; - return this; - } - - public MatomoRequestBuilder pluginDirector(Boolean pluginDirector) { - this.pluginDirector = pluginDirector; - return this; - } - - public MatomoRequestBuilder pluginFlash(Boolean pluginFlash) { - this.pluginFlash = pluginFlash; - return this; - } - - public MatomoRequestBuilder pluginGears(Boolean pluginGears) { - this.pluginGears = pluginGears; - return this; - } - - public MatomoRequestBuilder pluginJava(Boolean pluginJava) { - this.pluginJava = pluginJava; - return this; - } - - public MatomoRequestBuilder pluginPDF(Boolean pluginPDF) { - this.pluginPDF = pluginPDF; - return this; - } - - public MatomoRequestBuilder pluginQuicktime(Boolean pluginQuicktime) { - this.pluginQuicktime = pluginQuicktime; - return this; - } - - public MatomoRequestBuilder pluginRealPlayer(Boolean pluginRealPlayer) { - this.pluginRealPlayer = pluginRealPlayer; - return this; - } - - public MatomoRequestBuilder pluginSilverlight(Boolean pluginSilverlight) { - this.pluginSilverlight = pluginSilverlight; - return this; - } - - public MatomoRequestBuilder pluginWindowsMedia(Boolean pluginWindowsMedia) { - this.pluginWindowsMedia = pluginWindowsMedia; - return this; - } - - public MatomoRequestBuilder randomValue(String randomValue) { - this.randomValue = randomValue; - return this; - } - - public MatomoRequestBuilder referrerUrl(String referrerUrl) { - this.referrerUrl = referrerUrl; - return this; - } - - public MatomoRequestBuilder requestDatetime(MatomoDate requestDatetime) { - this.requestDatetime = requestDatetime; - return this; - } - - public MatomoRequestBuilder required(Boolean required) { - this.required = required; - return this; - } - - public MatomoRequestBuilder responseAsImage(Boolean responseAsImage) { - this.responseAsImage = responseAsImage; - return this; - } - - public MatomoRequestBuilder searchCategory(String searchCategory) { - this.searchCategory = searchCategory; - return this; - } - - public MatomoRequestBuilder searchQuery(String searchQuery) { - this.searchQuery = searchQuery; - return this; - } - - public MatomoRequestBuilder searchResultsCount(Long searchResultsCount) { - this.searchResultsCount = searchResultsCount; - return this; - } - - public MatomoRequestBuilder trackBotRequests(Boolean trackBotRequests) { - this.trackBotRequests = trackBotRequests; - return this; - } - - public MatomoRequestBuilder userId(String userId) { - this.userId = userId; - return this; - } - - public MatomoRequestBuilder visitorCity(String visitorCity) { - this.visitorCity = visitorCity; - return this; - } - - public MatomoRequestBuilder visitorCountry(MatomoLocale visitorCountry) { - this.visitorCountry = visitorCountry; - return this; - } - - public MatomoRequestBuilder visitorCustomId(String visitorCustomId) { - this.visitorCustomId = visitorCustomId; - return this; - } - - public MatomoRequestBuilder visitorFirstVisitTimestamp(Long visitorFirstVisitTimestamp) { - this.visitorFirstVisitTimestamp = visitorFirstVisitTimestamp; - return this; - } - - public MatomoRequestBuilder visitorId(String visitorId) { - this.visitorId = visitorId; - return this; - } - - public MatomoRequestBuilder visitorIp(String visitorIp) { - this.visitorIp = visitorIp; - return this; - } - - public MatomoRequestBuilder visitorLatitude(Double visitorLatitude) { - this.visitorLatitude = visitorLatitude; - return this; - } - - public MatomoRequestBuilder visitorLongitude(Double visitorLongitude) { - this.visitorLongitude = visitorLongitude; - return this; - } - - public MatomoRequestBuilder visitorPreviousVisitTimestamp(Long visitorPreviousVisitTimestamp) { - this.visitorPreviousVisitTimestamp = visitorPreviousVisitTimestamp; - return this; - } - - public MatomoRequestBuilder visitorRegion(String visitorRegion) { - this.visitorRegion = visitorRegion; - return this; - } - - public MatomoRequestBuilder visitorVisitCount(Integer visitorVisitCount) { - this.visitorVisitCount = visitorVisitCount; - return this; - } - - public MatomoRequestBuilder visitCustomVariables(List visitCustomVariables) { - this.visitCustomVariables = visitCustomVariables; - return this; - } - - public MatomoRequestBuilder pageCustomVariables(List pageCustomVariables) { - this.pageCustomVariables = pageCustomVariables; - return this; - } - - public MatomoRequestBuilder customTrackingParameters(Map customTrackingParameters) { - this.customTrackingParameters = customTrackingParameters; - return this; - } - - public MatomoRequest build() { - MatomoRequest matomoRequest = new MatomoRequest(siteId, actionUrl); - if (actionName != null) { - matomoRequest.setActionName(actionName); - } - if (actionTime != null) { - matomoRequest.setActionTime(actionTime); - } - if (apiVersion != null) { - matomoRequest.setApiVersion(apiVersion); - } - if (authToken != null) { - matomoRequest.setAuthToken(authToken); - } - if (campaignKeyword != null) { - matomoRequest.setCampaignKeyword(campaignKeyword); - } - if (campaignName != null) { - matomoRequest.setCampaignName(campaignName); - } - if (characterSet != null) { - matomoRequest.setCharacterSet(characterSet); - } - if (contentInteraction != null) { - matomoRequest.setContentInteraction(contentInteraction); - } - if (contentName != null) { - matomoRequest.setContentName(contentName); - } - if (contentPiece != null) { - matomoRequest.setContentPiece(contentPiece); - } - if (contentTarget != null) { - matomoRequest.setContentTarget(contentTarget); - } - if (currentHour != null) { - matomoRequest.setCurrentHour(currentHour); - } - if (currentMinute != null) { - matomoRequest.setCurrentMinute(currentMinute); - } - if (currentSecond != null) { - matomoRequest.setCurrentSecond(currentSecond); - } - if (customAction != null) { - matomoRequest.setCustomAction(customAction); - } - if (customTrackingParameters != null) { - for (Map.Entry customTrackingParameter : customTrackingParameters.entrySet()) { - matomoRequest.addCustomTrackingParameter(customTrackingParameter.getKey(), customTrackingParameter.getValue()); - } - } - if (deviceResolution != null) { - matomoRequest.setDeviceResolution(deviceResolution); - } - if (downloadUrl != null) { - matomoRequest.setDownloadUrl(downloadUrl); - } - if (ecommerceDiscount != null) { - matomoRequest.setEcommerceDiscount(ecommerceDiscount); - } - if (ecommerceId != null) { - matomoRequest.setEcommerceId(ecommerceId); - } - if (ecommerceLastOrderTimestamp != null) { - matomoRequest.setEcommerceLastOrderTimestamp(ecommerceLastOrderTimestamp); - } - if (ecommerceRevenue != null) { - matomoRequest.setEcommerceRevenue(ecommerceRevenue); - } - if (ecommerceShippingCost != null) { - matomoRequest.setEcommerceShippingCost(ecommerceShippingCost); - } - if (ecommerceSubtotal != null) { - matomoRequest.setEcommerceSubtotal(ecommerceSubtotal); - } - if (ecommerceTax != null) { - matomoRequest.setEcommerceTax(ecommerceTax); - } - if (eventAction != null) { - matomoRequest.setEventAction(eventAction); - } - if (eventCategory != null) { - matomoRequest.setEventCategory(eventCategory); - } - if (eventName != null) { - matomoRequest.setEventName(eventName); - } - if (eventValue != null) { - matomoRequest.setEventValue(eventValue); - } - if (goalId != null) { - matomoRequest.setGoalId(goalId); - } - if (goalRevenue != null) { - matomoRequest.setGoalRevenue(goalRevenue); - } - if (headerAcceptLanguage != null) { - matomoRequest.setHeaderAcceptLanguage(headerAcceptLanguage); - } - if (headerUserAgent != null) { - matomoRequest.setHeaderUserAgent(headerUserAgent); - } - if (newVisit != null) { - matomoRequest.setNewVisit(newVisit); - } - if (outlinkUrl != null) { - matomoRequest.setOutlinkUrl(outlinkUrl); - } - if (pageCustomVariables != null) { - for (int i = 0; i < pageCustomVariables.size(); i++) { - CustomVariable pageCustomVariable = pageCustomVariables.get(i); - matomoRequest.setPageCustomVariable(pageCustomVariable, i + 1); - } - } - if (pluginDirector != null) { - matomoRequest.setPluginDirector(pluginDirector); - } - if (pluginFlash != null) { - matomoRequest.setPluginFlash(pluginFlash); - } - if (pluginGears != null) { - matomoRequest.setPluginGears(pluginGears); - } - if (pluginJava != null) { - matomoRequest.setPluginJava(pluginJava); - } - if (pluginPDF != null) { - matomoRequest.setPluginPDF(pluginPDF); - } - if (pluginQuicktime != null) { - matomoRequest.setPluginQuicktime(pluginQuicktime); - } - if (pluginRealPlayer != null) { - matomoRequest.setPluginRealPlayer(pluginRealPlayer); - } - if (pluginSilverlight != null) { - matomoRequest.setPluginSilverlight(pluginSilverlight); - } - if (pluginWindowsMedia != null) { - matomoRequest.setPluginWindowsMedia(pluginWindowsMedia); - } - if (randomValue != null) { - matomoRequest.setRandomValue(randomValue); - } - if (referrerUrl != null) { - matomoRequest.setReferrerUrl(referrerUrl); - } - if (requestDatetime != null) { - matomoRequest.setRequestDatetime(requestDatetime); - } - if (required != null) { - matomoRequest.setRequired(required); - } - if (responseAsImage != null) { - matomoRequest.setResponseAsImage(responseAsImage); - } - if (searchQuery != null) { - matomoRequest.setSearchQuery(searchQuery); - } - if (searchCategory != null) { - matomoRequest.setSearchCategory(searchCategory); - } - if (searchResultsCount != null) { - matomoRequest.setSearchResultsCount(searchResultsCount); - } - if (trackBotRequests != null) { - matomoRequest.setTrackBotRequests(trackBotRequests); - } - if (visitCustomVariables != null) { - for (int i = 0; i < visitCustomVariables.size(); i++) { - CustomVariable visitCustomVariable = visitCustomVariables.get(i); - matomoRequest.setVisitCustomVariable(visitCustomVariable, i + 1); - } - } - if (userId != null) { - matomoRequest.setUserId(userId); - } - if (visitorCity != null) { - matomoRequest.setVisitorCity(visitorCity); - } - if (visitorCountry != null) { - matomoRequest.setVisitorCountry(visitorCountry); - } - if (visitorCustomId != null) { - matomoRequest.setVisitorCustomId(visitorCustomId); - } - if (visitorFirstVisitTimestamp != null) { - matomoRequest.setVisitorFirstVisitTimestamp(visitorFirstVisitTimestamp); - } - if (visitorId != null) { - matomoRequest.setVisitorId(visitorId); - } - if (visitorIp != null) { - matomoRequest.setVisitorIp(visitorIp); - } - if (visitorLatitude != null) { - matomoRequest.setVisitorLatitude(visitorLatitude); - } - if (visitorLongitude != null) { - matomoRequest.setVisitorLongitude(visitorLongitude); - } - if (visitorPreviousVisitTimestamp != null) { - matomoRequest.setVisitorPreviousVisitTimestamp(visitorPreviousVisitTimestamp); - } - if (visitorRegion != null) { - matomoRequest.setVisitorRegion(visitorRegion); - } - if (visitorVisitCount != null) { - matomoRequest.setVisitorVisitCount(visitorVisitCount); - } - return matomoRequest; - } - -} diff --git a/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/src/main/java/org/matomo/java/tracking/MatomoTracker.java index d5995beb..8819db90 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoTracker.java +++ b/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -4,30 +4,21 @@ * @link https://github.com/matomo/matomo-java-tracker * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ + package org.matomo.java.tracking; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.client.utils.URIBuilder; -import org.apache.http.concurrent.FutureCallback; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.io.IOException; import java.net.URI; -import java.net.URISyntaxException; -import java.util.concurrent.Future; +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.function.Consumer; + +import static java.util.Objects.requireNonNull; /** * A class that sends {@link MatomoRequest}s to a specified Matomo server. @@ -37,15 +28,9 @@ @Slf4j public class MatomoTracker { - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private final TrackerConfiguration trackerConfiguration; - private static final String AUTH_TOKEN = "token_auth"; - private static final String REQUESTS = "requests"; - private static final int DEFAULT_TIMEOUT = 5000; - private final URI hostUrl; - private final int timeout; - private final String proxyHost; - private final int proxyPort; + private final Sender sender; /** * Creates a tracker that will send {@link MatomoRequest}s to the specified @@ -53,9 +38,11 @@ public class MatomoTracker { * * @param hostUrl url endpoint to send requests to. Usually in the format * http://your-matomo-domain.tld/matomo.php. Must not be null + * @deprecated Please use {@link MatomoTracker#MatomoTracker(TrackerConfiguration)} */ - public MatomoTracker(@NonNull final String hostUrl) { - this(hostUrl, DEFAULT_TIMEOUT); + @Deprecated + public MatomoTracker(@NotNull String hostUrl) { + this(requireNonNull(hostUrl, "Host URL must not be null"), 0); } /** @@ -64,67 +51,83 @@ public MatomoTracker(@NonNull final String hostUrl) { * * @param hostUrl url endpoint to send requests to. Usually in the format * http://your-matomo-domain.tld/matomo.php. - * @param timeout the timeout of the sent request in milliseconds + * @param timeout the timeout of the sent request in milliseconds or -1 if not set + * @deprecated Please use {@link MatomoTracker#MatomoTracker(TrackerConfiguration)} */ - public MatomoTracker(@NonNull final String hostUrl, final int timeout) { - this(hostUrl, null, 0, timeout); - } - - public MatomoTracker(@NonNull final String hostUrl, @Nullable final String proxyHost, final int proxyPort, final int timeout) { - this.hostUrl = URI.create(hostUrl); - this.proxyHost = proxyHost; - this.proxyPort = proxyPort; - this.timeout = timeout; + @Deprecated + public MatomoTracker(@NotNull String hostUrl, int timeout) { + this(requireNonNull(hostUrl, "Host URL must not be null"), null, 0, timeout); } /** * Creates a tracker that will send {@link MatomoRequest}s to the specified - * Tracking HTTP API endpoint via the provided proxy + * Tracking HTTP API endpoint. * * @param hostUrl url endpoint to send requests to. Usually in the format * http://your-matomo-domain.tld/matomo.php. - * @param proxyHost url endpoint for the proxy - * @param proxyPort proxy server port number + * @param proxyHost The hostname or IP address of an optional HTTP proxy, null allowed + * @param proxyPort The port of an HTTP proxy or -1 if not set + * @param timeout the timeout of the request in milliseconds or -1 if not set + * @deprecated Please use {@link MatomoTracker#MatomoTracker(TrackerConfiguration)} */ - public MatomoTracker(@NonNull final String hostUrl, @Nullable final String proxyHost, final int proxyPort) { - this(hostUrl, proxyHost, proxyPort, DEFAULT_TIMEOUT); + @Deprecated + public MatomoTracker(@NotNull String hostUrl, @Nullable String proxyHost, int proxyPort, int timeout) { + this(TrackerConfiguration.builder().enabled(true).apiEndpoint( + URI.create(requireNonNull(hostUrl, "Host URL must not be null"))).proxyHost(proxyHost).proxyPort(proxyPort) + .connectTimeout(timeout == -1 ? Duration.ofSeconds(5L) : Duration.ofSeconds(timeout)) + .socketTimeout(timeout == -1 ? Duration.ofSeconds(5L) : Duration.ofSeconds(timeout)).build()); } /** - * Sends a tracking request to Matomo + * Creates a new Matomo Tracker instance. * - * @param request request to send. must not be null - * @return the response from this request - * @deprecated use sendRequestAsync instead + * @param trackerConfiguration Configurations parameters (you can use a builder) */ - @Deprecated - public HttpResponse sendRequest(@NonNull final MatomoRequest request) { - final HttpClient client = getHttpClient(); - HttpUriRequest get = createGetRequest(request); - log.debug("Sending request via GET: {}", request); - try { - return client.execute(get); - } catch (IOException e) { - throw new MatomoException("Could not send request to Matomo", e); - } + public MatomoTracker(@NotNull TrackerConfiguration trackerConfiguration) { + requireNonNull(trackerConfiguration, "Tracker configuration must not be null"); + trackerConfiguration.validate(); + this.trackerConfiguration = trackerConfiguration; + ScheduledThreadPoolExecutor threadPoolExecutor = createThreadPoolExecutor(); + sender = new Sender(trackerConfiguration, new QueryCreator(trackerConfiguration), threadPoolExecutor); } - @Nonnull - private HttpUriRequest createGetRequest(@NonNull MatomoRequest request) { - try { - return new HttpGet(new URIBuilder(hostUrl).addParameters(QueryParameters.fromMap(request.getParameters())).build()); - } catch (URISyntaxException e) { - throw new InvalidUrlException(e); - } + @NotNull + private static ScheduledThreadPoolExecutor createThreadPoolExecutor() { + DaemonThreadFactory threadFactory = new DaemonThreadFactory(); + ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(1, threadFactory); + threadPoolExecutor.setRemoveOnCancelPolicy(true); + return threadPoolExecutor; + } + + /** + * Creates a tracker that will send {@link MatomoRequest}s to the specified + * Tracking HTTP API endpoint via the provided proxy. + * + * @param hostUrl url endpoint to send requests to. Usually in the format + * http://your-matomo-domain.tld/matomo.php. + * @param proxyHost url endpoint for the proxy, null allowed + * @param proxyPort proxy server port number or -1 if not set + * @deprecated Please use {@link MatomoTracker#MatomoTracker(TrackerConfiguration)} + */ + @Deprecated + public MatomoTracker(@NotNull String hostUrl, @Nullable String proxyHost, int proxyPort) { + this(hostUrl, proxyHost, proxyPort, -1); } /** - * Get a HTTP client. With proxy if a proxy is provided in the constructor. + * Sends a tracking request to Matomo. * - * @return a HTTP client + * @param request request to send. must not be null + * @deprecated use sendRequestAsync instead */ - protected HttpClient getHttpClient() { - return HttpClientFactory.getInstanceFor(proxyHost, proxyPort, timeout); + @Deprecated + public void sendRequest(@NonNull MatomoRequest request) { + if (trackerConfiguration.isEnabled()) { + log.debug("Sending request via GET: {}", request); + sender.sendSingle(request); + } else { + log.warn("Not sending request, because tracker is disabled"); + } } /** @@ -133,7 +136,7 @@ protected HttpClient getHttpClient() { * @param request request to send * @return future with response from this request */ - public Future sendRequestAsync(@NonNull final MatomoRequest request) { + public CompletableFuture sendRequestAsync(@NotNull MatomoRequest request) { return sendRequestAsync(request, null); } @@ -141,24 +144,27 @@ public Future sendRequestAsync(@NonNull final MatomoRequest reques * Send a request. * * @param request request to send - * @param callback callback that gets executed when response arrives + * @param callback callback that gets executed when response arrives, null allowed * @return future with response from this request */ - public Future sendRequestAsync(@NonNull final MatomoRequest request, @Nullable FutureCallback callback) { - final CloseableHttpAsyncClient client = getHttpAsyncClient(); - client.start(); - HttpUriRequest get = createGetRequest(request); - log.debug("Sending async request via GET: {}", request); - return client.execute(get, callback); + public CompletableFuture sendRequestAsync(@NotNull MatomoRequest request, @Nullable Consumer callback) { + if (trackerConfiguration.isEnabled()) { + validate(request); + log.debug("Sending async request via GET: {}", request); + CompletableFuture future = sender.sendSingleAsync(request); + if (callback != null) { + return future.thenAccept(callback); + } + return future; + } + log.warn("Not sending request, because tracker is disabled"); + return CompletableFuture.completedFuture(null); } - /** - * Get an async HTTP client. With proxy if a proxy is provided in the constructor. - * - * @return an async HTTP client - */ - protected CloseableHttpAsyncClient getHttpAsyncClient() { - return HttpClientFactory.getAsyncInstanceFor(proxyHost, proxyPort, timeout); + private void validate(@NotNull MatomoRequest request) { + if (trackerConfiguration.getDefaultSiteId() == null && request.getSiteId() == null) { + throw new IllegalArgumentException("No default site ID and no request site ID is given"); + } } /** @@ -166,12 +172,11 @@ protected CloseableHttpAsyncClient getHttpAsyncClient() { * several individual requests. * * @param requests the requests to send - * @return the response from these requests * @deprecated use sendBulkRequestAsync instead */ @Deprecated - public HttpResponse sendBulkRequest(@NonNull final Iterable requests) { - return sendBulkRequest(requests, null); + public void sendBulkRequest(@NonNull Iterable requests) { + sendBulkRequest(requests, null); } /** @@ -180,37 +185,20 @@ public HttpResponse sendBulkRequest(@NonNull final Iterable requests, @Nullable final String authToken) { - if (authToken != null && authToken.length() != MatomoRequest.AUTH_TOKEN_LENGTH) { - throw new IllegalArgumentException(authToken + " is not " + MatomoRequest.AUTH_TOKEN_LENGTH + " characters long."); - } - HttpPost post = buildPost(requests, authToken); - final HttpClient client = getHttpClient(); - log.debug("Sending requests via POST: {}", requests); - try { - return client.execute(post); - } catch (IOException e) { - throw new MatomoException("Could not send bulk request", e); - } - } - - private HttpPost buildPost(@NonNull Iterable requests, @Nullable String authToken) { - ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); - ArrayNode requestsNode = objectNode.putArray(REQUESTS); - for (final MatomoRequest request : requests) { - requestsNode.add(new URIBuilder().addParameters(QueryParameters.fromMap(request.getParameters())).toString()); - } - if (authToken != null) { - objectNode.put(AUTH_TOKEN, authToken); + public void sendBulkRequest(@NonNull Iterable requests, @Nullable String authToken) { + if (trackerConfiguration.isEnabled()) { + for (MatomoRequest request : requests) { + validate(request); + } + log.debug("Sending requests via POST: {}", requests); + sender.sendBulk(requests, authToken); + } else { + log.warn("Not sending request, because tracker is disabled"); } - HttpPost post = new HttpPost(hostUrl); - post.setEntity(new StringEntity(objectNode.toString(), ContentType.APPLICATION_JSON)); - return post; } /** @@ -220,7 +208,7 @@ private HttpPost buildPost(@NonNull Iterable requests, * @param requests the requests to send * @return future with response from these requests */ - public Future sendBulkRequestAsync(@NonNull final Iterable requests) { + public CompletableFuture sendBulkRequestAsync(@NotNull Iterable requests) { return sendBulkRequestAsync(requests, null, null); } @@ -230,19 +218,26 @@ public Future sendBulkRequestAsync(@NonNull final Iterable sendBulkRequestAsync(@NonNull final Iterable requests, @Nullable final String authToken, @Nullable FutureCallback callback) { - if (authToken != null && authToken.length() != MatomoRequest.AUTH_TOKEN_LENGTH) { - throw new IllegalArgumentException(authToken + " is not " + MatomoRequest.AUTH_TOKEN_LENGTH + " characters long."); + public CompletableFuture sendBulkRequestAsync( + @NotNull Iterable requests, @Nullable String authToken, @Nullable Consumer callback + ) { + if (trackerConfiguration.isEnabled()) { + for (MatomoRequest request : requests) { + validate(request); + } + log.debug("Sending async requests via POST: {}", requests); + CompletableFuture future = sender.sendBulkAsync(requests, authToken); + if (callback != null) { + return future.thenAccept(callback); + } + return future; } - HttpPost post = buildPost(requests, authToken); - final CloseableHttpAsyncClient client = getHttpAsyncClient(); - client.start(); - log.debug("Sending async requests via POST: {}", requests); - return client.execute(post, callback); + log.warn("Tracker is disabled"); + return CompletableFuture.completedFuture(null); } /** @@ -250,10 +245,12 @@ public Future sendBulkRequestAsync(@NonNull final Iterable sendBulkRequestAsync(@NonNull final Iterable requests, @Nullable FutureCallback callback) { + public CompletableFuture sendBulkRequestAsync( + @NotNull Iterable requests, @Nullable Consumer callback + ) { return sendBulkRequestAsync(requests, null, callback); } @@ -263,10 +260,12 @@ public Future sendBulkRequestAsync(@NonNull final Iterable sendBulkRequestAsync(@NonNull final Iterable requests, @Nullable final String authToken) { + public CompletableFuture sendBulkRequestAsync( + @NotNull Iterable requests, @Nullable String authToken + ) { return sendBulkRequestAsync(requests, authToken, null); } } diff --git a/src/main/java/org/matomo/java/tracking/ProxyAuthenticator.java b/src/main/java/org/matomo/java/tracking/ProxyAuthenticator.java new file mode 100644 index 00000000..1b6641a6 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/ProxyAuthenticator.java @@ -0,0 +1,35 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking; + +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.Nullable; + +import java.net.Authenticator; +import java.net.PasswordAuthentication; + +@RequiredArgsConstructor +class ProxyAuthenticator extends Authenticator { + + @NonNull + private final String user; + + @NonNull + private final String password; + + @Nullable + @Override + protected PasswordAuthentication getPasswordAuthentication() { + if (getRequestorType() == RequestorType.PROXY) { + return new PasswordAuthentication(user, password.toCharArray()); + } + return null; + } + +} diff --git a/src/main/java/org/matomo/java/tracking/QueryCreator.java b/src/main/java/org/matomo/java/tracking/QueryCreator.java new file mode 100644 index 00000000..17a91a5d --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/QueryCreator.java @@ -0,0 +1,144 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking; + +import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.net.URLEncoder; +import java.nio.charset.Charset; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map.Entry; +import java.util.regex.Pattern; + +@RequiredArgsConstructor +class QueryCreator { + + private static final TrackingParameterMethod[] TRACKING_PARAMETER_METHODS = initializeTrackingParameterMethods(); + + private final TrackerConfiguration trackerConfiguration; + + private static TrackingParameterMethod[] initializeTrackingParameterMethods() { + Field[] declaredFields = MatomoRequest.class.getDeclaredFields(); + List methods = new ArrayList<>(declaredFields.length); + for (Field field : declaredFields) { + if (field.isAnnotationPresent(TrackingParameter.class)) { + addMethods(methods, field, field.getAnnotation(TrackingParameter.class)); + } + } + return methods.toArray(new TrackingParameterMethod[0]); + } + + private static void addMethods( + Collection methods, Member member, TrackingParameter trackingParameter + ) { + try { + for (PropertyDescriptor pd : Introspector.getBeanInfo(MatomoRequest.class).getPropertyDescriptors()) { + if (member.getName().equals(pd.getName())) { + String regex = trackingParameter.regex(); + methods.add(TrackingParameterMethod.builder() + .parameterName(trackingParameter.name()) + .method(pd.getReadMethod()) + .pattern(regex == null || regex.isEmpty() || regex.trim().isEmpty() ? null : + Pattern.compile(trackingParameter.regex())) + .build()); + } + } + } catch (IntrospectionException e) { + throw new MatomoException("Could not initialize read methods", e); + } + } + + String createQuery(@NotNull MatomoRequest request, @Nullable String authToken) { + StringBuilder query = new StringBuilder(100); + if (request.getSiteId() == null) { + appendAmpersand(query); + query.append("idsite=").append(trackerConfiguration.getDefaultSiteId()); + } + if (authToken != null) { + if (authToken.length() != 32) { + throw new IllegalArgumentException("Auth token must be exactly 32 characters long"); + } + query.append("token_auth=").append(authToken); + } + for (TrackingParameterMethod method : TRACKING_PARAMETER_METHODS) { + appendParameter(method, request, query); + } + if (request.getCustomTrackingParameters() != null) { + for (Entry> entry : request.getCustomTrackingParameters().entrySet()) { + for (Object value : entry.getValue()) { + if (value != null && !value.toString().trim().isEmpty()) { + appendAmpersand(query); + query.append(encode(entry.getKey())).append('=').append(encode(value.toString())); + } + } + } + } + if (request.getDimensions() != null) { + int i = 0; + for (Object dimension : request.getDimensions()) { + appendAmpersand(query); + query.append("dimension").append(i + 1).append('=').append(dimension); + i++; + } + } + return query.toString(); + } + + private static void appendAmpersand(StringBuilder query) { + if (query.length() != 0) { + query.append('&'); + } + } + + private static void appendParameter(TrackingParameterMethod method, MatomoRequest request, StringBuilder query) { + try { + Object parameterValue = method.getMethod().invoke(request); + if (parameterValue != null) { + method.validateParameterValue(parameterValue); + appendAmpersand(query); + query.append(method.getParameterName()).append('='); + if (parameterValue instanceof Boolean) { + query.append((boolean) parameterValue ? '1' : '0'); + } else if (parameterValue instanceof Charset) { + query.append(((Charset) parameterValue).name()); + } else if (parameterValue instanceof Instant) { + query.append(((Instant) parameterValue).getEpochSecond()); + } else { + String parameterValueString = parameterValue.toString(); + if (!parameterValueString.trim().isEmpty()) { + query.append(encode(parameterValueString)); + } + } + } + } catch (Exception e) { + throw new MatomoException("Could not append parameter", e); + } + } + + @NotNull + private static String encode(@NotNull String parameterValue) { + try { + return URLEncoder.encode(parameterValue, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new MatomoException("Could not encode parameter", e); + } + } + + +} diff --git a/src/main/java/org/matomo/java/tracking/QueryParameters.java b/src/main/java/org/matomo/java/tracking/QueryParameters.java deleted file mode 100644 index 2e1f94c3..00000000 --- a/src/main/java/org/matomo/java/tracking/QueryParameters.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.matomo.java.tracking; - -import lombok.NonNull; -import org.apache.http.NameValuePair; -import org.apache.http.message.BasicNameValuePair; - -import javax.annotation.Nonnull; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Comparator; -import java.util.List; -import java.util.Map; - -public final class QueryParameters { - - private QueryParameters() { - // utility - } - - @Nonnull - public static List fromMap(@NonNull Map> map) { - List queryParameters = new ArrayList<>(); - for (Map.Entry> entries : map.entrySet()) { - for (Object value : entries.getValue()) { - queryParameters.add(new BasicNameValuePair(entries.getKey(), value.toString())); - } - } - queryParameters.sort(Comparator.comparing(NameValuePair::getName)); - return queryParameters; - } - -} diff --git a/src/main/java/org/matomo/java/tracking/RequestValidator.java b/src/main/java/org/matomo/java/tracking/RequestValidator.java new file mode 100644 index 00000000..d28d6385 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/RequestValidator.java @@ -0,0 +1,55 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +import static java.util.Objects.requireNonNull; + +final class RequestValidator { + + private RequestValidator() { + // utility + } + + static void validate(@NotNull MatomoRequest request, @Nullable CharSequence authToken) { + requireNonNull(request, "Request must not be null"); + if (request.getSiteId() != null && request.getSiteId() < 0) { + throw new IllegalArgumentException("Site ID must not be negative"); + } + if (request.getGoalId() == null && (request.getEcommerceId() != null || request.getEcommerceRevenue() != null + || request.getEcommerceDiscount() != null || request.getEcommerceItems() != null + || request.getEcommerceLastOrderTimestamp() != null || request.getEcommerceShippingCost() != null + || request.getEcommerceSubtotal() != null || request.getEcommerceTax() != null)) { + throw new MatomoException("Goal ID must be set if ecommerce parameters are used"); + } + if (request.getSearchResultsCount() != null && request.getSearchQuery() == null) { + throw new MatomoException("Search query must be set if search results count is set"); + } + if (authToken == null) { + if (request.getVisitorLongitude() != null || request.getVisitorLatitude() != null + || request.getVisitorRegion() != null || request.getVisitorCity() != null + || request.getVisitorCountry() != null) { + throw new MatomoException("Auth token must be present if longitude, latitude, region, city or country are set"); + } + if (request.getRequestTimestamp() != null + && request.getRequestTimestamp().isBefore(Instant.now().minus(4, ChronoUnit.HOURS))) { + throw new MatomoException("Auth token must be present if request timestamp is more than four hours ago"); + } + } else { + if (authToken.length() != 32) { + throw new IllegalArgumentException("Auth token must be exactly 32 characters long"); + } + } + } +} + diff --git a/src/main/java/org/matomo/java/tracking/Sender.java b/src/main/java/org/matomo/java/tracking/Sender.java new file mode 100644 index 00000000..ce97d669 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/Sender.java @@ -0,0 +1,219 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.Authenticator; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.MalformedURLException; +import java.net.ProtocolException; +import java.net.Proxy; +import java.net.URI; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import static java.util.Collections.singleton; +import static java.util.Objects.requireNonNull; + +@Slf4j +@RequiredArgsConstructor +class Sender { + + private final TrackerConfiguration trackerConfiguration; + + private final QueryCreator queryCreator; + + private final Collection queries = new ArrayList<>(16); + + private final Executor executor; + + @NotNull CompletableFuture sendSingleAsync(@NotNull MatomoRequest request) { + return CompletableFuture.supplyAsync(() -> { + sendSingle(request); + return null; + }, executor); + } + + void sendSingle(@NotNull MatomoRequest request) { + String authToken = AuthToken.determineAuthToken(null, singleton(request), trackerConfiguration); + RequestValidator.validate(request, authToken); + HttpURLConnection connection; + URI apiEndpoint = trackerConfiguration.getApiEndpoint(); + try { + connection = openConnection( + apiEndpoint.resolve(String.format("%s?%s", apiEndpoint.getPath(), queryCreator.createQuery(request, authToken))) + .toURL()); + } catch (MalformedURLException e) { + throw new InvalidUrlException(e); + } + configureAgentsAndTimeouts(connection); + log.debug("Sending single request using URI {} asynchronously", apiEndpoint); + try { + connection.connect(); + checkResponse(connection); + } catch (IOException e) { + throw new MatomoException("Could not send request via GET", e); + } finally { + connection.disconnect(); + } + } + + private HttpURLConnection openConnection(URL url) { + try { + if (isEmpty(trackerConfiguration.getProxyHost()) || trackerConfiguration.getProxyPort() <= 0) { + log.debug("Proxy host or proxy port not configured. Will create connection without proxy"); + return (HttpURLConnection) url.openConnection(); + } + InetSocketAddress proxyAddress = + new InetSocketAddress(trackerConfiguration.getProxyHost(), trackerConfiguration.getProxyPort()); + Proxy proxy = new Proxy(Proxy.Type.HTTP, proxyAddress); + if (!isEmpty(trackerConfiguration.getProxyUserName()) && !isEmpty(trackerConfiguration.getProxyPassword())) { + Authenticator.setDefault( + new ProxyAuthenticator(trackerConfiguration.getProxyUserName(), trackerConfiguration.getProxyPassword())); + } + return (HttpURLConnection) url.openConnection(proxy); + } catch (IOException e) { + throw new MatomoException("Could not open connection", e); + } + } + + private void configureAgentsAndTimeouts(HttpURLConnection connection) { + connection.setUseCaches(false); + connection.setRequestProperty("User-Agent", trackerConfiguration.getUserAgent()); + if (trackerConfiguration.getConnectTimeout() != null) { + connection.setConnectTimeout((int) trackerConfiguration.getConnectTimeout().toMillis()); + } + if (trackerConfiguration.getSocketTimeout() != null) { + connection.setReadTimeout((int) trackerConfiguration.getSocketTimeout().toMillis()); + } + } + + private void checkResponse(HttpURLConnection connection) throws IOException { + if (connection.getResponseCode() > 399) { + if (trackerConfiguration.isLogFailedTracking()) { + log.error("Received error code {}", connection.getResponseCode()); + } + throw new MatomoException("Tracking endpoint responded with code " + connection.getResponseCode()); + } + } + + private static boolean isEmpty(@Nullable String str) { + return str == null || str.isEmpty() || str.trim().isEmpty(); + } + + void sendBulk(@NotNull Iterable requests, @Nullable String overrideAuthToken) { + String authToken = AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); + sendBulk(StreamSupport.stream(requests.spliterator(), false).map(request -> { + RequestValidator.validate(request, authToken); + return queryCreator.createQuery(request, null); + }).collect(Collectors.toList()), authToken); + } + + private void sendBulk(@NotNull Collection queries, @Nullable String authToken) { + requireNonNull(queries, "Queries must not be null"); + HttpURLConnection connection; + try { + connection = openConnection(trackerConfiguration.getApiEndpoint().toURL()); + } catch (MalformedURLException e) { + throw new InvalidUrlException(e); + } + preparePostConnection(connection); + configureAgentsAndTimeouts(connection); + log.debug("Sending bulk request using URI {} asynchronously", trackerConfiguration.getApiEndpoint()); + OutputStream outputStream = null; + try { + connection.connect(); + outputStream = connection.getOutputStream(); + outputStream.write(createPayload(queries, authToken)); + outputStream.flush(); + checkResponse(connection); + } catch (IOException e) { + throw new MatomoException("Could not send requests via POST", e); + } finally { + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException e) { + // ignore + } + } + connection.disconnect(); + } + } + + private static void preparePostConnection(HttpURLConnection connection) { + try { + connection.setRequestMethod("POST"); + } catch (ProtocolException e) { + throw new MatomoException("Could not set request method", e); + } + connection.setDoOutput(true); + connection.setRequestProperty("Accept", "*/*"); + connection.setRequestProperty("Content-Type", "application/json"); + + } + + private static byte[] createPayload(@NotNull Collection queries, @Nullable String authToken) { + requireNonNull(queries, "Queries must not be null"); + StringBuilder payload = new StringBuilder("{\"requests\":["); + Iterator iterator = queries.iterator(); + while (iterator.hasNext()) { + String query = iterator.next(); + payload.append("\"?").append(query).append('"'); + if (iterator.hasNext()) { + payload.append(','); + } + } + payload.append(']'); + if (authToken != null) { + payload.append(",\"token_auth\":\"").append(authToken).append('"'); + } + return payload.append('}').toString().getBytes(StandardCharsets.UTF_8); + } + + @NotNull CompletableFuture sendBulkAsync( + @NotNull Iterable requests, + @Nullable String overrideAuthToken + ) { + String authToken = AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); + synchronized (queries) { + for (MatomoRequest request : requests) { + RequestValidator.validate(request, authToken); + String query = queryCreator.createQuery(request, null); + queries.add(query); + } + } + return CompletableFuture.supplyAsync(() -> sendBulkAsync(authToken), executor); + } + + @Nullable + private Void sendBulkAsync(@Nullable String authToken) { + synchronized (queries) { + if (!queries.isEmpty()) { + sendBulk(queries, authToken); + queries.clear(); + } + return null; + } + } + +} diff --git a/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java b/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java new file mode 100644 index 00000000..083156d1 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java @@ -0,0 +1,118 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking; + +import java.net.URI; +import java.time.Duration; +import java.util.regex.Pattern; +import lombok.Builder; +import lombok.NonNull; +import lombok.Value; +import org.jetbrains.annotations.Nullable; + +/** + * Defines configuration settings for the Matomo tracking. + */ +@Builder +@Value +public class TrackerConfiguration { + + private static final Pattern AUTH_TOKEN_PATTERN = Pattern.compile("[a-z0-9]+"); + /** + * The Matomo Tracking HTTP API endpoint, e.g. https://your-matomo-domain.example/matomo.php + */ + @NonNull URI apiEndpoint; + + /** + * The default ID of the website that will be used if not specified explicitly. + */ + Integer defaultSiteId; + + /** + * The authorization token (parameter token_auth) to use if not specified explicitly. + */ + String defaultAuthToken; + + /** + * Allows to stop the tracker to send requests to the Matomo endpoint. + */ + @Builder.Default + boolean enabled = true; + + /** + * The timeout until a connection is established. + * + *

A timeout value of zero is interpreted as an infinite timeout. + * A `null` value is interpreted as undefined (system default if applicable).

+ * + *

Default: 10 seconds

+ */ + @Builder.Default + Duration connectTimeout = Duration.ofSeconds(5L); + + /** + * The socket timeout ({@code SO_TIMEOUT}), which is the timeout for waiting for data or, put differently, a maximum + * period inactivity between two consecutive data packets. + * + *

A timeout value of zero is interpreted as an infinite timeout. + * A `null value is interpreted as undefined (system default if applicable).

+ * + *

Default: 30 seconds

+ */ + @Builder.Default + Duration socketTimeout = Duration.ofSeconds(5L); + + /** + * The hostname or IP address of an optional HTTP proxy. {@code proxyPort} must be configured as well + */ + @Nullable + String proxyHost; + + /** + * The port of an HTTP proxy. {@code proxyHost} must be configured as well. + */ + int proxyPort; + + /** + * If the HTTP proxy requires a username for basic authentication, it can be configured here. Proxy host, port and + * password must also be set. + */ + @Nullable + String proxyUserName; + + /** + * The corresponding password for the basic auth proxy user. The proxy host, port and username must be set as well. + */ + @Nullable + String proxyPassword; + + /** + * A custom user agent to be set. Defaults to "MatomoJavaClient" + */ + @Builder.Default + @NonNull String userAgent = "MatomoJavaClient"; + + /** + * Logs if the Matomo Tracking API endpoint responds with an erroneous HTTP code. + */ + boolean logFailedTracking; + + /** + * Validates the auth token. The auth token must be exactly 32 characters long. + */ + public void validate() { + if (defaultAuthToken != null) { + if (defaultAuthToken.trim().length() != 32) { + throw new IllegalArgumentException("Auth token must be exactly 32 characters long"); + } + if (!AUTH_TOKEN_PATTERN.matcher(defaultAuthToken).matches()) { + throw new IllegalArgumentException("Auth token must contain only lowercase letters and numbers"); + } + } + } +} diff --git a/src/main/java/org/matomo/java/tracking/TrackingParameter.java b/src/main/java/org/matomo/java/tracking/TrackingParameter.java new file mode 100644 index 00000000..5e32184a --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/TrackingParameter.java @@ -0,0 +1,23 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@interface TrackingParameter { + + String name(); + + String regex() default ""; + +} diff --git a/src/main/java/org/matomo/java/tracking/TrackingParameterMethod.java b/src/main/java/org/matomo/java/tracking/TrackingParameterMethod.java new file mode 100644 index 00000000..01da635e --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/TrackingParameterMethod.java @@ -0,0 +1,37 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking; + +import lombok.Builder; +import lombok.Value; + +import java.lang.reflect.Method; +import java.util.regex.Pattern; + +@Builder +@Value +class TrackingParameterMethod { + + String parameterName; + + Method method; + + Pattern pattern; + + void validateParameterValue(Object parameterValue) { + if (pattern != null && parameterValue instanceof CharSequence && !pattern.matcher((CharSequence) parameterValue) + .matches()) { + throw new IllegalArgumentException(String.format( + "Invalid value for %s. Must match regex %s", + parameterName, + pattern + )); + } + } + +} diff --git a/src/main/java/org/matomo/java/tracking/package-info.java b/src/main/java/org/matomo/java/tracking/package-info.java new file mode 100644 index 00000000..ae38e3ef --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/package-info.java @@ -0,0 +1,14 @@ +/** + * This package contains classes that allow you to specify a {@link org.matomo.java.tracking.MatomoTracker} + * with the corresponding {@link org.matomo.java.tracking.TrackerConfiguration}. You can then send a + * {@link org.matomo.java.tracking.MatomoRequest} as a single HTTP GET request or multiple requests as a bulk HTTP POST + * request synchronously or asynchronously. If an exception occurs, {@link org.matomo.java.tracking.MatomoException} + * will be thrown. + * + *

For more information about the Matomo Tracking HTTP API, see the Matomo Tracking HTTP API. + * + */ + +package org.matomo.java.tracking; + + diff --git a/src/main/java/org/matomo/java/tracking/parameters/AcceptLanguage.java b/src/main/java/org/matomo/java/tracking/parameters/AcceptLanguage.java new file mode 100644 index 00000000..71a74020 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/parameters/AcceptLanguage.java @@ -0,0 +1,68 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking.parameters; + +import java.util.List; +import java.util.Locale.LanguageRange; +import java.util.Objects; +import java.util.stream.Collectors; +import lombok.Builder; +import lombok.Singular; +import lombok.Value; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Describes the content for the Accept-Language header field that can be overridden by a custom parameter. The format + * is specified in the corresponding RFC 4647 Matching of Language Tags + * + *

Example: "en-US,en;q=0.8,de;q=0.6" + */ +@Builder +@Value +public class AcceptLanguage { + + @Singular + List languageRanges; + + /** + * Creates the Accept-Language definition for a given header. + * + *

Please see {@link LanguageRange#parse(String)} for more information. Example: "en-US,en;q=0.8,de;q=0.6" + * + * @param header A header that can be null + * @return The parsed header (probably reformatted). null if the header is null. + * @see LanguageRange#parse(String) + */ + @Nullable + public static AcceptLanguage fromHeader(@Nullable String header) { + if (header == null || header.trim().isEmpty()) { + return null; + } + return new AcceptLanguage(LanguageRange.parse(header)); + } + + /** + * Returns the Accept Language header value. + * + * @return The header value, e.g. "en-US,en;q=0.8,de;q=0.6" + */ + @NotNull + public String toString() { + return languageRanges.stream() + .filter(Objects::nonNull) + .map(AcceptLanguage::format) + .collect(Collectors.joining(",")); + } + + private static String format(@NotNull LanguageRange languageRange) { + return languageRange.getWeight() == LanguageRange.MAX_WEIGHT ? languageRange.getRange() : + String.format("%s;q=%s", languageRange.getRange(), languageRange.getWeight()); + } + +} diff --git a/src/main/java/org/matomo/java/tracking/parameters/Country.java b/src/main/java/org/matomo/java/tracking/parameters/Country.java new file mode 100644 index 00000000..959f38af --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/parameters/Country.java @@ -0,0 +1,112 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking.parameters; + +import java.util.List; +import java.util.Locale; +import java.util.Locale.LanguageRange; +import lombok.AccessLevel; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A two-letter country code representing a country. + * + *

See ISO 3166-1 alpha-2 for a list of valid codes. + */ +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public class Country { + + @NonNull + private String code; + + /** + * Only for internal use to grant downwards compatibility to {@link org.matomo.java.tracking.MatomoLocale}. + * + * @param locale A locale that must contain a country code + */ + @Deprecated + protected Country(@NotNull Locale locale) { + setLocale(locale); + } + + /** + * Creates a country from a given code. + * + * @param code Must consist of two lower letters or simply null. Case is ignored + * @return The country or null if code was null + */ + @Nullable + public static Country fromCode(@Nullable String code) { + if (code == null || code.isEmpty() || code.trim().isEmpty()) { + return null; + } + if (code.length() == 2) { + return new Country(code.toLowerCase(Locale.ROOT)); + } + throw new IllegalArgumentException("Invalid country code"); + } + + /** + * Extracts the country from the given accept language header. + * + * @param ranges A language range list. See {@link LanguageRange#parse(String)} + * @return The country or null if ranges was null + */ + @Nullable + public static Country fromLanguageRanges(@Nullable String ranges) { + if (ranges == null || ranges.isEmpty() || ranges.trim().isEmpty()) { + return null; + } + List languageRanges = LanguageRange.parse(ranges); + for (LanguageRange languageRange : languageRanges) { + String range = languageRange.getRange(); + String[] split = range.split("-"); + if (split.length == 2 && split[1].length() == 2) { + return new Country(split[1].toLowerCase(Locale.ROOT)); + } + } + throw new IllegalArgumentException("Invalid country code"); + } + + /** + * Returns the locale for this country. + * + * @return The locale for this country + * @see Locale#forLanguageTag(String) + * @deprecated Since you instantiate this class, you can determine the language on your own + * using {@link Locale#forLanguageTag(String)} + */ + @Deprecated + public Locale getLocale() { + return Locale.forLanguageTag(code); + } + + /** + * Sets the locale for this country. + * + * @param locale A locale that must contain a country code + * @see Locale#getCountry() + * @deprecated Since you instantiate this class, you can determine the language on your own + * using {@link Locale#getCountry()} + */ + public final void setLocale(Locale locale) { + if (locale == null || locale.getCountry() == null || locale.getCountry().isEmpty()) { + throw new IllegalArgumentException("Invalid locale"); + } + code = locale.getCountry().toLowerCase(Locale.ENGLISH); + } + + @Override + public String toString() { + return code; + } + +} diff --git a/src/main/java/org/matomo/java/tracking/parameters/CustomVariable.java b/src/main/java/org/matomo/java/tracking/parameters/CustomVariable.java new file mode 100644 index 00000000..570c3579 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/parameters/CustomVariable.java @@ -0,0 +1,57 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking.parameters; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NonNull; +import lombok.Setter; +import lombok.ToString; + +/** + * A key-value pair that represents custom information. See + * How do I use Custom Variables? + * + *

If you are not already using Custom Variables to measure your custom data, Matomo recommends to use the + * Custom Dimensions feature instead. + * There are many advantages of Custom Dimensions over Custom + * variables. Custom variables will be deprecated in the future. + * + * @deprecated Should not be used according to the Matomo FAQ: How do I use Custom Variables? + */ +@Getter +@Setter +@AllArgsConstructor +@ToString +@EqualsAndHashCode(exclude = "index") +@Deprecated +public class CustomVariable { + + private int index; + + @NonNull + private String key; + + @NonNull + private String value; + + /** + * Instantiates a new custom variable. + * + * @param key the key of the custom variable (required) + * @param value the value of the custom variable (required) + */ + public CustomVariable(@NonNull String key, @NonNull String value) { + this.key = key; + this.value = value; + } +} + + + diff --git a/src/main/java/org/matomo/java/tracking/parameters/CustomVariables.java b/src/main/java/org/matomo/java/tracking/parameters/CustomVariables.java new file mode 100644 index 00000000..7cbd7cce --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/parameters/CustomVariables.java @@ -0,0 +1,151 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking.parameters; + +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import lombok.EqualsAndHashCode; +import lombok.NonNull; +import org.jetbrains.annotations.Nullable; + +/** + * A bunch of key-value pairs that represent custom information. See How do I use Custom Variables? + * + * @deprecated Should not be used according to the Matomo FAQ: How do I use Custom Variables? + */ +@EqualsAndHashCode +@Deprecated +public class CustomVariables { + + private final Map variables = new LinkedHashMap<>(); + + /** + * Adds a custom variable to the list with the next available index. + * + * @param variable The custom variable to add + * @return This object for method chaining + */ + public CustomVariables add(@NonNull CustomVariable variable) { + if (variable.getKey().isEmpty()) { + throw new IllegalArgumentException("Custom variable key must not be null or empty"); + } + if (variable.getValue().isEmpty()) { + throw new IllegalArgumentException("Custom variable value must not be null or empty"); + } + boolean found = false; + for (Entry entry : variables.entrySet()) { + CustomVariable customVariable = entry.getValue(); + if (customVariable.getKey().equals(variable.getKey())) { + variables.put(entry.getKey(), variable); + found = true; + } + } + if (!found) { + int i = 1; + while (variables.putIfAbsent(i, variable) != null) { + i++; + } + } + return this; + } + + /** + * Adds a custom variable to the list with the given index. + * + * @param cv The custom variable to add + * @param index The index to add the custom variable at + * @return This object for method chaining + */ + public CustomVariables add(@NonNull CustomVariable cv, int index) { + validateIndex(index); + variables.put(index, cv); + return this; + } + + private static void validateIndex(int index) { + if (index <= 0) { + throw new IllegalArgumentException("Index must be greater than 0"); + } + } + + /** + * Returns the custom variable at the given index. + * + * @param index The index of the custom variable + * @return The custom variable at the given index + */ + @Nullable + public CustomVariable get(int index) { + validateIndex(index); + return variables.get(index); + } + + /** + * Returns the value of the custom variable with the given key. If there are multiple custom variables with the same + * key, the first one is returned. If there is no custom variable with the given key, null is returned. + * + * @param key The key of the custom variable. Must not be null. + * @return The value of the custom variable with the given key. null if there is no variable with the given key. + */ + @Nullable + public String get(@NonNull String key) { + if (key.isEmpty()) { + throw new IllegalArgumentException("key must not be null or empty"); + } + return variables.values().stream().filter(variable -> variable.getKey().equals(key)).findFirst() + .map(CustomVariable::getValue).orElse(null); + } + + /** + * Removes the custom variable at the given index. If there is no custom variable at the given index, nothing happens. + * + * @param index The index of the custom variable to remove. Must be greater than 0. + */ + public void remove(int index) { + validateIndex(index); + variables.remove(index); + } + + /** + * Removes the custom variable with the given key. If there is no custom variable with the given key, nothing happens. + * + * @param key The key of the custom variable to remove. Must not be null. + */ + public void remove(@NonNull String key) { + variables.entrySet().removeIf(entry -> entry.getValue().getKey().equals(key)); + } + + boolean isEmpty() { + return variables.isEmpty(); + } + + /** + * Creates a JSON representation of the custom variables. The format is as follows: + * {@code {"1":["key1","value1"],"2":["key2","value2"]}} + * + * @return A JSON representation of the custom variables + */ + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder("{"); + Iterator> iterator = variables.entrySet().iterator(); + while (iterator.hasNext()) { + Entry entry = iterator.next(); + stringBuilder.append('"').append(entry.getKey()).append("\":[\"").append(entry.getValue().getKey()) + .append("\",\"").append(entry.getValue().getValue()).append("\"]"); + if (iterator.hasNext()) { + stringBuilder.append(','); + } + } + stringBuilder.append('}'); + return stringBuilder.toString(); + } + +} diff --git a/src/main/java/org/matomo/java/tracking/parameters/DeviceResolution.java b/src/main/java/org/matomo/java/tracking/parameters/DeviceResolution.java new file mode 100644 index 00000000..d1643938 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/parameters/DeviceResolution.java @@ -0,0 +1,51 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking.parameters; + +import lombok.Builder; +import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.Nullable; + +/** + * The resolution (width and height) of the user's output device (monitor / phone). + */ +@Builder +@RequiredArgsConstructor +public class DeviceResolution { + + private final int width; + + private final int height; + + /** + * Creates a device resolution from a string representation. + * + *

The string must be in the format "widthxheight", e.g. "1920x1080". + * + * @param deviceResolution The string representation of the device resolution, e.g. "1920x1080" + * @return The device resolution representation + */ + @Nullable + public static DeviceResolution fromString(@Nullable String deviceResolution) { + if (deviceResolution == null) { + return null; + } + String[] dimensions = deviceResolution.split("x"); + if (dimensions.length != 2) { + throw new IllegalArgumentException("Wrong dimension size"); + } + return builder().width(Integer.parseInt(dimensions[0])).height(Integer.parseInt(dimensions[1])) + .build(); + } + + @Override + public String toString() { + return String.format("%dx%d", width, height); + } + +} diff --git a/src/main/java/org/matomo/java/tracking/parameters/EcommerceItem.java b/src/main/java/org/matomo/java/tracking/parameters/EcommerceItem.java new file mode 100644 index 00000000..7f5ba051 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/parameters/EcommerceItem.java @@ -0,0 +1,41 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking.parameters; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +/** + * Represents an item in an ecommerce order. + */ +@Builder +@AllArgsConstructor +@Getter +@Setter +public class EcommerceItem { + + private String sku; + + @Builder.Default + private String name = ""; + + @Builder.Default + private String category = ""; + + @Builder.Default + private Double price = 0.0; + + @Builder.Default + private Integer quantity = 0; + + public String toString() { + return String.format("[\"%s\",\"%s\",\"%s\",%s,%d]", sku, name, category, price, quantity); + } +} diff --git a/src/main/java/org/matomo/java/tracking/parameters/EcommerceItems.java b/src/main/java/org/matomo/java/tracking/parameters/EcommerceItems.java new file mode 100644 index 00000000..d9b85754 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/parameters/EcommerceItems.java @@ -0,0 +1,42 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking.parameters; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.Singular; +import lombok.experimental.Delegate; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Multiple things that you can buy online. + */ +@Builder +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode +@Getter +@Setter +public class EcommerceItems { + + @Delegate + @Singular + private List items = new ArrayList<>(); + + public String toString() { + return items.stream().map(String::valueOf).collect(Collectors.joining(",", "[", "]")); + } + +} diff --git a/src/main/java/org/matomo/java/tracking/parameters/Hex.java b/src/main/java/org/matomo/java/tracking/parameters/Hex.java new file mode 100644 index 00000000..3387afe5 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/parameters/Hex.java @@ -0,0 +1,24 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking.parameters; + +final class Hex { + + private Hex() { + // utility class + } + + static String fromBytes(byte[] bytes) { + StringBuilder result = new StringBuilder(bytes.length * 2); + for (byte b : bytes) { + result.append(String.format("%02x", b)); + } + return result.toString(); + } + +} diff --git a/src/main/java/org/matomo/java/tracking/parameters/RandomValue.java b/src/main/java/org/matomo/java/tracking/parameters/RandomValue.java new file mode 100644 index 00000000..2b000b6e --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/parameters/RandomValue.java @@ -0,0 +1,55 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking.parameters; + +import java.security.SecureRandom; +import java.util.Random; + +/** + * A random value to avoid the tracking request being cached by the browser or a proxy. + */ +public class RandomValue { + + private static final Random RANDOM = new SecureRandom(); + + private final byte[] representation = new byte[10]; + + private String override; + + /** + * Static factory to generate a random value. + * + * @return A randomly generated value + */ + public static RandomValue random() { + RandomValue randomValue = new RandomValue(); + RANDOM.nextBytes(randomValue.representation); + return randomValue; + } + + /** + * Static factory to generate a random value from a given string. The string will be used as is and not hashed. + * + * @param override The string to use as random value + * @return A random value from the given string + */ + public static RandomValue fromString(String override) { + RandomValue randomValue = new RandomValue(); + randomValue.override = override; + return randomValue; + } + + @Override + public String toString() { + if (override != null) { + return override; + } + return Hex.fromBytes(representation); + } + +} diff --git a/src/main/java/org/matomo/java/tracking/parameters/UniqueId.java b/src/main/java/org/matomo/java/tracking/parameters/UniqueId.java new file mode 100644 index 00000000..e66f2717 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/parameters/UniqueId.java @@ -0,0 +1,55 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking.parameters; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; + +import java.security.SecureRandom; +import java.util.Random; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * A six character unique ID consisting of the characters [0-9a-Z]. + */ +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public final class UniqueId { + + private static final String CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + private static final Random RANDOM = new SecureRandom(); + + private final long value; + + /** + * Static factory to generate a random unique id. + * + * @return A randomly generated unique id + */ + public static UniqueId random() { + return fromValue(RANDOM.nextLong()); + } + + /** + * Creates a unique id from a number. + * + * @param value A number to create this unique id from + * @return The unique id for the given value + */ + public static UniqueId fromValue(long value) { + return new UniqueId(value); + } + + @Override + public String toString() { + return IntStream.range(0, 6).map(i -> (int) (value >> i * 8)).mapToObj( + codePoint -> String.valueOf(CHARS.charAt(Math.abs(codePoint % CHARS.length())))).collect(Collectors.joining()); + } + +} diff --git a/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java b/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java new file mode 100644 index 00000000..bb267ee3 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java @@ -0,0 +1,98 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking.parameters; + +import java.security.SecureRandom; +import java.util.Random; +import java.util.regex.Pattern; + +/** + * The unique visitor ID, must be a 16 characters hexadecimal string. Every unique visitor must be assigned a different + * ID and this ID must not change after it is assigned. If this value is not set Matomo will still track visits, but the + * unique visitors metric might be less accurate. + */ +public class VisitorId { + + private static final Random RANDOM = new SecureRandom(); + private static final Pattern HEX_DIGITS = Pattern.compile("[0-9a-fA-F]+"); + + private final byte[] representation = new byte[8]; + + /** + * Static factory to generate a random visitor id. + * + *

Please consider creating a fixed id for each visitor by getting a hash code from e.g. the username and + * using {@link #fromHash(long)} + * + * @return A randomly generated visitor id + */ + public static VisitorId random() { + VisitorId visitorId = new VisitorId(); + RANDOM.nextBytes(visitorId.representation); + return visitorId; + } + + /** + * Creates always the same visitor id for the given input. + * + *

You can use e.g. {@link Object#hashCode()} to generate a hash code for an object, e.g. a username + * string as input. + * + * @param hash A number (e.g. a hash code) to create the visitor id from + * @return Always the same visitor id for the same input + */ + public static VisitorId fromHash(long hash) { + VisitorId visitorId = new VisitorId(); + long remainingHash = hash; + for (int i = visitorId.representation.length - 1; i >= 0; i--) { + visitorId.representation[i] = (byte) (remainingHash & 0xFF); + remainingHash >>= Byte.SIZE; + } + return visitorId; + } + + /** + * Creates a visitor id from a hexadecimal string. + * + *

The input must be a valid hexadecimal string with a maximum length of 16 characters. If the input is shorter + * than 16 characters it will be padded with zeros.

+ * + * @param inputHex A hexadecimal string to create the visitor id from + * @return The visitor id for the given input + */ + public static VisitorId fromHex(String inputHex) { + if (inputHex == null || inputHex.trim().isEmpty()) { + throw new IllegalArgumentException("Hex string must not be null or empty"); + } + if (inputHex.length() > 16) { + throw new IllegalArgumentException("Hex string must not be longer than 16 characters"); + } + if (!HEX_DIGITS.matcher(inputHex).matches()) { + throw new IllegalArgumentException("Input must be a valid hex string"); + } + VisitorId visitorId = new VisitorId(); + for (int charIndex = inputHex.length() - 1, representationIndex = visitorId.representation.length - 1; + charIndex >= 0; charIndex -= 2, representationIndex--) { + String hex = inputHex.substring(Math.max(0, charIndex - 1), charIndex + 1); + try { + visitorId.representation[representationIndex] = (byte) Integer.parseInt(hex, 16); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Input must be a valid hex string", e); + } + } + + + return visitorId; + } + + @Override + public String toString() { + return Hex.fromBytes(representation); + } + +} diff --git a/src/main/java/org/matomo/java/tracking/parameters/package-info.java b/src/main/java/org/matomo/java/tracking/parameters/package-info.java new file mode 100644 index 00000000..f709edce --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/parameters/package-info.java @@ -0,0 +1,9 @@ +/** + * Contains types for Matomo Tracking Parameters according to + * the Matomo Tracking HTTP API. + * + *

The types help you to use the correct format for the tracking parameters. The package was introduced in Matomo + * Java Tracker version 3 to let the tracker be more self-explanatory and better maintainable. + */ + +package org.matomo.java.tracking.parameters; diff --git a/src/main/java/org/piwik/java/tracking/CustomVariable.java b/src/main/java/org/piwik/java/tracking/CustomVariable.java index 48b3813a..20845bed 100644 --- a/src/main/java/org/piwik/java/tracking/CustomVariable.java +++ b/src/main/java/org/piwik/java/tracking/CustomVariable.java @@ -4,19 +4,26 @@ * @link https://github.com/matomo/matomo-java-tracker * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ -package org.piwik.java.tracking; -import org.matomo.java.tracking.MatomoRequest; +package org.piwik.java.tracking; /** + * A user defined custom variable. + * + *

Renamed to {@link org.matomo.java.tracking.parameters.CustomVariable} in 3.0.0. + * * @author brettcsorba - * @deprecated Use {@link org.matomo.java.tracking.CustomVariable} instead. + * @deprecated Use {@link org.matomo.java.tracking.parameters.CustomVariable} instead. */ @Deprecated -public class CustomVariable extends org.matomo.java.tracking.CustomVariable { +public class CustomVariable extends org.matomo.java.tracking.parameters.CustomVariable { /** - * @deprecated Use {@link MatomoRequest} instead. + * Instantiates a new custom variable. + * + * @param key the key of the custom variable (required) + * @param value the value of the custom variable (required) + * @deprecated Use {@link org.matomo.java.tracking.parameters.CustomVariable} instead. */ @Deprecated public CustomVariable(String key, String value) { diff --git a/src/main/java/org/piwik/java/tracking/EcommerceItem.java b/src/main/java/org/piwik/java/tracking/EcommerceItem.java index 65b21446..49c3a83c 100644 --- a/src/main/java/org/piwik/java/tracking/EcommerceItem.java +++ b/src/main/java/org/piwik/java/tracking/EcommerceItem.java @@ -4,22 +4,31 @@ * @link https://github.com/matomo/matomo-java-tracker * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ -package org.piwik.java.tracking; -import org.matomo.java.tracking.MatomoRequest; +package org.piwik.java.tracking; /** + * Describes an item in an ecommerce transaction. + * * @author brettcsorba - * @deprecated Use {@link org.matomo.java.tracking.EcommerceItem} instead. + * @deprecated Use {@link org.matomo.java.tracking.parameters.EcommerceItem} instead. */ @Deprecated -public class EcommerceItem extends org.matomo.java.tracking.EcommerceItem { +public class EcommerceItem extends org.matomo.java.tracking.parameters.EcommerceItem { /** - * @deprecated Use {@link MatomoRequest} instead. + * Creates a new ecommerce item. + * + * @param sku the sku (Stock Keeping Unit) of the item. + * @param name the name of the item. + * @param category the category of the item. + * @param price the price of the item. + * @param quantity the quantity of the item. + * @deprecated Use {@link org.matomo.java.tracking.parameters.EcommerceItem} instead. */ @Deprecated public EcommerceItem(String sku, String name, String category, Double price, Integer quantity) { super(sku, name, category, price, quantity); } + } diff --git a/src/main/java/org/piwik/java/tracking/PiwikDate.java b/src/main/java/org/piwik/java/tracking/PiwikDate.java index 6105bd8d..c463543b 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikDate.java +++ b/src/main/java/org/piwik/java/tracking/PiwikDate.java @@ -4,39 +4,47 @@ * @link https://github.com/matomo/matomo-java-tracker * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ + package org.piwik.java.tracking; import org.matomo.java.tracking.MatomoDate; -import java.time.ZoneId; +import java.time.Instant; +import java.time.ZonedDateTime; import java.util.TimeZone; /** + * A date object that can be used to send dates to Matomo. This class is deprecated and will be removed in a future. + * * @author brettcsorba - * @deprecated Use {@link MatomoDate} instead. + * @deprecated Please use {@link Instant} */ @Deprecated public class PiwikDate extends MatomoDate { /** - * @author brettcsorba - * @deprecated Use {@link MatomoDate} instead. + * Creates a new date object with the current time. + * + * @deprecated Use {@link Instant} instead. */ public PiwikDate() { - super(); } /** - * @author brettcsorba - * @deprecated Use {@link MatomoDate} instead. + * Creates a new date object with the specified time. The time is specified in milliseconds since the epoch. + * + * @param epochMilli The time in milliseconds since the epoch + * @deprecated Use {@link Instant} instead. */ public PiwikDate(long epochMilli) { super(epochMilli); } /** - * @author brettcsorba - * @deprecated Use {@link MatomoDate#setTimeZone(ZoneId)} instead. + * Sets the time zone for this date object. This is used to convert the date to UTC before sending it to Matomo. + * + * @param zone the time zone to use + * @deprecated Use {@link ZonedDateTime#toInstant()} instead. */ @Deprecated public void setTimeZone(TimeZone zone) { diff --git a/src/main/java/org/piwik/java/tracking/PiwikLocale.java b/src/main/java/org/piwik/java/tracking/PiwikLocale.java index 686bbfd7..6b5bf1f9 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikLocale.java +++ b/src/main/java/org/piwik/java/tracking/PiwikLocale.java @@ -4,21 +4,28 @@ * @link https://github.com/matomo/matomo-java-tracker * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ + package org.piwik.java.tracking; -import org.matomo.java.tracking.MatomoLocale; +import org.matomo.java.tracking.parameters.Country; import java.util.Locale; /** + * A locale object that can be used to send visitor country to Matomo. This class is deprecated and will be removed in + * the future. + * * @author brettcsorba - * @deprecated Use {@link org.matomo.java.tracking.MatomoLocale} instead. + * @deprecated Use {@link Country} instead. */ @Deprecated -public class PiwikLocale extends MatomoLocale { +public class PiwikLocale extends Country { /** - * @deprecated Use {@link MatomoLocale} instead. + * Creates a new Piwik locale object with the specified locale. + * + * @param locale the locale to use + * @deprecated Use {@link Country} instead. */ @Deprecated public PiwikLocale(Locale locale) { diff --git a/src/main/java/org/piwik/java/tracking/PiwikRequest.java b/src/main/java/org/piwik/java/tracking/PiwikRequest.java index bab72b01..e4d64fe4 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikRequest.java +++ b/src/main/java/org/piwik/java/tracking/PiwikRequest.java @@ -4,14 +4,18 @@ * @link https://github.com/matomo/matomo-java-tracker * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ + package org.piwik.java.tracking; -import lombok.NonNull; -import org.matomo.java.tracking.MatomoRequest; +import static java.util.Objects.requireNonNull; import java.net.URL; +import org.matomo.java.tracking.MatomoRequest; /** + * A request object that can be used to send requests to Matomo. This class is deprecated and will be removed in the + * future. + * * @author brettcsorba * @deprecated Use {@link MatomoRequest} instead. */ @@ -19,10 +23,14 @@ public class PiwikRequest extends MatomoRequest { /** + * Creates a new request object with the specified site ID and action URL. + * + * @param siteId the site ID + * @param actionUrl the action URL. Must not be null. * @deprecated Use {@link MatomoRequest} instead. */ @Deprecated - public PiwikRequest(int siteId, @NonNull URL actionUrl) { - super(siteId, actionUrl.toString()); + public PiwikRequest(int siteId, URL actionUrl) { + super(siteId, requireNonNull(actionUrl, "Action URL must not be null").toString()); } } diff --git a/src/main/java/org/piwik/java/tracking/PiwikTracker.java b/src/main/java/org/piwik/java/tracking/PiwikTracker.java index 4e91e0b0..95acad84 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikTracker.java +++ b/src/main/java/org/piwik/java/tracking/PiwikTracker.java @@ -4,11 +4,14 @@ * @link https://github.com/matomo/matomo-java-tracker * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ + package org.piwik.java.tracking; import org.matomo.java.tracking.MatomoTracker; /** + * Creates a new PiwikTracker instance. This class is deprecated and will be removed in the future. + * * @author brettcsorba * @deprecated Use {@link MatomoTracker} instead. */ @@ -16,34 +19,53 @@ public class PiwikTracker extends MatomoTracker { /** + * Creates a new PiwikTracker instance with the given host URL. + * + * @param hostUrl the host URL of the Matomo server * @deprecated Use {@link MatomoTracker} instead. */ @Deprecated - public PiwikTracker(final String hostUrl) { + public PiwikTracker(String hostUrl) { super(hostUrl); } /** + * Creates a new PiwikTracker instance with the given host URL and timeout in milliseconds. Use -1 for no timeout. + * + * @param hostUrl the host URL of the Matomo server + * @param timeout the timeout in milliseconds or -1 for no timeout * @deprecated Use {@link MatomoTracker} instead. */ @Deprecated - public PiwikTracker(final String hostUrl, final int timeout) { + public PiwikTracker(String hostUrl, int timeout) { super(hostUrl, timeout); } /** + * Creates a new PiwikTracker instance with the given host URL and proxy settings. + * + * @param hostUrl the host URL of the Matomo server + * @param proxyHost the proxy host + * @param proxyPort the proxy port * @deprecated Use {@link MatomoTracker} instead. */ @Deprecated - public PiwikTracker(final String hostUrl, final String proxyHost, final int proxyPort) { + public PiwikTracker(String hostUrl, String proxyHost, int proxyPort) { super(hostUrl, proxyHost, proxyPort); } /** + * Creates a new PiwikTracker instance with the given host URL, proxy settings and timeout in milliseconds. Use -1 for + * no timeout. + * + * @param hostUrl the host URL of the Matomo server + * @param proxyHost the proxy host + * @param proxyPort the proxy port + * @param timeout the timeout in milliseconds or -1 for no timeout * @deprecated Use {@link MatomoTracker} instead. */ @Deprecated - public PiwikTracker(final String hostUrl, final String proxyHost, final int proxyPort, final int timeout) { + public PiwikTracker(String hostUrl, String proxyHost, int proxyPort, int timeout) { super(hostUrl, proxyHost, proxyPort, timeout); } diff --git a/src/main/java/org/piwik/java/tracking/package-info.java b/src/main/java/org/piwik/java/tracking/package-info.java new file mode 100644 index 00000000..c6427194 --- /dev/null +++ b/src/main/java/org/piwik/java/tracking/package-info.java @@ -0,0 +1,6 @@ +/** + * Piwik Java Tracking API. Renamed to {@link org.matomo.java.tracking} in 3.0.0. + * + */ + +package org.piwik.java.tracking; diff --git a/src/test/java/org/matomo/java/tracking/AuthTokenTest.java b/src/test/java/org/matomo/java/tracking/AuthTokenTest.java new file mode 100644 index 00000000..da161596 --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/AuthTokenTest.java @@ -0,0 +1,68 @@ +package org.matomo.java.tracking; + +import static java.util.Collections.singleton; +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.URI; +import org.junit.jupiter.api.Test; + +class AuthTokenTest { + + @Test + void determineAuthTokenReturnsAuthTokenFromRequest() { + + MatomoRequest request = MatomoRequest.builder().authToken("bdeca231a312ab12cde124131bedfa23") + .build(); + + String authToken = AuthToken.determineAuthToken(null, singleton(request), null); + + assertThat(authToken).isEqualTo("bdeca231a312ab12cde124131bedfa23"); + + } + + @Test + void determineAuthTokenReturnsAuthTokenFromTrackerConfiguration() { + + TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder() + .apiEndpoint(URI.create("https://your-matomo-domain.example/matomo.")) + .defaultAuthToken("bdeca231a312ab12cde124131bedfa23") + .build(); + + String authToken = AuthToken.determineAuthToken(null, null, trackerConfiguration); + + assertThat(authToken).isEqualTo("bdeca231a312ab12cde124131bedfa23"); + } + + @Test + void determineAuthTokenFromTrackerConfigurationIfRequestTokenIsEmpty() { + + MatomoRequest request = MatomoRequest.builder() + .authToken("").build(); + + TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder() + .apiEndpoint(URI.create("https://your-matomo-domain.example/matomo")) + .defaultAuthToken("bdeca231a312ab12cde124131bedfa23").build(); + + String authToken = AuthToken.determineAuthToken(null, singleton(request), trackerConfiguration); + + assertThat(authToken).isEqualTo("bdeca231a312ab12cde124131bedfa23"); + + } + + @Test + void determineAuthTokenFromTrackerConfigurationIfRequestTokenIsBlank() { + + MatomoRequest request = MatomoRequest.builder() + .authToken(" ").build(); + + TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder() + .apiEndpoint(URI.create("https://your-matomo-domain.example/matomo")) + .defaultAuthToken("bdeca231a312ab12cde124131bedfa23").build(); + + String authToken = AuthToken.determineAuthToken(null, singleton(request), trackerConfiguration); + + assertThat(authToken).isEqualTo("bdeca231a312ab12cde124131bedfa23"); + + } + +} diff --git a/src/test/java/org/matomo/java/tracking/CustomVariableTest.java b/src/test/java/org/matomo/java/tracking/CustomVariableTest.java index 9818bc8d..a9499bda 100644 --- a/src/test/java/org/matomo/java/tracking/CustomVariableTest.java +++ b/src/test/java/org/matomo/java/tracking/CustomVariableTest.java @@ -1,54 +1,37 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.matomo.java.tracking; -import org.junit.Before; -import org.junit.Test; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import org.junit.jupiter.api.Test; -/** - * @author Katie - */ -public class CustomVariableTest { - private CustomVariable customVariable; - - @Before - public void setUp() { - customVariable = new CustomVariable("key", "value"); - } +class CustomVariableTest { @Test - public void testConstructorNullKey() { - try { - new CustomVariable(null, null); - fail("Exception should have been throw."); - } catch (NullPointerException e) { - assertEquals("key is marked non-null but is null", e.getLocalizedMessage()); - } + void createsCustomVariable() { + CustomVariable customVariable = new CustomVariable("key", "value"); + + assertThat(customVariable.getKey()).isEqualTo("key"); + assertThat(customVariable.getValue()).isEqualTo("value"); } @Test - public void testConstructorNullValue() { - try { - new CustomVariable("key", null); - fail("Exception should have been throw."); - } catch (NullPointerException e) { - assertEquals("value is marked non-null but is null", e.getLocalizedMessage()); - } + void failsOnNullKey() { + assertThatThrownBy(() -> new CustomVariable(null, "value")).isInstanceOf( + NullPointerException.class); } @Test - public void testGetKey() { - assertEquals("key", customVariable.getKey()); + void failsOnNullValue() { + assertThatThrownBy(() -> new CustomVariable("key", null)).isInstanceOf( + NullPointerException.class); } @Test - public void testGetValue() { - assertEquals("value", customVariable.getValue()); + void failsOnNullKeyAndValue() { + assertThatThrownBy(() -> new CustomVariable(null, null)).isInstanceOf( + NullPointerException.class); } + + } diff --git a/src/test/java/org/matomo/java/tracking/CustomVariablesTest.java b/src/test/java/org/matomo/java/tracking/CustomVariablesTest.java deleted file mode 100644 index bdc4e8d1..00000000 --- a/src/test/java/org/matomo/java/tracking/CustomVariablesTest.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.matomo.java.tracking; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -/** - * @author Katie - */ -public class CustomVariablesTest { - private final CustomVariables customVariables = new CustomVariables(); - - @Test - public void testAdd_CustomVariable() { - CustomVariable a = new CustomVariable("a", "b"); - CustomVariable b = new CustomVariable("c", "d"); - CustomVariable c = new CustomVariable("a", "e"); - CustomVariable d = new CustomVariable("a", "f"); - - assertTrue(customVariables.isEmpty()); - customVariables.add(a); - assertFalse(customVariables.isEmpty()); - assertEquals("b", customVariables.get("a")); - assertEquals(a, customVariables.get(1)); - assertEquals("{\"1\":[\"a\",\"b\"]}", customVariables.toString()); - - customVariables.add(b); - assertEquals("d", customVariables.get("c")); - assertEquals(b, customVariables.get(2)); - assertEquals("{\"1\":[\"a\",\"b\"],\"2\":[\"c\",\"d\"]}", customVariables.toString()); - - customVariables.add(c, 5); - assertEquals("b", customVariables.get("a")); - assertEquals(c, customVariables.get(5)); - assertNull(customVariables.get(3)); - assertEquals("{\"1\":[\"a\",\"b\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"e\"]}", customVariables.toString()); - - customVariables.add(d); - assertEquals("f", customVariables.get("a")); - assertEquals(d, customVariables.get(1)); - assertEquals(d, customVariables.get(5)); - assertEquals("{\"1\":[\"a\",\"f\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"f\"]}", customVariables.toString()); - - customVariables.remove("a"); - assertNull(customVariables.get("a")); - assertNull(customVariables.get(1)); - assertNull(customVariables.get(5)); - assertEquals("{\"2\":[\"c\",\"d\"]}", customVariables.toString()); - - customVariables.remove(2); - assertNull(customVariables.get("c")); - assertNull(customVariables.get(2)); - assertTrue(customVariables.isEmpty()); - assertEquals("{}", customVariables.toString()); - } - - @Test - public void testAddCustomVariableIndexLessThan1() { - try { - customVariables.add(new CustomVariable("a", "b"), 0); - fail("Exception should have been throw."); - } catch (IllegalArgumentException e) { - assertEquals("Index must be greater than 0.", e.getLocalizedMessage()); - } - } - - @Test - public void testGetCustomVariableIntegerLessThan1() { - try { - customVariables.get(0); - fail("Exception should have been throw."); - } catch (IllegalArgumentException e) { - assertEquals("Index must be greater than 0.", e.getLocalizedMessage()); - } - } -} diff --git a/src/test/java/org/matomo/java/tracking/DaemonThreadFactoryTest.java b/src/test/java/org/matomo/java/tracking/DaemonThreadFactoryTest.java new file mode 100644 index 00000000..81dd76ed --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/DaemonThreadFactoryTest.java @@ -0,0 +1,37 @@ +package org.matomo.java.tracking; + +import org.junit.jupiter.api.Test; + +import java.util.concurrent.ThreadFactory; + +import static org.assertj.core.api.Assertions.assertThat; + +class DaemonThreadFactoryTest { + + private final ThreadFactory daemonThreadFactory = new DaemonThreadFactory(); + + private Thread thread; + + @Test + void threadIsDaemonThread() { + + whenCreatesThread(); + + assertThat(thread.isDaemon()).isTrue(); + + } + + private void whenCreatesThread() { + thread = daemonThreadFactory.newThread(null); + } + + @Test + void threadHasName() { + + whenCreatesThread(); + + assertThat(thread.getName()).isEqualTo("MatomoJavaTracker"); + + } + +} diff --git a/src/test/java/org/matomo/java/tracking/EcommerceItemTest.java b/src/test/java/org/matomo/java/tracking/EcommerceItemTest.java index 986fb305..97349e6c 100644 --- a/src/test/java/org/matomo/java/tracking/EcommerceItemTest.java +++ b/src/test/java/org/matomo/java/tracking/EcommerceItemTest.java @@ -1,108 +1,68 @@ -/* - * Matomo Java Tracker - * - * @link https://github.com/matomo/matomo-java-tracker - * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause - */ package org.matomo.java.tracking; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; -/** - * @author brettcsorba - */ -public class EcommerceItemTest { - EcommerceItem ecommerceItem; +class EcommerceItemTest { - public EcommerceItemTest() { - } - - @BeforeClass - public static void setUpClass() { - } - - @AfterClass - public static void tearDownClass() { - } - - @Before - public void setUp() { - ecommerceItem = new EcommerceItem(null, null, null, null, null); - } - - @After - public void tearDown() { - } + private EcommerceItem ecommerceItem = new EcommerceItem(null, null, null, null, null); /** * Test of constructor, of class EcommerceItem. */ @Test - public void testConstructor() { - EcommerceItem ecommerceItem = new EcommerceItem("sku", "name", "category", 1.0, 1); - - assertEquals("sku", ecommerceItem.getSku()); - assertEquals("name", ecommerceItem.getName()); - assertEquals("category", ecommerceItem.getCategory()); - assertEquals(new Double(1.0), ecommerceItem.getPrice()); - assertEquals(new Integer(1), ecommerceItem.getQuantity()); + void testConstructor() { + EcommerceItem ecommerceItem = new EcommerceItem("sku", "name", "category", 2.0, 2); + assertThat(ecommerceItem.getSku()).isEqualTo("sku"); + assertThat(ecommerceItem.getName()).isEqualTo("name"); + assertThat(ecommerceItem.getCategory()).isEqualTo("category"); + assertThat(ecommerceItem.getPrice()).isEqualTo(2.0); + assertThat(ecommerceItem.getQuantity()).isEqualTo(2); } /** * Test of getSku method, of class EcommerceItem. */ @Test - public void testGetSku() { + void testGetSku() { ecommerceItem.setSku("sku"); - - assertEquals("sku", ecommerceItem.getSku()); + assertThat(ecommerceItem.getSku()).isEqualTo("sku"); } /** * Test of getName method, of class EcommerceItem. */ @Test - public void testGetName() { + void testGetName() { ecommerceItem.setName("name"); - - assertEquals("name", ecommerceItem.getName()); + assertThat(ecommerceItem.getName()).isEqualTo("name"); } /** * Test of getCategory method, of class EcommerceItem. */ @Test - public void testGetCategory() { + void testGetCategory() { ecommerceItem.setCategory("category"); - - assertEquals("category", ecommerceItem.getCategory()); + assertThat(ecommerceItem.getCategory()).isEqualTo("category"); } /** * Test of getPrice method, of class EcommerceItem. */ @Test - public void testGetPrice() { - ecommerceItem.setPrice(1.0); - - assertEquals(new Double(1.0), ecommerceItem.getPrice()); + void testGetPrice() { + ecommerceItem.setPrice(2.0); + assertThat(ecommerceItem.getPrice()).isEqualTo(2.0); } /** * Test of getQuantity method, of class EcommerceItem. */ @Test - public void testGetQuantity() { - ecommerceItem.setQuantity(1); - - assertEquals(new Integer(1), ecommerceItem.getQuantity()); + void testGetQuantity() { + ecommerceItem.setQuantity(2); + assertThat(ecommerceItem.getQuantity()).isEqualTo(2); } - - } diff --git a/src/test/java/org/matomo/java/tracking/EcommerceItemsTest.java b/src/test/java/org/matomo/java/tracking/EcommerceItemsTest.java deleted file mode 100644 index ee76b7ce..00000000 --- a/src/test/java/org/matomo/java/tracking/EcommerceItemsTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.matomo.java.tracking; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -public class EcommerceItemsTest { - - @Test - public void formatsJson() { - - EcommerceItems ecommerceItems = new EcommerceItems(); - ecommerceItems.add(new EcommerceItem("sku", "name", "category", 1.0, 1)); - - assertEquals("[[\"sku\",\"name\",\"category\",1.0,1]]", ecommerceItems.toString()); - - } -} diff --git a/src/test/java/org/matomo/java/tracking/InvalidUrlExceptionTest.java b/src/test/java/org/matomo/java/tracking/InvalidUrlExceptionTest.java new file mode 100644 index 00000000..17605f04 --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/InvalidUrlExceptionTest.java @@ -0,0 +1,18 @@ +package org.matomo.java.tracking; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class InvalidUrlExceptionTest { + + @Test + void createsInvalidUrlException() { + InvalidUrlException invalidUrlException = new InvalidUrlException(new Throwable()); + + assertThat(invalidUrlException).isNotNull(); + assertThat(invalidUrlException.getCause()).isNotNull(); + + } + +} diff --git a/src/test/java/org/matomo/java/tracking/MatomoExceptionTest.java b/src/test/java/org/matomo/java/tracking/MatomoExceptionTest.java new file mode 100644 index 00000000..9c998a37 --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/MatomoExceptionTest.java @@ -0,0 +1,25 @@ +package org.matomo.java.tracking; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class MatomoExceptionTest { + + @Test + void createsMatomoExceptionWithMessage() { + MatomoException matomoException = new MatomoException("message"); + + assertEquals("message", matomoException.getMessage()); + } + + @Test + void createsMatomoExceptionWithMessageAndCause() { + Throwable cause = new Throwable(); + MatomoException matomoException = new MatomoException("message", cause); + + assertEquals("message", matomoException.getMessage()); + assertEquals(cause, matomoException.getCause()); + } + +} diff --git a/src/test/java/org/matomo/java/tracking/MatomoLocaleTest.java b/src/test/java/org/matomo/java/tracking/MatomoLocaleTest.java new file mode 100644 index 00000000..9dce779e --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/MatomoLocaleTest.java @@ -0,0 +1,23 @@ +package org.matomo.java.tracking; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.Locale; +import org.junit.jupiter.api.Test; + +class MatomoLocaleTest { + + @Test + void createsMatomoLocaleFromLocale() { + MatomoLocale locale = new MatomoLocale(Locale.US); + assertThat(locale.toString()).isEqualTo("us"); + } + + @Test + void failsIfLocaleIsNull() { + assertThatThrownBy(() -> new MatomoLocale(null)).isInstanceOf(NullPointerException.class) + .hasMessage("Locale must not be null"); + } + +} diff --git a/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java b/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java index 9d2804ed..ec2a71d0 100644 --- a/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java +++ b/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java @@ -1,49 +1,43 @@ package org.matomo.java.tracking; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.matomo.java.tracking.parameters.CustomVariables; -import java.util.Collection; import java.util.Collections; -import java.util.Map; -import static org.hamcrest.CoreMatchers.hasItem; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; +import static java.util.Collections.singleton; +import static org.assertj.core.api.Assertions.assertThat; -public class MatomoRequestBuilderTest { + +class MatomoRequestBuilderTest { @Test - public void buildsRequest() { - - CustomVariable customVariable = new CustomVariable("pageCustomVariableName", "pageCustomVariableValue"); - MatomoRequest matomoRequest = MatomoRequest.builder() - .siteId(42) - .actionName("ACTION_NAME") - .actionUrl("https://www.your-domain.tld/some/page?query=foo") - .referrerUrl("https://referrer.com") - .customTrackingParameters(Collections.singletonMap("trackingParameterName", "trackingParameterValue")) - .pageCustomVariables(Collections.singletonList(customVariable)) - .visitCustomVariables(Collections.singletonList(customVariable)) - .customAction(true) - .build(); - - Map> parameters = matomoRequest.getParameters(); - assertThat(parameters.get("idsite"), hasItem(42)); - assertThat(parameters.get("action_name"), hasItem("ACTION_NAME")); - assertThat(parameters.get("apiv"), hasItem("1")); - assertThat(parameters.get("url"), hasItem("https://www.your-domain.tld/some/page?query=foo")); - assertThat(parameters.get("_id").isEmpty(), is(false)); - assertThat(parameters.get("rand").isEmpty(), is(false)); - assertThat(parameters.get("send_image"), hasItem(new MatomoBoolean(false))); - assertThat(parameters.get("rec"), hasItem(new MatomoBoolean(true))); - assertThat(parameters.get("urlref"), hasItem("https://referrer.com")); - assertThat(parameters.get("trackingParameterName"), hasItem("trackingParameterValue")); - CustomVariables customVariables = new CustomVariables(); - customVariables.add(customVariable); - assertThat(parameters.get("cvar"), hasItem(customVariables)); - assertThat(parameters.get("_cvar"), hasItem(customVariables)); - assertThat(parameters.get("ca"), hasItem(new MatomoBoolean(true))); + void buildsRequest() { + CustomVariable pageCustomVariable = new CustomVariable("pageCustomVariableName", "pageCustomVariableValue"); + CustomVariable visitCustomVariable = new CustomVariable("visitCustomVariableName", "visitCustomVariableValue"); + + MatomoRequest matomoRequest = MatomoRequest.builder().siteId(42).actionName("ACTION_NAME") + .actionUrl("https://www.your-domain.tld/some/page?query=foo").referrerUrl("https://referrer.com") + .customTrackingParameters(Collections.singletonMap("trackingParameterName", singleton("trackingParameterValue"))) + .pageCustomVariables(new CustomVariables().add(pageCustomVariable, 2)) + .visitCustomVariables(new CustomVariables().add(visitCustomVariable, 3)).customAction(true).build(); + + assertThat(matomoRequest.getSiteId()).isEqualTo(42); + assertThat(matomoRequest.getActionName()).isEqualTo("ACTION_NAME"); + assertThat(matomoRequest.getApiVersion()).isEqualTo("1"); + assertThat(matomoRequest.getActionUrl()).isEqualTo("https://www.your-domain.tld/some/page?query=foo"); + assertThat(matomoRequest.getVisitorId().toString()).hasSize(16).isHexadecimal(); + assertThat(matomoRequest.getRandomValue().toString()).hasSize(20).isHexadecimal(); + assertThat(matomoRequest.getResponseAsImage()).isFalse(); + assertThat(matomoRequest.getRequired()).isTrue(); + assertThat(matomoRequest.getReferrerUrl()).isEqualTo("https://referrer.com"); + assertThat(matomoRequest.getCustomTrackingParameter("trackingParameterName")) + .containsExactly("trackingParameterValue"); + assertThat(matomoRequest.getPageCustomVariables()) + .hasToString("{\"2\":[\"pageCustomVariableName\",\"pageCustomVariableValue\"]}"); + assertThat(matomoRequest.getVisitCustomVariables()) + .hasToString("{\"3\":[\"visitCustomVariableName\",\"visitCustomVariableValue\"]}"); + assertThat(matomoRequest.getCustomAction()).isTrue(); } - } diff --git a/src/test/java/org/matomo/java/tracking/MatomoRequestTest.java b/src/test/java/org/matomo/java/tracking/MatomoRequestTest.java new file mode 100644 index 00000000..45abd2ac --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/MatomoRequestTest.java @@ -0,0 +1,95 @@ +package org.matomo.java.tracking; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; + +import org.junit.jupiter.api.Test; + +class MatomoRequestTest { + + private MatomoRequest request = new MatomoRequest(); + + @Test + void returnsEmptyListWhenCustomTrackingParametersDoesNotContainKey() { + + request.setCustomTrackingParameter("foo", "bar"); + + assertThat(request.getCustomTrackingParameter("baz")).isEmpty(); + assertThat(request.getCustomTrackingParameters()).isNotEmpty(); + assertThat(request.getCustomTrackingParameter("foo")).isNotEmpty(); + } + + @Test + void getPageCustomVariableReturnsNullIfPageCustomVariablesIsNull() { + assertThat(request.getPageCustomVariable("foo")).isNull(); + } + + @Test + void getPageCustomVariableReturnsValueIfPageCustomVariablesIsNotNull() { + request.setPageCustomVariable("foo", "bar"); + assertThat(request.getPageCustomVariable("foo")).isEqualTo("bar"); + } + + @Test + void setPageCustomVariableRequiresNonNullKey() { + assertThatThrownBy(() -> request.setPageCustomVariable(null, "bar")) + .isInstanceOf(NullPointerException.class); + } + + @Test + void setPageCustomVariableDoesNothingIfValueIsNull() { + request.setPageCustomVariable("foo", null); + assertThat(request.getPageCustomVariable("foo")).isNull(); + } + + @Test + void setPageCustomVariableRemovesValueIfValueIsNull() { + request.setPageCustomVariable("foo", "bar"); + request.setPageCustomVariable("foo", null); + assertThat(request.getPageCustomVariable("foo")).isNull(); + } + + @Test + void setPageCustomVariableAddsCustomVariableIfValueIsNotNull() { + request.setPageCustomVariable("foo", "bar"); + assertThat(request.getPageCustomVariable("foo")).isEqualTo("bar"); + } + + @Test + void setPageCustomVariableDoesNothingIfCustomVariableParameterIsNullAndIndexIsPositive() { + request.setPageCustomVariable(null, 1); + assertThat(request.getPageCustomVariable(1)).isNull(); + } + + @Test + void setPageCustomVariableInitializesPageCustomVariablesIfCustomVariableParameterIsNullAndIndexIsPositive() { + request.setPageCustomVariable(new CustomVariable("key", "value"), 1); + assertThat(request.getPageCustomVariables()).isNotNull(); + } + + @Test + void setUserCustomVariableDoesNothingIfValueIsNull() { + request.setUserCustomVariable("foo", null); + assertThat(request.getUserCustomVariable("foo")).isNull(); + } + + @Test + void setUserCustomVariableRemovesValueIfValueIsNull() { + request.setUserCustomVariable("foo", "bar"); + request.setUserCustomVariable("foo", null); + assertThat(request.getUserCustomVariable("foo")).isNull(); + } + + @Test + void setVisitCustomVariableDoesNothingIfCustomVariableParameterIsNullAndIndexIsPositive() { + request.setVisitCustomVariable(null, 1); + assertThat(request.getVisitCustomVariable(1)).isNull(); + } + + @Test + void setVisitCustomVariableInitializesVisitCustomVariablesIfCustomVariableParameterIsNullAndIndexIsPositive() { + request.setVisitCustomVariable(new CustomVariable("key", "value"), 1); + assertThat(request.getVisitCustomVariables()).isNotNull(); + } + +} diff --git a/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java new file mode 100644 index 00000000..1e18b942 --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java @@ -0,0 +1,526 @@ +package org.matomo.java.tracking; + +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.status; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static java.util.Collections.singleton; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import java.net.URI; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Arrays; +import java.util.Locale.LanguageRange; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.matomo.java.tracking.MatomoRequest.MatomoRequestBuilder; +import org.matomo.java.tracking.TrackerConfiguration.TrackerConfigurationBuilder; +import org.matomo.java.tracking.parameters.AcceptLanguage; +import org.matomo.java.tracking.parameters.Country; +import org.matomo.java.tracking.parameters.CustomVariable; +import org.matomo.java.tracking.parameters.CustomVariables; +import org.matomo.java.tracking.parameters.DeviceResolution; +import org.matomo.java.tracking.parameters.EcommerceItem; +import org.matomo.java.tracking.parameters.EcommerceItems; +import org.matomo.java.tracking.parameters.RandomValue; +import org.matomo.java.tracking.parameters.UniqueId; +import org.matomo.java.tracking.parameters.VisitorId; + +class MatomoTrackerIT { + + private static final WireMockServer wireMockServer = new WireMockServer( + WireMockConfiguration.options().dynamicPort()); + + private static final int SITE_ID = 42; + + private final TrackerConfigurationBuilder trackerConfigurationBuilder = + TrackerConfiguration.builder(); + + private final MatomoRequestBuilder requestBuilder = + MatomoRequest.builder().visitorId(VisitorId.fromHex("bbccddeeff1122")) + .randomValue(RandomValue.fromString("someRandom")); + + private CompletableFuture future; + + @BeforeAll + static void beforeAll() { + wireMockServer.start(); + } + + @BeforeEach + void givenStub() { + wireMockServer.resetRequests(); + wireMockServer.stubFor(post(urlPathEqualTo("/matomo.php")).willReturn(status(204))); + wireMockServer.stubFor(get(urlPathEqualTo("/matomo.php")).willReturn(status(204))); + } + + @Test + void requiresApiEndpoint() { + + assertThatThrownBy(() -> trackerConfigurationBuilder.defaultSiteId(SITE_ID).build()) + .isInstanceOf( + NullPointerException.class).hasMessage("apiEndpoint is marked non-null but is null"); + + } + + @Test + void requiresSiteId() { + + trackerConfigurationBuilder.apiEndpoint(URI.create("http://localhost:8099/matomo.php")).build(); + + assertThatThrownBy(this::whenSendsRequestAsync).isInstanceOf(IllegalArgumentException.class) + .hasMessage("No default site ID and no request site ID is given"); + + } + + private void whenSendsRequestAsync() { + future = new MatomoTracker(trackerConfigurationBuilder.build()).sendRequestAsync( + requestBuilder.build()); + try { + future.get(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private void whenSendsSingleRequest() { + new MatomoTracker(trackerConfigurationBuilder.build()).sendRequest(requestBuilder.build()); + } + + private void whenSendsBulkRequest() { + new MatomoTracker(trackerConfigurationBuilder.build()).sendBulkRequest( + singleton(requestBuilder.build())); + } + + @Test + void usesDefaultSiteId() { + + givenTrackerConfigurationWithDefaultSiteId(); + + whenSendsRequestAsync(); + + thenGetsRequest("idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom"); + + } + + private void givenTrackerConfigurationWithDefaultSiteId() { + trackerConfigurationBuilder.apiEndpoint(URI.create(String.format( + "http://localhost:%s/matomo.php", wireMockServer.port()))).defaultSiteId(SITE_ID); + } + + private void thenGetsRequest(String expectedQuery) { + assertThat(future).isNotCompletedExceptionally(); + wireMockServer.verify( + getRequestedFor(urlEqualTo(String.format("/matomo.php?%s", expectedQuery))) + .withHeader("User-Agent", equalTo("MatomoJavaClient"))); + } + + @Test + void overridesDefaultSiteId() { + + givenTrackerConfigurationWithDefaultSiteId(); + requestBuilder.siteId(123); + + whenSendsRequestAsync(); + + thenGetsRequest("rec=1&idsite=123&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom"); + + } + + @Test + void validatesTokenAuth() { + + givenTrackerConfigurationWithDefaultSiteId(); + requestBuilder.authToken("invalid-token-auth"); + + assertThatThrownBy(this::whenSendsRequestAsync).hasRootCauseInstanceOf( + IllegalArgumentException.class) + .hasRootCauseMessage("Auth token must be exactly 32 characters long"); + + } + + @Test + void convertsTrueBooleanTo1() { + + givenTrackerConfigurationWithDefaultSiteId(); + requestBuilder.pluginFlash(true); + + whenSendsRequestAsync(); + + thenGetsRequest( + "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&fla=1&send_image=0&rand=someRandom" + ); + + } + + @Test + void convertsFalseBooleanTo0() { + + givenTrackerConfigurationWithDefaultSiteId(); + requestBuilder.pluginJava(false); + + whenSendsRequestAsync(); + + thenGetsRequest( + "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&java=0&send_image=0&rand=someRandom" + ); + + } + + @Test + void encodesUrl() { + + givenTrackerConfigurationWithDefaultSiteId(); + requestBuilder.actionUrl("https://www.daniel-heid.de/some/page?foo=bar"); + + whenSendsRequestAsync(); + + thenGetsRequest( + "idsite=42&rec=1&url=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fpage%3Ffoo%3Dbar&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom" + ); + + } + + @Test + void encodesReferrerUrl() { + + givenTrackerConfigurationWithDefaultSiteId(); + requestBuilder.referrerUrl("https://www.daniel-heid.de/some/referrer?foo2=bar2"); + + whenSendsRequestAsync(); + + thenGetsRequest( + "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Freferrer%3Ffoo2%3Dbar2&send_image=0&rand=someRandom" + ); + + } + + @Test + void encodesLink() { + + givenTrackerConfigurationWithDefaultSiteId(); + requestBuilder.outlinkUrl("https://www.daniel-heid.de/some/external/link#"); + + whenSendsBulkRequestAsync(); + + thenPostsRequestWithoutAuthToken( + "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&link=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fexternal%2Flink%23&send_image=0&rand=someRandom", + "156" + ); + + } + + private void whenSendsBulkRequestAsync() { + future = + new MatomoTracker(trackerConfigurationBuilder.build()).sendBulkRequestAsync( + singleton(requestBuilder.build())); + try { + future.get(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private void thenPostsRequestWithoutAuthToken(String expectedQuery, String contentLength) { + assertThat(future).isNotCompletedExceptionally(); + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo(contentLength)) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody( + equalToJson("{\"requests\":[\"?" + expectedQuery + "\"]}"))); + } + + @Test + void encodesDownloadUrl() { + + givenTrackerConfigurationWithDefaultSiteId(); + requestBuilder.downloadUrl("https://www.daniel-heid.de/some/download.pdf"); + + whenSendsBulkRequestAsync(); + + thenPostsRequestWithoutAuthToken( + "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&download=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fdownload.pdf&send_image=0&rand=someRandom", + "154" + ); + + } + + @Test + void getContainsHeaders() { + + givenTrackerConfigurationWithDefaultSiteId(); + + whenSendsRequestAsync(); + + assertThat(future).isNotCompletedExceptionally(); + wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php")) + .withHeader("User-Agent", equalTo("MatomoJavaClient"))); + + } + + @Test + void postContainsHeaders() { + + givenTrackerConfigurationWithDefaultSiteId(); + + whenSendsBulkRequestAsync(); + + assertThat(future).isNotCompletedExceptionally(); + wireMockServer.verify(postRequestedFor(urlPathEqualTo("/matomo.php")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Length", equalTo("90")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient"))); + + } + + @Test + void allowsToOverrideUserAgent() { + + givenTrackerConfigurationWithDefaultSiteId(); + trackerConfigurationBuilder.userAgent("Mozilla/5.0"); + + whenSendsRequestAsync(); + + assertThat(future).isNotCompletedExceptionally(); + wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php")) + .withHeader("User-Agent", equalTo("Mozilla/5.0"))); + + } + + @Test + void tracksMinimalRequest() { + + givenTrackerConfigurationWithDefaultSiteId(); + requestBuilder.actionName("Help / Feedback").actionUrl("https://www.daniel-heid.de/portfolio") + .visitorId(VisitorId.fromHash(3434343434343434343L)).referrerUrl( + "https://www.daniel-heid.de/referrer") + .visitCustomVariables( + new CustomVariables() + .add(new CustomVariable("customVariable1Key", "customVariable1Value"), 4) + .add(new CustomVariable("customVariable2Key", "customVariable2Value"), 5)) + .visitorVisitCount(2) + .visitorFirstVisitTimestamp( + LocalDateTime.of(2022, 8, 9, 18, 34, 12).toInstant(ZoneOffset.UTC)) + .deviceResolution(DeviceResolution.builder().width(1024).height(768).build()) + .headerAcceptLanguage( + AcceptLanguage.builder().languageRange(new LanguageRange("de")).languageRange( + new LanguageRange("de-DE", 0.9)) + .languageRange(new LanguageRange("en", 0.8)).build()).pageViewId( + UniqueId.fromValue(999999999999999999L)) + .goalId(0).ecommerceRevenue(12.34).ecommerceItems( + EcommerceItems.builder().item( + org.matomo.java.tracking.parameters.EcommerceItem.builder().sku("SKU").build()) + .item(EcommerceItem.builder().sku("SKU").name("NAME").category("CATEGORY").price(123.4) + .build()).build()) + .authToken("fdf6e8461ea9de33176b222519627f78") + .visitorCountry( + Country.fromLanguageRanges("en-GB;q=0.7,de,de-DE;q=0.9,en;q=0.8,en-US;q=0.6")); + + whenSendsBulkRequestAsync(); + + assertThat(future).isNotCompletedExceptionally(); + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")).withHeader( + "Content-Length", equalTo("711")) + .withHeader("Accept", equalTo("*/*")).withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody( + equalToJson("{\"requests\":[\"?" + + "idsite=42&rec=1&action_name=Help+%2F+Feedback&url=https%3A%2F%2Fwww.daniel-heid.de%2Fportfolio&apiv=1&_id=2fa93d2858bc4867&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Freferrer&_cvar=%7B%224%22%3A%5B%22customVariable1Key%22%2C%22customVariable1Value%22%5D%2C%225%22%3A%5B%22customVariable2Key%22%2C%22customVariable2Value%22%5D%7D&_idvc=2&_idts=1660070052&res=1024x768&lang=de%2Cde-de%3Bq%3D0.9%2Cen%3Bq%3D0.8&pv_id=lbBbxG&idgoal=0&revenue=12.34&ec_items=%5B%5B%22SKU%22%2C%22%22%2C%22%22%2C0.0%2C0%5D%2C%5B%22SKU%22%2C%22NAME%22%2C%22CATEGORY%22%2C123.4%2C0%5D%5D&token_auth=fdf6e8461ea9de33176b222519627f78&country=de&send_image=0&rand=someRandom" + + "\"],\"token_auth\" : \"" + "fdf6e8461ea9de33176b222519627f78" + "\"}"))); + + } + + @Test + void doesNothingIfNotEnabled() { + + wireMockServer.resetRequests(); + givenTrackerConfigurationWithDefaultSiteId(); + trackerConfigurationBuilder.enabled(false); + + whenSendsRequestAsync(); + + assertThat(future).isNotCompletedExceptionally(); + wireMockServer.verify(0, postRequestedFor(urlPathEqualTo("/matomo.php"))); + + } + + @Test + void exampleWorks() { + + TrackerConfiguration config = + TrackerConfiguration.builder().apiEndpoint( + URI.create("https://your-domain.net/matomo/matomo.php")) + .defaultSiteId(42) // if not explicitly specified by action + .build(); + + // Prepare the tracker (stateless - can be used for multiple actions) + MatomoTracker tracker = new MatomoTracker(config); + + // Track an action + CompletableFuture future = tracker.sendRequestAsync( + MatomoRequest.builder().actionName("User Profile / Upload Profile Picture") + .actionUrl("https://your-domain.net/user/profile/picture") + .visitorId(VisitorId.fromHash("some@email-adress.org".hashCode())) + // ... + .build()); + + // If you want to ensure the request has been handled: + if (future.isCompletedExceptionally()) { + // log, throw, ... + } + } + + @Test + void reportsErrors() { + + wireMockServer.stubFor(get(urlPathEqualTo("/failing")).willReturn(status(500))); + trackerConfigurationBuilder.apiEndpoint(URI.create(String.format( + "http://localhost:%d/failing", + wireMockServer.port() + ))).defaultSiteId(SITE_ID); + + assertThatThrownBy(this::whenSendsRequestAsync).hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Tracking endpoint responded with code 500"); + + assertThat(future).isCompletedExceptionally(); + + } + + @Test + void includesDefaultTokenAuth() { + + givenTrackerConfigurationWithDefaultSiteId(); + trackerConfigurationBuilder.defaultAuthToken("fdf6e8461ea9de33176b222519627f78"); + + whenSendsRequestAsync(); + + assertThat(future).isNotCompletedExceptionally(); + wireMockServer.verify( + getRequestedFor( + urlEqualTo( + "/matomo.php?idsite=42token_auth=fdf6e8461ea9de33176b222519627f78&rec=1&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom")) + .withHeader("User-Agent", equalTo("MatomoJavaClient"))); + + } + + @Test + void includesMultipleQueriesInBulkRequest() throws Exception { + + givenTrackerConfigurationWithDefaultSiteId(); + MatomoTracker tracker = new MatomoTracker(trackerConfigurationBuilder.build()); + + CompletableFuture future1 = tracker.sendBulkRequestAsync( + Arrays.asList( + requestBuilder.actionName("First").build(), + requestBuilder.actionName("Second").build(), + requestBuilder.actionName("Third").build() + )); + future1.get(); + + assertThat(future1).isNotCompletedExceptionally(); + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")).withHeader( + "Content-Length", equalTo("297")) + .withHeader("Accept", equalTo("*/*")).withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")).withRequestBody(equalToJson( + "{\"requests\" : [ \"?idsite=42&rec=1&action_name=First&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\", \"?idsite=42&rec=1&action_name=Second&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\", \"?idsite=42&rec=1&action_name=Third&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\" ]}"))); + + } + + @Test + void failsOnNegativeSiteId() { + + givenTrackerConfigurationWithDefaultSiteId(); + requestBuilder.siteId(-1); + + assertThatThrownBy(this::whenSendsRequestAsync).hasRootCauseInstanceOf( + IllegalArgumentException.class) + .hasRootCauseMessage("Site ID must not be negative"); + } + + @Test + void doesNotSendRequestAsyncIfTrackerConfigurationIsDisabled() { + givenTrackerConfigurationWithDefaultSiteId(); + trackerConfigurationBuilder.enabled(false); + + whenSendsRequestAsync(); + + assertThat(future).isNotCompletedExceptionally(); + wireMockServer.verify(0, getRequestedFor(urlPathEqualTo("/matomo.php"))); + + } + + @Test + void doesNotSendRequestIfTrackerConfigurationIsDisabled() { + givenTrackerConfigurationWithDefaultSiteId(); + trackerConfigurationBuilder.enabled(false); + + whenSendsSingleRequest(); + + wireMockServer.verify(0, getRequestedFor(urlPathEqualTo("/matomo.php"))); + } + + @Test + void doesNotSendBulkRequestIfTrackerConfigurationIsDisabled() { + givenTrackerConfigurationWithDefaultSiteId(); + trackerConfigurationBuilder.enabled(false); + + whenSendsBulkRequest(); + + wireMockServer.verify(0, postRequestedFor(urlPathEqualTo("/matomo.php"))); + } + + @Test + void doesNotSendBulkRequestAsyncIfTrackerConfigurationIsDisabled() { + givenTrackerConfigurationWithDefaultSiteId(); + trackerConfigurationBuilder.enabled(false); + + whenSendsBulkRequestAsync(); + + assertThat(future).isNotCompletedExceptionally(); + wireMockServer.verify(0, postRequestedFor(urlPathEqualTo("/matomo.php"))); + } + + @Test + void sendsRequestAsyncAndAcceptsCallback() throws Exception { + givenTrackerConfigurationWithDefaultSiteId(); + MatomoTracker tracker = new MatomoTracker(trackerConfigurationBuilder.build()); + AtomicBoolean success = new AtomicBoolean(); + CompletableFuture future = tracker.sendRequestAsync(requestBuilder.build(), v -> { + success.set(true); + }); + future.get(); + assertThat(future).isNotCompletedExceptionally(); + wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php"))); + assertThat(success).isTrue(); + } + + @Test + void sendsRequestsAsyncAndAcceptsCallback() throws Exception { + givenTrackerConfigurationWithDefaultSiteId(); + MatomoTracker tracker = new MatomoTracker(trackerConfigurationBuilder.build()); + AtomicBoolean success = new AtomicBoolean(); + CompletableFuture future = tracker.sendBulkRequestAsync(singleton(requestBuilder.build()), v -> { + success.set(true); + }); + future.get(); + assertThat(future).isNotCompletedExceptionally(); + wireMockServer.verify(postRequestedFor(urlPathEqualTo("/matomo.php"))); + assertThat(success).isTrue(); + } + +} diff --git a/src/test/java/org/matomo/java/tracking/PiwikDateTest.java b/src/test/java/org/matomo/java/tracking/PiwikDateTest.java index 0ba201d5..848b402d 100644 --- a/src/test/java/org/matomo/java/tracking/PiwikDateTest.java +++ b/src/test/java/org/matomo/java/tracking/PiwikDateTest.java @@ -1,56 +1,44 @@ -/* - * Matomo Java Tracker - * - * @link https://github.com/matomo/matomo-java-tracker - * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause - */ package org.matomo.java.tracking; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.piwik.java.tracking.PiwikDate; import java.util.TimeZone; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.assertj.core.api.Assertions.assertThat; + + +class PiwikDateTest { -/** - * @author brettcsorba - */ -public class PiwikDateTest { /** * Test of constructor, of class PiwikDate. */ @Test - public void testConstructor0() { + void testConstructor0() { PiwikDate date = new PiwikDate(); - - assertNotNull(date); + assertThat(date).isNotNull(); } @Test - public void testConstructor1() { + void testConstructor1() { PiwikDate date = new PiwikDate(1433186085092L); + assertThat(date).isNotNull(); + assertThat(date.getTime()).isEqualTo(1433186085092L); + } - assertNotNull(date); - - assertEquals("2015-06-01 19:14:45", date.toString()); - - date = new PiwikDate(1467437553000L); - - assertEquals("2016-07-02 05:32:33", date.toString()); + @Test + void testConstructor2() { + PiwikDate date = new PiwikDate(1467437553000L); + assertThat(date.getTime()).isEqualTo(1467437553000L); } /** * Test of setTimeZone method, of class PiwikDate. */ @Test - public void testSetTimeZone() { + void testSetTimeZone() { PiwikDate date = new PiwikDate(1433186085092L); - date.setTimeZone(TimeZone.getTimeZone("America/New_York")); - - assertEquals("2015-06-01 15:14:45", date.toString()); + assertThat(date.getTime()).isEqualTo(1433186085092L); } - } diff --git a/src/test/java/org/matomo/java/tracking/PiwikLocaleTest.java b/src/test/java/org/matomo/java/tracking/PiwikLocaleTest.java index 1931e5e6..318e111d 100644 --- a/src/test/java/org/matomo/java/tracking/PiwikLocaleTest.java +++ b/src/test/java/org/matomo/java/tracking/PiwikLocaleTest.java @@ -1,71 +1,30 @@ -/* - * Piwik Java Tracker - * - * @link https://github.com/matomo/matomo-java-tracker - * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause - */ package org.matomo.java.tracking; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.piwik.java.tracking.PiwikLocale; import java.util.Locale; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; -/** - * @author brettcsorba - */ -public class PiwikLocaleTest { - PiwikLocale locale; +class PiwikLocaleTest { - public PiwikLocaleTest() { - } - - @BeforeClass - public static void setUpClass() { - } - - @AfterClass - public static void tearDownClass() { - } - - @Before - public void setUp() { - locale = new PiwikLocale(Locale.US); - } - - @After - public void tearDown() { - } - - /** - * Test of getLocale method, of class PiwikLocale. - */ - @Test - public void testConstructor() { - assertEquals(Locale.US, locale.getLocale()); - } + private final PiwikLocale locale = new PiwikLocale(Locale.US); /** * Test of setLocale method, of class PiwikLocale. */ @Test - public void testLocale() { + void testLocale() { locale.setLocale(Locale.GERMANY); - assertEquals(Locale.GERMANY, locale.getLocale()); + assertThat(locale.getLocale()).isEqualTo(Locale.GERMAN); } /** * Test of toString method, of class PiwikLocale. */ @Test - public void testToString() { - assertEquals("us", locale.toString()); + void testToString() { + assertThat(locale).hasToString("us"); } - } diff --git a/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java b/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java index e50fc017..245bf4e3 100644 --- a/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java +++ b/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java @@ -1,523 +1,349 @@ -/* - * Piwik Java Tracker - * - * @link https://github.com/matomo/matomo-java-tracker - * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause - */ package org.matomo.java.tracking; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.piwik.java.tracking.PiwikDate; -import org.piwik.java.tracking.PiwikLocale; -import org.piwik.java.tracking.PiwikRequest; +import static java.time.temporal.ChronoUnit.MINUTES; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.fail; +import static org.assertj.core.api.Assertions.within; import java.net.URL; import java.nio.charset.Charset; +import java.time.Instant; +import java.time.ZonedDateTime; import java.util.List; import java.util.Locale; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.matomo.java.tracking.parameters.AcceptLanguage; +import org.matomo.java.tracking.parameters.DeviceResolution; +import org.matomo.java.tracking.parameters.RandomValue; +import org.matomo.java.tracking.parameters.VisitorId; +import org.piwik.java.tracking.PiwikDate; +import org.piwik.java.tracking.PiwikLocale; +import org.piwik.java.tracking.PiwikRequest; + +class PiwikRequestTest { -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -/** - * @author brettcsorba - */ -public class PiwikRequestTest { private PiwikRequest request; - @Before - public void setUp() throws Exception { + @BeforeEach + void setUp() throws Exception { request = new PiwikRequest(3, new URL("http://test.com")); } - @After - public void tearDown() { - } - @Test - public void testConstructor() throws Exception { + void testConstructor() throws Exception { request = new PiwikRequest(3, new URL("http://test.com")); - assertEquals(Integer.valueOf(3), request.getSiteId()); - assertTrue(request.getRequired()); - assertEquals(new URL("http://test.com"), request.getActionUrl()); - assertNotNull(request.getVisitorId()); - assertNotNull(request.getRandomValue()); - assertEquals("1", request.getApiVersion()); - assertFalse(request.getResponseAsImage()); + assertThat(request.getSiteId()).isEqualTo(Integer.valueOf(3)); + assertThat(request.getRequired()).isTrue(); + assertThat(request.getActionUrl()).isEqualTo("http://test.com"); + assertThat(request.getVisitorId()).isNotNull(); + assertThat(request.getRandomValue()).isNotNull(); + assertThat(request.getApiVersion()).isEqualTo("1"); + assertThat(request.getResponseAsImage()).isFalse(); } /** * Test of getActionName method, of class PiwikRequest. */ @Test - public void testActionName() { + void testActionName() { request.setActionName("action"); - assertEquals("action", request.getActionName()); + assertThat(request.getActionName()).isEqualTo("action"); request.setActionName(null); - assertNull(request.getActionName()); - } - - /** - * Test of getActionTime method, of class PiwikRequest. - */ - @Test - public void testActionTime() { - request.setActionTime(1000L); - assertEquals(Long.valueOf(1000L), request.getActionTime()); + assertThat(request.getActionName()).isNull(); } /** * Test of getActionUrl method, of class PiwikRequest. */ @Test - public void testActionUrl() throws Exception { - request.setActionUrl((String) null); - assertNull(request.getActionUrl()); - assertNull(request.getActionUrlAsString()); - - URL url = new URL("http://action.com"); - request.setActionUrl(url); - assertEquals(url, request.getActionUrl()); - assertEquals("http://action.com", request.getActionUrlAsString()); - - request.setActionUrlWithString(null); - assertNull(request.getActionUrl()); - assertNull(request.getActionUrlAsString()); - - request.setActionUrlWithString("http://actionstring.com"); - assertEquals("http://actionstring.com", request.getActionUrlAsString()); - assertEquals(new URL("http://actionstring.com"), request.getActionUrl()); + void testActionUrl() { + request.setActionUrl(null); + assertThat(request.getActionUrl()).isNull(); + request.setActionUrl("http://action.com"); + assertThat(request.getActionUrl()).isEqualTo("http://action.com"); } /** * Test of getApiVersion method, of class PiwikRequest. */ @Test - public void testApiVersion() { + void testApiVersion() { request.setApiVersion("2"); - assertEquals("2", request.getApiVersion()); + assertThat(request.getApiVersion()).isEqualTo("2"); } - /** - * Test of getAuthToken method, of class PiwikRequest. - */ @Test - public void testAuthTokenTT() { - try { - request.setAuthToken("1234"); - fail("Exception should have been thrown."); - } catch (IllegalArgumentException e) { - assertEquals("1234 is not 32 characters long.", e.getLocalizedMessage()); - } - } - - @Test - public void testAuthTokenTF() { + void testAuthTokenTF() { request.setAuthToken("12345678901234567890123456789012"); - assertEquals("12345678901234567890123456789012", request.getAuthToken()); + assertThat(request.getAuthToken()).isEqualTo("12345678901234567890123456789012"); } @Test - public void testAuthTokenF() { + void testAuthTokenF() { request.setAuthToken("12345678901234567890123456789012"); request.setAuthToken(null); - assertNull(request.getAuthToken()); - } - - /** - * Test of verifyAuthTokenSet method, of class PiwikRequest. - */ - @Test - public void testVerifyAuthTokenSet() { - try { - request.verifyAuthTokenSet(); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("AuthToken must be set before this value can be set.", e.getLocalizedMessage()); - } + assertThat(request.getAuthToken()).isNull(); } /** * Test of getCampaignKeyword method, of class PiwikRequest. */ @Test - public void testCampaignKeyword() { + void testCampaignKeyword() { request.setCampaignKeyword("keyword"); - assertEquals("keyword", request.getCampaignKeyword()); + assertThat(request.getCampaignKeyword()).isEqualTo("keyword"); } /** * Test of getCampaignName method, of class PiwikRequest. */ @Test - public void testCampaignName() { + void testCampaignName() { request.setCampaignName("name"); - assertEquals("name", request.getCampaignName()); + assertThat(request.getCampaignName()).isEqualTo("name"); } /** * Test of getCharacterSet method, of class PiwikRequest. */ @Test - public void testCharacterSet() { + void testCharacterSet() { Charset charset = Charset.defaultCharset(); request.setCharacterSet(charset); - assertEquals(charset, request.getCharacterSet()); + assertThat(request.getCharacterSet()).isEqualTo(charset); } /** * Test of getContentInteraction method, of class PiwikRequest. */ @Test - public void testContentInteraction() { + void testContentInteraction() { request.setContentInteraction("interaction"); - assertEquals("interaction", request.getContentInteraction()); + assertThat(request.getContentInteraction()).isEqualTo("interaction"); } /** * Test of getContentName method, of class PiwikRequest. */ @Test - public void testContentName() { + void testContentName() { request.setContentName("name"); - assertEquals("name", request.getContentName()); + assertThat(request.getContentName()).isEqualTo("name"); } /** * Test of getContentPiece method, of class PiwikRequest. */ @Test - public void testContentPiece() { + void testContentPiece() { request.setContentPiece("piece"); - assertEquals("piece", request.getContentPiece()); + assertThat(request.getContentPiece()).isEqualTo("piece"); } /** * Test of getContentTarget method, of class PiwikRequest. */ @Test - public void testContentTarget() throws Exception { - URL url = new URL("http://target.com"); - request.setContentTarget(url); - assertEquals(url, request.getContentTarget()); - assertEquals("http://target.com", request.getContentTargetAsString()); - - request.setContentTargetWithString("http://targetstring.com"); - assertEquals("http://targetstring.com", request.getContentTargetAsString()); - assertEquals(new URL("http://targetstring.com"), request.getContentTarget()); - + void testContentTarget() { + request.setContentTarget("http://target.com"); + assertThat(request.getContentTarget()).isEqualTo("http://target.com"); } /** * Test of getCurrentHour method, of class PiwikRequest. */ @Test - public void testCurrentHour() { + void testCurrentHour() { request.setCurrentHour(1); - assertEquals(Integer.valueOf(1), request.getCurrentHour()); + assertThat(request.getCurrentHour()).isEqualTo(Integer.valueOf(1)); } /** * Test of getCurrentMinute method, of class PiwikRequest. */ @Test - public void testCurrentMinute() { + void testCurrentMinute() { request.setCurrentMinute(2); - assertEquals(Integer.valueOf(2), request.getCurrentMinute()); + assertThat(request.getCurrentMinute()).isEqualTo(Integer.valueOf(2)); } /** * Test of getCurrentSecond method, of class PiwikRequest. */ @Test - public void testCurrentSecond() { + void testCurrentSecond() { request.setCurrentSecond(3); - assertEquals(Integer.valueOf(3), request.getCurrentSecond()); + assertThat(request.getCurrentSecond()).isEqualTo(Integer.valueOf(3)); } /** * Test of getCustomTrackingParameter method, of class PiwikRequest. */ @Test - public void testGetCustomTrackingParameter_T() { + void testGetCustomTrackingParameter_T() { try { request.getCustomTrackingParameter(null); fail("Exception should have been thrown."); } catch (NullPointerException e) { - assertEquals("key is marked non-null but is null", e.getLocalizedMessage()); + assertThat(e.getLocalizedMessage()).isEqualTo("key is marked non-null but is null"); } } @Test - public void testGetCustomTrackingParameter_FT() { - assertTrue(request.getCustomTrackingParameter("key").isEmpty()); + void testGetCustomTrackingParameter_FT() { + assertThat(request.getCustomTrackingParameter("key")).isEmpty(); } @Test - public void testSetCustomTrackingParameter_T() { + void testSetCustomTrackingParameter_T() { try { request.setCustomTrackingParameter(null, null); fail("Exception should have been thrown."); } catch (NullPointerException e) { - assertEquals("key is marked non-null but is null", e.getLocalizedMessage()); + assertThat(e.getLocalizedMessage()).isEqualTo("key is marked non-null but is null"); } } @Test - public void testSetCustomTrackingParameter_F() { + void testSetCustomTrackingParameter1() { request.setCustomTrackingParameter("key", "value"); - List l = request.getCustomTrackingParameter("key"); - assertEquals(1, l.size()); - assertEquals("value", l.get(0)); + List l = request.getCustomTrackingParameter("key"); + assertThat(l).hasSize(1); + assertThat(l.get(0)).isEqualTo("value"); + request.setCustomTrackingParameter("key", "value2"); + } + @Test + void testSetCustomTrackingParameter2() { request.setCustomTrackingParameter("key", "value2"); + List l = request.getCustomTrackingParameter("key"); + assertThat(l).hasSize(1); + assertThat(l.get(0)).isEqualTo("value2"); + request.setCustomTrackingParameter("key", null); l = request.getCustomTrackingParameter("key"); - assertEquals(1, l.size()); - assertEquals("value2", l.get(0)); + assertThat(l).isEmpty(); + } + @Test + void testSetCustomTrackingParameter3() { request.setCustomTrackingParameter("key", null); - l = request.getCustomTrackingParameter("key"); - assertTrue(l.isEmpty()); + List l = request.getCustomTrackingParameter("key"); + assertThat(l).isEmpty(); } @Test - public void testAddCustomTrackingParameter_T() { + void testAddCustomTrackingParameter_T() { try { request.addCustomTrackingParameter(null, null); fail("Exception should have been thrown."); } catch (NullPointerException e) { - assertEquals("key is marked non-null but is null", e.getLocalizedMessage()); + assertThat(e.getLocalizedMessage()).isEqualTo("key is marked non-null but is null"); } } @Test - public void testAddCustomTrackingParameter_FT() { + void testAddCustomTrackingParameter_FT() { try { request.addCustomTrackingParameter("key", null); fail("Exception should have been thrown."); } catch (NullPointerException e) { - assertEquals("value is marked non-null but is null", e.getLocalizedMessage()); + assertThat(e.getLocalizedMessage()).isEqualTo("value is marked non-null but is null"); } } @Test - public void testAddCustomTrackingParameter_FF() { + void testAddCustomTrackingParameter1() { request.addCustomTrackingParameter("key", "value"); - List l = request.getCustomTrackingParameter("key"); - assertEquals(1, l.size()); - assertEquals("value", l.get(0)); + List l = request.getCustomTrackingParameter("key"); + assertThat(l).hasSize(1); + assertThat(l.get(0)).isEqualTo("value"); + } + @Test + void testAddCustomTrackingParameter2() { + request.addCustomTrackingParameter("key", "value"); request.addCustomTrackingParameter("key", "value2"); - l = request.getCustomTrackingParameter("key"); - assertEquals(2, l.size()); - assertTrue(l.contains("value")); - assertTrue(l.contains("value2")); + List l = request.getCustomTrackingParameter("key"); + assertThat(l).hasSize(2) + .contains(new String[] {"value"}) + .contains(new String[] {"value2"}); } @Test - public void testClearCustomTrackingParameter() { + void testClearCustomTrackingParameter() { request.setCustomTrackingParameter("key", "value"); request.clearCustomTrackingParameter(); - List l = request.getCustomTrackingParameter("key"); - assertTrue(l.isEmpty()); + List l = request.getCustomTrackingParameter("key"); + assertThat(l).isEmpty(); } /** * Test of getDeviceResolution method, of class PiwikRequest. */ @Test - public void testDeviceResolution() { - request.setDeviceResolution("1x2"); - assertEquals("1x2", request.getDeviceResolution()); + void testDeviceResolution() { + request.setDeviceResolution(DeviceResolution.fromString("100x200")); + assertThat(request.getDeviceResolution()).hasToString("100x200"); } /** * Test of getDownloadUrl method, of class PiwikRequest. */ @Test - public void testDownloadUrl() throws Exception { - URL url = new URL("http://download.com"); - request.setDownloadUrl(url); - assertEquals(url, request.getDownloadUrl()); - assertEquals("http://download.com", request.getDownloadUrlAsString()); - - request.setDownloadUrlWithString("http://downloadstring.com"); - assertEquals("http://downloadstring.com", request.getDownloadUrlAsString()); - assertEquals(new URL("http://downloadstring.com"), request.getDownloadUrl()); + void testDownloadUrl() { + request.setDownloadUrl("http://download.com"); + assertThat(request.getDownloadUrl()).isEqualTo("http://download.com"); } /** * Test of enableEcommerce method, of class PiwikRequest. */ @Test - public void testEnableEcommerce() { - request.enableEcommerce(); - assertEquals(Integer.valueOf(0), request.getGoalId()); - } - - /** - * Test of verifyEcommerceEnabled method, of class PiwikRequest. - */ - @Test - public void testVerifyEcommerceEnabledT() { - try { - request.verifyEcommerceEnabled(); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); - } - } - - @Test - public void testVerifyEcommerceEnabledFT() { - try { - request.setGoalId(1); - request.verifyEcommerceEnabled(); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); - } - } - - @Test - public void testVerifyEcommerceEnabledFF() { - request.enableEcommerce(); - request.verifyEcommerceEnabled(); - } - - /** - * Test of verifyEcommerceState method, of class PiwikRequest. - */ - @Test - public void testVerifyEcommerceStateE() { - try { - request.verifyEcommerceState(); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); - } - } - - @Test - public void testVerifyEcommerceStateT() { - try { - request.enableEcommerce(); - request.verifyEcommerceState(); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("EcommerceId must be set before this value can be set.", - e.getLocalizedMessage()); - } - } - - @Test - public void testVerifyEcommerceStateFT() { - try { - request.enableEcommerce(); - request.setEcommerceId("1"); - request.verifyEcommerceState(); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("EcommerceRevenue must be set before this value can be set.", - e.getLocalizedMessage()); - } - } - - @Test - public void testVerifyEcommerceStateFF() { + void testEnableEcommerce() { request.enableEcommerce(); - request.setEcommerceId("1"); - request.setEcommerceRevenue(2.0); - request.verifyEcommerceState(); + assertThat(request.getGoalId()).isEqualTo(Integer.valueOf(0)); } /** * Test of getEcommerceDiscount method, of class PiwikRequest. */ @Test - public void testEcommerceDiscountT() { + void testEcommerceDiscountT() { request.enableEcommerce(); request.setEcommerceId("1"); request.setEcommerceRevenue(2.0); request.setEcommerceDiscount(1.0); - - assertEquals(Double.valueOf(1.0), request.getEcommerceDiscount()); + assertThat(request.getEcommerceDiscount()).isEqualTo(Double.valueOf(1.0)); } - @Test - public void testEcommerceDiscountTE() { - try { - request.setEcommerceDiscount(1.0); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); - } - } @Test - public void testEcommerceDiscountF() { + void testEcommerceDiscountF() { request.setEcommerceDiscount(null); - - assertNull(request.getEcommerceDiscount()); + assertThat(request.getEcommerceDiscount()).isNull(); } /** * Test of getEcommerceId method, of class PiwikRequest. */ @Test - public void testEcommerceIdT() { + void testEcommerceIdT() { request.enableEcommerce(); request.setEcommerceId("1"); - - assertEquals("1", request.getEcommerceId()); - } - - @Test - public void testEcommerceIdTE() { - try { - request.setEcommerceId("1"); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); - } + assertThat(request.getEcommerceId()).isEqualTo("1"); } @Test - public void testEcommerceIdF() { + void testEcommerceIdF() { request.setEcommerceId(null); - - assertNull(request.getEcommerceId()); - } - - /** - * Test of getEcommerceItem method, of class PiwikRequest. - */ - @Test - public void testEcommerceItemE() { - try { - EcommerceItem item = new EcommerceItem("sku", "name", "category", 1.0, 2); - request.addEcommerceItem(item); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); - } + assertThat(request.getEcommerceId()).isNull(); } @Test - public void testEcommerceItemE2() { + void testEcommerceItemE2() { try { request.enableEcommerce(); request.setEcommerceId("1"); @@ -525,1013 +351,603 @@ public void testEcommerceItemE2() { request.addEcommerceItem(null); fail("Exception should have been thrown."); } catch (NullPointerException e) { - assertEquals("item is marked non-null but is null", e.getLocalizedMessage()); + assertThat(e.getLocalizedMessage()).isEqualTo("item is marked non-null but is null"); } } @Test - public void testEcommerceItem() { - assertNull(request.getEcommerceItem(0)); - + void testEcommerceItem() { + assertThat(request.getEcommerceItem(0)).isNull(); EcommerceItem item = new EcommerceItem("sku", "name", "category", 1.0, 2); request.enableEcommerce(); request.setEcommerceId("1"); request.setEcommerceRevenue(2.0); request.addEcommerceItem(item); - - assertEquals(item, request.getEcommerceItem(0)); - + assertThat(request.getEcommerceItem(0)).isEqualTo(item); request.clearEcommerceItems(); - assertNull(request.getEcommerceItem(0)); + assertThat(request.getEcommerceItem(0)).isNull(); } /** * Test of getEcommerceLastOrderTimestamp method, of class PiwikRequest. */ @Test - public void testEcommerceLastOrderTimestampT() { + void testEcommerceLastOrderTimestampT() { request.enableEcommerce(); request.setEcommerceId("1"); request.setEcommerceRevenue(2.0); - request.setEcommerceLastOrderTimestamp(1000L); - - assertEquals(Long.valueOf(1000L), request.getEcommerceLastOrderTimestamp()); - } - - @Test - public void testEcommerceLastOrderTimestampTE() { - try { - request.setEcommerceLastOrderTimestamp(1000L); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); - } + request.setEcommerceLastOrderTimestamp(Instant.ofEpochSecond(1000L)); + assertThat(request.getEcommerceLastOrderTimestamp()).isEqualTo("1970-01-01T00:16:40Z"); } @Test - public void testEcommerceLastOrderTimestampF() { + void testEcommerceLastOrderTimestampF() { request.setEcommerceLastOrderTimestamp(null); - - assertNull(request.getEcommerceLastOrderTimestamp()); + assertThat(request.getEcommerceLastOrderTimestamp()).isNull(); } /** * Test of getEcommerceRevenue method, of class PiwikRequest. */ @Test - public void testEcommerceRevenueT() { + void testEcommerceRevenueT() { request.enableEcommerce(); request.setEcommerceId("1"); request.setEcommerceRevenue(20.0); - - assertEquals(Double.valueOf(20.0), request.getEcommerceRevenue()); + assertThat(request.getEcommerceRevenue()).isEqualTo(Double.valueOf(20.0)); } - @Test - public void testEcommerceRevenueTE() { - try { - request.setEcommerceRevenue(20.0); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); - } - } @Test - public void testEcommerceRevenueF() { + void testEcommerceRevenueF() { request.setEcommerceRevenue(null); - - assertNull(request.getEcommerceRevenue()); + assertThat(request.getEcommerceRevenue()).isNull(); } /** * Test of getEcommerceShippingCost method, of class PiwikRequest. */ @Test - public void testEcommerceShippingCostT() { + void testEcommerceShippingCostT() { request.enableEcommerce(); request.setEcommerceId("1"); request.setEcommerceRevenue(2.0); request.setEcommerceShippingCost(20.0); - - assertEquals(Double.valueOf(20.0), request.getEcommerceShippingCost()); - } - - @Test - public void testEcommerceShippingCostTE() { - try { - request.setEcommerceShippingCost(20.0); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); - } + assertThat(request.getEcommerceShippingCost()).isEqualTo(Double.valueOf(20.0)); } @Test - public void testEcommerceShippingCostF() { + void testEcommerceShippingCostF() { request.setEcommerceShippingCost(null); - - assertNull(request.getEcommerceShippingCost()); + assertThat(request.getEcommerceShippingCost()).isNull(); } /** * Test of getEcommerceSubtotal method, of class PiwikRequest. */ @Test - public void testEcommerceSubtotalT() { + void testEcommerceSubtotalT() { request.enableEcommerce(); request.setEcommerceId("1"); request.setEcommerceRevenue(2.0); request.setEcommerceSubtotal(20.0); - - assertEquals(Double.valueOf(20.0), request.getEcommerceSubtotal()); - } - - @Test - public void testEcommerceSubtotalTE() { - try { - request.setEcommerceSubtotal(20.0); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); - } + assertThat(request.getEcommerceSubtotal()).isEqualTo(Double.valueOf(20.0)); } @Test - public void testEcommerceSubtotalF() { + void testEcommerceSubtotalF() { request.setEcommerceSubtotal(null); - - assertNull(request.getEcommerceSubtotal()); + assertThat(request.getEcommerceSubtotal()).isNull(); } /** * Test of getEcommerceTax method, of class PiwikRequest. */ @Test - public void testEcommerceTaxT() { + void testEcommerceTaxT() { request.enableEcommerce(); request.setEcommerceId("1"); request.setEcommerceRevenue(2.0); request.setEcommerceTax(20.0); - - assertEquals(Double.valueOf(20.0), request.getEcommerceTax()); - } - - @Test - public void testEcommerceTaxTE() { - try { - request.setEcommerceTax(20.0); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); - } + assertThat(request.getEcommerceTax()).isEqualTo(Double.valueOf(20.0)); } @Test - public void testEcommerceTaxF() { + void testEcommerceTaxF() { request.setEcommerceTax(null); - - assertNull(request.getEcommerceTax()); + assertThat(request.getEcommerceTax()).isNull(); } /** * Test of getEventAction method, of class PiwikRequest. */ @Test - public void testEventAction() { + void testEventAction() { request.setEventAction("action"); - assertEquals("action", request.getEventAction()); + assertThat(request.getEventAction()).isEqualTo("action"); request.setEventAction(null); - assertNull(request.getEventAction()); - } - - @Test - public void testEventActionException() { - try { - request.setEventAction(""); - fail("Exception should have been thrown"); - } catch (IllegalArgumentException e) { - assertEquals("Value cannot be empty.", e.getLocalizedMessage()); - } + assertThat(request.getEventAction()).isNull(); } /** * Test of getEventCategory method, of class PiwikRequest. */ @Test - public void testEventCategory() { + void testEventCategory() { request.setEventCategory("category"); - assertEquals("category", request.getEventCategory()); + assertThat(request.getEventCategory()).isEqualTo("category"); } /** * Test of getEventName method, of class PiwikRequest. */ @Test - public void testEventName() { + void testEventName() { request.setEventName("name"); - assertEquals("name", request.getEventName()); + assertThat(request.getEventName()).isEqualTo("name"); } - /** * Test of getEventValue method, of class PiwikRequest. */ @Test - public void testEventValue() { - request.setEventValue(1); - assertEquals(1, request.getEventValue()); + void testEventValue() { + request.setEventValue(1.0); + assertThat(request.getEventValue()).isOne(); } /** * Test of getGoalId method, of class PiwikRequest. */ @Test - public void testGoalId() { + void testGoalId() { request.setGoalId(1); - assertEquals(Integer.valueOf(1), request.getGoalId()); - } - - /** - * Test of getGoalRevenue method, of class PiwikRequest. - */ - @Test - public void testGoalRevenueTT() { - try { - request.setGoalRevenue(20.0); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("GoalId must be set before GoalRevenue can be set.", - e.getLocalizedMessage()); - } - } - - @Test - public void testGoalRevenueTF() { - request.setGoalId(1); - request.setGoalRevenue(20.0); - - assertEquals(Double.valueOf(20.0), request.getGoalRevenue()); - } - - @Test - public void testGoalRevenueF() { - request.setGoalRevenue(null); - - assertNull(request.getGoalRevenue()); + assertThat(request.getGoalId()).isEqualTo(Integer.valueOf(1)); } /** * Test of getHeaderAcceptLanguage method, of class PiwikRequest. */ @Test - public void testHeaderAcceptLanguage() { - request.setHeaderAcceptLanguage("language"); - assertEquals("language", request.getHeaderAcceptLanguage()); + void testHeaderAcceptLanguage() { + request.setHeaderAcceptLanguage(AcceptLanguage.fromHeader("en")); + assertThat(request.getHeaderAcceptLanguage()).hasToString("en"); } /** * Test of getHeaderUserAgent method, of class PiwikRequest. */ @Test - public void testHeaderUserAgent() { + void testHeaderUserAgent() { request.setHeaderUserAgent("agent"); - assertEquals("agent", request.getHeaderUserAgent()); + assertThat(request.getHeaderUserAgent()).isEqualTo("agent"); } /** * Test of getNewVisit method, of class PiwikRequest. */ @Test - public void testNewVisit() { + void testNewVisit() { request.setNewVisit(true); - assertEquals(true, request.getNewVisit()); + assertThat(request.getNewVisit()).isTrue(); request.setNewVisit(null); - assertNull(request.getNewVisit()); + assertThat(request.getNewVisit()).isNull(); } /** * Test of getOutlinkUrl method, of class PiwikRequest. */ @Test - public void testOutlinkUrl() throws Exception { - URL url = new URL("http://outlink.com"); - request.setOutlinkUrl(url); - assertEquals(url, request.getOutlinkUrl()); - assertEquals("http://outlink.com", request.getOutlinkUrlAsString()); - - request.setOutlinkUrlWithString("http://outlinkstring.com"); - assertEquals("http://outlinkstring.com", request.getOutlinkUrlAsString()); - assertEquals(new URL("http://outlinkstring.com"), request.getOutlinkUrl()); - + void testOutlinkUrl() { + request.setOutlinkUrl("http://outlink.com"); + assertThat(request.getOutlinkUrl()).isEqualTo("http://outlink.com"); } /** * Test of getPageCustomVariable method, of class PiwikRequest. */ @Test - public void testPageCustomVariableStringStringE() { - try { - request.setPageCustomVariable(null, null); - fail("Exception should have been thrown"); - } catch (NullPointerException e) { - assertEquals("key is marked non-null but is null", e.getLocalizedMessage()); - } - } - - @Test - public void testPageCustomVariableStringStringE2() { - try { - request.setPageCustomVariable(null, "pageVal"); - fail("Exception should have been thrown"); - } catch (NullPointerException e) { - assertEquals("key is marked non-null but is null", e.getLocalizedMessage()); - } - } - - @Test - public void testPageCustomVariableStringStringE3() { - try { - request.getPageCustomVariable(null); - fail("Exception should have been thrown"); - } catch (NullPointerException e) { - assertEquals("key is marked non-null but is null", e.getLocalizedMessage()); - } + void testPageCustomVariableStringStringE() { + assertThatThrownBy(() -> request.setPageCustomVariable(null, null)); } @Test - public void testPageCustomVariableStringString() { - assertNull(request.getPageCustomVariable("pageKey")); - request.setPageCustomVariable("pageKey", "pageVal"); - assertEquals("pageVal", request.getPageCustomVariable("pageKey")); - request.setPageCustomVariable("pageKey", null); - assertNull(request.getPageCustomVariable("pageKey")); - request.setPageCustomVariable("pageKey", "pageVal"); - assertEquals("pageVal", request.getPageCustomVariable("pageKey")); + void testPageCustomVariableStringStringE2() { + assertThatThrownBy(() -> request.setPageCustomVariable(null, "pageVal")); } @Test - public void testPageCustomVariableCustomVariable() { - assertNull(request.getPageCustomVariable(1)); + void testPageCustomVariableCustomVariable() { + assertThat(request.getPageCustomVariable(1)).isNull(); CustomVariable cv = new CustomVariable("pageKey", "pageVal"); request.setPageCustomVariable(cv, 1); - assertEquals(cv, request.getPageCustomVariable(1)); + assertThat(request.getPageCustomVariable(1)).isEqualTo(cv); request.setPageCustomVariable(null, 1); - assertNull(request.getPageCustomVariable(1)); + assertThat(request.getPageCustomVariable(1)).isNull(); request.setPageCustomVariable(cv, 2); - assertEquals(cv, request.getPageCustomVariable(2)); + assertThat(request.getPageCustomVariable(2)).isEqualTo(cv); } /** * Test of getPluginDirector method, of class PiwikRequest. */ @Test - public void testPluginDirector() { + void testPluginDirector() { request.setPluginDirector(true); - assertEquals(true, request.getPluginDirector()); + assertThat(request.getPluginDirector()).isTrue(); } /** * Test of getPluginFlash method, of class PiwikRequest. */ @Test - public void testPluginFlash() { + void testPluginFlash() { request.setPluginFlash(true); - assertEquals(true, request.getPluginFlash()); + assertThat(request.getPluginFlash()).isTrue(); } /** * Test of getPluginGears method, of class PiwikRequest. */ @Test - public void testPluginGears() { + void testPluginGears() { request.setPluginGears(true); - assertEquals(true, request.getPluginGears()); + assertThat(request.getPluginGears()).isTrue(); } /** * Test of getPluginJava method, of class PiwikRequest. */ @Test - public void testPluginJava() { + void testPluginJava() { request.setPluginJava(true); - assertEquals(true, request.getPluginJava()); + assertThat(request.getPluginJava()).isTrue(); } /** * Test of getPluginPDF method, of class PiwikRequest. */ @Test - public void testPluginPDF() { + void testPluginPDF() { request.setPluginPDF(true); - assertEquals(true, request.getPluginPDF()); + assertThat(request.getPluginPDF()).isTrue(); } /** * Test of getPluginQuicktime method, of class PiwikRequest. */ @Test - public void testPluginQuicktime() { + void testPluginQuicktime() { request.setPluginQuicktime(true); - assertEquals(true, request.getPluginQuicktime()); + assertThat(request.getPluginQuicktime()).isTrue(); } /** * Test of getPluginRealPlayer method, of class PiwikRequest. */ @Test - public void testPluginRealPlayer() { + void testPluginRealPlayer() { request.setPluginRealPlayer(true); - assertEquals(true, request.getPluginRealPlayer()); + assertThat(request.getPluginRealPlayer()).isTrue(); } /** * Test of getPluginSilverlight method, of class PiwikRequest. */ @Test - public void testPluginSilverlight() { + void testPluginSilverlight() { request.setPluginSilverlight(true); - assertEquals(true, request.getPluginSilverlight()); + assertThat(request.getPluginSilverlight()).isTrue(); } /** * Test of getPluginWindowsMedia method, of class PiwikRequest. */ @Test - public void testPluginWindowsMedia() { + void testPluginWindowsMedia() { request.setPluginWindowsMedia(true); - assertEquals(true, request.getPluginWindowsMedia()); + assertThat(request.getPluginWindowsMedia()).isTrue(); } /** * Test of getRandomValue method, of class PiwikRequest. */ @Test - public void testRandomValue() { - request.setRandomValue("value"); - assertEquals("value", request.getRandomValue()); + void testRandomValue() { + request.setRandomValue(RandomValue.fromString("value")); + assertThat(request.getRandomValue()).hasToString("value"); } /** * Test of setReferrerUrl method, of class PiwikRequest. */ @Test - public void testReferrerUrl() throws Exception { - URL url = new URL("http://referrer.com"); - request.setReferrerUrl(url); - assertEquals(url, request.getReferrerUrl()); - assertEquals("http://referrer.com", request.getReferrerUrlAsString()); - - request.setReferrerUrlWithString("http://referrerstring.com"); - assertEquals("http://referrerstring.com", request.getReferrerUrlAsString()); - assertEquals(new URL("http://referrerstring.com"), request.getReferrerUrl()); - + void testReferrerUrl() { + request.setReferrerUrl("http://referrer.com"); + assertThat(request.getReferrerUrl()).isEqualTo("http://referrer.com"); } /** * Test of getRequestDatetime method, of class PiwikRequest. */ @Test - public void testRequestDatetimeTTT() { + void testRequestDatetimeTTT() { request.setAuthToken("12345678901234567890123456789012"); PiwikDate date = new PiwikDate(1000L); request.setRequestDatetime(date); - - assertEquals(date, request.getRequestDatetime()); + assertThat(request.getRequestDatetime().getTime()).isEqualTo(1000L); } - @Test - public void testRequestDatetimeTTF() { - try { - PiwikDate date = new PiwikDate(1000L); - request.setRequestDatetime(date); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("Because you are trying to set RequestDatetime for a time greater than 4 hours ago, AuthToken must be set first.", - e.getLocalizedMessage()); - } - } @Test - public void testRequestDatetimeTF() { - PiwikDate date = new PiwikDate(); - request.setRequestDatetime(date); - assertEquals(date, request.getRequestDatetime()); + void testRequestDatetimeTF() { + request.setRequestDatetime(new PiwikDate()); + assertThat(request.getRequestDatetime().getZonedDateTime()).isCloseTo(ZonedDateTime.now(), within(2, MINUTES)); } @Test - public void testRequestDatetimeF() { + void testRequestDatetimeF() { PiwikDate date = new PiwikDate(); request.setRequestDatetime(date); request.setRequestDatetime(null); - assertNull(request.getRequestDatetime()); + assertThat(request.getRequestDatetime()).isNull(); } /** * Test of getRequired method, of class PiwikRequest. */ @Test - public void testRequired() { + void testRequired() { request.setRequired(false); - assertEquals(false, request.getRequired()); + assertThat(request.getRequired()).isFalse(); } /** * Test of getResponseAsImage method, of class PiwikRequest. */ @Test - public void testResponseAsImage() { + void testResponseAsImage() { request.setResponseAsImage(true); - assertEquals(true, request.getResponseAsImage()); - } - - /** - * Test of getSearchCategory method, of class PiwikRequest. - */ - @Test - public void testSearchCategoryTT() { - try { - request.setSearchCategory("category"); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("SearchQuery must be set before SearchCategory can be set.", - e.getLocalizedMessage()); - } + assertThat(request.getResponseAsImage()).isTrue(); } @Test - public void testSearchCategoryTF() { + void testSearchCategoryTF() { request.setSearchQuery("query"); request.setSearchCategory("category"); - assertEquals("category", request.getSearchCategory()); + assertThat(request.getSearchCategory()).isEqualTo("category"); } @Test - public void testSearchCategoryF() { + void testSearchCategoryF() { request.setSearchCategory(null); - assertNull(request.getSearchCategory()); + assertThat(request.getSearchCategory()).isNull(); } /** * Test of getSearchQuery method, of class PiwikRequest. */ @Test - public void testSearchQuery() { + void testSearchQuery() { request.setSearchQuery("query"); - assertEquals("query", request.getSearchQuery()); - } - - /** - * Test of getSearchResultsCount method, of class PiwikRequest. - */ - @Test - public void testSearchResultsCountTT() { - try { - request.setSearchResultsCount(100L); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("SearchQuery must be set before SearchResultsCount can be set.", - e.getLocalizedMessage()); - } + assertThat(request.getSearchQuery()).isEqualTo("query"); } @Test - public void testSearchResultsCountTF() { + void testSearchResultsCountTF() { request.setSearchQuery("query"); request.setSearchResultsCount(100L); - assertEquals(Long.valueOf(100L), request.getSearchResultsCount()); + assertThat(request.getSearchResultsCount()).isEqualTo(Long.valueOf(100L)); } @Test - public void testSearchResultsCountF() { + void testSearchResultsCountF() { request.setSearchResultsCount(null); - assertNull(request.getSearchResultsCount()); + assertThat(request.getSearchResultsCount()).isNull(); } /** * Test of getSiteId method, of class PiwikRequest. */ @Test - public void testSiteId() { + void testSiteId() { request.setSiteId(2); - assertEquals(Integer.valueOf(2), request.getSiteId()); + assertThat(request.getSiteId()).isEqualTo(Integer.valueOf(2)); } /** * Test of setTrackBotRequest method, of class PiwikRequest. */ @Test - public void testTrackBotRequests() { + void testTrackBotRequests() { request.setTrackBotRequests(true); - assertEquals(true, request.getTrackBotRequests()); + assertThat(request.getTrackBotRequests()).isTrue(); } - /** - * Test of getUserrCustomVariable method, of class PiwikRequest. + * Test of getUserCustomVariable method, of class PiwikRequest. */ @Test - public void testUserCustomVariableStringString() { + void testUserCustomVariableStringString() { request.setUserCustomVariable("userKey", "userValue"); - assertEquals("userValue", request.getUserCustomVariable("userKey")); + assertThat(request.getUserCustomVariable("userKey")).isEqualTo("userValue"); } - @Test - public void testVisitCustomVariableCustomVariable() { - request.setRandomValue("random"); - request.setVisitorId("1234567890123456"); - - assertNull(request.getVisitCustomVariable(1)); - CustomVariable cv = new CustomVariable("visitKey", "visitVal"); - request.setVisitCustomVariable(cv, 1); - assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&_cvar={\"1\":[\"visitKey\",\"visitVal\"]}", request.getQueryString()); - - request.setUserCustomVariable("key", "val"); - assertEquals(cv, request.getVisitCustomVariable(1)); - assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&_cvar={\"1\":[\"visitKey\",\"visitVal\"],\"2\":[\"key\",\"val\"]}", request.getQueryString()); - - request.setVisitCustomVariable(null, 1); - assertNull(request.getVisitCustomVariable(1)); - assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&_cvar={\"2\":[\"key\",\"val\"]}", request.getQueryString()); - - request.setVisitCustomVariable(cv, 2); - assertEquals(cv, request.getVisitCustomVariable(2)); - assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&_cvar={\"2\":[\"visitKey\",\"visitVal\"]}", request.getQueryString()); - - request.setUserCustomVariable("visitKey", null); - assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456", request.getQueryString()); - } /** * Test of getUserId method, of class PiwikRequest. */ @Test - public void testUserId() { + void testUserId() { request.setUserId("id"); - assertEquals("id", request.getUserId()); + assertThat(request.getUserId()).isEqualTo("id"); } /** * Test of getVisitorCity method, of class PiwikRequest. */ @Test - public void testVisitorCityT() { + void testVisitorCityT() { request.setAuthToken("12345678901234567890123456789012"); request.setVisitorCity("city"); - assertEquals("city", request.getVisitorCity()); - } - - @Test - public void testVisitorCityTE() { - try { - request.setVisitorCity("city"); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); - } + assertThat(request.getVisitorCity()).isEqualTo("city"); } @Test - public void testVisitorCityF() { + void testVisitorCityF() { request.setVisitorCity(null); - assertNull(request.getVisitorCity()); + assertThat(request.getVisitorCity()).isNull(); } /** * Test of getVisitorCountry method, of class PiwikRequest. */ @Test - public void testVisitorCountryT() { + void testVisitorCountryT() { PiwikLocale country = new PiwikLocale(Locale.US); request.setAuthToken("12345678901234567890123456789012"); request.setVisitorCountry(country); - - assertEquals(country, request.getVisitorCountry()); + assertThat(request.getVisitorCountry()).isEqualTo(country); } @Test - public void testVisitorCountryTE() { - try { - PiwikLocale country = new PiwikLocale(Locale.US); - request.setVisitorCountry(country); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); - } - } - - @Test - public void testVisitorCountryF() { + void testVisitorCountryF() { request.setVisitorCountry(null); - - assertNull(request.getVisitorCountry()); - } - - /** - * Test of getVisitorCustomId method, of class PiwikRequest. - */ - @Test - public void testVisitorCustomTT() { - try { - request.setVisitorCustomId("1"); - fail("Exception should have been thrown."); - } catch (IllegalArgumentException e) { - assertEquals("1 is not 16 characters long.", - e.getLocalizedMessage()); - } - } - - @Test - public void testVisitorCustomTFT() { - try { - request.setVisitorCustomId("1234567890abcdeg"); - fail("Exception should have been thrown."); - } catch (IllegalArgumentException e) { - assertEquals("1234567890abcdeg is not a hexadecimal string.", - e.getLocalizedMessage()); - } + assertThat(request.getVisitorCountry()).isNull(); } @Test - public void testVisitorCustomIdTFF() { - request.setVisitorCustomId("1234567890abcdef"); - assertEquals("1234567890abcdef", request.getVisitorCustomId()); + void testVisitorCustomTF() { + request.setVisitorCustomId(VisitorId.fromHex("1234567890abcdef")); + assertThat(request.getVisitorCustomId()).hasToString("1234567890abcdef"); } @Test - public void testVisitorCustomIdF() { - request.setVisitorCustomId("1234567890abcdef"); + void testVisitorCustomIdF() { + request.setVisitorCustomId(VisitorId.fromHex("1234567890abcdef")); request.setVisitorCustomId(null); - assertNull(request.getVisitorCustomId()); + assertThat(request.getVisitorCustomId()).isNull(); } /** * Test of getVisitorFirstVisitTimestamp method, of class PiwikRequest. */ @Test - public void testVisitorFirstVisitTimestamp() { - request.setVisitorFirstVisitTimestamp(1000L); - assertEquals(Long.valueOf(1000L), request.getVisitorFirstVisitTimestamp()); - } - - /** - * Test of getVisitorId method, of class PiwikRequest. - */ - @Test - public void testVisitorIdTT() { - try { - request.setVisitorId("1"); - fail("Exception should have been thrown."); - } catch (IllegalArgumentException e) { - assertEquals("1 is not 16 characters long.", - e.getLocalizedMessage()); - } + void testVisitorFirstVisitTimestamp() { + request.setVisitorFirstVisitTimestamp(Instant.parse("2021-03-10T10:22:22.123Z")); + assertThat(request.getVisitorFirstVisitTimestamp()).isEqualTo("2021-03-10T10:22:22.123Z"); } @Test - public void testVisitorIdTFT() { + void testVisitorIdTFT() { try { - request.setVisitorId("1234567890abcdeg"); + request.setVisitorId(VisitorId.fromHex("1234567890abcdeg")); fail("Exception should have been thrown."); } catch (IllegalArgumentException e) { - assertEquals("1234567890abcdeg is not a hexadecimal string.", - e.getLocalizedMessage()); + assertThat(e.getLocalizedMessage()).isEqualTo("Input must be a valid hex string"); } } @Test - public void testVisitorIdTFF() { - request.setVisitorId("1234567890abcdef"); - assertEquals("1234567890abcdef", request.getVisitorId()); + void testVisitorIdTFF() { + request.setVisitorId(VisitorId.fromHex("1234567890abcdef")); + assertThat(request.getVisitorId()).hasToString("1234567890abcdef"); } @Test - public void testVisitorIdF() { - request.setVisitorId("1234567890abcdef"); + void testVisitorIdF() { + request.setVisitorId(VisitorId.fromHex("1234567890abcdef")); request.setVisitorId(null); - assertNull(request.getVisitorId()); + assertThat(request.getVisitorId()).isNull(); } /** * Test of getVisitorIp method, of class PiwikRequest. */ @Test - public void testVisitorIpT() { + void testVisitorIpT() { request.setAuthToken("12345678901234567890123456789012"); request.setVisitorIp("ip"); - assertEquals("ip", request.getVisitorIp()); - } - - @Test - public void testVisitorIpTE() { - try { - request.setVisitorIp("ip"); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); - } + assertThat(request.getVisitorIp()).isEqualTo("ip"); } @Test - public void testVisitorIpF() { + void testVisitorIpF() { request.setVisitorIp(null); - assertNull(request.getVisitorIp()); + assertThat(request.getVisitorIp()).isNull(); } /** * Test of getVisitorLatitude method, of class PiwikRequest. */ @Test - public void testVisitorLatitudeT() { + void testVisitorLatitudeT() { request.setAuthToken("12345678901234567890123456789012"); request.setVisitorLatitude(10.5); - assertEquals(Double.valueOf(10.5), request.getVisitorLatitude()); - } - - @Test - public void testVisitorLatitudeTE() { - try { - request.setVisitorLatitude(10.5); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); - } + assertThat(request.getVisitorLatitude()).isEqualTo(Double.valueOf(10.5)); } @Test - public void testVisitorLatitudeF() { + void testVisitorLatitudeF() { request.setVisitorLatitude(null); - assertNull(request.getVisitorLatitude()); + assertThat(request.getVisitorLatitude()).isNull(); } /** * Test of getVisitorLongitude method, of class PiwikRequest. */ @Test - public void testVisitorLongitudeT() { + void testVisitorLongitudeT() { request.setAuthToken("12345678901234567890123456789012"); request.setVisitorLongitude(20.5); - assertEquals(Double.valueOf(20.5), request.getVisitorLongitude()); - } - - @Test - public void testVisitorLongitudeTE() { - try { - request.setVisitorLongitude(20.5); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); - } + assertThat(request.getVisitorLongitude()).isEqualTo(Double.valueOf(20.5)); } @Test - public void testVisitorLongitudeF() { + void testVisitorLongitudeF() { request.setVisitorLongitude(null); - assertNull(request.getVisitorLongitude()); + assertThat(request.getVisitorLongitude()).isNull(); } /** * Test of getVisitorPreviousVisitTimestamp method, of class PiwikRequest. */ @Test - public void testVisitorPreviousVisitTimestamp() { - request.setVisitorPreviousVisitTimestamp(1000L); - assertEquals(Long.valueOf(1000L), request.getVisitorPreviousVisitTimestamp()); + void testVisitorPreviousVisitTimestamp() { + request.setVisitorPreviousVisitTimestamp(Instant.ofEpochSecond(1000L)); + assertThat(request.getVisitorPreviousVisitTimestamp()).isEqualTo("1970-01-01T00:16:40Z"); } /** * Test of getVisitorRegion method, of class PiwikRequest. */ @Test - public void testVisitorRegionT() { + void testVisitorRegionT() { request.setAuthToken("12345678901234567890123456789012"); request.setVisitorRegion("region"); - - assertEquals("region", request.getVisitorRegion()); - } - - @Test - public void testGetVisitorRegionTE() { - try { - request.setVisitorRegion("region"); - fail("Exception should have been thrown."); - } catch (IllegalStateException e) { - assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); - } + assertThat(request.getVisitorRegion()).isEqualTo("region"); } @Test - public void testVisitorRegionF() { + void testVisitorRegionF() { request.setVisitorRegion(null); - - assertNull(request.getVisitorRegion()); + assertThat(request.getVisitorRegion()).isNull(); } /** * Test of getVisitorVisitCount method, of class PiwikRequest. */ @Test - public void testVisitorVisitCount() { + void testVisitorVisitCount() { request.setVisitorVisitCount(100); - assertEquals(Integer.valueOf(100), request.getVisitorVisitCount()); - } - - /** - * Test of getQueryString method, of class PiwikRequest. - */ - @Test - public void testGetQueryString() { - request.setRandomValue("random"); - request.setVisitorId("1234567890123456"); - assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456", request.getQueryString()); - request.setPageCustomVariable("key", "val"); - assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&cvar={\"1\":[\"key\",\"val\"]}", - request.getQueryString()); - request.setPageCustomVariable("key", null); - assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456", request.getQueryString()); - request.addCustomTrackingParameter("key", "test"); - assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&key=test", request.getQueryString()); - request.addCustomTrackingParameter("key", "test2"); - assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&key=test&key=test2", request.getQueryString()); - request.setCustomTrackingParameter("key2", "test3"); - assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&key=test&key=test2&key2=test3", request.getQueryString()); - request.setCustomTrackingParameter("key", "test4"); - assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&key2=test3&key=test4", request.getQueryString()); - request.setRandomValue(null); - request.setSiteId(null); - request.setRequired(null); - request.setApiVersion(null); - request.setResponseAsImage(null); - request.setVisitorId(null); - request.setActionUrl((String) null); - assertEquals("key2=test3&key=test4", request.getQueryString()); - request.clearCustomTrackingParameter(); - assertEquals("", request.getQueryString()); - } - - @Test - public void testGetQueryString2() { - request.setActionUrlWithString("http://test.com"); - request.setRandomValue("random"); - request.setVisitorId("1234567890123456"); - assertEquals("idsite=3&rec=1&apiv=1&send_image=0&url=http://test.com&rand=random&_id=1234567890123456", request.getQueryString()); - } - - /** - * Test of getUrlEncodedQueryString method, of class PiwikRequest. - */ - @Test - public void testGetUrlEncodedQueryString() { - request.setRandomValue("random"); - request.setVisitorId("1234567890123456"); - assertEquals("_id=1234567890123456&apiv=1&idsite=3&rand=random&rec=1&send_image=0&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); - request.addCustomTrackingParameter("ke/y", "te:st"); - assertEquals("_id=1234567890123456&apiv=1&idsite=3&ke%2Fy=te%3Ast&rand=random&rec=1&send_image=0&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); - request.addCustomTrackingParameter("ke/y", "te:st2"); - assertEquals("_id=1234567890123456&apiv=1&idsite=3&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2&rand=random&rec=1&send_image=0&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); - request.setCustomTrackingParameter("ke/y2", "te:st3"); - assertEquals("_id=1234567890123456&apiv=1&idsite=3&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2&ke%2Fy2=te%3Ast3&rand=random&rec=1&send_image=0&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); - request.setCustomTrackingParameter("ke/y", "te:st4"); - assertEquals("_id=1234567890123456&apiv=1&idsite=3&ke%2Fy=te%3Ast4&ke%2Fy2=te%3Ast3&rand=random&rec=1&send_image=0&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); - request.setRandomValue(null); - request.setSiteId(null); - request.setRequired(null); - request.setApiVersion(null); - request.setResponseAsImage(null); - request.setVisitorId(null); - request.setActionUrl((String) null); - assertEquals("ke%2Fy=te%3Ast4&ke%2Fy2=te%3Ast3", request.getUrlEncodedQueryString()); - request.clearCustomTrackingParameter(); - assertEquals("", request.getUrlEncodedQueryString()); + assertThat(request.getVisitorVisitCount()).isEqualTo(Integer.valueOf(100)); } @Test - public void testGetUrlEncodedQueryString2() { - request.setActionUrlWithString("http://test.com"); - request.setRandomValue("random"); - request.setVisitorId("1234567890123456"); - assertEquals("_id=1234567890123456&apiv=1&idsite=3&rand=random&rec=1&send_image=0&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); + void failsIfActionUrlIsNull() { + assertThatThrownBy(() -> new PiwikRequest(3, null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("Action URL must not be null"); } - /** - * Test of getRandomHexString method, of class PiwikRequest. - */ - @Test - public void testGetRandomHexString() { - String s = PiwikRequest.getRandomHexString(10); - - assertEquals(10, s.length()); - Long.parseLong(s, 16); - } } diff --git a/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java b/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java new file mode 100644 index 00000000..17365eb2 --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java @@ -0,0 +1,253 @@ +package org.matomo.java.tracking; + +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.status; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import java.net.ConnectException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.matomo.java.tracking.parameters.RandomValue; +import org.matomo.java.tracking.parameters.VisitorId; +import org.piwik.java.tracking.PiwikRequest; +import org.piwik.java.tracking.PiwikTracker; + +class PiwikTrackerIT { + + private static final WireMockServer wireMockServer = new WireMockServer( + WireMockConfiguration.options().dynamicPort()); + + + private static final int SITE_ID = 42; + + private PiwikTracker piwikTracker; + + private PiwikRequest request; + + @BeforeAll + static void beforeAll() { + wireMockServer.start(); + } + + @BeforeEach + void setUp() throws MalformedURLException { + piwikTracker = new PiwikTracker( + String.format("http://localhost:%d/matomo.php", wireMockServer.port()), -1); + wireMockServer.resetRequests(); + wireMockServer.stubFor(post(urlPathEqualTo("/matomo.php")).willReturn(status(204))); + wireMockServer.stubFor(get(urlPathEqualTo("/matomo.php")).willReturn(status(204))); + request = new PiwikRequest(SITE_ID, new URL("https://test.local/test/path?id=123")); + request.setRandomValue(RandomValue.fromString("rand")); + request.setVisitorId(VisitorId.fromHash(999999999999999999L)); + } + + /** + * Test of sendRequest method, of class PiwikTracker. + */ + @Test + void testSendRequest() { + request.setCustomTrackingParameter("parameterName", "parameterValue"); + + piwikTracker.sendRequest(request); + + wireMockServer.verify(getRequestedFor(urlEqualTo( + "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue")) + .withHeader("User-Agent", equalTo("MatomoJavaClient"))) + ; + } + + /** + * Test of sendRequestAsync method, of class PiwikTracker. + */ + @Test + void testSendRequestAsync() throws Exception { + request.setCustomTrackingParameter("parameterName", "parameterValue"); + + CompletableFuture future = piwikTracker.sendRequestAsync(request); + future.get(); + + assertThat(future).isNotCompletedExceptionally(); + wireMockServer.verify(getRequestedFor(urlEqualTo( + "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue")) + .withHeader("User-Agent", equalTo("MatomoJavaClient"))); + } + + + /** + * Test of sendBulkRequest method, of class PiwikTracker. + */ + @Test + void testSendBulkRequest_Iterable() { + List requests = Collections.singletonList(request); + request.setCustomTrackingParameter("parameterName", "parameterValue"); + + piwikTracker.sendBulkRequest(requests); + + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("167")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{ \"requests\" : [ \"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\" ]}"))); + + } + + /** + * Test of sendBulkRequest method, of class PiwikTracker. + */ + @Test + void testSendBulkRequest_Iterable_StringTT() { + List requests = Collections.singletonList(request); + request.setCustomTrackingParameter("parameterName", "parameterValue"); + + assertThatThrownBy(() -> piwikTracker.sendBulkRequest(requests, "1")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Auth token must be exactly 32 characters long"); + } + + @Test + void testSendBulkRequest_Iterable_StringFF() { + List requests = Collections.singletonList(request); + request.setCustomTrackingParameter("parameterName", "parameterValue"); + + piwikTracker.sendBulkRequest(requests, null); + + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("167")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{\"requests\":[\"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\"]}"))); + + } + + @Test + void testSendBulkRequest_Iterable_StringFT() { + List requests = Collections.singletonList(request); + request.setCustomTrackingParameter("parameterName", "parameterValue"); + + piwikTracker.sendBulkRequest(requests, "12345678901234567890123456789012"); + + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("215")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{\"requests\":[\"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\" ],\"token_auth\":\"12345678901234567890123456789012\"}"))); + + } + + /** + * Test of sendBulkRequestAsync method, of class PiwikTracker. + */ + @Test + void testSendBulkRequestAsync_Iterable() throws Exception { + List requests = Collections.singletonList(request); + request.setCustomTrackingParameter("parameterName", "parameterValue"); + + CompletableFuture future = piwikTracker.sendBulkRequestAsync(requests); + future.get(); + + assertThat(future).isNotCompletedExceptionally(); + + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("167")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{\"requests\" : [ \"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\" ]}"))); + + } + + /** + * Test of sendBulkRequestAsync method, of class PiwikTracker. + */ + @Test + void testSendBulkRequestAsync_Iterable_StringTT() { + + List requests = Collections.singletonList(request); + request.setCustomTrackingParameter("parameterName", "parameterValue"); + + assertThatThrownBy(() -> piwikTracker.sendBulkRequestAsync(requests, "1").get()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Auth token must be exactly 32 characters long"); + + } + + + @Test + void testSendBulkRequestAsync_Iterable_String() throws Exception { + + List requests = Collections.singletonList(request); + request.setCustomTrackingParameter("parameterName", "parameterValue"); + + CompletableFuture future = piwikTracker.sendBulkRequestAsync( + requests, "12345678901234567890123456789012"); + future.get(); + + assertThat(future).isNotCompletedExceptionally(); + + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("215")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{\"requests\":[ \"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\"],\"token_auth\":\"12345678901234567890123456789012\"}"))); + + } + + @Test + void createsPiwikTrackerWithHostUrl() { + PiwikTracker piwikTracker = new PiwikTracker(String.format("http://localhost:%d/matomo.php", wireMockServer.port())); + + piwikTracker.sendRequest(request); + + wireMockServer.verify(getRequestedFor(urlEqualTo( + "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand")) + .withHeader("User-Agent", equalTo("MatomoJavaClient"))); + } + + @Test + void createPiwikTrackerWithHostUrlAndProxyHostAndPort() { + PiwikTracker piwikTracker = new PiwikTracker(String.format("http://localhost:%d/matomo.php", wireMockServer.port()), "localhost", 8080); + + assertThatThrownBy(() -> piwikTracker.sendRequest(request)) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not send request via GET") + .hasRootCauseInstanceOf(ConnectException.class) + .hasRootCauseMessage("Connection refused (Connection refused)"); + + } + + @Test + void createPiwikTrackerWithHostUrlAndProxyHostAndPortAndTimeout() { + PiwikTracker piwikTracker = new PiwikTracker(String.format("http://localhost:%d/matomo.php", wireMockServer.port()), "localhost", 8080, 1000); + + assertThatThrownBy(() -> piwikTracker.sendRequest(request)) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not send request via GET") + .hasRootCauseInstanceOf(ConnectException.class) + .hasRootCauseMessage("Connection refused (Connection refused)");} + +} diff --git a/src/test/java/org/matomo/java/tracking/PiwikTrackerTest.java b/src/test/java/org/matomo/java/tracking/PiwikTrackerTest.java deleted file mode 100644 index 6c1dcc12..00000000 --- a/src/test/java/org/matomo/java/tracking/PiwikTrackerTest.java +++ /dev/null @@ -1,433 +0,0 @@ -/* - * Piwik Java Tracker - * - * @link https://github.com/matomo/matomo-java-tracker - * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause - */ -package org.matomo.java.tracking; - -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpServer; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.concurrent.FutureCallback; -import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; -import org.apache.http.util.EntityUtils; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.mockito.ArgumentMatcher; -import org.piwik.java.tracking.PiwikRequest; -import org.piwik.java.tracking.PiwikTracker; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.net.URL; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Future; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; - -/** - * @author brettcsorba - */ -public class PiwikTrackerTest { - private static final Map> PARAMETERS = Collections.singletonMap("parameterName", Collections.singleton("parameterValue")); - - // https://stackoverflow.com/a/3732328 - static class Handler implements HttpHandler { - @Override - public void handle(HttpExchange exchange) throws IOException { - String response = "OK"; - exchange.sendResponseHeaders(200, response.length()); - OutputStream os = exchange.getResponseBody(); - os.write(response.getBytes()); - os.close(); - } - } - - PiwikTracker piwikTracker; - PiwikTracker localTracker; - HttpServer server; - - public PiwikTrackerTest() { - } - - @BeforeClass - public static void setUpClass() { - } - - @AfterClass - public static void tearDownClass() { - } - - @Before - public void setUp() { - // test with mocks - piwikTracker = spy(new PiwikTracker("http://test.com")); - - // test with local server - localTracker = new PiwikTracker("http://localhost:8001/test"); - try { - server = HttpServer.create(new InetSocketAddress(8001), 0); - server.createContext("/test", new Handler()); - server.setExecutor(null); // creates a default executor - server.start(); - } catch (IOException ex) { - } - } - - @After - public void tearDown() { - server.stop(0); - } - - /** - * Test of addParameter method, of class PiwikTracker. - */ - @Test - public void testAddParameter() { - } - - /** - * Test of sendRequest method, of class PiwikTracker. - */ - @Test - public void testSendRequest() throws Exception { - PiwikRequest request = mock(PiwikRequest.class); - HttpClient client = mock(HttpClient.class); - HttpResponse response = mock(HttpResponse.class); - - doReturn(client).when(piwikTracker).getHttpClient(); - doReturn(PARAMETERS).when(request).getParameters(); - doReturn(response).when(client) - .execute(argThat(new CorrectGetRequest("http://test.com?parameterName=parameterValue"))); - - assertEquals(response, piwikTracker.sendRequest(request)); - } - - /** - * Test of sendRequestAsync method, of class PiwikTracker. - */ - @Test - public void testSendRequestAsync() throws Exception { - PiwikRequest request = mock(PiwikRequest.class); - CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); - HttpResponse response = mock(HttpResponse.class); - Future future = mock(Future.class); - - doReturn(client).when(piwikTracker).getHttpAsyncClient(); - doReturn(PARAMETERS).when(request).getParameters(); - doReturn(response).when(future).get(); - doReturn(future).when(client) - .execute(argThat(new CorrectGetRequest("http://test.com?parameterName=parameterValue")), any()); - - assertEquals(response, piwikTracker.sendRequestAsync(request).get()); - } - - /** - * Test sync API with local server - */ - @Test - public void testWithLocalServer() throws Exception { - // one - PiwikRequest request = new PiwikRequest(3, new URL("http://test.com")); - HttpResponse response = localTracker.sendRequest(request); - String msg = EntityUtils.toString(response.getEntity()); - assertEquals("OK", msg); - - // bulk - List requests = Collections.singletonList(request); - HttpResponse responseBulk = localTracker.sendBulkRequest(requests); - String msgBulk = EntityUtils.toString(responseBulk.getEntity()); - assertEquals("OK", msgBulk); - } - - /** - * Test async API with local server - */ - @Test - public void testWithLocalServerAsync() throws Exception { - // one - PiwikRequest request = new PiwikRequest(3, new URL("http://test.com")); - HttpResponse response = localTracker.sendRequestAsync(request).get(); - String msg = EntityUtils.toString(response.getEntity()); - assertEquals("OK", msg); - - // bulk - List requests = Collections.singletonList(request); - HttpResponse responseBulk = localTracker.sendBulkRequestAsync(requests).get(); - String msgBulk = EntityUtils.toString(responseBulk.getEntity()); - assertEquals("OK", msgBulk); - } - - /** - * Test async API with local server - */ - @Test - public void testWithLocalServerAsyncCallback() throws Exception { - CountDownLatch latch = new CountDownLatch(2); - BlockingQueue responses = new LinkedBlockingQueue<>(); - BlockingQueue exceptions = new LinkedBlockingQueue<>(); - AtomicInteger cancelled = new AtomicInteger(); - - FutureCallback cb = new FutureCallback() { - - @Override - public void completed(HttpResponse httpResponse) { - responses.add(httpResponse); - latch.countDown(); - } - - @Override - public void failed(Exception e) { - exceptions.add(e); - latch.countDown(); - } - - @Override - public void cancelled() { - cancelled.incrementAndGet(); - latch.countDown(); - - } - }; - - // one - PiwikRequest request = new PiwikRequest(3, new URL("http://test.com")); - Future respFuture = localTracker.sendRequestAsync(request, cb); - // bulk - List requests = Collections.singletonList(request); - Future bulkFuture = localTracker.sendBulkRequestAsync(requests, cb); - - assertTrue("Responses not received", latch.await(100, TimeUnit.MILLISECONDS)); - assertEquals("Not expecting cancelled responses", 0, cancelled.get()); - assertEquals("Not expecting exceptions", exceptions.size(), 0); - assertTrue("Single response future not done", respFuture.isDone()); - assertTrue("Bulk response future not done", bulkFuture.isDone()); - HttpResponse response = responses.poll(1, TimeUnit.MILLISECONDS); - assertEquals("OK", EntityUtils.toString(response.getEntity())); - - HttpResponse bulkResponse = responses.poll(1, TimeUnit.MILLISECONDS); - assertEquals("OK", EntityUtils.toString(bulkResponse.getEntity())); - } - - static class CorrectGetRequest implements ArgumentMatcher { - String url; - - public CorrectGetRequest(String url) { - this.url = url; - } - - @Override - public boolean matches(HttpGet get) { - return url.equals(get.getURI().toString()); - } - } - - /** - * Test of sendBulkRequest method, of class PiwikTracker. - */ - @Test - public void testSendBulkRequest_Iterable() { - List requests = new ArrayList<>(); - HttpResponse response = mock(HttpResponse.class); - - doReturn(response).when(piwikTracker).sendBulkRequest(requests, null); - - assertEquals(response, piwikTracker.sendBulkRequest(requests)); - } - - /** - * Test of sendBulkRequest method, of class PiwikTracker. - */ - @Test - public void testSendBulkRequest_Iterable_StringTT() { - try { - List requests = new ArrayList<>(); - HttpClient client = mock(HttpClient.class); - PiwikRequest request = mock(PiwikRequest.class); - - requests.add(request); - - piwikTracker.sendBulkRequest(requests, "1"); - fail("Exception should have been thrown."); - } catch (IllegalArgumentException e) { - assertEquals("1 is not 32 characters long.", e.getLocalizedMessage()); - } - } - - @Test - public void testSendBulkRequest_Iterable_StringFF() throws Exception { - List requests = new ArrayList<>(); - HttpClient client = mock(HttpClient.class); - PiwikRequest request = mock(PiwikRequest.class); - HttpResponse response = mock(HttpResponse.class); - - doReturn(PARAMETERS).when(request).getParameters(); - requests.add(request); - doReturn(client).when(piwikTracker).getHttpClient(); - doReturn(response).when(client).execute(argThat(new CorrectPostRequest("{\"requests\":[\"?parameterName=parameterValue\"]}"))); - - assertEquals(response, piwikTracker.sendBulkRequest(requests, null)); - } - - @Test - public void testSendBulkRequest_Iterable_StringFT() throws Exception { - List requests = new ArrayList<>(); - HttpClient client = mock(HttpClient.class); - PiwikRequest request = mock(PiwikRequest.class); - HttpResponse response = mock(HttpResponse.class); - - doReturn(PARAMETERS).when(request).getParameters(); - requests.add(request); - doReturn(client).when(piwikTracker).getHttpClient(); - doReturn(response).when(client) - .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?parameterName=parameterValue\"],\"token_auth\":\"12345678901234567890123456789012\"}"))); - - assertEquals(response, piwikTracker.sendBulkRequest(requests, "12345678901234567890123456789012")); - } - - /** - * Test of sendBulkRequestAsync method, of class PiwikTracker. - */ - @Test - public void testSendBulkRequestAsync_Iterable() throws Exception { - List requests = new ArrayList<>(); - HttpResponse response = mock(HttpResponse.class); - Future future = mock(Future.class); - doReturn(response).when(future).get(); - - doReturn(future).when(piwikTracker).sendBulkRequestAsync(requests); - - assertEquals(response, piwikTracker.sendBulkRequestAsync(requests).get()); - } - - /** - * Test of sendBulkRequestAsync method, of class PiwikTracker. - */ - @Test - public void testSendBulkRequestAsync_Iterable_StringTT() { - try { - List requests = new ArrayList<>(); - CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); - PiwikRequest request = mock(PiwikRequest.class); - - requests.add(request); - - piwikTracker.sendBulkRequestAsync(requests, "1"); - fail("Exception should have been thrown."); - } catch (IllegalArgumentException e) { - assertEquals("1 is not 32 characters long.", e.getLocalizedMessage()); - } - } - - @Test - public void testSendBulkRequestAsync_Iterable_StringFF() throws Exception { - List requests = new ArrayList<>(); - CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); - PiwikRequest request = mock(PiwikRequest.class); - HttpResponse response = mock(HttpResponse.class); - Future future = mock(Future.class); - doReturn(response).when(future).get(); - - doReturn(PARAMETERS).when(request).getParameters(); - requests.add(request); - doReturn(client).when(piwikTracker).getHttpAsyncClient(); - doReturn(future).when(client) - .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?parameterName=parameterValue\"]}")), any()); - - assertEquals(response, piwikTracker.sendBulkRequestAsync(requests).get()); - } - - @Test - public void testSendBulkRequestAsync_Iterable_StringFT() throws Exception { - List requests = new ArrayList<>(); - CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); - PiwikRequest request = mock(PiwikRequest.class); - HttpResponse response = mock(HttpResponse.class); - Future future = mock(Future.class); - doReturn(response).when(future).get(); - - doReturn(PARAMETERS).when(request).getParameters(); - requests.add(request); - doReturn(client).when(piwikTracker).getHttpAsyncClient(); - doReturn(future).when(client) - .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?parameterName=parameterValue\"],\"token_auth\":\"12345678901234567890123456789012\"}")), any()); - - assertEquals(response, piwikTracker.sendBulkRequestAsync(requests, "12345678901234567890123456789012").get()); - } - - static class CorrectPostRequest implements ArgumentMatcher { - String body; - - public CorrectPostRequest(String body) { - this.body = body; - } - - @Override - public boolean matches(HttpPost post) { - try { - InputStream bais = post.getEntity().getContent(); - byte[] bytes = new byte[bais.available()]; - bais.read(bytes); - String str = new String(bytes); - return body.equals(str); - } catch (IOException e) { - fail("Exception should not have been throw."); - } - return false; - } - } - - /** - * Test of getHttpClient method, of class PiwikTracker. - */ - @Test - public void testGetHttpClient() { - assertNotNull(piwikTracker.getHttpClient()); - } - - /** - * Test of getHttpAsyncClient method, of class PiwikTracker. - */ - @Test - public void testGetHttpAsyncClient() { - assertNotNull(piwikTracker.getHttpAsyncClient()); - } - - /** - * Test of getHttpClient method, of class PiwikTracker, with proxy. - */ - @Test - public void testGetHttpClientWithProxy() { - piwikTracker = new PiwikTracker("http://test.com", "http://proxy", 8080); - HttpClient httpClient = piwikTracker.getHttpClient(); - - assertNotNull(httpClient); - } -} diff --git a/src/test/java/org/matomo/java/tracking/ProxyAuthenticatorTest.java b/src/test/java/org/matomo/java/tracking/ProxyAuthenticatorTest.java new file mode 100644 index 00000000..f4199853 --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/ProxyAuthenticatorTest.java @@ -0,0 +1,52 @@ +package org.matomo.java.tracking; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.Authenticator; +import java.net.Authenticator.RequestorType; +import java.net.InetAddress; +import java.net.PasswordAuthentication; +import java.net.URL; +import org.junit.jupiter.api.Test; + +class ProxyAuthenticatorTest { + + private PasswordAuthentication passwordAuthentication; + + @Test + void createsPasswordAuthentication() throws Exception { + + ProxyAuthenticator proxyAuthenticator = new ProxyAuthenticator("user", "password"); + Authenticator.setDefault(proxyAuthenticator); + givenPasswordAuthentication(RequestorType.PROXY); + + assertThat(passwordAuthentication.getUserName()).isEqualTo("user"); + assertThat(passwordAuthentication.getPassword()).contains('p', 'a', 's', 's', 'w', 'o', 'r', 'd'); + + } + @Test + void returnsNullIfNoPasswordAuthentication() throws Exception { + + ProxyAuthenticator proxyAuthenticator = new ProxyAuthenticator("user", "password"); + Authenticator.setDefault(proxyAuthenticator); + givenPasswordAuthentication(RequestorType.SERVER); + + assertThat(passwordAuthentication).isNull(); + + } + + private void givenPasswordAuthentication(RequestorType proxy) throws Exception { + passwordAuthentication = Authenticator.requestPasswordAuthentication( + "host", + InetAddress.getLocalHost(), + 8080, + "http", + "prompt", + "https", + new URL("https://www.daniel-heid.de"), + proxy + ); + } + + +} diff --git a/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java b/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java new file mode 100644 index 00000000..0909c016 --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java @@ -0,0 +1,363 @@ +package org.matomo.java.tracking; + +import static java.util.Arrays.asList; +import static java.util.Collections.singleton; +import static java.util.Collections.singletonMap; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Locale.LanguageRange; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.matomo.java.tracking.parameters.AcceptLanguage; +import org.matomo.java.tracking.parameters.Country; +import org.matomo.java.tracking.parameters.CustomVariable; +import org.matomo.java.tracking.parameters.CustomVariables; +import org.matomo.java.tracking.parameters.DeviceResolution; +import org.matomo.java.tracking.parameters.EcommerceItem; +import org.matomo.java.tracking.parameters.EcommerceItems; +import org.matomo.java.tracking.parameters.RandomValue; +import org.matomo.java.tracking.parameters.UniqueId; +import org.matomo.java.tracking.parameters.VisitorId; + +class QueryCreatorTest { + + private final MatomoRequest.MatomoRequestBuilder matomoRequestBuilder = + MatomoRequest.builder().visitorId(VisitorId.fromHash(1234567890123456789L)).randomValue( + RandomValue.fromString("random-value")); + + private String defaultAuthToken = "876de1876fb2cda2816c362a61bfc712"; + + private String query; + + private MatomoRequest request; + + @Test + void usesDefaultSiteId() { + + whenCreatesQuery(); + + assertThat(query).isEqualTo( + "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value"); + + } + + private void whenCreatesQuery() { + request = matomoRequestBuilder.build(); + TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( + URI.create("http://localhost")).defaultSiteId(42).defaultAuthToken(defaultAuthToken).build(); + String authToken = AuthToken.determineAuthToken(null, singleton(request), trackerConfiguration); + query = new QueryCreator(trackerConfiguration).createQuery(request, authToken); + } + + @Test + void overridesDefaultSiteId() { + + matomoRequestBuilder.siteId(123); + + whenCreatesQuery(); + + assertThat(query).isEqualTo( + "token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&idsite=123&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value"); + + } + + @Test + void usesDefaultTokenAuth() { + + defaultAuthToken = "f123bfc9a46de0bb5453afdab6f93200"; + + whenCreatesQuery(); + + assertThat(query).isEqualTo( + "idsite=42token_auth=f123bfc9a46de0bb5453afdab6f93200&rec=1&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value"); + + } + + @Test + void overridesDefaultTokenAuth() { + + defaultAuthToken = "f123bfc9a46de0bb5453afdab6f93200"; + matomoRequestBuilder.authToken("e456bfc9a46de0bb5453afdab6f93200"); + + whenCreatesQuery(); + + assertThat(query).isEqualTo( + "idsite=42token_auth=e456bfc9a46de0bb5453afdab6f93200&rec=1&apiv=1&_id=112210f47de98115&token_auth=e456bfc9a46de0bb5453afdab6f93200&send_image=0&rand=random-value"); + + } + + @Test + void validatesTokenAuth() { + + matomoRequestBuilder.authToken("invalid-token-auth"); + + assertThatThrownBy(this::whenCreatesQuery).isInstanceOf(IllegalArgumentException.class) + .hasMessage("Auth token must be exactly 32 characters long"); + + } + + @Test + void convertsTrueBooleanTo1() { + + matomoRequestBuilder.pluginFlash(true); + + whenCreatesQuery(); + + assertThat(query).isEqualTo( + "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&fla=1&send_image=0&rand=random-value"); + + } + + @Test + void convertsFalseBooleanTo0() { + + matomoRequestBuilder.pluginJava(false); + + whenCreatesQuery(); + + assertThat(query).isEqualTo( + "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&java=0&send_image=0&rand=random-value"); + + } + + @Test + void encodesUrl() { + + matomoRequestBuilder.actionUrl("https://www.daniel-heid.de/some/page?foo=bar"); + + whenCreatesQuery(); + + assertThat(query).isEqualTo( + "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&url=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fpage%3Ffoo%3Dbar&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value"); + + } + + @Test + void encodesReferrerUrl() { + + matomoRequestBuilder.referrerUrl("https://www.daniel-heid.de/some/referrer?foo2=bar2"); + + whenCreatesQuery(); + + assertThat(query).isEqualTo( + "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Freferrer%3Ffoo2%3Dbar2&send_image=0&rand=random-value"); + + } + + @Test + void encodesLink() { + + matomoRequestBuilder.outlinkUrl("https://www.daniel-heid.de/some/external/link#"); + + whenCreatesQuery(); + + assertThat(query).isEqualTo( + "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&link=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fexternal%2Flink%23&send_image=0&rand=random-value"); + + } + + @Test + void encodesDownloadUrl() { + + matomoRequestBuilder.downloadUrl("https://www.daniel-heid.de/some/download.pdf"); + + whenCreatesQuery(); + + assertThat(query).isEqualTo( + "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&download=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fdownload.pdf&send_image=0&rand=random-value"); + + } + + @Test + void tracksMinimalRequest() { + + matomoRequestBuilder.actionName("Help / Feedback").actionUrl( + "https://www.daniel-heid.de/portfolio").visitorId(VisitorId.fromHash(3434343434343434343L)) + .referrerUrl("https://www.daniel-heid.de/referrer").visitCustomVariables( + new CustomVariables().add(new CustomVariable("customVariable1Key", "customVariable1Value"), 5) + .add(new CustomVariable("customVariable2Key", "customVariable2Value"), 6)) + .visitorVisitCount(2).visitorPreviousVisitTimestamp(Instant.parse("2022-08-09T18:34:12Z")) + .deviceResolution(DeviceResolution.builder().width(1024).height(768).build()) + .headerAcceptLanguage(AcceptLanguage.builder().languageRange(new LanguageRange("de")) + .languageRange(new LanguageRange("de-DE", 0.9)).languageRange(new LanguageRange("en", 0.8)) + .build()).pageViewId(UniqueId.fromValue(999999999999999999L)).goalId(0).ecommerceRevenue( + 12.34).ecommerceItems(EcommerceItems.builder().item( + EcommerceItem.builder().sku("SKU").build()).item(EcommerceItem.builder().sku("SKU").name( + "NAME").category("CATEGORY").price(123.4).build()).build()).authToken( + "fdf6e8461ea9de33176b222519627f78").visitorCountry( + Country.fromLanguageRanges("en-GB;q=0.7,de,de-DE;q=0.9,en;q=0.8,en-US;q=0.6")); + + whenCreatesQuery(); + + assertThat(query).isEqualTo( + "idsite=42token_auth=fdf6e8461ea9de33176b222519627f78&rec=1&action_name=Help+%2F+Feedback&url=https%3A%2F%2Fwww.daniel-heid.de%2Fportfolio&apiv=1&_id=2fa93d2858bc4867&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Freferrer&_cvar=%7B%225%22%3A%5B%22customVariable1Key%22%2C%22customVariable1Value%22%5D%2C%226%22%3A%5B%22customVariable2Key%22%2C%22customVariable2Value%22%5D%7D&_idvc=2&_viewts=1660070052&res=1024x768&lang=de%2Cde-de%3Bq%3D0.9%2Cen%3Bq%3D0.8&pv_id=lbBbxG&idgoal=0&revenue=12.34&ec_items=%5B%5B%22SKU%22%2C%22%22%2C%22%22%2C0.0%2C0%5D%2C%5B%22SKU%22%2C%22NAME%22%2C%22CATEGORY%22%2C123.4%2C0%5D%5D&token_auth=fdf6e8461ea9de33176b222519627f78&country=de&send_image=0&rand=random-value"); + + } + + @Test + void testGetQueryString() { + matomoRequestBuilder.siteId(3).actionUrl("http://test.com").randomValue( + RandomValue.fromString("random")).visitorId(VisitorId.fromHex("1234567890123456")); + defaultAuthToken = null; + whenCreatesQuery(); + assertThat(query).isEqualTo( + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random"); + matomoRequestBuilder.pageCustomVariables( + new CustomVariables().add(new CustomVariable("key", "val"), 7)); + whenCreatesQuery(); + assertThat(query).isEqualTo( + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random"); + matomoRequestBuilder.customTrackingParameters(singletonMap("key", singleton("test"))); + whenCreatesQuery(); + assertThat(query).isEqualTo( + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key=test"); + matomoRequestBuilder.customTrackingParameters(singletonMap("key", asList("test", "test2"))); + whenCreatesQuery(); + assertThat(query).isEqualTo( + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key=test&key=test2"); + Map> customTrackingParameters = new HashMap<>(); + customTrackingParameters.put("key", asList("test", "test2")); + customTrackingParameters.put("key2", Collections.singletonList("test3")); + matomoRequestBuilder.customTrackingParameters(customTrackingParameters); + whenCreatesQuery(); + assertThat(query).isEqualTo( + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key2=test3&key=test&key=test2"); + customTrackingParameters.put("key", Collections.singletonList("test4")); + whenCreatesQuery(); + assertThat(query).isEqualTo( + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key2=test3&key=test4"); + matomoRequestBuilder.randomValue(null); + matomoRequestBuilder.siteId(null); + matomoRequestBuilder.required(null); + matomoRequestBuilder.apiVersion(null); + matomoRequestBuilder.responseAsImage(null); + matomoRequestBuilder.visitorId(null); + matomoRequestBuilder.actionUrl(null); + whenCreatesQuery(); + assertThat(query).isEqualTo( + "idsite=42&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&key2=test3&key=test4"); + } + + @Test + void testGetQueryString2() { + matomoRequestBuilder.actionUrl("http://test.com").randomValue(RandomValue.fromString("random")) + .visitorId(VisitorId.fromHex("1234567890123456")).siteId(3); + defaultAuthToken = null; + + whenCreatesQuery(); + + assertThat(query).isEqualTo( + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random"); + } + + @Test + void testGetUrlEncodedQueryString() { + defaultAuthToken = null; + matomoRequestBuilder.actionUrl("http://test.com").randomValue(RandomValue.fromString("random")) + .visitorId(VisitorId.fromHex("1234567890123456")).siteId(3); + whenCreatesQuery(); + assertThat(query).isEqualTo( + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random"); + Map> customTrackingParameters = new HashMap<>(); + customTrackingParameters.put("ke/y", Collections.singletonList("te:st")); + matomoRequestBuilder.customTrackingParameters(customTrackingParameters); + whenCreatesQuery(); + assertThat(query).isEqualTo( + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast"); + customTrackingParameters.put("ke/y", asList("te:st", "te:st2")); + whenCreatesQuery(); + assertThat(query).isEqualTo( + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2"); + customTrackingParameters.put("ke/y2", Collections.singletonList("te:st3")); + whenCreatesQuery(); + assertThat(query).isEqualTo( + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2&ke%2Fy2=te%3Ast3"); + customTrackingParameters.put("ke/y", asList("te:st3", "te:st4")); + whenCreatesQuery(); + assertThat(query).isEqualTo( + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast3&ke%2Fy=te%3Ast4&ke%2Fy2=te%3Ast3"); + matomoRequestBuilder.randomValue(null).siteId(null).required(null).apiVersion(null) + .responseAsImage(null).visitorId(null).actionUrl(null); + whenCreatesQuery(); + assertThat(query).isEqualTo("idsite=42&ke%2Fy=te%3Ast3&ke%2Fy=te%3Ast4&ke%2Fy2=te%3Ast3"); + + } + + @Test + void testGetUrlEncodedQueryString2() { + matomoRequestBuilder.actionUrl("http://test.com").randomValue(RandomValue.fromString("random")) + .visitorId(VisitorId.fromHex("1234567890123456")); + defaultAuthToken = null; + + whenCreatesQuery(); + + assertThat(query).isEqualTo( + "idsite=42&rec=1&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random"); + + } + + @Test + void testVisitCustomVariableCustomVariable() { + matomoRequestBuilder.randomValue(RandomValue.fromString("random")).visitorId( + VisitorId.fromHex("1234567890123456")).siteId(3); + org.matomo.java.tracking.CustomVariable cv = new org.matomo.java.tracking.CustomVariable( + "visitKey", "visitVal"); + matomoRequestBuilder.visitCustomVariables(new CustomVariables().add(cv, 8)); + defaultAuthToken = null; + + whenCreatesQuery(); + + assertThat(request.getVisitCustomVariable(1)).isNull(); + assertThat(query).isEqualTo( + "rec=1&idsite=3&apiv=1&_id=1234567890123456&_cvar=%7B%228%22%3A%5B%22visitKey%22%2C%22visitVal%22%5D%7D&send_image=0&rand=random"); + } + + @Test + void doesNotAppendEmptyString() { + + matomoRequestBuilder.eventAction(""); + + whenCreatesQuery(); + + assertThat(query).isEqualTo( + "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&e_a=&send_image=0&rand=random-value"); + + } + + @Test + void testAuthTokenTT() { + + matomoRequestBuilder.authToken("1234"); + + assertThatThrownBy(this::whenCreatesQuery).isInstanceOf(IllegalArgumentException.class) + .hasMessage("Auth token must be exactly 32 characters long"); + } + + @Test + void createsQueryWithDimensions() { + matomoRequestBuilder.dimensions(asList("firstDimension", "secondDimension")); + + whenCreatesQuery(); + + assertThat(query).isEqualTo( + "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value&dimension1=firstDimension&dimension2=secondDimension"); + } + + @Test + void appendsCharsetParameters() { + matomoRequestBuilder.characterSet(StandardCharsets.ISO_8859_1); + + whenCreatesQuery(); + + assertThat(query).isEqualTo( + "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&cs=ISO-8859-1&send_image=0&rand=random-value"); + } + +} diff --git a/src/test/java/org/matomo/java/tracking/RequestValidatorTest.java b/src/test/java/org/matomo/java/tracking/RequestValidatorTest.java new file mode 100644 index 00000000..2cf0653e --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/RequestValidatorTest.java @@ -0,0 +1,162 @@ +package org.matomo.java.tracking; + +import org.junit.jupiter.api.Test; +import org.piwik.java.tracking.PiwikDate; +import org.piwik.java.tracking.PiwikLocale; + +import java.time.Instant; +import java.util.Locale; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class RequestValidatorTest { + + private final MatomoRequest request = new MatomoRequest(); + + @Test + void testEcommerceRevenue() { + + request.setEcommerceRevenue(20.0); + + assertThatThrownBy(() -> RequestValidator.validate(request, null)) + .isInstanceOf(MatomoException.class) + .hasMessage("Goal ID must be set if ecommerce parameters are used"); + + } + + @Test + void testEcommerceDiscount() { + request.setEcommerceDiscount(1.0); + + assertThatThrownBy(() -> RequestValidator.validate(request, null)) + .isInstanceOf(MatomoException.class) + .hasMessage("Goal ID must be set if ecommerce parameters are used"); + + } + + @Test + void testEcommerceId() { + request.setEcommerceId("1"); + + assertThatThrownBy(() -> RequestValidator.validate(request, null)) + .isInstanceOf(MatomoException.class) + .hasMessage("Goal ID must be set if ecommerce parameters are used"); + } + + @Test + void testEcommerceSubtotal() { + request.setEcommerceSubtotal(20.0); + + assertThatThrownBy(() -> RequestValidator.validate(request, null)) + .isInstanceOf(MatomoException.class) + .hasMessage("Goal ID must be set if ecommerce parameters are used"); + } + + @Test + void testEcommerceShippingCost() { + request.setEcommerceShippingCost(20.0); + + assertThatThrownBy(() -> RequestValidator.validate(request, null)) + .isInstanceOf(MatomoException.class) + .hasMessage("Goal ID must be set if ecommerce parameters are used"); + } + + @Test + void testEcommerceLastOrderTimestamp() { + request.setEcommerceLastOrderTimestamp(Instant.ofEpochSecond(1000L)); + + assertThatThrownBy(() -> RequestValidator.validate(request, null)) + .isInstanceOf(MatomoException.class) + .hasMessage("Goal ID must be set if ecommerce parameters are used"); + } + + @Test + void testEcommerceTax() { + request.setEcommerceTax(20.0); + + assertThatThrownBy(() -> RequestValidator.validate(request, null)) + .isInstanceOf(MatomoException.class) + .hasMessage("Goal ID must be set if ecommerce parameters are used"); + } + + @Test + void testEcommerceItemE() { + + request.addEcommerceItem(new EcommerceItem("sku", "name", "category", 1.0, 2)); + + assertThatThrownBy(() -> RequestValidator.validate(request, null)) + .isInstanceOf(MatomoException.class) + .hasMessage("Goal ID must be set if ecommerce parameters are used"); + } + + @Test + void testSearchResultsCount() { + + request.setSearchResultsCount(100L); + + assertThatThrownBy(() -> RequestValidator.validate(request, null)) + .isInstanceOf(MatomoException.class) + .hasMessage("Search query must be set if search results count is set"); + + } + + @Test + void testVisitorLongitude() { + request.setVisitorLongitude(20.5); + + assertThatThrownBy(() -> RequestValidator.validate(request, null)) + .isInstanceOf(MatomoException.class) + .hasMessage("Auth token must be present if longitude, latitude, region, city or country are set"); + } + + @Test + void testVisitorLatitude() { + request.setVisitorLatitude(10.5); + + assertThatThrownBy(() -> RequestValidator.validate(request, null)) + .isInstanceOf(MatomoException.class) + .hasMessage("Auth token must be present if longitude, latitude, region, city or country are set"); + } + + @Test + void testVisitorCity() { + request.setVisitorCity("city"); + + assertThatThrownBy(() -> RequestValidator.validate(request, null)) + .isInstanceOf(MatomoException.class) + .hasMessage("Auth token must be present if longitude, latitude, region, city or country are set"); + } + + @Test + void testVisitorRegion() { + request.setVisitorRegion("region"); + + assertThatThrownBy(() -> RequestValidator.validate(request, null)) + .isInstanceOf(MatomoException.class) + .hasMessage("Auth token must be present if longitude, latitude, region, city or country are set"); + } + + @Test + void testVisitorCountryTE() { + PiwikLocale country = new PiwikLocale(Locale.US); + request.setVisitorCountry(country); + + + assertThatThrownBy(() -> RequestValidator.validate(request, null)) + .isInstanceOf(MatomoException.class) + .hasMessage("Auth token must be present if longitude, latitude, region, city or country are set"); + } + + @Test + void testRequestDatetime() { + + PiwikDate date = new PiwikDate(1000L); + request.setRequestDatetime(date); + + assertThatThrownBy(() -> RequestValidator.validate(request, null)) + .isInstanceOf(MatomoException.class) + .hasMessage("Auth token must be present if request timestamp is more than four hours ago"); + + } + +} diff --git a/src/test/java/org/matomo/java/tracking/SenderIT.java b/src/test/java/org/matomo/java/tracking/SenderIT.java new file mode 100644 index 00000000..32763878 --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/SenderIT.java @@ -0,0 +1,96 @@ +package org.matomo.java.tracking; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import java.net.MalformedURLException; +import java.net.URI; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +class SenderIT { + + private static final WireMockServer wireMockServer = new WireMockServer( + WireMockConfiguration.options().dynamicPort()); + + @BeforeAll + static void beforeAll() { + wireMockServer.start(); + } + + @Test + void sendSingleFailsIfQueryIsMalformed() { + TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( + URI.create("telnet://localhost")).build(); + Sender sender = new Sender( + trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())).isInstanceOf( + InvalidUrlException.class).hasRootCause( + new MalformedURLException("unknown protocol: telnet")); + } + + @Test + void failsIfEndpointReturnsNotFound() { + TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( + URI.create(wireMockServer.baseUrl())).build(); + + Sender sender = new Sender( + trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())).isInstanceOf( + MatomoException.class).hasMessage("Tracking endpoint responded with code 404"); + } + + @Test + void failsIfCouldNotConnectToEndpoint() { + TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( + URI.create("http://localhost:1234")).build(); + + Sender sender = new Sender( + trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())).isInstanceOf( + MatomoException.class).hasMessage("Could not send request via GET"); + } + + @Test + void connectsViaProxy() throws Exception { + TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( + URI.create(wireMockServer.baseUrl())).proxyHost("localhost").proxyPort(wireMockServer.port()) + .build(); + + Sender sender = new Sender( + trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())).isInstanceOf( + MatomoException.class).hasMessage("Tracking endpoint responded with code 400"); + } + + @Test + void connectsViaProxyWithProxyUserNameAndPassword() throws Exception { + TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( + URI.create(wireMockServer.baseUrl())).proxyHost("localhost").proxyPort(wireMockServer.port()) + .proxyUserName("user").proxyPassword("password").build(); + + Sender sender = new Sender( + trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())).isInstanceOf( + MatomoException.class).hasMessage("Tracking endpoint responded with code 400"); + } + + @Test + void logsFailedTracking() throws Exception { + TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( + URI.create(wireMockServer.baseUrl())).logFailedTracking(true).build(); + + Sender sender = new Sender( + trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())).isInstanceOf( + MatomoException.class).hasMessage("Tracking endpoint responded with code 404"); + } + +} diff --git a/src/test/java/org/matomo/java/tracking/TrackerConfigurationTest.java b/src/test/java/org/matomo/java/tracking/TrackerConfigurationTest.java new file mode 100644 index 00000000..be5ebc90 --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/TrackerConfigurationTest.java @@ -0,0 +1,61 @@ +package org.matomo.java.tracking; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.net.URI; +import org.junit.jupiter.api.Test; + +class TrackerConfigurationTest { + + @Test + void validateDoesNotFailIfDefaultAuthTokenIsNull() throws Exception { + TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( + URI.create("https://matomo.example/matomo.php")).defaultSiteId(1).defaultAuthToken(null) + .build(); + trackerConfiguration.validate(); + } + + @Test + void validateFailsIfDefaultAuthTokenIsEmpty() throws Exception { + TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( + URI.create("https://matomo.example/matomo.php")).defaultSiteId(1).defaultAuthToken("") + .build(); + + assertThatThrownBy(trackerConfiguration::validate).isInstanceOf(IllegalArgumentException.class) + .hasMessage("Auth token must be exactly 32 characters long"); + } + + @Test + void validateFailsIfDefaultAuthTokenIsTooLong() throws Exception { + TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( + URI.create("https://matomo.example/matomo.php")).defaultSiteId(1).defaultAuthToken( + "123456789012345678901234567890123") + .build(); + + assertThatThrownBy(trackerConfiguration::validate).isInstanceOf(IllegalArgumentException.class) + .hasMessage("Auth token must be exactly 32 characters long"); + } + + @Test + void validateFailsIfDefaultAuthTokenIsTooShort() throws Exception { + TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( + URI.create("https://matomo.example/matomo.php")).defaultSiteId(1).defaultAuthToken( + "1234567890123456789012345678901") + .build(); + + assertThatThrownBy(trackerConfiguration::validate).isInstanceOf(IllegalArgumentException.class) + .hasMessage("Auth token must be exactly 32 characters long"); + } + + @Test + void validateFailsIfDefaultAuthTokenContainsInvalidCharacters() throws Exception { + TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( + URI.create("https://matomo.example/matomo.php")).defaultSiteId(1).defaultAuthToken( + "1234567890123456789012345678901!") + .build(); + + assertThatThrownBy(trackerConfiguration::validate).isInstanceOf(IllegalArgumentException.class) + .hasMessage("Auth token must contain only lowercase letters and numbers"); + } + +} diff --git a/src/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java b/src/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java new file mode 100644 index 00000000..a637e20f --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java @@ -0,0 +1,50 @@ +package org.matomo.java.tracking; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.regex.Pattern; +import org.junit.jupiter.api.Test; + +class TrackingParameterMethodTest { + + @Test + void validateParameterValueFailsIfPatternDoesNotMatch() { + TrackingParameterMethod trackingParameterMethod = TrackingParameterMethod.builder() + .parameterName("foo") + .pattern(Pattern.compile("bar")) + .build(); + + assertThatThrownBy(() -> trackingParameterMethod.validateParameterValue("baz")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid value for foo. Must match regex bar"); + } + + @Test + void doNothingIfPatternIsNull() { + TrackingParameterMethod trackingParameterMethod = TrackingParameterMethod.builder() + .parameterName("foo") + .build(); + + trackingParameterMethod.validateParameterValue("baz"); + } + + @Test + void doNothingIfParameterValueIsNotCharSequence() { + TrackingParameterMethod trackingParameterMethod = TrackingParameterMethod.builder() + .parameterName("foo") + .pattern(Pattern.compile("bar")) + .build(); + + trackingParameterMethod.validateParameterValue(1); + } + + @Test + void doNothingIfParameterValueIsNull() { + TrackingParameterMethod trackingParameterMethod = TrackingParameterMethod.builder() + .parameterName("foo") + .pattern(Pattern.compile("bar")) + .build(); + + trackingParameterMethod.validateParameterValue(null);} + +} diff --git a/src/test/java/org/matomo/java/tracking/parameters/AcceptLanguageTest.java b/src/test/java/org/matomo/java/tracking/parameters/AcceptLanguageTest.java new file mode 100644 index 00000000..6f289697 --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/parameters/AcceptLanguageTest.java @@ -0,0 +1,46 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking.parameters; + +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; + +class AcceptLanguageTest { + + @Test + void fromHeader() { + + AcceptLanguage acceptLanguage = AcceptLanguage.fromHeader( + "de,de-DE;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"); + + assertThat(acceptLanguage).hasToString( + "de,de-de;q=0.9,de-dd;q=0.9,en;q=0.8,en-gb;q=0.7,en-us;q=0.6"); + + } + + @ParameterizedTest + @NullAndEmptySource + void fromHeaderToleratesNull(String header) { + + AcceptLanguage acceptLanguage = AcceptLanguage.fromHeader(header); + + assertThat(acceptLanguage).isNull(); + + } + + @Test + void failsOnNullLanguageRange() { + assertThat(AcceptLanguage.builder().languageRanges(singletonList(null)).build()).hasToString( + ""); + } + +} diff --git a/src/test/java/org/matomo/java/tracking/parameters/CountryTest.java b/src/test/java/org/matomo/java/tracking/parameters/CountryTest.java new file mode 100644 index 00000000..f0ae93ee --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/parameters/CountryTest.java @@ -0,0 +1,165 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking.parameters; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.Locale; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; + +class CountryTest { + + @Test + void createsCountryFromCode() { + + Country country = Country.fromCode("DE"); + + assertThat(country).hasToString("de"); + + } + + @Test + void createsCountryFromAcceptLanguageHeader() { + + Country country = Country.fromLanguageRanges("en-GB;q=0.7,de,de-DE;q=0.9,en;q=0.8,en-US;q=0.6"); + + assertThat(country).hasToString("de"); + + } + + @ParameterizedTest + @NullAndEmptySource + void returnsNullOnEmptyRanges(String ranges) { + + Country country = Country.fromLanguageRanges(ranges); + + assertThat(country).isNull(); + + } + + @Test + void failsOnInvalidCountryCode() { + + assertThatThrownBy(() -> Country.fromCode("invalid")).isInstanceOf( + IllegalArgumentException.class).hasMessage( + "Invalid country code"); + + } + + @Test + void failsOnInvalidCountryCodeLength() { + + assertThatThrownBy(() -> Country.fromCode("invalid")).isInstanceOf( + IllegalArgumentException.class).hasMessage( + "Invalid country code"); + + } + + @Test + void returnsNullOnNullCode() { + + Country country = Country.fromCode(null); + + assertThat(country).isNull(); + + } + + @Test + void returnsNullOnEmptyCode() { + + Country country = Country.fromCode(""); + + assertThat(country).isNull(); + + } + + @Test + void returnsNullOnBlankCode() { + + Country country = Country.fromCode(" "); + + assertThat(country).isNull(); + + } + + @Test + void returnsNullOnNullRanges() { + + Country country = Country.fromLanguageRanges(null); + + assertThat(country).isNull(); + + } + + @Test + void returnsNullOnEmptyRanges() { + + Country country = Country.fromLanguageRanges(""); + + assertThat(country).isNull(); + + } + + @Test + void returnsNullOnBlankRanges() { + + Country country = Country.fromLanguageRanges(" "); + + assertThat(country).isNull(); + + } + + @Test + void failsOnInvalidRanges() { + + assertThatThrownBy(() -> Country.fromLanguageRanges("invalid")).isInstanceOf( + IllegalArgumentException.class).hasMessage( + "Invalid country code"); + + } + + @Test + void failsOnLocaleWithoutCountryCode() { + + assertThatThrownBy(() -> new Country(Locale.forLanguageTag("de"))).isInstanceOf( + IllegalArgumentException.class).hasMessage( + "Invalid locale"); + + } + + @Test + void setLocaleFailsOnNullLocale() { + + assertThatThrownBy(() -> new Country(Locale.forLanguageTag("de")).setLocale(null)).isInstanceOf( + IllegalArgumentException.class).hasMessage( + "Invalid locale"); + + } + + @Test + void setLocaleFailsOnNullCountryCode() { + + assertThatThrownBy(() -> new Country(Locale.forLanguageTag("de")).setLocale(Locale.forLanguageTag("de"))).isInstanceOf( + IllegalArgumentException.class).hasMessage( + "Invalid locale"); + + } + + @Test + void setLocaleFailsOnEmptyCountryCode() { + + assertThatThrownBy(() -> new Country(Locale.forLanguageTag("de")).setLocale(Locale.forLanguageTag("de"))).isInstanceOf( + IllegalArgumentException.class).hasMessage( + "Invalid locale"); + + } + +} diff --git a/src/test/java/org/matomo/java/tracking/parameters/CustomVariableTest.java b/src/test/java/org/matomo/java/tracking/parameters/CustomVariableTest.java new file mode 100644 index 00000000..f072d09e --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/parameters/CustomVariableTest.java @@ -0,0 +1,69 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking.parameters; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class CustomVariableTest { + + private CustomVariable customVariable; + + @BeforeEach + void setUp() { + customVariable = new CustomVariable("key", "value"); + } + + @Test + void testConstructorNullKey() { + try { + new CustomVariable(null, null); + fail("Exception should have been throw."); + } catch (NullPointerException e) { + assertThat(e.getLocalizedMessage()).isEqualTo("key is marked non-null but is null"); + } + } + + @Test + void testConstructorNullValue() { + try { + new CustomVariable("key", null); + fail("Exception should have been throw."); + } catch (NullPointerException e) { + assertThat(e.getLocalizedMessage()).isEqualTo("value is marked non-null but is null"); + } + } + + @Test + void testGetKey() { + assertThat(customVariable.getKey()).isEqualTo("key"); + } + + @Test + void testGetValue() { + assertThat(customVariable.getValue()).isEqualTo("value"); + } + + @Test + void equalsCustomVariable() { + CustomVariable variableA = new CustomVariable("a", "b"); + CustomVariable variableB = new CustomVariable("a", "b"); + assertThat(variableA).isEqualTo(variableB); + assertThat(variableA.hashCode()).isEqualTo(variableB.hashCode()); + CustomVariable c = new CustomVariable("a", "c"); + assertThat(variableA).isNotEqualTo(c); + assertThat(variableA.hashCode()).isNotEqualTo(c.hashCode()); + CustomVariable d = new CustomVariable("d", "b"); + assertThat(variableA).isNotEqualTo(d); + assertThat(variableA.hashCode()).isNotEqualTo(d.hashCode()); + } + +} diff --git a/src/test/java/org/matomo/java/tracking/parameters/CustomVariablesTest.java b/src/test/java/org/matomo/java/tracking/parameters/CustomVariablesTest.java new file mode 100644 index 00000000..cfb5087c --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/parameters/CustomVariablesTest.java @@ -0,0 +1,170 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking.parameters; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.fail; + +import org.junit.jupiter.api.Test; + +class CustomVariablesTest { + + private final CustomVariables customVariables = new CustomVariables(); + + @Test + void testAdd_CustomVariable() { + CustomVariable a = new CustomVariable("a", "b"); + assertThat(customVariables.isEmpty()).isTrue(); + customVariables.add(a); + assertThat(customVariables.isEmpty()).isFalse(); + assertThat(customVariables.get("a")).isEqualTo("b"); + assertThat(customVariables.get(1)).isEqualTo(a); + assertThat(customVariables).hasToString("{\"1\":[\"a\",\"b\"]}"); + CustomVariable b = new CustomVariable("c", "d"); + customVariables.add(b); + assertThat(customVariables.get("c")).isEqualTo("d"); + assertThat(customVariables.get(2)).isEqualTo(b); + assertThat(customVariables).hasToString("{\"1\":[\"a\",\"b\"],\"2\":[\"c\",\"d\"]}"); + CustomVariable c = new CustomVariable("a", "e"); + customVariables.add(c, 5); + assertThat(customVariables.get("a")).isEqualTo("b"); + assertThat(customVariables.get(5)).isEqualTo(c); + assertThat(customVariables.get(3)).isNull(); + assertThat(customVariables).hasToString( + "{\"1\":[\"a\",\"b\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"e\"]}"); + CustomVariable d = new CustomVariable("a", "f"); + customVariables.add(d); + assertThat(customVariables.get("a")).isEqualTo("f"); + assertThat(customVariables.get(1)).isEqualTo(d); + assertThat(customVariables.get(5)).isEqualTo(d); + assertThat(customVariables).hasToString( + "{\"1\":[\"a\",\"f\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"f\"]}"); + customVariables.remove("a"); + assertThat(customVariables.get("a")).isNull(); + assertThat(customVariables.get(1)).isNull(); + assertThat(customVariables.get(5)).isNull(); + assertThat(customVariables).hasToString("{\"2\":[\"c\",\"d\"]}"); + customVariables.remove(2); + assertThat(customVariables.get("c")).isNull(); + assertThat(customVariables.get(2)).isNull(); + assertThat(customVariables.isEmpty()).isTrue(); + assertThat(customVariables).hasToString("{}"); + } + + @Test + void testAddCustomVariableIndexLessThan1() { + try { + customVariables.add(new CustomVariable("a", "b"), 0); + fail("Exception should have been throw."); + } catch (IllegalArgumentException e) { + assertThat(e.getLocalizedMessage()).isEqualTo("Index must be greater than 0"); + } + } + + @Test + void equalCustomVariables() { + CustomVariables customVariables = new CustomVariables(); + customVariables.add(new CustomVariable("a", "b")); + customVariables.add(new CustomVariable("c", "d")); + customVariables.add(new CustomVariable("a", "e")); + customVariables.add(new CustomVariable("a", "f")); + assertThat(customVariables).isEqualTo(customVariables); + assertThat(customVariables).hasSameHashCodeAs(customVariables); + } + + @Test + void notEqualCustomVariables() { + CustomVariables customVariablesA = new CustomVariables(); + customVariablesA.add(new CustomVariable("a", "b")); + customVariablesA.add(new CustomVariable("c", "d")); + customVariablesA.add(new CustomVariable("a", "e")); + customVariablesA.add(new CustomVariable("a", "f")); + CustomVariables customVariablesB = new CustomVariables(); + customVariablesB.add(new CustomVariable("a", "b")); + customVariablesB.add(new CustomVariable("c", "d")); + customVariablesB.add(new CustomVariable("a", "e")); + assertThat(customVariablesA).isNotEqualTo(customVariablesB); + assertThat(customVariablesA).doesNotHaveSameHashCodeAs(customVariablesB); + } + + @Test + void testAddCustomVariableNull() { + assertThatThrownBy(() -> customVariables.add(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("variable" + + " is marked non-null but is null") + .hasNoCause(); + } + + @Test + void testAddCustomVariableKeyEmpty() { + assertThatThrownBy(() -> customVariables.add(new CustomVariable("", "b"))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Custom variable key must not be null or empty") + .hasNoCause(); + } + + @Test + void testAddCustomVariableValueEmpty() { + assertThatThrownBy(() -> customVariables.add(new CustomVariable("a", ""))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Custom variable value must not be null or empty") + .hasNoCause(); + } + + @Test + void testAddCustomVariableNullIndex() { + assertThatThrownBy(() -> customVariables.add(new CustomVariable("a", "b"), 0)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Index must be greater than 0") + .hasNoCause(); + } + + + @Test + void testAddNullCustomVariableIndex() { + assertThatThrownBy(() -> customVariables.add(null, 1)) + .isInstanceOf(NullPointerException.class) + .hasMessage("cv is marked non-null but is null") + .hasNoCause(); + } + + @Test + void testGetCustomVariableIntegerNull() { + assertThatThrownBy(() -> customVariables.get(0)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Index must be greater than 0") + .hasNoCause(); + } + + @Test + void testGetCustomVariableKeyNull() { + assertThatThrownBy(() -> customVariables.get(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("key is marked non-null but is null") + .hasNoCause(); + } + + @Test + void testGetCustomVariableKeyEmpty() { + assertThatThrownBy(() -> customVariables.get("")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("key must not be null or empty") + .hasNoCause(); + } + + @Test + void testRemoveCustomVariableKeyNull() { + assertThatThrownBy(() -> customVariables.remove(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("key is marked non-null but is null") + .hasNoCause(); + } + +} diff --git a/src/test/java/org/matomo/java/tracking/parameters/DeviceResolutionTest.java b/src/test/java/org/matomo/java/tracking/parameters/DeviceResolutionTest.java new file mode 100644 index 00000000..c87bb778 --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/parameters/DeviceResolutionTest.java @@ -0,0 +1,42 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking.parameters; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.Test; + +class DeviceResolutionTest { + + @Test + void formatsDeviceResolution() { + + DeviceResolution deviceResolution = DeviceResolution.builder().width(1280).height(1080).build(); + + assertThat(deviceResolution).hasToString("1280x1080"); + + } + + @Test + void returnsNullOnNull() { + + DeviceResolution deviceResolution = DeviceResolution.fromString(null); + + assertThat(deviceResolution).isNull(); + + } + + @Test + void failsOnWrongDimensionSize() { + assertThatThrownBy(() -> DeviceResolution.fromString("1920x1080x720")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Wrong dimension size"); + } + +} diff --git a/src/test/java/org/matomo/java/tracking/parameters/EcommerceItemsTest.java b/src/test/java/org/matomo/java/tracking/parameters/EcommerceItemsTest.java new file mode 100644 index 00000000..76f67d25 --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/parameters/EcommerceItemsTest.java @@ -0,0 +1,17 @@ +package org.matomo.java.tracking.parameters; + +import org.junit.jupiter.api.Test; +import org.matomo.java.tracking.EcommerceItem; + +import static org.assertj.core.api.Assertions.assertThat; + + +class EcommerceItemsTest { + + @Test + void formatsJson() { + EcommerceItems ecommerceItems = new EcommerceItems(); + ecommerceItems.add(new EcommerceItem("sku", "name", "category", 1.0, 1)); + assertThat(ecommerceItems).hasToString("[[\"sku\",\"name\",\"category\",1.0,1]]"); + } +} diff --git a/src/test/java/org/matomo/java/tracking/parameters/UniqueIdTest.java b/src/test/java/org/matomo/java/tracking/parameters/UniqueIdTest.java new file mode 100644 index 00000000..0bd59140 --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/parameters/UniqueIdTest.java @@ -0,0 +1,28 @@ +package org.matomo.java.tracking.parameters; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class UniqueIdTest { + + @Test + void createsRandomUniqueId() { + + UniqueId uniqueId = UniqueId.random(); + + assertThat(uniqueId.toString()).matches("[0-9a-zA-Z]{6}"); + + } + + @Test + void createsSameUniqueIds() { + + UniqueId uniqueId1 = UniqueId.fromValue(868686868L); + UniqueId uniqueId2 = UniqueId.fromValue(868686868); + + assertThat(uniqueId1).hasToString(uniqueId2.toString()); + + } + +} diff --git a/src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java b/src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java new file mode 100644 index 00000000..11dd23e5 --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java @@ -0,0 +1,164 @@ +package org.matomo.java.tracking.parameters; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class VisitorIdTest { + + private static Stream validHexStrings() { + return Stream.of( + Arguments.of("0", "0000000000000000"), + Arguments.of("0000", "0000000000000000"), + Arguments.of("1", "0000000000000001"), + Arguments.of("a", "000000000000000a"), + Arguments.of("1a", "000000000000001a"), + Arguments.of("01a", "000000000000001a"), + Arguments.of("1a2b", "0000000000001a2b"), + Arguments.of("1a2b3c", "00000000001a2b3c"), + Arguments.of("1a2b3c4d", "000000001a2b3c4d"), + Arguments.of("1a2b3c4d5e", "0000001a2b3c4d5e"), + Arguments.of("1A2B3C4D5E", "0000001a2b3c4d5e"), + Arguments.of("1a2b3c4d5e6f", "00001a2b3c4d5e6f"), + Arguments.of("1a2b3c4d5e6f7a", "001a2b3c4d5e6f7a") + ); + } + + @Test + void hasCorrectFormat() { + + VisitorId visitorId = VisitorId.random(); + + assertThat(visitorId.toString()).matches("^[a-z0-9]{16}$"); + + } + + @Test + void createsRandomVisitorId() { + + VisitorId first = VisitorId.random(); + VisitorId second = VisitorId.random(); + + assertThat(first).doesNotHaveToString(second.toString()); + + } + + @Test + void fixedVisitorIdForLongHash() { + + VisitorId visitorId = VisitorId.fromHash(987654321098765432L); + + assertThat(visitorId).hasToString("0db4da5f49f8b478"); + + } + + @Test + void fixedVisitorIdForIntHash() { + + VisitorId visitorId = VisitorId.fromHash(777777777); + + assertThat(visitorId).hasToString("000000002e5bf271"); + + } + + @Test + void sameVisitorIdForSameHash() { + + VisitorId first = VisitorId.fromHash(1234567890L); + VisitorId second = VisitorId.fromHash(1234567890); + + assertThat(first).hasToString(second.toString()); + + } + + @Test + void alwaysTheSameToString() { + + VisitorId visitorId = VisitorId.random(); + + assertThat(visitorId).hasToString(visitorId.toString()); + + } + + @Test + void createsVisitorIdFrom16CharacterHex() { + + VisitorId visitorId = VisitorId.fromHex("1234567890abcdef"); + + assertThat(visitorId).hasToString("1234567890abcdef"); + + } + + @Test + void createsVisitorIdFrom1CharacterHex() { + + VisitorId visitorId = VisitorId.fromHex("a"); + + assertThat(visitorId).hasToString("000000000000000a"); + + } + + @Test + void createsVisitorIdFrom2CharacterHex() { + + VisitorId visitorId = VisitorId.fromHex("12"); + + assertThat(visitorId).hasToString("0000000000000012"); + + } + + @Test + void failsOnInvalidHexString() { + + assertThatThrownBy(() -> VisitorId.fromHex("invalid123456789")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Input must be a valid hex string") + ; + + } + + @ParameterizedTest + @ValueSource(strings = {"g", "gh", "ghi", "ghij", "ghijk", "ghijkl", "ghijklm", "ghijklmn", "ghijklmn", "-1"}) + void failsOnInvalidHexString(String hex) { + assertThatThrownBy(() -> VisitorId.fromHex(hex)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Input must be a valid hex string") + ; + } + + @ParameterizedTest + @MethodSource("validHexStrings") + void createsVisitorIdFromHex(String hex, String expected) { + + VisitorId visitorId = VisitorId.fromHex(hex); + + assertThat(visitorId).hasToString(expected); + + } + + @ParameterizedTest + @ValueSource(strings = {"", " "}) + void failsOnEmptyStrings(String hex) { + assertThatThrownBy(() -> VisitorId.fromHex(hex)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Hex string must not be null or empty") + ; + } + + @ParameterizedTest + @ValueSource(strings = {"1234567890abcdefg", "1234567890abcdeff"}) + void failsOnInvalidHexStringLength(String hex) { + assertThatThrownBy(() -> VisitorId.fromHex(hex)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Hex string must not be longer than 16 characters") + ; + } + +} diff --git a/src/test/java/org/piwik/java/tracking/CustomVariableTest.java b/src/test/java/org/piwik/java/tracking/CustomVariableTest.java new file mode 100644 index 00000000..4492d0a2 --- /dev/null +++ b/src/test/java/org/piwik/java/tracking/CustomVariableTest.java @@ -0,0 +1,17 @@ +package org.piwik.java.tracking; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class CustomVariableTest { + + @Test + void createsCustomVariable() { + CustomVariable customVariable = new CustomVariable("key", "value"); + + assertThat(customVariable.getKey()).isEqualTo("key"); + assertThat(customVariable.getValue()).isEqualTo("value"); + } + +} diff --git a/src/test/java/org/piwik/java/tracking/EcommerceItemTest.java b/src/test/java/org/piwik/java/tracking/EcommerceItemTest.java new file mode 100644 index 00000000..78a311f5 --- /dev/null +++ b/src/test/java/org/piwik/java/tracking/EcommerceItemTest.java @@ -0,0 +1,19 @@ +package org.piwik.java.tracking; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class EcommerceItemTest { + + @Test + void createsEcItem() { + EcommerceItem item = new EcommerceItem("sku", "name", "category", 1.0, 1); + + assertThat(item.getSku()).isEqualTo("sku"); + assertThat(item.getName()).isEqualTo("name"); + assertThat(item.getCategory()).isEqualTo("category"); + assertThat(item.getPrice()).isEqualTo(1.0); + assertThat(item.getQuantity()).isEqualTo(1); + } +} From e32e6e276821f3dcdf7bd97d688ab93988568e3a Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Thu, 2 Nov 2023 22:30:08 +0100 Subject: [PATCH 138/467] Update build status badge in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 232366e1..220c475b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Matomo Java Tracker [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/matomo-java-tracker/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/matomo-java-tracker) -[![Build Status](https://travis-ci.org/matomo-org/matomo-java-tracker.svg?branch=master)](https://travis-ci.org/matomo-org/matomo-java-tracker) +[![Build Status](https://github.com/matomo-org/matomo-java-tracker/actions/workflows/build.yml/badge.svg)](https://github.com/matomo-org/matomo-java-tracker/actions/workflows/build.yml) [![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/matomo-org/matomo-java-tracker.svg)](https://isitmaintained.com/project/matomo-org/matomo-java-tracker "Average time to resolve an issue") [![Percentage of issues still open](https://isitmaintained.com/badge/open/matomo-org/matomo-java-tracker.svg)](https://isitmaintained.com/project/matomo-org/matomo-java-tracker "Percentage of issues still open") From e8b255b35b94b1b56af98ee46427bc12d2d5de9d Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Thu, 2 Nov 2023 22:36:21 +0100 Subject: [PATCH 139/467] Add Javadoc generation (#141) --- .github/workflows/release.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e1bd90b8..e1b43070 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,3 +34,10 @@ jobs: publish: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: MathieuSoysal/Javadoc-publisher.yml@v2.4.0 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + javadoc-branch: javadoc + java-version: 8 + target-folder: javadoc + project: maven From bead80bdc6a90b051a9de691d57ef68b4052ec71 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Thu, 2 Nov 2023 22:46:15 +0100 Subject: [PATCH 140/467] Use HTTPS developer connection --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b4818b1d..9b2fcdc7 100644 --- a/pom.xml +++ b/pom.xml @@ -39,8 +39,8 @@ - scm:git:ssh://git@github.com/matomo-org/matomo-java-tracker.git - scm:git:ssh://git@github.com/matomo-org/matomo-java-tracker.git + scm:git:https://@github.com/matomo-org/matomo-java-tracker.git + scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker HEAD From e374fb1624a13f2c7d64d5dd9ad366f23668844e Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Thu, 2 Nov 2023 21:47:33 +0000 Subject: [PATCH 141/467] [maven-release-plugin] prepare release matomo-java-tracker-3.0.0-rc1 --- pom.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 9b2fcdc7..2cf25c01 100644 --- a/pom.xml +++ b/pom.xml @@ -1,10 +1,9 @@ - + 4.0.0 org.piwik.java.tracking matomo-java-tracker - 3.0.0-rc1-SNAPSHOT + 3.0.0-rc1 jar Matomo Java Tracker @@ -42,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - HEAD + matomo-java-tracker-3.0.0-rc1 From 31a7ec13eff78a5fed2c49469cd0d69bc242f922 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Thu, 2 Nov 2023 21:47:34 +0000 Subject: [PATCH 142/467] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2cf25c01..1047eed0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker - 3.0.0-rc1 + 3.0.0-rc2-SNAPSHOT jar Matomo Java Tracker @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - matomo-java-tracker-3.0.0-rc1 + HEAD From 8c1049c5a432c6d60580a376d8aafb3a82cc7c03 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Thu, 2 Nov 2023 23:01:32 +0100 Subject: [PATCH 143/467] Remove pitest (not compatible with JUnit 5) --- README.md | 10 +--------- pom.xml | 13 ------------- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/README.md b/README.md index 220c475b..ad1ae341 100644 --- a/README.md +++ b/README.md @@ -375,14 +375,6 @@ version can be used in your local Maven repository for testing purposes, e.g. ``` -This project also supports [Pitest](http://pitest.org/) mutation testing. This report can be generated by calling - -```shell -mvn org.pitest:pitest-maven:mutationCoverage -``` - -and will produce an HTML report at `target/pit-reports/YYYYMMDDHHMI` - Clean this project using ```shell @@ -400,7 +392,7 @@ free to: * Write awesome test to test your awesome code * Verify that everything is working as it should by running _mvn test_. If everything passes, you may want to make sure that your tests are covering everything you think they are! - Run `mvn org.pitest:pitest-maven:mutationCoverage` to find out! + Run `mvn verify` to find out! * Commit this code to your repository * Submit a pull request from your branch to our dev branch and let us know why you made the changes you did * We'll take a look at your request and work to get it integrated with the repo! diff --git a/pom.xml b/pom.xml index 1047eed0..90efbbee 100644 --- a/pom.xml +++ b/pom.xml @@ -203,19 +203,6 @@ - - org.pitest - pitest-maven - 1.15.2 - - - org.matomo.java.tracking* - - - org.matomo.java.tracking* - - - org.jacoco jacoco-maven-plugin From 285dcaa93f9a3102868a16b289724f30664b253d Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Thu, 2 Nov 2023 23:31:23 +0100 Subject: [PATCH 144/467] Enhance README.md --- README.md | 149 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 84 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index ad1ae341..b1438320 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,54 @@ [![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/matomo-org/matomo-java-tracker.svg)](https://isitmaintained.com/project/matomo-org/matomo-java-tracker "Average time to resolve an issue") [![Percentage of issues still open](https://isitmaintained.com/badge/open/matomo-org/matomo-java-tracker.svg)](https://isitmaintained.com/project/matomo-org/matomo-java-tracker "Percentage of issues still open") -Official Java implementation of the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api). +Matomo Java Tracker is the official Java implementation of +the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api). The tracker allows you to track +visits, goals and ecommerce transactions and items. It is designed to be used in server-side applications, such as +Java-based web applications or web services. + +Features include: + +* Track page views, goals, ecommerce transactions and items +* Supports custom dimensions and custom variables +* Includes tracking parameters for campaigns, events, downloads, outlinks, site search, devices, visiors +* Supports Java 8 and higher +* Contains nearly no dependencies +* Allows asynchronous requests +* Supports Matomo 4 and 5 +* Single and multiple requests can be sent + +## What Is New? + +Do you still use Matomo Java Tracker 2.x? We created version 3, that is compatible with Matomo 4 and 5 and contains less +dependencies. Release notes can be found here: https://github.com/matomo-org/matomo-java-tracker/releases ## Javadoc The Javadoc for this project is hosted as a GitHub page for this repo. The latest Javadoc can be -found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/HEAD/index.html). Javadoc for the latest and all -releases can be found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/index.html). +found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/index.html). + +Javadoc folder older versions can be found here: https://javadoc.io/doc/org.piwik.java.tracking/matomo-java-tracker + +## Need help? + +* Open an issue in this repository: https://github.com/matomo-org/matomo-java-tracker/issues +* Use discussions: https://github.com/matomo-org/matomo-java-tracker/discussions +* Ask your question on Stackoverflow with the tag `matomo`: https://stackoverflow.com/questions/tagged/matomo +* Use other communication channels: + * Matomo forum: https://forum.matomo.org/ + * Matomo Slack: https://matomo.org/slack/ + * Matomo Telegram: https://t.me/matomo_analytics + * Matomo Discord: https://discord.gg/7M2gtrT + * Matomo Matrix: https://matrix.to/#/#matomo:matrix.org + * Matomo IRC: https://web.libera.chat/#matomo + * Matomo Twitter: https://twitter.com/matomo + * Matomo LinkedIn: https://www.linkedin.com/company/matomo-analytics + * Matomo Facebook: https://www.facebook.com/matomo.org + * Matomo Instagram: https://www.instagram.com/matomo_org/ + * Matomo YouTube: https://www.youtube.com/user/MatomoAnalytics + * Matomo Reddit: https://www.reddit.com/r/matomo/ + * Matomo Pinterest: https://www.pinterest.com/matomoorg/ + * Matomo Mastodon: https://fosstodon.org/@matomo ## Using this API @@ -24,7 +65,7 @@ Add a dependency on Matomo Java Tracker using Maven: org.piwik.java.tracking matomo-java-tracker - 2.1 + 3.0.0-rc1 ``` @@ -32,7 +73,7 @@ or Gradle: ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker:2.1") + implementation("org.piwik.java.tracking:matomo-java-tracker:3.0.0-rc1") } ``` @@ -99,20 +140,9 @@ See [Tracking Campaigns](https://matomo.org/docs/tracking-campaigns/) for more i All HTTP query parameters denoted on the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate -getters and setters. See _MatomoRequest.java_ for the mappings of the parameters to their corresponding -Java getters/setters. - -Some parameters are dependent on the state of other parameters: -_EcommerceEnabled_ must be called before the following parameters are set: *EcommerceId* and * -EcommerceRevenue*. - -_EcommerceId_ and _EcommerceRevenue_ must be set before the following parameters are -set: *EcommerceDiscount*, *EcommerceItem*, *EcommerceLastOrderTimestamp*, * -EcommerceShippingCost*, *EcommerceSubtotal*, and *EcommerceTax*. +getters and setters. See _MatomoRequest.java_ for the mappings of the parameters to their corresponding attributes. -_AuthToken_ must be set before the following parameters are set: *VisitorCity*, * -VisitorCountry*, *VisitorIp*, *VisitorLatitude*, *VisitorLongitude*, and *VisitorRegion* -. +Requests are validated prior to sending. If a request is invalid, a `MatomoException` will be thrown. ### Sending Requests @@ -121,13 +151,16 @@ Create a MatomoTracker using the constructor ```java package example; +import java.net.URI; import org.matomo.java.tracking.MatomoTracker; +import org.matomo.java.tracking.TrackerConfiguration; public class YourImplementation { public void yourMethod() { - MatomoTracker tracker = new MatomoTracker("https://your-matomo-domain.tld/matomo.php"); + MatomoTracker tracker = new MatomoTracker( + TrackerConfiguration.builder().apiEndpoint(URI.create("https://your-matomo-domain.tld/matomo.php")).build()); } @@ -157,20 +190,12 @@ public class YourImplementation { MatomoRequest request = MatomoRequest.builder().siteId(42).actionUrl("https://www.mydomain.com/some/page").actionName("Signup").build(); - MatomoTracker tracker = new MatomoTracker("https://your-matomo-domain.tld/matomo.php"); - try { - Future response = tracker.sendRequestAsync(request); - // usually not needed: - HttpResponse httpResponse = response.get(); - int statusCode = httpResponse.getStatusLine().getStatusCode(); - if (statusCode > 399) { - // problem - } - } catch (IOException e) { - throw new UncheckedIOException("Could not send request to Matomo", e); - } catch (ExecutionException | InterruptedException e) { - throw new RuntimeException("Error while getting response", e); - } + MatomoTracker tracker = new MatomoTracker( + TrackerConfiguration.builder().apiEndpoint(URI.create("https://your-matomo-domain.tld/matomo.php")).build()); + + CompletableFuture future = tracker.sendRequestAsync(request); + // execute the request: + future.get(); } @@ -183,6 +208,7 @@ send a bulk request. Place your requests in an _Iterable_ data structure and cal ```java package example; +import java.util.concurrent.CompletableFuture; import org.apache.http.HttpResponse; import org.matomo.java.tracking.MatomoRequest; import org.matomo.java.tracking.MatomoTracker; @@ -203,20 +229,12 @@ public class YourImplementation { requests.add(builder.actionUrl("https://www.mydomain.com/some/page").actionName("Some Page").build()); requests.add(builder.actionUrl("https://www.mydomain.com/another/page").actionName("Another Page").build()); - MatomoTracker tracker = new MatomoTracker("https://your-matomo-domain.tld/matomo.php"); - try { - Future response = tracker.sendBulkRequestAsync(requests); - // usually not needed: - HttpResponse httpResponse = response.get(); - int statusCode = httpResponse.getStatusLine().getStatusCode(); - if (statusCode > 399) { - // problem - } - } catch (IOException e) { - throw new UncheckedIOException("Could not send request to Matomo", e); - } catch (ExecutionException | InterruptedException e) { - throw new RuntimeException("Error while getting response", e); - } + MatomoTracker tracker = new MatomoTracker( + TrackerConfiguration.builder().apiEndpoint(URI.create("https://your-matomo-domain.tld/matomo.php")).build()); + + CompletableFuture future = tracker.sendBulkRequestAsync(requests); + // execute the request + future.get(); } @@ -230,6 +248,7 @@ the bulk request through ```java package example; +import java.util.concurrent.CompletableFuture; import org.apache.http.HttpResponse; import org.matomo.java.tracking.MatomoLocale; import org.matomo.java.tracking.MatomoRequest; @@ -253,23 +272,16 @@ public class YourImplementation { requests.add(builder.actionUrl("https://www.mydomain.com/another/page").actionName("Another Page") .visitorCountry(new MatomoLocale(Locale.GERMANY)).build()); - MatomoTracker tracker = new MatomoTracker("https://your-matomo-domain.tld/matomo.php"); - try { - Future response = tracker.sendBulkRequestAsync( - requests, - "33dc3f2536d3025974cccb4b4d2d98f4" - ); // second parameter is authentication token need for country override - // usually not needed: - HttpResponse httpResponse = response.get(); - int statusCode = httpResponse.getStatusLine().getStatusCode(); - if (statusCode > 399) { - // problem - } - } catch (IOException e) { - throw new UncheckedIOException("Could not send request to Matomo", e); - } catch (ExecutionException | InterruptedException e) { - throw new RuntimeException("Error while getting response", e); - } + MatomoTracker tracker = new MatomoTracker( + TrackerConfiguration.builder().apiEndpoint(URI.create("https://your-matomo-domain.tld/matomo.php")).build()); + + CompletableFuture future = tracker.sendBulkRequestAsync( + requests, + "33dc3f2536d3025974cccb4b4d2d98f4" + ); // second parameter is authentication token need for country override + // execute the request: + future.get(); + } @@ -371,7 +383,7 @@ version can be used in your local Maven repository for testing purposes, e.g. org.piwik.java.tracking matomo-java-tracker - 2.1-SNAPSHOT + 3.0.0-rc2-SNAPSHOT ``` @@ -397,6 +409,13 @@ free to: * Submit a pull request from your branch to our dev branch and let us know why you made the changes you did * We'll take a look at your request and work to get it integrated with the repo! +## Further information + +* [Matomo PHP Tracker](https://github.com/matomo-org/matomo-php-tracker) +* [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) +* [Introducting the Matomo Java Tracker](https://matomo.org/blog/2015/11/introducing-piwik-java-tracker/) +* [Tracking API User Guide](https://matomo.org/guide/apis/tracking-api/) + ## License This software is released under the BSD 3-Clause license. See [LICENSE](LICENSE). From ee2f16b9bbc087c3dd36a2a24b842bcf0e0b8462 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Thu, 2 Nov 2023 23:36:52 +0100 Subject: [PATCH 145/467] Enhance README.md again --- README.md | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b1438320..0dcd2b1b 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,10 @@ Features include: * Allows asynchronous requests * Supports Matomo 4 and 5 * Single and multiple requests can be sent +* Well documented with Javadoc +* Ensures correct values are sent to Matomo Tracking API +* Includes debug and error logging +* Easy to integrate in frameworks, e.g. Spring: Just create the MatomoTracker Spring bean and use it in other beans ## What Is New? @@ -77,6 +81,12 @@ dependencies { } ``` +or Gradle with Kotlin DSL: + +```kotlin +implementation("org.piwik.java.tracking:matomo-java-tracker:3.0.0-rc1") +``` + ### Create a Request Each MatomoRequest represents an action the user has taken that you want tracked by your Matomo server. Create a @@ -167,7 +177,28 @@ public class YourImplementation { } ``` -using the Matomo Endpoint URL as the first parameter. +The Matomo Tracker currently supports the following builder methods: + +* `.apiEndpoint(...)` An `URI` object that points to the Matomo Tracking API endpoint of your Matomo installation. Must be set. +* `.defaultSiteId(...)` If you provide a default site id, it will be taken if the action does not contain a site id. +* `.defaultTokenAuth(...)` If you provide a default token auth, it will be taken if the action does not contain a token + auth. +* `.delay(...)` The duration on how long the tracker collects actions until they will be sent out as a bulk request. + Default: 1 seconds +* `.enabled(...)` The tracker is enabled per default. You can disable it per configuration with this flag. +* `.logFailedTracking(...)` Will send errors to the log if the Matomo Tracking API responds with an errornous HTTP code +* `.connectTimeout(...)` allows you to change the default connection timeout of 10 seconds. 0 is + interpreted as infinite, null uses the system default +* `.socketTimeout(...)` allows you to change the default socket timeout of 10 seconds. 0 is + interpreted as infinite, null uses the system default +* `.userAgent(...)` used by the request made to the endpoint is `MatomoJavaClient` per default. You can change it by using this builder method. +* `.proxyHost(...)` The hostname or IP address of an optional HTTP proxy. `proxyPort` must be + configured as well +* `.proxyPort(...)` The port of an HTTP proxy. `proxyHost` must be configured as well. +* `.proxyUserName(...)` If the HTTP proxy requires a user name for basic authentication, it can be + configured with this method. Proxy host, port and password must also be set. +* `.proxyPassword(...)` The corresponding password for the basic auth proxy user. The proxy host, + port and user name must be set as well. To send a single request, call @@ -393,6 +424,11 @@ Clean this project using mvn clean ``` +## Versioning + +We use [SemVer](http://semver.org/) for versioning. For the versions available, see +the [tags on this repository](https://github.com/matomo-org/matomo-java-tracker/tags). + ## Contribute Have a fantastic feature idea? Spot a bug? We would absolutely love for you to contribute to this project! Please feel @@ -409,6 +445,9 @@ free to: * Submit a pull request from your branch to our dev branch and let us know why you made the changes you did * We'll take a look at your request and work to get it integrated with the repo! +Please read [the contribution document](CONTRIBUTING.md) for details on our code of conduct, and the +process for submitting pull requests to us. + ## Further information * [Matomo PHP Tracker](https://github.com/matomo-org/matomo-php-tracker) From 974053a0147b96853c8902dd68c5419a05b59579 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Thu, 2 Nov 2023 23:51:25 +0100 Subject: [PATCH 146/467] Add ToC and more to README.md --- README.md | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0dcd2b1b..50b77395 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,37 @@ Features include: * Includes debug and error logging * Easy to integrate in frameworks, e.g. Spring: Just create the MatomoTracker Spring bean and use it in other beans +## Table of Contents + +* [What Is New?](#what-is-new) +* [Javadoc](#javadoc) +* [Need help?](#need-help) +* [Using this API](#using-this-api) +* [Migration from Version 2 to 3](#migration-from-version-2-to-3) +* [Building](#building) +* [Versioning](#versioning) +* [Contribute](#contribute) +* [Further information](#further-information) +* [License](#license) + ## What Is New? Do you still use Matomo Java Tracker 2.x? We created version 3, that is compatible with Matomo 4 and 5 and contains less dependencies. Release notes can be found here: https://github.com/matomo-org/matomo-java-tracker/releases +Here are the most important changes: + +* Matomo Java Tracker 3.x is compatible with Matomo 4 and 5 +* less dependencies +* new dimension parameter +* new visitor id parameter +* new random value parameter +* new device resolution parameter +* new locale parameter +* new country parameter +* new page view id parameter +* special types allow to provide valid parameters now + ## Javadoc The Javadoc for this project is hosted as a GitHub page for this repo. The latest Javadoc can be @@ -37,6 +63,8 @@ found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/index.html Javadoc folder older versions can be found here: https://javadoc.io/doc/org.piwik.java.tracking/matomo-java-tracker +You can also build the Javadoc yourself. See the section _Building_ below. + ## Need help? * Open an issue in this repository: https://github.com/matomo-org/matomo-java-tracker/issues @@ -60,6 +88,10 @@ Javadoc folder older versions can be found here: https://javadoc.io/doc/org.piwi ## Using this API +See the following sections for information on how to use this API. For more information, see the Javadoc. We also recommend +to read the [Tracking API User Guide](https://matomo.org/guide/apis/tracking-api/). The Matomo Tracking HTTP API is well +documented and contains many examples. See https://developer.matomo.org/api-reference/tracking-api + ### Add library to your build Add a dependency on Matomo Java Tracker using Maven: @@ -397,9 +429,6 @@ following breaking changes: ## Building -You need a GPG signing key on your machine. Please follow these -instructions: https://docs.github.com/en/authentication/managing-commit-signature-verification/generating-a-new-gpg-key - This project can be tested and built by calling ```shell @@ -448,12 +477,16 @@ free to: Please read [the contribution document](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us. +We use Checkstyle and JaCoCo to ensure code quality. Please run `mvn verify` before submitting a pull request. Please +provide tests for your changes. We use JUnit 5 for testing. Coverage should be at least 80%. + ## Further information * [Matomo PHP Tracker](https://github.com/matomo-org/matomo-php-tracker) * [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) * [Introducting the Matomo Java Tracker](https://matomo.org/blog/2015/11/introducing-piwik-java-tracker/) * [Tracking API User Guide](https://matomo.org/guide/apis/tracking-api/) +* [Matomo](https://matomo.org/) - The Matomo project ## License @@ -462,3 +495,5 @@ This software is released under the BSD 3-Clause license. See [LICENSE](LICENSE) ## Copyright Copyright (c) 2015 General Electric Company. All rights reserved. + + From e2fb8d62f1f45f39156756d591ad6d3f2f861134 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Fri, 3 Nov 2023 10:40:52 +0100 Subject: [PATCH 147/467] Update README.md and integrate coverage badge --- .github/workflows/build.yml | 2 + README.md | 121 ++++++++++++++---------------------- 2 files changed, 48 insertions(+), 75 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7abf6b97..f62bdf95 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,3 +25,5 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} min-coverage-overall: 80 min-coverage-changed-files: 80 + - uses: cicirello/jacoco-badge-generator@v2.11.0 + diff --git a/README.md b/README.md index 50b77395..8433e10f 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ [![Build Status](https://github.com/matomo-org/matomo-java-tracker/actions/workflows/build.yml/badge.svg)](https://github.com/matomo-org/matomo-java-tracker/actions/workflows/build.yml) [![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/matomo-org/matomo-java-tracker.svg)](https://isitmaintained.com/project/matomo-org/matomo-java-tracker "Average time to resolve an issue") [![Percentage of issues still open](https://isitmaintained.com/badge/open/matomo-org/matomo-java-tracker.svg)](https://isitmaintained.com/project/matomo-org/matomo-java-tracker "Percentage of issues still open") +![Line Coverage](.github/badges/jacoco.svg) +![Branch Coverage](.github/badges/branches.svg) Matomo Java Tracker is the official Java implementation of the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api). The tracker allows you to track @@ -25,6 +27,15 @@ Features include: * Includes debug and error logging * Easy to integrate in frameworks, e.g. Spring: Just create the MatomoTracker Spring bean and use it in other beans +Further information on Matomo and Matomo HTTP tracking: + +* [Matomo PHP Tracker](https://github.com/matomo-org/matomo-php-tracker) +* [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) +* [Introducting the Matomo Java Tracker](https://matomo.org/blog/2015/11/introducing-piwik-java-tracker/) +* [Tracking API User Guide](https://matomo.org/guide/apis/tracking-api/) +* [Matomo Developer](https://developer.matomo.org/) +* [The Matomo project](https://matomo.org/) + ## Table of Contents * [What Is New?](#what-is-new) @@ -35,7 +46,6 @@ Features include: * [Building](#building) * [Versioning](#versioning) * [Contribute](#contribute) -* [Further information](#further-information) * [License](#license) ## What Is New? @@ -48,43 +58,21 @@ Here are the most important changes: * Matomo Java Tracker 3.x is compatible with Matomo 4 and 5 * less dependencies * new dimension parameter -* new visitor id parameter -* new random value parameter -* new device resolution parameter -* new locale parameter -* new country parameter -* new page view id parameter * special types allow to provide valid parameters now ## Javadoc The Javadoc for this project is hosted as a GitHub page for this repo. The latest Javadoc can be -found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/index.html). - -Javadoc folder older versions can be found here: https://javadoc.io/doc/org.piwik.java.tracking/matomo-java-tracker - -You can also build the Javadoc yourself. See the section _Building_ below. +found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/index.html). Javadoc folder older versions can be found here: https://javadoc.io/doc/org.piwik.java.tracking/matomo-java-tracker +You can also build the Javadoc yourself. See the section [Building](#building) below. ## Need help? -* Open an issue in this repository: https://github.com/matomo-org/matomo-java-tracker/issues -* Use discussions: https://github.com/matomo-org/matomo-java-tracker/discussions -* Ask your question on Stackoverflow with the tag `matomo`: https://stackoverflow.com/questions/tagged/matomo -* Use other communication channels: - * Matomo forum: https://forum.matomo.org/ - * Matomo Slack: https://matomo.org/slack/ - * Matomo Telegram: https://t.me/matomo_analytics - * Matomo Discord: https://discord.gg/7M2gtrT - * Matomo Matrix: https://matrix.to/#/#matomo:matrix.org - * Matomo IRC: https://web.libera.chat/#matomo - * Matomo Twitter: https://twitter.com/matomo - * Matomo LinkedIn: https://www.linkedin.com/company/matomo-analytics - * Matomo Facebook: https://www.facebook.com/matomo.org - * Matomo Instagram: https://www.instagram.com/matomo_org/ - * Matomo YouTube: https://www.youtube.com/user/MatomoAnalytics - * Matomo Reddit: https://www.reddit.com/r/matomo/ - * Matomo Pinterest: https://www.pinterest.com/matomoorg/ - * Matomo Mastodon: https://fosstodon.org/@matomo +* Open an issue in the (Issue Tracker)[https://github.com/matomo-org/matomo-java-tracker/issues] +* Use [our GitHub discussions](https://github.com/matomo-org/matomo-java-tracker/discussions) +* Ask your question on (Stackoverflow with the tag `matomo`)[https://stackoverflow.com/questions/tagged/matomo] +* Create a thread in the [Matomo Forum](https://forum.matomo.org/) +* Contact [Matomo Support](https://matomo.org/support/) ## Using this API @@ -178,11 +166,10 @@ public class YourImplementation { } ``` -See [Tracking Campaigns](https://matomo.org/docs/tracking-campaigns/) for more information. - -All HTTP query parameters denoted on +See [Tracking Campaigns](https://matomo.org/docs/tracking-campaigns/) for more information. All HTTP query parameters denoted on the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate -getters and setters. See _MatomoRequest.java_ for the mappings of the parameters to their corresponding attributes. +getters and setters. See [MatomoRequest.java](src/main/java/org/matomo/api/MatomoRequest.java) for the mappings of the +parameters to their corresponding attributes. Requests are validated prior to sending. If a request is invalid, a `MatomoException` will be thrown. @@ -232,7 +219,7 @@ The Matomo Tracker currently supports the following builder methods: * `.proxyPassword(...)` The corresponding password for the basic auth proxy user. The proxy host, port and user name must be set as well. -To send a single request, call +To send a single request synchronously via GET, call ```java package example; @@ -363,9 +350,9 @@ following breaking changes: * The parameter `actionTime` (`gt_ms`) is no longer supported by Matomo 5 and was removed. * Many methods marked as deprecated in version 2 were removed. Please see the - former [Javadoc](https://matomo-org.github.io/matomo-java-tracker/javadoc/HEAD/index.html) of version 2 to get the + former [Javadoc](https://javadoc.io/doc/org.piwik.java.tracking/matomo-java-tracker/2.1/index.html) of version 2 to get the deprecated methods. -* We removed the vulnerable dependency to the Apache HTTP client. Callbacks are no longer of type `FutureCallback`, but +* We removed the vulnerable dependency to the Apache HTTP client. Callbacks are no longer of type `FutureCallback`, but `Consumer` instead. * The `send...` methods of `MatomoTracker` no longer return a value (usually Matomo always returns an HTTP 204 response without a body). If the request fails, an exception will be thrown. @@ -379,55 +366,55 @@ following breaking changes: will be validated prior to sending and not during construction. * `getRandomHexString` was removed. Use `RandomValue.random()` or `VisitorId.random()` instead. -## Type Changes and Renaming +### Type Changes and Renaming * `requestDatetime`, `visitorPreviousVisitTimestamp`, `visitorFirstVisitTimestamp`, `ecommerceLastOrderTimestamp` are - now of type `Instant`. You can use `Instant.ofEpochSecond()` to create + of type `Instant`. You can use `Instant.ofEpochSecond()` to create them from epoch seconds. * `requestDatetime` was renamed to `requestTimestamp` due to setter collision and downwards compatibility * `goalRevenue` is the same parameter as `ecommerceRevenue` and was removed to prevent duplication. Use `ecommerceRevenue` instead. -* `setEventValue` requires a double parameter now -* `setEcommerceLastOrderTimestamp` requires an `Instant` parameter now -* `headerAcceptLanguage` is now of type `AcceptLanguage`. You can build it easily +* `setEventValue` requires a double parameter +* `setEcommerceLastOrderTimestamp` requires an `Instant` parameter +* `headerAcceptLanguage` is of type `AcceptLanguage`. You can build it easily using `AcceptLanguage.fromHeader("de")` -* `visitorCountry` is now of type `Country`. You can build it easily using `AcceptLanguage.fromCode("fr")` -* `deviceResolution` is now of type `DeviceResolution`. You can build it easily +* `visitorCountry` is of type `Country`. You can build it easily using `AcceptLanguage.fromCode("fr")` +* `deviceResolution` is of type `DeviceResolution`. You can build it easily using `DeviceResolution.builder.width(...).height(...).build()`. To easy the migration, we added a constructor method `DeviceResolution.fromString()` that accepts inputs of kind _width_x_height_, e.g. `100x200` -* `pageViewId` is now of type `UniqueId`. You can build it easily using `UniqueId.random()` -* `randomValue` is now of type `RandomValue`. You can build it easily using `RandomValue.random()`. However, if you +* `pageViewId` is of type `UniqueId`. You can build it easily using `UniqueId.random()` +* `randomValue` is of type `RandomValue`. You can build it easily using `RandomValue.random()`. However, if you really want to insert a custom string here, use `RandomValue.fromString()` construction method. * URL was removed due to performance and complicated exception handling and problems with parsing of complex - URLs. `actionUrl`, `referrerUrl`, `outlinkUrl`, `contentTarget` and `downloadUrl` are now strings. -* `getCustomTrackingParameter()` of `MatomoRequest` returns an unmodifiable list now. + URLs. `actionUrl`, `referrerUrl`, `outlinkUrl`, `contentTarget` and `downloadUrl` are strings. +* `getCustomTrackingParameter()` of `MatomoRequest` returns an unmodifiable list. * Instead of `IllegalStateException` the tracker throws `MatomoException` -* In former versions the goal id had always to be zero or null. You can now define higher numbers than zero. +* In former versions the goal id had always to be zero or null. You can define higher numbers than zero. * For more type changes see the sections below. ### Visitor ID -* `visitorId` and `visitorCustomId` are now of type `VisitorId`. You can build them easily +* `visitorId` and `visitorCustomId` are of type `VisitorId`. You can build them easily using `VisitorId.fromHash(...)`. * You can use `VisitorId.fromHex()` to create a `VisitorId` from a string that contains only hexadecimal characters. -* VisitorId.fromHex() now supports less than 16 hexadecimal characters. If the string is shorter than 16 characters, +* VisitorId.fromHex() supports less than 16 hexadecimal characters. If the string is shorter than 16 characters, the remaining characters will be filled with zeros. ### Custom Variables * According to Matomo, custom variables should no longer be used. Please use dimensions instead. Dimension support has been introduced. -* `CustomVariable` is now in package `org.matomo.java.tracking.parameters`. +* `CustomVariable` is in package `org.matomo.java.tracking.parameters`. * `customTrackingParameters` in `MatomoRequestBuilder` requires a `Map>` instead - of `Map` now -* `pageCustomVariables` and `visitCustomVariables` are now of type `CustomVariables` instead of collections. Create them + of `Map` +* `pageCustomVariables` and `visitCustomVariables` are of type `CustomVariables` instead of collections. Create them with `CustomVariables.builder().variable(customVariable)` -* `setPageCustomVariable` and `getPageCustomVariable` now longer accept a string as an index. Please use integers +* `setPageCustomVariable` and `getPageCustomVariable` no longer accept a string as an index. Please use integers instead. -* Custom variables will now be sent URL encoded +* Custom variables will be sent URL encoded -## Building +## Building and testing This project can be tested and built by calling @@ -435,11 +422,10 @@ This project can be tested and built by calling mvn install ``` -The built jars and javadoc can be found in `target`. By using the Maven goal `install, the snapshot +The built jars and javadoc can be found in `target`. By using the Maven goal `install, a snapshot version can be used in your local Maven repository for testing purposes, e.g. ```xml - org.piwik.java.tracking matomo-java-tracker @@ -447,16 +433,9 @@ version can be used in your local Maven repository for testing purposes, e.g. ``` -Clean this project using - -```shell -mvn clean -``` - ## Versioning -We use [SemVer](http://semver.org/) for versioning. For the versions available, see -the [tags on this repository](https://github.com/matomo-org/matomo-java-tracker/tags). +We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/matomo-org/matomo-java-tracker/tags). ## Contribute @@ -480,14 +459,6 @@ process for submitting pull requests to us. We use Checkstyle and JaCoCo to ensure code quality. Please run `mvn verify` before submitting a pull request. Please provide tests for your changes. We use JUnit 5 for testing. Coverage should be at least 80%. -## Further information - -* [Matomo PHP Tracker](https://github.com/matomo-org/matomo-php-tracker) -* [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) -* [Introducting the Matomo Java Tracker](https://matomo.org/blog/2015/11/introducing-piwik-java-tracker/) -* [Tracking API User Guide](https://matomo.org/guide/apis/tracking-api/) -* [Matomo](https://matomo.org/) - The Matomo project - ## License This software is released under the BSD 3-Clause license. See [LICENSE](LICENSE). From 0a1797aaf0209cc9c45f3d0252934252305164f7 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Fri, 3 Nov 2023 10:47:27 +0100 Subject: [PATCH 148/467] Push JaCoCo badges on main branch build --- .github/workflows/build.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f62bdf95..8e23e7d8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,4 +26,12 @@ jobs: min-coverage-overall: 80 min-coverage-changed-files: 80 - uses: cicirello/jacoco-badge-generator@v2.11.0 - + - if: github.ref == 'refs/heads/main' + run: | + if [[ `git status --porcelain` ]]; then + git config --global user.name "Matomo Java Tracker" + git config --global user.email "matomo-java-tracker@daniel-heid.de" + git add -A + git commit -m "Autogenerated JaCoCo coverage badge" + git push + fi From 2c4bdd7449e92ca35aa89e552001114305be2fac Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Fri, 3 Nov 2023 10:49:25 +0100 Subject: [PATCH 149/467] Fix Markdown syntax --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8433e10f..74496899 100644 --- a/README.md +++ b/README.md @@ -68,17 +68,17 @@ You can also build the Javadoc yourself. See the section [Building](#building) b ## Need help? -* Open an issue in the (Issue Tracker)[https://github.com/matomo-org/matomo-java-tracker/issues] +* Open an issue in the [Issue Tracker](https://github.com/matomo-org/matomo-java-tracker/issues) * Use [our GitHub discussions](https://github.com/matomo-org/matomo-java-tracker/discussions) -* Ask your question on (Stackoverflow with the tag `matomo`)[https://stackoverflow.com/questions/tagged/matomo] +* Ask your question on [Stackoverflow with the tag `matomo`](https://stackoverflow.com/questions/tagged/matomo) * Create a thread in the [Matomo Forum](https://forum.matomo.org/) * Contact [Matomo Support](https://matomo.org/support/) ## Using this API See the following sections for information on how to use this API. For more information, see the Javadoc. We also recommend -to read the [Tracking API User Guide](https://matomo.org/guide/apis/tracking-api/). The Matomo Tracking HTTP API is well -documented and contains many examples. See https://developer.matomo.org/api-reference/tracking-api +to read the [Tracking API User Guide](https://matomo.org/guide/apis/tracking-api/). The [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) is well +documented and contains many examples. ### Add library to your build From 9aed0766798db74abd93010a4ec2f0aec2f70f06 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Fri, 3 Nov 2023 10:57:02 +0100 Subject: [PATCH 150/467] Allow build job to write contents --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8e23e7d8..e454470f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,6 +11,7 @@ jobs: runs-on: ubuntu-latest permissions: pull-requests: write + contents: write steps: - uses: actions/checkout@v4 - uses: actions/setup-java@v3 From c5ebeb6027b206e2036c48bb50031bdfacfedead Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Fri, 3 Nov 2023 11:23:44 +0100 Subject: [PATCH 151/467] Replace JetBrains with SpotBugs annotations --- .editorconfig | 705 +++++++++++++++++- .github/workflows/build.yml | 11 - README.md | 171 +++-- checkstyle.xml | 608 +++++++-------- pom.xml | 671 ++++++++--------- .../org/matomo/java/tracking/AuthToken.java | 16 +- .../matomo/java/tracking/CustomVariable.java | 2 +- .../java/tracking/DaemonThreadFactory.java | 8 +- .../matomo/java/tracking/EcommerceItem.java | 3 +- .../org/matomo/java/tracking/MatomoDate.java | 3 +- .../matomo/java/tracking/MatomoLocale.java | 7 +- .../matomo/java/tracking/MatomoRequest.java | 46 +- .../matomo/java/tracking/MatomoTracker.java | 107 ++- .../java/tracking/ProxyAuthenticator.java | 7 +- .../matomo/java/tracking/QueryCreator.java | 53 +- .../java/tracking/RequestValidator.java | 40 +- .../java/org/matomo/java/tracking/Sender.java | 103 ++- .../java/tracking/TrackerConfiguration.java | 5 +- .../tracking/TrackingParameterMethod.java | 17 +- .../matomo/java/tracking/package-info.java | 1 - .../tracking/parameters/AcceptLanguage.java | 27 +- .../java/tracking/parameters/Country.java | 22 +- .../tracking/parameters/CustomVariable.java | 2 +- .../tracking/parameters/CustomVariables.java | 21 +- .../tracking/parameters/DeviceResolution.java | 13 +- .../tracking/parameters/EcommerceItems.java | 7 +- .../java/tracking/parameters/UniqueId.java | 15 +- .../java/tracking/parameters/VisitorId.java | 6 +- .../org/piwik/java/tracking/PiwikDate.java | 3 +- .../org/piwik/java/tracking/PiwikLocale.java | 3 +- .../org/piwik/java/tracking/PiwikTracker.java | 6 +- .../org/piwik/java/tracking/package-info.java | 1 - .../matomo/java/tracking/AuthTokenTest.java | 35 +- .../java/tracking/CustomVariableTest.java | 18 +- .../tracking/DaemonThreadFactoryTest.java | 5 +- .../java/tracking/EcommerceItemTest.java | 4 +- .../java/tracking/MatomoLocaleTest.java | 5 +- .../tracking/MatomoRequestBuilderTest.java | 51 +- .../java/tracking/MatomoRequestTest.java | 4 +- .../matomo/java/tracking/MatomoTrackerIT.java | 265 ++++--- .../matomo/java/tracking/PiwikDateTest.java | 7 +- .../matomo/java/tracking/PiwikLocaleTest.java | 7 +- .../java/tracking/PiwikRequestTest.java | 13 +- .../matomo/java/tracking/PiwikTrackerIT.java | 132 ++-- .../java/tracking/ProxyAuthenticatorTest.java | 37 +- .../java/tracking/QueryCreatorTest.java | 184 +++-- .../java/tracking/RequestValidatorTest.java | 74 +- .../org/matomo/java/tracking/SenderIT.java | 112 +-- .../tracking/TrackerConfigurationTest.java | 68 +- .../tracking/TrackingParameterMethodTest.java | 39 +- .../parameters/AcceptLanguageTest.java | 12 +- .../java/tracking/parameters/CountryTest.java | 49 +- .../parameters/CustomVariablesTest.java | 59 +- .../parameters/DeviceResolutionTest.java | 4 +- .../parameters/EcommerceItemsTest.java | 4 +- .../tracking/parameters/UniqueIdTest.java | 4 +- .../tracking/parameters/VisitorIdTest.java | 62 +- 57 files changed, 2500 insertions(+), 1464 deletions(-) diff --git a/.editorconfig b/.editorconfig index c6c8b362..7525a5d7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,9 +1,702 @@ -root = true - [*] +charset = utf-8 +end_of_line = lf +indent_size = 4 indent_style = space +insert_final_newline = false +max_line_length = 120 +tab_width = 4 +ij_continuation_indent_size = 8 +ij_formatter_off_tag = @formatter:off +ij_formatter_on_tag = @formatter:on +ij_formatter_tags_enabled = true +ij_smart_tabs = false +ij_visual_guides = +ij_wrap_on_typing = false + +[*.java] indent_size = 2 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true +max_line_length = 100 +ij_continuation_indent_size = 4 +ij_java_align_consecutive_assignments = false +ij_java_align_consecutive_variable_declarations = false +ij_java_align_group_field_declarations = false +ij_java_align_multiline_annotation_parameters = false +ij_java_align_multiline_array_initializer_expression = true +ij_java_align_multiline_assignment = false +ij_java_align_multiline_binary_operation = false +ij_java_align_multiline_chained_methods = true +ij_java_align_multiline_deconstruction_list_components = false +ij_java_align_multiline_extends_list = false +ij_java_align_multiline_for = true +ij_java_align_multiline_method_parentheses = false +ij_java_align_multiline_parameters = false +ij_java_align_multiline_parameters_in_calls = false +ij_java_align_multiline_parenthesized_expression = false +ij_java_align_multiline_records = false +ij_java_align_multiline_resources = false +ij_java_align_multiline_ternary_operation = false +ij_java_align_multiline_text_blocks = false +ij_java_align_multiline_throws_list = false +ij_java_align_subsequent_simple_methods = false +ij_java_align_throws_keyword = true +ij_java_align_types_in_multi_catch = true +ij_java_annotation_parameter_wrap = split_into_lines +ij_java_array_initializer_new_line_after_left_brace = true +ij_java_array_initializer_right_brace_on_new_line = true +ij_java_array_initializer_wrap = on_every_item +ij_java_assert_statement_colon_on_next_line = false +ij_java_assert_statement_wrap = normal +ij_java_assignment_wrap = normal +ij_java_binary_operation_sign_on_next_line = true +ij_java_binary_operation_wrap = normal +ij_java_blank_lines_after_anonymous_class_header = 0 +ij_java_blank_lines_after_class_header = 0 +ij_java_blank_lines_after_imports = 1 +ij_java_blank_lines_after_package = 1 +ij_java_blank_lines_around_class = 1 +ij_java_blank_lines_around_field = 0 +ij_java_blank_lines_around_field_in_interface = 0 +ij_java_blank_lines_around_initializer = 1 +ij_java_blank_lines_around_method = 1 +ij_java_blank_lines_around_method_in_interface = 1 +ij_java_blank_lines_before_class_end = 0 +ij_java_blank_lines_before_imports = 1 +ij_java_blank_lines_before_method_body = 0 +ij_java_blank_lines_before_package = 1 +ij_java_block_brace_style = end_of_line +ij_java_block_comment_add_space = false +ij_java_block_comment_at_first_column = true +ij_java_builder_methods = +ij_java_call_parameters_new_line_after_left_paren = true +ij_java_call_parameters_right_paren_on_new_line = true +ij_java_call_parameters_wrap = on_every_item +ij_java_case_statement_on_separate_line = true +ij_java_catch_on_new_line = false +ij_java_class_annotation_wrap = split_into_lines +ij_java_class_brace_style = end_of_line +ij_java_class_count_to_use_import_on_demand = 999 +ij_java_class_names_in_javadoc = 1 +ij_java_deconstruction_list_wrap = on_every_item +ij_java_do_not_indent_top_level_class_members = false +ij_java_do_not_wrap_after_single_annotation = false +ij_java_do_not_wrap_after_single_annotation_in_parameter = false +ij_java_do_while_brace_force = always +ij_java_doc_add_blank_line_after_description = true +ij_java_doc_add_blank_line_after_param_comments = false +ij_java_doc_add_blank_line_after_return = false +ij_java_doc_add_p_tag_on_empty_lines = true +ij_java_doc_align_exception_comments = true +ij_java_doc_align_param_comments = true +ij_java_doc_do_not_wrap_if_one_line = false +ij_java_doc_enable_formatting = true +ij_java_doc_enable_leading_asterisks = true +ij_java_doc_indent_on_continuation = false +ij_java_doc_keep_empty_lines = true +ij_java_doc_keep_empty_parameter_tag = true +ij_java_doc_keep_empty_return_tag = true +ij_java_doc_keep_empty_throws_tag = true +ij_java_doc_keep_invalid_tags = true +ij_java_doc_param_description_on_new_line = false +ij_java_doc_preserve_line_breaks = false +ij_java_doc_use_throws_not_exception_tag = true +ij_java_else_on_new_line = false +ij_java_enum_constants_wrap = split_into_lines +ij_java_extends_keyword_wrap = normal +ij_java_extends_list_wrap = on_every_item +ij_java_field_annotation_wrap = split_into_lines +ij_java_field_name_prefix = +ij_java_field_name_suffix = +ij_java_finally_on_new_line = false +ij_java_for_brace_force = always +ij_java_for_statement_new_line_after_left_paren = false +ij_java_for_statement_right_paren_on_new_line = false +ij_java_for_statement_wrap = on_every_item +ij_java_generate_final_locals = false +ij_java_generate_final_parameters = false +ij_java_if_brace_force = always +ij_java_imports_layout = $*, |, * +ij_java_indent_case_from_switch = true +ij_java_insert_inner_class_imports = false +ij_java_insert_override_annotation = true +ij_java_keep_blank_lines_before_right_brace = 2 +ij_java_keep_blank_lines_between_package_declaration_and_header = 2 +ij_java_keep_blank_lines_in_code = 2 +ij_java_keep_blank_lines_in_declarations = 2 +ij_java_keep_builder_methods_indents = false +ij_java_keep_control_statement_in_one_line = true +ij_java_keep_first_column_comment = false +ij_java_keep_indents_on_empty_lines = false +ij_java_keep_line_breaks = false +ij_java_keep_multiple_expressions_in_one_line = false +ij_java_keep_simple_blocks_in_one_line = false +ij_java_keep_simple_classes_in_one_line = false +ij_java_keep_simple_lambdas_in_one_line = false +ij_java_keep_simple_methods_in_one_line = false +ij_java_label_indent_absolute = false +ij_java_label_indent_size = 0 +ij_java_lambda_brace_style = end_of_line +ij_java_layout_static_imports_separately = true +ij_java_line_comment_add_space = false +ij_java_line_comment_add_space_on_reformat = false +ij_java_line_comment_at_first_column = true +ij_java_local_variable_name_prefix = +ij_java_local_variable_name_suffix = +ij_java_method_annotation_wrap = split_into_lines +ij_java_method_brace_style = end_of_line +ij_java_method_call_chain_wrap = on_every_item +ij_java_method_parameters_new_line_after_left_paren = true +ij_java_method_parameters_right_paren_on_new_line = true +ij_java_method_parameters_wrap = on_every_item +ij_java_modifier_list_wrap = false +ij_java_multi_catch_types_wrap = on_every_item +ij_java_names_count_to_use_import_on_demand = 999 +ij_java_new_line_after_lparen_in_annotation = true +ij_java_new_line_after_lparen_in_deconstruction_pattern = true +ij_java_new_line_after_lparen_in_record_header = true +ij_java_packages_to_use_import_on_demand = +ij_java_parameter_annotation_wrap = split_into_lines +ij_java_parameter_name_prefix = +ij_java_parameter_name_suffix = +ij_java_parentheses_expression_new_line_after_left_paren = true +ij_java_parentheses_expression_right_paren_on_new_line = true +ij_java_place_assignment_sign_on_next_line = false +ij_java_prefer_longer_names = true +ij_java_prefer_parameters_wrap = false +ij_java_record_components_wrap = on_every_item +ij_java_repeat_annotations = +ij_java_repeat_synchronized = true +ij_java_replace_instanceof_and_cast = false +ij_java_replace_null_check = true +ij_java_replace_sum_lambda_with_method_ref = true +ij_java_resource_list_new_line_after_left_paren = true +ij_java_resource_list_right_paren_on_new_line = true +ij_java_resource_list_wrap = on_every_item +ij_java_rparen_on_new_line_in_annotation = true +ij_java_rparen_on_new_line_in_deconstruction_pattern = true +ij_java_rparen_on_new_line_in_record_header = true +ij_java_space_after_closing_angle_bracket_in_type_argument = false +ij_java_space_after_colon = true +ij_java_space_after_comma = true +ij_java_space_after_comma_in_type_arguments = true +ij_java_space_after_for_semicolon = true +ij_java_space_after_quest = true +ij_java_space_after_type_cast = true +ij_java_space_before_annotation_array_initializer_left_brace = false +ij_java_space_before_annotation_parameter_list = false +ij_java_space_before_array_initializer_left_brace = true +ij_java_space_before_catch_keyword = true +ij_java_space_before_catch_left_brace = true +ij_java_space_before_catch_parentheses = true +ij_java_space_before_class_left_brace = true +ij_java_space_before_colon = true +ij_java_space_before_colon_in_foreach = true +ij_java_space_before_comma = false +ij_java_space_before_deconstruction_list = false +ij_java_space_before_do_left_brace = true +ij_java_space_before_else_keyword = true +ij_java_space_before_else_left_brace = true +ij_java_space_before_finally_keyword = true +ij_java_space_before_finally_left_brace = true +ij_java_space_before_for_left_brace = true +ij_java_space_before_for_parentheses = true +ij_java_space_before_for_semicolon = false +ij_java_space_before_if_left_brace = true +ij_java_space_before_if_parentheses = true +ij_java_space_before_method_call_parentheses = false +ij_java_space_before_method_left_brace = true +ij_java_space_before_method_parentheses = false +ij_java_space_before_opening_angle_bracket_in_type_parameter = false +ij_java_space_before_quest = true +ij_java_space_before_switch_left_brace = true +ij_java_space_before_switch_parentheses = true +ij_java_space_before_synchronized_left_brace = true +ij_java_space_before_synchronized_parentheses = true +ij_java_space_before_try_left_brace = true +ij_java_space_before_try_parentheses = true +ij_java_space_before_type_parameter_list = false +ij_java_space_before_while_keyword = true +ij_java_space_before_while_left_brace = true +ij_java_space_before_while_parentheses = true +ij_java_space_inside_one_line_enum_braces = false +ij_java_space_within_empty_array_initializer_braces = false +ij_java_space_within_empty_method_call_parentheses = false +ij_java_space_within_empty_method_parentheses = false +ij_java_spaces_around_additive_operators = true +ij_java_spaces_around_annotation_eq = true +ij_java_spaces_around_assignment_operators = true +ij_java_spaces_around_bitwise_operators = true +ij_java_spaces_around_equality_operators = true +ij_java_spaces_around_lambda_arrow = true +ij_java_spaces_around_logical_operators = true +ij_java_spaces_around_method_ref_dbl_colon = false +ij_java_spaces_around_multiplicative_operators = true +ij_java_spaces_around_relational_operators = true +ij_java_spaces_around_shift_operators = true +ij_java_spaces_around_type_bounds_in_type_parameters = true +ij_java_spaces_around_unary_operator = false +ij_java_spaces_within_angle_brackets = false +ij_java_spaces_within_annotation_parentheses = false +ij_java_spaces_within_array_initializer_braces = false +ij_java_spaces_within_braces = false +ij_java_spaces_within_brackets = false +ij_java_spaces_within_cast_parentheses = false +ij_java_spaces_within_catch_parentheses = false +ij_java_spaces_within_deconstruction_list = false +ij_java_spaces_within_for_parentheses = false +ij_java_spaces_within_if_parentheses = false +ij_java_spaces_within_method_call_parentheses = false +ij_java_spaces_within_method_parentheses = false +ij_java_spaces_within_parentheses = false +ij_java_spaces_within_record_header = false +ij_java_spaces_within_switch_parentheses = false +ij_java_spaces_within_synchronized_parentheses = false +ij_java_spaces_within_try_parentheses = false +ij_java_spaces_within_while_parentheses = false +ij_java_special_else_if_treatment = true +ij_java_static_field_name_prefix = +ij_java_static_field_name_suffix = +ij_java_subclass_name_prefix = +ij_java_subclass_name_suffix = Impl +ij_java_ternary_operation_signs_on_next_line = false +ij_java_ternary_operation_wrap = normal +ij_java_test_name_prefix = +ij_java_test_name_suffix = Test +ij_java_throws_keyword_wrap = normal +ij_java_throws_list_wrap = on_every_item +ij_java_use_external_annotations = false +ij_java_use_fq_class_names = false +ij_java_use_relative_indents = false +ij_java_use_single_class_imports = true +ij_java_variable_annotation_wrap = split_into_lines +ij_java_visibility = public +ij_java_while_brace_force = always +ij_java_while_on_new_line = false +ij_java_wrap_comments = false +ij_java_wrap_first_method_in_call_chain = true +ij_java_wrap_long_lines = false + +[*.properties] +ij_properties_align_group_field_declarations = false +ij_properties_keep_blank_lines = false +ij_properties_key_value_delimiter = equals +ij_properties_spaces_around_key_value_delimiter = false + +[*.proto] +indent_size = 2 +tab_width = 2 +ij_continuation_indent_size = 4 +ij_protobuf_keep_blank_lines_in_code = 2 +ij_protobuf_keep_indents_on_empty_lines = false +ij_protobuf_keep_line_breaks = true +ij_protobuf_space_after_comma = true +ij_protobuf_space_before_comma = false +ij_protobuf_spaces_around_assignment_operators = true +ij_protobuf_spaces_within_braces = false +ij_protobuf_spaces_within_brackets = false + +[.editorconfig] +ij_editorconfig_align_group_field_declarations = false +ij_editorconfig_space_after_colon = false +ij_editorconfig_space_after_comma = true +ij_editorconfig_space_before_colon = false +ij_editorconfig_space_before_comma = false +ij_editorconfig_spaces_around_assignment_operators = true + +[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.jspx,*.pom,*.rng,*.tagx,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}] +ij_xml_align_attributes = true +ij_xml_align_text = false +ij_xml_attribute_wrap = normal +ij_xml_block_comment_add_space = false +ij_xml_block_comment_at_first_column = true +ij_xml_keep_blank_lines = 2 +ij_xml_keep_indents_on_empty_lines = false +ij_xml_keep_line_breaks = true +ij_xml_keep_line_breaks_in_text = true +ij_xml_keep_whitespaces = false +ij_xml_keep_whitespaces_around_cdata = preserve +ij_xml_keep_whitespaces_inside_cdata = false +ij_xml_line_comment_at_first_column = true +ij_xml_space_after_tag_name = false +ij_xml_space_around_equals_in_attribute = false +ij_xml_space_inside_empty_tag = false +ij_xml_text_wrap = normal +ij_xml_use_custom_settings = false + +[{*.bash,*.sh,*.zsh}] +indent_size = 2 +tab_width = 2 +ij_shell_binary_ops_start_line = false +ij_shell_keep_column_alignment_padding = false +ij_shell_minify_program = false +ij_shell_redirect_followed_by_space = false +ij_shell_switch_cases_indented = false +ij_shell_use_unix_line_separator = true + +[{*.gant,*.groovy,*.gy}] +ij_groovy_align_group_field_declarations = false +ij_groovy_align_multiline_array_initializer_expression = false +ij_groovy_align_multiline_assignment = false +ij_groovy_align_multiline_binary_operation = false +ij_groovy_align_multiline_chained_methods = false +ij_groovy_align_multiline_extends_list = false +ij_groovy_align_multiline_for = true +ij_groovy_align_multiline_list_or_map = true +ij_groovy_align_multiline_method_parentheses = false +ij_groovy_align_multiline_parameters = true +ij_groovy_align_multiline_parameters_in_calls = false +ij_groovy_align_multiline_resources = true +ij_groovy_align_multiline_ternary_operation = false +ij_groovy_align_multiline_throws_list = false +ij_groovy_align_named_args_in_map = true +ij_groovy_align_throws_keyword = false +ij_groovy_array_initializer_new_line_after_left_brace = false +ij_groovy_array_initializer_right_brace_on_new_line = false +ij_groovy_array_initializer_wrap = off +ij_groovy_assert_statement_wrap = off +ij_groovy_assignment_wrap = off +ij_groovy_binary_operation_wrap = off +ij_groovy_blank_lines_after_class_header = 0 +ij_groovy_blank_lines_after_imports = 1 +ij_groovy_blank_lines_after_package = 1 +ij_groovy_blank_lines_around_class = 1 +ij_groovy_blank_lines_around_field = 0 +ij_groovy_blank_lines_around_field_in_interface = 0 +ij_groovy_blank_lines_around_method = 1 +ij_groovy_blank_lines_around_method_in_interface = 1 +ij_groovy_blank_lines_before_imports = 1 +ij_groovy_blank_lines_before_method_body = 0 +ij_groovy_blank_lines_before_package = 0 +ij_groovy_block_brace_style = end_of_line +ij_groovy_block_comment_add_space = false +ij_groovy_block_comment_at_first_column = true +ij_groovy_call_parameters_new_line_after_left_paren = false +ij_groovy_call_parameters_right_paren_on_new_line = false +ij_groovy_call_parameters_wrap = off +ij_groovy_catch_on_new_line = false +ij_groovy_class_annotation_wrap = split_into_lines +ij_groovy_class_brace_style = end_of_line +ij_groovy_class_count_to_use_import_on_demand = 5 +ij_groovy_do_while_brace_force = never +ij_groovy_else_on_new_line = false +ij_groovy_enable_groovydoc_formatting = true +ij_groovy_enum_constants_wrap = off +ij_groovy_extends_keyword_wrap = off +ij_groovy_extends_list_wrap = off +ij_groovy_field_annotation_wrap = split_into_lines +ij_groovy_finally_on_new_line = false +ij_groovy_for_brace_force = never +ij_groovy_for_statement_new_line_after_left_paren = false +ij_groovy_for_statement_right_paren_on_new_line = false +ij_groovy_for_statement_wrap = off +ij_groovy_ginq_general_clause_wrap_policy = 2 +ij_groovy_ginq_having_wrap_policy = 1 +ij_groovy_ginq_indent_having_clause = true +ij_groovy_ginq_indent_on_clause = true +ij_groovy_ginq_on_wrap_policy = 1 +ij_groovy_ginq_space_after_keyword = true +ij_groovy_if_brace_force = never +ij_groovy_import_annotation_wrap = 2 +ij_groovy_imports_layout = *, |, javax.**, java.**, |, $* +ij_groovy_indent_case_from_switch = true +ij_groovy_indent_label_blocks = true +ij_groovy_insert_inner_class_imports = false +ij_groovy_keep_blank_lines_before_right_brace = 2 +ij_groovy_keep_blank_lines_in_code = 2 +ij_groovy_keep_blank_lines_in_declarations = 2 +ij_groovy_keep_control_statement_in_one_line = true +ij_groovy_keep_first_column_comment = true +ij_groovy_keep_indents_on_empty_lines = false +ij_groovy_keep_line_breaks = true +ij_groovy_keep_multiple_expressions_in_one_line = false +ij_groovy_keep_simple_blocks_in_one_line = false +ij_groovy_keep_simple_classes_in_one_line = true +ij_groovy_keep_simple_lambdas_in_one_line = true +ij_groovy_keep_simple_methods_in_one_line = true +ij_groovy_label_indent_absolute = false +ij_groovy_label_indent_size = 0 +ij_groovy_lambda_brace_style = end_of_line +ij_groovy_layout_static_imports_separately = true +ij_groovy_line_comment_add_space = false +ij_groovy_line_comment_add_space_on_reformat = false +ij_groovy_line_comment_at_first_column = true +ij_groovy_method_annotation_wrap = split_into_lines +ij_groovy_method_brace_style = end_of_line +ij_groovy_method_call_chain_wrap = off +ij_groovy_method_parameters_new_line_after_left_paren = false +ij_groovy_method_parameters_right_paren_on_new_line = false +ij_groovy_method_parameters_wrap = off +ij_groovy_modifier_list_wrap = false +ij_groovy_names_count_to_use_import_on_demand = 3 +ij_groovy_packages_to_use_import_on_demand = java.awt.*, javax.swing.* +ij_groovy_parameter_annotation_wrap = off +ij_groovy_parentheses_expression_new_line_after_left_paren = false +ij_groovy_parentheses_expression_right_paren_on_new_line = false +ij_groovy_prefer_parameters_wrap = false +ij_groovy_resource_list_new_line_after_left_paren = false +ij_groovy_resource_list_right_paren_on_new_line = false +ij_groovy_resource_list_wrap = off +ij_groovy_space_after_assert_separator = true +ij_groovy_space_after_colon = true +ij_groovy_space_after_comma = true +ij_groovy_space_after_comma_in_type_arguments = true +ij_groovy_space_after_for_semicolon = true +ij_groovy_space_after_quest = true +ij_groovy_space_after_type_cast = true +ij_groovy_space_before_annotation_parameter_list = false +ij_groovy_space_before_array_initializer_left_brace = false +ij_groovy_space_before_assert_separator = false +ij_groovy_space_before_catch_keyword = true +ij_groovy_space_before_catch_left_brace = true +ij_groovy_space_before_catch_parentheses = true +ij_groovy_space_before_class_left_brace = true +ij_groovy_space_before_closure_left_brace = true +ij_groovy_space_before_colon = true +ij_groovy_space_before_comma = false +ij_groovy_space_before_do_left_brace = true +ij_groovy_space_before_else_keyword = true +ij_groovy_space_before_else_left_brace = true +ij_groovy_space_before_finally_keyword = true +ij_groovy_space_before_finally_left_brace = true +ij_groovy_space_before_for_left_brace = true +ij_groovy_space_before_for_parentheses = true +ij_groovy_space_before_for_semicolon = false +ij_groovy_space_before_if_left_brace = true +ij_groovy_space_before_if_parentheses = true +ij_groovy_space_before_method_call_parentheses = false +ij_groovy_space_before_method_left_brace = true +ij_groovy_space_before_method_parentheses = false +ij_groovy_space_before_quest = true +ij_groovy_space_before_record_parentheses = false +ij_groovy_space_before_switch_left_brace = true +ij_groovy_space_before_switch_parentheses = true +ij_groovy_space_before_synchronized_left_brace = true +ij_groovy_space_before_synchronized_parentheses = true +ij_groovy_space_before_try_left_brace = true +ij_groovy_space_before_try_parentheses = true +ij_groovy_space_before_while_keyword = true +ij_groovy_space_before_while_left_brace = true +ij_groovy_space_before_while_parentheses = true +ij_groovy_space_in_named_argument = true +ij_groovy_space_in_named_argument_before_colon = false +ij_groovy_space_within_empty_array_initializer_braces = false +ij_groovy_space_within_empty_method_call_parentheses = false +ij_groovy_spaces_around_additive_operators = true +ij_groovy_spaces_around_assignment_operators = true +ij_groovy_spaces_around_bitwise_operators = true +ij_groovy_spaces_around_equality_operators = true +ij_groovy_spaces_around_lambda_arrow = true +ij_groovy_spaces_around_logical_operators = true +ij_groovy_spaces_around_multiplicative_operators = true +ij_groovy_spaces_around_regex_operators = true +ij_groovy_spaces_around_relational_operators = true +ij_groovy_spaces_around_shift_operators = true +ij_groovy_spaces_within_annotation_parentheses = false +ij_groovy_spaces_within_array_initializer_braces = false +ij_groovy_spaces_within_braces = true +ij_groovy_spaces_within_brackets = false +ij_groovy_spaces_within_cast_parentheses = false +ij_groovy_spaces_within_catch_parentheses = false +ij_groovy_spaces_within_for_parentheses = false +ij_groovy_spaces_within_gstring_injection_braces = false +ij_groovy_spaces_within_if_parentheses = false +ij_groovy_spaces_within_list_or_map = false +ij_groovy_spaces_within_method_call_parentheses = false +ij_groovy_spaces_within_method_parentheses = false +ij_groovy_spaces_within_parentheses = false +ij_groovy_spaces_within_switch_parentheses = false +ij_groovy_spaces_within_synchronized_parentheses = false +ij_groovy_spaces_within_try_parentheses = false +ij_groovy_spaces_within_tuple_expression = false +ij_groovy_spaces_within_while_parentheses = false +ij_groovy_special_else_if_treatment = true +ij_groovy_ternary_operation_wrap = off +ij_groovy_throws_keyword_wrap = off +ij_groovy_throws_list_wrap = off +ij_groovy_use_flying_geese_braces = false +ij_groovy_use_fq_class_names = false +ij_groovy_use_fq_class_names_in_javadoc = true +ij_groovy_use_relative_indents = false +ij_groovy_use_single_class_imports = true +ij_groovy_variable_annotation_wrap = off +ij_groovy_while_brace_force = never +ij_groovy_while_on_new_line = false +ij_groovy_wrap_chain_calls_after_dot = false +ij_groovy_wrap_long_lines = false + +[{*.har,*.json}] +indent_size = 2 +ij_json_array_wrapping = split_into_lines +ij_json_keep_blank_lines_in_code = 0 +ij_json_keep_indents_on_empty_lines = false +ij_json_keep_line_breaks = true +ij_json_keep_trailing_comma = false +ij_json_object_wrapping = split_into_lines +ij_json_property_alignment = do_not_align +ij_json_space_after_colon = true +ij_json_space_after_comma = true +ij_json_space_before_colon = false +ij_json_space_before_comma = false +ij_json_spaces_within_braces = false +ij_json_spaces_within_brackets = false +ij_json_wrap_long_lines = false + +[{*.htm,*.html,*.sht,*.shtm,*.shtml}] +ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3 +ij_html_align_attributes = true +ij_html_align_text = false +ij_html_attribute_wrap = normal +ij_html_block_comment_add_space = false +ij_html_block_comment_at_first_column = true +ij_html_do_not_align_children_of_min_lines = 0 +ij_html_do_not_break_if_inline_tags = title, h1, h2, h3, h4, h5, h6, p +ij_html_do_not_indent_children_of_tags = html, body, thead, tbody, tfoot +ij_html_enforce_quotes = false +ij_html_inline_tags = a, abbr, acronym, b, basefont, bdo, big, br, cite, cite, code, dfn, em, font, i, img, input, kbd, label, q, s, samp, select, small, span, strike, strong, sub, sup, textarea, tt, u, var +ij_html_keep_blank_lines = 2 +ij_html_keep_indents_on_empty_lines = false +ij_html_keep_line_breaks = true +ij_html_keep_line_breaks_in_text = true +ij_html_keep_whitespaces = false +ij_html_keep_whitespaces_inside = span, pre, textarea +ij_html_line_comment_at_first_column = true +ij_html_new_line_after_last_attribute = never +ij_html_new_line_before_first_attribute = never +ij_html_quote_style = double +ij_html_remove_new_line_before_tags = br +ij_html_space_after_tag_name = false +ij_html_space_around_equality_in_attribute = false +ij_html_space_inside_empty_tag = false +ij_html_text_wrap = normal + +[{*.kt,*.kts}] +ij_kotlin_align_in_columns_case_branch = false +ij_kotlin_align_multiline_binary_operation = false +ij_kotlin_align_multiline_extends_list = false +ij_kotlin_align_multiline_method_parentheses = false +ij_kotlin_align_multiline_parameters = true +ij_kotlin_align_multiline_parameters_in_calls = false +ij_kotlin_allow_trailing_comma = false +ij_kotlin_allow_trailing_comma_on_call_site = false +ij_kotlin_assignment_wrap = off +ij_kotlin_blank_lines_after_class_header = 0 +ij_kotlin_blank_lines_around_block_when_branches = 0 +ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1 +ij_kotlin_block_comment_add_space = false +ij_kotlin_block_comment_at_first_column = true +ij_kotlin_call_parameters_new_line_after_left_paren = false +ij_kotlin_call_parameters_right_paren_on_new_line = false +ij_kotlin_call_parameters_wrap = off +ij_kotlin_catch_on_new_line = false +ij_kotlin_class_annotation_wrap = split_into_lines +ij_kotlin_continuation_indent_for_chained_calls = true +ij_kotlin_continuation_indent_for_expression_bodies = true +ij_kotlin_continuation_indent_in_argument_lists = true +ij_kotlin_continuation_indent_in_elvis = true +ij_kotlin_continuation_indent_in_if_conditions = true +ij_kotlin_continuation_indent_in_parameter_lists = true +ij_kotlin_continuation_indent_in_supertype_lists = true +ij_kotlin_else_on_new_line = false +ij_kotlin_enum_constants_wrap = off +ij_kotlin_extends_list_wrap = off +ij_kotlin_field_annotation_wrap = split_into_lines +ij_kotlin_finally_on_new_line = false +ij_kotlin_if_rparen_on_new_line = false +ij_kotlin_import_nested_classes = false +ij_kotlin_imports_layout = *, java.**, javax.**, kotlin.**, ^ +ij_kotlin_insert_whitespaces_in_simple_one_line_method = true +ij_kotlin_keep_blank_lines_before_right_brace = 2 +ij_kotlin_keep_blank_lines_in_code = 2 +ij_kotlin_keep_blank_lines_in_declarations = 2 +ij_kotlin_keep_first_column_comment = true +ij_kotlin_keep_indents_on_empty_lines = false +ij_kotlin_keep_line_breaks = true +ij_kotlin_lbrace_on_next_line = false +ij_kotlin_line_break_after_multiline_when_entry = true +ij_kotlin_line_comment_add_space = false +ij_kotlin_line_comment_add_space_on_reformat = false +ij_kotlin_line_comment_at_first_column = true +ij_kotlin_method_annotation_wrap = split_into_lines +ij_kotlin_method_call_chain_wrap = off +ij_kotlin_method_parameters_new_line_after_left_paren = false +ij_kotlin_method_parameters_right_paren_on_new_line = false +ij_kotlin_method_parameters_wrap = off +ij_kotlin_name_count_to_use_star_import = 5 +ij_kotlin_name_count_to_use_star_import_for_members = 3 +ij_kotlin_packages_to_use_import_on_demand = java.util.*, kotlinx.android.synthetic.**, io.ktor.** +ij_kotlin_parameter_annotation_wrap = off +ij_kotlin_space_after_comma = true +ij_kotlin_space_after_extend_colon = true +ij_kotlin_space_after_type_colon = true +ij_kotlin_space_before_catch_parentheses = true +ij_kotlin_space_before_comma = false +ij_kotlin_space_before_extend_colon = true +ij_kotlin_space_before_for_parentheses = true +ij_kotlin_space_before_if_parentheses = true +ij_kotlin_space_before_lambda_arrow = true +ij_kotlin_space_before_type_colon = false +ij_kotlin_space_before_when_parentheses = true +ij_kotlin_space_before_while_parentheses = true +ij_kotlin_spaces_around_additive_operators = true +ij_kotlin_spaces_around_assignment_operators = true +ij_kotlin_spaces_around_equality_operators = true +ij_kotlin_spaces_around_function_type_arrow = true +ij_kotlin_spaces_around_logical_operators = true +ij_kotlin_spaces_around_multiplicative_operators = true +ij_kotlin_spaces_around_range = false +ij_kotlin_spaces_around_relational_operators = true +ij_kotlin_spaces_around_unary_operator = false +ij_kotlin_spaces_around_when_arrow = true +ij_kotlin_variable_annotation_wrap = off +ij_kotlin_while_on_new_line = false +ij_kotlin_wrap_elvis_expressions = 1 +ij_kotlin_wrap_expression_body_functions = 0 +ij_kotlin_wrap_first_method_in_call_chain = false + +[{*.markdown,*.md}] +ij_markdown_force_one_space_after_blockquote_symbol = true +ij_markdown_force_one_space_after_header_symbol = true +ij_markdown_force_one_space_after_list_bullet = true +ij_markdown_force_one_space_between_words = true +ij_markdown_format_tables = true +ij_markdown_insert_quote_arrows_on_wrap = true +ij_markdown_keep_indents_on_empty_lines = false +ij_markdown_keep_line_breaks_inside_text_blocks = true +ij_markdown_max_lines_around_block_elements = 1 +ij_markdown_max_lines_around_header = 1 +ij_markdown_max_lines_between_paragraphs = 1 +ij_markdown_min_lines_around_block_elements = 1 +ij_markdown_min_lines_around_header = 1 +ij_markdown_min_lines_between_paragraphs = 1 +ij_markdown_wrap_text_if_long = true +ij_markdown_wrap_text_inside_blockquotes = true + +[{*.pb,*.textproto}] +indent_size = 2 +tab_width = 2 +ij_continuation_indent_size = 4 +ij_prototext_keep_blank_lines_in_code = 2 +ij_prototext_keep_indents_on_empty_lines = false +ij_prototext_keep_line_breaks = true +ij_prototext_space_after_colon = true +ij_prototext_space_after_comma = true +ij_prototext_space_before_colon = false +ij_prototext_space_before_comma = false +ij_prototext_spaces_within_braces = true +ij_prototext_spaces_within_brackets = false + +[{*.toml,Cargo.lock,Cargo.toml.orig,Gopkg.lock,Pipfile,poetry.lock}] +ij_toml_keep_indents_on_empty_lines = false + +[{*.yaml,*.yml}] +indent_size = 2 +ij_yaml_align_values_properties = do_not_align +ij_yaml_autoinsert_sequence_marker = true +ij_yaml_block_mapping_on_new_line = false +ij_yaml_indent_sequence_value = true +ij_yaml_keep_indents_on_empty_lines = false +ij_yaml_keep_line_breaks = true +ij_yaml_sequence_on_new_line = false +ij_yaml_space_before_colon = false +ij_yaml_spaces_within_braces = true +ij_yaml_spaces_within_brackets = true diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e454470f..7abf6b97 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,6 @@ jobs: runs-on: ubuntu-latest permissions: pull-requests: write - contents: write steps: - uses: actions/checkout@v4 - uses: actions/setup-java@v3 @@ -26,13 +25,3 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} min-coverage-overall: 80 min-coverage-changed-files: 80 - - uses: cicirello/jacoco-badge-generator@v2.11.0 - - if: github.ref == 'refs/heads/main' - run: | - if [[ `git status --porcelain` ]]; then - git config --global user.name "Matomo Java Tracker" - git config --global user.email "matomo-java-tracker@daniel-heid.de" - git add -A - git commit -m "Autogenerated JaCoCo coverage badge" - git push - fi diff --git a/README.md b/README.md index 74496899..67f4572f 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,6 @@ [![Build Status](https://github.com/matomo-org/matomo-java-tracker/actions/workflows/build.yml/badge.svg)](https://github.com/matomo-org/matomo-java-tracker/actions/workflows/build.yml) [![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/matomo-org/matomo-java-tracker.svg)](https://isitmaintained.com/project/matomo-org/matomo-java-tracker "Average time to resolve an issue") [![Percentage of issues still open](https://isitmaintained.com/badge/open/matomo-org/matomo-java-tracker.svg)](https://isitmaintained.com/project/matomo-org/matomo-java-tracker "Percentage of issues still open") -![Line Coverage](.github/badges/jacoco.svg) -![Branch Coverage](.github/badges/branches.svg) Matomo Java Tracker is the official Java implementation of the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api). The tracker allows you to track @@ -63,7 +61,8 @@ Here are the most important changes: ## Javadoc The Javadoc for this project is hosted as a GitHub page for this repo. The latest Javadoc can be -found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/index.html). Javadoc folder older versions can be found here: https://javadoc.io/doc/org.piwik.java.tracking/matomo-java-tracker +found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/index.html). Javadoc folder older versions can be +found here: https://javadoc.io/doc/org.piwik.java.tracking/matomo-java-tracker You can also build the Javadoc yourself. See the section [Building](#building) below. ## Need help? @@ -76,8 +75,10 @@ You can also build the Javadoc yourself. See the section [Building](#building) b ## Using this API -See the following sections for information on how to use this API. For more information, see the Javadoc. We also recommend -to read the [Tracking API User Guide](https://matomo.org/guide/apis/tracking-api/). The [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) is well +See the following sections for information on how to use this API. For more information, see the Javadoc. We also +recommend +to read the [Tracking API User Guide](https://matomo.org/guide/apis/tracking-api/). +The [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) is well documented and contains many examples. ### Add library to your build @@ -87,9 +88,9 @@ Add a dependency on Matomo Java Tracker using Maven: ```xml - org.piwik.java.tracking - matomo-java-tracker - 3.0.0-rc1 + org.piwik.java.tracking + matomo-java-tracker + 3.0.0-rc1 ``` @@ -97,7 +98,7 @@ or Gradle: ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker:3.0.0-rc1") + implementation("org.piwik.java.tracking:matomo-java-tracker:3.0.0-rc1") } ``` @@ -118,13 +119,14 @@ import org.matomo.java.tracking.MatomoRequest; public class YourImplementation { - public void yourMethod() { - MatomoRequest request = MatomoRequest.builder() - .siteId(42) - .actionUrl("https://www.mydomain.com/signup") - .actionName("Signup") - .build(); - } + public void yourMethod() { + MatomoRequest request = MatomoRequest + .builder() + .siteId(42) + .actionUrl("https://www.mydomain.com/signup") + .actionName("Signup") + .build(); + } } @@ -153,22 +155,24 @@ import org.matomo.java.tracking.MatomoRequest; public class YourImplementation { - public void yourMethod() { + public void yourMethod() { - MatomoRequest request = MatomoRequest.builder() - .siteId(42) - .actionUrl( - "http://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore") // include the query parameters to the url - .actionName("LearnMore") - .build(); - } + MatomoRequest request = MatomoRequest + .builder() + .siteId(42) + .actionUrl("http://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore") // include the query parameters to the url + .actionName("LearnMore") + .build(); + } } ``` -See [Tracking Campaigns](https://matomo.org/docs/tracking-campaigns/) for more information. All HTTP query parameters denoted on +See [Tracking Campaigns](https://matomo.org/docs/tracking-campaigns/) for more information. All HTTP query parameters +denoted on the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate -getters and setters. See [MatomoRequest.java](src/main/java/org/matomo/api/MatomoRequest.java) for the mappings of the +getters and setters. See [MatomoRequest](src/main/java/org/matomo/java/tracking/MatomoRequest.java) for the mappings of +the parameters to their corresponding attributes. Requests are validated prior to sending. If a request is invalid, a `MatomoException` will be thrown. @@ -186,19 +190,22 @@ import org.matomo.java.tracking.TrackerConfiguration; public class YourImplementation { - public void yourMethod() { + public void yourMethod() { - MatomoTracker tracker = new MatomoTracker( - TrackerConfiguration.builder().apiEndpoint(URI.create("https://your-matomo-domain.tld/matomo.php")).build()); + MatomoTracker tracker = new MatomoTracker(TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://your-matomo-domain.tld/matomo.php")) + .build()); - } + } } ``` The Matomo Tracker currently supports the following builder methods: -* `.apiEndpoint(...)` An `URI` object that points to the Matomo Tracking API endpoint of your Matomo installation. Must be set. +* `.apiEndpoint(...)` An `URI` object that points to the Matomo Tracking API endpoint of your Matomo installation. Must + be set. * `.defaultSiteId(...)` If you provide a default site id, it will be taken if the action does not contain a site id. * `.defaultTokenAuth(...)` If you provide a default token auth, it will be taken if the action does not contain a token auth. @@ -210,7 +217,8 @@ The Matomo Tracker currently supports the following builder methods: interpreted as infinite, null uses the system default * `.socketTimeout(...)` allows you to change the default socket timeout of 10 seconds. 0 is interpreted as infinite, null uses the system default -* `.userAgent(...)` used by the request made to the endpoint is `MatomoJavaClient` per default. You can change it by using this builder method. +* `.userAgent(...)` used by the request made to the endpoint is `MatomoJavaClient` per default. You can change it by + using this builder method. * `.proxyHost(...)` The hostname or IP address of an optional HTTP proxy. `proxyPort` must be configured as well * `.proxyPort(...)` The port of an HTTP proxy. `proxyHost` must be configured as well. @@ -235,19 +243,25 @@ import java.util.concurrent.Future; public class YourImplementation { - public void yourMethod() { + public void yourMethod() { - MatomoRequest request = - MatomoRequest.builder().siteId(42).actionUrl("https://www.mydomain.com/some/page").actionName("Signup").build(); + MatomoRequest request = MatomoRequest + .builder() + .siteId(42) + .actionUrl("https://www.mydomain.com/some/page") + .actionName("Signup") + .build(); - MatomoTracker tracker = new MatomoTracker( - TrackerConfiguration.builder().apiEndpoint(URI.create("https://your-matomo-domain.tld/matomo.php")).build()); + MatomoTracker tracker = new MatomoTracker(TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://your-matomo-domain.tld/matomo.php")) + .build()); - CompletableFuture future = tracker.sendRequestAsync(request); - // execute the request: - future.get(); + CompletableFuture future = tracker.sendRequestAsync(request); + // execute the request: + future.get(); - } + } } ``` @@ -272,21 +286,23 @@ import java.util.concurrent.Future; public class YourImplementation { - public void yourMethod() { + public void yourMethod() { - Collection requests = new ArrayList<>(); - MatomoRequestBuilder builder = MatomoRequest.builder().siteId(42); - requests.add(builder.actionUrl("https://www.mydomain.com/some/page").actionName("Some Page").build()); - requests.add(builder.actionUrl("https://www.mydomain.com/another/page").actionName("Another Page").build()); + Collection requests = new ArrayList<>(); + MatomoRequestBuilder builder = MatomoRequest.builder().siteId(42); + requests.add(builder.actionUrl("https://www.mydomain.com/some/page").actionName("Some Page").build()); + requests.add(builder.actionUrl("https://www.mydomain.com/another/page").actionName("Another Page").build()); - MatomoTracker tracker = new MatomoTracker( - TrackerConfiguration.builder().apiEndpoint(URI.create("https://your-matomo-domain.tld/matomo.php")).build()); + MatomoTracker tracker = new MatomoTracker(TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://your-matomo-domain.tld/matomo.php")) + .build()); - CompletableFuture future = tracker.sendBulkRequestAsync(requests); - // execute the request - future.get(); + CompletableFuture future = tracker.sendBulkRequestAsync(requests); + // execute the request + future.get(); - } + } } @@ -314,26 +330,31 @@ import java.util.concurrent.Future; public class YourImplementation { - public void yourMethod() { + public void yourMethod() { - Collection requests = new ArrayList<>(); - MatomoRequestBuilder builder = MatomoRequest.builder().siteId(42); - requests.add(builder.actionUrl("https://www.mydomain.com/some/page").actionName("Some Page").build()); - requests.add(builder.actionUrl("https://www.mydomain.com/another/page").actionName("Another Page") - .visitorCountry(new MatomoLocale(Locale.GERMANY)).build()); + Collection requests = new ArrayList<>(); + MatomoRequestBuilder builder = MatomoRequest.builder().siteId(42); + requests.add(builder.actionUrl("https://www.mydomain.com/some/page").actionName("Some Page").build()); + requests.add(builder + .actionUrl("https://www.mydomain.com/another/page") + .actionName("Another Page") + .visitorCountry(new MatomoLocale(Locale.GERMANY)) + .build()); - MatomoTracker tracker = new MatomoTracker( - TrackerConfiguration.builder().apiEndpoint(URI.create("https://your-matomo-domain.tld/matomo.php")).build()); + MatomoTracker tracker = new MatomoTracker(TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://your-matomo-domain.tld/matomo.php")) + .build()); - CompletableFuture future = tracker.sendBulkRequestAsync( - requests, - "33dc3f2536d3025974cccb4b4d2d98f4" - ); // second parameter is authentication token need for country override - // execute the request: - future.get(); + CompletableFuture future = tracker.sendBulkRequestAsync( + requests, + "33dc3f2536d3025974cccb4b4d2d98f4" + ); // second parameter is authentication token need for country override + // execute the request: + future.get(); - } + } } @@ -350,9 +371,11 @@ following breaking changes: * The parameter `actionTime` (`gt_ms`) is no longer supported by Matomo 5 and was removed. * Many methods marked as deprecated in version 2 were removed. Please see the - former [Javadoc](https://javadoc.io/doc/org.piwik.java.tracking/matomo-java-tracker/2.1/index.html) of version 2 to get the + former [Javadoc](https://javadoc.io/doc/org.piwik.java.tracking/matomo-java-tracker/2.1/index.html) of version 2 to + get the deprecated methods. -* We removed the vulnerable dependency to the Apache HTTP client. Callbacks are no longer of type `FutureCallback`, but +* We removed the vulnerable dependency to the Apache HTTP client. Callbacks are no longer of + type `FutureCallback`, but `Consumer` instead. * The `send...` methods of `MatomoTracker` no longer return a value (usually Matomo always returns an HTTP 204 response without a body). If the request fails, an exception will be thrown. @@ -426,16 +449,18 @@ The built jars and javadoc can be found in `target`. By using the Maven goal `in version can be used in your local Maven repository for testing purposes, e.g. ```xml + - org.piwik.java.tracking - matomo-java-tracker - 3.0.0-rc2-SNAPSHOT + org.piwik.java.tracking + matomo-java-tracker + 3.0.0-rc2-SNAPSHOT ``` ## Versioning -We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/matomo-org/matomo-java-tracker/tags). +We use [SemVer](http://semver.org/) for versioning. For the versions available, see +the [tags on this repository](https://github.com/matomo-org/matomo-java-tracker/tags). ## Contribute diff --git a/checkstyle.xml b/checkstyle.xml index c71071b6..2e632c85 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -1,7 +1,7 @@ + "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" + "https://checkstyle.org/dtds/configuration_1_3.dtd"> - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + - - - - - - - - + + + + + - - + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + - - - - - + + + - - - - - + + + - - - + - - - - - - - - - + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + - - - + - - - - + + + - - - - + + - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - diff --git a/pom.xml b/pom.xml index 90efbbee..0c8c418b 100644 --- a/pom.xml +++ b/pom.xml @@ -1,348 +1,349 @@ - - 4.0.0 + + 4.0.0 - org.piwik.java.tracking - matomo-java-tracker - 3.0.0-rc2-SNAPSHOT - jar + org.piwik.java.tracking + matomo-java-tracker + 3.0.0-rc2-SNAPSHOT + jar - Matomo Java Tracker - Official Java implementation of the Matomo Tracking HTTP API. - https://github.com/matomo-org/matomo-java-tracker - - - - BSD 3-Clause License - https://github.com/matomo-org/matomo-java-tracker/blob/master/LICENSE - - + Matomo Java Tracker + Official Java implementation of the Matomo Tracking HTTP API. + https://github.com/matomo-org/matomo-java-tracker - - - bcsorba - Brett Csorba - brett.csorba@gmail.com - - - tholu - Thomas Lutz - thomaslutz.de@gmail.com - - - dheid - Daniel Heid - mail@daniel-heid.de - Freelancer - https://www.daniel-heid.de/ - - + + + BSD 3-Clause License + https://github.com/matomo-org/matomo-java-tracker/blob/master/LICENSE + + - - scm:git:https://@github.com/matomo-org/matomo-java-tracker.git - scm:git:https://github.com/matomo-org/matomo-java-tracker.git - https://github.com/matomo-org/matomo-java-tracker - HEAD - + + + bcsorba + Brett Csorba + brett.csorba@gmail.com + + + tholu + Thomas Lutz + thomaslutz.de@gmail.com + + + dheid + Daniel Heid + mail@daniel-heid.de + Freelancer + https://www.daniel-heid.de/ + + - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - + + scm:git:https://@github.com/matomo-org/matomo-java-tracker.git + scm:git:https://github.com/matomo-org/matomo-java-tracker.git + https://github.com/matomo-org/matomo-java-tracker + HEAD + - - UTF-8 - UTF-8 - UTF-8 - 1.8 - 1.8 - 2.0.9 - + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.11.0 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.4.5 - - - org.apache.maven.plugins - maven-clean-plugin - 3.3.2 - - - org.apache.maven.plugins - maven-deploy-plugin - 3.1.1 - - - org.apache.maven.plugins - maven-install-plugin - 3.1.1 - - - org.apache.maven.plugins - maven-jar-plugin - 3.3.0 - - - org.apache.maven.plugins - maven-resources-plugin - 3.3.1 - - - org.apache.maven.plugins - maven-site-plugin - 3.12.1 - - - org.apache.maven.plugins - maven-surefire-plugin - 3.2.1 - - - org.apache.maven.plugins - maven-gpg-plugin - 3.1.0 - - - sign-artifacts - verify - - sign - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.13 - true - - ossrh - https://oss.sonatype.org/ - true - - - - - - - org.apache.maven.plugins - maven-failsafe-plugin - 3.2.1 - - - - integration-test - verify - - - - - - org.apache.maven.plugins - maven-source-plugin - 3.3.0 - - - attach-sources - - jar-no-fork - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.6.0 - - true - all,-missing,-reference - - - - attach-javadocs - - jar - - - - - - org.apache.maven.plugins - maven-enforcer-plugin - 3.4.1 - - - enforce-maven - - enforce - - - - - 3.2.5 - - - - - - - - org.jacoco - jacoco-maven-plugin - 0.8.10 - - - prepare-agent - - prepare-agent - - - - report - - report - - - - check - - check - - - - - CLASS - - - LINE - COVEREDRATIO - 0.9 - - - BRANCH - COVEREDRATIO - 0.5 - - - - - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - 3.3.1 - - warning - checkstyle.xml - true - - - - - validate - - - check - - - - - - com.github.spotbugs - spotbugs-maven-plugin - 4.7.3.6 - - - org.owasp - dependency-check-maven - 8.4.2 - - true - - - - + + UTF-8 + UTF-8 + UTF-8 + 1.8 + 1.8 + 2.0.9 + - - - release - + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.4.5 + + + org.apache.maven.plugins + maven-clean-plugin + 3.3.2 + + + org.apache.maven.plugins + maven-deploy-plugin + 3.1.1 + + + org.apache.maven.plugins + maven-install-plugin + 3.1.1 + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.1 + + + org.apache.maven.plugins + maven-site-plugin + 3.12.1 + + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.1 + + + org.apache.maven.plugins + maven-gpg-plugin + 3.1.0 + + + sign-artifacts + verify + + sign + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.13 + true + + ossrh + https://oss.sonatype.org/ + true + + + + - - org.apache.maven.plugins - maven-gpg-plugin - - - org.sonatype.plugins - nexus-staging-maven-plugin - + + org.apache.maven.plugins + maven-failsafe-plugin + 3.2.1 + + + + integration-test + verify + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.3.0 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.6.0 + + true + all,-missing,-reference + + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.4.1 + + + enforce-maven + + enforce + + + + + 3.2.5 + + + + + + + + org.jacoco + jacoco-maven-plugin + 0.8.10 + + + prepare-agent + + prepare-agent + + + + report + + report + + + + check + + check + + + + + CLASS + + + LINE + COVEREDRATIO + 0.9 + + + BRANCH + COVEREDRATIO + 0.5 + + + + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.3.1 + + warning + checkstyle.xml + true + + + + + validate + + + check + + + + + + com.github.spotbugs + spotbugs-maven-plugin + 4.7.3.6 + + + org.owasp + dependency-check-maven + 8.4.2 + + true + + - - - + + + + + release + + + + org.apache.maven.plugins + maven-gpg-plugin + + + org.sonatype.plugins + nexus-staging-maven-plugin + + + + + - - - org.jetbrains - annotations - 24.0.1 - provided - - - org.slf4j - slf4j-api - ${slf4j.version} - - - org.projectlombok - lombok - 1.18.30 - provided - - - org.junit.jupiter - junit-jupiter - 5.10.0 - test - - - org.assertj - assertj-core - 3.24.2 - test - - - org.slf4j - slf4j-simple - ${slf4j.version} - test - - - com.github.tomakehurst - wiremock - 2.27.2 - test - + + + com.github.spotbugs + spotbugs-annotations + 4.8.0 + provided + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.projectlombok + lombok + 1.18.30 + provided + + + org.junit.jupiter + junit-jupiter + 5.10.0 + test + + + org.assertj + assertj-core + 3.24.2 + test + + + org.slf4j + slf4j-simple + ${slf4j.version} + test + + + com.github.tomakehurst + wiremock + 2.27.2 + test + - + diff --git a/src/main/java/org/matomo/java/tracking/AuthToken.java b/src/main/java/org/matomo/java/tracking/AuthToken.java index 1c2f1ecb..1c775234 100644 --- a/src/main/java/org/matomo/java/tracking/AuthToken.java +++ b/src/main/java/org/matomo/java/tracking/AuthToken.java @@ -7,7 +7,7 @@ package org.matomo.java.tracking; -import org.jetbrains.annotations.Nullable; +import edu.umd.cs.findbugs.annotations.Nullable; final class AuthToken { @@ -17,9 +17,12 @@ private AuthToken() { @Nullable static String determineAuthToken( - @Nullable String overrideAuthToken, - @Nullable Iterable requests, - @Nullable TrackerConfiguration trackerConfiguration + @Nullable + String overrideAuthToken, + @Nullable + Iterable requests, + @Nullable + TrackerConfiguration trackerConfiguration ) { if (isNotBlank(overrideAuthToken)) { return overrideAuthToken; @@ -37,7 +40,10 @@ static String determineAuthToken( return null; } - private static boolean isNotBlank(@Nullable String str) { + private static boolean isNotBlank( + @Nullable + String str + ) { return str != null && !str.isEmpty() && !str.trim().isEmpty(); } } diff --git a/src/main/java/org/matomo/java/tracking/CustomVariable.java b/src/main/java/org/matomo/java/tracking/CustomVariable.java index ad148b46..04cbbee5 100644 --- a/src/main/java/org/matomo/java/tracking/CustomVariable.java +++ b/src/main/java/org/matomo/java/tracking/CustomVariable.java @@ -21,7 +21,7 @@ public class CustomVariable extends org.matomo.java.tracking.parameters.CustomVa /** * Instantiates a new custom variable. * - * @param key the key of the custom variable (required) + * @param key the key of the custom variable (required) * @param value the value of the custom variable (required) */ public CustomVariable(@NonNull String key, @NonNull String value) { diff --git a/src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java b/src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java index 2bd4df9d..35752f6f 100644 --- a/src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java +++ b/src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java @@ -7,14 +7,16 @@ package org.matomo.java.tracking; -import org.jetbrains.annotations.Nullable; - +import edu.umd.cs.findbugs.annotations.Nullable; import java.util.concurrent.ThreadFactory; class DaemonThreadFactory implements ThreadFactory { @Override - public Thread newThread(@Nullable Runnable runnable) { + public Thread newThread( + @Nullable + Runnable runnable + ) { Thread thread = new Thread(runnable); thread.setDaemon(true); thread.setName("MatomoJavaTracker"); diff --git a/src/main/java/org/matomo/java/tracking/EcommerceItem.java b/src/main/java/org/matomo/java/tracking/EcommerceItem.java index e7df1ed0..64ac6037 100644 --- a/src/main/java/org/matomo/java/tracking/EcommerceItem.java +++ b/src/main/java/org/matomo/java/tracking/EcommerceItem.java @@ -28,8 +28,7 @@ public class EcommerceItem extends org.matomo.java.tracking.parameters.Ecommerce * @param quantity the quantity of the item */ public EcommerceItem( - String sku, String name, String category, - Double price, Integer quantity + String sku, String name, String category, Double price, Integer quantity ) { super(sku, name, category, price, quantity); } diff --git a/src/main/java/org/matomo/java/tracking/MatomoDate.java b/src/main/java/org/matomo/java/tracking/MatomoDate.java index 9c02e400..331f4923 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoDate.java +++ b/src/main/java/org/matomo/java/tracking/MatomoDate.java @@ -7,12 +7,11 @@ package org.matomo.java.tracking; -import lombok.Getter; - import java.time.Instant; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import lombok.Getter; /** * A datetime object that will return the datetime in the format {@code yyyy-MM-dd hh:mm:ss}. diff --git a/src/main/java/org/matomo/java/tracking/MatomoLocale.java b/src/main/java/org/matomo/java/tracking/MatomoLocale.java index f889e443..d0dad67d 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoLocale.java +++ b/src/main/java/org/matomo/java/tracking/MatomoLocale.java @@ -9,10 +9,10 @@ import static java.util.Objects.requireNonNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Locale; import lombok.Getter; import lombok.Setter; -import org.jetbrains.annotations.NotNull; import org.matomo.java.tracking.parameters.Country; /** @@ -33,7 +33,10 @@ public class MatomoLocale extends Country { * @deprecated Please use {@link Country} */ @Deprecated - public MatomoLocale(@NotNull Locale locale) { + public MatomoLocale( + @NonNull + Locale locale + ) { super(requireNonNull(locale, "Locale must not be null")); } diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/src/main/java/org/matomo/java/tracking/MatomoRequest.java index 46570479..f7778af0 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -9,6 +9,7 @@ import static java.util.Objects.requireNonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import java.nio.charset.Charset; import java.time.Instant; import java.util.ArrayList; @@ -25,8 +26,6 @@ import lombok.NonNull; import lombok.Setter; import lombok.ToString; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.matomo.java.tracking.parameters.AcceptLanguage; import org.matomo.java.tracking.parameters.Country; import org.matomo.java.tracking.parameters.CustomVariable; @@ -473,7 +472,10 @@ public class MatomoRequest { * 32 character authorization key used to authenticate the API request. We recommend to create a user specifically for * accessing the Tracking API, and give the user only write permission on the website(s). */ - @TrackingParameter(name = "token_auth", regex = "[a-z0-9]{32}") + @TrackingParameter( + name = "token_auth", + regex = "[a-z0-9]{32}" + ) private String authToken; @@ -628,7 +630,11 @@ public List getCustomTrackingParameter(@NonNull String key) { * @param key the parameter's key. Cannot be null * @param value the parameter's value. Removes the parameter if null */ - public void setCustomTrackingParameter(@NonNull String key, @Nullable Object value) { + public void setCustomTrackingParameter( + @NonNull String key, + @Nullable + Object value + ) { if (value == null) { if (customTrackingParameters != null) { @@ -638,7 +644,8 @@ public void setCustomTrackingParameter(@NonNull String key, @Nullable Object val if (customTrackingParameters == null) { customTrackingParameters = new LinkedHashMap<>(); } - Collection values = customTrackingParameters.computeIfAbsent(key, k -> new ArrayList<>()); + Collection values = + customTrackingParameters.computeIfAbsent(key, k -> new ArrayList<>()); values.clear(); values.add(value); } @@ -757,7 +764,12 @@ private static CustomVariable getCustomVariable(CustomVariables customVariables, * @deprecated Use {@link MatomoRequest#getPageCustomVariables()} instead */ @Deprecated - public void setPageCustomVariable(@NotNull String key, @Nullable String value) { + public void setPageCustomVariable( + @edu.umd.cs.findbugs.annotations.NonNull + String key, + @Nullable + String value + ) { requireNonNull(key, "Key must not be null"); if (value == null) { if (pageCustomVariables == null) { @@ -782,7 +794,10 @@ public void setPageCustomVariable(@NotNull String key, @Nullable String value) { * @deprecated Use {@link #getPageCustomVariables()} instead */ @Deprecated - public void setPageCustomVariable(@Nullable CustomVariable customVariable, int index) { + public void setPageCustomVariable( + @Nullable + CustomVariable customVariable, int index + ) { if (pageCustomVariables == null) { if (customVariable == null) { return; @@ -793,8 +808,9 @@ public void setPageCustomVariable(@Nullable CustomVariable customVariable, int i } private static void setCustomVariable( - CustomVariables customVariables, @Nullable CustomVariable customVariable, - int index + CustomVariables customVariables, + @Nullable + CustomVariable customVariable, int index ) { if (customVariable == null) { customVariables.remove(index); @@ -876,7 +892,12 @@ public CustomVariable getVisitCustomVariable(int index) { * @deprecated Use {@link #setVisitCustomVariables(CustomVariables)} instead */ @Deprecated - public void setUserCustomVariable(@NotNull String key, @Nullable String value) { + public void setUserCustomVariable( + @edu.umd.cs.findbugs.annotations.NonNull + String key, + @Nullable + String value + ) { requireNonNull(key, "Key must not be null"); if (value == null) { if (visitCustomVariables == null) { @@ -901,7 +922,10 @@ public void setUserCustomVariable(@NotNull String key, @Nullable String value) { * @deprecated Use {@link #setVisitCustomVariables(CustomVariables)} instead */ @Deprecated - public void setVisitCustomVariable(@Nullable CustomVariable customVariable, int index) { + public void setVisitCustomVariable( + @Nullable + CustomVariable customVariable, int index + ) { if (visitCustomVariables == null) { if (customVariable == null) { return; diff --git a/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/src/main/java/org/matomo/java/tracking/MatomoTracker.java index 8819db90..98c26877 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoTracker.java +++ b/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -7,18 +7,16 @@ package org.matomo.java.tracking; -import lombok.NonNull; -import lombok.extern.slf4j.Slf4j; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import static java.util.Objects.requireNonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import java.net.URI; import java.time.Duration; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.function.Consumer; - -import static java.util.Objects.requireNonNull; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; /** * A class that sends {@link MatomoRequest}s to a specified Matomo server. @@ -41,7 +39,10 @@ public class MatomoTracker { * @deprecated Please use {@link MatomoTracker#MatomoTracker(TrackerConfiguration)} */ @Deprecated - public MatomoTracker(@NotNull String hostUrl) { + public MatomoTracker( + @edu.umd.cs.findbugs.annotations.NonNull + String hostUrl + ) { this(requireNonNull(hostUrl, "Host URL must not be null"), 0); } @@ -55,7 +56,10 @@ public MatomoTracker(@NotNull String hostUrl) { * @deprecated Please use {@link MatomoTracker#MatomoTracker(TrackerConfiguration)} */ @Deprecated - public MatomoTracker(@NotNull String hostUrl, int timeout) { + public MatomoTracker( + @edu.umd.cs.findbugs.annotations.NonNull + String hostUrl, int timeout + ) { this(requireNonNull(hostUrl, "Host URL must not be null"), null, 0, timeout); } @@ -71,11 +75,21 @@ public MatomoTracker(@NotNull String hostUrl, int timeout) { * @deprecated Please use {@link MatomoTracker#MatomoTracker(TrackerConfiguration)} */ @Deprecated - public MatomoTracker(@NotNull String hostUrl, @Nullable String proxyHost, int proxyPort, int timeout) { - this(TrackerConfiguration.builder().enabled(true).apiEndpoint( - URI.create(requireNonNull(hostUrl, "Host URL must not be null"))).proxyHost(proxyHost).proxyPort(proxyPort) - .connectTimeout(timeout == -1 ? Duration.ofSeconds(5L) : Duration.ofSeconds(timeout)) - .socketTimeout(timeout == -1 ? Duration.ofSeconds(5L) : Duration.ofSeconds(timeout)).build()); + public MatomoTracker( + @edu.umd.cs.findbugs.annotations.NonNull + String hostUrl, + @Nullable + String proxyHost, int proxyPort, int timeout + ) { + this(TrackerConfiguration + .builder() + .enabled(true) + .apiEndpoint(URI.create(requireNonNull(hostUrl, "Host URL must not be null"))) + .proxyHost(proxyHost) + .proxyPort(proxyPort) + .connectTimeout(timeout == -1 ? Duration.ofSeconds(5L) : Duration.ofSeconds(timeout)) + .socketTimeout(timeout == -1 ? Duration.ofSeconds(5L) : Duration.ofSeconds(timeout)) + .build()); } /** @@ -83,18 +97,25 @@ public MatomoTracker(@NotNull String hostUrl, @Nullable String proxyHost, int pr * * @param trackerConfiguration Configurations parameters (you can use a builder) */ - public MatomoTracker(@NotNull TrackerConfiguration trackerConfiguration) { + public MatomoTracker( + @edu.umd.cs.findbugs.annotations.NonNull + TrackerConfiguration trackerConfiguration + ) { requireNonNull(trackerConfiguration, "Tracker configuration must not be null"); trackerConfiguration.validate(); this.trackerConfiguration = trackerConfiguration; ScheduledThreadPoolExecutor threadPoolExecutor = createThreadPoolExecutor(); - sender = new Sender(trackerConfiguration, new QueryCreator(trackerConfiguration), threadPoolExecutor); + sender = new Sender(trackerConfiguration, + new QueryCreator(trackerConfiguration), + threadPoolExecutor + ); } - @NotNull + @edu.umd.cs.findbugs.annotations.NonNull private static ScheduledThreadPoolExecutor createThreadPoolExecutor() { DaemonThreadFactory threadFactory = new DaemonThreadFactory(); - ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(1, threadFactory); + ScheduledThreadPoolExecutor threadPoolExecutor = + new ScheduledThreadPoolExecutor(1, threadFactory); threadPoolExecutor.setRemoveOnCancelPolicy(true); return threadPoolExecutor; } @@ -110,7 +131,12 @@ private static ScheduledThreadPoolExecutor createThreadPoolExecutor() { * @deprecated Please use {@link MatomoTracker#MatomoTracker(TrackerConfiguration)} */ @Deprecated - public MatomoTracker(@NotNull String hostUrl, @Nullable String proxyHost, int proxyPort) { + public MatomoTracker( + @edu.umd.cs.findbugs.annotations.NonNull + String hostUrl, + @Nullable + String proxyHost, int proxyPort + ) { this(hostUrl, proxyHost, proxyPort, -1); } @@ -136,7 +162,10 @@ public void sendRequest(@NonNull MatomoRequest request) { * @param request request to send * @return future with response from this request */ - public CompletableFuture sendRequestAsync(@NotNull MatomoRequest request) { + public CompletableFuture sendRequestAsync( + @edu.umd.cs.findbugs.annotations.NonNull + MatomoRequest request + ) { return sendRequestAsync(request, null); } @@ -147,7 +176,12 @@ public CompletableFuture sendRequestAsync(@NotNull MatomoRequest request) * @param callback callback that gets executed when response arrives, null allowed * @return future with response from this request */ - public CompletableFuture sendRequestAsync(@NotNull MatomoRequest request, @Nullable Consumer callback) { + public CompletableFuture sendRequestAsync( + @edu.umd.cs.findbugs.annotations.NonNull + MatomoRequest request, + @Nullable + Consumer callback + ) { if (trackerConfiguration.isEnabled()) { validate(request); log.debug("Sending async request via GET: {}", request); @@ -161,7 +195,10 @@ public CompletableFuture sendRequestAsync(@NotNull MatomoRequest request, return CompletableFuture.completedFuture(null); } - private void validate(@NotNull MatomoRequest request) { + private void validate( + @edu.umd.cs.findbugs.annotations.NonNull + MatomoRequest request + ) { if (trackerConfiguration.getDefaultSiteId() == null && request.getSiteId() == null) { throw new IllegalArgumentException("No default site ID and no request site ID is given"); } @@ -189,7 +226,11 @@ public void sendBulkRequest(@NonNull Iterable requests) * @deprecated use sendBulkRequestAsync instead */ @Deprecated - public void sendBulkRequest(@NonNull Iterable requests, @Nullable String authToken) { + public void sendBulkRequest( + @NonNull Iterable requests, + @Nullable + String authToken + ) { if (trackerConfiguration.isEnabled()) { for (MatomoRequest request : requests) { validate(request); @@ -208,7 +249,10 @@ public void sendBulkRequest(@NonNull Iterable requests, * @param requests the requests to send * @return future with response from these requests */ - public CompletableFuture sendBulkRequestAsync(@NotNull Iterable requests) { + public CompletableFuture sendBulkRequestAsync( + @edu.umd.cs.findbugs.annotations.NonNull + Iterable requests + ) { return sendBulkRequestAsync(requests, null, null); } @@ -223,7 +267,12 @@ public CompletableFuture sendBulkRequestAsync(@NotNull Iterable sendBulkRequestAsync( - @NotNull Iterable requests, @Nullable String authToken, @Nullable Consumer callback + @edu.umd.cs.findbugs.annotations.NonNull + Iterable requests, + @Nullable + String authToken, + @Nullable + Consumer callback ) { if (trackerConfiguration.isEnabled()) { for (MatomoRequest request : requests) { @@ -249,7 +298,10 @@ public CompletableFuture sendBulkRequestAsync( * @return future with response from these requests */ public CompletableFuture sendBulkRequestAsync( - @NotNull Iterable requests, @Nullable Consumer callback + @edu.umd.cs.findbugs.annotations.NonNull + Iterable requests, + @Nullable + Consumer callback ) { return sendBulkRequestAsync(requests, null, callback); } @@ -264,7 +316,10 @@ public CompletableFuture sendBulkRequestAsync( * @return the response from these requests */ public CompletableFuture sendBulkRequestAsync( - @NotNull Iterable requests, @Nullable String authToken + @edu.umd.cs.findbugs.annotations.NonNull + Iterable requests, + @Nullable + String authToken ) { return sendBulkRequestAsync(requests, authToken, null); } diff --git a/src/main/java/org/matomo/java/tracking/ProxyAuthenticator.java b/src/main/java/org/matomo/java/tracking/ProxyAuthenticator.java index 1b6641a6..ec314cde 100644 --- a/src/main/java/org/matomo/java/tracking/ProxyAuthenticator.java +++ b/src/main/java/org/matomo/java/tracking/ProxyAuthenticator.java @@ -7,12 +7,11 @@ package org.matomo.java.tracking; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import org.jetbrains.annotations.Nullable; - +import edu.umd.cs.findbugs.annotations.Nullable; import java.net.Authenticator; import java.net.PasswordAuthentication; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; @RequiredArgsConstructor class ProxyAuthenticator extends Authenticator { diff --git a/src/main/java/org/matomo/java/tracking/QueryCreator.java b/src/main/java/org/matomo/java/tracking/QueryCreator.java index 17a91a5d..2e029caf 100644 --- a/src/main/java/org/matomo/java/tracking/QueryCreator.java +++ b/src/main/java/org/matomo/java/tracking/QueryCreator.java @@ -7,10 +7,8 @@ package org.matomo.java.tracking; -import lombok.RequiredArgsConstructor; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; @@ -25,11 +23,13 @@ import java.util.List; import java.util.Map.Entry; import java.util.regex.Pattern; +import lombok.RequiredArgsConstructor; @RequiredArgsConstructor class QueryCreator { - private static final TrackingParameterMethod[] TRACKING_PARAMETER_METHODS = initializeTrackingParameterMethods(); + private static final TrackingParameterMethod[] TRACKING_PARAMETER_METHODS = + initializeTrackingParameterMethods(); private final TrackerConfiguration trackerConfiguration; @@ -45,18 +45,23 @@ private static TrackingParameterMethod[] initializeTrackingParameterMethods() { } private static void addMethods( - Collection methods, Member member, TrackingParameter trackingParameter + Collection methods, + Member member, + TrackingParameter trackingParameter ) { try { - for (PropertyDescriptor pd : Introspector.getBeanInfo(MatomoRequest.class).getPropertyDescriptors()) { + for (PropertyDescriptor pd : Introspector + .getBeanInfo(MatomoRequest.class) + .getPropertyDescriptors()) { if (member.getName().equals(pd.getName())) { String regex = trackingParameter.regex(); - methods.add(TrackingParameterMethod.builder() - .parameterName(trackingParameter.name()) - .method(pd.getReadMethod()) - .pattern(regex == null || regex.isEmpty() || regex.trim().isEmpty() ? null : - Pattern.compile(trackingParameter.regex())) - .build()); + methods.add(TrackingParameterMethod + .builder() + .parameterName(trackingParameter.name()) + .method(pd.getReadMethod()) + .pattern(regex == null || regex.isEmpty() || regex.trim().isEmpty() ? null : + Pattern.compile(trackingParameter.regex())) + .build()); } } } catch (IntrospectionException e) { @@ -64,7 +69,12 @@ private static void addMethods( } } - String createQuery(@NotNull MatomoRequest request, @Nullable String authToken) { + String createQuery( + @NonNull + MatomoRequest request, + @Nullable + String authToken + ) { StringBuilder query = new StringBuilder(100); if (request.getSiteId() == null) { appendAmpersand(query); @@ -80,7 +90,9 @@ String createQuery(@NotNull MatomoRequest request, @Nullable String authToken) { appendParameter(method, request, query); } if (request.getCustomTrackingParameters() != null) { - for (Entry> entry : request.getCustomTrackingParameters().entrySet()) { + for (Entry> entry : request + .getCustomTrackingParameters() + .entrySet()) { for (Object value : entry.getValue()) { if (value != null && !value.toString().trim().isEmpty()) { appendAmpersand(query); @@ -106,7 +118,9 @@ private static void appendAmpersand(StringBuilder query) { } } - private static void appendParameter(TrackingParameterMethod method, MatomoRequest request, StringBuilder query) { + private static void appendParameter( + TrackingParameterMethod method, MatomoRequest request, StringBuilder query + ) { try { Object parameterValue = method.getMethod().invoke(request); if (parameterValue != null) { @@ -131,8 +145,11 @@ private static void appendParameter(TrackingParameterMethod method, MatomoReques } } - @NotNull - private static String encode(@NotNull String parameterValue) { + @NonNull + private static String encode( + @NonNull + String parameterValue + ) { try { return URLEncoder.encode(parameterValue, "UTF-8"); } catch (UnsupportedEncodingException e) { diff --git a/src/main/java/org/matomo/java/tracking/RequestValidator.java b/src/main/java/org/matomo/java/tracking/RequestValidator.java index d28d6385..c802e061 100644 --- a/src/main/java/org/matomo/java/tracking/RequestValidator.java +++ b/src/main/java/org/matomo/java/tracking/RequestValidator.java @@ -7,29 +7,36 @@ package org.matomo.java.tracking; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import static java.util.Objects.requireNonNull; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import java.time.Instant; import java.time.temporal.ChronoUnit; -import static java.util.Objects.requireNonNull; - final class RequestValidator { private RequestValidator() { // utility } - static void validate(@NotNull MatomoRequest request, @Nullable CharSequence authToken) { + static void validate( + @NonNull + MatomoRequest request, + @Nullable + CharSequence authToken + ) { requireNonNull(request, "Request must not be null"); if (request.getSiteId() != null && request.getSiteId() < 0) { throw new IllegalArgumentException("Site ID must not be negative"); } - if (request.getGoalId() == null && (request.getEcommerceId() != null || request.getEcommerceRevenue() != null - || request.getEcommerceDiscount() != null || request.getEcommerceItems() != null - || request.getEcommerceLastOrderTimestamp() != null || request.getEcommerceShippingCost() != null - || request.getEcommerceSubtotal() != null || request.getEcommerceTax() != null)) { + if (request.getGoalId() == null && ( + request.getEcommerceId() != null || request.getEcommerceRevenue() != null + || request.getEcommerceDiscount() != null || request.getEcommerceItems() != null + || request.getEcommerceLastOrderTimestamp() != null + || request.getEcommerceShippingCost() != null || request.getEcommerceSubtotal() != null + || request.getEcommerceTax() != null)) { throw new MatomoException("Goal ID must be set if ecommerce parameters are used"); } if (request.getSearchResultsCount() != null && request.getSearchQuery() == null) { @@ -37,13 +44,16 @@ static void validate(@NotNull MatomoRequest request, @Nullable CharSequence auth } if (authToken == null) { if (request.getVisitorLongitude() != null || request.getVisitorLatitude() != null - || request.getVisitorRegion() != null || request.getVisitorCity() != null - || request.getVisitorCountry() != null) { - throw new MatomoException("Auth token must be present if longitude, latitude, region, city or country are set"); + || request.getVisitorRegion() != null || request.getVisitorCity() != null + || request.getVisitorCountry() != null) { + throw new MatomoException( + "Auth token must be present if longitude, latitude, region, city or country are set"); } - if (request.getRequestTimestamp() != null - && request.getRequestTimestamp().isBefore(Instant.now().minus(4, ChronoUnit.HOURS))) { - throw new MatomoException("Auth token must be present if request timestamp is more than four hours ago"); + if (request.getRequestTimestamp() != null && request + .getRequestTimestamp() + .isBefore(Instant.now().minus(4, ChronoUnit.HOURS))) { + throw new MatomoException( + "Auth token must be present if request timestamp is more than four hours ago"); } } else { if (authToken.length() != 32) { diff --git a/src/main/java/org/matomo/java/tracking/Sender.java b/src/main/java/org/matomo/java/tracking/Sender.java index ce97d669..12a2c561 100644 --- a/src/main/java/org/matomo/java/tracking/Sender.java +++ b/src/main/java/org/matomo/java/tracking/Sender.java @@ -7,11 +7,11 @@ package org.matomo.java.tracking; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import static java.util.Collections.singleton; +import static java.util.Objects.requireNonNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import java.io.IOException; import java.io.OutputStream; import java.net.Authenticator; @@ -30,9 +30,8 @@ import java.util.concurrent.Executor; import java.util.stream.Collectors; import java.util.stream.StreamSupport; - -import static java.util.Collections.singleton; -import static java.util.Objects.requireNonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; @Slf4j @RequiredArgsConstructor @@ -46,21 +45,31 @@ class Sender { private final Executor executor; - @NotNull CompletableFuture sendSingleAsync(@NotNull MatomoRequest request) { + @NonNull + CompletableFuture sendSingleAsync( + @NonNull + MatomoRequest request + ) { return CompletableFuture.supplyAsync(() -> { sendSingle(request); return null; }, executor); } - void sendSingle(@NotNull MatomoRequest request) { + void sendSingle( + @NonNull + MatomoRequest request + ) { String authToken = AuthToken.determineAuthToken(null, singleton(request), trackerConfiguration); RequestValidator.validate(request, authToken); HttpURLConnection connection; URI apiEndpoint = trackerConfiguration.getApiEndpoint(); try { - connection = openConnection( - apiEndpoint.resolve(String.format("%s?%s", apiEndpoint.getPath(), queryCreator.createQuery(request, authToken))) + connection = openConnection(apiEndpoint + .resolve(String.format("%s?%s", + apiEndpoint.getPath(), + queryCreator.createQuery(request, authToken) + )) .toURL()); } catch (MalformedURLException e) { throw new InvalidUrlException(e); @@ -79,16 +88,22 @@ void sendSingle(@NotNull MatomoRequest request) { private HttpURLConnection openConnection(URL url) { try { - if (isEmpty(trackerConfiguration.getProxyHost()) || trackerConfiguration.getProxyPort() <= 0) { + if (isEmpty(trackerConfiguration.getProxyHost()) + || trackerConfiguration.getProxyPort() <= 0) { log.debug("Proxy host or proxy port not configured. Will create connection without proxy"); return (HttpURLConnection) url.openConnection(); } - InetSocketAddress proxyAddress = - new InetSocketAddress(trackerConfiguration.getProxyHost(), trackerConfiguration.getProxyPort()); + InetSocketAddress proxyAddress = new InetSocketAddress( + trackerConfiguration.getProxyHost(), + trackerConfiguration.getProxyPort() + ); Proxy proxy = new Proxy(Proxy.Type.HTTP, proxyAddress); - if (!isEmpty(trackerConfiguration.getProxyUserName()) && !isEmpty(trackerConfiguration.getProxyPassword())) { - Authenticator.setDefault( - new ProxyAuthenticator(trackerConfiguration.getProxyUserName(), trackerConfiguration.getProxyPassword())); + if (!isEmpty(trackerConfiguration.getProxyUserName()) + && !isEmpty(trackerConfiguration.getProxyPassword())) { + Authenticator.setDefault(new ProxyAuthenticator( + trackerConfiguration.getProxyUserName(), + trackerConfiguration.getProxyPassword() + )); } return (HttpURLConnection) url.openConnection(proxy); } catch (IOException e) { @@ -112,23 +127,38 @@ private void checkResponse(HttpURLConnection connection) throws IOException { if (trackerConfiguration.isLogFailedTracking()) { log.error("Received error code {}", connection.getResponseCode()); } - throw new MatomoException("Tracking endpoint responded with code " + connection.getResponseCode()); + throw new MatomoException( + "Tracking endpoint responded with code " + connection.getResponseCode()); } } - private static boolean isEmpty(@Nullable String str) { + private static boolean isEmpty( + @Nullable + String str + ) { return str == null || str.isEmpty() || str.trim().isEmpty(); } - void sendBulk(@NotNull Iterable requests, @Nullable String overrideAuthToken) { - String authToken = AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); + void sendBulk( + @NonNull + Iterable requests, + @Nullable + String overrideAuthToken + ) { + String authToken = + AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); sendBulk(StreamSupport.stream(requests.spliterator(), false).map(request -> { RequestValidator.validate(request, authToken); return queryCreator.createQuery(request, null); }).collect(Collectors.toList()), authToken); } - private void sendBulk(@NotNull Collection queries, @Nullable String authToken) { + private void sendBulk( + @NonNull + Collection queries, + @Nullable + String authToken + ) { requireNonNull(queries, "Queries must not be null"); HttpURLConnection connection; try { @@ -138,7 +168,10 @@ private void sendBulk(@NotNull Collection queries, @Nullable String auth } preparePostConnection(connection); configureAgentsAndTimeouts(connection); - log.debug("Sending bulk request using URI {} asynchronously", trackerConfiguration.getApiEndpoint()); + log.debug( + "Sending bulk request using URI {} asynchronously", + trackerConfiguration.getApiEndpoint() + ); OutputStream outputStream = null; try { connection.connect(); @@ -172,7 +205,12 @@ private static void preparePostConnection(HttpURLConnection connection) { } - private static byte[] createPayload(@NotNull Collection queries, @Nullable String authToken) { + private static byte[] createPayload( + @NonNull + Collection queries, + @Nullable + String authToken + ) { requireNonNull(queries, "Queries must not be null"); StringBuilder payload = new StringBuilder("{\"requests\":["); Iterator iterator = queries.iterator(); @@ -190,11 +228,15 @@ private static byte[] createPayload(@NotNull Collection queries, @Nullab return payload.append('}').toString().getBytes(StandardCharsets.UTF_8); } - @NotNull CompletableFuture sendBulkAsync( - @NotNull Iterable requests, - @Nullable String overrideAuthToken + @NonNull + CompletableFuture sendBulkAsync( + @NonNull + Iterable requests, + @Nullable + String overrideAuthToken ) { - String authToken = AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); + String authToken = + AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); synchronized (queries) { for (MatomoRequest request : requests) { RequestValidator.validate(request, authToken); @@ -206,7 +248,10 @@ private static byte[] createPayload(@NotNull Collection queries, @Nullab } @Nullable - private Void sendBulkAsync(@Nullable String authToken) { + private Void sendBulkAsync( + @Nullable + String authToken + ) { synchronized (queries) { if (!queries.isEmpty()) { sendBulk(queries, authToken); diff --git a/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java b/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java index 083156d1..be22aeed 100644 --- a/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java +++ b/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java @@ -7,13 +7,13 @@ package org.matomo.java.tracking; +import edu.umd.cs.findbugs.annotations.Nullable; import java.net.URI; import java.time.Duration; import java.util.regex.Pattern; import lombok.Builder; import lombok.NonNull; import lombok.Value; -import org.jetbrains.annotations.Nullable; /** * Defines configuration settings for the Matomo tracking. @@ -111,7 +111,8 @@ public void validate() { throw new IllegalArgumentException("Auth token must be exactly 32 characters long"); } if (!AUTH_TOKEN_PATTERN.matcher(defaultAuthToken).matches()) { - throw new IllegalArgumentException("Auth token must contain only lowercase letters and numbers"); + throw new IllegalArgumentException( + "Auth token must contain only lowercase letters and numbers"); } } } diff --git a/src/main/java/org/matomo/java/tracking/TrackingParameterMethod.java b/src/main/java/org/matomo/java/tracking/TrackingParameterMethod.java index 01da635e..ae18e1db 100644 --- a/src/main/java/org/matomo/java/tracking/TrackingParameterMethod.java +++ b/src/main/java/org/matomo/java/tracking/TrackingParameterMethod.java @@ -7,11 +7,10 @@ package org.matomo.java.tracking; -import lombok.Builder; -import lombok.Value; - import java.lang.reflect.Method; import java.util.regex.Pattern; +import lombok.Builder; +import lombok.Value; @Builder @Value @@ -24,12 +23,12 @@ class TrackingParameterMethod { Pattern pattern; void validateParameterValue(Object parameterValue) { - if (pattern != null && parameterValue instanceof CharSequence && !pattern.matcher((CharSequence) parameterValue) - .matches()) { - throw new IllegalArgumentException(String.format( - "Invalid value for %s. Must match regex %s", - parameterName, - pattern + if (pattern != null && parameterValue instanceof CharSequence && !pattern + .matcher((CharSequence) parameterValue) + .matches()) { + throw new IllegalArgumentException(String.format("Invalid value for %s. Must match regex %s", + parameterName, + pattern )); } } diff --git a/src/main/java/org/matomo/java/tracking/package-info.java b/src/main/java/org/matomo/java/tracking/package-info.java index ae38e3ef..5c08c6dc 100644 --- a/src/main/java/org/matomo/java/tracking/package-info.java +++ b/src/main/java/org/matomo/java/tracking/package-info.java @@ -6,7 +6,6 @@ * will be thrown. * *

For more information about the Matomo Tracking HTTP API, see the Matomo Tracking HTTP API. - * */ package org.matomo.java.tracking; diff --git a/src/main/java/org/matomo/java/tracking/parameters/AcceptLanguage.java b/src/main/java/org/matomo/java/tracking/parameters/AcceptLanguage.java index 71a74020..934ec85f 100644 --- a/src/main/java/org/matomo/java/tracking/parameters/AcceptLanguage.java +++ b/src/main/java/org/matomo/java/tracking/parameters/AcceptLanguage.java @@ -7,6 +7,8 @@ package org.matomo.java.tracking.parameters; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import java.util.List; import java.util.Locale.LanguageRange; import java.util.Objects; @@ -14,8 +16,6 @@ import lombok.Builder; import lombok.Singular; import lombok.Value; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** * Describes the content for the Accept-Language header field that can be overridden by a custom parameter. The format @@ -40,7 +40,10 @@ public class AcceptLanguage { * @see LanguageRange#parse(String) */ @Nullable - public static AcceptLanguage fromHeader(@Nullable String header) { + public static AcceptLanguage fromHeader( + @Nullable + String header + ) { if (header == null || header.trim().isEmpty()) { return null; } @@ -52,17 +55,21 @@ public static AcceptLanguage fromHeader(@Nullable String header) { * * @return The header value, e.g. "en-US,en;q=0.8,de;q=0.6" */ - @NotNull + @NonNull public String toString() { - return languageRanges.stream() - .filter(Objects::nonNull) - .map(AcceptLanguage::format) - .collect(Collectors.joining(",")); + return languageRanges + .stream() + .filter(Objects::nonNull) + .map(AcceptLanguage::format) + .collect(Collectors.joining(",")); } - private static String format(@NotNull LanguageRange languageRange) { + private static String format( + @NonNull + LanguageRange languageRange + ) { return languageRange.getWeight() == LanguageRange.MAX_WEIGHT ? languageRange.getRange() : - String.format("%s;q=%s", languageRange.getRange(), languageRange.getWeight()); + String.format("%s;q=%s", languageRange.getRange(), languageRange.getWeight()); } } diff --git a/src/main/java/org/matomo/java/tracking/parameters/Country.java b/src/main/java/org/matomo/java/tracking/parameters/Country.java index 959f38af..2435c1fe 100644 --- a/src/main/java/org/matomo/java/tracking/parameters/Country.java +++ b/src/main/java/org/matomo/java/tracking/parameters/Country.java @@ -7,14 +7,13 @@ package org.matomo.java.tracking.parameters; +import edu.umd.cs.findbugs.annotations.Nullable; import java.util.List; import java.util.Locale; import java.util.Locale.LanguageRange; import lombok.AccessLevel; import lombok.NonNull; import lombok.RequiredArgsConstructor; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** * A two-letter country code representing a country. @@ -33,7 +32,10 @@ public class Country { * @param locale A locale that must contain a country code */ @Deprecated - protected Country(@NotNull Locale locale) { + protected Country( + @edu.umd.cs.findbugs.annotations.NonNull + Locale locale + ) { setLocale(locale); } @@ -44,7 +46,10 @@ protected Country(@NotNull Locale locale) { * @return The country or null if code was null */ @Nullable - public static Country fromCode(@Nullable String code) { + public static Country fromCode( + @Nullable + String code + ) { if (code == null || code.isEmpty() || code.trim().isEmpty()) { return null; } @@ -61,7 +66,10 @@ public static Country fromCode(@Nullable String code) { * @return The country or null if ranges was null */ @Nullable - public static Country fromLanguageRanges(@Nullable String ranges) { + public static Country fromLanguageRanges( + @Nullable + String ranges + ) { if (ranges == null || ranges.isEmpty() || ranges.trim().isEmpty()) { return null; } @@ -82,7 +90,7 @@ public static Country fromLanguageRanges(@Nullable String ranges) { * @return The locale for this country * @see Locale#forLanguageTag(String) * @deprecated Since you instantiate this class, you can determine the language on your own - * using {@link Locale#forLanguageTag(String)} + * using {@link Locale#forLanguageTag(String)} */ @Deprecated public Locale getLocale() { @@ -95,7 +103,7 @@ public Locale getLocale() { * @param locale A locale that must contain a country code * @see Locale#getCountry() * @deprecated Since you instantiate this class, you can determine the language on your own - * using {@link Locale#getCountry()} + * using {@link Locale#getCountry()} */ public final void setLocale(Locale locale) { if (locale == null || locale.getCountry() == null || locale.getCountry().isEmpty()) { diff --git a/src/main/java/org/matomo/java/tracking/parameters/CustomVariable.java b/src/main/java/org/matomo/java/tracking/parameters/CustomVariable.java index 570c3579..0f6a3fa9 100644 --- a/src/main/java/org/matomo/java/tracking/parameters/CustomVariable.java +++ b/src/main/java/org/matomo/java/tracking/parameters/CustomVariable.java @@ -44,7 +44,7 @@ public class CustomVariable { /** * Instantiates a new custom variable. * - * @param key the key of the custom variable (required) + * @param key the key of the custom variable (required) * @param value the value of the custom variable (required) */ public CustomVariable(@NonNull String key, @NonNull String value) { diff --git a/src/main/java/org/matomo/java/tracking/parameters/CustomVariables.java b/src/main/java/org/matomo/java/tracking/parameters/CustomVariables.java index 7cbd7cce..c887e7d8 100644 --- a/src/main/java/org/matomo/java/tracking/parameters/CustomVariables.java +++ b/src/main/java/org/matomo/java/tracking/parameters/CustomVariables.java @@ -7,13 +7,13 @@ package org.matomo.java.tracking.parameters; +import edu.umd.cs.findbugs.annotations.Nullable; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import lombok.EqualsAndHashCode; import lombok.NonNull; -import org.jetbrains.annotations.Nullable; /** * A bunch of key-value pairs that represent custom information. See How do I use Custom Variables? @@ -99,8 +99,13 @@ public String get(@NonNull String key) { if (key.isEmpty()) { throw new IllegalArgumentException("key must not be null or empty"); } - return variables.values().stream().filter(variable -> variable.getKey().equals(key)).findFirst() - .map(CustomVariable::getValue).orElse(null); + return variables + .values() + .stream() + .filter(variable -> variable.getKey().equals(key)) + .findFirst() + .map(CustomVariable::getValue) + .orElse(null); } /** @@ -138,8 +143,14 @@ public String toString() { Iterator> iterator = variables.entrySet().iterator(); while (iterator.hasNext()) { Entry entry = iterator.next(); - stringBuilder.append('"').append(entry.getKey()).append("\":[\"").append(entry.getValue().getKey()) - .append("\",\"").append(entry.getValue().getValue()).append("\"]"); + stringBuilder + .append('"') + .append(entry.getKey()) + .append("\":[\"") + .append(entry.getValue().getKey()) + .append("\",\"") + .append(entry.getValue().getValue()) + .append("\"]"); if (iterator.hasNext()) { stringBuilder.append(','); } diff --git a/src/main/java/org/matomo/java/tracking/parameters/DeviceResolution.java b/src/main/java/org/matomo/java/tracking/parameters/DeviceResolution.java index d1643938..1b9ae88b 100644 --- a/src/main/java/org/matomo/java/tracking/parameters/DeviceResolution.java +++ b/src/main/java/org/matomo/java/tracking/parameters/DeviceResolution.java @@ -7,9 +7,9 @@ package org.matomo.java.tracking.parameters; +import edu.umd.cs.findbugs.annotations.Nullable; import lombok.Builder; import lombok.RequiredArgsConstructor; -import org.jetbrains.annotations.Nullable; /** * The resolution (width and height) of the user's output device (monitor / phone). @@ -31,7 +31,10 @@ public class DeviceResolution { * @return The device resolution representation */ @Nullable - public static DeviceResolution fromString(@Nullable String deviceResolution) { + public static DeviceResolution fromString( + @Nullable + String deviceResolution + ) { if (deviceResolution == null) { return null; } @@ -39,8 +42,10 @@ public static DeviceResolution fromString(@Nullable String deviceResolution) { if (dimensions.length != 2) { throw new IllegalArgumentException("Wrong dimension size"); } - return builder().width(Integer.parseInt(dimensions[0])).height(Integer.parseInt(dimensions[1])) - .build(); + return builder() + .width(Integer.parseInt(dimensions[0])) + .height(Integer.parseInt(dimensions[1])) + .build(); } @Override diff --git a/src/main/java/org/matomo/java/tracking/parameters/EcommerceItems.java b/src/main/java/org/matomo/java/tracking/parameters/EcommerceItems.java index d9b85754..7000ded7 100644 --- a/src/main/java/org/matomo/java/tracking/parameters/EcommerceItems.java +++ b/src/main/java/org/matomo/java/tracking/parameters/EcommerceItems.java @@ -7,6 +7,9 @@ package org.matomo.java.tracking.parameters; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.EqualsAndHashCode; @@ -16,10 +19,6 @@ import lombok.Singular; import lombok.experimental.Delegate; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - /** * Multiple things that you can buy online. */ diff --git a/src/main/java/org/matomo/java/tracking/parameters/UniqueId.java b/src/main/java/org/matomo/java/tracking/parameters/UniqueId.java index e66f2717..06a82be6 100644 --- a/src/main/java/org/matomo/java/tracking/parameters/UniqueId.java +++ b/src/main/java/org/matomo/java/tracking/parameters/UniqueId.java @@ -7,13 +7,12 @@ package org.matomo.java.tracking.parameters; -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; - import java.security.SecureRandom; import java.util.Random; import java.util.stream.Collectors; import java.util.stream.IntStream; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; /** * A six character unique ID consisting of the characters [0-9a-Z]. @@ -21,7 +20,8 @@ @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class UniqueId { - private static final String CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + private static final String CHARS = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; private static final Random RANDOM = new SecureRandom(); @@ -48,8 +48,11 @@ public static UniqueId fromValue(long value) { @Override public String toString() { - return IntStream.range(0, 6).map(i -> (int) (value >> i * 8)).mapToObj( - codePoint -> String.valueOf(CHARS.charAt(Math.abs(codePoint % CHARS.length())))).collect(Collectors.joining()); + return IntStream + .range(0, 6) + .map(i -> (int) (value >> i * 8)) + .mapToObj(codePoint -> String.valueOf(CHARS.charAt(Math.abs(codePoint % CHARS.length())))) + .collect(Collectors.joining()); } } diff --git a/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java b/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java index bb267ee3..ba545daf 100644 --- a/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java +++ b/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java @@ -76,8 +76,10 @@ public static VisitorId fromHex(String inputHex) { throw new IllegalArgumentException("Input must be a valid hex string"); } VisitorId visitorId = new VisitorId(); - for (int charIndex = inputHex.length() - 1, representationIndex = visitorId.representation.length - 1; - charIndex >= 0; charIndex -= 2, representationIndex--) { + for (int charIndex = inputHex.length() - 1, representationIndex = + visitorId.representation.length - 1; + charIndex >= 0; + charIndex -= 2, representationIndex--) { String hex = inputHex.substring(Math.max(0, charIndex - 1), charIndex + 1); try { visitorId.representation[representationIndex] = (byte) Integer.parseInt(hex, 16); diff --git a/src/main/java/org/piwik/java/tracking/PiwikDate.java b/src/main/java/org/piwik/java/tracking/PiwikDate.java index c463543b..23927402 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikDate.java +++ b/src/main/java/org/piwik/java/tracking/PiwikDate.java @@ -7,11 +7,10 @@ package org.piwik.java.tracking; -import org.matomo.java.tracking.MatomoDate; - import java.time.Instant; import java.time.ZonedDateTime; import java.util.TimeZone; +import org.matomo.java.tracking.MatomoDate; /** * A date object that can be used to send dates to Matomo. This class is deprecated and will be removed in a future. diff --git a/src/main/java/org/piwik/java/tracking/PiwikLocale.java b/src/main/java/org/piwik/java/tracking/PiwikLocale.java index 6b5bf1f9..34f156d6 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikLocale.java +++ b/src/main/java/org/piwik/java/tracking/PiwikLocale.java @@ -7,9 +7,8 @@ package org.piwik.java.tracking; -import org.matomo.java.tracking.parameters.Country; - import java.util.Locale; +import org.matomo.java.tracking.parameters.Country; /** * A locale object that can be used to send visitor country to Matomo. This class is deprecated and will be removed in diff --git a/src/main/java/org/piwik/java/tracking/PiwikTracker.java b/src/main/java/org/piwik/java/tracking/PiwikTracker.java index 95acad84..1c9ed2f0 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikTracker.java +++ b/src/main/java/org/piwik/java/tracking/PiwikTracker.java @@ -44,7 +44,7 @@ public PiwikTracker(String hostUrl, int timeout) { /** * Creates a new PiwikTracker instance with the given host URL and proxy settings. * - * @param hostUrl the host URL of the Matomo server + * @param hostUrl the host URL of the Matomo server * @param proxyHost the proxy host * @param proxyPort the proxy port * @deprecated Use {@link MatomoTracker} instead. @@ -58,10 +58,10 @@ public PiwikTracker(String hostUrl, String proxyHost, int proxyPort) { * Creates a new PiwikTracker instance with the given host URL, proxy settings and timeout in milliseconds. Use -1 for * no timeout. * - * @param hostUrl the host URL of the Matomo server + * @param hostUrl the host URL of the Matomo server * @param proxyHost the proxy host * @param proxyPort the proxy port - * @param timeout the timeout in milliseconds or -1 for no timeout + * @param timeout the timeout in milliseconds or -1 for no timeout * @deprecated Use {@link MatomoTracker} instead. */ @Deprecated diff --git a/src/main/java/org/piwik/java/tracking/package-info.java b/src/main/java/org/piwik/java/tracking/package-info.java index c6427194..eb647ae7 100644 --- a/src/main/java/org/piwik/java/tracking/package-info.java +++ b/src/main/java/org/piwik/java/tracking/package-info.java @@ -1,6 +1,5 @@ /** * Piwik Java Tracking API. Renamed to {@link org.matomo.java.tracking} in 3.0.0. - * */ package org.piwik.java.tracking; diff --git a/src/test/java/org/matomo/java/tracking/AuthTokenTest.java b/src/test/java/org/matomo/java/tracking/AuthTokenTest.java index da161596..ab6819ef 100644 --- a/src/test/java/org/matomo/java/tracking/AuthTokenTest.java +++ b/src/test/java/org/matomo/java/tracking/AuthTokenTest.java @@ -11,8 +11,8 @@ class AuthTokenTest { @Test void determineAuthTokenReturnsAuthTokenFromRequest() { - MatomoRequest request = MatomoRequest.builder().authToken("bdeca231a312ab12cde124131bedfa23") - .build(); + MatomoRequest request = + MatomoRequest.builder().authToken("bdeca231a312ab12cde124131bedfa23").build(); String authToken = AuthToken.determineAuthToken(null, singleton(request), null); @@ -23,10 +23,11 @@ void determineAuthTokenReturnsAuthTokenFromRequest() { @Test void determineAuthTokenReturnsAuthTokenFromTrackerConfiguration() { - TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder() - .apiEndpoint(URI.create("https://your-matomo-domain.example/matomo.")) - .defaultAuthToken("bdeca231a312ab12cde124131bedfa23") - .build(); + TrackerConfiguration trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://your-matomo-domain.example/matomo.")) + .defaultAuthToken("bdeca231a312ab12cde124131bedfa23") + .build(); String authToken = AuthToken.determineAuthToken(null, null, trackerConfiguration); @@ -36,12 +37,13 @@ void determineAuthTokenReturnsAuthTokenFromTrackerConfiguration() { @Test void determineAuthTokenFromTrackerConfigurationIfRequestTokenIsEmpty() { - MatomoRequest request = MatomoRequest.builder() - .authToken("").build(); + MatomoRequest request = MatomoRequest.builder().authToken("").build(); - TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder() - .apiEndpoint(URI.create("https://your-matomo-domain.example/matomo")) - .defaultAuthToken("bdeca231a312ab12cde124131bedfa23").build(); + TrackerConfiguration trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://your-matomo-domain.example/matomo")) + .defaultAuthToken("bdeca231a312ab12cde124131bedfa23") + .build(); String authToken = AuthToken.determineAuthToken(null, singleton(request), trackerConfiguration); @@ -52,12 +54,13 @@ void determineAuthTokenFromTrackerConfigurationIfRequestTokenIsEmpty() { @Test void determineAuthTokenFromTrackerConfigurationIfRequestTokenIsBlank() { - MatomoRequest request = MatomoRequest.builder() - .authToken(" ").build(); + MatomoRequest request = MatomoRequest.builder().authToken(" ").build(); - TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder() - .apiEndpoint(URI.create("https://your-matomo-domain.example/matomo")) - .defaultAuthToken("bdeca231a312ab12cde124131bedfa23").build(); + TrackerConfiguration trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://your-matomo-domain.example/matomo")) + .defaultAuthToken("bdeca231a312ab12cde124131bedfa23") + .build(); String authToken = AuthToken.determineAuthToken(null, singleton(request), trackerConfiguration); diff --git a/src/test/java/org/matomo/java/tracking/CustomVariableTest.java b/src/test/java/org/matomo/java/tracking/CustomVariableTest.java index a9499bda..e58a10dd 100644 --- a/src/test/java/org/matomo/java/tracking/CustomVariableTest.java +++ b/src/test/java/org/matomo/java/tracking/CustomVariableTest.java @@ -17,20 +17,26 @@ void createsCustomVariable() { @Test void failsOnNullKey() { - assertThatThrownBy(() -> new CustomVariable(null, "value")).isInstanceOf( - NullPointerException.class); + assertThatThrownBy(() -> new CustomVariable( + null, + "value" + )).isInstanceOf(NullPointerException.class); } @Test void failsOnNullValue() { - assertThatThrownBy(() -> new CustomVariable("key", null)).isInstanceOf( - NullPointerException.class); + assertThatThrownBy(() -> new CustomVariable( + "key", + null + )).isInstanceOf(NullPointerException.class); } @Test void failsOnNullKeyAndValue() { - assertThatThrownBy(() -> new CustomVariable(null, null)).isInstanceOf( - NullPointerException.class); + assertThatThrownBy(() -> new CustomVariable( + null, + null + )).isInstanceOf(NullPointerException.class); } diff --git a/src/test/java/org/matomo/java/tracking/DaemonThreadFactoryTest.java b/src/test/java/org/matomo/java/tracking/DaemonThreadFactoryTest.java index 81dd76ed..eb569c9b 100644 --- a/src/test/java/org/matomo/java/tracking/DaemonThreadFactoryTest.java +++ b/src/test/java/org/matomo/java/tracking/DaemonThreadFactoryTest.java @@ -1,10 +1,9 @@ package org.matomo.java.tracking; -import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; import java.util.concurrent.ThreadFactory; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; class DaemonThreadFactoryTest { diff --git a/src/test/java/org/matomo/java/tracking/EcommerceItemTest.java b/src/test/java/org/matomo/java/tracking/EcommerceItemTest.java index 97349e6c..71e9fcc0 100644 --- a/src/test/java/org/matomo/java/tracking/EcommerceItemTest.java +++ b/src/test/java/org/matomo/java/tracking/EcommerceItemTest.java @@ -1,9 +1,9 @@ package org.matomo.java.tracking; -import org.junit.jupiter.api.Test; - import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; + class EcommerceItemTest { private EcommerceItem ecommerceItem = new EcommerceItem(null, null, null, null, null); diff --git a/src/test/java/org/matomo/java/tracking/MatomoLocaleTest.java b/src/test/java/org/matomo/java/tracking/MatomoLocaleTest.java index 9dce779e..7822acd3 100644 --- a/src/test/java/org/matomo/java/tracking/MatomoLocaleTest.java +++ b/src/test/java/org/matomo/java/tracking/MatomoLocaleTest.java @@ -16,8 +16,9 @@ void createsMatomoLocaleFromLocale() { @Test void failsIfLocaleIsNull() { - assertThatThrownBy(() -> new MatomoLocale(null)).isInstanceOf(NullPointerException.class) - .hasMessage("Locale must not be null"); + assertThatThrownBy(() -> new MatomoLocale(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("Locale must not be null"); } } diff --git a/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java b/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java index ec2a71d0..918670cd 100644 --- a/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java +++ b/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java @@ -1,42 +1,53 @@ package org.matomo.java.tracking; -import org.junit.jupiter.api.Test; -import org.matomo.java.tracking.parameters.CustomVariables; - -import java.util.Collections; - import static java.util.Collections.singleton; import static org.assertj.core.api.Assertions.assertThat; +import java.util.Collections; +import org.junit.jupiter.api.Test; +import org.matomo.java.tracking.parameters.CustomVariables; + class MatomoRequestBuilderTest { @Test void buildsRequest() { - CustomVariable pageCustomVariable = new CustomVariable("pageCustomVariableName", "pageCustomVariableValue"); - CustomVariable visitCustomVariable = new CustomVariable("visitCustomVariableName", "visitCustomVariableValue"); - - MatomoRequest matomoRequest = MatomoRequest.builder().siteId(42).actionName("ACTION_NAME") - .actionUrl("https://www.your-domain.tld/some/page?query=foo").referrerUrl("https://referrer.com") - .customTrackingParameters(Collections.singletonMap("trackingParameterName", singleton("trackingParameterValue"))) - .pageCustomVariables(new CustomVariables().add(pageCustomVariable, 2)) - .visitCustomVariables(new CustomVariables().add(visitCustomVariable, 3)).customAction(true).build(); + CustomVariable pageCustomVariable = + new CustomVariable("pageCustomVariableName", "pageCustomVariableValue"); + CustomVariable visitCustomVariable = + new CustomVariable("visitCustomVariableName", "visitCustomVariableValue"); + + MatomoRequest matomoRequest = MatomoRequest + .builder() + .siteId(42) + .actionName("ACTION_NAME") + .actionUrl("https://www.your-domain.tld/some/page?query=foo") + .referrerUrl("https://referrer.com") + .customTrackingParameters(Collections.singletonMap( + "trackingParameterName", + singleton("trackingParameterValue") + )) + .pageCustomVariables(new CustomVariables().add(pageCustomVariable, 2)) + .visitCustomVariables(new CustomVariables().add(visitCustomVariable, 3)) + .customAction(true) + .build(); assertThat(matomoRequest.getSiteId()).isEqualTo(42); assertThat(matomoRequest.getActionName()).isEqualTo("ACTION_NAME"); assertThat(matomoRequest.getApiVersion()).isEqualTo("1"); - assertThat(matomoRequest.getActionUrl()).isEqualTo("https://www.your-domain.tld/some/page?query=foo"); + assertThat(matomoRequest.getActionUrl()).isEqualTo( + "https://www.your-domain.tld/some/page?query=foo"); assertThat(matomoRequest.getVisitorId().toString()).hasSize(16).isHexadecimal(); assertThat(matomoRequest.getRandomValue().toString()).hasSize(20).isHexadecimal(); assertThat(matomoRequest.getResponseAsImage()).isFalse(); assertThat(matomoRequest.getRequired()).isTrue(); assertThat(matomoRequest.getReferrerUrl()).isEqualTo("https://referrer.com"); - assertThat(matomoRequest.getCustomTrackingParameter("trackingParameterName")) - .containsExactly("trackingParameterValue"); - assertThat(matomoRequest.getPageCustomVariables()) - .hasToString("{\"2\":[\"pageCustomVariableName\",\"pageCustomVariableValue\"]}"); - assertThat(matomoRequest.getVisitCustomVariables()) - .hasToString("{\"3\":[\"visitCustomVariableName\",\"visitCustomVariableValue\"]}"); + assertThat(matomoRequest.getCustomTrackingParameter("trackingParameterName")).containsExactly( + "trackingParameterValue"); + assertThat(matomoRequest.getPageCustomVariables()).hasToString( + "{\"2\":[\"pageCustomVariableName\",\"pageCustomVariableValue\"]}"); + assertThat(matomoRequest.getVisitCustomVariables()).hasToString( + "{\"3\":[\"visitCustomVariableName\",\"visitCustomVariableValue\"]}"); assertThat(matomoRequest.getCustomAction()).isTrue(); } diff --git a/src/test/java/org/matomo/java/tracking/MatomoRequestTest.java b/src/test/java/org/matomo/java/tracking/MatomoRequestTest.java index 45abd2ac..bf130b6e 100644 --- a/src/test/java/org/matomo/java/tracking/MatomoRequestTest.java +++ b/src/test/java/org/matomo/java/tracking/MatomoRequestTest.java @@ -32,8 +32,8 @@ void getPageCustomVariableReturnsValueIfPageCustomVariablesIsNotNull() { @Test void setPageCustomVariableRequiresNonNullKey() { - assertThatThrownBy(() -> request.setPageCustomVariable(null, "bar")) - .isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> request.setPageCustomVariable(null, "bar")).isInstanceOf( + NullPointerException.class); } @Test diff --git a/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java index 1e18b942..0f869f93 100644 --- a/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java +++ b/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java @@ -40,16 +40,17 @@ class MatomoTrackerIT { - private static final WireMockServer wireMockServer = new WireMockServer( - WireMockConfiguration.options().dynamicPort()); + private static final WireMockServer wireMockServer = + new WireMockServer(WireMockConfiguration.options().dynamicPort()); private static final int SITE_ID = 42; private final TrackerConfigurationBuilder trackerConfigurationBuilder = - TrackerConfiguration.builder(); + TrackerConfiguration.builder(); - private final MatomoRequestBuilder requestBuilder = - MatomoRequest.builder().visitorId(VisitorId.fromHex("bbccddeeff1122")) + private final MatomoRequestBuilder requestBuilder = MatomoRequest + .builder() + .visitorId(VisitorId.fromHex("bbccddeeff1122")) .randomValue(RandomValue.fromString("someRandom")); private CompletableFuture future; @@ -70,8 +71,8 @@ void givenStub() { void requiresApiEndpoint() { assertThatThrownBy(() -> trackerConfigurationBuilder.defaultSiteId(SITE_ID).build()) - .isInstanceOf( - NullPointerException.class).hasMessage("apiEndpoint is marked non-null but is null"); + .isInstanceOf(NullPointerException.class) + .hasMessage("apiEndpoint is marked non-null but is null"); } @@ -80,14 +81,15 @@ void requiresSiteId() { trackerConfigurationBuilder.apiEndpoint(URI.create("http://localhost:8099/matomo.php")).build(); - assertThatThrownBy(this::whenSendsRequestAsync).isInstanceOf(IllegalArgumentException.class) - .hasMessage("No default site ID and no request site ID is given"); + assertThatThrownBy(this::whenSendsRequestAsync) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("No default site ID and no request site ID is given"); } private void whenSendsRequestAsync() { - future = new MatomoTracker(trackerConfigurationBuilder.build()).sendRequestAsync( - requestBuilder.build()); + future = + new MatomoTracker(trackerConfigurationBuilder.build()).sendRequestAsync(requestBuilder.build()); try { future.get(); } catch (Exception e) { @@ -95,15 +97,6 @@ private void whenSendsRequestAsync() { } } - private void whenSendsSingleRequest() { - new MatomoTracker(trackerConfigurationBuilder.build()).sendRequest(requestBuilder.build()); - } - - private void whenSendsBulkRequest() { - new MatomoTracker(trackerConfigurationBuilder.build()).sendBulkRequest( - singleton(requestBuilder.build())); - } - @Test void usesDefaultSiteId() { @@ -116,15 +109,20 @@ void usesDefaultSiteId() { } private void givenTrackerConfigurationWithDefaultSiteId() { - trackerConfigurationBuilder.apiEndpoint(URI.create(String.format( - "http://localhost:%s/matomo.php", wireMockServer.port()))).defaultSiteId(SITE_ID); + trackerConfigurationBuilder + .apiEndpoint(URI.create(String.format( + "http://localhost:%s/matomo.php", + wireMockServer.port() + ))) + .defaultSiteId(SITE_ID); } private void thenGetsRequest(String expectedQuery) { assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify( - getRequestedFor(urlEqualTo(String.format("/matomo.php?%s", expectedQuery))) - .withHeader("User-Agent", equalTo("MatomoJavaClient"))); + wireMockServer.verify(getRequestedFor(urlEqualTo(String.format( + "/matomo.php?%s", + expectedQuery + ))).withHeader("User-Agent", equalTo("MatomoJavaClient"))); } @Test @@ -145,9 +143,9 @@ void validatesTokenAuth() { givenTrackerConfigurationWithDefaultSiteId(); requestBuilder.authToken("invalid-token-auth"); - assertThatThrownBy(this::whenSendsRequestAsync).hasRootCauseInstanceOf( - IllegalArgumentException.class) - .hasRootCauseMessage("Auth token must be exactly 32 characters long"); + assertThatThrownBy(this::whenSendsRequestAsync) + .hasRootCauseInstanceOf(IllegalArgumentException.class) + .hasRootCauseMessage("Auth token must be exactly 32 characters long"); } @@ -159,9 +157,7 @@ void convertsTrueBooleanTo1() { whenSendsRequestAsync(); - thenGetsRequest( - "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&fla=1&send_image=0&rand=someRandom" - ); + thenGetsRequest("idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&fla=1&send_image=0&rand=someRandom"); } @@ -174,8 +170,7 @@ void convertsFalseBooleanTo0() { whenSendsRequestAsync(); thenGetsRequest( - "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&java=0&send_image=0&rand=someRandom" - ); + "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&java=0&send_image=0&rand=someRandom"); } @@ -188,8 +183,7 @@ void encodesUrl() { whenSendsRequestAsync(); thenGetsRequest( - "idsite=42&rec=1&url=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fpage%3Ffoo%3Dbar&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom" - ); + "idsite=42&rec=1&url=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fpage%3Ffoo%3Dbar&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom"); } @@ -202,8 +196,7 @@ void encodesReferrerUrl() { whenSendsRequestAsync(); thenGetsRequest( - "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Freferrer%3Ffoo2%3Dbar2&send_image=0&rand=someRandom" - ); + "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Freferrer%3Ffoo2%3Dbar2&send_image=0&rand=someRandom"); } @@ -216,16 +209,15 @@ void encodesLink() { whenSendsBulkRequestAsync(); thenPostsRequestWithoutAuthToken( - "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&link=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fexternal%2Flink%23&send_image=0&rand=someRandom", - "156" + "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&link=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fexternal%2Flink%23&send_image=0&rand=someRandom", + "156" ); } private void whenSendsBulkRequestAsync() { - future = - new MatomoTracker(trackerConfigurationBuilder.build()).sendBulkRequestAsync( - singleton(requestBuilder.build())); + future = new MatomoTracker(trackerConfigurationBuilder.build()).sendBulkRequestAsync(singleton( + requestBuilder.build())); try { future.get(); } catch (Exception e) { @@ -236,12 +228,11 @@ private void whenSendsBulkRequestAsync() { private void thenPostsRequestWithoutAuthToken(String expectedQuery, String contentLength) { assertThat(future).isNotCompletedExceptionally(); wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) - .withHeader("Content-Length", equalTo(contentLength)) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient")) - .withRequestBody( - equalToJson("{\"requests\":[\"?" + expectedQuery + "\"]}"))); + .withHeader("Content-Length", equalTo(contentLength)) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson("{\"requests\":[\"?" + expectedQuery + "\"]}"))); } @Test @@ -253,8 +244,8 @@ void encodesDownloadUrl() { whenSendsBulkRequestAsync(); thenPostsRequestWithoutAuthToken( - "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&download=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fdownload.pdf&send_image=0&rand=someRandom", - "154" + "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&download=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fdownload.pdf&send_image=0&rand=someRandom", + "154" ); } @@ -267,8 +258,10 @@ void getContainsHeaders() { whenSendsRequestAsync(); assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php")) - .withHeader("User-Agent", equalTo("MatomoJavaClient"))); + wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php")).withHeader( + "User-Agent", + equalTo("MatomoJavaClient") + )); } @@ -281,10 +274,10 @@ void postContainsHeaders() { assertThat(future).isNotCompletedExceptionally(); wireMockServer.verify(postRequestedFor(urlPathEqualTo("/matomo.php")) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Length", equalTo("90")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient"))); + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Length", equalTo("90")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient"))); } @@ -297,8 +290,10 @@ void allowsToOverrideUserAgent() { whenSendsRequestAsync(); assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php")) - .withHeader("User-Agent", equalTo("Mozilla/5.0"))); + wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php")).withHeader( + "User-Agent", + equalTo("Mozilla/5.0") + )); } @@ -306,42 +301,53 @@ void allowsToOverrideUserAgent() { void tracksMinimalRequest() { givenTrackerConfigurationWithDefaultSiteId(); - requestBuilder.actionName("Help / Feedback").actionUrl("https://www.daniel-heid.de/portfolio") - .visitorId(VisitorId.fromHash(3434343434343434343L)).referrerUrl( - "https://www.daniel-heid.de/referrer") - .visitCustomVariables( - new CustomVariables() - .add(new CustomVariable("customVariable1Key", "customVariable1Value"), 4) - .add(new CustomVariable("customVariable2Key", "customVariable2Value"), 5)) - .visitorVisitCount(2) - .visitorFirstVisitTimestamp( - LocalDateTime.of(2022, 8, 9, 18, 34, 12).toInstant(ZoneOffset.UTC)) - .deviceResolution(DeviceResolution.builder().width(1024).height(768).build()) - .headerAcceptLanguage( - AcceptLanguage.builder().languageRange(new LanguageRange("de")).languageRange( - new LanguageRange("de-DE", 0.9)) - .languageRange(new LanguageRange("en", 0.8)).build()).pageViewId( - UniqueId.fromValue(999999999999999999L)) - .goalId(0).ecommerceRevenue(12.34).ecommerceItems( - EcommerceItems.builder().item( - org.matomo.java.tracking.parameters.EcommerceItem.builder().sku("SKU").build()) - .item(EcommerceItem.builder().sku("SKU").name("NAME").category("CATEGORY").price(123.4) - .build()).build()) - .authToken("fdf6e8461ea9de33176b222519627f78") - .visitorCountry( - Country.fromLanguageRanges("en-GB;q=0.7,de,de-DE;q=0.9,en;q=0.8,en-US;q=0.6")); + requestBuilder + .actionName("Help / Feedback") + .actionUrl("https://www.daniel-heid.de/portfolio") + .visitorId(VisitorId.fromHash(3434343434343434343L)) + .referrerUrl("https://www.daniel-heid.de/referrer") + .visitCustomVariables(new CustomVariables() + .add(new CustomVariable("customVariable1Key", "customVariable1Value"), 4) + .add(new CustomVariable("customVariable2Key", "customVariable2Value"), 5)) + .visitorVisitCount(2) + .visitorFirstVisitTimestamp(LocalDateTime + .of(2022, 8, 9, 18, 34, 12) + .toInstant(ZoneOffset.UTC)) + .deviceResolution(DeviceResolution.builder().width(1024).height(768).build()) + .headerAcceptLanguage(AcceptLanguage + .builder() + .languageRange(new LanguageRange("de")) + .languageRange(new LanguageRange("de-DE", 0.9)) + .languageRange(new LanguageRange("en", 0.8)) + .build()) + .pageViewId(UniqueId.fromValue(999999999999999999L)) + .goalId(0) + .ecommerceRevenue(12.34) + .ecommerceItems(EcommerceItems + .builder() + .item(org.matomo.java.tracking.parameters.EcommerceItem.builder().sku("SKU").build()) + .item(EcommerceItem + .builder() + .sku("SKU") + .name("NAME") + .category("CATEGORY") + .price(123.4) + .build()) + .build()) + .authToken("fdf6e8461ea9de33176b222519627f78") + .visitorCountry(Country.fromLanguageRanges("en-GB;q=0.7,de,de-DE;q=0.9,en;q=0.8,en-US;q=0.6")); whenSendsBulkRequestAsync(); assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")).withHeader( - "Content-Length", equalTo("711")) - .withHeader("Accept", equalTo("*/*")).withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient")) - .withRequestBody( - equalToJson("{\"requests\":[\"?" - + "idsite=42&rec=1&action_name=Help+%2F+Feedback&url=https%3A%2F%2Fwww.daniel-heid.de%2Fportfolio&apiv=1&_id=2fa93d2858bc4867&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Freferrer&_cvar=%7B%224%22%3A%5B%22customVariable1Key%22%2C%22customVariable1Value%22%5D%2C%225%22%3A%5B%22customVariable2Key%22%2C%22customVariable2Value%22%5D%7D&_idvc=2&_idts=1660070052&res=1024x768&lang=de%2Cde-de%3Bq%3D0.9%2Cen%3Bq%3D0.8&pv_id=lbBbxG&idgoal=0&revenue=12.34&ec_items=%5B%5B%22SKU%22%2C%22%22%2C%22%22%2C0.0%2C0%5D%2C%5B%22SKU%22%2C%22NAME%22%2C%22CATEGORY%22%2C123.4%2C0%5D%5D&token_auth=fdf6e8461ea9de33176b222519627f78&country=de&send_image=0&rand=someRandom" - + "\"],\"token_auth\" : \"" + "fdf6e8461ea9de33176b222519627f78" + "\"}"))); + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("711")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson("{\"requests\":[\"?" + + "idsite=42&rec=1&action_name=Help+%2F+Feedback&url=https%3A%2F%2Fwww.daniel-heid.de%2Fportfolio&apiv=1&_id=2fa93d2858bc4867&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Freferrer&_cvar=%7B%224%22%3A%5B%22customVariable1Key%22%2C%22customVariable1Value%22%5D%2C%225%22%3A%5B%22customVariable2Key%22%2C%22customVariable2Value%22%5D%7D&_idvc=2&_idts=1660070052&res=1024x768&lang=de%2Cde-de%3Bq%3D0.9%2Cen%3Bq%3D0.8&pv_id=lbBbxG&idgoal=0&revenue=12.34&ec_items=%5B%5B%22SKU%22%2C%22%22%2C%22%22%2C0.0%2C0%5D%2C%5B%22SKU%22%2C%22NAME%22%2C%22CATEGORY%22%2C123.4%2C0%5D%5D&token_auth=fdf6e8461ea9de33176b222519627f78&country=de&send_image=0&rand=someRandom" + + "\"],\"token_auth\" : \"" + "fdf6e8461ea9de33176b222519627f78" + "\"}"))); } @@ -362,9 +368,9 @@ void doesNothingIfNotEnabled() { @Test void exampleWorks() { - TrackerConfiguration config = - TrackerConfiguration.builder().apiEndpoint( - URI.create("https://your-domain.net/matomo/matomo.php")) + TrackerConfiguration config = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://your-domain.net/matomo/matomo.php")) .defaultSiteId(42) // if not explicitly specified by action .build(); @@ -372,8 +378,9 @@ void exampleWorks() { MatomoTracker tracker = new MatomoTracker(config); // Track an action - CompletableFuture future = tracker.sendRequestAsync( - MatomoRequest.builder().actionName("User Profile / Upload Profile Picture") + CompletableFuture future = tracker.sendRequestAsync(MatomoRequest + .builder() + .actionName("User Profile / Upload Profile Picture") .actionUrl("https://your-domain.net/user/profile/picture") .visitorId(VisitorId.fromHash("some@email-adress.org".hashCode())) // ... @@ -389,13 +396,15 @@ void exampleWorks() { void reportsErrors() { wireMockServer.stubFor(get(urlPathEqualTo("/failing")).willReturn(status(500))); - trackerConfigurationBuilder.apiEndpoint(URI.create(String.format( - "http://localhost:%d/failing", - wireMockServer.port() - ))).defaultSiteId(SITE_ID); + trackerConfigurationBuilder + .apiEndpoint(URI.create(String.format("http://localhost:%d/failing", + wireMockServer.port() + ))) + .defaultSiteId(SITE_ID); - assertThatThrownBy(this::whenSendsRequestAsync).hasRootCauseInstanceOf(MatomoException.class) - .hasRootCauseMessage("Tracking endpoint responded with code 500"); + assertThatThrownBy(this::whenSendsRequestAsync) + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Tracking endpoint responded with code 500"); assertThat(future).isCompletedExceptionally(); @@ -410,11 +419,11 @@ void includesDefaultTokenAuth() { whenSendsRequestAsync(); assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify( - getRequestedFor( - urlEqualTo( - "/matomo.php?idsite=42token_auth=fdf6e8461ea9de33176b222519627f78&rec=1&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom")) - .withHeader("User-Agent", equalTo("MatomoJavaClient"))); + wireMockServer.verify(getRequestedFor(urlEqualTo( + "/matomo.php?idsite=42token_auth=fdf6e8461ea9de33176b222519627f78&rec=1&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom")).withHeader( + "User-Agent", + equalTo("MatomoJavaClient") + )); } @@ -424,20 +433,21 @@ void includesMultipleQueriesInBulkRequest() throws Exception { givenTrackerConfigurationWithDefaultSiteId(); MatomoTracker tracker = new MatomoTracker(trackerConfigurationBuilder.build()); - CompletableFuture future1 = tracker.sendBulkRequestAsync( - Arrays.asList( - requestBuilder.actionName("First").build(), - requestBuilder.actionName("Second").build(), - requestBuilder.actionName("Third").build() - )); + CompletableFuture future1 = + tracker.sendBulkRequestAsync(Arrays.asList(requestBuilder.actionName("First").build(), + requestBuilder.actionName("Second").build(), + requestBuilder.actionName("Third").build() + )); future1.get(); assertThat(future1).isNotCompletedExceptionally(); - wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")).withHeader( - "Content-Length", equalTo("297")) - .withHeader("Accept", equalTo("*/*")).withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient")).withRequestBody(equalToJson( - "{\"requests\" : [ \"?idsite=42&rec=1&action_name=First&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\", \"?idsite=42&rec=1&action_name=Second&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\", \"?idsite=42&rec=1&action_name=Third&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\" ]}"))); + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("297")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{\"requests\" : [ \"?idsite=42&rec=1&action_name=First&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\", \"?idsite=42&rec=1&action_name=Second&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\", \"?idsite=42&rec=1&action_name=Third&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\" ]}"))); } @@ -447,9 +457,9 @@ void failsOnNegativeSiteId() { givenTrackerConfigurationWithDefaultSiteId(); requestBuilder.siteId(-1); - assertThatThrownBy(this::whenSendsRequestAsync).hasRootCauseInstanceOf( - IllegalArgumentException.class) - .hasRootCauseMessage("Site ID must not be negative"); + assertThatThrownBy(this::whenSendsRequestAsync) + .hasRootCauseInstanceOf(IllegalArgumentException.class) + .hasRootCauseMessage("Site ID must not be negative"); } @Test @@ -474,6 +484,10 @@ void doesNotSendRequestIfTrackerConfigurationIsDisabled() { wireMockServer.verify(0, getRequestedFor(urlPathEqualTo("/matomo.php"))); } + private void whenSendsSingleRequest() { + new MatomoTracker(trackerConfigurationBuilder.build()).sendRequest(requestBuilder.build()); + } + @Test void doesNotSendBulkRequestIfTrackerConfigurationIsDisabled() { givenTrackerConfigurationWithDefaultSiteId(); @@ -484,6 +498,10 @@ void doesNotSendBulkRequestIfTrackerConfigurationIsDisabled() { wireMockServer.verify(0, postRequestedFor(urlPathEqualTo("/matomo.php"))); } + private void whenSendsBulkRequest() { + new MatomoTracker(trackerConfigurationBuilder.build()).sendBulkRequest(singleton(requestBuilder.build())); + } + @Test void doesNotSendBulkRequestAsyncIfTrackerConfigurationIsDisabled() { givenTrackerConfigurationWithDefaultSiteId(); @@ -514,9 +532,10 @@ void sendsRequestsAsyncAndAcceptsCallback() throws Exception { givenTrackerConfigurationWithDefaultSiteId(); MatomoTracker tracker = new MatomoTracker(trackerConfigurationBuilder.build()); AtomicBoolean success = new AtomicBoolean(); - CompletableFuture future = tracker.sendBulkRequestAsync(singleton(requestBuilder.build()), v -> { - success.set(true); - }); + CompletableFuture future = + tracker.sendBulkRequestAsync(singleton(requestBuilder.build()), v -> { + success.set(true); + }); future.get(); assertThat(future).isNotCompletedExceptionally(); wireMockServer.verify(postRequestedFor(urlPathEqualTo("/matomo.php"))); diff --git a/src/test/java/org/matomo/java/tracking/PiwikDateTest.java b/src/test/java/org/matomo/java/tracking/PiwikDateTest.java index 848b402d..40c8bd80 100644 --- a/src/test/java/org/matomo/java/tracking/PiwikDateTest.java +++ b/src/test/java/org/matomo/java/tracking/PiwikDateTest.java @@ -1,11 +1,10 @@ package org.matomo.java.tracking; -import org.junit.jupiter.api.Test; -import org.piwik.java.tracking.PiwikDate; +import static org.assertj.core.api.Assertions.assertThat; import java.util.TimeZone; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; +import org.piwik.java.tracking.PiwikDate; class PiwikDateTest { diff --git a/src/test/java/org/matomo/java/tracking/PiwikLocaleTest.java b/src/test/java/org/matomo/java/tracking/PiwikLocaleTest.java index 318e111d..46e1c563 100644 --- a/src/test/java/org/matomo/java/tracking/PiwikLocaleTest.java +++ b/src/test/java/org/matomo/java/tracking/PiwikLocaleTest.java @@ -1,11 +1,10 @@ package org.matomo.java.tracking; -import org.junit.jupiter.api.Test; -import org.piwik.java.tracking.PiwikLocale; +import static org.assertj.core.api.Assertions.assertThat; import java.util.Locale; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; +import org.piwik.java.tracking.PiwikLocale; class PiwikLocaleTest { diff --git a/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java b/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java index 245bf4e3..04693cf9 100644 --- a/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java +++ b/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java @@ -266,9 +266,7 @@ void testAddCustomTrackingParameter2() { request.addCustomTrackingParameter("key", "value"); request.addCustomTrackingParameter("key", "value2"); List l = request.getCustomTrackingParameter("key"); - assertThat(l).hasSize(2) - .contains(new String[] {"value"}) - .contains(new String[] {"value2"}); + assertThat(l).hasSize(2).contains(new String[] {"value"}).contains(new String[] {"value2"}); } @Test @@ -682,7 +680,10 @@ void testRequestDatetimeTTT() { @Test void testRequestDatetimeTF() { request.setRequestDatetime(new PiwikDate()); - assertThat(request.getRequestDatetime().getZonedDateTime()).isCloseTo(ZonedDateTime.now(), within(2, MINUTES)); + assertThat(request.getRequestDatetime().getZonedDateTime()).isCloseTo( + ZonedDateTime.now(), + within(2, MINUTES) + ); } @Test @@ -946,8 +947,8 @@ void testVisitorVisitCount() { @Test void failsIfActionUrlIsNull() { assertThatThrownBy(() -> new PiwikRequest(3, null)) - .isInstanceOf(NullPointerException.class) - .hasMessage("Action URL must not be null"); + .isInstanceOf(NullPointerException.class) + .hasMessage("Action URL must not be null"); } } diff --git a/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java b/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java index 17365eb2..d760d634 100644 --- a/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java +++ b/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java @@ -30,8 +30,8 @@ class PiwikTrackerIT { - private static final WireMockServer wireMockServer = new WireMockServer( - WireMockConfiguration.options().dynamicPort()); + private static final WireMockServer wireMockServer = + new WireMockServer(WireMockConfiguration.options().dynamicPort()); private static final int SITE_ID = 42; @@ -48,7 +48,9 @@ static void beforeAll() { @BeforeEach void setUp() throws MalformedURLException { piwikTracker = new PiwikTracker( - String.format("http://localhost:%d/matomo.php", wireMockServer.port()), -1); + String.format("http://localhost:%d/matomo.php", wireMockServer.port()), + -1 + ); wireMockServer.resetRequests(); wireMockServer.stubFor(post(urlPathEqualTo("/matomo.php")).willReturn(status(204))); wireMockServer.stubFor(get(urlPathEqualTo("/matomo.php")).willReturn(status(204))); @@ -67,9 +69,10 @@ void testSendRequest() { piwikTracker.sendRequest(request); wireMockServer.verify(getRequestedFor(urlEqualTo( - "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue")) - .withHeader("User-Agent", equalTo("MatomoJavaClient"))) - ; + "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue")).withHeader( + "User-Agent", + equalTo("MatomoJavaClient") + )); } /** @@ -84,8 +87,10 @@ void testSendRequestAsync() throws Exception { assertThat(future).isNotCompletedExceptionally(); wireMockServer.verify(getRequestedFor(urlEqualTo( - "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue")) - .withHeader("User-Agent", equalTo("MatomoJavaClient"))); + "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue")).withHeader( + "User-Agent", + equalTo("MatomoJavaClient") + )); } @@ -100,12 +105,12 @@ void testSendBulkRequest_Iterable() { piwikTracker.sendBulkRequest(requests); wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) - .withHeader("Content-Length", equalTo("167")) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient")) - .withRequestBody(equalToJson( - "{ \"requests\" : [ \"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\" ]}"))); + .withHeader("Content-Length", equalTo("167")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{ \"requests\" : [ \"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\" ]}"))); } @@ -118,8 +123,8 @@ void testSendBulkRequest_Iterable_StringTT() { request.setCustomTrackingParameter("parameterName", "parameterValue"); assertThatThrownBy(() -> piwikTracker.sendBulkRequest(requests, "1")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Auth token must be exactly 32 characters long"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Auth token must be exactly 32 characters long"); } @Test @@ -130,12 +135,12 @@ void testSendBulkRequest_Iterable_StringFF() { piwikTracker.sendBulkRequest(requests, null); wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) - .withHeader("Content-Length", equalTo("167")) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient")) - .withRequestBody(equalToJson( - "{\"requests\":[\"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\"]}"))); + .withHeader("Content-Length", equalTo("167")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{\"requests\":[\"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\"]}"))); } @@ -147,12 +152,12 @@ void testSendBulkRequest_Iterable_StringFT() { piwikTracker.sendBulkRequest(requests, "12345678901234567890123456789012"); wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) - .withHeader("Content-Length", equalTo("215")) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient")) - .withRequestBody(equalToJson( - "{\"requests\":[\"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\" ],\"token_auth\":\"12345678901234567890123456789012\"}"))); + .withHeader("Content-Length", equalTo("215")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{\"requests\":[\"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\" ],\"token_auth\":\"12345678901234567890123456789012\"}"))); } @@ -170,12 +175,12 @@ void testSendBulkRequestAsync_Iterable() throws Exception { assertThat(future).isNotCompletedExceptionally(); wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) - .withHeader("Content-Length", equalTo("167")) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient")) - .withRequestBody(equalToJson( - "{\"requests\" : [ \"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\" ]}"))); + .withHeader("Content-Length", equalTo("167")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{\"requests\" : [ \"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\" ]}"))); } @@ -189,8 +194,8 @@ void testSendBulkRequestAsync_Iterable_StringTT() { request.setCustomTrackingParameter("parameterName", "parameterValue"); assertThatThrownBy(() -> piwikTracker.sendBulkRequestAsync(requests, "1").get()) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Auth token must be exactly 32 characters long"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Auth token must be exactly 32 characters long"); } @@ -201,53 +206,66 @@ void testSendBulkRequestAsync_Iterable_String() throws Exception { List requests = Collections.singletonList(request); request.setCustomTrackingParameter("parameterName", "parameterValue"); - CompletableFuture future = piwikTracker.sendBulkRequestAsync( - requests, "12345678901234567890123456789012"); + CompletableFuture future = + piwikTracker.sendBulkRequestAsync(requests, "12345678901234567890123456789012"); future.get(); assertThat(future).isNotCompletedExceptionally(); wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) - .withHeader("Content-Length", equalTo("215")) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient")) - .withRequestBody(equalToJson( - "{\"requests\":[ \"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\"],\"token_auth\":\"12345678901234567890123456789012\"}"))); + .withHeader("Content-Length", equalTo("215")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{\"requests\":[ \"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\"],\"token_auth\":\"12345678901234567890123456789012\"}"))); } @Test void createsPiwikTrackerWithHostUrl() { - PiwikTracker piwikTracker = new PiwikTracker(String.format("http://localhost:%d/matomo.php", wireMockServer.port())); + PiwikTracker piwikTracker = + new PiwikTracker(String.format("http://localhost:%d/matomo.php", wireMockServer.port())); piwikTracker.sendRequest(request); wireMockServer.verify(getRequestedFor(urlEqualTo( - "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand")) - .withHeader("User-Agent", equalTo("MatomoJavaClient"))); + "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand")).withHeader( + "User-Agent", + equalTo("MatomoJavaClient") + )); } @Test void createPiwikTrackerWithHostUrlAndProxyHostAndPort() { - PiwikTracker piwikTracker = new PiwikTracker(String.format("http://localhost:%d/matomo.php", wireMockServer.port()), "localhost", 8080); + PiwikTracker piwikTracker = + new PiwikTracker(String.format("http://localhost:%d/matomo.php", wireMockServer.port()), + "localhost", + 8080 + ); assertThatThrownBy(() -> piwikTracker.sendRequest(request)) - .isInstanceOf(MatomoException.class) - .hasMessage("Could not send request via GET") - .hasRootCauseInstanceOf(ConnectException.class) - .hasRootCauseMessage("Connection refused (Connection refused)"); + .isInstanceOf(MatomoException.class) + .hasMessage("Could not send request via GET") + .hasRootCauseInstanceOf(ConnectException.class) + .hasRootCauseMessage("Connection refused (Connection refused)"); } @Test void createPiwikTrackerWithHostUrlAndProxyHostAndPortAndTimeout() { - PiwikTracker piwikTracker = new PiwikTracker(String.format("http://localhost:%d/matomo.php", wireMockServer.port()), "localhost", 8080, 1000); + PiwikTracker piwikTracker = + new PiwikTracker(String.format("http://localhost:%d/matomo.php", wireMockServer.port()), + "localhost", + 8080, + 1000 + ); assertThatThrownBy(() -> piwikTracker.sendRequest(request)) - .isInstanceOf(MatomoException.class) - .hasMessage("Could not send request via GET") - .hasRootCauseInstanceOf(ConnectException.class) - .hasRootCauseMessage("Connection refused (Connection refused)");} + .isInstanceOf(MatomoException.class) + .hasMessage("Could not send request via GET") + .hasRootCauseInstanceOf(ConnectException.class) + .hasRootCauseMessage("Connection refused (Connection refused)"); + } } diff --git a/src/test/java/org/matomo/java/tracking/ProxyAuthenticatorTest.java b/src/test/java/org/matomo/java/tracking/ProxyAuthenticatorTest.java index f4199853..08893677 100644 --- a/src/test/java/org/matomo/java/tracking/ProxyAuthenticatorTest.java +++ b/src/test/java/org/matomo/java/tracking/ProxyAuthenticatorTest.java @@ -21,9 +21,31 @@ void createsPasswordAuthentication() throws Exception { givenPasswordAuthentication(RequestorType.PROXY); assertThat(passwordAuthentication.getUserName()).isEqualTo("user"); - assertThat(passwordAuthentication.getPassword()).contains('p', 'a', 's', 's', 'w', 'o', 'r', 'd'); + assertThat(passwordAuthentication.getPassword()).contains( + 'p', + 'a', + 's', + 's', + 'w', + 'o', + 'r', + 'd' + ); + + } + private void givenPasswordAuthentication(RequestorType proxy) throws Exception { + passwordAuthentication = Authenticator.requestPasswordAuthentication("host", + InetAddress.getLocalHost(), + 8080, + "http", + "prompt", + "https", + new URL("https://www.daniel-heid.de"), + proxy + ); } + @Test void returnsNullIfNoPasswordAuthentication() throws Exception { @@ -35,18 +57,5 @@ void returnsNullIfNoPasswordAuthentication() throws Exception { } - private void givenPasswordAuthentication(RequestorType proxy) throws Exception { - passwordAuthentication = Authenticator.requestPasswordAuthentication( - "host", - InetAddress.getLocalHost(), - 8080, - "http", - "prompt", - "https", - new URL("https://www.daniel-heid.de"), - proxy - ); - } - } diff --git a/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java b/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java index 0909c016..834016ce 100644 --- a/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java +++ b/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java @@ -28,9 +28,10 @@ class QueryCreatorTest { - private final MatomoRequest.MatomoRequestBuilder matomoRequestBuilder = - MatomoRequest.builder().visitorId(VisitorId.fromHash(1234567890123456789L)).randomValue( - RandomValue.fromString("random-value")); + private final MatomoRequest.MatomoRequestBuilder matomoRequestBuilder = MatomoRequest + .builder() + .visitorId(VisitorId.fromHash(1234567890123456789L)) + .randomValue(RandomValue.fromString("random-value")); private String defaultAuthToken = "876de1876fb2cda2816c362a61bfc712"; @@ -44,14 +45,18 @@ void usesDefaultSiteId() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value"); + "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value"); } private void whenCreatesQuery() { request = matomoRequestBuilder.build(); - TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( - URI.create("http://localhost")).defaultSiteId(42).defaultAuthToken(defaultAuthToken).build(); + TrackerConfiguration trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("http://localhost")) + .defaultSiteId(42) + .defaultAuthToken(defaultAuthToken) + .build(); String authToken = AuthToken.determineAuthToken(null, singleton(request), trackerConfiguration); query = new QueryCreator(trackerConfiguration).createQuery(request, authToken); } @@ -64,7 +69,7 @@ void overridesDefaultSiteId() { whenCreatesQuery(); assertThat(query).isEqualTo( - "token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&idsite=123&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value"); + "token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&idsite=123&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value"); } @@ -76,7 +81,7 @@ void usesDefaultTokenAuth() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=f123bfc9a46de0bb5453afdab6f93200&rec=1&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value"); + "idsite=42token_auth=f123bfc9a46de0bb5453afdab6f93200&rec=1&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value"); } @@ -89,7 +94,7 @@ void overridesDefaultTokenAuth() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=e456bfc9a46de0bb5453afdab6f93200&rec=1&apiv=1&_id=112210f47de98115&token_auth=e456bfc9a46de0bb5453afdab6f93200&send_image=0&rand=random-value"); + "idsite=42token_auth=e456bfc9a46de0bb5453afdab6f93200&rec=1&apiv=1&_id=112210f47de98115&token_auth=e456bfc9a46de0bb5453afdab6f93200&send_image=0&rand=random-value"); } @@ -98,8 +103,9 @@ void validatesTokenAuth() { matomoRequestBuilder.authToken("invalid-token-auth"); - assertThatThrownBy(this::whenCreatesQuery).isInstanceOf(IllegalArgumentException.class) - .hasMessage("Auth token must be exactly 32 characters long"); + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Auth token must be exactly 32 characters long"); } @@ -111,7 +117,7 @@ void convertsTrueBooleanTo1() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&fla=1&send_image=0&rand=random-value"); + "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&fla=1&send_image=0&rand=random-value"); } @@ -123,7 +129,7 @@ void convertsFalseBooleanTo0() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&java=0&send_image=0&rand=random-value"); + "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&java=0&send_image=0&rand=random-value"); } @@ -135,7 +141,7 @@ void encodesUrl() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&url=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fpage%3Ffoo%3Dbar&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value"); + "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&url=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fpage%3Ffoo%3Dbar&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value"); } @@ -147,7 +153,7 @@ void encodesReferrerUrl() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Freferrer%3Ffoo2%3Dbar2&send_image=0&rand=random-value"); + "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Freferrer%3Ffoo2%3Dbar2&send_image=0&rand=random-value"); } @@ -159,7 +165,7 @@ void encodesLink() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&link=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fexternal%2Flink%23&send_image=0&rand=random-value"); + "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&link=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fexternal%2Flink%23&send_image=0&rand=random-value"); } @@ -171,68 +177,91 @@ void encodesDownloadUrl() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&download=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fdownload.pdf&send_image=0&rand=random-value"); + "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&download=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fdownload.pdf&send_image=0&rand=random-value"); } @Test void tracksMinimalRequest() { - matomoRequestBuilder.actionName("Help / Feedback").actionUrl( - "https://www.daniel-heid.de/portfolio").visitorId(VisitorId.fromHash(3434343434343434343L)) - .referrerUrl("https://www.daniel-heid.de/referrer").visitCustomVariables( - new CustomVariables().add(new CustomVariable("customVariable1Key", "customVariable1Value"), 5) - .add(new CustomVariable("customVariable2Key", "customVariable2Value"), 6)) - .visitorVisitCount(2).visitorPreviousVisitTimestamp(Instant.parse("2022-08-09T18:34:12Z")) - .deviceResolution(DeviceResolution.builder().width(1024).height(768).build()) - .headerAcceptLanguage(AcceptLanguage.builder().languageRange(new LanguageRange("de")) - .languageRange(new LanguageRange("de-DE", 0.9)).languageRange(new LanguageRange("en", 0.8)) - .build()).pageViewId(UniqueId.fromValue(999999999999999999L)).goalId(0).ecommerceRevenue( - 12.34).ecommerceItems(EcommerceItems.builder().item( - EcommerceItem.builder().sku("SKU").build()).item(EcommerceItem.builder().sku("SKU").name( - "NAME").category("CATEGORY").price(123.4).build()).build()).authToken( - "fdf6e8461ea9de33176b222519627f78").visitorCountry( - Country.fromLanguageRanges("en-GB;q=0.7,de,de-DE;q=0.9,en;q=0.8,en-US;q=0.6")); + matomoRequestBuilder + .actionName("Help / Feedback") + .actionUrl("https://www.daniel-heid.de/portfolio") + .visitorId(VisitorId.fromHash(3434343434343434343L)) + .referrerUrl("https://www.daniel-heid.de/referrer") + .visitCustomVariables(new CustomVariables() + .add(new CustomVariable("customVariable1Key", "customVariable1Value"), 5) + .add(new CustomVariable("customVariable2Key", "customVariable2Value"), 6)) + .visitorVisitCount(2) + .visitorPreviousVisitTimestamp(Instant.parse("2022-08-09T18:34:12Z")) + .deviceResolution(DeviceResolution.builder().width(1024).height(768).build()) + .headerAcceptLanguage(AcceptLanguage + .builder() + .languageRange(new LanguageRange("de")) + .languageRange(new LanguageRange("de-DE", 0.9)) + .languageRange(new LanguageRange("en", 0.8)) + .build()) + .pageViewId(UniqueId.fromValue(999999999999999999L)) + .goalId(0) + .ecommerceRevenue(12.34) + .ecommerceItems(EcommerceItems + .builder() + .item(EcommerceItem.builder().sku("SKU").build()) + .item(EcommerceItem + .builder() + .sku("SKU") + .name("NAME") + .category("CATEGORY") + .price(123.4) + .build()) + .build()) + .authToken("fdf6e8461ea9de33176b222519627f78") + .visitorCountry(Country.fromLanguageRanges("en-GB;q=0.7,de,de-DE;q=0.9,en;q=0.8,en-US;q=0.6")); whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=fdf6e8461ea9de33176b222519627f78&rec=1&action_name=Help+%2F+Feedback&url=https%3A%2F%2Fwww.daniel-heid.de%2Fportfolio&apiv=1&_id=2fa93d2858bc4867&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Freferrer&_cvar=%7B%225%22%3A%5B%22customVariable1Key%22%2C%22customVariable1Value%22%5D%2C%226%22%3A%5B%22customVariable2Key%22%2C%22customVariable2Value%22%5D%7D&_idvc=2&_viewts=1660070052&res=1024x768&lang=de%2Cde-de%3Bq%3D0.9%2Cen%3Bq%3D0.8&pv_id=lbBbxG&idgoal=0&revenue=12.34&ec_items=%5B%5B%22SKU%22%2C%22%22%2C%22%22%2C0.0%2C0%5D%2C%5B%22SKU%22%2C%22NAME%22%2C%22CATEGORY%22%2C123.4%2C0%5D%5D&token_auth=fdf6e8461ea9de33176b222519627f78&country=de&send_image=0&rand=random-value"); + "idsite=42token_auth=fdf6e8461ea9de33176b222519627f78&rec=1&action_name=Help+%2F+Feedback&url=https%3A%2F%2Fwww.daniel-heid.de%2Fportfolio&apiv=1&_id=2fa93d2858bc4867&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Freferrer&_cvar=%7B%225%22%3A%5B%22customVariable1Key%22%2C%22customVariable1Value%22%5D%2C%226%22%3A%5B%22customVariable2Key%22%2C%22customVariable2Value%22%5D%7D&_idvc=2&_viewts=1660070052&res=1024x768&lang=de%2Cde-de%3Bq%3D0.9%2Cen%3Bq%3D0.8&pv_id=lbBbxG&idgoal=0&revenue=12.34&ec_items=%5B%5B%22SKU%22%2C%22%22%2C%22%22%2C0.0%2C0%5D%2C%5B%22SKU%22%2C%22NAME%22%2C%22CATEGORY%22%2C123.4%2C0%5D%5D&token_auth=fdf6e8461ea9de33176b222519627f78&country=de&send_image=0&rand=random-value"); } @Test void testGetQueryString() { - matomoRequestBuilder.siteId(3).actionUrl("http://test.com").randomValue( - RandomValue.fromString("random")).visitorId(VisitorId.fromHex("1234567890123456")); + matomoRequestBuilder + .siteId(3) + .actionUrl("http://test.com") + .randomValue(RandomValue.fromString("random")) + .visitorId(VisitorId.fromHex("1234567890123456")); defaultAuthToken = null; whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random"); - matomoRequestBuilder.pageCustomVariables( - new CustomVariables().add(new CustomVariable("key", "val"), 7)); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random"); + matomoRequestBuilder.pageCustomVariables(new CustomVariables().add(new CustomVariable( + "key", + "val" + ), 7)); whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random"); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random"); matomoRequestBuilder.customTrackingParameters(singletonMap("key", singleton("test"))); whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key=test"); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key=test"); matomoRequestBuilder.customTrackingParameters(singletonMap("key", asList("test", "test2"))); whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key=test&key=test2"); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key=test&key=test2"); Map> customTrackingParameters = new HashMap<>(); customTrackingParameters.put("key", asList("test", "test2")); customTrackingParameters.put("key2", Collections.singletonList("test3")); matomoRequestBuilder.customTrackingParameters(customTrackingParameters); whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key2=test3&key=test&key=test2"); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key2=test3&key=test&key=test2"); customTrackingParameters.put("key", Collections.singletonList("test4")); whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key2=test3&key=test4"); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key2=test3&key=test4"); matomoRequestBuilder.randomValue(null); matomoRequestBuilder.siteId(null); matomoRequestBuilder.required(null); @@ -242,49 +271,61 @@ void testGetQueryString() { matomoRequestBuilder.actionUrl(null); whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&key2=test3&key=test4"); + "idsite=42&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&key2=test3&key=test4"); } @Test void testGetQueryString2() { - matomoRequestBuilder.actionUrl("http://test.com").randomValue(RandomValue.fromString("random")) - .visitorId(VisitorId.fromHex("1234567890123456")).siteId(3); + matomoRequestBuilder + .actionUrl("http://test.com") + .randomValue(RandomValue.fromString("random")) + .visitorId(VisitorId.fromHex("1234567890123456")) + .siteId(3); defaultAuthToken = null; whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random"); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random"); } @Test void testGetUrlEncodedQueryString() { defaultAuthToken = null; - matomoRequestBuilder.actionUrl("http://test.com").randomValue(RandomValue.fromString("random")) - .visitorId(VisitorId.fromHex("1234567890123456")).siteId(3); + matomoRequestBuilder + .actionUrl("http://test.com") + .randomValue(RandomValue.fromString("random")) + .visitorId(VisitorId.fromHex("1234567890123456")) + .siteId(3); whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random"); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random"); Map> customTrackingParameters = new HashMap<>(); customTrackingParameters.put("ke/y", Collections.singletonList("te:st")); matomoRequestBuilder.customTrackingParameters(customTrackingParameters); whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast"); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast"); customTrackingParameters.put("ke/y", asList("te:st", "te:st2")); whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2"); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2"); customTrackingParameters.put("ke/y2", Collections.singletonList("te:st3")); whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2&ke%2Fy2=te%3Ast3"); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2&ke%2Fy2=te%3Ast3"); customTrackingParameters.put("ke/y", asList("te:st3", "te:st4")); whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast3&ke%2Fy=te%3Ast4&ke%2Fy2=te%3Ast3"); - matomoRequestBuilder.randomValue(null).siteId(null).required(null).apiVersion(null) - .responseAsImage(null).visitorId(null).actionUrl(null); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast3&ke%2Fy=te%3Ast4&ke%2Fy2=te%3Ast3"); + matomoRequestBuilder + .randomValue(null) + .siteId(null) + .required(null) + .apiVersion(null) + .responseAsImage(null) + .visitorId(null) + .actionUrl(null); whenCreatesQuery(); assertThat(query).isEqualTo("idsite=42&ke%2Fy=te%3Ast3&ke%2Fy=te%3Ast4&ke%2Fy2=te%3Ast3"); @@ -292,23 +333,27 @@ void testGetUrlEncodedQueryString() { @Test void testGetUrlEncodedQueryString2() { - matomoRequestBuilder.actionUrl("http://test.com").randomValue(RandomValue.fromString("random")) - .visitorId(VisitorId.fromHex("1234567890123456")); + matomoRequestBuilder + .actionUrl("http://test.com") + .randomValue(RandomValue.fromString("random")) + .visitorId(VisitorId.fromHex("1234567890123456")); defaultAuthToken = null; whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42&rec=1&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random"); + "idsite=42&rec=1&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random"); } @Test void testVisitCustomVariableCustomVariable() { - matomoRequestBuilder.randomValue(RandomValue.fromString("random")).visitorId( - VisitorId.fromHex("1234567890123456")).siteId(3); - org.matomo.java.tracking.CustomVariable cv = new org.matomo.java.tracking.CustomVariable( - "visitKey", "visitVal"); + matomoRequestBuilder + .randomValue(RandomValue.fromString("random")) + .visitorId(VisitorId.fromHex("1234567890123456")) + .siteId(3); + org.matomo.java.tracking.CustomVariable cv = + new org.matomo.java.tracking.CustomVariable("visitKey", "visitVal"); matomoRequestBuilder.visitCustomVariables(new CustomVariables().add(cv, 8)); defaultAuthToken = null; @@ -316,7 +361,7 @@ void testVisitCustomVariableCustomVariable() { assertThat(request.getVisitCustomVariable(1)).isNull(); assertThat(query).isEqualTo( - "rec=1&idsite=3&apiv=1&_id=1234567890123456&_cvar=%7B%228%22%3A%5B%22visitKey%22%2C%22visitVal%22%5D%7D&send_image=0&rand=random"); + "rec=1&idsite=3&apiv=1&_id=1234567890123456&_cvar=%7B%228%22%3A%5B%22visitKey%22%2C%22visitVal%22%5D%7D&send_image=0&rand=random"); } @Test @@ -327,7 +372,7 @@ void doesNotAppendEmptyString() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&e_a=&send_image=0&rand=random-value"); + "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&e_a=&send_image=0&rand=random-value"); } @@ -336,8 +381,9 @@ void testAuthTokenTT() { matomoRequestBuilder.authToken("1234"); - assertThatThrownBy(this::whenCreatesQuery).isInstanceOf(IllegalArgumentException.class) - .hasMessage("Auth token must be exactly 32 characters long"); + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Auth token must be exactly 32 characters long"); } @Test @@ -347,7 +393,7 @@ void createsQueryWithDimensions() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value&dimension1=firstDimension&dimension2=secondDimension"); + "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value&dimension1=firstDimension&dimension2=secondDimension"); } @Test @@ -357,7 +403,7 @@ void appendsCharsetParameters() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&cs=ISO-8859-1&send_image=0&rand=random-value"); + "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&cs=ISO-8859-1&send_image=0&rand=random-value"); } } diff --git a/src/test/java/org/matomo/java/tracking/RequestValidatorTest.java b/src/test/java/org/matomo/java/tracking/RequestValidatorTest.java index 2cf0653e..b6aec7ab 100644 --- a/src/test/java/org/matomo/java/tracking/RequestValidatorTest.java +++ b/src/test/java/org/matomo/java/tracking/RequestValidatorTest.java @@ -1,13 +1,12 @@ package org.matomo.java.tracking; -import org.junit.jupiter.api.Test; -import org.piwik.java.tracking.PiwikDate; -import org.piwik.java.tracking.PiwikLocale; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.time.Instant; import java.util.Locale; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import org.junit.jupiter.api.Test; +import org.piwik.java.tracking.PiwikDate; +import org.piwik.java.tracking.PiwikLocale; class RequestValidatorTest { @@ -19,8 +18,8 @@ void testEcommerceRevenue() { request.setEcommerceRevenue(20.0); assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); + .isInstanceOf(MatomoException.class) + .hasMessage("Goal ID must be set if ecommerce parameters are used"); } @@ -29,8 +28,8 @@ void testEcommerceDiscount() { request.setEcommerceDiscount(1.0); assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); + .isInstanceOf(MatomoException.class) + .hasMessage("Goal ID must be set if ecommerce parameters are used"); } @@ -39,8 +38,8 @@ void testEcommerceId() { request.setEcommerceId("1"); assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); + .isInstanceOf(MatomoException.class) + .hasMessage("Goal ID must be set if ecommerce parameters are used"); } @Test @@ -48,8 +47,8 @@ void testEcommerceSubtotal() { request.setEcommerceSubtotal(20.0); assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); + .isInstanceOf(MatomoException.class) + .hasMessage("Goal ID must be set if ecommerce parameters are used"); } @Test @@ -57,8 +56,8 @@ void testEcommerceShippingCost() { request.setEcommerceShippingCost(20.0); assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); + .isInstanceOf(MatomoException.class) + .hasMessage("Goal ID must be set if ecommerce parameters are used"); } @Test @@ -66,8 +65,8 @@ void testEcommerceLastOrderTimestamp() { request.setEcommerceLastOrderTimestamp(Instant.ofEpochSecond(1000L)); assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); + .isInstanceOf(MatomoException.class) + .hasMessage("Goal ID must be set if ecommerce parameters are used"); } @Test @@ -75,8 +74,8 @@ void testEcommerceTax() { request.setEcommerceTax(20.0); assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); + .isInstanceOf(MatomoException.class) + .hasMessage("Goal ID must be set if ecommerce parameters are used"); } @Test @@ -85,8 +84,8 @@ void testEcommerceItemE() { request.addEcommerceItem(new EcommerceItem("sku", "name", "category", 1.0, 2)); assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); + .isInstanceOf(MatomoException.class) + .hasMessage("Goal ID must be set if ecommerce parameters are used"); } @Test @@ -95,8 +94,8 @@ void testSearchResultsCount() { request.setSearchResultsCount(100L); assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Search query must be set if search results count is set"); + .isInstanceOf(MatomoException.class) + .hasMessage("Search query must be set if search results count is set"); } @@ -105,8 +104,9 @@ void testVisitorLongitude() { request.setVisitorLongitude(20.5); assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Auth token must be present if longitude, latitude, region, city or country are set"); + .isInstanceOf(MatomoException.class) + .hasMessage( + "Auth token must be present if longitude, latitude, region, city or country are set"); } @Test @@ -114,8 +114,9 @@ void testVisitorLatitude() { request.setVisitorLatitude(10.5); assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Auth token must be present if longitude, latitude, region, city or country are set"); + .isInstanceOf(MatomoException.class) + .hasMessage( + "Auth token must be present if longitude, latitude, region, city or country are set"); } @Test @@ -123,8 +124,9 @@ void testVisitorCity() { request.setVisitorCity("city"); assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Auth token must be present if longitude, latitude, region, city or country are set"); + .isInstanceOf(MatomoException.class) + .hasMessage( + "Auth token must be present if longitude, latitude, region, city or country are set"); } @Test @@ -132,8 +134,9 @@ void testVisitorRegion() { request.setVisitorRegion("region"); assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Auth token must be present if longitude, latitude, region, city or country are set"); + .isInstanceOf(MatomoException.class) + .hasMessage( + "Auth token must be present if longitude, latitude, region, city or country are set"); } @Test @@ -143,8 +146,9 @@ void testVisitorCountryTE() { assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Auth token must be present if longitude, latitude, region, city or country are set"); + .isInstanceOf(MatomoException.class) + .hasMessage( + "Auth token must be present if longitude, latitude, region, city or country are set"); } @Test @@ -154,8 +158,8 @@ void testRequestDatetime() { request.setRequestDatetime(date); assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Auth token must be present if request timestamp is more than four hours ago"); + .isInstanceOf(MatomoException.class) + .hasMessage("Auth token must be present if request timestamp is more than four hours ago"); } diff --git a/src/test/java/org/matomo/java/tracking/SenderIT.java b/src/test/java/org/matomo/java/tracking/SenderIT.java index 32763878..96301c2b 100644 --- a/src/test/java/org/matomo/java/tracking/SenderIT.java +++ b/src/test/java/org/matomo/java/tracking/SenderIT.java @@ -11,8 +11,8 @@ class SenderIT { - private static final WireMockServer wireMockServer = new WireMockServer( - WireMockConfiguration.options().dynamicPort()); + private static final WireMockServer wireMockServer = + new WireMockServer(WireMockConfiguration.options().dynamicPort()); @BeforeAll static void beforeAll() { @@ -21,76 +21,92 @@ static void beforeAll() { @Test void sendSingleFailsIfQueryIsMalformed() { - TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( - URI.create("telnet://localhost")).build(); - Sender sender = new Sender( - trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); - - assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())).isInstanceOf( - InvalidUrlException.class).hasRootCause( - new MalformedURLException("unknown protocol: telnet")); + TrackerConfiguration trackerConfiguration = + TrackerConfiguration.builder().apiEndpoint(URI.create("telnet://localhost")).build(); + Sender sender = + new Sender(trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) + .isInstanceOf(InvalidUrlException.class) + .hasRootCause(new MalformedURLException("unknown protocol: telnet")); } @Test void failsIfEndpointReturnsNotFound() { - TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( - URI.create(wireMockServer.baseUrl())).build(); + TrackerConfiguration trackerConfiguration = + TrackerConfiguration.builder().apiEndpoint(URI.create(wireMockServer.baseUrl())).build(); - Sender sender = new Sender( - trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); + Sender sender = + new Sender(trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); - assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())).isInstanceOf( - MatomoException.class).hasMessage("Tracking endpoint responded with code 404"); + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) + .isInstanceOf(MatomoException.class) + .hasMessage("Tracking endpoint responded with code 404"); } @Test void failsIfCouldNotConnectToEndpoint() { - TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( - URI.create("http://localhost:1234")).build(); + TrackerConfiguration trackerConfiguration = + TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost:1234")).build(); - Sender sender = new Sender( - trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); + Sender sender = + new Sender(trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); - assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())).isInstanceOf( - MatomoException.class).hasMessage("Could not send request via GET"); + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not send request via GET"); } @Test void connectsViaProxy() throws Exception { - TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( - URI.create(wireMockServer.baseUrl())).proxyHost("localhost").proxyPort(wireMockServer.port()) - .build(); - - Sender sender = new Sender( - trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); - - assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())).isInstanceOf( - MatomoException.class).hasMessage("Tracking endpoint responded with code 400"); + TrackerConfiguration trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create(wireMockServer.baseUrl())) + .proxyHost("localhost") + .proxyPort(wireMockServer.port()) + .build(); + + Sender sender = + new Sender(trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) + .isInstanceOf(MatomoException.class) + .hasMessage("Tracking endpoint responded with code 400"); } @Test void connectsViaProxyWithProxyUserNameAndPassword() throws Exception { - TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( - URI.create(wireMockServer.baseUrl())).proxyHost("localhost").proxyPort(wireMockServer.port()) - .proxyUserName("user").proxyPassword("password").build(); - - Sender sender = new Sender( - trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); - - assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())).isInstanceOf( - MatomoException.class).hasMessage("Tracking endpoint responded with code 400"); + TrackerConfiguration trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create(wireMockServer.baseUrl())) + .proxyHost("localhost") + .proxyPort(wireMockServer.port()) + .proxyUserName("user") + .proxyPassword("password") + .build(); + + Sender sender = + new Sender(trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) + .isInstanceOf(MatomoException.class) + .hasMessage("Tracking endpoint responded with code 400"); } @Test void logsFailedTracking() throws Exception { - TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( - URI.create(wireMockServer.baseUrl())).logFailedTracking(true).build(); - - Sender sender = new Sender( - trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); - - assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())).isInstanceOf( - MatomoException.class).hasMessage("Tracking endpoint responded with code 404"); + TrackerConfiguration trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create(wireMockServer.baseUrl())) + .logFailedTracking(true) + .build(); + + Sender sender = + new Sender(trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) + .isInstanceOf(MatomoException.class) + .hasMessage("Tracking endpoint responded with code 404"); } } diff --git a/src/test/java/org/matomo/java/tracking/TrackerConfigurationTest.java b/src/test/java/org/matomo/java/tracking/TrackerConfigurationTest.java index be5ebc90..11a7a9ba 100644 --- a/src/test/java/org/matomo/java/tracking/TrackerConfigurationTest.java +++ b/src/test/java/org/matomo/java/tracking/TrackerConfigurationTest.java @@ -9,53 +9,69 @@ class TrackerConfigurationTest { @Test void validateDoesNotFailIfDefaultAuthTokenIsNull() throws Exception { - TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( - URI.create("https://matomo.example/matomo.php")).defaultSiteId(1).defaultAuthToken(null) - .build(); + TrackerConfiguration trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken(null) + .build(); trackerConfiguration.validate(); } @Test void validateFailsIfDefaultAuthTokenIsEmpty() throws Exception { - TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( - URI.create("https://matomo.example/matomo.php")).defaultSiteId(1).defaultAuthToken("") - .build(); + TrackerConfiguration trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("") + .build(); - assertThatThrownBy(trackerConfiguration::validate).isInstanceOf(IllegalArgumentException.class) - .hasMessage("Auth token must be exactly 32 characters long"); + assertThatThrownBy(trackerConfiguration::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Auth token must be exactly 32 characters long"); } @Test void validateFailsIfDefaultAuthTokenIsTooLong() throws Exception { - TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( - URI.create("https://matomo.example/matomo.php")).defaultSiteId(1).defaultAuthToken( - "123456789012345678901234567890123") - .build(); + TrackerConfiguration trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("123456789012345678901234567890123") + .build(); - assertThatThrownBy(trackerConfiguration::validate).isInstanceOf(IllegalArgumentException.class) - .hasMessage("Auth token must be exactly 32 characters long"); + assertThatThrownBy(trackerConfiguration::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Auth token must be exactly 32 characters long"); } @Test void validateFailsIfDefaultAuthTokenIsTooShort() throws Exception { - TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( - URI.create("https://matomo.example/matomo.php")).defaultSiteId(1).defaultAuthToken( - "1234567890123456789012345678901") - .build(); + TrackerConfiguration trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("1234567890123456789012345678901") + .build(); - assertThatThrownBy(trackerConfiguration::validate).isInstanceOf(IllegalArgumentException.class) - .hasMessage("Auth token must be exactly 32 characters long"); + assertThatThrownBy(trackerConfiguration::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Auth token must be exactly 32 characters long"); } @Test void validateFailsIfDefaultAuthTokenContainsInvalidCharacters() throws Exception { - TrackerConfiguration trackerConfiguration = TrackerConfiguration.builder().apiEndpoint( - URI.create("https://matomo.example/matomo.php")).defaultSiteId(1).defaultAuthToken( - "1234567890123456789012345678901!") - .build(); + TrackerConfiguration trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("1234567890123456789012345678901!") + .build(); - assertThatThrownBy(trackerConfiguration::validate).isInstanceOf(IllegalArgumentException.class) - .hasMessage("Auth token must contain only lowercase letters and numbers"); + assertThatThrownBy(trackerConfiguration::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Auth token must contain only lowercase letters and numbers"); } } diff --git a/src/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java b/src/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java index a637e20f..337c71b4 100644 --- a/src/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java +++ b/src/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java @@ -9,42 +9,45 @@ class TrackingParameterMethodTest { @Test void validateParameterValueFailsIfPatternDoesNotMatch() { - TrackingParameterMethod trackingParameterMethod = TrackingParameterMethod.builder() - .parameterName("foo") - .pattern(Pattern.compile("bar")) - .build(); + TrackingParameterMethod trackingParameterMethod = TrackingParameterMethod + .builder() + .parameterName("foo") + .pattern(Pattern.compile("bar")) + .build(); assertThatThrownBy(() -> trackingParameterMethod.validateParameterValue("baz")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Invalid value for foo. Must match regex bar"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid value for foo. Must match regex bar"); } @Test void doNothingIfPatternIsNull() { - TrackingParameterMethod trackingParameterMethod = TrackingParameterMethod.builder() - .parameterName("foo") - .build(); + TrackingParameterMethod trackingParameterMethod = + TrackingParameterMethod.builder().parameterName("foo").build(); trackingParameterMethod.validateParameterValue("baz"); } @Test void doNothingIfParameterValueIsNotCharSequence() { - TrackingParameterMethod trackingParameterMethod = TrackingParameterMethod.builder() - .parameterName("foo") - .pattern(Pattern.compile("bar")) - .build(); + TrackingParameterMethod trackingParameterMethod = TrackingParameterMethod + .builder() + .parameterName("foo") + .pattern(Pattern.compile("bar")) + .build(); trackingParameterMethod.validateParameterValue(1); } @Test void doNothingIfParameterValueIsNull() { - TrackingParameterMethod trackingParameterMethod = TrackingParameterMethod.builder() - .parameterName("foo") - .pattern(Pattern.compile("bar")) - .build(); + TrackingParameterMethod trackingParameterMethod = TrackingParameterMethod + .builder() + .parameterName("foo") + .pattern(Pattern.compile("bar")) + .build(); - trackingParameterMethod.validateParameterValue(null);} + trackingParameterMethod.validateParameterValue(null); + } } diff --git a/src/test/java/org/matomo/java/tracking/parameters/AcceptLanguageTest.java b/src/test/java/org/matomo/java/tracking/parameters/AcceptLanguageTest.java index 6f289697..6235949d 100644 --- a/src/test/java/org/matomo/java/tracking/parameters/AcceptLanguageTest.java +++ b/src/test/java/org/matomo/java/tracking/parameters/AcceptLanguageTest.java @@ -19,11 +19,11 @@ class AcceptLanguageTest { @Test void fromHeader() { - AcceptLanguage acceptLanguage = AcceptLanguage.fromHeader( - "de,de-DE;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"); + AcceptLanguage acceptLanguage = + AcceptLanguage.fromHeader("de,de-DE;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"); assertThat(acceptLanguage).hasToString( - "de,de-de;q=0.9,de-dd;q=0.9,en;q=0.8,en-gb;q=0.7,en-us;q=0.6"); + "de,de-de;q=0.9,de-dd;q=0.9,en;q=0.8,en-gb;q=0.7,en-us;q=0.6"); } @@ -39,8 +39,10 @@ void fromHeaderToleratesNull(String header) { @Test void failsOnNullLanguageRange() { - assertThat(AcceptLanguage.builder().languageRanges(singletonList(null)).build()).hasToString( - ""); + assertThat(AcceptLanguage + .builder() + .languageRanges(singletonList(null)) + .build()).hasToString(""); } } diff --git a/src/test/java/org/matomo/java/tracking/parameters/CountryTest.java b/src/test/java/org/matomo/java/tracking/parameters/CountryTest.java index f0ae93ee..097357c1 100644 --- a/src/test/java/org/matomo/java/tracking/parameters/CountryTest.java +++ b/src/test/java/org/matomo/java/tracking/parameters/CountryTest.java @@ -48,18 +48,18 @@ void returnsNullOnEmptyRanges(String ranges) { @Test void failsOnInvalidCountryCode() { - assertThatThrownBy(() -> Country.fromCode("invalid")).isInstanceOf( - IllegalArgumentException.class).hasMessage( - "Invalid country code"); + assertThatThrownBy(() -> Country.fromCode("invalid")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid country code"); } @Test void failsOnInvalidCountryCodeLength() { - assertThatThrownBy(() -> Country.fromCode("invalid")).isInstanceOf( - IllegalArgumentException.class).hasMessage( - "Invalid country code"); + assertThatThrownBy(() -> Country.fromCode("invalid")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid country code"); } @@ -99,15 +99,6 @@ void returnsNullOnNullRanges() { } - @Test - void returnsNullOnEmptyRanges() { - - Country country = Country.fromLanguageRanges(""); - - assertThat(country).isNull(); - - } - @Test void returnsNullOnBlankRanges() { @@ -120,45 +111,43 @@ void returnsNullOnBlankRanges() { @Test void failsOnInvalidRanges() { - assertThatThrownBy(() -> Country.fromLanguageRanges("invalid")).isInstanceOf( - IllegalArgumentException.class).hasMessage( - "Invalid country code"); + assertThatThrownBy(() -> Country.fromLanguageRanges("invalid")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid country code"); } @Test void failsOnLocaleWithoutCountryCode() { - assertThatThrownBy(() -> new Country(Locale.forLanguageTag("de"))).isInstanceOf( - IllegalArgumentException.class).hasMessage( - "Invalid locale"); + assertThatThrownBy(() -> new Country(Locale.forLanguageTag("de"))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid locale"); } @Test void setLocaleFailsOnNullLocale() { - assertThatThrownBy(() -> new Country(Locale.forLanguageTag("de")).setLocale(null)).isInstanceOf( - IllegalArgumentException.class).hasMessage( - "Invalid locale"); + assertThatThrownBy(() -> new Country(Locale.forLanguageTag("de")).setLocale(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid locale"); } @Test void setLocaleFailsOnNullCountryCode() { - assertThatThrownBy(() -> new Country(Locale.forLanguageTag("de")).setLocale(Locale.forLanguageTag("de"))).isInstanceOf( - IllegalArgumentException.class).hasMessage( - "Invalid locale"); + assertThatThrownBy(() -> new Country(Locale.forLanguageTag("de")).setLocale(Locale.forLanguageTag( + "de"))).isInstanceOf(IllegalArgumentException.class).hasMessage("Invalid locale"); } @Test void setLocaleFailsOnEmptyCountryCode() { - assertThatThrownBy(() -> new Country(Locale.forLanguageTag("de")).setLocale(Locale.forLanguageTag("de"))).isInstanceOf( - IllegalArgumentException.class).hasMessage( - "Invalid locale"); + assertThatThrownBy(() -> new Country(Locale.forLanguageTag("de")).setLocale(Locale.forLanguageTag( + "de"))).isInstanceOf(IllegalArgumentException.class).hasMessage("Invalid locale"); } diff --git a/src/test/java/org/matomo/java/tracking/parameters/CustomVariablesTest.java b/src/test/java/org/matomo/java/tracking/parameters/CustomVariablesTest.java index cfb5087c..c31e2bdc 100644 --- a/src/test/java/org/matomo/java/tracking/parameters/CustomVariablesTest.java +++ b/src/test/java/org/matomo/java/tracking/parameters/CustomVariablesTest.java @@ -37,14 +37,14 @@ void testAdd_CustomVariable() { assertThat(customVariables.get(5)).isEqualTo(c); assertThat(customVariables.get(3)).isNull(); assertThat(customVariables).hasToString( - "{\"1\":[\"a\",\"b\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"e\"]}"); + "{\"1\":[\"a\",\"b\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"e\"]}"); CustomVariable d = new CustomVariable("a", "f"); customVariables.add(d); assertThat(customVariables.get("a")).isEqualTo("f"); assertThat(customVariables.get(1)).isEqualTo(d); assertThat(customVariables.get(5)).isEqualTo(d); assertThat(customVariables).hasToString( - "{\"1\":[\"a\",\"f\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"f\"]}"); + "{\"1\":[\"a\",\"f\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"f\"]}"); customVariables.remove("a"); assertThat(customVariables.get("a")).isNull(); assertThat(customVariables.get(1)).isNull(); @@ -96,75 +96,74 @@ void notEqualCustomVariables() { @Test void testAddCustomVariableNull() { assertThatThrownBy(() -> customVariables.add(null)) - .isInstanceOf(NullPointerException.class) - .hasMessage("variable" - + " is marked non-null but is null") - .hasNoCause(); + .isInstanceOf(NullPointerException.class) + .hasMessage("variable" + " is marked non-null but is null") + .hasNoCause(); } @Test void testAddCustomVariableKeyEmpty() { assertThatThrownBy(() -> customVariables.add(new CustomVariable("", "b"))) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Custom variable key must not be null or empty") - .hasNoCause(); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Custom variable key must not be null or empty") + .hasNoCause(); } @Test void testAddCustomVariableValueEmpty() { assertThatThrownBy(() -> customVariables.add(new CustomVariable("a", ""))) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Custom variable value must not be null or empty") - .hasNoCause(); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Custom variable value must not be null or empty") + .hasNoCause(); } @Test void testAddCustomVariableNullIndex() { assertThatThrownBy(() -> customVariables.add(new CustomVariable("a", "b"), 0)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Index must be greater than 0") - .hasNoCause(); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Index must be greater than 0") + .hasNoCause(); } @Test void testAddNullCustomVariableIndex() { assertThatThrownBy(() -> customVariables.add(null, 1)) - .isInstanceOf(NullPointerException.class) - .hasMessage("cv is marked non-null but is null") - .hasNoCause(); + .isInstanceOf(NullPointerException.class) + .hasMessage("cv is marked non-null but is null") + .hasNoCause(); } @Test void testGetCustomVariableIntegerNull() { assertThatThrownBy(() -> customVariables.get(0)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Index must be greater than 0") - .hasNoCause(); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Index must be greater than 0") + .hasNoCause(); } @Test void testGetCustomVariableKeyNull() { assertThatThrownBy(() -> customVariables.get(null)) - .isInstanceOf(NullPointerException.class) - .hasMessage("key is marked non-null but is null") - .hasNoCause(); + .isInstanceOf(NullPointerException.class) + .hasMessage("key is marked non-null but is null") + .hasNoCause(); } @Test void testGetCustomVariableKeyEmpty() { assertThatThrownBy(() -> customVariables.get("")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("key must not be null or empty") - .hasNoCause(); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("key must not be null or empty") + .hasNoCause(); } @Test void testRemoveCustomVariableKeyNull() { assertThatThrownBy(() -> customVariables.remove(null)) - .isInstanceOf(NullPointerException.class) - .hasMessage("key is marked non-null but is null") - .hasNoCause(); + .isInstanceOf(NullPointerException.class) + .hasMessage("key is marked non-null but is null") + .hasNoCause(); } } diff --git a/src/test/java/org/matomo/java/tracking/parameters/DeviceResolutionTest.java b/src/test/java/org/matomo/java/tracking/parameters/DeviceResolutionTest.java index c87bb778..f05e4707 100644 --- a/src/test/java/org/matomo/java/tracking/parameters/DeviceResolutionTest.java +++ b/src/test/java/org/matomo/java/tracking/parameters/DeviceResolutionTest.java @@ -35,8 +35,8 @@ void returnsNullOnNull() { @Test void failsOnWrongDimensionSize() { assertThatThrownBy(() -> DeviceResolution.fromString("1920x1080x720")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Wrong dimension size"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Wrong dimension size"); } } diff --git a/src/test/java/org/matomo/java/tracking/parameters/EcommerceItemsTest.java b/src/test/java/org/matomo/java/tracking/parameters/EcommerceItemsTest.java index 76f67d25..1a467a24 100644 --- a/src/test/java/org/matomo/java/tracking/parameters/EcommerceItemsTest.java +++ b/src/test/java/org/matomo/java/tracking/parameters/EcommerceItemsTest.java @@ -1,10 +1,10 @@ package org.matomo.java.tracking.parameters; +import static org.assertj.core.api.Assertions.assertThat; + import org.junit.jupiter.api.Test; import org.matomo.java.tracking.EcommerceItem; -import static org.assertj.core.api.Assertions.assertThat; - class EcommerceItemsTest { diff --git a/src/test/java/org/matomo/java/tracking/parameters/UniqueIdTest.java b/src/test/java/org/matomo/java/tracking/parameters/UniqueIdTest.java index 0bd59140..00d1bca9 100644 --- a/src/test/java/org/matomo/java/tracking/parameters/UniqueIdTest.java +++ b/src/test/java/org/matomo/java/tracking/parameters/UniqueIdTest.java @@ -1,9 +1,9 @@ package org.matomo.java.tracking.parameters; -import org.junit.jupiter.api.Test; - import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; + class UniqueIdTest { @Test diff --git a/src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java b/src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java index 11dd23e5..eea9a45e 100644 --- a/src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java +++ b/src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java @@ -1,33 +1,32 @@ package org.matomo.java.tracking.parameters; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - class VisitorIdTest { private static Stream validHexStrings() { return Stream.of( - Arguments.of("0", "0000000000000000"), - Arguments.of("0000", "0000000000000000"), - Arguments.of("1", "0000000000000001"), - Arguments.of("a", "000000000000000a"), - Arguments.of("1a", "000000000000001a"), - Arguments.of("01a", "000000000000001a"), - Arguments.of("1a2b", "0000000000001a2b"), - Arguments.of("1a2b3c", "00000000001a2b3c"), - Arguments.of("1a2b3c4d", "000000001a2b3c4d"), - Arguments.of("1a2b3c4d5e", "0000001a2b3c4d5e"), - Arguments.of("1A2B3C4D5E", "0000001a2b3c4d5e"), - Arguments.of("1a2b3c4d5e6f", "00001a2b3c4d5e6f"), - Arguments.of("1a2b3c4d5e6f7a", "001a2b3c4d5e6f7a") + Arguments.of("0", "0000000000000000"), + Arguments.of("0000", "0000000000000000"), + Arguments.of("1", "0000000000000001"), + Arguments.of("a", "000000000000000a"), + Arguments.of("1a", "000000000000001a"), + Arguments.of("01a", "000000000000001a"), + Arguments.of("1a2b", "0000000000001a2b"), + Arguments.of("1a2b3c", "00000000001a2b3c"), + Arguments.of("1a2b3c4d", "000000001a2b3c4d"), + Arguments.of("1a2b3c4d5e", "0000001a2b3c4d5e"), + Arguments.of("1A2B3C4D5E", "0000001a2b3c4d5e"), + Arguments.of("1a2b3c4d5e6f", "00001a2b3c4d5e6f"), + Arguments.of("1a2b3c4d5e6f7a", "001a2b3c4d5e6f7a") ); } @@ -118,24 +117,25 @@ void createsVisitorIdFrom2CharacterHex() { void failsOnInvalidHexString() { assertThatThrownBy(() -> VisitorId.fromHex("invalid123456789")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Input must be a valid hex string") - ; + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Input must be a valid hex string"); } @ParameterizedTest - @ValueSource(strings = {"g", "gh", "ghi", "ghij", "ghijk", "ghijkl", "ghijklm", "ghijklmn", "ghijklmn", "-1"}) + @ValueSource(strings = + {"g", "gh", "ghi", "ghij", "ghijk", "ghijkl", "ghijklm", "ghijklmn", "ghijklmn", "-1"}) void failsOnInvalidHexString(String hex) { assertThatThrownBy(() -> VisitorId.fromHex(hex)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Input must be a valid hex string") - ; + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Input must be a valid hex string"); } @ParameterizedTest @MethodSource("validHexStrings") - void createsVisitorIdFromHex(String hex, String expected) { + void createsVisitorIdFromHex( + String hex, String expected + ) { VisitorId visitorId = VisitorId.fromHex(hex); @@ -147,18 +147,16 @@ void createsVisitorIdFromHex(String hex, String expected) { @ValueSource(strings = {"", " "}) void failsOnEmptyStrings(String hex) { assertThatThrownBy(() -> VisitorId.fromHex(hex)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Hex string must not be null or empty") - ; + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Hex string must not be null or empty"); } @ParameterizedTest @ValueSource(strings = {"1234567890abcdefg", "1234567890abcdeff"}) void failsOnInvalidHexStringLength(String hex) { assertThatThrownBy(() -> VisitorId.fromHex(hex)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Hex string must not be longer than 16 characters") - ; + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Hex string must not be longer than 16 characters"); } } From f5b6bceed8ef04e66e899ec48e38e9f4f6a68727 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Fri, 3 Nov 2023 11:48:34 +0100 Subject: [PATCH 152/467] Create VisitorID from UUID --- README.md | 1 + .../matomo/java/tracking/MatomoRequest.java | 8 +++++++- .../java/tracking/parameters/VisitorId.java | 14 ++++++++++++++ .../tracking/parameters/VisitorIdTest.java | 18 ++++++++++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 67f4572f..83036daf 100644 --- a/README.md +++ b/README.md @@ -421,6 +421,7 @@ following breaking changes: * `visitorId` and `visitorCustomId` are of type `VisitorId`. You can build them easily using `VisitorId.fromHash(...)`. * You can use `VisitorId.fromHex()` to create a `VisitorId` from a string that contains only hexadecimal characters. +* Or simply use `VisitorId.fromUUID()` to create a `VisitorId` from a `UUID` object. * VisitorId.fromHex() supports less than 16 hexadecimal characters. If the string is shorter than 16 characters, the remaining characters will be filled with zeros. diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/src/main/java/org/matomo/java/tracking/MatomoRequest.java index f7778af0..3a5e5fb6 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -18,6 +18,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.UUID; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Builder.Default; @@ -86,7 +87,12 @@ public class MatomoRequest { private String apiVersion = "1"; /** - * The unique visitor ID. See {@link VisitorId} + * The unique visitor ID. See {@link VisitorId}. Default is {@link VisitorId#random()} + * + *

Since version 3.0.0 this parameter is of type {@link VisitorId} and not a String anymore. + * Use {@link VisitorId#fromHex(String)} to create a VisitorId from a hex string, + * {@link VisitorId#fromUUID(UUID)} to create it from a UUID or {@link VisitorId#fromHash(long)} + * to create it from a long value. */ @TrackingParameter(name = "_id") @Default diff --git a/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java b/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java index ba545daf..fcc4e64f 100644 --- a/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java +++ b/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java @@ -9,7 +9,9 @@ import java.security.SecureRandom; import java.util.Random; +import java.util.UUID; import java.util.regex.Pattern; +import lombok.NonNull; /** * The unique visitor ID, must be a 16 characters hexadecimal string. Every unique visitor must be assigned a different @@ -56,6 +58,18 @@ public static VisitorId fromHash(long hash) { return visitorId; } + /** + * Creates a visitor id from a UUID. + * + *

Uses the most significant bits of the UUID to create the visitor id. + * + * @param uuid A UUID to create the visitor id from + * @return The visitor id for the given UUID + */ + public static VisitorId fromUUID(@NonNull UUID uuid) { + return fromHash(uuid.getMostSignificantBits()); + } + /** * Creates a visitor id from a hexadecimal string. * diff --git a/src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java b/src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java index eea9a45e..aff51fbf 100644 --- a/src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java +++ b/src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java @@ -159,4 +159,22 @@ void failsOnInvalidHexStringLength(String hex) { .hasMessage("Hex string must not be longer than 16 characters"); } + @Test + void createsVisitorIdFromUUID() { + + VisitorId visitorId = VisitorId.fromUUID( + java.util.UUID.fromString("12345678-90ab-cdef-1234-567890abcdef") + ); + + assertThat(visitorId).hasToString("1234567890abcdef"); + + } + + @Test + void failsOnNullUUID() { + assertThatThrownBy(() -> VisitorId.fromUUID(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("uuid is marked non-null but is null"); + } + } From 0231d55db669883cc1fd61ea0e6517573df98c6d Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Fri, 3 Nov 2023 12:30:49 +0100 Subject: [PATCH 153/467] Allow to disable SSL checks --- .editorconfig | 48 ++++++------ README.md | 11 ++- .../java/org/matomo/java/tracking/Sender.java | 77 +++++++++++++++---- .../java/tracking/TrackerConfiguration.java | 23 +++++- .../tracking/TrustingHostnameVerifier.java | 18 +++++ .../tracking/TrustingX509TrustManager.java | 28 +++++++ .../org/matomo/java/tracking/SenderIT.java | 46 +++++++++-- .../TrustingHostnameVerifierTest.java | 17 ++++ .../TrustingX509TrustManagerTest.java | 28 +++++++ 9 files changed, 247 insertions(+), 49 deletions(-) create mode 100644 src/main/java/org/matomo/java/tracking/TrustingHostnameVerifier.java create mode 100644 src/main/java/org/matomo/java/tracking/TrustingX509TrustManager.java create mode 100644 src/test/java/org/matomo/java/tracking/TrustingHostnameVerifierTest.java create mode 100644 src/test/java/org/matomo/java/tracking/TrustingX509TrustManagerTest.java diff --git a/.editorconfig b/.editorconfig index 7525a5d7..13e36c31 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,7 +11,7 @@ ij_formatter_off_tag = @formatter:off ij_formatter_on_tag = @formatter:on ij_formatter_tags_enabled = true ij_smart_tabs = false -ij_visual_guides = +ij_visual_guides = ij_wrap_on_typing = false [*.java] @@ -67,7 +67,7 @@ ij_java_blank_lines_before_package = 1 ij_java_block_brace_style = end_of_line ij_java_block_comment_add_space = false ij_java_block_comment_at_first_column = true -ij_java_builder_methods = +ij_java_builder_methods = ij_java_call_parameters_new_line_after_left_paren = true ij_java_call_parameters_right_paren_on_new_line = true ij_java_call_parameters_wrap = on_every_item @@ -105,8 +105,8 @@ ij_java_enum_constants_wrap = split_into_lines ij_java_extends_keyword_wrap = normal ij_java_extends_list_wrap = on_every_item ij_java_field_annotation_wrap = split_into_lines -ij_java_field_name_prefix = -ij_java_field_name_suffix = +ij_java_field_name_prefix = +ij_java_field_name_suffix = ij_java_finally_on_new_line = false ij_java_for_brace_force = always ij_java_for_statement_new_line_after_left_paren = false @@ -115,7 +115,7 @@ ij_java_for_statement_wrap = on_every_item ij_java_generate_final_locals = false ij_java_generate_final_parameters = false ij_java_if_brace_force = always -ij_java_imports_layout = $*, |, * +ij_java_imports_layout = $*,|,* ij_java_indent_case_from_switch = true ij_java_insert_inner_class_imports = false ij_java_insert_override_annotation = true @@ -140,8 +140,8 @@ ij_java_layout_static_imports_separately = true ij_java_line_comment_add_space = false ij_java_line_comment_add_space_on_reformat = false ij_java_line_comment_at_first_column = true -ij_java_local_variable_name_prefix = -ij_java_local_variable_name_suffix = +ij_java_local_variable_name_prefix = +ij_java_local_variable_name_suffix = ij_java_method_annotation_wrap = split_into_lines ij_java_method_brace_style = end_of_line ij_java_method_call_chain_wrap = on_every_item @@ -154,17 +154,17 @@ ij_java_names_count_to_use_import_on_demand = 999 ij_java_new_line_after_lparen_in_annotation = true ij_java_new_line_after_lparen_in_deconstruction_pattern = true ij_java_new_line_after_lparen_in_record_header = true -ij_java_packages_to_use_import_on_demand = +ij_java_packages_to_use_import_on_demand = ij_java_parameter_annotation_wrap = split_into_lines -ij_java_parameter_name_prefix = -ij_java_parameter_name_suffix = +ij_java_parameter_name_prefix = +ij_java_parameter_name_suffix = ij_java_parentheses_expression_new_line_after_left_paren = true ij_java_parentheses_expression_right_paren_on_new_line = true ij_java_place_assignment_sign_on_next_line = false ij_java_prefer_longer_names = true ij_java_prefer_parameters_wrap = false ij_java_record_components_wrap = on_every_item -ij_java_repeat_annotations = +ij_java_repeat_annotations = ij_java_repeat_synchronized = true ij_java_replace_instanceof_and_cast = false ij_java_replace_null_check = true @@ -254,13 +254,13 @@ ij_java_spaces_within_synchronized_parentheses = false ij_java_spaces_within_try_parentheses = false ij_java_spaces_within_while_parentheses = false ij_java_special_else_if_treatment = true -ij_java_static_field_name_prefix = -ij_java_static_field_name_suffix = -ij_java_subclass_name_prefix = +ij_java_static_field_name_prefix = +ij_java_static_field_name_suffix = +ij_java_subclass_name_prefix = ij_java_subclass_name_suffix = Impl ij_java_ternary_operation_signs_on_next_line = false ij_java_ternary_operation_wrap = normal -ij_java_test_name_prefix = +ij_java_test_name_prefix = ij_java_test_name_suffix = Test ij_java_throws_keyword_wrap = normal ij_java_throws_list_wrap = on_every_item @@ -397,7 +397,7 @@ ij_groovy_ginq_on_wrap_policy = 1 ij_groovy_ginq_space_after_keyword = true ij_groovy_if_brace_force = never ij_groovy_import_annotation_wrap = 2 -ij_groovy_imports_layout = *, |, javax.**, java.**, |, $* +ij_groovy_imports_layout = *,|,javax.**,java.**,|,$* ij_groovy_indent_case_from_switch = true ij_groovy_indent_label_blocks = true ij_groovy_insert_inner_class_imports = false @@ -428,7 +428,7 @@ ij_groovy_method_parameters_right_paren_on_new_line = false ij_groovy_method_parameters_wrap = off ij_groovy_modifier_list_wrap = false ij_groovy_names_count_to_use_import_on_demand = 3 -ij_groovy_packages_to_use_import_on_demand = java.awt.*, javax.swing.* +ij_groovy_packages_to_use_import_on_demand = java.awt.*,javax.swing.* ij_groovy_parameter_annotation_wrap = off ij_groovy_parentheses_expression_new_line_after_left_paren = false ij_groovy_parentheses_expression_right_paren_on_new_line = false @@ -542,23 +542,23 @@ ij_json_spaces_within_brackets = false ij_json_wrap_long_lines = false [{*.htm,*.html,*.sht,*.shtm,*.shtml}] -ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3 +ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3 ij_html_align_attributes = true ij_html_align_text = false ij_html_attribute_wrap = normal ij_html_block_comment_add_space = false ij_html_block_comment_at_first_column = true ij_html_do_not_align_children_of_min_lines = 0 -ij_html_do_not_break_if_inline_tags = title, h1, h2, h3, h4, h5, h6, p -ij_html_do_not_indent_children_of_tags = html, body, thead, tbody, tfoot +ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p +ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot ij_html_enforce_quotes = false -ij_html_inline_tags = a, abbr, acronym, b, basefont, bdo, big, br, cite, cite, code, dfn, em, font, i, img, input, kbd, label, q, s, samp, select, small, span, strike, strong, sub, sup, textarea, tt, u, var +ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var ij_html_keep_blank_lines = 2 ij_html_keep_indents_on_empty_lines = false ij_html_keep_line_breaks = true ij_html_keep_line_breaks_in_text = true ij_html_keep_whitespaces = false -ij_html_keep_whitespaces_inside = span, pre, textarea +ij_html_keep_whitespaces_inside = span,pre,textarea ij_html_line_comment_at_first_column = true ij_html_new_line_after_last_attribute = never ij_html_new_line_before_first_attribute = never @@ -603,7 +603,7 @@ ij_kotlin_field_annotation_wrap = split_into_lines ij_kotlin_finally_on_new_line = false ij_kotlin_if_rparen_on_new_line = false ij_kotlin_import_nested_classes = false -ij_kotlin_imports_layout = *, java.**, javax.**, kotlin.**, ^ +ij_kotlin_imports_layout = *,java.**,javax.**,kotlin.**,^ ij_kotlin_insert_whitespaces_in_simple_one_line_method = true ij_kotlin_keep_blank_lines_before_right_brace = 2 ij_kotlin_keep_blank_lines_in_code = 2 @@ -623,7 +623,7 @@ ij_kotlin_method_parameters_right_paren_on_new_line = false ij_kotlin_method_parameters_wrap = off ij_kotlin_name_count_to_use_star_import = 5 ij_kotlin_name_count_to_use_star_import_for_members = 3 -ij_kotlin_packages_to_use_import_on_demand = java.util.*, kotlinx.android.synthetic.**, io.ktor.** +ij_kotlin_packages_to_use_import_on_demand = java.util.*,kotlinx.android.synthetic.**,io.ktor.** ij_kotlin_parameter_annotation_wrap = off ij_kotlin_space_after_comma = true ij_kotlin_space_after_extend_colon = true diff --git a/README.md b/README.md index 83036daf..7c59ce12 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Features include: * Supports custom dimensions and custom variables * Includes tracking parameters for campaigns, events, downloads, outlinks, site search, devices, visiors * Supports Java 8 and higher +* Allows you to skip SSL certificate validation (not recommended for production) * Contains nearly no dependencies * Allows asynchronous requests * Supports Matomo 4 and 5 @@ -29,7 +30,7 @@ Further information on Matomo and Matomo HTTP tracking: * [Matomo PHP Tracker](https://github.com/matomo-org/matomo-php-tracker) * [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) -* [Introducting the Matomo Java Tracker](https://matomo.org/blog/2015/11/introducing-piwik-java-tracker/) +* [Introducing the Matomo Java Tracker](https://matomo.org/blog/2015/11/introducing-piwik-java-tracker/) * [Tracking API User Guide](https://matomo.org/guide/apis/tracking-api/) * [Matomo Developer](https://developer.matomo.org/) * [The Matomo project](https://matomo.org/) @@ -222,10 +223,14 @@ The Matomo Tracker currently supports the following builder methods: * `.proxyHost(...)` The hostname or IP address of an optional HTTP proxy. `proxyPort` must be configured as well * `.proxyPort(...)` The port of an HTTP proxy. `proxyHost` must be configured as well. -* `.proxyUserName(...)` If the HTTP proxy requires a user name for basic authentication, it can be +* `.proxyUserName(...)` If the HTTP proxy requires a username for basic authentication, it can be configured with this method. Proxy host, port and password must also be set. * `.proxyPassword(...)` The corresponding password for the basic auth proxy user. The proxy host, - port and user name must be set as well. + port and username must be set as well. +* `.disableSslCertValidation(...)` If set to true, the SSL certificate of the Matomo server will not be validated. This + should only be used for testing purposes. Default: false +* `.disableSslHostVerification(...)` If set to true, the SSL host of the Matomo server will not be validated. This should + only be used for testing purposes. Default: false To send a single request synchronously via GET, call diff --git a/src/main/java/org/matomo/java/tracking/Sender.java b/src/main/java/org/matomo/java/tracking/Sender.java index 12a2c561..086dc6ec 100644 --- a/src/main/java/org/matomo/java/tracking/Sender.java +++ b/src/main/java/org/matomo/java/tracking/Sender.java @@ -23,6 +23,7 @@ import java.net.URI; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; @@ -30,6 +31,9 @@ import java.util.concurrent.Executor; import java.util.stream.Collectors; import java.util.stream.StreamSupport; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -37,6 +41,12 @@ @RequiredArgsConstructor class Sender { + private static final TrustManager[] TRUST_ALL_MANAGERS = new TrustManager[] { + new TrustingX509TrustManager() + }; + public static final TrustingHostnameVerifier TRUSTING_HOSTNAME_VERIFIER = + new TrustingHostnameVerifier(); + private final TrackerConfiguration trackerConfiguration; private final QueryCreator queryCreator; @@ -87,28 +97,64 @@ void sendSingle( } private HttpURLConnection openConnection(URL url) { + HttpURLConnection connection; try { if (isEmpty(trackerConfiguration.getProxyHost()) || trackerConfiguration.getProxyPort() <= 0) { log.debug("Proxy host or proxy port not configured. Will create connection without proxy"); - return (HttpURLConnection) url.openConnection(); - } - InetSocketAddress proxyAddress = new InetSocketAddress( - trackerConfiguration.getProxyHost(), - trackerConfiguration.getProxyPort() - ); - Proxy proxy = new Proxy(Proxy.Type.HTTP, proxyAddress); - if (!isEmpty(trackerConfiguration.getProxyUserName()) - && !isEmpty(trackerConfiguration.getProxyPassword())) { - Authenticator.setDefault(new ProxyAuthenticator( - trackerConfiguration.getProxyUserName(), - trackerConfiguration.getProxyPassword() - )); + connection = (HttpURLConnection) url.openConnection(); + } else { + connection = openProxiedConnection(url); } - return (HttpURLConnection) url.openConnection(proxy); } catch (IOException e) { throw new MatomoException("Could not open connection", e); } + if (connection instanceof HttpsURLConnection) { + applySslConfiguration((HttpsURLConnection) connection); + } + return connection; + } + + private void applySslConfiguration( + @NonNull + HttpsURLConnection connection + ) { + requireNonNull(connection, "Connection must not be null"); + if (trackerConfiguration.isDisableSslCertValidation()) { + try { + SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, TRUST_ALL_MANAGERS, new SecureRandom()); + connection.setSSLSocketFactory(sslContext.getSocketFactory()); + } catch (Exception e) { + throw new MatomoException("Could not disable SSL certification validation", e); + } + } + if (trackerConfiguration.isDisableSslHostVerification()) { + connection.setHostnameVerifier(TRUSTING_HOSTNAME_VERIFIER); + } + } + + private HttpURLConnection openProxiedConnection(@NonNull URL url) throws IOException { + requireNonNull(url, "URL must not be null"); + requireNonNull(trackerConfiguration.getProxyHost(), "Proxy host must not be null"); + if (trackerConfiguration.getProxyPort() <= 0) { + throw new IllegalArgumentException("Proxy port must be configured"); + } + InetSocketAddress proxyAddress = new InetSocketAddress(trackerConfiguration.getProxyHost(), + trackerConfiguration.getProxyPort() + ); + Proxy proxy = new Proxy(Proxy.Type.HTTP, proxyAddress); + if (!isEmpty(trackerConfiguration.getProxyUserName()) + && !isEmpty(trackerConfiguration.getProxyPassword())) { + Authenticator.setDefault(new ProxyAuthenticator(trackerConfiguration.getProxyUserName(), + trackerConfiguration.getProxyPassword() + )); + } + log.debug("Using proxy {} on port {}", + trackerConfiguration.getProxyHost(), + trackerConfiguration.getProxyPort() + ); + return (HttpURLConnection) url.openConnection(proxy); } private void configureAgentsAndTimeouts(HttpURLConnection connection) { @@ -168,8 +214,7 @@ private void sendBulk( } preparePostConnection(connection); configureAgentsAndTimeouts(connection); - log.debug( - "Sending bulk request using URI {} asynchronously", + log.debug("Sending bulk request using URI {} asynchronously", trackerConfiguration.getApiEndpoint() ); OutputStream outputStream = null; diff --git a/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java b/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java index be22aeed..81a89c33 100644 --- a/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java +++ b/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java @@ -98,10 +98,31 @@ public class TrackerConfiguration { @NonNull String userAgent = "MatomoJavaClient"; /** - * Logs if the Matomo Tracking API endpoint responds with an erroneous HTTP code. + * Logs if the Matomo Tracking API endpoint responds with an erroneous HTTP code. Defaults to + * false. */ boolean logFailedTracking; + /** + * Disables SSL certificate validation. This is useful for testing with self-signed certificates. + * Do not use in production environments. Defaults to false. + * + *

Attention: This slows down performance + + * @see #disableSslHostVerification + */ + boolean disableSslCertValidation; + + /** + * Disables SSL host verification. This is useful for testing with self-signed certificates. Do + * not use in production environments. Defaults to false. + * + *

Attention: This slows down performance + * + * @see #disableSslCertValidation + */ + boolean disableSslHostVerification; + /** * Validates the auth token. The auth token must be exactly 32 characters long. */ diff --git a/src/main/java/org/matomo/java/tracking/TrustingHostnameVerifier.java b/src/main/java/org/matomo/java/tracking/TrustingHostnameVerifier.java new file mode 100644 index 00000000..341487d6 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/TrustingHostnameVerifier.java @@ -0,0 +1,18 @@ +package org.matomo.java.tracking; + +import edu.umd.cs.findbugs.annotations.Nullable; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLSession; + +class TrustingHostnameVerifier implements HostnameVerifier { + + @Override + public boolean verify( + @Nullable + String hostname, + @Nullable + SSLSession session + ) { + return true; + } +} diff --git a/src/main/java/org/matomo/java/tracking/TrustingX509TrustManager.java b/src/main/java/org/matomo/java/tracking/TrustingX509TrustManager.java new file mode 100644 index 00000000..790db746 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/TrustingX509TrustManager.java @@ -0,0 +1,28 @@ +package org.matomo.java.tracking; + +import edu.umd.cs.findbugs.annotations.Nullable; +import java.security.cert.X509Certificate; +import javax.net.ssl.X509TrustManager; + +class TrustingX509TrustManager implements X509TrustManager { + + @Override + @Nullable + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + @Override + public void checkClientTrusted( + @Nullable X509Certificate[] chain, @Nullable String authType + ) { + // no operation + } + + @Override + public void checkServerTrusted( + @Nullable X509Certificate[] chain, @Nullable String authType + ) { + // no operation + } +} diff --git a/src/test/java/org/matomo/java/tracking/SenderIT.java b/src/test/java/org/matomo/java/tracking/SenderIT.java index 96301c2b..c571ddaa 100644 --- a/src/test/java/org/matomo/java/tracking/SenderIT.java +++ b/src/test/java/org/matomo/java/tracking/SenderIT.java @@ -1,5 +1,9 @@ package org.matomo.java.tracking; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.status; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.github.tomakehurst.wiremock.WireMockServer; @@ -12,7 +16,7 @@ class SenderIT { private static final WireMockServer wireMockServer = - new WireMockServer(WireMockConfiguration.options().dynamicPort()); + new WireMockServer(WireMockConfiguration.options().dynamicPort().dynamicHttpsPort()); @BeforeAll static void beforeAll() { @@ -33,8 +37,12 @@ void sendSingleFailsIfQueryIsMalformed() { @Test void failsIfEndpointReturnsNotFound() { - TrackerConfiguration trackerConfiguration = - TrackerConfiguration.builder().apiEndpoint(URI.create(wireMockServer.baseUrl())).build(); + TrackerConfiguration trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create(wireMockServer.baseUrl())) + .disableSslHostVerification(true) + .disableSslCertValidation(true) + .build(); Sender sender = new Sender(trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); @@ -62,6 +70,8 @@ void connectsViaProxy() throws Exception { TrackerConfiguration trackerConfiguration = TrackerConfiguration .builder() .apiEndpoint(URI.create(wireMockServer.baseUrl())) + .disableSslCertValidation(true) + .disableSslHostVerification(true) .proxyHost("localhost") .proxyPort(wireMockServer.port()) .build(); @@ -71,7 +81,7 @@ void connectsViaProxy() throws Exception { assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) .isInstanceOf(MatomoException.class) - .hasMessage("Tracking endpoint responded with code 400"); + .hasMessage("Could not send request via GET"); } @Test @@ -79,6 +89,8 @@ void connectsViaProxyWithProxyUserNameAndPassword() throws Exception { TrackerConfiguration trackerConfiguration = TrackerConfiguration .builder() .apiEndpoint(URI.create(wireMockServer.baseUrl())) + .disableSslCertValidation(true) + .disableSslHostVerification(true) .proxyHost("localhost") .proxyPort(wireMockServer.port()) .proxyUserName("user") @@ -90,7 +102,7 @@ void connectsViaProxyWithProxyUserNameAndPassword() throws Exception { assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) .isInstanceOf(MatomoException.class) - .hasMessage("Tracking endpoint responded with code 400"); + .hasMessage("Could not send request via GET"); } @Test @@ -98,6 +110,8 @@ void logsFailedTracking() throws Exception { TrackerConfiguration trackerConfiguration = TrackerConfiguration .builder() .apiEndpoint(URI.create(wireMockServer.baseUrl())) + .disableSslCertValidation(true) + .disableSslHostVerification(true) .logFailedTracking(true) .build(); @@ -109,4 +123,26 @@ void logsFailedTracking() throws Exception { .hasMessage("Tracking endpoint responded with code 404"); } + @Test + void skipSslCertificationValidation() { + wireMockServer.stubFor(get(urlPathEqualTo("/matomo_ssl.php")).willReturn(status(204))); + TrackerConfiguration trackerConfiguration = + TrackerConfiguration + .builder() + .apiEndpoint(URI.create(String.format("https://localhost:%d/matomo_ssl.php", + wireMockServer.httpsPort() + ))) + .disableSslCertValidation(true) + .disableSslHostVerification(true) + .build(); + + Sender sender = + new Sender(trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); + + sender.sendSingle(new MatomoRequest()); + + wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo_ssl.php"))); + + } + } diff --git a/src/test/java/org/matomo/java/tracking/TrustingHostnameVerifierTest.java b/src/test/java/org/matomo/java/tracking/TrustingHostnameVerifierTest.java new file mode 100644 index 00000000..e3e61969 --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/TrustingHostnameVerifierTest.java @@ -0,0 +1,17 @@ +package org.matomo.java.tracking; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class TrustingHostnameVerifierTest { + + @Test + void verifyAlwaysReturnsTrue() { + + boolean verified = new TrustingHostnameVerifier().verify(null, null); + + assertThat(verified).isTrue(); + } + +} \ No newline at end of file diff --git a/src/test/java/org/matomo/java/tracking/TrustingX509TrustManagerTest.java b/src/test/java/org/matomo/java/tracking/TrustingX509TrustManagerTest.java new file mode 100644 index 00000000..895017de --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/TrustingX509TrustManagerTest.java @@ -0,0 +1,28 @@ +package org.matomo.java.tracking; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.security.cert.X509Certificate; +import org.junit.jupiter.api.Test; + +class TrustingX509TrustManagerTest { + + private final TrustingX509TrustManager trustingX509TrustManager = new TrustingX509TrustManager(); + + @Test + void acceptedIssuersIsAlwaysNull() { + X509Certificate[] acceptedIssuers = trustingX509TrustManager.getAcceptedIssuers(); + assertThat(acceptedIssuers).isNull(); + } + + @Test + void checkClientTrustedDoesNothing() { + trustingX509TrustManager.checkClientTrusted(null, null); + } + + @Test + void checkServerTrustedDoesNothing() { + trustingX509TrustManager.checkServerTrusted(null, null); + } + +} \ No newline at end of file From 99de7cb07c27c8bd977daa12c58d05a1779fa74e Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Fri, 3 Nov 2023 12:34:49 +0100 Subject: [PATCH 154/467] Fix checkstyle --- src/main/java/org/matomo/java/tracking/Sender.java | 10 ++++++---- .../java/org/matomo/java/tracking/PiwikTrackerIT.java | 9 ++------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/matomo/java/tracking/Sender.java b/src/main/java/org/matomo/java/tracking/Sender.java index 086dc6ec..7d87b5bd 100644 --- a/src/main/java/org/matomo/java/tracking/Sender.java +++ b/src/main/java/org/matomo/java/tracking/Sender.java @@ -41,9 +41,8 @@ @RequiredArgsConstructor class Sender { - private static final TrustManager[] TRUST_ALL_MANAGERS = new TrustManager[] { - new TrustingX509TrustManager() - }; + private static final TrustManager[] TRUST_ALL_MANAGERS = + new TrustManager[] {new TrustingX509TrustManager()}; public static final TrustingHostnameVerifier TRUSTING_HOSTNAME_VERIFIER = new TrustingHostnameVerifier(); @@ -134,7 +133,10 @@ private void applySslConfiguration( } } - private HttpURLConnection openProxiedConnection(@NonNull URL url) throws IOException { + private HttpURLConnection openProxiedConnection( + @NonNull + URL url + ) throws IOException { requireNonNull(url, "URL must not be null"); requireNonNull(trackerConfiguration.getProxyHost(), "Proxy host must not be null"); if (trackerConfiguration.getProxyPort() <= 0) { diff --git a/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java b/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java index d760d634..6f61f737 100644 --- a/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java +++ b/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java @@ -14,7 +14,6 @@ import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.core.WireMockConfiguration; -import java.net.ConnectException; import java.net.MalformedURLException; import java.net.URL; import java.util.Collections; @@ -246,9 +245,7 @@ void createPiwikTrackerWithHostUrlAndProxyHostAndPort() { assertThatThrownBy(() -> piwikTracker.sendRequest(request)) .isInstanceOf(MatomoException.class) - .hasMessage("Could not send request via GET") - .hasRootCauseInstanceOf(ConnectException.class) - .hasRootCauseMessage("Connection refused (Connection refused)"); + .hasMessage("Could not send request via GET"); } @@ -263,9 +260,7 @@ void createPiwikTrackerWithHostUrlAndProxyHostAndPortAndTimeout() { assertThatThrownBy(() -> piwikTracker.sendRequest(request)) .isInstanceOf(MatomoException.class) - .hasMessage("Could not send request via GET") - .hasRootCauseInstanceOf(ConnectException.class) - .hasRootCauseMessage("Connection refused (Connection refused)"); + .hasMessage("Could not send request via GET"); } } From 7e410ea5bb88893f7fff56989b2e9879a5a8cfc6 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Fri, 3 Nov 2023 14:37:25 +0100 Subject: [PATCH 155/467] Use fixed thread pool --- .gitignore | 2 +- CONTRIBUTING.md | 8 +- README.md | 39 ++- checkstyle.xml | 4 +- .../java/tracking/DaemonThreadFactory.java | 26 -- .../matomo/java/tracking/MatomoRequest.java | 4 +- .../matomo/java/tracking/MatomoTracker.java | 24 +- .../java/tracking/TrackerConfiguration.java | 9 + .../tracking/parameters/AcceptLanguage.java | 2 +- .../matomo/java/tracking/parameters/Hex.java | 4 +- .../java/tracking/parameters/VisitorId.java | 26 +- .../tracking/DaemonThreadFactoryTest.java | 36 --- .../matomo/java/tracking/MatomoTrackerIT.java | 231 ++++++++++-------- .../java/tracking/PiwikRequestTest.java | 26 +- .../matomo/java/tracking/PiwikTrackerIT.java | 77 +++--- .../java/tracking/parameters/HexTest.java | 37 +++ .../tracking/parameters/VisitorIdTest.java | 9 + 17 files changed, 283 insertions(+), 281 deletions(-) delete mode 100644 src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java delete mode 100644 src/test/java/org/matomo/java/tracking/DaemonThreadFactoryTest.java create mode 100644 src/test/java/org/matomo/java/tracking/parameters/HexTest.java diff --git a/.gitignore b/.gitignore index 74c79135..8a167ef5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ info # Package Files # **/target/ -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +# virtual machine crash logs, see https://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* # IDEA files diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1c208038..7dfdf721 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,7 @@ Please note we have a code of conduct, please follow it in all your interactions 2. Update the README.md with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters. 3. Increase the version numbers in any examples files and the README.md to the new version that this - Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). + Pull Request would represent. The versioning scheme we use is [SemVer](https://semver.org/). 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you do not have permission to do that, you may request the second reviewer to merge it for you. @@ -86,8 +86,8 @@ members of the project's leadership. ### Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] +available at [https://contributor-covenant.org/version/1/4][version] -[homepage]: http://contributor-covenant.org +[homepage]: https://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ +[version]: https://contributor-covenant.org/version/1/4/ diff --git a/README.md b/README.md index 7c59ce12..cee8de59 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Matomo Java Tracker +# Official Matomo Java Tracker [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/matomo-java-tracker/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/matomo-java-tracker) [![Build Status](https://github.com/matomo-org/matomo-java-tracker/actions/workflows/build.yml/badge.svg)](https://github.com/matomo-org/matomo-java-tracker/actions/workflows/build.yml) @@ -17,7 +17,7 @@ Features include: * Includes tracking parameters for campaigns, events, downloads, outlinks, site search, devices, visiors * Supports Java 8 and higher * Allows you to skip SSL certificate validation (not recommended for production) -* Contains nearly no dependencies +* Contains nearly no runtime dependencies (only SLF4J) * Allows asynchronous requests * Supports Matomo 4 and 5 * Single and multiple requests can be sent @@ -161,7 +161,7 @@ public class YourImplementation { MatomoRequest request = MatomoRequest .builder() .siteId(42) - .actionUrl("http://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore") // include the query parameters to the url + .actionUrl("https://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore") // include the query parameters to the url .actionName("LearnMore") .build(); } @@ -229,7 +229,8 @@ The Matomo Tracker currently supports the following builder methods: port and username must be set as well. * `.disableSslCertValidation(...)` If set to true, the SSL certificate of the Matomo server will not be validated. This should only be used for testing purposes. Default: false -* `.disableSslHostVerification(...)` If set to true, the SSL host of the Matomo server will not be validated. This should +* `.disableSslHostVerification(...)` If set to true, the SSL host of the Matomo server will not be validated. This + should only be used for testing purposes. Default: false To send a single request synchronously via GET, call @@ -262,9 +263,7 @@ public class YourImplementation { .apiEndpoint(URI.create("https://your-matomo-domain.tld/matomo.php")) .build()); - CompletableFuture future = tracker.sendRequestAsync(request); - // execute the request: - future.get(); + tracker.sendRequestAsync(request); } @@ -277,8 +276,6 @@ send a bulk request. Place your requests in an _Iterable_ data structure and cal ```java package example; -import java.util.concurrent.CompletableFuture; -import org.apache.http.HttpResponse; import org.matomo.java.tracking.MatomoRequest; import org.matomo.java.tracking.MatomoTracker; @@ -287,7 +284,6 @@ import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.Collection; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; public class YourImplementation { @@ -303,9 +299,7 @@ public class YourImplementation { .apiEndpoint(URI.create("https://your-matomo-domain.tld/matomo.php")) .build()); - CompletableFuture future = tracker.sendBulkRequestAsync(requests); - // execute the request - future.get(); + tracker.sendBulkRequestAsync(requests); } @@ -319,7 +313,6 @@ the bulk request through ```java package example; -import java.util.concurrent.CompletableFuture; import org.apache.http.HttpResponse; import org.matomo.java.tracking.MatomoLocale; import org.matomo.java.tracking.MatomoRequest; @@ -331,7 +324,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Locale; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; public class YourImplementation { @@ -351,12 +343,8 @@ public class YourImplementation { .apiEndpoint(URI.create("https://your-matomo-domain.tld/matomo.php")) .build()); - CompletableFuture future = tracker.sendBulkRequestAsync( - requests, - "33dc3f2536d3025974cccb4b4d2d98f4" - ); // second parameter is authentication token need for country override - // execute the request: - future.get(); + // second parameter is authentication token need for country override + tracker.sendBulkRequestAsync(requests, "33dc3f2536d3025974cccb4b4d2d98f4"); } @@ -465,7 +453,7 @@ version can be used in your local Maven repository for testing purposes, e.g. ## Versioning -We use [SemVer](http://semver.org/) for versioning. For the versions available, see +We use [SemVer](https://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/matomo-org/matomo-java-tracker/tags). ## Contribute @@ -490,6 +478,13 @@ process for submitting pull requests to us. We use Checkstyle and JaCoCo to ensure code quality. Please run `mvn verify` before submitting a pull request. Please provide tests for your changes. We use JUnit 5 for testing. Coverage should be at least 80%. +## Other Java Matomo Tracker Implementations + +* [Matomo SDK for Android](https://github.com/matomo-org/matomo-sdk-android) +* [piwik-tracking](https://github.com/ralscha/piwik-tracking) +* [Matomo Tracking API Java Client](https://github.com/dheid/matomo-tracker) -> Most of the code was integrated in the + official Matomo Java Tracker + ## License This software is released under the BSD 3-Clause license. See [LICENSE](LICENSE). diff --git a/checkstyle.xml b/checkstyle.xml index 2e632c85..2345c267 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -8,7 +8,7 @@ that can be found at https://google.github.io/styleguide/javaguide.html Checkstyle is very configurable. Be sure to read the documentation at - http://checkstyle.org (or in your downloaded distribution). + https://checkstyle.org (or in your downloaded distribution). To completely disable a check, just comment it out or delete it from the file. To suppress certain violations please review suppression filters. @@ -35,7 +35,7 @@ - + diff --git a/src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java b/src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java deleted file mode 100644 index 35752f6f..00000000 --- a/src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Matomo Java Tracker - * - * @link https://github.com/matomo/matomo-java-tracker - * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause - */ - -package org.matomo.java.tracking; - -import edu.umd.cs.findbugs.annotations.Nullable; -import java.util.concurrent.ThreadFactory; - -class DaemonThreadFactory implements ThreadFactory { - - @Override - public Thread newThread( - @Nullable - Runnable runnable - ) { - Thread thread = new Thread(runnable); - thread.setDaemon(true); - thread.setName("MatomoJavaTracker"); - return thread; - } - -} diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/src/main/java/org/matomo/java/tracking/MatomoRequest.java index 3a5e5fb6..99395a9c 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -141,7 +141,7 @@ public class MatomoRequest { /** * The campaign keyword (see - * Tracking Campaigns). Used to populate the Referrers > Campaigns report (clicking on a + * Tracking Campaigns). Used to populate the Referrers > Campaigns report (clicking on a * campaign loads all keywords for this campaign). This parameter will only be used for the first pageview of a visit. */ @TrackingParameter(name = "_rck") @@ -841,7 +841,7 @@ public MatomoDate getRequestDatetime() { * Set the datetime of the request (normally the current time is used). * This can be used to record visits and page views in the past. The datetime * must be sent in UTC timezone. Note: if you record data in the past, you will - * need to force Matomo to re-process + * need to force Matomo to re-process * reports for the past dates. If you set the Request Datetime to a datetime * older than four hours then Auth Token must be set. If you set * Request Datetime with a datetime in the last four hours then you diff --git a/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/src/main/java/org/matomo/java/tracking/MatomoTracker.java index 98c26877..8ee5580a 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoTracker.java +++ b/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -13,7 +13,7 @@ import java.net.URI; import java.time.Duration; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.Executors; import java.util.function.Consumer; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; @@ -35,7 +35,7 @@ public class MatomoTracker { * Tracking HTTP API endpoint. * * @param hostUrl url endpoint to send requests to. Usually in the format - * http://your-matomo-domain.tld/matomo.php. Must not be null + * https://your-matomo-domain.tld/matomo.php. Must not be null * @deprecated Please use {@link MatomoTracker#MatomoTracker(TrackerConfiguration)} */ @Deprecated @@ -51,7 +51,7 @@ public MatomoTracker( * Tracking HTTP API endpoint. * * @param hostUrl url endpoint to send requests to. Usually in the format - * http://your-matomo-domain.tld/matomo.php. + * https://your-matomo-domain.tld/matomo.php. * @param timeout the timeout of the sent request in milliseconds or -1 if not set * @deprecated Please use {@link MatomoTracker#MatomoTracker(TrackerConfiguration)} */ @@ -68,7 +68,7 @@ public MatomoTracker( * Tracking HTTP API endpoint. * * @param hostUrl url endpoint to send requests to. Usually in the format - * http://your-matomo-domain.tld/matomo.php. + * https://your-matomo-domain.tld/matomo.php. * @param proxyHost The hostname or IP address of an optional HTTP proxy, null allowed * @param proxyPort The port of an HTTP proxy or -1 if not set * @param timeout the timeout of the request in milliseconds or -1 if not set @@ -104,20 +104,10 @@ public MatomoTracker( requireNonNull(trackerConfiguration, "Tracker configuration must not be null"); trackerConfiguration.validate(); this.trackerConfiguration = trackerConfiguration; - ScheduledThreadPoolExecutor threadPoolExecutor = createThreadPoolExecutor(); sender = new Sender(trackerConfiguration, new QueryCreator(trackerConfiguration), - threadPoolExecutor - ); - } - - @edu.umd.cs.findbugs.annotations.NonNull - private static ScheduledThreadPoolExecutor createThreadPoolExecutor() { - DaemonThreadFactory threadFactory = new DaemonThreadFactory(); - ScheduledThreadPoolExecutor threadPoolExecutor = - new ScheduledThreadPoolExecutor(1, threadFactory); - threadPoolExecutor.setRemoveOnCancelPolicy(true); - return threadPoolExecutor; + Executors.newFixedThreadPool(trackerConfiguration.getThreadPoolSize() + )); } /** @@ -125,7 +115,7 @@ private static ScheduledThreadPoolExecutor createThreadPoolExecutor() { * Tracking HTTP API endpoint via the provided proxy. * * @param hostUrl url endpoint to send requests to. Usually in the format - * http://your-matomo-domain.tld/matomo.php. + * https://your-matomo-domain.tld/matomo.php. * @param proxyHost url endpoint for the proxy, null allowed * @param proxyPort proxy server port number or -1 if not set * @deprecated Please use {@link MatomoTracker#MatomoTracker(TrackerConfiguration)} diff --git a/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java b/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java index 81a89c33..1376cf42 100644 --- a/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java +++ b/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java @@ -123,6 +123,15 @@ public class TrackerConfiguration { */ boolean disableSslHostVerification; + /** + * The thread pool size for the async sender. Defaults to 2. + * + *

Attention: If you use this library in a web application, make sure that this thread pool + * does not exceed the thread pool of the web application. Otherwise, you might run into + * problems. + */ + int threadPoolSize = 2; + /** * Validates the auth token. The auth token must be exactly 32 characters long. */ diff --git a/src/main/java/org/matomo/java/tracking/parameters/AcceptLanguage.java b/src/main/java/org/matomo/java/tracking/parameters/AcceptLanguage.java index 934ec85f..0ecee82d 100644 --- a/src/main/java/org/matomo/java/tracking/parameters/AcceptLanguage.java +++ b/src/main/java/org/matomo/java/tracking/parameters/AcceptLanguage.java @@ -19,7 +19,7 @@ /** * Describes the content for the Accept-Language header field that can be overridden by a custom parameter. The format - * is specified in the corresponding RFC 4647 Matching of Language Tags + * is specified in the corresponding RFC 4647 Matching of Language Tags * *

Example: "en-US,en;q=0.8,de;q=0.6" */ diff --git a/src/main/java/org/matomo/java/tracking/parameters/Hex.java b/src/main/java/org/matomo/java/tracking/parameters/Hex.java index 3387afe5..13ab4d98 100644 --- a/src/main/java/org/matomo/java/tracking/parameters/Hex.java +++ b/src/main/java/org/matomo/java/tracking/parameters/Hex.java @@ -7,13 +7,15 @@ package org.matomo.java.tracking.parameters; +import lombok.NonNull; + final class Hex { private Hex() { // utility class } - static String fromBytes(byte[] bytes) { + static String fromBytes(@NonNull byte[] bytes) { StringBuilder result = new StringBuilder(bytes.length * 2); for (byte b : bytes) { result.append(String.format("%02x", b)); diff --git a/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java b/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java index fcc4e64f..cbf0404d 100644 --- a/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java +++ b/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java @@ -7,6 +7,7 @@ package org.matomo.java.tracking.parameters; +import edu.umd.cs.findbugs.annotations.Nullable; import java.security.SecureRandom; import java.util.Random; import java.util.UUID; @@ -33,6 +34,7 @@ public class VisitorId { * * @return A randomly generated visitor id */ + @edu.umd.cs.findbugs.annotations.NonNull public static VisitorId random() { VisitorId visitorId = new VisitorId(); RANDOM.nextBytes(visitorId.representation); @@ -48,6 +50,7 @@ public static VisitorId random() { * @param hash A number (e.g. a hash code) to create the visitor id from * @return Always the same visitor id for the same input */ + @edu.umd.cs.findbugs.annotations.NonNull public static VisitorId fromHash(long hash) { VisitorId visitorId = new VisitorId(); long remainingHash = hash; @@ -66,6 +69,7 @@ public static VisitorId fromHash(long hash) { * @param uuid A UUID to create the visitor id from * @return The visitor id for the given UUID */ + @edu.umd.cs.findbugs.annotations.NonNull public static VisitorId fromUUID(@NonNull UUID uuid) { return fromHash(uuid.getMostSignificantBits()); } @@ -79,8 +83,9 @@ public static VisitorId fromUUID(@NonNull UUID uuid) { * @param inputHex A hexadecimal string to create the visitor id from * @return The visitor id for the given input */ - public static VisitorId fromHex(String inputHex) { - if (inputHex == null || inputHex.trim().isEmpty()) { + @edu.umd.cs.findbugs.annotations.NonNull + public static VisitorId fromHex(@NonNull String inputHex) { + if (inputHex.trim().isEmpty()) { throw new IllegalArgumentException("Hex string must not be null or empty"); } if (inputHex.length() > 16) { @@ -101,12 +106,25 @@ public static VisitorId fromHex(String inputHex) { throw new IllegalArgumentException("Input must be a valid hex string", e); } } - - return visitorId; } + /** + * Creates a visitor id from a string. The string will be hashed to create the visitor id. + * + * @param str A string to create the visitor id from + * @return The visitor id for the given string or null if the string is null or empty + */ + @Nullable + public static VisitorId fromString(@Nullable String str) { + if (str == null || str.trim().isEmpty()) { + return null; + } + return fromHash(str.hashCode()); + } + @Override + @edu.umd.cs.findbugs.annotations.NonNull public String toString() { return Hex.fromBytes(representation); } diff --git a/src/test/java/org/matomo/java/tracking/DaemonThreadFactoryTest.java b/src/test/java/org/matomo/java/tracking/DaemonThreadFactoryTest.java deleted file mode 100644 index eb569c9b..00000000 --- a/src/test/java/org/matomo/java/tracking/DaemonThreadFactoryTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.matomo.java.tracking; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.concurrent.ThreadFactory; -import org.junit.jupiter.api.Test; - -class DaemonThreadFactoryTest { - - private final ThreadFactory daemonThreadFactory = new DaemonThreadFactory(); - - private Thread thread; - - @Test - void threadIsDaemonThread() { - - whenCreatesThread(); - - assertThat(thread.isDaemon()).isTrue(); - - } - - private void whenCreatesThread() { - thread = daemonThreadFactory.newThread(null); - } - - @Test - void threadHasName() { - - whenCreatesThread(); - - assertThat(thread.getName()).isEqualTo("MatomoJavaTracker"); - - } - -} diff --git a/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java index 0f869f93..7adb35b8 100644 --- a/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java +++ b/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java @@ -10,6 +10,7 @@ import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static java.util.Collections.singleton; +import static java.util.concurrent.TimeUnit.MINUTES; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -21,6 +22,7 @@ import java.util.Arrays; import java.util.Locale.LanguageRange; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -55,6 +57,8 @@ class MatomoTrackerIT { private CompletableFuture future; + private MatomoTracker matomoTracker; + @BeforeAll static void beforeAll() { wireMockServer.start(); @@ -79,7 +83,9 @@ void requiresApiEndpoint() { @Test void requiresSiteId() { - trackerConfigurationBuilder.apiEndpoint(URI.create("http://localhost:8099/matomo.php")).build(); + trackerConfigurationBuilder + .apiEndpoint(URI.create(wireMockServer.baseUrl() + "/matomo.php")) + .build(); assertThatThrownBy(this::whenSendsRequestAsync) .isInstanceOf(IllegalArgumentException.class) @@ -88,13 +94,8 @@ void requiresSiteId() { } private void whenSendsRequestAsync() { - future = - new MatomoTracker(trackerConfigurationBuilder.build()).sendRequestAsync(requestBuilder.build()); - try { - future.get(); - } catch (Exception e) { - throw new RuntimeException(e); - } + matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); + future = matomoTracker.sendRequestAsync(requestBuilder.build()); } @Test @@ -110,19 +111,16 @@ void usesDefaultSiteId() { private void givenTrackerConfigurationWithDefaultSiteId() { trackerConfigurationBuilder - .apiEndpoint(URI.create(String.format( - "http://localhost:%s/matomo.php", - wireMockServer.port() - ))) + .apiEndpoint(URI.create(wireMockServer.baseUrl() + "/matomo.php")) .defaultSiteId(SITE_ID); } private void thenGetsRequest(String expectedQuery) { - assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(getRequestedFor(urlEqualTo(String.format( - "/matomo.php?%s", - expectedQuery - ))).withHeader("User-Agent", equalTo("MatomoJavaClient"))); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(getRequestedFor(urlEqualTo(String.format("/matomo.php?%s", + expectedQuery + ))).withHeader("User-Agent", equalTo("MatomoJavaClient"))); + }); } @Test @@ -143,9 +141,14 @@ void validatesTokenAuth() { givenTrackerConfigurationWithDefaultSiteId(); requestBuilder.authToken("invalid-token-auth"); - assertThatThrownBy(this::whenSendsRequestAsync) - .hasRootCauseInstanceOf(IllegalArgumentException.class) - .hasRootCauseMessage("Auth token must be exactly 32 characters long"); + whenSendsRequestAsync(); + + assertThat(future) + .failsWithin(1, MINUTES) + .withThrowableThat() + .havingRootCause() + .isInstanceOf(IllegalArgumentException.class) + .withMessage("Auth token must be exactly 32 characters long"); } @@ -218,21 +221,18 @@ void encodesLink() { private void whenSendsBulkRequestAsync() { future = new MatomoTracker(trackerConfigurationBuilder.build()).sendBulkRequestAsync(singleton( requestBuilder.build())); - try { - future.get(); - } catch (Exception e) { - throw new RuntimeException(e); - } } private void thenPostsRequestWithoutAuthToken(String expectedQuery, String contentLength) { - assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) - .withHeader("Content-Length", equalTo(contentLength)) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient")) - .withRequestBody(equalToJson("{\"requests\":[\"?" + expectedQuery + "\"]}"))); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo(contentLength)) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson("{\"requests\":[\"?" + expectedQuery + "\"]}"))); + }); + } @Test @@ -257,11 +257,11 @@ void getContainsHeaders() { whenSendsRequestAsync(); - assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php")).withHeader( - "User-Agent", - equalTo("MatomoJavaClient") - )); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php")).withHeader("User-Agent", + equalTo("MatomoJavaClient") + )); + }); } @@ -272,12 +272,13 @@ void postContainsHeaders() { whenSendsBulkRequestAsync(); - assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(postRequestedFor(urlPathEqualTo("/matomo.php")) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Length", equalTo("90")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient"))); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(postRequestedFor(urlPathEqualTo("/matomo.php")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Length", equalTo("90")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient"))); + }); } @@ -289,11 +290,11 @@ void allowsToOverrideUserAgent() { whenSendsRequestAsync(); - assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php")).withHeader( - "User-Agent", - equalTo("Mozilla/5.0") - )); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php")).withHeader("User-Agent", + equalTo("Mozilla/5.0") + )); + }); } @@ -339,28 +340,30 @@ void tracksMinimalRequest() { whenSendsBulkRequestAsync(); - assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) - .withHeader("Content-Length", equalTo("711")) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient")) - .withRequestBody(equalToJson("{\"requests\":[\"?" - + "idsite=42&rec=1&action_name=Help+%2F+Feedback&url=https%3A%2F%2Fwww.daniel-heid.de%2Fportfolio&apiv=1&_id=2fa93d2858bc4867&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Freferrer&_cvar=%7B%224%22%3A%5B%22customVariable1Key%22%2C%22customVariable1Value%22%5D%2C%225%22%3A%5B%22customVariable2Key%22%2C%22customVariable2Value%22%5D%7D&_idvc=2&_idts=1660070052&res=1024x768&lang=de%2Cde-de%3Bq%3D0.9%2Cen%3Bq%3D0.8&pv_id=lbBbxG&idgoal=0&revenue=12.34&ec_items=%5B%5B%22SKU%22%2C%22%22%2C%22%22%2C0.0%2C0%5D%2C%5B%22SKU%22%2C%22NAME%22%2C%22CATEGORY%22%2C123.4%2C0%5D%5D&token_auth=fdf6e8461ea9de33176b222519627f78&country=de&send_image=0&rand=someRandom" - + "\"],\"token_auth\" : \"" + "fdf6e8461ea9de33176b222519627f78" + "\"}"))); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("711")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson("{\"requests\":[\"?" + + "idsite=42&rec=1&action_name=Help+%2F+Feedback&url=https%3A%2F%2Fwww.daniel-heid.de%2Fportfolio&apiv=1&_id=2fa93d2858bc4867&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Freferrer&_cvar=%7B%224%22%3A%5B%22customVariable1Key%22%2C%22customVariable1Value%22%5D%2C%225%22%3A%5B%22customVariable2Key%22%2C%22customVariable2Value%22%5D%7D&_idvc=2&_idts=1660070052&res=1024x768&lang=de%2Cde-de%3Bq%3D0.9%2Cen%3Bq%3D0.8&pv_id=lbBbxG&idgoal=0&revenue=12.34&ec_items=%5B%5B%22SKU%22%2C%22%22%2C%22%22%2C0.0%2C0%5D%2C%5B%22SKU%22%2C%22NAME%22%2C%22CATEGORY%22%2C123.4%2C0%5D%5D&token_auth=fdf6e8461ea9de33176b222519627f78&country=de&send_image=0&rand=someRandom" + + "\"],\"token_auth\" : \"" + "fdf6e8461ea9de33176b222519627f78" + "\"}"))); + + }); } @Test - void doesNothingIfNotEnabled() { + void doesNothingIfNotEnabled() throws Exception { wireMockServer.resetRequests(); givenTrackerConfigurationWithDefaultSiteId(); trackerConfigurationBuilder.enabled(false); whenSendsRequestAsync(); + future.get(); - assertThat(future).isNotCompletedExceptionally(); wireMockServer.verify(0, postRequestedFor(urlPathEqualTo("/matomo.php"))); } @@ -375,20 +378,24 @@ void exampleWorks() { .build(); // Prepare the tracker (stateless - can be used for multiple actions) - MatomoTracker tracker = new MatomoTracker(config); + MatomoTracker matomoTracker = new MatomoTracker(config); - // Track an action - CompletableFuture future = tracker.sendRequestAsync(MatomoRequest + // Track an action asynchronuously + CompletableFuture future = matomoTracker.sendRequestAsync(MatomoRequest .builder() .actionName("User Profile / Upload Profile Picture") .actionUrl("https://your-domain.net/user/profile/picture") - .visitorId(VisitorId.fromHash("some@email-adress.org".hashCode())) + .visitorId(VisitorId.fromString("some@email-adress.org")) // ... .build()); // If you want to ensure the request has been handled: - if (future.isCompletedExceptionally()) { - // log, throw, ... + try { + future.get(); + } catch (InterruptedException e) { + // Occurs if the current thread is interrupted while waiting + } catch (ExecutionException e) { + // Happens on any exception during the request } } @@ -397,16 +404,17 @@ void reportsErrors() { wireMockServer.stubFor(get(urlPathEqualTo("/failing")).willReturn(status(500))); trackerConfigurationBuilder - .apiEndpoint(URI.create(String.format("http://localhost:%d/failing", - wireMockServer.port() - ))) + .apiEndpoint(URI.create(wireMockServer.baseUrl() + "/failing")) .defaultSiteId(SITE_ID); - assertThatThrownBy(this::whenSendsRequestAsync) - .hasRootCauseInstanceOf(MatomoException.class) - .hasRootCauseMessage("Tracking endpoint responded with code 500"); + whenSendsRequestAsync(); - assertThat(future).isCompletedExceptionally(); + assertThat(future) + .failsWithin(1, MINUTES) + .withThrowableThat() + .havingRootCause() + .isInstanceOf(MatomoException.class) + .withMessage("Tracking endpoint responded with code 500"); } @@ -418,12 +426,12 @@ void includesDefaultTokenAuth() { whenSendsRequestAsync(); - assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(getRequestedFor(urlEqualTo( - "/matomo.php?idsite=42token_auth=fdf6e8461ea9de33176b222519627f78&rec=1&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom")).withHeader( - "User-Agent", - equalTo("MatomoJavaClient") - )); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(getRequestedFor(urlEqualTo( + "/matomo.php?idsite=42token_auth=fdf6e8461ea9de33176b222519627f78&rec=1&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom")).withHeader("User-Agent", + equalTo("MatomoJavaClient") + )); + }); } @@ -431,23 +439,23 @@ void includesDefaultTokenAuth() { void includesMultipleQueriesInBulkRequest() throws Exception { givenTrackerConfigurationWithDefaultSiteId(); - MatomoTracker tracker = new MatomoTracker(trackerConfigurationBuilder.build()); + matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); - CompletableFuture future1 = - tracker.sendBulkRequestAsync(Arrays.asList(requestBuilder.actionName("First").build(), + future = + matomoTracker.sendBulkRequestAsync(Arrays.asList(requestBuilder.actionName("First").build(), requestBuilder.actionName("Second").build(), requestBuilder.actionName("Third").build() )); - future1.get(); - assertThat(future1).isNotCompletedExceptionally(); - wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) - .withHeader("Content-Length", equalTo("297")) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient")) - .withRequestBody(equalToJson( - "{\"requests\" : [ \"?idsite=42&rec=1&action_name=First&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\", \"?idsite=42&rec=1&action_name=Second&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\", \"?idsite=42&rec=1&action_name=Third&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\" ]}"))); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("297")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{\"requests\" : [ \"?idsite=42&rec=1&action_name=First&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\", \"?idsite=42&rec=1&action_name=Second&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\", \"?idsite=42&rec=1&action_name=Third&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\" ]}"))); + }); } @@ -457,19 +465,26 @@ void failsOnNegativeSiteId() { givenTrackerConfigurationWithDefaultSiteId(); requestBuilder.siteId(-1); - assertThatThrownBy(this::whenSendsRequestAsync) - .hasRootCauseInstanceOf(IllegalArgumentException.class) - .hasRootCauseMessage("Site ID must not be negative"); + whenSendsRequestAsync(); + + assertThat(future) + .failsWithin(1, MINUTES) + .withThrowableThat() + .havingRootCause() + .isInstanceOf(IllegalArgumentException.class) + .withMessage("Site ID must not be negative"); + + ; } @Test - void doesNotSendRequestAsyncIfTrackerConfigurationIsDisabled() { + void doesNotSendRequestAsyncIfTrackerConfigurationIsDisabled() throws Exception { givenTrackerConfigurationWithDefaultSiteId(); trackerConfigurationBuilder.enabled(false); whenSendsRequestAsync(); + future.get(); - assertThat(future).isNotCompletedExceptionally(); wireMockServer.verify(0, getRequestedFor(urlPathEqualTo("/matomo.php"))); } @@ -503,42 +518,42 @@ private void whenSendsBulkRequest() { } @Test - void doesNotSendBulkRequestAsyncIfTrackerConfigurationIsDisabled() { + void doesNotSendBulkRequestAsyncIfTrackerConfigurationIsDisabled() throws Exception { givenTrackerConfigurationWithDefaultSiteId(); trackerConfigurationBuilder.enabled(false); whenSendsBulkRequestAsync(); - assertThat(future).isNotCompletedExceptionally(); + future.get(); wireMockServer.verify(0, postRequestedFor(urlPathEqualTo("/matomo.php"))); } @Test void sendsRequestAsyncAndAcceptsCallback() throws Exception { givenTrackerConfigurationWithDefaultSiteId(); - MatomoTracker tracker = new MatomoTracker(trackerConfigurationBuilder.build()); + matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); AtomicBoolean success = new AtomicBoolean(); - CompletableFuture future = tracker.sendRequestAsync(requestBuilder.build(), v -> { + future = matomoTracker.sendRequestAsync(requestBuilder.build(), v -> { success.set(true); }); - future.get(); - assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php"))); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php"))); + }); assertThat(success).isTrue(); } @Test void sendsRequestsAsyncAndAcceptsCallback() throws Exception { givenTrackerConfigurationWithDefaultSiteId(); - MatomoTracker tracker = new MatomoTracker(trackerConfigurationBuilder.build()); + matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); AtomicBoolean success = new AtomicBoolean(); - CompletableFuture future = - tracker.sendBulkRequestAsync(singleton(requestBuilder.build()), v -> { + future = + matomoTracker.sendBulkRequestAsync(singleton(requestBuilder.build()), v -> { success.set(true); }); - future.get(); - assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(postRequestedFor(urlPathEqualTo("/matomo.php"))); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(postRequestedFor(urlPathEqualTo("/matomo.php"))); + }); assertThat(success).isTrue(); } diff --git a/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java b/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java index 04693cf9..49432619 100644 --- a/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java +++ b/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java @@ -28,15 +28,15 @@ class PiwikRequestTest { @BeforeEach void setUp() throws Exception { - request = new PiwikRequest(3, new URL("http://test.com")); + request = new PiwikRequest(3, new URL("https://test.com")); } @Test void testConstructor() throws Exception { - request = new PiwikRequest(3, new URL("http://test.com")); + request = new PiwikRequest(3, new URL("https://test.com")); assertThat(request.getSiteId()).isEqualTo(Integer.valueOf(3)); assertThat(request.getRequired()).isTrue(); - assertThat(request.getActionUrl()).isEqualTo("http://test.com"); + assertThat(request.getActionUrl()).isEqualTo("https://test.com"); assertThat(request.getVisitorId()).isNotNull(); assertThat(request.getRandomValue()).isNotNull(); assertThat(request.getApiVersion()).isEqualTo("1"); @@ -61,8 +61,8 @@ void testActionName() { void testActionUrl() { request.setActionUrl(null); assertThat(request.getActionUrl()).isNull(); - request.setActionUrl("http://action.com"); - assertThat(request.getActionUrl()).isEqualTo("http://action.com"); + request.setActionUrl("https://action.com"); + assertThat(request.getActionUrl()).isEqualTo("https://action.com"); } /** @@ -147,8 +147,8 @@ void testContentPiece() { */ @Test void testContentTarget() { - request.setContentTarget("http://target.com"); - assertThat(request.getContentTarget()).isEqualTo("http://target.com"); + request.setContentTarget("https://target.com"); + assertThat(request.getContentTarget()).isEqualTo("https://target.com"); } /** @@ -292,8 +292,8 @@ void testDeviceResolution() { @Test void testDownloadUrl() { - request.setDownloadUrl("http://download.com"); - assertThat(request.getDownloadUrl()).isEqualTo("http://download.com"); + request.setDownloadUrl("https://download.com"); + assertThat(request.getDownloadUrl()).isEqualTo("https://download.com"); } /** @@ -537,8 +537,8 @@ void testNewVisit() { */ @Test void testOutlinkUrl() { - request.setOutlinkUrl("http://outlink.com"); - assertThat(request.getOutlinkUrl()).isEqualTo("http://outlink.com"); + request.setOutlinkUrl("https://outlink.com"); + assertThat(request.getOutlinkUrl()).isEqualTo("https://outlink.com"); } /** @@ -661,8 +661,8 @@ void testRandomValue() { */ @Test void testReferrerUrl() { - request.setReferrerUrl("http://referrer.com"); - assertThat(request.getReferrerUrl()).isEqualTo("http://referrer.com"); + request.setReferrerUrl("https://referrer.com"); + assertThat(request.getReferrerUrl()).isEqualTo("https://referrer.com"); } /** diff --git a/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java b/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java index 6f61f737..1df7512d 100644 --- a/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java +++ b/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java @@ -9,6 +9,7 @@ import static com.github.tomakehurst.wiremock.client.WireMock.status; import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static java.util.concurrent.TimeUnit.MINUTES; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -46,10 +47,7 @@ static void beforeAll() { @BeforeEach void setUp() throws MalformedURLException { - piwikTracker = new PiwikTracker( - String.format("http://localhost:%d/matomo.php", wireMockServer.port()), - -1 - ); + piwikTracker = new PiwikTracker(wireMockServer.baseUrl() + "/matomo.php", -1); wireMockServer.resetRequests(); wireMockServer.stubFor(post(urlPathEqualTo("/matomo.php")).willReturn(status(204))); wireMockServer.stubFor(get(urlPathEqualTo("/matomo.php")).willReturn(status(204))); @@ -68,8 +66,7 @@ void testSendRequest() { piwikTracker.sendRequest(request); wireMockServer.verify(getRequestedFor(urlEqualTo( - "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue")).withHeader( - "User-Agent", + "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue")).withHeader("User-Agent", equalTo("MatomoJavaClient") )); } @@ -82,14 +79,15 @@ void testSendRequestAsync() throws Exception { request.setCustomTrackingParameter("parameterName", "parameterValue"); CompletableFuture future = piwikTracker.sendRequestAsync(request); - future.get(); assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(getRequestedFor(urlEqualTo( - "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue")).withHeader( - "User-Agent", - equalTo("MatomoJavaClient") - )); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(getRequestedFor(urlEqualTo( + "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue")) + .withHeader("User-Agent", equalTo("MatomoJavaClient") + )); + }); + } @@ -169,17 +167,17 @@ void testSendBulkRequestAsync_Iterable() throws Exception { request.setCustomTrackingParameter("parameterName", "parameterValue"); CompletableFuture future = piwikTracker.sendBulkRequestAsync(requests); - future.get(); - assertThat(future).isNotCompletedExceptionally(); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("167")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{\"requests\" : [ \"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\" ]}"))); - wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) - .withHeader("Content-Length", equalTo("167")) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient")) - .withRequestBody(equalToJson( - "{\"requests\" : [ \"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\" ]}"))); + }); } @@ -207,30 +205,28 @@ void testSendBulkRequestAsync_Iterable_String() throws Exception { CompletableFuture future = piwikTracker.sendBulkRequestAsync(requests, "12345678901234567890123456789012"); - future.get(); assertThat(future).isNotCompletedExceptionally(); - - wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) - .withHeader("Content-Length", equalTo("215")) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient")) - .withRequestBody(equalToJson( - "{\"requests\":[ \"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\"],\"token_auth\":\"12345678901234567890123456789012\"}"))); - + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("215")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{\"requests\":[ \"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\"],\"token_auth\":\"12345678901234567890123456789012\"}"))); + + }); } @Test void createsPiwikTrackerWithHostUrl() { - PiwikTracker piwikTracker = - new PiwikTracker(String.format("http://localhost:%d/matomo.php", wireMockServer.port())); + PiwikTracker piwikTracker = new PiwikTracker(wireMockServer.baseUrl() + "/matomo.php"); piwikTracker.sendRequest(request); wireMockServer.verify(getRequestedFor(urlEqualTo( - "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand")).withHeader( - "User-Agent", + "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand")).withHeader("User-Agent", equalTo("MatomoJavaClient") )); } @@ -238,10 +234,7 @@ void createsPiwikTrackerWithHostUrl() { @Test void createPiwikTrackerWithHostUrlAndProxyHostAndPort() { PiwikTracker piwikTracker = - new PiwikTracker(String.format("http://localhost:%d/matomo.php", wireMockServer.port()), - "localhost", - 8080 - ); + new PiwikTracker(wireMockServer.baseUrl() + "/matomo.php", "localhost", 8080); assertThatThrownBy(() -> piwikTracker.sendRequest(request)) .isInstanceOf(MatomoException.class) @@ -252,11 +245,7 @@ void createPiwikTrackerWithHostUrlAndProxyHostAndPort() { @Test void createPiwikTrackerWithHostUrlAndProxyHostAndPortAndTimeout() { PiwikTracker piwikTracker = - new PiwikTracker(String.format("http://localhost:%d/matomo.php", wireMockServer.port()), - "localhost", - 8080, - 1000 - ); + new PiwikTracker(wireMockServer.baseUrl() + "/matomo.php", "localhost", 8080, 1000); assertThatThrownBy(() -> piwikTracker.sendRequest(request)) .isInstanceOf(MatomoException.class) diff --git a/src/test/java/org/matomo/java/tracking/parameters/HexTest.java b/src/test/java/org/matomo/java/tracking/parameters/HexTest.java new file mode 100644 index 00000000..5f07f27d --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/parameters/HexTest.java @@ -0,0 +1,37 @@ +package org.matomo.java.tracking.parameters; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class HexTest { + + + + @Test + void failsIfBytesAreNull() { + assertThatThrownBy(() -> Hex.fromBytes(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("bytes is marked non-null but is null"); + } + + private static Stream testBytes() { + return Stream.of( + Arguments.of(new byte[] {0x00, 0x01, 0x02, 0x03}, "00010203"), + Arguments.of(new byte[] {(byte) 0xFF, (byte) 0xFE, (byte) 0xFD, (byte) 0xFC}, "fffefdfc"), + Arguments.of(new byte[0], "") + ); + } + + @ParameterizedTest + @MethodSource("testBytes") + void convertsBytesIntoHex(byte[] bytes, String expected) { + assertThat(Hex.fromBytes(bytes)).isEqualTo(expected); + } + +} \ No newline at end of file diff --git a/src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java b/src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java index aff51fbf..45aa34de 100644 --- a/src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java +++ b/src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java @@ -177,4 +177,13 @@ void failsOnNullUUID() { .hasMessage("uuid is marked non-null but is null"); } + @Test + void createsVisitorIdFromString() { + + VisitorId visitorId = VisitorId.fromString("test"); + + assertThat(visitorId).hasToString("0000000000364492"); + + } + } From 955d84233e81548b8a59f36afa347393a2056534 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Fri, 3 Nov 2023 16:11:03 +0100 Subject: [PATCH 156/467] Provide more backwards compatibility --- pom.xml | 32 +++++++- .../matomo/java/tracking/MatomoRequest.java | 80 ++++++++++++++++--- .../java/tracking/MatomoRequestBuilder.java | 69 ++++++++++++++++ .../matomo/java/tracking/QueryCreator.java | 4 +- .../java/tracking/parameters/Country.java | 1 + .../tracking/parameters/DeviceResolution.java | 5 +- .../org/piwik/java/tracking/PiwikDate.java | 2 + .../tracking/MatomoRequestBuilderTest.java | 43 +++++++++- .../java/tracking/MatomoRequestTest.java | 41 +++++++++- .../java/tracking/QueryCreatorTest.java | 8 +- .../parameters/DeviceResolutionTest.java | 12 +++ 11 files changed, 270 insertions(+), 27 deletions(-) create mode 100644 src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java diff --git a/pom.xml b/pom.xml index 0c8c418b..753038bb 100644 --- a/pom.xml +++ b/pom.xml @@ -63,6 +63,8 @@ 1.8 1.8 2.0.9 + ${project.build.outputDirectory}/delombok + 1.18.30 @@ -167,13 +169,39 @@ + + org.projectlombok + lombok-maven-plugin + 1.18.20.0 + + + org.projectlombok + lombok + ${lombok.version} + + + + ${project.basedir}/src/main/java + ${delombok.output} + false + + + + generate-sources + + delombok + + + + org.apache.maven.plugins maven-javadoc-plugin 3.6.0 true - all,-missing,-reference + ${delombok.output} + none @@ -317,7 +345,7 @@ org.projectlombok lombok - 1.18.30 + ${lombok.version} provided diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/src/main/java/org/matomo/java/tracking/MatomoRequest.java index 99395a9c..71cc7ee9 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -7,6 +7,7 @@ package org.matomo.java.tracking; +import static java.util.Collections.singleton; import static java.util.Objects.requireNonNull; import edu.umd.cs.findbugs.annotations.Nullable; @@ -27,6 +28,7 @@ import lombok.NonNull; import lombok.Setter; import lombok.ToString; +import lombok.experimental.Tolerate; import org.matomo.java.tracking.parameters.AcceptLanguage; import org.matomo.java.tracking.parameters.Country; import org.matomo.java.tracking.parameters.CustomVariable; @@ -44,7 +46,7 @@ * * @author brettcsorba */ -@Builder +@Builder(builderMethodName = "request") @Getter @Setter @NoArgsConstructor @@ -576,7 +578,7 @@ public class MatomoRequest { private Iterable dimensions; - private Map> customTrackingParameters; + private Map> additionalParameters; /** * Create a new request from the id of the site being tracked and the full @@ -616,10 +618,10 @@ public MatomoRequest(int siteId, String actionUrl) { * @return the list of objects currently stored at the specified key */ public List getCustomTrackingParameter(@NonNull String key) { - if (customTrackingParameters == null || customTrackingParameters.isEmpty()) { + if (additionalParameters == null || additionalParameters.isEmpty()) { return Collections.emptyList(); } - Collection parameterValues = customTrackingParameters.get(key); + Collection parameterValues = additionalParameters.get(key); if (parameterValues == null || parameterValues.isEmpty()) { return Collections.emptyList(); } @@ -643,15 +645,15 @@ public void setCustomTrackingParameter( ) { if (value == null) { - if (customTrackingParameters != null) { - customTrackingParameters.remove(key); + if (additionalParameters != null) { + additionalParameters.remove(key); } } else { - if (customTrackingParameters == null) { - customTrackingParameters = new LinkedHashMap<>(); + if (additionalParameters == null) { + additionalParameters = new LinkedHashMap<>(); } Collection values = - customTrackingParameters.computeIfAbsent(key, k -> new ArrayList<>()); + additionalParameters.computeIfAbsent(key, k -> new ArrayList<>()); values.clear(); values.add(value); } @@ -666,17 +668,17 @@ public void setCustomTrackingParameter( * @param value the parameter's value. Cannot be null */ public void addCustomTrackingParameter(@NonNull String key, @NonNull Object value) { - if (customTrackingParameters == null) { - customTrackingParameters = new LinkedHashMap<>(); + if (additionalParameters == null) { + additionalParameters = new LinkedHashMap<>(); } - customTrackingParameters.computeIfAbsent(key, k -> new ArrayList<>()).add(value); + additionalParameters.computeIfAbsent(key, k -> new ArrayList<>()).add(value); } /** * Removes all custom tracking parameters. */ public void clearCustomTrackingParameter() { - customTrackingParameters.clear(); + additionalParameters.clear(); } /** @@ -941,4 +943,56 @@ public void setVisitCustomVariable( setCustomVariable(visitCustomVariables, customVariable, index); } + /** + * Sets a custom parameter to append to the Matomo tracking parameters. + * + *

Attention: If a parameter with the same name already exists, it will be appended twice! + * + * @param parameterName The name of the query parameter to append. Must not be null or empty. + * @param value The value of the query parameter to append. To remove the parameter, pass null. + */ + @Deprecated + public void setParameter(@NonNull String parameterName, Object value) { + if (parameterName.trim().isEmpty()) { + throw new IllegalArgumentException("Parameter name must not be empty"); + } + if (additionalParameters == null) { + if (value == null) { + return; + } + additionalParameters = new LinkedHashMap<>(); + } + if (value == null) { + additionalParameters.remove(parameterName); + } else { + additionalParameters.put(parameterName, singleton(value)); + } + } + + /** + * Creates a new {@link MatomoRequestBuilder} instance. Only here for backwards compatibility. + * + * @deprecated Use {@link MatomoRequest#request()} instead. + */ + @Deprecated + public static org.matomo.java.tracking.MatomoRequestBuilder builder() { + return new org.matomo.java.tracking.MatomoRequestBuilder(); + } + + /** + * Parses the given device resolution string and sets the {@link #deviceResolution} field. + * + * @param deviceResolution the device resolution string to parse. Format: "WIDTHxHEIGHT" + * @deprecated Use {@link #setDeviceResolution(DeviceResolution)} instead. + */ + @Tolerate + @Deprecated + public void setDeviceResolution(@Nullable String deviceResolution) { + if (deviceResolution == null || deviceResolution.trim().isEmpty()) { + this.deviceResolution = null; + } else { + this.deviceResolution = DeviceResolution.fromString(deviceResolution); + } + } + } diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java b/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java new file mode 100644 index 00000000..1dd8f9db --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java @@ -0,0 +1,69 @@ +package org.matomo.java.tracking; + +import static java.util.Collections.singleton; + +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; +import java.util.stream.Collectors; +import lombok.NonNull; +import org.matomo.java.tracking.parameters.AcceptLanguage; + +/** + * The former MatomoRequestBuilder class has been moved to MatomoRequest.MatomoRequestBuilder. + * This class is only here for backwards compatibility. + * + * @deprecated Use {@link MatomoRequest.MatomoRequestBuilder} instead. + */ +@Deprecated +public class MatomoRequestBuilder extends MatomoRequest.MatomoRequestBuilder { + + + /** + * Sets the tracking parameter for the accept languages of a user. Only here for backwards + * compatibility. + * + * @param headerAcceptLanguage The accept language header of a user. Must be in the format + * specified in RFC 2616. + * @return This builder + * @deprecated Use {@link MatomoRequest.MatomoRequestBuilder#headerAcceptLanguage(AcceptLanguage)} + * in combination with {@link AcceptLanguage#fromHeader(String)} instead. + */ + @Deprecated + public MatomoRequestBuilder headerAcceptLanguage(@Nullable String headerAcceptLanguage) { + headerAcceptLanguage(AcceptLanguage.fromHeader(headerAcceptLanguage)); + return this; + } + + /** + * Sets the custom tracking parameters to the given parameters. + * + *

This converts the given map to a map of collections. Only included for backwards + * compatibility. + * + * @param parameters The custom tracking parameters to set + * @return This builder + * @deprecated Use {@link MatomoRequest.MatomoRequestBuilder#additionalParameters(Map)}} + */ + @Deprecated + public MatomoRequestBuilder customTrackingParameters(@Nullable Map parameters) { + if (parameters == null || parameters.isEmpty()) { + additionalParameters(null); + } else { + additionalParameters(parameters.entrySet().stream().collect(Collectors.toMap( + e -> e.getKey(), + e -> toCollection(e) + ))); + } + return this; + } + + private static Collection toCollection(@NonNull Map.Entry e) { + if (e.getValue() instanceof Collection) { + return (Collection) e.getValue(); + } + return new ArrayList<>(singleton(e.getValue())); + } + +} diff --git a/src/main/java/org/matomo/java/tracking/QueryCreator.java b/src/main/java/org/matomo/java/tracking/QueryCreator.java index 2e029caf..d15a6011 100644 --- a/src/main/java/org/matomo/java/tracking/QueryCreator.java +++ b/src/main/java/org/matomo/java/tracking/QueryCreator.java @@ -89,9 +89,9 @@ String createQuery( for (TrackingParameterMethod method : TRACKING_PARAMETER_METHODS) { appendParameter(method, request, query); } - if (request.getCustomTrackingParameters() != null) { + if (request.getAdditionalParameters() != null) { for (Entry> entry : request - .getCustomTrackingParameters() + .getAdditionalParameters() .entrySet()) { for (Object value : entry.getValue()) { if (value != null && !value.toString().trim().isEmpty()) { diff --git a/src/main/java/org/matomo/java/tracking/parameters/Country.java b/src/main/java/org/matomo/java/tracking/parameters/Country.java index 2435c1fe..ab51e14f 100644 --- a/src/main/java/org/matomo/java/tracking/parameters/Country.java +++ b/src/main/java/org/matomo/java/tracking/parameters/Country.java @@ -105,6 +105,7 @@ public Locale getLocale() { * @deprecated Since you instantiate this class, you can determine the language on your own * using {@link Locale#getCountry()} */ + @Deprecated public final void setLocale(Locale locale) { if (locale == null || locale.getCountry() == null || locale.getCountry().isEmpty()) { throw new IllegalArgumentException("Invalid locale"); diff --git a/src/main/java/org/matomo/java/tracking/parameters/DeviceResolution.java b/src/main/java/org/matomo/java/tracking/parameters/DeviceResolution.java index 1b9ae88b..6729443c 100644 --- a/src/main/java/org/matomo/java/tracking/parameters/DeviceResolution.java +++ b/src/main/java/org/matomo/java/tracking/parameters/DeviceResolution.java @@ -35,9 +35,12 @@ public static DeviceResolution fromString( @Nullable String deviceResolution ) { - if (deviceResolution == null) { + if (deviceResolution == null || deviceResolution.trim().isEmpty()) { return null; } + if (deviceResolution.length() < 3) { + throw new IllegalArgumentException("Wrong device resolution size"); + } String[] dimensions = deviceResolution.split("x"); if (dimensions.length != 2) { throw new IllegalArgumentException("Wrong dimension size"); diff --git a/src/main/java/org/piwik/java/tracking/PiwikDate.java b/src/main/java/org/piwik/java/tracking/PiwikDate.java index 23927402..d8029269 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikDate.java +++ b/src/main/java/org/piwik/java/tracking/PiwikDate.java @@ -26,6 +26,7 @@ public class PiwikDate extends MatomoDate { * * @deprecated Use {@link Instant} instead. */ + @Deprecated public PiwikDate() { } @@ -35,6 +36,7 @@ public PiwikDate() { * @param epochMilli The time in milliseconds since the epoch * @deprecated Use {@link Instant} instead. */ + @Deprecated public PiwikDate(long epochMilli) { super(epochMilli); } diff --git a/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java b/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java index 918670cd..18eae8cb 100644 --- a/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java +++ b/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java @@ -1,9 +1,9 @@ package org.matomo.java.tracking; import static java.util.Collections.singleton; +import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; -import java.util.Collections; import org.junit.jupiter.api.Test; import org.matomo.java.tracking.parameters.CustomVariables; @@ -17,13 +17,12 @@ void buildsRequest() { CustomVariable visitCustomVariable = new CustomVariable("visitCustomVariableName", "visitCustomVariableValue"); - MatomoRequest matomoRequest = MatomoRequest - .builder() + MatomoRequest matomoRequest = new MatomoRequestBuilder() .siteId(42) .actionName("ACTION_NAME") .actionUrl("https://www.your-domain.tld/some/page?query=foo") .referrerUrl("https://referrer.com") - .customTrackingParameters(Collections.singletonMap( + .additionalParameters(singletonMap( "trackingParameterName", singleton("trackingParameterValue") )) @@ -51,4 +50,40 @@ void buildsRequest() { assertThat(matomoRequest.getCustomAction()).isTrue(); } + + @Test + void setCustomTrackingParameters() { + MatomoRequest matomoRequest = new MatomoRequestBuilder() + .customTrackingParameters(singletonMap("foo", "bar")) + .siteId(42) + .actionName("ACTION_NAME") + .actionUrl("https://www.your-domain.tld/some/page?query=foo") + .referrerUrl("https://referrer.com") + .build(); + + assertThat(matomoRequest.getCustomTrackingParameter("foo")).containsExactly("bar"); + } + + @Test + void setCustomTrackingParametersWithCollectopm() { + MatomoRequest matomoRequest = new MatomoRequestBuilder() + .customTrackingParameters(singletonMap("foo", singleton("bar"))) + .siteId(42) + .actionName("ACTION_NAME") + .actionUrl("https://www.your-domain.tld/some/page?query=foo") + .referrerUrl("https://referrer.com") + .build(); + + assertThat(matomoRequest.getCustomTrackingParameter("foo")).containsExactly("bar"); + } + + @Test + void acceptsNullAsHeaderAcceptLanguage() { + MatomoRequest matomoRequest = new MatomoRequestBuilder() + .headerAcceptLanguage((String) null) + .build(); + + assertThat(matomoRequest.getHeaderAcceptLanguage()).isNull(); + } + } diff --git a/src/test/java/org/matomo/java/tracking/MatomoRequestTest.java b/src/test/java/org/matomo/java/tracking/MatomoRequestTest.java index bf130b6e..65e83def 100644 --- a/src/test/java/org/matomo/java/tracking/MatomoRequestTest.java +++ b/src/test/java/org/matomo/java/tracking/MatomoRequestTest.java @@ -15,7 +15,7 @@ void returnsEmptyListWhenCustomTrackingParametersDoesNotContainKey() { request.setCustomTrackingParameter("foo", "bar"); assertThat(request.getCustomTrackingParameter("baz")).isEmpty(); - assertThat(request.getCustomTrackingParameters()).isNotEmpty(); + assertThat(request.getAdditionalParameters()).isNotEmpty(); assertThat(request.getCustomTrackingParameter("foo")).isNotEmpty(); } @@ -92,4 +92,43 @@ void setVisitCustomVariableInitializesVisitCustomVariablesIfCustomVariableParame assertThat(request.getVisitCustomVariables()).isNotNull(); } + @Test + void setsCustomParameter() { + request.setParameter("foo", 1); + assertThat(request.getCustomTrackingParameter("foo")).contains(1); + } + + @Test + void failsToSetCustomParameterIfKeyIsNull() { + assertThatThrownBy(() -> request.setParameter( + null, + 1 + )).isInstanceOf(NullPointerException.class); + } + + @Test + void doesNothingWhenSettingCustomParameterIfValueIsNull() { + request.setParameter("foo", null); + assertThat(request.getAdditionalParameters()).isNull(); + } + + @Test + void removesCustomParameter() { + request.setParameter("foo", 1); + request.setParameter("foo", null); + assertThat(request.getAdditionalParameters()).isEmpty(); + } + + @Test + void setsDeviceResolutionString() { + request.setDeviceResolution("1920x1080"); + assertThat(request.getDeviceResolution().toString()).isEqualTo("1920x1080"); + } + + @Test + void failsIfSetParameterParameterNameIsBlank() { + assertThatThrownBy(() -> request.setParameter(" ", "bar")).isInstanceOf( + IllegalArgumentException.class); + } + } diff --git a/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java b/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java index 834016ce..fd34c068 100644 --- a/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java +++ b/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java @@ -243,18 +243,18 @@ void testGetQueryString() { whenCreatesQuery(); assertThat(query).isEqualTo( "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random"); - matomoRequestBuilder.customTrackingParameters(singletonMap("key", singleton("test"))); + matomoRequestBuilder.additionalParameters(singletonMap("key", singleton("test"))); whenCreatesQuery(); assertThat(query).isEqualTo( "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key=test"); - matomoRequestBuilder.customTrackingParameters(singletonMap("key", asList("test", "test2"))); + matomoRequestBuilder.additionalParameters(singletonMap("key", asList("test", "test2"))); whenCreatesQuery(); assertThat(query).isEqualTo( "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key=test&key=test2"); Map> customTrackingParameters = new HashMap<>(); customTrackingParameters.put("key", asList("test", "test2")); customTrackingParameters.put("key2", Collections.singletonList("test3")); - matomoRequestBuilder.customTrackingParameters(customTrackingParameters); + matomoRequestBuilder.additionalParameters(customTrackingParameters); whenCreatesQuery(); assertThat(query).isEqualTo( "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key2=test3&key=test&key=test2"); @@ -302,7 +302,7 @@ void testGetUrlEncodedQueryString() { "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random"); Map> customTrackingParameters = new HashMap<>(); customTrackingParameters.put("ke/y", Collections.singletonList("te:st")); - matomoRequestBuilder.customTrackingParameters(customTrackingParameters); + matomoRequestBuilder.additionalParameters(customTrackingParameters); whenCreatesQuery(); assertThat(query).isEqualTo( "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast"); diff --git a/src/test/java/org/matomo/java/tracking/parameters/DeviceResolutionTest.java b/src/test/java/org/matomo/java/tracking/parameters/DeviceResolutionTest.java index f05e4707..829c915f 100644 --- a/src/test/java/org/matomo/java/tracking/parameters/DeviceResolutionTest.java +++ b/src/test/java/org/matomo/java/tracking/parameters/DeviceResolutionTest.java @@ -39,4 +39,16 @@ void failsOnWrongDimensionSize() { .hasMessage("Wrong dimension size"); } + @Test + void failsIfDeviceResolutionIsTooShort() { + assertThatThrownBy(() -> DeviceResolution.fromString("1")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Wrong device resolution size"); + } + + @Test + void returnsNullIfDeviceResolutionIsEmpty() { + assertThat(DeviceResolution.fromString("")).isNull(); + } + } From 2988cab05d80d06cbc687752d6cb35c149e1cffd Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Fri, 3 Nov 2023 16:20:01 +0100 Subject: [PATCH 157/467] Add surefire report action --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7abf6b97..894b8250 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,3 +25,4 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} min-coverage-overall: 80 min-coverage-changed-files: 80 + - uses: scacap/action-surefire-report@v1.7.2 \ No newline at end of file From ad3c52c8bf7bedadf2c9e22597573a5ee3f22fe6 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Fri, 3 Nov 2023 16:23:18 +0100 Subject: [PATCH 158/467] Update SECURITY.md (#150) --- SECURITY.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index 5379a4ed..2ebd49e5 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -5,9 +5,10 @@ The following versions of this library are currently being supported with security updates. -| Version | Supported | -|---------|--------------------| -| 2.x | :white_check_mark: | +| Version | Supported | +|---------|------------------------| +| >3 | :white_check_mark: yes | +| <=2 | ✖️ no | ## Reporting a Vulnerability From 5d7dc2c8d1a97612d4c34ddf8d39e1516117f27f Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Fri, 3 Nov 2023 16:24:25 +0100 Subject: [PATCH 159/467] Add checks and contents permission --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 894b8250..4c6c89fc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,6 +11,8 @@ jobs: runs-on: ubuntu-latest permissions: pull-requests: write + checks: write + contents: read steps: - uses: actions/checkout@v4 - uses: actions/setup-java@v3 From cc2434280c36402944062a8083ff039422ad59e9 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Sat, 4 Nov 2023 13:57:29 +0100 Subject: [PATCH 160/467] Update Javadoc --- src/main/java/org/matomo/java/tracking/MatomoRequest.java | 8 ++++---- .../org/matomo/java/tracking/TrackerConfiguration.java | 2 +- .../org/matomo/java/tracking/parameters/VisitorId.java | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/src/main/java/org/matomo/java/tracking/MatomoRequest.java index 71cc7ee9..e58e21d1 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -391,13 +391,13 @@ public class MatomoRequest { private String eventCategory; /** - * e.g. Play, Pause, Duration, Add Playlist, Downloaded, Clicked... + * An event action like Play, Pause, Duration, Add Playlist, Downloaded, Clicked... */ @TrackingParameter(name = "e_a") private String eventAction; /** - * e.g. a Movie name, or Song name, or File name... + * The event name for example a Movie name, or Song name, or File name... */ @TrackingParameter(name = "e_n") private String eventName; @@ -541,7 +541,7 @@ public class MatomoRequest { /** * If set to 0 (send_image=0) Matomo will respond with an HTTP 204 response code instead of a GIF image. This improves - * performance and can fix errors if images are not allowed to be obtained directly (e.g. Chrome Apps). Available + * performance and can fix errors if images are not allowed to be obtained directly (like Chrome Apps). Available * since Matomo 2.10.0 * *

Default is {@code false} @@ -596,7 +596,7 @@ public class MatomoRequest { * * @param siteId the id of the website we're tracking a visit/action for * @param actionUrl the full URL for the current action - * @deprecated Please use {@link MatomoRequest#builder()} + * @deprecated Please use {@link MatomoRequest#request()} */ @Deprecated public MatomoRequest(int siteId, String actionUrl) { diff --git a/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java b/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java index 1376cf42..31d4129b 100644 --- a/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java +++ b/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java @@ -24,7 +24,7 @@ public class TrackerConfiguration { private static final Pattern AUTH_TOKEN_PATTERN = Pattern.compile("[a-z0-9]+"); /** - * The Matomo Tracking HTTP API endpoint, e.g. https://your-matomo-domain.example/matomo.php + * The Matomo Tracking HTTP API endpoint, for example https://your-matomo-domain.example/matomo.php */ @NonNull URI apiEndpoint; diff --git a/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java b/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java index cbf0404d..39b54e30 100644 --- a/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java +++ b/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java @@ -30,7 +30,7 @@ public class VisitorId { * Static factory to generate a random visitor id. * *

Please consider creating a fixed id for each visitor by getting a hash code from e.g. the username and - * using {@link #fromHash(long)} + * using {@link #fromHash(long)} or {@link #fromString(String)} instead of using this method. * * @return A randomly generated visitor id */ @@ -47,7 +47,7 @@ public static VisitorId random() { *

You can use e.g. {@link Object#hashCode()} to generate a hash code for an object, e.g. a username * string as input. * - * @param hash A number (e.g. a hash code) to create the visitor id from + * @param hash A number (a hash code) to create the visitor id from * @return Always the same visitor id for the same input */ @edu.umd.cs.findbugs.annotations.NonNull From e9130a663cab694eeab670dfc0c53da7f63eda9f Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Sat, 4 Nov 2023 13:00:24 +0000 Subject: [PATCH 161/467] [maven-release-plugin] prepare release matomo-java-tracker-3.0.0-rc2 --- pom.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 753038bb..58b19ae7 100644 --- a/pom.xml +++ b/pom.xml @@ -1,10 +1,9 @@ - + 4.0.0 org.piwik.java.tracking matomo-java-tracker - 3.0.0-rc2-SNAPSHOT + 3.0.0-rc2 jar Matomo Java Tracker @@ -42,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - HEAD + matomo-java-tracker-3.0.0-rc2 From 4eea62684dbecb796334f7fb0e19624de5a3b876 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Sat, 4 Nov 2023 13:00:26 +0000 Subject: [PATCH 162/467] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 58b19ae7..d0297a99 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker - 3.0.0-rc2 + 3.0.0-rc3-SNAPSHOT jar Matomo Java Tracker @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - matomo-java-tracker-3.0.0-rc2 + HEAD From 5cb92dc8858873bcfa2f8beb81579c3c268120a5 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Sat, 4 Nov 2023 17:19:00 +0100 Subject: [PATCH 163/467] Use request() instead of deprecated builder() --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cee8de59..81d62050 100644 --- a/README.md +++ b/README.md @@ -290,7 +290,7 @@ public class YourImplementation { public void yourMethod() { Collection requests = new ArrayList<>(); - MatomoRequestBuilder builder = MatomoRequest.builder().siteId(42); + MatomoRequestBuilder builder = MatomoRequest.request().siteId(42); requests.add(builder.actionUrl("https://www.mydomain.com/some/page").actionName("Some Page").build()); requests.add(builder.actionUrl("https://www.mydomain.com/another/page").actionName("Another Page").build()); @@ -330,7 +330,7 @@ public class YourImplementation { public void yourMethod() { Collection requests = new ArrayList<>(); - MatomoRequestBuilder builder = MatomoRequest.builder().siteId(42); + MatomoRequestBuilder builder = MatomoRequest.request().siteId(42); requests.add(builder.actionUrl("https://www.mydomain.com/some/page").actionName("Some Page").build()); requests.add(builder .actionUrl("https://www.mydomain.com/another/page") From 1b7e2ee3bbad9b29bf5b3340d6e546aa6f49b5b3 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Sat, 4 Nov 2023 17:35:28 +0100 Subject: [PATCH 164/467] List projects that use Matomo Java Tracker --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 81d62050..ca9af6bb 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,17 @@ Further information on Matomo and Matomo HTTP tracking: * [Matomo Developer](https://developer.matomo.org/) * [The Matomo project](https://matomo.org/) +Projects that use Matomo Java Tracker: + +* [Box-c - supports the UNC Libraries' Digital Collections Repository](https://github.com/UNC-Libraries/box-c) +* [DSpace - provide durable access to digital resources](https://github.com/thanvanlong/dspace) +* [Identifiers.org satellite Web SPA](https://github.com/identifiers-org/cloud-satellite-web-spa) +* [Cloud native Resolver Web Service for identifiers.org](https://github.com/identifiers-org/cloud-ws-resolver) +* [Resource Catalogue](https://github.com/madgeek-arc/resource-catalogue) +* [INCEpTION - A semantic annotation platform offering intelligent assistance and knowledge management](https://github.com/inception-project/inception) +* [QualiChain Analytics Intelligent Profiling](https://github.com/JoaoCabrita95/IP) +* And many closed source projects that we are not aware of :smile: + ## Table of Contents * [What Is New?](#what-is-new) From a5b20b97e7cdb5622c9a566d12c56181751bf02b Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Sun, 5 Nov 2023 11:40:26 +0100 Subject: [PATCH 165/467] Fix spelling in README --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ca9af6bb..8a5dbf44 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Features include: * Track page views, goals, ecommerce transactions and items * Supports custom dimensions and custom variables -* Includes tracking parameters for campaigns, events, downloads, outlinks, site search, devices, visiors +* Includes tracking parameters for campaigns, events, downloads, outlinks, site search, devices, visitors * Supports Java 8 and higher * Allows you to skip SSL certificate validation (not recommended for production) * Contains nearly no runtime dependencies (only SLF4J) @@ -53,14 +53,14 @@ Projects that use Matomo Java Tracker: * [Need help?](#need-help) * [Using this API](#using-this-api) * [Migration from Version 2 to 3](#migration-from-version-2-to-3) -* [Building](#building) +* [Building and Testing](#building-and-testing) * [Versioning](#versioning) * [Contribute](#contribute) * [License](#license) ## What Is New? -Do you still use Matomo Java Tracker 2.x? We created version 3, that is compatible with Matomo 4 and 5 and contains less +Do you still use Matomo Java Tracker 2.x? We created version 3, that is compatible with Matomo 4 and 5 and contains fewer dependencies. Release notes can be found here: https://github.com/matomo-org/matomo-java-tracker/releases Here are the most important changes: @@ -75,7 +75,7 @@ Here are the most important changes: The Javadoc for this project is hosted as a GitHub page for this repo. The latest Javadoc can be found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/index.html). Javadoc folder older versions can be found here: https://javadoc.io/doc/org.piwik.java.tracking/matomo-java-tracker -You can also build the Javadoc yourself. See the section [Building](#building) below. +You can also build the Javadoc yourself. See the section [Building](#building-and-testing) below. ## Need help? @@ -224,7 +224,7 @@ The Matomo Tracker currently supports the following builder methods: * `.delay(...)` The duration on how long the tracker collects actions until they will be sent out as a bulk request. Default: 1 seconds * `.enabled(...)` The tracker is enabled per default. You can disable it per configuration with this flag. -* `.logFailedTracking(...)` Will send errors to the log if the Matomo Tracking API responds with an errornous HTTP code +* `.logFailedTracking(...)` Will send errors to the log if the Matomo Tracking API responds with an erroneous HTTP code * `.connectTimeout(...)` allows you to change the default connection timeout of 10 seconds. 0 is interpreted as infinite, null uses the system default * `.socketTimeout(...)` allows you to change the default socket timeout of 10 seconds. 0 is From 39e07eb0c4c4bd4f31a0df43395aee13cb328088 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Sun, 5 Nov 2023 15:37:34 +0100 Subject: [PATCH 166/467] Enhance Javadoc in MatomoTracker --- .editorconfig | 3 +- .../matomo/java/tracking/MatomoTracker.java | 129 +++++++++--------- 2 files changed, 67 insertions(+), 65 deletions(-) diff --git a/.editorconfig b/.editorconfig index 13e36c31..f472ee82 100644 --- a/.editorconfig +++ b/.editorconfig @@ -16,7 +16,6 @@ ij_wrap_on_typing = false [*.java] indent_size = 2 -max_line_length = 100 ij_continuation_indent_size = 4 ij_java_align_consecutive_assignments = false ij_java_align_consecutive_variable_declarations = false @@ -155,7 +154,7 @@ ij_java_new_line_after_lparen_in_annotation = true ij_java_new_line_after_lparen_in_deconstruction_pattern = true ij_java_new_line_after_lparen_in_record_header = true ij_java_packages_to_use_import_on_demand = -ij_java_parameter_annotation_wrap = split_into_lines +ij_java_parameter_annotation_wrap = normal ij_java_parameter_name_prefix = ij_java_parameter_name_suffix = ij_java_parentheses_expression_new_line_after_left_paren = true diff --git a/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/src/main/java/org/matomo/java/tracking/MatomoTracker.java index 8ee5580a..09bd6248 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoTracker.java +++ b/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -40,8 +40,7 @@ public class MatomoTracker { */ @Deprecated public MatomoTracker( - @edu.umd.cs.findbugs.annotations.NonNull - String hostUrl + @edu.umd.cs.findbugs.annotations.NonNull String hostUrl ) { this(requireNonNull(hostUrl, "Host URL must not be null"), 0); } @@ -57,8 +56,7 @@ public MatomoTracker( */ @Deprecated public MatomoTracker( - @edu.umd.cs.findbugs.annotations.NonNull - String hostUrl, int timeout + @edu.umd.cs.findbugs.annotations.NonNull String hostUrl, int timeout ) { this(requireNonNull(hostUrl, "Host URL must not be null"), null, 0, timeout); } @@ -76,10 +74,7 @@ public MatomoTracker( */ @Deprecated public MatomoTracker( - @edu.umd.cs.findbugs.annotations.NonNull - String hostUrl, - @Nullable - String proxyHost, int proxyPort, int timeout + @edu.umd.cs.findbugs.annotations.NonNull String hostUrl, @Nullable String proxyHost, int proxyPort, int timeout ) { this(TrackerConfiguration .builder() @@ -98,16 +93,16 @@ public MatomoTracker( * @param trackerConfiguration Configurations parameters (you can use a builder) */ public MatomoTracker( - @edu.umd.cs.findbugs.annotations.NonNull - TrackerConfiguration trackerConfiguration + @edu.umd.cs.findbugs.annotations.NonNull TrackerConfiguration trackerConfiguration ) { requireNonNull(trackerConfiguration, "Tracker configuration must not be null"); trackerConfiguration.validate(); this.trackerConfiguration = trackerConfiguration; - sender = new Sender(trackerConfiguration, + sender = new Sender( + trackerConfiguration, new QueryCreator(trackerConfiguration), - Executors.newFixedThreadPool(trackerConfiguration.getThreadPoolSize() - )); + Executors.newFixedThreadPool(trackerConfiguration.getThreadPoolSize()) + ); } /** @@ -122,16 +117,17 @@ public MatomoTracker( */ @Deprecated public MatomoTracker( - @edu.umd.cs.findbugs.annotations.NonNull - String hostUrl, - @Nullable - String proxyHost, int proxyPort + @edu.umd.cs.findbugs.annotations.NonNull String hostUrl, @Nullable String proxyHost, int proxyPort ) { this(hostUrl, proxyHost, proxyPort, -1); } /** - * Sends a tracking request to Matomo. + * Sends a tracking request to Matomo using the HTTP GET method. + * + *

Use this method if you want to send a single request. If you want to send multiple requests at once, use + * {@link #sendBulkRequest(Iterable)} instead. If you want to send multiple requests asynchronously, use + * {@link #sendRequestAsync(MatomoRequest)} or {@link #sendBulkRequestAsync(Iterable)} instead. * * @param request request to send. must not be null * @deprecated use sendRequestAsync instead @@ -147,30 +143,37 @@ public void sendRequest(@NonNull MatomoRequest request) { } /** - * Send a request. + * Send a request asynchronously via HTTP GET. + * + *

Use this method if you want to send a single request. If you want to send multiple requests at once, use + * {@link #sendBulkRequestAsync(Iterable)} instead. If you want to send multiple requests synchronously, use + * {@link #sendRequest(MatomoRequest)} or {@link #sendBulkRequest(Iterable)} instead. * * @param request request to send - * @return future with response from this request + * @return completable future to let you know when the request is done */ public CompletableFuture sendRequestAsync( - @edu.umd.cs.findbugs.annotations.NonNull - MatomoRequest request + @edu.umd.cs.findbugs.annotations.NonNull MatomoRequest request ) { return sendRequestAsync(request, null); } /** - * Send a request. + * Send a request asynchronously via HTTP GET and specify a callback that gets executed when the response arrives. + * + *

Use this method if you want to send a single request. If you want to send multiple requests at once, use + * {@link #sendBulkRequestAsync(Iterable, Consumer)} instead. If you want to send multiple requests synchronously, use + * {@link #sendRequest(MatomoRequest)} or {@link #sendBulkRequest(Iterable)} instead. * * @param request request to send * @param callback callback that gets executed when response arrives, null allowed - * @return future with response from this request + * @return a completable future to let you know when the request is done + * @deprecated Please use {@link MatomoTracker#sendRequestAsync(MatomoRequest)} in combination with + * {@link CompletableFuture#thenAccept(Consumer)} instead */ + @Deprecated public CompletableFuture sendRequestAsync( - @edu.umd.cs.findbugs.annotations.NonNull - MatomoRequest request, - @Nullable - Consumer callback + @edu.umd.cs.findbugs.annotations.NonNull MatomoRequest request, @Nullable Consumer callback ) { if (trackerConfiguration.isEnabled()) { validate(request); @@ -186,8 +189,7 @@ public CompletableFuture sendRequestAsync( } private void validate( - @edu.umd.cs.findbugs.annotations.NonNull - MatomoRequest request + @edu.umd.cs.findbugs.annotations.NonNull MatomoRequest request ) { if (trackerConfiguration.getDefaultSiteId() == null && request.getSiteId() == null) { throw new IllegalArgumentException("No default site ID and no request site ID is given"); @@ -195,31 +197,35 @@ private void validate( } /** - * Send multiple requests in a single HTTP call. More efficient than sending - * several individual requests. + * Send multiple requests in a single HTTP POST call. + * + *

More efficient than sending several individual requests. If you want to send a single request, use + * {@link #sendRequest(MatomoRequest)} instead. If you want to send multiple requests asynchronously, use + * {@link #sendBulkRequestAsync(Iterable)} instead. * * @param requests the requests to send - * @deprecated use sendBulkRequestAsync instead */ - @Deprecated public void sendBulkRequest(@NonNull Iterable requests) { sendBulkRequest(requests, null); } /** - * Send multiple requests in a single HTTP call. More efficient than sending - * several individual requests. Specify the AuthToken if parameters that require - * an auth token is used. + * Send multiple requests in a single HTTP POST call. More efficient than sending + * several individual requests. + * + *

Specify the AuthToken if parameters that require an auth token is used. If you want to send a single request, + * use {@link #sendRequest(MatomoRequest)} instead. If you want to send multiple requests asynchronously, use + * {@link #sendBulkRequestAsync(Iterable)} instead. * * @param requests the requests to send - * @param authToken specify if any of the parameters use require AuthToken, null allowed - * @deprecated use sendBulkRequestAsync instead + * @param authToken specify if any of the parameters use require AuthToken, if null the default auth token from the + * request or the tracker configuration is used. + * @deprecated use {@link #sendBulkRequest(Iterable)} instead and set the auth token in the tracker configuration or + * the requests directly. */ @Deprecated public void sendBulkRequest( - @NonNull Iterable requests, - @Nullable - String authToken + @NonNull Iterable requests, @Nullable String authToken ) { if (trackerConfiguration.isEnabled()) { for (MatomoRequest request : requests) { @@ -237,11 +243,10 @@ public void sendBulkRequest( * several individual requests. * * @param requests the requests to send - * @return future with response from these requests + * @return completable future to let you know when the request is done */ public CompletableFuture sendBulkRequestAsync( - @edu.umd.cs.findbugs.annotations.NonNull - Iterable requests + @edu.umd.cs.findbugs.annotations.NonNull Iterable requests ) { return sendBulkRequestAsync(requests, null, null); } @@ -252,17 +257,18 @@ public CompletableFuture sendBulkRequestAsync( * an auth token is used. * * @param requests the requests to send - * @param authToken specify if any of the parameters use require AuthToken, null allowed + * @param authToken specify if any of the parameters use require AuthToken, if null the default auth token from the + * request or the tracker configuration is used * @param callback callback that gets executed when response arrives, null allowed - * @return the response from these requests + * @return a completable future to let you know when the request is done + * @deprecated Please set the auth token in the tracker configuration or the requests directly and use + * {@link CompletableFuture#thenAccept(Consumer)} instead for the callback. */ + @Deprecated public CompletableFuture sendBulkRequestAsync( - @edu.umd.cs.findbugs.annotations.NonNull - Iterable requests, - @Nullable - String authToken, - @Nullable - Consumer callback + @edu.umd.cs.findbugs.annotations.NonNull Iterable requests, + @Nullable String authToken, + @Nullable Consumer callback ) { if (trackerConfiguration.isEnabled()) { for (MatomoRequest request : requests) { @@ -285,13 +291,11 @@ public CompletableFuture sendBulkRequestAsync( * * @param requests the requests to send * @param callback callback that gets executed when response arrives, null allowed - * @return future with response from these requests + * @return completable future to let you know when the request is done */ public CompletableFuture sendBulkRequestAsync( - @edu.umd.cs.findbugs.annotations.NonNull - Iterable requests, - @Nullable - Consumer callback + @edu.umd.cs.findbugs.annotations.NonNull Iterable requests, + @Nullable Consumer callback ) { return sendBulkRequestAsync(requests, null, callback); } @@ -303,13 +307,12 @@ public CompletableFuture sendBulkRequestAsync( * * @param requests the requests to send * @param authToken specify if any of the parameters use require AuthToken, null allowed - * @return the response from these requests + * @return completable future to let you know when the request is done + * @deprecated Please set the auth token in the tracker configuration or the requests directly and use + * {@link #sendBulkRequestAsync(Iterable)} instead. */ public CompletableFuture sendBulkRequestAsync( - @edu.umd.cs.findbugs.annotations.NonNull - Iterable requests, - @Nullable - String authToken + @edu.umd.cs.findbugs.annotations.NonNull Iterable requests, @Nullable String authToken ) { return sendBulkRequestAsync(requests, authToken, null); } From 9529b3a47559437831a0fb3ac8831866506425fb Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Sun, 5 Nov 2023 15:40:36 +0100 Subject: [PATCH 167/467] Enhance Javadoc in MatomoTracker class again --- .../java/org/matomo/java/tracking/MatomoTracker.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/src/main/java/org/matomo/java/tracking/MatomoTracker.java index 09bd6248..282f4152 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoTracker.java +++ b/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -19,7 +19,17 @@ import lombok.extern.slf4j.Slf4j; /** - * A class that sends {@link MatomoRequest}s to a specified Matomo server. + * The main class that sends {@link MatomoRequest}s to a specified Matomo server. + * + *

Contains several methods to send requests synchronously and asynchronously. The asynchronous methods return a + * {@link CompletableFuture} that can be used to wait for the request to finish. The synchronous methods block until + * the request is finished. The asynchronous methods are more efficient if you want to send multiple requests at once. + * + * Configure this tracker using the {@link TrackerConfiguration} class. You can use the + * {@link TrackerConfiguration#builder()} to create a new configuration. The configuration is immutable and can be + * reused for multiple trackers. + * + * The tracker is thread-safe and can be used by multiple threads at once. * * @author brettcsorba */ From 6156aab189f5759ecc2648d529501241649a7d77 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Sun, 5 Nov 2023 15:44:25 +0100 Subject: [PATCH 168/467] Fix checkstyle --- src/main/java/org/matomo/java/tracking/MatomoTracker.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/src/main/java/org/matomo/java/tracking/MatomoTracker.java index 282f4152..f51788d5 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoTracker.java +++ b/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -25,11 +25,11 @@ * {@link CompletableFuture} that can be used to wait for the request to finish. The synchronous methods block until * the request is finished. The asynchronous methods are more efficient if you want to send multiple requests at once. * - * Configure this tracker using the {@link TrackerConfiguration} class. You can use the + *

Configure this tracker using the {@link TrackerConfiguration} class. You can use the * {@link TrackerConfiguration#builder()} to create a new configuration. The configuration is immutable and can be * reused for multiple trackers. * - * The tracker is thread-safe and can be used by multiple threads at once. + *

The tracker is thread-safe and can be used by multiple threads at once. * * @author brettcsorba */ From 4cf2f08497420c7ad220e656697b698574c37384 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Sun, 5 Nov 2023 15:53:33 +0100 Subject: [PATCH 169/467] Add convenience varargs send method --- .../matomo/java/tracking/MatomoTracker.java | 12 +++ .../matomo/java/tracking/MatomoTrackerIT.java | 73 +++++++++---------- 2 files changed, 46 insertions(+), 39 deletions(-) diff --git a/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/src/main/java/org/matomo/java/tracking/MatomoTracker.java index f51788d5..7fc47268 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoTracker.java +++ b/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -12,6 +12,7 @@ import edu.umd.cs.findbugs.annotations.Nullable; import java.net.URI; import java.time.Duration; +import java.util.Arrays; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; import java.util.function.Consumer; @@ -248,6 +249,17 @@ public void sendBulkRequest( } } + /** + * Send multiple requests in a single HTTP call. More efficient than sending + * several individual requests. + * + * @param requests the requests to send + * @return completable future to let you know when the request is done + */ + public CompletableFuture sendBulkRequestAsync(MatomoRequest... requests) { + return sendBulkRequestAsync(Arrays.asList(requests), null, null); + } + /** * Send multiple requests in a single HTTP call. More efficient than sending * several individual requests. diff --git a/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java index 7adb35b8..1d61d500 100644 --- a/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java +++ b/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java @@ -47,8 +47,7 @@ class MatomoTrackerIT { private static final int SITE_ID = 42; - private final TrackerConfigurationBuilder trackerConfigurationBuilder = - TrackerConfiguration.builder(); + private final TrackerConfigurationBuilder trackerConfigurationBuilder = TrackerConfiguration.builder(); private final MatomoRequestBuilder requestBuilder = MatomoRequest .builder() @@ -83,9 +82,7 @@ void requiresApiEndpoint() { @Test void requiresSiteId() { - trackerConfigurationBuilder - .apiEndpoint(URI.create(wireMockServer.baseUrl() + "/matomo.php")) - .build(); + trackerConfigurationBuilder.apiEndpoint(URI.create(wireMockServer.baseUrl() + "/matomo.php")).build(); assertThatThrownBy(this::whenSendsRequestAsync) .isInstanceOf(IllegalArgumentException.class) @@ -117,9 +114,9 @@ private void givenTrackerConfigurationWithDefaultSiteId() { private void thenGetsRequest(String expectedQuery) { assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { - wireMockServer.verify(getRequestedFor(urlEqualTo(String.format("/matomo.php?%s", - expectedQuery - ))).withHeader("User-Agent", equalTo("MatomoJavaClient"))); + wireMockServer.verify(getRequestedFor(urlEqualTo(String.format("/matomo.php?%s", expectedQuery))).withHeader("User-Agent", + equalTo("MatomoJavaClient") + )); }); } @@ -172,8 +169,7 @@ void convertsFalseBooleanTo0() { whenSendsRequestAsync(); - thenGetsRequest( - "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&java=0&send_image=0&rand=someRandom"); + thenGetsRequest("idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&java=0&send_image=0&rand=someRandom"); } @@ -218,9 +214,20 @@ void encodesLink() { } + @Test + void sendsRequestsBulkAsynchronously() { + + givenTrackerConfigurationWithDefaultSiteId(); + matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); + + future = matomoTracker.sendBulkRequestAsync(requestBuilder.build()); + + thenPostsRequestWithoutAuthToken("idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom", "90"); + + } + private void whenSendsBulkRequestAsync() { - future = new MatomoTracker(trackerConfigurationBuilder.build()).sendBulkRequestAsync(singleton( - requestBuilder.build())); + future = new MatomoTracker(trackerConfigurationBuilder.build()).sendBulkRequestAsync(singleton(requestBuilder.build())); } private void thenPostsRequestWithoutAuthToken(String expectedQuery, String contentLength) { @@ -311,9 +318,7 @@ void tracksMinimalRequest() { .add(new CustomVariable("customVariable1Key", "customVariable1Value"), 4) .add(new CustomVariable("customVariable2Key", "customVariable2Value"), 5)) .visitorVisitCount(2) - .visitorFirstVisitTimestamp(LocalDateTime - .of(2022, 8, 9, 18, 34, 12) - .toInstant(ZoneOffset.UTC)) + .visitorFirstVisitTimestamp(LocalDateTime.of(2022, 8, 9, 18, 34, 12).toInstant(ZoneOffset.UTC)) .deviceResolution(DeviceResolution.builder().width(1024).height(768).build()) .headerAcceptLanguage(AcceptLanguage .builder() @@ -327,13 +332,7 @@ void tracksMinimalRequest() { .ecommerceItems(EcommerceItems .builder() .item(org.matomo.java.tracking.parameters.EcommerceItem.builder().sku("SKU").build()) - .item(EcommerceItem - .builder() - .sku("SKU") - .name("NAME") - .category("CATEGORY") - .price(123.4) - .build()) + .item(EcommerceItem.builder().sku("SKU").name("NAME").category("CATEGORY").price(123.4).build()) .build()) .authToken("fdf6e8461ea9de33176b222519627f78") .visitorCountry(Country.fromLanguageRanges("en-GB;q=0.7,de,de-DE;q=0.9,en;q=0.8,en-US;q=0.6")); @@ -371,17 +370,17 @@ void doesNothingIfNotEnabled() throws Exception { @Test void exampleWorks() { - TrackerConfiguration config = TrackerConfiguration + TrackerConfiguration configuration = TrackerConfiguration .builder() .apiEndpoint(URI.create("https://your-domain.net/matomo/matomo.php")) .defaultSiteId(42) // if not explicitly specified by action .build(); - // Prepare the tracker (stateless - can be used for multiple actions) - MatomoTracker matomoTracker = new MatomoTracker(config); + // Prepare the tracker (stateless - can be used for multiple requests) + MatomoTracker tracker = new MatomoTracker(configuration); - // Track an action asynchronuously - CompletableFuture future = matomoTracker.sendRequestAsync(MatomoRequest + // Track an action asynchronously + CompletableFuture future = matomoTracker.sendBulkRequestAsync(MatomoRequest .builder() .actionName("User Profile / Upload Profile Picture") .actionUrl("https://your-domain.net/user/profile/picture") @@ -403,9 +402,7 @@ void exampleWorks() { void reportsErrors() { wireMockServer.stubFor(get(urlPathEqualTo("/failing")).willReturn(status(500))); - trackerConfigurationBuilder - .apiEndpoint(URI.create(wireMockServer.baseUrl() + "/failing")) - .defaultSiteId(SITE_ID); + trackerConfigurationBuilder.apiEndpoint(URI.create(wireMockServer.baseUrl() + "/failing")).defaultSiteId(SITE_ID); whenSendsRequestAsync(); @@ -441,11 +438,10 @@ void includesMultipleQueriesInBulkRequest() throws Exception { givenTrackerConfigurationWithDefaultSiteId(); matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); - future = - matomoTracker.sendBulkRequestAsync(Arrays.asList(requestBuilder.actionName("First").build(), - requestBuilder.actionName("Second").build(), - requestBuilder.actionName("Third").build() - )); + future = matomoTracker.sendBulkRequestAsync(Arrays.asList(requestBuilder.actionName("First").build(), + requestBuilder.actionName("Second").build(), + requestBuilder.actionName("Third").build() + )); assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) @@ -547,10 +543,9 @@ void sendsRequestsAsyncAndAcceptsCallback() throws Exception { givenTrackerConfigurationWithDefaultSiteId(); matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); AtomicBoolean success = new AtomicBoolean(); - future = - matomoTracker.sendBulkRequestAsync(singleton(requestBuilder.build()), v -> { - success.set(true); - }); + future = matomoTracker.sendBulkRequestAsync(singleton(requestBuilder.build()), v -> { + success.set(true); + }); assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { wireMockServer.verify(postRequestedFor(urlPathEqualTo("/matomo.php"))); }); From 4479861edf76f44cd627c3862413234db1c2b857 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Sun, 5 Nov 2023 15:54:11 +0100 Subject: [PATCH 170/467] Fix checkstyle --- src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java index 1d61d500..d09255c5 100644 --- a/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java +++ b/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java @@ -114,7 +114,8 @@ private void givenTrackerConfigurationWithDefaultSiteId() { private void thenGetsRequest(String expectedQuery) { assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { - wireMockServer.verify(getRequestedFor(urlEqualTo(String.format("/matomo.php?%s", expectedQuery))).withHeader("User-Agent", + wireMockServer.verify(getRequestedFor(urlEqualTo( + String.format("/matomo.php?%s", expectedQuery))).withHeader("User-Agent", equalTo("MatomoJavaClient") )); }); @@ -227,7 +228,8 @@ void sendsRequestsBulkAsynchronously() { } private void whenSendsBulkRequestAsync() { - future = new MatomoTracker(trackerConfigurationBuilder.build()).sendBulkRequestAsync(singleton(requestBuilder.build())); + future = + new MatomoTracker(trackerConfigurationBuilder.build()).sendBulkRequestAsync(singleton(requestBuilder.build())); } private void thenPostsRequestWithoutAuthToken(String expectedQuery, String contentLength) { From ced4ebed34827000d2ca7b5cd382fcb732357945 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Sun, 5 Nov 2023 17:13:25 +0100 Subject: [PATCH 171/467] Add more items to Ecommerce test --- README.md | 2 -- checkstyle.xml | 2 +- .../matomo/java/tracking/MatomoRequest.java | 9 ++++++ .../matomo/java/tracking/MatomoTrackerIT.java | 29 +++++++++---------- .../parameters/EcommerceItemsTest.java | 26 ++++++++++++++--- 5 files changed, 45 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 8a5dbf44..7d30c91d 100644 --- a/README.md +++ b/README.md @@ -361,8 +361,6 @@ public class YourImplementation { } } - - ``` ## Migration from Version 2 to 3 diff --git a/checkstyle.xml b/checkstyle.xml index 2345c267..8a41bc88 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -44,7 +44,7 @@ + value="^package.*|^import.*|a href|href|http://|https://|ftp://|Exception\(.*|idsite=.*|hasToString"/> diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/src/main/java/org/matomo/java/tracking/MatomoRequest.java index e58e21d1..c009b1f0 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -637,7 +637,9 @@ public List getCustomTrackingParameter(@NonNull String key) { * * @param key the parameter's key. Cannot be null * @param value the parameter's value. Removes the parameter if null + * @deprecated Use {@link MatomoRequest.MatomoRequestBuilder#additionalParameters(Map)} instead. */ + @Deprecated public void setCustomTrackingParameter( @NonNull String key, @Nullable @@ -666,6 +668,7 @@ public void setCustomTrackingParameter( * * @param key the parameter's key. Cannot be null * @param value the parameter's value. Cannot be null + * @deprecated Use {@link MatomoRequest.MatomoRequestBuilder#additionalParameters(Map)} instead. */ public void addCustomTrackingParameter(@NonNull String key, @NonNull Object value) { if (additionalParameters == null) { @@ -676,7 +679,11 @@ public void addCustomTrackingParameter(@NonNull String key, @NonNull Object valu /** * Removes all custom tracking parameters. + * + * @deprecated Please use {@link MatomoRequest.MatomoRequestBuilder#additionalParameters(Map)} instead so that you can + * manage the map yourself. */ + @Deprecated public void clearCustomTrackingParameter() { additionalParameters.clear(); } @@ -697,8 +704,10 @@ public void enableEcommerce() { * * @param index the index of the {@link EcommerceItem} to return * @return the {@link EcommerceItem} at the specified index + * @deprecated Use @link {@link MatomoRequest.MatomoRequestBuilder#ecommerceItems(EcommerceItems)} instead */ @Nullable + @Deprecated public EcommerceItem getEcommerceItem(int index) { if (ecommerceItems == null || ecommerceItems.isEmpty()) { return null; diff --git a/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java index d09255c5..b0b68b24 100644 --- a/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java +++ b/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java @@ -22,7 +22,6 @@ import java.util.Arrays; import java.util.Locale.LanguageRange; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -370,10 +369,10 @@ void doesNothingIfNotEnabled() throws Exception { } @Test - void exampleWorks() { + void example() { - TrackerConfiguration configuration = TrackerConfiguration - .builder() + // Create the tracker configuration + TrackerConfiguration configuration = TrackerConfiguration.builder() .apiEndpoint(URI.create("https://your-domain.net/matomo/matomo.php")) .defaultSiteId(42) // if not explicitly specified by action .build(); @@ -381,23 +380,21 @@ void exampleWorks() { // Prepare the tracker (stateless - can be used for multiple requests) MatomoTracker tracker = new MatomoTracker(configuration); - // Track an action asynchronously - CompletableFuture future = matomoTracker.sendBulkRequestAsync(MatomoRequest + MatomoRequest request = MatomoRequest .builder() .actionName("User Profile / Upload Profile Picture") .actionUrl("https://your-domain.net/user/profile/picture") .visitorId(VisitorId.fromString("some@email-adress.org")) // ... - .build()); - - // If you want to ensure the request has been handled: - try { - future.get(); - } catch (InterruptedException e) { - // Occurs if the current thread is interrupted while waiting - } catch (ExecutionException e) { - // Happens on any exception during the request - } + .build(); + + // Send the request asynchronously (non-blocking) as an HTTP POST request (payload is JSON and contains the + // tracking parameters) + CompletableFuture future = tracker.sendBulkRequestAsync(request); + + // If you want to ensure the request was sent without exceptions: + future.join(); // throws an unchecked exception if the request failed + } @Test diff --git a/src/test/java/org/matomo/java/tracking/parameters/EcommerceItemsTest.java b/src/test/java/org/matomo/java/tracking/parameters/EcommerceItemsTest.java index 1a467a24..9d13a5d8 100644 --- a/src/test/java/org/matomo/java/tracking/parameters/EcommerceItemsTest.java +++ b/src/test/java/org/matomo/java/tracking/parameters/EcommerceItemsTest.java @@ -3,15 +3,33 @@ import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Test; -import org.matomo.java.tracking.EcommerceItem; class EcommerceItemsTest { @Test void formatsJson() { - EcommerceItems ecommerceItems = new EcommerceItems(); - ecommerceItems.add(new EcommerceItem("sku", "name", "category", 1.0, 1)); - assertThat(ecommerceItems).hasToString("[[\"sku\",\"name\",\"category\",1.0,1]]"); + EcommerceItems ecommerceItems = EcommerceItems + .builder() + .item(EcommerceItem + .builder() + .sku("XYZ12345") + .name("Matomo - The big book about web analytics") + .category("Education & Teaching") + .price(23.1) + .quantity(2) + .build()) + .item(EcommerceItem + .builder() + .sku("B0C2WV3MRJ") + .name("Matomo for data visualization") + .category("Education & Teaching") + .price(15.1) + .quantity(1) + .build()) + .build(); + assertThat(ecommerceItems).hasToString("[[\"XYZ12345\",\"Matomo - The big book about web analytics\",\"Education & Teaching\",23.1,2],[\"B0C2WV3MRJ\",\"Matomo for data visualization\",\"Education & Teaching\",15.1,1]]"); } + + } From 79f79c2385cc92db6ef020b6f98c6eb26bf0b14c Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 6 Nov 2023 00:21:51 +0100 Subject: [PATCH 172/467] Add test module and fix some bugs --- .gitignore | 11 +- README.md | 143 +++++++++----- core/pom.xml | 62 ++++++ {src => core/src}/main/java/lombok.config | 0 .../org/matomo/java/tracking/AuthToken.java | 0 .../matomo/java/tracking/CustomVariable.java | 0 .../java/tracking/DaemonThreadFactory.java | 12 ++ .../matomo/java/tracking/EcommerceItem.java | 0 .../java/tracking/InvalidUrlException.java | 0 .../org/matomo/java/tracking/MatomoDate.java | 0 .../matomo/java/tracking/MatomoException.java | 0 .../matomo/java/tracking/MatomoLocale.java | 0 .../matomo/java/tracking/MatomoRequest.java | 12 +- .../java/tracking/MatomoRequestBuilder.java | 0 .../matomo/java/tracking/MatomoTracker.java | 17 +- .../java/tracking/ProxyAuthenticator.java | 0 .../matomo/java/tracking/QueryCreator.java | 27 +-- .../java/tracking/RequestValidator.java | 0 .../java/org/matomo/java/tracking/Sender.java | 84 +++----- .../java/tracking/TrackerConfiguration.java | 0 .../java/tracking/TrackingParameter.java | 2 + .../tracking/TrackingParameterMethod.java | 13 +- .../tracking/TrustingHostnameVerifier.java | 0 .../tracking/TrustingX509TrustManager.java | 0 .../matomo/java/tracking/package-info.java | 0 .../tracking/parameters/AcceptLanguage.java | 0 .../java/tracking/parameters/Country.java | 0 .../tracking/parameters/CustomVariable.java | 0 .../tracking/parameters/CustomVariables.java | 0 .../tracking/parameters/DeviceResolution.java | 0 .../tracking/parameters/EcommerceItem.java | 0 .../tracking/parameters/EcommerceItems.java | 0 .../matomo/java/tracking/parameters/Hex.java | 0 .../java/tracking/parameters/RandomValue.java | 0 .../java/tracking/parameters/UniqueId.java | 0 .../java/tracking/parameters/VisitorId.java | 0 .../tracking/parameters/package-info.java | 0 .../piwik/java/tracking/CustomVariable.java | 0 .../piwik/java/tracking/EcommerceItem.java | 0 .../org/piwik/java/tracking/PiwikDate.java | 0 .../org/piwik/java/tracking/PiwikLocale.java | 0 .../org/piwik/java/tracking/PiwikRequest.java | 0 .../org/piwik/java/tracking/PiwikTracker.java | 0 .../org/piwik/java/tracking/package-info.java | 0 .../matomo/java/tracking/AuthTokenTest.java | 0 .../java/tracking/CustomVariableTest.java | 0 .../java/tracking/EcommerceItemTest.java | 0 .../tracking/InvalidUrlExceptionTest.java | 0 .../java/tracking/MatomoExceptionTest.java | 0 .../java/tracking/MatomoLocaleTest.java | 0 .../tracking/MatomoRequestBuilderTest.java | 0 .../java/tracking/MatomoRequestTest.java | 0 .../matomo/java/tracking/MatomoTrackerIT.java | 31 +-- .../matomo/java/tracking/PiwikDateTest.java | 0 .../matomo/java/tracking/PiwikLocaleTest.java | 0 .../java/tracking/PiwikRequestTest.java | 0 .../matomo/java/tracking/PiwikTrackerIT.java | 4 +- .../java/tracking/ProxyAuthenticatorTest.java | 0 .../java/tracking/QueryCreatorTest.java | 26 +-- .../java/tracking/RequestValidatorTest.java | 0 .../org/matomo/java/tracking/SenderIT.java | 0 .../tracking/TrackerConfigurationTest.java | 0 .../tracking/TrackingParameterMethodTest.java | 22 ++- .../TrustingHostnameVerifierTest.java | 0 .../TrustingX509TrustManagerTest.java | 0 .../parameters/AcceptLanguageTest.java | 0 .../java/tracking/parameters/CountryTest.java | 0 .../parameters/CustomVariableTest.java | 0 .../parameters/CustomVariablesTest.java | 0 .../parameters/DeviceResolutionTest.java | 0 .../parameters/EcommerceItemsTest.java | 0 .../java/tracking/parameters/HexTest.java | 0 .../tracking/parameters/UniqueIdTest.java | 0 .../tracking/parameters/VisitorIdTest.java | 0 .../java/tracking/CustomVariableTest.java | 0 .../java/tracking/EcommerceItemTest.java | 0 docker-compose.yml | 21 ++ pom.xml | 58 +----- test/pom.xml | 39 ++++ .../java/tracking/MatomoJavaTrackerTest.java | 186 ++++++++++++++++++ 80 files changed, 532 insertions(+), 238 deletions(-) create mode 100644 core/pom.xml rename {src => core/src}/main/java/lombok.config (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/AuthToken.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/CustomVariable.java (100%) create mode 100644 core/src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java rename {src => core/src}/main/java/org/matomo/java/tracking/EcommerceItem.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/InvalidUrlException.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/MatomoDate.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/MatomoException.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/MatomoLocale.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/MatomoRequest.java (98%) rename {src => core/src}/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/MatomoTracker.java (96%) rename {src => core/src}/main/java/org/matomo/java/tracking/ProxyAuthenticator.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/QueryCreator.java (89%) rename {src => core/src}/main/java/org/matomo/java/tracking/RequestValidator.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/Sender.java (79%) rename {src => core/src}/main/java/org/matomo/java/tracking/TrackerConfiguration.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/TrackingParameter.java (91%) rename {src => core/src}/main/java/org/matomo/java/tracking/TrackingParameterMethod.java (60%) rename {src => core/src}/main/java/org/matomo/java/tracking/TrustingHostnameVerifier.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/TrustingX509TrustManager.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/package-info.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/parameters/AcceptLanguage.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/parameters/Country.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/parameters/CustomVariable.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/parameters/CustomVariables.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/parameters/DeviceResolution.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/parameters/EcommerceItem.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/parameters/EcommerceItems.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/parameters/Hex.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/parameters/RandomValue.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/parameters/UniqueId.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/parameters/VisitorId.java (100%) rename {src => core/src}/main/java/org/matomo/java/tracking/parameters/package-info.java (100%) rename {src => core/src}/main/java/org/piwik/java/tracking/CustomVariable.java (100%) rename {src => core/src}/main/java/org/piwik/java/tracking/EcommerceItem.java (100%) rename {src => core/src}/main/java/org/piwik/java/tracking/PiwikDate.java (100%) rename {src => core/src}/main/java/org/piwik/java/tracking/PiwikLocale.java (100%) rename {src => core/src}/main/java/org/piwik/java/tracking/PiwikRequest.java (100%) rename {src => core/src}/main/java/org/piwik/java/tracking/PiwikTracker.java (100%) rename {src => core/src}/main/java/org/piwik/java/tracking/package-info.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/AuthTokenTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/CustomVariableTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/EcommerceItemTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/InvalidUrlExceptionTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/MatomoExceptionTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/MatomoLocaleTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/MatomoRequestTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/MatomoTrackerIT.java (93%) rename {src => core/src}/test/java/org/matomo/java/tracking/PiwikDateTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/PiwikLocaleTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/PiwikRequestTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/PiwikTrackerIT.java (98%) rename {src => core/src}/test/java/org/matomo/java/tracking/ProxyAuthenticatorTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/QueryCreatorTest.java (83%) rename {src => core/src}/test/java/org/matomo/java/tracking/RequestValidatorTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/SenderIT.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/TrackerConfigurationTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java (60%) rename {src => core/src}/test/java/org/matomo/java/tracking/TrustingHostnameVerifierTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/TrustingX509TrustManagerTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/parameters/AcceptLanguageTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/parameters/CountryTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/parameters/CustomVariableTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/parameters/CustomVariablesTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/parameters/DeviceResolutionTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/parameters/EcommerceItemsTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/parameters/HexTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/parameters/UniqueIdTest.java (100%) rename {src => core/src}/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java (100%) rename {src => core/src}/test/java/org/piwik/java/tracking/CustomVariableTest.java (100%) rename {src => core/src}/test/java/org/piwik/java/tracking/EcommerceItemTest.java (100%) create mode 100644 docker-compose.yml create mode 100644 test/pom.xml create mode 100644 test/src/main/java/org/matomo/java/tracking/MatomoJavaTrackerTest.java diff --git a/.gitignore b/.gitignore index 8a167ef5..60bd73bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,20 +1,13 @@ -# General System Files # *~ *\.DS_Store *#*# *.swp info - -# Package Files # **/target/ - -# virtual machine crash logs, see https://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* - -# IDEA files *.iml .idea - -# Eclipse files .classpath .project +test/logs/* +!test/logs/.gitkeep \ No newline at end of file diff --git a/README.md b/README.md index 7d30c91d..40f4e110 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,8 @@ Projects that use Matomo Java Tracker: ## What Is New? -Do you still use Matomo Java Tracker 2.x? We created version 3, that is compatible with Matomo 4 and 5 and contains fewer +Do you still use Matomo Java Tracker 2.x? We created version 3, that is compatible with Matomo 4 and 5 and contains +fewer dependencies. Release notes can be found here: https://github.com/matomo-org/matomo-java-tracker/releases Here are the most important changes: @@ -132,12 +133,11 @@ import org.matomo.java.tracking.MatomoRequest; public class YourImplementation { public void yourMethod() { - MatomoRequest request = MatomoRequest - .builder() - .siteId(42) - .actionUrl("https://www.mydomain.com/signup") - .actionName("Signup") - .build(); + MatomoRequest request = MatomoRequest.builder() + .siteId(42) + .actionUrl("https://www.mydomain.com/signup") + .actionName("Signup") + .build(); } } @@ -157,11 +157,11 @@ Per default every request has the following default parameters: Overwrite these properties as desired. Note that if you want to be able to track campaigns using *Referrers > Campaigns*, you must add the correct -URL parameters to your actionUrl. For example, +URL parameters to your actionUrl. For matomoJavaTrackerTest, ```java -package example; +package matomoJavaTrackerTest; import org.matomo.java.tracking.MatomoRequest; @@ -169,12 +169,12 @@ public class YourImplementation { public void yourMethod() { - MatomoRequest request = MatomoRequest - .builder() - .siteId(42) - .actionUrl("https://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore") // include the query parameters to the url - .actionName("LearnMore") - .build(); + MatomoRequest request = MatomoRequest.builder() + .siteId(42) + .actionUrl( + "https://matomoJavaTrackerTest.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore") // include the query parameters to the url + .actionName("LearnMore") + .build(); } } @@ -183,7 +183,7 @@ public class YourImplementation { See [Tracking Campaigns](https://matomo.org/docs/tracking-campaigns/) for more information. All HTTP query parameters denoted on the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate -getters and setters. See [MatomoRequest](src/main/java/org/matomo/java/tracking/MatomoRequest.java) for the mappings of +getters and setters. See [MatomoRequest](core/src/main/java/org/matomo/java/tracking/MatomoRequest.java) for the mappings of the parameters to their corresponding attributes. @@ -194,7 +194,7 @@ Requests are validated prior to sending. If a request is invalid, a `MatomoExcep Create a MatomoTracker using the constructor ```java -package example; +package matomoJavaTrackerTest; import java.net.URI; import org.matomo.java.tracking.MatomoTracker; @@ -204,10 +204,10 @@ public class YourImplementation { public void yourMethod() { - MatomoTracker tracker = new MatomoTracker(TrackerConfiguration - .builder() - .apiEndpoint(URI.create("https://your-matomo-domain.tld/matomo.php")) - .build()); + MatomoTracker tracker = new MatomoTracker(TrackerConfiguration.builder() + .apiEndpoint(URI.create( + "https://your-matomo-domain.tld/matomo.php")) + .build()); } @@ -247,7 +247,7 @@ The Matomo Tracker currently supports the following builder methods: To send a single request synchronously via GET, call ```java -package example; +package matomoJavaTrackerTest; import org.apache.http.HttpResponse; import org.matomo.java.tracking.MatomoRequest; @@ -262,17 +262,16 @@ public class YourImplementation { public void yourMethod() { - MatomoRequest request = MatomoRequest - .builder() - .siteId(42) - .actionUrl("https://www.mydomain.com/some/page") - .actionName("Signup") - .build(); + MatomoRequest request = MatomoRequest.builder() + .siteId(42) + .actionUrl("https://www.mydomain.com/some/page") + .actionName("Signup") + .build(); - MatomoTracker tracker = new MatomoTracker(TrackerConfiguration - .builder() - .apiEndpoint(URI.create("https://your-matomo-domain.tld/matomo.php")) - .build()); + MatomoTracker tracker = new MatomoTracker(TrackerConfiguration.builder() + .apiEndpoint(URI.create( + "https://your-matomo-domain.tld/matomo.php")) + .build()); tracker.sendRequestAsync(request); @@ -285,7 +284,7 @@ If you have multiple requests to wish to track, it may be more efficient to send send a bulk request. Place your requests in an _Iterable_ data structure and call ```java -package example; +package matomoJavaTrackerTest; import org.matomo.java.tracking.MatomoRequest; import org.matomo.java.tracking.MatomoTracker; @@ -305,10 +304,10 @@ public class YourImplementation { requests.add(builder.actionUrl("https://www.mydomain.com/some/page").actionName("Some Page").build()); requests.add(builder.actionUrl("https://www.mydomain.com/another/page").actionName("Another Page").build()); - MatomoTracker tracker = new MatomoTracker(TrackerConfiguration - .builder() - .apiEndpoint(URI.create("https://your-matomo-domain.tld/matomo.php")) - .build()); + MatomoTracker tracker = new MatomoTracker(TrackerConfiguration.builder() + .apiEndpoint(URI.create( + "https://your-matomo-domain.tld/matomo.php")) + .build()); tracker.sendBulkRequestAsync(requests); @@ -322,7 +321,7 @@ If some of the parameters that you've specified in the bulk request require Auth the bulk request through ```java -package example; +package matomoJavaTrackerTest; import org.apache.http.HttpResponse; import org.matomo.java.tracking.MatomoLocale; @@ -343,16 +342,15 @@ public class YourImplementation { Collection requests = new ArrayList<>(); MatomoRequestBuilder builder = MatomoRequest.request().siteId(42); requests.add(builder.actionUrl("https://www.mydomain.com/some/page").actionName("Some Page").build()); - requests.add(builder - .actionUrl("https://www.mydomain.com/another/page") - .actionName("Another Page") - .visitorCountry(new MatomoLocale(Locale.GERMANY)) - .build()); + requests.add(builder.actionUrl("https://www.mydomain.com/another/page") + .actionName("Another Page") + .visitorCountry(new MatomoLocale(Locale.GERMANY)) + .build()); - MatomoTracker tracker = new MatomoTracker(TrackerConfiguration - .builder() - .apiEndpoint(URI.create("https://your-matomo-domain.tld/matomo.php")) - .build()); + MatomoTracker tracker = new MatomoTracker(TrackerConfiguration.builder() + .apiEndpoint(URI.create( + "https://your-matomo-domain.tld/matomo.php")) + .build()); // second parameter is authentication token need for country override tracker.sendBulkRequestAsync(requests, "33dc3f2536d3025974cccb4b4d2d98f4"); @@ -448,7 +446,7 @@ This project can be tested and built by calling mvn install ``` -The built jars and javadoc can be found in `target`. By using the Maven goal `install, a snapshot +This repository contains the modules `core` and `test`. The built jars and javadoc can be found in `target`. By using the Maven goal `install, a snapshot version can be used in your local Maven repository for testing purposes, e.g. ```xml @@ -460,6 +458,55 @@ version can be used in your local Maven repository for testing purposes, e.g. ``` +## Testing on a local Matomo instance + +To start a local Matomo instance for testing, you can use the docker-compose file in the root directory of this project. +Start the docker containers with + +```shell +docker-compose up -d +``` +You need to adapt your config.ini.php file and change +the following line: + +```ini +[General] +trusted_hosts[] = "localhost:8080" +``` + +to + +```ini +[General] +trusted_hosts[] = "localhost:8080" +``` + +After that you can access Matomo at http://localhost:8080. You have to set up Matomo first. The database credentials are +`matomo` and `matomo`. The database name is `matomo`. The (internal) database host address is `database`. The database +port is `3306`. Set the URL to http://localhost and enable ecommerce. + +The following snippets helps you to do this quickly: + +```shell +docker-compose exec matomo sed -i 's/localhost/localhost:8080/g' /var/www/html/config/config.ini.php +``` + +After the installation you can run `MatomoJavaTrackerTest` in the module `test` to test the tracker. It will send multiple randomized +requests to the local Matomo instance. + +To enable debug logging, you append the following line to the `config.ini.php` file: + +```ini +[Tracker] +debug = 1 +``` + +Use the following snippet to do this: + +```shell +docker-compose exec matomo sh -c 'echo -e "\n\n[Tracker]\ndebug = 1\n" >> /var/www/html/config/config.ini.php' +``` + ## Versioning We use [SemVer](https://semver.org/) for versioning. For the versions available, see diff --git a/core/pom.xml b/core/pom.xml new file mode 100644 index 00000000..f4cc6cc8 --- /dev/null +++ b/core/pom.xml @@ -0,0 +1,62 @@ + + 4.0.0 + + + org.piwik.java.tracking + matomo-java-tracker-parent + 3.0.0-rc3-SNAPSHOT + ../pom.xml + + + matomo-java-tracker + jar + + Matomo Java Tracker + Official Java implementation of the Matomo Tracking HTTP API. + + + + com.github.spotbugs + spotbugs-annotations + 4.8.0 + provided + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.projectlombok + lombok + ${lombok.version} + provided + + + org.junit.jupiter + junit-jupiter + 5.10.0 + test + + + org.assertj + assertj-core + 3.24.2 + test + + + org.slf4j + slf4j-simple + ${slf4j.version} + test + + + com.github.tomakehurst + wiremock + 2.27.2 + test + + + + + diff --git a/src/main/java/lombok.config b/core/src/main/java/lombok.config similarity index 100% rename from src/main/java/lombok.config rename to core/src/main/java/lombok.config diff --git a/src/main/java/org/matomo/java/tracking/AuthToken.java b/core/src/main/java/org/matomo/java/tracking/AuthToken.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/AuthToken.java rename to core/src/main/java/org/matomo/java/tracking/AuthToken.java diff --git a/src/main/java/org/matomo/java/tracking/CustomVariable.java b/core/src/main/java/org/matomo/java/tracking/CustomVariable.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/CustomVariable.java rename to core/src/main/java/org/matomo/java/tracking/CustomVariable.java diff --git a/core/src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java b/core/src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java new file mode 100644 index 00000000..75366390 --- /dev/null +++ b/core/src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java @@ -0,0 +1,12 @@ +package org.matomo.java.tracking; + +import java.util.concurrent.ThreadFactory; + +class DaemonThreadFactory implements ThreadFactory { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + return thread; + } +} \ No newline at end of file diff --git a/src/main/java/org/matomo/java/tracking/EcommerceItem.java b/core/src/main/java/org/matomo/java/tracking/EcommerceItem.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/EcommerceItem.java rename to core/src/main/java/org/matomo/java/tracking/EcommerceItem.java diff --git a/src/main/java/org/matomo/java/tracking/InvalidUrlException.java b/core/src/main/java/org/matomo/java/tracking/InvalidUrlException.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/InvalidUrlException.java rename to core/src/main/java/org/matomo/java/tracking/InvalidUrlException.java diff --git a/src/main/java/org/matomo/java/tracking/MatomoDate.java b/core/src/main/java/org/matomo/java/tracking/MatomoDate.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/MatomoDate.java rename to core/src/main/java/org/matomo/java/tracking/MatomoDate.java diff --git a/src/main/java/org/matomo/java/tracking/MatomoException.java b/core/src/main/java/org/matomo/java/tracking/MatomoException.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/MatomoException.java rename to core/src/main/java/org/matomo/java/tracking/MatomoException.java diff --git a/src/main/java/org/matomo/java/tracking/MatomoLocale.java b/core/src/main/java/org/matomo/java/tracking/MatomoLocale.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/MatomoLocale.java rename to core/src/main/java/org/matomo/java/tracking/MatomoLocale.java diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java similarity index 98% rename from src/main/java/org/matomo/java/tracking/MatomoRequest.java rename to core/src/main/java/org/matomo/java/tracking/MatomoRequest.java index c009b1f0..611a1916 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -503,14 +503,14 @@ public class MatomoRequest { /** * An override value for the country. Must be a two-letter ISO 3166 Alpha-2 country code. */ - @TrackingParameter(name = "country") + @TrackingParameter(name = "country", maxLength = 2) private Country visitorCountry; /** * An override value for the region. Should be set to a ISO 3166-2 region code, which are used by MaxMind's and * DB-IP's GeoIP2 databases. See here for a list of them for every country. */ - @TrackingParameter(name = "region") + @TrackingParameter(name = "region", maxLength = 2) private String visitorRegion; /** @@ -576,6 +576,14 @@ public class MatomoRequest { @Default private RandomValue randomValue = RandomValue.random(); + + /** + * Meant to hold a random value that is generated before each request. Using it helps avoid the tracking request + * being cached by the browser or a proxy. + */ + @TrackingParameter(name = "debug") + private Boolean debug; + private Iterable dimensions; private Map> additionalParameters; diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java b/core/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java rename to core/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java diff --git a/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java similarity index 96% rename from src/main/java/org/matomo/java/tracking/MatomoTracker.java rename to core/src/main/java/org/matomo/java/tracking/MatomoTracker.java index 7fc47268..cdc501fa 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoTracker.java +++ b/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -112,7 +112,7 @@ public MatomoTracker( sender = new Sender( trackerConfiguration, new QueryCreator(trackerConfiguration), - Executors.newFixedThreadPool(trackerConfiguration.getThreadPoolSize()) + Executors.newFixedThreadPool(trackerConfiguration.getThreadPoolSize(), new DaemonThreadFactory()) ); } @@ -141,9 +141,7 @@ public MatomoTracker( * {@link #sendRequestAsync(MatomoRequest)} or {@link #sendBulkRequestAsync(Iterable)} instead. * * @param request request to send. must not be null - * @deprecated use sendRequestAsync instead */ - @Deprecated public void sendRequest(@NonNull MatomoRequest request) { if (trackerConfiguration.isEnabled()) { log.debug("Sending request via GET: {}", request); @@ -207,6 +205,19 @@ private void validate( } } + /** + * Send multiple requests in a single HTTP POST call. + * + *

More efficient than sending several individual requests. If you want to send a single request, use + * {@link #sendRequest(MatomoRequest)} instead. If you want to send multiple requests asynchronously, use + * {@link #sendBulkRequestAsync(Iterable)} instead. + * + * @param requests the requests to send + */ + public void sendBulkRequest(MatomoRequest... requests) { + sendBulkRequest(Arrays.asList(requests), null); + } + /** * Send multiple requests in a single HTTP POST call. * diff --git a/src/main/java/org/matomo/java/tracking/ProxyAuthenticator.java b/core/src/main/java/org/matomo/java/tracking/ProxyAuthenticator.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/ProxyAuthenticator.java rename to core/src/main/java/org/matomo/java/tracking/ProxyAuthenticator.java diff --git a/src/main/java/org/matomo/java/tracking/QueryCreator.java b/core/src/main/java/org/matomo/java/tracking/QueryCreator.java similarity index 89% rename from src/main/java/org/matomo/java/tracking/QueryCreator.java rename to core/src/main/java/org/matomo/java/tracking/QueryCreator.java index d15a6011..f324219e 100644 --- a/src/main/java/org/matomo/java/tracking/QueryCreator.java +++ b/core/src/main/java/org/matomo/java/tracking/QueryCreator.java @@ -28,8 +28,7 @@ @RequiredArgsConstructor class QueryCreator { - private static final TrackingParameterMethod[] TRACKING_PARAMETER_METHODS = - initializeTrackingParameterMethods(); + private static final TrackingParameterMethod[] TRACKING_PARAMETER_METHODS = initializeTrackingParameterMethods(); private final TrackerConfiguration trackerConfiguration; @@ -45,19 +44,16 @@ private static TrackingParameterMethod[] initializeTrackingParameterMethods() { } private static void addMethods( - Collection methods, - Member member, - TrackingParameter trackingParameter + Collection methods, Member member, TrackingParameter trackingParameter ) { try { - for (PropertyDescriptor pd : Introspector - .getBeanInfo(MatomoRequest.class) - .getPropertyDescriptors()) { + for (PropertyDescriptor pd : Introspector.getBeanInfo(MatomoRequest.class).getPropertyDescriptors()) { if (member.getName().equals(pd.getName())) { String regex = trackingParameter.regex(); methods.add(TrackingParameterMethod .builder() .parameterName(trackingParameter.name()) + .maxLength(trackingParameter.maxLength()) .method(pd.getReadMethod()) .pattern(regex == null || regex.isEmpty() || regex.trim().isEmpty() ? null : Pattern.compile(trackingParameter.regex())) @@ -70,10 +66,7 @@ private static void addMethods( } String createQuery( - @NonNull - MatomoRequest request, - @Nullable - String authToken + @NonNull MatomoRequest request, @Nullable String authToken ) { StringBuilder query = new StringBuilder(100); if (request.getSiteId() == null) { @@ -84,15 +77,14 @@ String createQuery( if (authToken.length() != 32) { throw new IllegalArgumentException("Auth token must be exactly 32 characters long"); } + appendAmpersand(query); query.append("token_auth=").append(authToken); } for (TrackingParameterMethod method : TRACKING_PARAMETER_METHODS) { appendParameter(method, request, query); } if (request.getAdditionalParameters() != null) { - for (Entry> entry : request - .getAdditionalParameters() - .entrySet()) { + for (Entry> entry : request.getAdditionalParameters().entrySet()) { for (Object value : entry.getValue()) { if (value != null && !value.toString().trim().isEmpty()) { appendAmpersand(query); @@ -105,7 +97,7 @@ String createQuery( int i = 0; for (Object dimension : request.getDimensions()) { appendAmpersand(query); - query.append("dimension").append(i + 1).append('=').append(dimension); + query.append("dimension").append(i + 1).append('=').append(encode(dimension.toString())); i++; } } @@ -147,8 +139,7 @@ private static void appendParameter( @NonNull private static String encode( - @NonNull - String parameterValue + @NonNull String parameterValue ) { try { return URLEncoder.encode(parameterValue, "UTF-8"); diff --git a/src/main/java/org/matomo/java/tracking/RequestValidator.java b/core/src/main/java/org/matomo/java/tracking/RequestValidator.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/RequestValidator.java rename to core/src/main/java/org/matomo/java/tracking/RequestValidator.java diff --git a/src/main/java/org/matomo/java/tracking/Sender.java b/core/src/main/java/org/matomo/java/tracking/Sender.java similarity index 79% rename from src/main/java/org/matomo/java/tracking/Sender.java rename to core/src/main/java/org/matomo/java/tracking/Sender.java index 7d87b5bd..44940e67 100644 --- a/src/main/java/org/matomo/java/tracking/Sender.java +++ b/core/src/main/java/org/matomo/java/tracking/Sender.java @@ -31,6 +31,7 @@ import java.util.concurrent.Executor; import java.util.stream.Collectors; import java.util.stream.StreamSupport; +import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; @@ -41,10 +42,8 @@ @RequiredArgsConstructor class Sender { - private static final TrustManager[] TRUST_ALL_MANAGERS = - new TrustManager[] {new TrustingX509TrustManager()}; - public static final TrustingHostnameVerifier TRUSTING_HOSTNAME_VERIFIER = - new TrustingHostnameVerifier(); + private static final TrustManager[] TRUST_ALL_MANAGERS = {new TrustingX509TrustManager()}; + private static final HostnameVerifier TRUSTING_HOSTNAME_VERIFIER = new TrustingHostnameVerifier(); private final TrackerConfiguration trackerConfiguration; @@ -56,8 +55,7 @@ class Sender { @NonNull CompletableFuture sendSingleAsync( - @NonNull - MatomoRequest request + @NonNull MatomoRequest request ) { return CompletableFuture.supplyAsync(() -> { sendSingle(request); @@ -66,8 +64,7 @@ CompletableFuture sendSingleAsync( } void sendSingle( - @NonNull - MatomoRequest request + @NonNull MatomoRequest request ) { String authToken = AuthToken.determineAuthToken(null, singleton(request), trackerConfiguration); RequestValidator.validate(request, authToken); @@ -75,10 +72,7 @@ void sendSingle( URI apiEndpoint = trackerConfiguration.getApiEndpoint(); try { connection = openConnection(apiEndpoint - .resolve(String.format("%s?%s", - apiEndpoint.getPath(), - queryCreator.createQuery(request, authToken) - )) + .resolve(String.format("%s?%s", apiEndpoint.getPath(), queryCreator.createQuery(request, authToken))) .toURL()); } catch (MalformedURLException e) { throw new InvalidUrlException(e); @@ -98,8 +92,7 @@ void sendSingle( private HttpURLConnection openConnection(URL url) { HttpURLConnection connection; try { - if (isEmpty(trackerConfiguration.getProxyHost()) - || trackerConfiguration.getProxyPort() <= 0) { + if (isEmpty(trackerConfiguration.getProxyHost()) || trackerConfiguration.getProxyPort() <= 0) { log.debug("Proxy host or proxy port not configured. Will create connection without proxy"); connection = (HttpURLConnection) url.openConnection(); } else { @@ -115,8 +108,7 @@ private HttpURLConnection openConnection(URL url) { } private void applySslConfiguration( - @NonNull - HttpsURLConnection connection + @NonNull HttpsURLConnection connection ) { requireNonNull(connection, "Connection must not be null"); if (trackerConfiguration.isDisableSslCertValidation()) { @@ -134,28 +126,22 @@ private void applySslConfiguration( } private HttpURLConnection openProxiedConnection( - @NonNull - URL url + @NonNull URL url ) throws IOException { requireNonNull(url, "URL must not be null"); requireNonNull(trackerConfiguration.getProxyHost(), "Proxy host must not be null"); if (trackerConfiguration.getProxyPort() <= 0) { throw new IllegalArgumentException("Proxy port must be configured"); } - InetSocketAddress proxyAddress = new InetSocketAddress(trackerConfiguration.getProxyHost(), - trackerConfiguration.getProxyPort() - ); + InetSocketAddress proxyAddress = + new InetSocketAddress(trackerConfiguration.getProxyHost(), trackerConfiguration.getProxyPort()); Proxy proxy = new Proxy(Proxy.Type.HTTP, proxyAddress); - if (!isEmpty(trackerConfiguration.getProxyUserName()) - && !isEmpty(trackerConfiguration.getProxyPassword())) { + if (!isEmpty(trackerConfiguration.getProxyUserName()) && !isEmpty(trackerConfiguration.getProxyPassword())) { Authenticator.setDefault(new ProxyAuthenticator(trackerConfiguration.getProxyUserName(), trackerConfiguration.getProxyPassword() )); } - log.debug("Using proxy {} on port {}", - trackerConfiguration.getProxyHost(), - trackerConfiguration.getProxyPort() - ); + log.debug("Using proxy {} on port {}", trackerConfiguration.getProxyHost(), trackerConfiguration.getProxyPort()); return (HttpURLConnection) url.openConnection(proxy); } @@ -171,30 +157,25 @@ private void configureAgentsAndTimeouts(HttpURLConnection connection) { } private void checkResponse(HttpURLConnection connection) throws IOException { - if (connection.getResponseCode() > 399) { + int responseCode = connection.getResponseCode(); + if (responseCode > 399) { if (trackerConfiguration.isLogFailedTracking()) { - log.error("Received error code {}", connection.getResponseCode()); + log.error("Received HTTP error code {} for URL {}", responseCode, connection.getURL()); } - throw new MatomoException( - "Tracking endpoint responded with code " + connection.getResponseCode()); + throw new MatomoException(String.format("Tracking endpoint responded with code %d", responseCode)); } } private static boolean isEmpty( - @Nullable - String str + @Nullable String str ) { return str == null || str.isEmpty() || str.trim().isEmpty(); } void sendBulk( - @NonNull - Iterable requests, - @Nullable - String overrideAuthToken + @NonNull Iterable requests, @Nullable String overrideAuthToken ) { - String authToken = - AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); + String authToken = AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); sendBulk(StreamSupport.stream(requests.spliterator(), false).map(request -> { RequestValidator.validate(request, authToken); return queryCreator.createQuery(request, null); @@ -202,10 +183,7 @@ void sendBulk( } private void sendBulk( - @NonNull - Collection queries, - @Nullable - String authToken + @NonNull Collection queries, @Nullable String authToken ) { requireNonNull(queries, "Queries must not be null"); HttpURLConnection connection; @@ -216,9 +194,7 @@ private void sendBulk( } preparePostConnection(connection); configureAgentsAndTimeouts(connection); - log.debug("Sending bulk request using URI {} asynchronously", - trackerConfiguration.getApiEndpoint() - ); + log.debug("Sending bulk request using URI {} asynchronously", trackerConfiguration.getApiEndpoint()); OutputStream outputStream = null; try { connection.connect(); @@ -253,10 +229,7 @@ private static void preparePostConnection(HttpURLConnection connection) { } private static byte[] createPayload( - @NonNull - Collection queries, - @Nullable - String authToken + @NonNull Collection queries, @Nullable String authToken ) { requireNonNull(queries, "Queries must not be null"); StringBuilder payload = new StringBuilder("{\"requests\":["); @@ -277,13 +250,9 @@ private static byte[] createPayload( @NonNull CompletableFuture sendBulkAsync( - @NonNull - Iterable requests, - @Nullable - String overrideAuthToken + @NonNull Iterable requests, @Nullable String overrideAuthToken ) { - String authToken = - AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); + String authToken = AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); synchronized (queries) { for (MatomoRequest request : requests) { RequestValidator.validate(request, authToken); @@ -296,8 +265,7 @@ CompletableFuture sendBulkAsync( @Nullable private Void sendBulkAsync( - @Nullable - String authToken + @Nullable String authToken ) { synchronized (queries) { if (!queries.isEmpty()) { diff --git a/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java b/core/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/TrackerConfiguration.java rename to core/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java diff --git a/src/main/java/org/matomo/java/tracking/TrackingParameter.java b/core/src/main/java/org/matomo/java/tracking/TrackingParameter.java similarity index 91% rename from src/main/java/org/matomo/java/tracking/TrackingParameter.java rename to core/src/main/java/org/matomo/java/tracking/TrackingParameter.java index 5e32184a..f94e226e 100644 --- a/src/main/java/org/matomo/java/tracking/TrackingParameter.java +++ b/core/src/main/java/org/matomo/java/tracking/TrackingParameter.java @@ -20,4 +20,6 @@ String regex() default ""; + int maxLength() default Integer.MAX_VALUE; + } diff --git a/src/main/java/org/matomo/java/tracking/TrackingParameterMethod.java b/core/src/main/java/org/matomo/java/tracking/TrackingParameterMethod.java similarity index 60% rename from src/main/java/org/matomo/java/tracking/TrackingParameterMethod.java rename to core/src/main/java/org/matomo/java/tracking/TrackingParameterMethod.java index ae18e1db..bbfca14c 100644 --- a/src/main/java/org/matomo/java/tracking/TrackingParameterMethod.java +++ b/core/src/main/java/org/matomo/java/tracking/TrackingParameterMethod.java @@ -10,6 +10,7 @@ import java.lang.reflect.Method; import java.util.regex.Pattern; import lombok.Builder; +import lombok.NonNull; import lombok.Value; @Builder @@ -22,15 +23,23 @@ class TrackingParameterMethod { Pattern pattern; - void validateParameterValue(Object parameterValue) { + int maxLength; + + void validateParameterValue(@NonNull Object parameterValue) { if (pattern != null && parameterValue instanceof CharSequence && !pattern .matcher((CharSequence) parameterValue) .matches()) { - throw new IllegalArgumentException(String.format("Invalid value for %s. Must match regex %s", + throw new MatomoException(String.format("Invalid value for %s. Must match regex %s", parameterName, pattern )); } + if (parameterValue.toString().length() > maxLength) { + throw new MatomoException(String.format("Invalid value for %s. Must be less or equal than %d characters", + parameterName, + maxLength + )); + } } } diff --git a/src/main/java/org/matomo/java/tracking/TrustingHostnameVerifier.java b/core/src/main/java/org/matomo/java/tracking/TrustingHostnameVerifier.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/TrustingHostnameVerifier.java rename to core/src/main/java/org/matomo/java/tracking/TrustingHostnameVerifier.java diff --git a/src/main/java/org/matomo/java/tracking/TrustingX509TrustManager.java b/core/src/main/java/org/matomo/java/tracking/TrustingX509TrustManager.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/TrustingX509TrustManager.java rename to core/src/main/java/org/matomo/java/tracking/TrustingX509TrustManager.java diff --git a/src/main/java/org/matomo/java/tracking/package-info.java b/core/src/main/java/org/matomo/java/tracking/package-info.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/package-info.java rename to core/src/main/java/org/matomo/java/tracking/package-info.java diff --git a/src/main/java/org/matomo/java/tracking/parameters/AcceptLanguage.java b/core/src/main/java/org/matomo/java/tracking/parameters/AcceptLanguage.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/parameters/AcceptLanguage.java rename to core/src/main/java/org/matomo/java/tracking/parameters/AcceptLanguage.java diff --git a/src/main/java/org/matomo/java/tracking/parameters/Country.java b/core/src/main/java/org/matomo/java/tracking/parameters/Country.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/parameters/Country.java rename to core/src/main/java/org/matomo/java/tracking/parameters/Country.java diff --git a/src/main/java/org/matomo/java/tracking/parameters/CustomVariable.java b/core/src/main/java/org/matomo/java/tracking/parameters/CustomVariable.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/parameters/CustomVariable.java rename to core/src/main/java/org/matomo/java/tracking/parameters/CustomVariable.java diff --git a/src/main/java/org/matomo/java/tracking/parameters/CustomVariables.java b/core/src/main/java/org/matomo/java/tracking/parameters/CustomVariables.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/parameters/CustomVariables.java rename to core/src/main/java/org/matomo/java/tracking/parameters/CustomVariables.java diff --git a/src/main/java/org/matomo/java/tracking/parameters/DeviceResolution.java b/core/src/main/java/org/matomo/java/tracking/parameters/DeviceResolution.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/parameters/DeviceResolution.java rename to core/src/main/java/org/matomo/java/tracking/parameters/DeviceResolution.java diff --git a/src/main/java/org/matomo/java/tracking/parameters/EcommerceItem.java b/core/src/main/java/org/matomo/java/tracking/parameters/EcommerceItem.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/parameters/EcommerceItem.java rename to core/src/main/java/org/matomo/java/tracking/parameters/EcommerceItem.java diff --git a/src/main/java/org/matomo/java/tracking/parameters/EcommerceItems.java b/core/src/main/java/org/matomo/java/tracking/parameters/EcommerceItems.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/parameters/EcommerceItems.java rename to core/src/main/java/org/matomo/java/tracking/parameters/EcommerceItems.java diff --git a/src/main/java/org/matomo/java/tracking/parameters/Hex.java b/core/src/main/java/org/matomo/java/tracking/parameters/Hex.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/parameters/Hex.java rename to core/src/main/java/org/matomo/java/tracking/parameters/Hex.java diff --git a/src/main/java/org/matomo/java/tracking/parameters/RandomValue.java b/core/src/main/java/org/matomo/java/tracking/parameters/RandomValue.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/parameters/RandomValue.java rename to core/src/main/java/org/matomo/java/tracking/parameters/RandomValue.java diff --git a/src/main/java/org/matomo/java/tracking/parameters/UniqueId.java b/core/src/main/java/org/matomo/java/tracking/parameters/UniqueId.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/parameters/UniqueId.java rename to core/src/main/java/org/matomo/java/tracking/parameters/UniqueId.java diff --git a/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java b/core/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/parameters/VisitorId.java rename to core/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java diff --git a/src/main/java/org/matomo/java/tracking/parameters/package-info.java b/core/src/main/java/org/matomo/java/tracking/parameters/package-info.java similarity index 100% rename from src/main/java/org/matomo/java/tracking/parameters/package-info.java rename to core/src/main/java/org/matomo/java/tracking/parameters/package-info.java diff --git a/src/main/java/org/piwik/java/tracking/CustomVariable.java b/core/src/main/java/org/piwik/java/tracking/CustomVariable.java similarity index 100% rename from src/main/java/org/piwik/java/tracking/CustomVariable.java rename to core/src/main/java/org/piwik/java/tracking/CustomVariable.java diff --git a/src/main/java/org/piwik/java/tracking/EcommerceItem.java b/core/src/main/java/org/piwik/java/tracking/EcommerceItem.java similarity index 100% rename from src/main/java/org/piwik/java/tracking/EcommerceItem.java rename to core/src/main/java/org/piwik/java/tracking/EcommerceItem.java diff --git a/src/main/java/org/piwik/java/tracking/PiwikDate.java b/core/src/main/java/org/piwik/java/tracking/PiwikDate.java similarity index 100% rename from src/main/java/org/piwik/java/tracking/PiwikDate.java rename to core/src/main/java/org/piwik/java/tracking/PiwikDate.java diff --git a/src/main/java/org/piwik/java/tracking/PiwikLocale.java b/core/src/main/java/org/piwik/java/tracking/PiwikLocale.java similarity index 100% rename from src/main/java/org/piwik/java/tracking/PiwikLocale.java rename to core/src/main/java/org/piwik/java/tracking/PiwikLocale.java diff --git a/src/main/java/org/piwik/java/tracking/PiwikRequest.java b/core/src/main/java/org/piwik/java/tracking/PiwikRequest.java similarity index 100% rename from src/main/java/org/piwik/java/tracking/PiwikRequest.java rename to core/src/main/java/org/piwik/java/tracking/PiwikRequest.java diff --git a/src/main/java/org/piwik/java/tracking/PiwikTracker.java b/core/src/main/java/org/piwik/java/tracking/PiwikTracker.java similarity index 100% rename from src/main/java/org/piwik/java/tracking/PiwikTracker.java rename to core/src/main/java/org/piwik/java/tracking/PiwikTracker.java diff --git a/src/main/java/org/piwik/java/tracking/package-info.java b/core/src/main/java/org/piwik/java/tracking/package-info.java similarity index 100% rename from src/main/java/org/piwik/java/tracking/package-info.java rename to core/src/main/java/org/piwik/java/tracking/package-info.java diff --git a/src/test/java/org/matomo/java/tracking/AuthTokenTest.java b/core/src/test/java/org/matomo/java/tracking/AuthTokenTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/AuthTokenTest.java rename to core/src/test/java/org/matomo/java/tracking/AuthTokenTest.java diff --git a/src/test/java/org/matomo/java/tracking/CustomVariableTest.java b/core/src/test/java/org/matomo/java/tracking/CustomVariableTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/CustomVariableTest.java rename to core/src/test/java/org/matomo/java/tracking/CustomVariableTest.java diff --git a/src/test/java/org/matomo/java/tracking/EcommerceItemTest.java b/core/src/test/java/org/matomo/java/tracking/EcommerceItemTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/EcommerceItemTest.java rename to core/src/test/java/org/matomo/java/tracking/EcommerceItemTest.java diff --git a/src/test/java/org/matomo/java/tracking/InvalidUrlExceptionTest.java b/core/src/test/java/org/matomo/java/tracking/InvalidUrlExceptionTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/InvalidUrlExceptionTest.java rename to core/src/test/java/org/matomo/java/tracking/InvalidUrlExceptionTest.java diff --git a/src/test/java/org/matomo/java/tracking/MatomoExceptionTest.java b/core/src/test/java/org/matomo/java/tracking/MatomoExceptionTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/MatomoExceptionTest.java rename to core/src/test/java/org/matomo/java/tracking/MatomoExceptionTest.java diff --git a/src/test/java/org/matomo/java/tracking/MatomoLocaleTest.java b/core/src/test/java/org/matomo/java/tracking/MatomoLocaleTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/MatomoLocaleTest.java rename to core/src/test/java/org/matomo/java/tracking/MatomoLocaleTest.java diff --git a/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java b/core/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java rename to core/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java diff --git a/src/test/java/org/matomo/java/tracking/MatomoRequestTest.java b/core/src/test/java/org/matomo/java/tracking/MatomoRequestTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/MatomoRequestTest.java rename to core/src/test/java/org/matomo/java/tracking/MatomoRequestTest.java diff --git a/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/core/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java similarity index 93% rename from src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java rename to core/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java index b0b68b24..ab55026c 100644 --- a/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java +++ b/core/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java @@ -368,35 +368,6 @@ void doesNothingIfNotEnabled() throws Exception { } - @Test - void example() { - - // Create the tracker configuration - TrackerConfiguration configuration = TrackerConfiguration.builder() - .apiEndpoint(URI.create("https://your-domain.net/matomo/matomo.php")) - .defaultSiteId(42) // if not explicitly specified by action - .build(); - - // Prepare the tracker (stateless - can be used for multiple requests) - MatomoTracker tracker = new MatomoTracker(configuration); - - MatomoRequest request = MatomoRequest - .builder() - .actionName("User Profile / Upload Profile Picture") - .actionUrl("https://your-domain.net/user/profile/picture") - .visitorId(VisitorId.fromString("some@email-adress.org")) - // ... - .build(); - - // Send the request asynchronously (non-blocking) as an HTTP POST request (payload is JSON and contains the - // tracking parameters) - CompletableFuture future = tracker.sendBulkRequestAsync(request); - - // If you want to ensure the request was sent without exceptions: - future.join(); // throws an unchecked exception if the request failed - - } - @Test void reportsErrors() { @@ -424,7 +395,7 @@ void includesDefaultTokenAuth() { assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { wireMockServer.verify(getRequestedFor(urlEqualTo( - "/matomo.php?idsite=42token_auth=fdf6e8461ea9de33176b222519627f78&rec=1&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom")).withHeader("User-Agent", + "/matomo.php?idsite=42&token_auth=fdf6e8461ea9de33176b222519627f78&rec=1&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom")).withHeader("User-Agent", equalTo("MatomoJavaClient") )); }); diff --git a/src/test/java/org/matomo/java/tracking/PiwikDateTest.java b/core/src/test/java/org/matomo/java/tracking/PiwikDateTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/PiwikDateTest.java rename to core/src/test/java/org/matomo/java/tracking/PiwikDateTest.java diff --git a/src/test/java/org/matomo/java/tracking/PiwikLocaleTest.java b/core/src/test/java/org/matomo/java/tracking/PiwikLocaleTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/PiwikLocaleTest.java rename to core/src/test/java/org/matomo/java/tracking/PiwikLocaleTest.java diff --git a/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java b/core/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/PiwikRequestTest.java rename to core/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java diff --git a/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java b/core/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java similarity index 98% rename from src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java rename to core/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java index 1df7512d..bc28ec22 100644 --- a/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java +++ b/core/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java @@ -238,7 +238,7 @@ void createPiwikTrackerWithHostUrlAndProxyHostAndPort() { assertThatThrownBy(() -> piwikTracker.sendRequest(request)) .isInstanceOf(MatomoException.class) - .hasMessage("Could not send request via GET"); + ; } @@ -249,7 +249,7 @@ void createPiwikTrackerWithHostUrlAndProxyHostAndPortAndTimeout() { assertThatThrownBy(() -> piwikTracker.sendRequest(request)) .isInstanceOf(MatomoException.class) - .hasMessage("Could not send request via GET"); + ; } } diff --git a/src/test/java/org/matomo/java/tracking/ProxyAuthenticatorTest.java b/core/src/test/java/org/matomo/java/tracking/ProxyAuthenticatorTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/ProxyAuthenticatorTest.java rename to core/src/test/java/org/matomo/java/tracking/ProxyAuthenticatorTest.java diff --git a/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java b/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java similarity index 83% rename from src/test/java/org/matomo/java/tracking/QueryCreatorTest.java rename to core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java index fd34c068..f31c5837 100644 --- a/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java +++ b/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java @@ -45,7 +45,7 @@ void usesDefaultSiteId() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value"); + "idsite=42&token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value"); } @@ -81,7 +81,7 @@ void usesDefaultTokenAuth() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=f123bfc9a46de0bb5453afdab6f93200&rec=1&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value"); + "idsite=42&token_auth=f123bfc9a46de0bb5453afdab6f93200&rec=1&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value"); } @@ -94,7 +94,7 @@ void overridesDefaultTokenAuth() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=e456bfc9a46de0bb5453afdab6f93200&rec=1&apiv=1&_id=112210f47de98115&token_auth=e456bfc9a46de0bb5453afdab6f93200&send_image=0&rand=random-value"); + "idsite=42&token_auth=e456bfc9a46de0bb5453afdab6f93200&rec=1&apiv=1&_id=112210f47de98115&token_auth=e456bfc9a46de0bb5453afdab6f93200&send_image=0&rand=random-value"); } @@ -117,7 +117,7 @@ void convertsTrueBooleanTo1() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&fla=1&send_image=0&rand=random-value"); + "idsite=42&token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&fla=1&send_image=0&rand=random-value"); } @@ -129,7 +129,7 @@ void convertsFalseBooleanTo0() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&java=0&send_image=0&rand=random-value"); + "idsite=42&token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&java=0&send_image=0&rand=random-value"); } @@ -141,7 +141,7 @@ void encodesUrl() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&url=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fpage%3Ffoo%3Dbar&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value"); + "idsite=42&token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&url=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fpage%3Ffoo%3Dbar&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value"); } @@ -153,7 +153,7 @@ void encodesReferrerUrl() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Freferrer%3Ffoo2%3Dbar2&send_image=0&rand=random-value"); + "idsite=42&token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Freferrer%3Ffoo2%3Dbar2&send_image=0&rand=random-value"); } @@ -165,7 +165,7 @@ void encodesLink() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&link=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fexternal%2Flink%23&send_image=0&rand=random-value"); + "idsite=42&token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&link=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fexternal%2Flink%23&send_image=0&rand=random-value"); } @@ -177,7 +177,7 @@ void encodesDownloadUrl() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&download=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fdownload.pdf&send_image=0&rand=random-value"); + "idsite=42&token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&download=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fdownload.pdf&send_image=0&rand=random-value"); } @@ -221,7 +221,7 @@ void tracksMinimalRequest() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=fdf6e8461ea9de33176b222519627f78&rec=1&action_name=Help+%2F+Feedback&url=https%3A%2F%2Fwww.daniel-heid.de%2Fportfolio&apiv=1&_id=2fa93d2858bc4867&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Freferrer&_cvar=%7B%225%22%3A%5B%22customVariable1Key%22%2C%22customVariable1Value%22%5D%2C%226%22%3A%5B%22customVariable2Key%22%2C%22customVariable2Value%22%5D%7D&_idvc=2&_viewts=1660070052&res=1024x768&lang=de%2Cde-de%3Bq%3D0.9%2Cen%3Bq%3D0.8&pv_id=lbBbxG&idgoal=0&revenue=12.34&ec_items=%5B%5B%22SKU%22%2C%22%22%2C%22%22%2C0.0%2C0%5D%2C%5B%22SKU%22%2C%22NAME%22%2C%22CATEGORY%22%2C123.4%2C0%5D%5D&token_auth=fdf6e8461ea9de33176b222519627f78&country=de&send_image=0&rand=random-value"); + "idsite=42&token_auth=fdf6e8461ea9de33176b222519627f78&rec=1&action_name=Help+%2F+Feedback&url=https%3A%2F%2Fwww.daniel-heid.de%2Fportfolio&apiv=1&_id=2fa93d2858bc4867&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Freferrer&_cvar=%7B%225%22%3A%5B%22customVariable1Key%22%2C%22customVariable1Value%22%5D%2C%226%22%3A%5B%22customVariable2Key%22%2C%22customVariable2Value%22%5D%7D&_idvc=2&_viewts=1660070052&res=1024x768&lang=de%2Cde-de%3Bq%3D0.9%2Cen%3Bq%3D0.8&pv_id=lbBbxG&idgoal=0&revenue=12.34&ec_items=%5B%5B%22SKU%22%2C%22%22%2C%22%22%2C0.0%2C0%5D%2C%5B%22SKU%22%2C%22NAME%22%2C%22CATEGORY%22%2C123.4%2C0%5D%5D&token_auth=fdf6e8461ea9de33176b222519627f78&country=de&send_image=0&rand=random-value"); } @@ -372,7 +372,7 @@ void doesNotAppendEmptyString() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&e_a=&send_image=0&rand=random-value"); + "idsite=42&token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&e_a=&send_image=0&rand=random-value"); } @@ -393,7 +393,7 @@ void createsQueryWithDimensions() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value&dimension1=firstDimension&dimension2=secondDimension"); + "idsite=42&token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value&dimension1=firstDimension&dimension2=secondDimension"); } @Test @@ -403,7 +403,7 @@ void appendsCharsetParameters() { whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&cs=ISO-8859-1&send_image=0&rand=random-value"); + "idsite=42&token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&cs=ISO-8859-1&send_image=0&rand=random-value"); } } diff --git a/src/test/java/org/matomo/java/tracking/RequestValidatorTest.java b/core/src/test/java/org/matomo/java/tracking/RequestValidatorTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/RequestValidatorTest.java rename to core/src/test/java/org/matomo/java/tracking/RequestValidatorTest.java diff --git a/src/test/java/org/matomo/java/tracking/SenderIT.java b/core/src/test/java/org/matomo/java/tracking/SenderIT.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/SenderIT.java rename to core/src/test/java/org/matomo/java/tracking/SenderIT.java diff --git a/src/test/java/org/matomo/java/tracking/TrackerConfigurationTest.java b/core/src/test/java/org/matomo/java/tracking/TrackerConfigurationTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/TrackerConfigurationTest.java rename to core/src/test/java/org/matomo/java/tracking/TrackerConfigurationTest.java diff --git a/src/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java b/core/src/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java similarity index 60% rename from src/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java rename to core/src/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java index 337c71b4..4458568f 100644 --- a/src/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java +++ b/core/src/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java @@ -16,14 +16,14 @@ void validateParameterValueFailsIfPatternDoesNotMatch() { .build(); assertThatThrownBy(() -> trackingParameterMethod.validateParameterValue("baz")) - .isInstanceOf(IllegalArgumentException.class) + .isInstanceOf(MatomoException.class) .hasMessage("Invalid value for foo. Must match regex bar"); } @Test void doNothingIfPatternIsNull() { TrackingParameterMethod trackingParameterMethod = - TrackingParameterMethod.builder().parameterName("foo").build(); + TrackingParameterMethod.builder().parameterName("foo").maxLength(255).build(); trackingParameterMethod.validateParameterValue("baz"); } @@ -34,20 +34,34 @@ void doNothingIfParameterValueIsNotCharSequence() { .builder() .parameterName("foo") .pattern(Pattern.compile("bar")) + .maxLength(255) .build(); trackingParameterMethod.validateParameterValue(1); } @Test - void doNothingIfParameterValueIsNull() { + void failIfParameterValueIsNull() { TrackingParameterMethod trackingParameterMethod = TrackingParameterMethod .builder() .parameterName("foo") .pattern(Pattern.compile("bar")) + .maxLength(255) .build(); - trackingParameterMethod.validateParameterValue(null); + assertThatThrownBy(() -> trackingParameterMethod.validateParameterValue(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("parameterValue is marked non-null but is null"); + } + + @Test + void validateParameterValueFailsIfMaxLengthIsExceeded() { + TrackingParameterMethod trackingParameterMethod = + TrackingParameterMethod.builder().parameterName("foo").maxLength(3).build(); + + assertThatThrownBy(() -> trackingParameterMethod.validateParameterValue("foobar")) + .isInstanceOf(MatomoException.class) + .hasMessage("Invalid value for foo. Must be less or equal than 3 characters"); } } diff --git a/src/test/java/org/matomo/java/tracking/TrustingHostnameVerifierTest.java b/core/src/test/java/org/matomo/java/tracking/TrustingHostnameVerifierTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/TrustingHostnameVerifierTest.java rename to core/src/test/java/org/matomo/java/tracking/TrustingHostnameVerifierTest.java diff --git a/src/test/java/org/matomo/java/tracking/TrustingX509TrustManagerTest.java b/core/src/test/java/org/matomo/java/tracking/TrustingX509TrustManagerTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/TrustingX509TrustManagerTest.java rename to core/src/test/java/org/matomo/java/tracking/TrustingX509TrustManagerTest.java diff --git a/src/test/java/org/matomo/java/tracking/parameters/AcceptLanguageTest.java b/core/src/test/java/org/matomo/java/tracking/parameters/AcceptLanguageTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/parameters/AcceptLanguageTest.java rename to core/src/test/java/org/matomo/java/tracking/parameters/AcceptLanguageTest.java diff --git a/src/test/java/org/matomo/java/tracking/parameters/CountryTest.java b/core/src/test/java/org/matomo/java/tracking/parameters/CountryTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/parameters/CountryTest.java rename to core/src/test/java/org/matomo/java/tracking/parameters/CountryTest.java diff --git a/src/test/java/org/matomo/java/tracking/parameters/CustomVariableTest.java b/core/src/test/java/org/matomo/java/tracking/parameters/CustomVariableTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/parameters/CustomVariableTest.java rename to core/src/test/java/org/matomo/java/tracking/parameters/CustomVariableTest.java diff --git a/src/test/java/org/matomo/java/tracking/parameters/CustomVariablesTest.java b/core/src/test/java/org/matomo/java/tracking/parameters/CustomVariablesTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/parameters/CustomVariablesTest.java rename to core/src/test/java/org/matomo/java/tracking/parameters/CustomVariablesTest.java diff --git a/src/test/java/org/matomo/java/tracking/parameters/DeviceResolutionTest.java b/core/src/test/java/org/matomo/java/tracking/parameters/DeviceResolutionTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/parameters/DeviceResolutionTest.java rename to core/src/test/java/org/matomo/java/tracking/parameters/DeviceResolutionTest.java diff --git a/src/test/java/org/matomo/java/tracking/parameters/EcommerceItemsTest.java b/core/src/test/java/org/matomo/java/tracking/parameters/EcommerceItemsTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/parameters/EcommerceItemsTest.java rename to core/src/test/java/org/matomo/java/tracking/parameters/EcommerceItemsTest.java diff --git a/src/test/java/org/matomo/java/tracking/parameters/HexTest.java b/core/src/test/java/org/matomo/java/tracking/parameters/HexTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/parameters/HexTest.java rename to core/src/test/java/org/matomo/java/tracking/parameters/HexTest.java diff --git a/src/test/java/org/matomo/java/tracking/parameters/UniqueIdTest.java b/core/src/test/java/org/matomo/java/tracking/parameters/UniqueIdTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/parameters/UniqueIdTest.java rename to core/src/test/java/org/matomo/java/tracking/parameters/UniqueIdTest.java diff --git a/src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java b/core/src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java similarity index 100% rename from src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java rename to core/src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java diff --git a/src/test/java/org/piwik/java/tracking/CustomVariableTest.java b/core/src/test/java/org/piwik/java/tracking/CustomVariableTest.java similarity index 100% rename from src/test/java/org/piwik/java/tracking/CustomVariableTest.java rename to core/src/test/java/org/piwik/java/tracking/CustomVariableTest.java diff --git a/src/test/java/org/piwik/java/tracking/EcommerceItemTest.java b/core/src/test/java/org/piwik/java/tracking/EcommerceItemTest.java similarity index 100% rename from src/test/java/org/piwik/java/tracking/EcommerceItemTest.java rename to core/src/test/java/org/piwik/java/tracking/EcommerceItemTest.java diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..4064bcf8 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,21 @@ +version: '3.8' +services: + database: + image: mariadb:10.11.5-jammy + command: --max-allowed-packet=64MB + environment: + - MYSQL_ROOT_PASSWORD=matomo + - MYSQL_PASSWORD=matomo + - MYSQL_DATABASE=matomo + - MYSQL_USER=matomo + matomo: + image: matomo:4.15.1-apache + environment: + - MATOMO_DATABASE_HOST=database + - MATOMO_DATABASE_ADAPTER=mysql + - MATOMO_DATABASE_TABLES_PREFIX=matomo_ + - MATOMO_DATABASE_USERNAME=matomo + - MATOMO_DATABASE_PASSWORD=matomo + - MATOMO_DATABASE_DBNAME=matomo + ports: + - '8080:80' diff --git a/pom.xml b/pom.xml index d0297a99..29c06a06 100644 --- a/pom.xml +++ b/pom.xml @@ -2,12 +2,12 @@ 4.0.0 org.piwik.java.tracking - matomo-java-tracker + matomo-java-tracker-parent 3.0.0-rc3-SNAPSHOT - jar + pom - Matomo Java Tracker - Official Java implementation of the Matomo Tracking HTTP API. + Matomo Java Tracker Parent + Parent for Matomo Java Tracker modules. https://github.com/matomo-org/matomo-java-tracker @@ -180,7 +180,7 @@ - ${project.basedir}/src/main/java + core/src/main/java ${delombok.output} false @@ -329,48 +329,8 @@ - - - com.github.spotbugs - spotbugs-annotations - 4.8.0 - provided - - - org.slf4j - slf4j-api - ${slf4j.version} - - - org.projectlombok - lombok - ${lombok.version} - provided - - - org.junit.jupiter - junit-jupiter - 5.10.0 - test - - - org.assertj - assertj-core - 3.24.2 - test - - - org.slf4j - slf4j-simple - ${slf4j.version} - test - - - com.github.tomakehurst - wiremock - 2.27.2 - test - - - + + core + test + diff --git a/test/pom.xml b/test/pom.xml new file mode 100644 index 00000000..6027ddbb --- /dev/null +++ b/test/pom.xml @@ -0,0 +1,39 @@ + + 4.0.0 + + + org.piwik.java.tracking + matomo-java-tracker-parent + 3.0.0-rc3-SNAPSHOT + ../pom.xml + + + matomo-java-tracker-test + + Matomo Java Tracker Test + Test application for Matomo Java Tracker + + + true + + + + + org.piwik.java.tracking + matomo-java-tracker + ${project.version} + + + com.github.javafaker + javafaker + 1.0.2 + + + org.slf4j + slf4j-simple + ${slf4j.version} + + + + diff --git a/test/src/main/java/org/matomo/java/tracking/MatomoJavaTrackerTest.java b/test/src/main/java/org/matomo/java/tracking/MatomoJavaTrackerTest.java new file mode 100644 index 00000000..fdb0f040 --- /dev/null +++ b/test/src/main/java/org/matomo/java/tracking/MatomoJavaTrackerTest.java @@ -0,0 +1,186 @@ +package org.matomo.java.tracking; + +import static java.util.Arrays.asList; + +import com.github.javafaker.Country; +import com.github.javafaker.Faker; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.matomo.java.tracking.parameters.AcceptLanguage; +import org.matomo.java.tracking.parameters.CustomVariable; +import org.matomo.java.tracking.parameters.CustomVariables; +import org.matomo.java.tracking.parameters.DeviceResolution; +import org.matomo.java.tracking.parameters.EcommerceItem; +import org.matomo.java.tracking.parameters.EcommerceItems; +import org.matomo.java.tracking.parameters.UniqueId; +import org.matomo.java.tracking.parameters.VisitorId; + +class MatomoJavaTrackerTest { + + private final MatomoTracker tracker; + + private final Faker faker = new Faker(); + private final List vistors = new ArrayList<>(5); + + MatomoJavaTrackerTest(TrackerConfiguration configuration) { + tracker = new MatomoTracker(configuration); + for (int i = 0; i < 5; i++) { + vistors.add(VisitorId.random()); + } + } + + public static void main(String[] args) { + + TrackerConfiguration configuration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("http://localhost:8080/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("ee6e3dd9ed1b61f5328cf5978b5a8c71") + .logFailedTracking(true) + .build(); + + MatomoJavaTrackerTest matomoJavaTrackerTest = new MatomoJavaTrackerTest(configuration); + + matomoJavaTrackerTest.sendRequestAsync(); + matomoJavaTrackerTest.sendBulkRequestsAsync(); + matomoJavaTrackerTest.sendRequest(); + matomoJavaTrackerTest.sendBulkRequests(); + + } + + private void sendRequest() { + MatomoRequest request = randomRequest(); + tracker.sendRequest(request); + System.out.printf("Successfully sent single request to Matomo server: %s%n", request); + } + + private void sendBulkRequests() { + List requests = randomRequests(); + tracker.sendBulkRequest(requests); + System.out.printf("Successfully sent bulk requests to Matomo server: %s%n", requests); + } + + private void sendRequestAsync() { + MatomoRequest request = randomRequest(); + CompletableFuture future = tracker.sendRequestAsync(request); + future.thenAccept(v -> System.out.printf("Successfully sent async single request to Matomo server: %s%n", request)); + } + + private void sendBulkRequestsAsync() { + List requests = randomRequests(); + tracker + .sendBulkRequestAsync(requests) + .thenAccept(v -> System.out.printf("Successfully sent async bulk requests to Matomo server: %s%n", requests)); + } + + private List randomRequests() { + return IntStream + .range(0, 5) + .mapToObj(i -> randomRequest()) + .collect(Collectors.toCollection(() -> new ArrayList<>(10))); + } + + private MatomoRequest randomRequest() { + Country country = faker.country(); + return MatomoRequest + .request() + .actionName(faker.funnyName().name()) + .actionUrl("https://" + faker.internet().url()) + .visitorId(vistors.get(faker.random().nextInt(vistors.size()))) + .referrerUrl("https://" + faker.internet().url()) + .visitCustomVariables(new CustomVariables() + .add(new CustomVariable("color", faker.color().hex())) + .add(new CustomVariable("beer", faker.beer().name()))) + .visitorVisitCount(faker.random().nextInt(10)) + .visitorPreviousVisitTimestamp(Instant.now().minusSeconds(faker.random().nextInt(10000))) + .visitorFirstVisitTimestamp(Instant.now().minusSeconds(faker.random().nextInt(10000))) + .campaignName(faker.dragonBall().character()) + .campaignKeyword(faker.buffy().celebrities()) + .deviceResolution(DeviceResolution + .builder() + .width(faker.random().nextInt(1920)) + .height(faker.random().nextInt(1280)) + .build()) + .currentHour(faker.random().nextInt(24)) + .currentMinute(faker.random().nextInt(60)) + .currentSecond(faker.random().nextInt(60)) + .pluginJava(true) + .pluginFlash(true) + .pluginDirector(true) + .pluginQuicktime(true) + .pluginPDF(true) + .pluginWindowsMedia(true) + .pluginGears(true) + .pluginSilverlight(true) + .supportsCookies(true) + .headerUserAgent(faker.internet().userAgentAny()) + .headerAcceptLanguage(AcceptLanguage.fromHeader("de")) + .userId(faker.random().hex()) + .visitorCustomId(VisitorId.random()) + .newVisit(true) + .pageCustomVariables(new CustomVariables() + .add(new CustomVariable("job", faker.job().position())) + .add(new CustomVariable("team", faker.team().name()))) + .outlinkUrl("https://" + faker.internet().url()) + .downloadUrl("https://" + faker.internet().url()) + .searchQuery(faker.cat().name()) + .searchCategory(faker.hipster().word()) + .searchResultsCount(Long.valueOf(faker.random().nextInt(20))) + .pageViewId(UniqueId.random()) + .goalId(0) + .ecommerceRevenue(faker.random().nextInt(50) + faker.random().nextDouble()) + .ecommerceId(faker.random().hex()) + .ecommerceItems(EcommerceItems + .builder() + .item(EcommerceItem + .builder() + .sku(faker.random().hex()) + .name(faker.commerce().productName()) + .quantity(faker.random().nextInt(10)) + .price(faker.random().nextInt(100) + faker.random().nextDouble()) + .build()) + .item(EcommerceItem + .builder() + .sku(faker.random().hex()) + .name(faker.commerce().productName()) + .quantity(faker.random().nextInt(10)) + .price(faker.random().nextInt(100) + faker.random().nextDouble()) + .build()) + .build()) + .ecommerceSubtotal(faker.random().nextInt(1000) + faker.random().nextDouble()) + .ecommerceTax(faker.random().nextInt(100) + faker.random().nextDouble()) + .ecommerceDiscount(faker.random().nextInt(100) + faker.random().nextDouble()) + .ecommerceLastOrderTimestamp(Instant.now()) + .visitorIp(faker.internet().ipV4Address()) + .requestTimestamp(Instant.now()) + .visitorCountry(org.matomo.java.tracking.parameters.Country.fromCode(faker.address().countryCode())) + .visitorCity(faker.address().cityName()) + .visitorLatitude(faker.random().nextDouble() * 180 - 90) + .visitorLongitude(faker.random().nextDouble() * 360 - 180) + .trackBotRequests(true) + .characterSet(StandardCharsets.UTF_8) + .customAction(true) + .networkTime(Long.valueOf(faker.random().nextInt(100))) + .serverTime(Long.valueOf(faker.random().nextInt(100))) + .transferTime(Long.valueOf(faker.random().nextInt(100))) + .domProcessingTime(Long.valueOf(faker.random().nextInt(100))) + .domCompletionTime(Long.valueOf(faker.random().nextInt(100))) + .onloadTime(Long.valueOf(faker.random().nextInt(100))) + .eventCategory(country.name()) + .eventAction(country.capital()) + .eventName(country.currencyCode()) + .contentName(faker.aviation().aircraft()) + .contentPiece(faker.ancient().god()) + .contentTarget("https://" + faker.internet().url()) + .contentInteraction(faker.app().name()) + .dimensions(asList(faker.artist().name(), faker.dog().name())) + .debug(true) + .build(); + } +} From 2df07c072db14ad3076fde285351b88149993699 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 6 Nov 2023 00:26:40 +0100 Subject: [PATCH 173/467] Add deprecated annotation --- .../matomo/java/tracking/MatomoRequest.java | 52 ++++++++++--------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java index 611a1916..d38a941c 100644 --- a/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -503,14 +503,20 @@ public class MatomoRequest { /** * An override value for the country. Must be a two-letter ISO 3166 Alpha-2 country code. */ - @TrackingParameter(name = "country", maxLength = 2) + @TrackingParameter( + name = "country", + maxLength = 2 + ) private Country visitorCountry; /** * An override value for the region. Should be set to a ISO 3166-2 region code, which are used by MaxMind's and * DB-IP's GeoIP2 databases. See here for a list of them for every country. */ - @TrackingParameter(name = "region", maxLength = 2) + @TrackingParameter( + name = "region", + maxLength = 2 + ) private String visitorRegion; /** @@ -649,9 +655,7 @@ public List getCustomTrackingParameter(@NonNull String key) { */ @Deprecated public void setCustomTrackingParameter( - @NonNull String key, - @Nullable - Object value + @NonNull String key, @Nullable Object value ) { if (value == null) { @@ -662,8 +666,7 @@ public void setCustomTrackingParameter( if (additionalParameters == null) { additionalParameters = new LinkedHashMap<>(); } - Collection values = - additionalParameters.computeIfAbsent(key, k -> new ArrayList<>()); + Collection values = additionalParameters.computeIfAbsent(key, k -> new ArrayList<>()); values.clear(); values.add(value); } @@ -678,6 +681,7 @@ public void setCustomTrackingParameter( * @param value the parameter's value. Cannot be null * @deprecated Use {@link MatomoRequest.MatomoRequestBuilder#additionalParameters(Map)} instead. */ + @Deprecated public void addCustomTrackingParameter(@NonNull String key, @NonNull Object value) { if (additionalParameters == null) { additionalParameters = new LinkedHashMap<>(); @@ -728,7 +732,9 @@ public EcommerceItem getEcommerceItem(int index) { * and EcommerceId and EcommerceRevenue must first be set. * * @param item the {@link EcommerceItem} to add. Cannot be null + * @deprecated Use @link {@link MatomoRequest.MatomoRequestBuilder#ecommerceItems(EcommerceItems)} instead */ + @Deprecated public void addEcommerceItem(@NonNull EcommerceItem item) { if (ecommerceItems == null) { ecommerceItems = new EcommerceItems(); @@ -738,7 +744,10 @@ public void addEcommerceItem(@NonNull EcommerceItem item) { /** * Clears all {@link EcommerceItem} from this order. + * + * @deprecated Use @link {@link MatomoRequest.MatomoRequestBuilder#ecommerceItems(EcommerceItems)} instead */ + @Deprecated public void clearEcommerceItems() { ecommerceItems.clear(); } @@ -748,7 +757,7 @@ public void clearEcommerceItems() { * * @param key the key of the variable to get * @return the variable at the specified key, null if key is not present - * @deprecated Use the {@link #getPageCustomVariable(int)} method instead. + * @deprecated Use the {@link #getPageCustomVariables()} method instead. */ @Nullable @Deprecated @@ -773,6 +782,7 @@ public CustomVariable getPageCustomVariable(int index) { } @Nullable + @Deprecated private static CustomVariable getCustomVariable(CustomVariables customVariables, int index) { if (customVariables == null) { return null; @@ -790,10 +800,7 @@ private static CustomVariable getCustomVariable(CustomVariables customVariables, */ @Deprecated public void setPageCustomVariable( - @edu.umd.cs.findbugs.annotations.NonNull - String key, - @Nullable - String value + @edu.umd.cs.findbugs.annotations.NonNull String key, @Nullable String value ) { requireNonNull(key, "Key must not be null"); if (value == null) { @@ -820,8 +827,7 @@ public void setPageCustomVariable( */ @Deprecated public void setPageCustomVariable( - @Nullable - CustomVariable customVariable, int index + @Nullable CustomVariable customVariable, int index ) { if (pageCustomVariables == null) { if (customVariable == null) { @@ -832,10 +838,9 @@ public void setPageCustomVariable( setCustomVariable(pageCustomVariables, customVariable, index); } + @Deprecated private static void setCustomVariable( - CustomVariables customVariables, - @Nullable - CustomVariable customVariable, int index + CustomVariables customVariables, @Nullable CustomVariable customVariable, int index ) { if (customVariable == null) { customVariables.remove(index); @@ -884,7 +889,7 @@ public void setRequestDatetime(MatomoDate matomoDate) { * * @param key the key of the variable to get * @return the variable at the specified key, null if key is not present - * @deprecated Use the {@link #getVisitCustomVariable(int)} method instead. + * @deprecated Use the {@link #getVisitCustomVariables()} method instead. */ @Nullable @Deprecated @@ -918,10 +923,7 @@ public CustomVariable getVisitCustomVariable(int index) { */ @Deprecated public void setUserCustomVariable( - @edu.umd.cs.findbugs.annotations.NonNull - String key, - @Nullable - String value + @edu.umd.cs.findbugs.annotations.NonNull String key, @Nullable String value ) { requireNonNull(key, "Key must not be null"); if (value == null) { @@ -948,8 +950,7 @@ public void setUserCustomVariable( */ @Deprecated public void setVisitCustomVariable( - @Nullable - CustomVariable customVariable, int index + @Nullable CustomVariable customVariable, int index ) { if (visitCustomVariables == null) { if (customVariable == null) { @@ -966,7 +967,8 @@ public void setVisitCustomVariable( *

Attention: If a parameter with the same name already exists, it will be appended twice! * * @param parameterName The name of the query parameter to append. Must not be null or empty. - * @param value The value of the query parameter to append. To remove the parameter, pass null. + * @param value The value of the query parameter to append. To remove the parameter, pass null. + * @deprecated Use @link {@link MatomoRequest.MatomoRequestBuilder#additionalParameters(Map)} instead */ @Deprecated public void setParameter(@NonNull String parameterName, Object value) { From a9fa8714cc5263adc62598583e5a83dd306d63be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 22:58:50 +0100 Subject: [PATCH 174/467] build(deps): bump org.apache.maven.plugins:maven-surefire-plugin from 3.2.1 to 3.2.2 (#155) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 29c06a06..63340cc3 100644 --- a/pom.xml +++ b/pom.xml @@ -112,7 +112,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.2.1 + 3.2.2 org.apache.maven.plugins From c5175d682612ec099eea9221b0e26596eb3d49db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 22:59:05 +0100 Subject: [PATCH 175/467] build(deps): bump org.jacoco:jacoco-maven-plugin from 0.8.10 to 0.8.11 (#154) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 63340cc3..e947c5b3 100644 --- a/pom.xml +++ b/pom.xml @@ -234,7 +234,7 @@ org.jacoco jacoco-maven-plugin - 0.8.10 + 0.8.11 prepare-agent From 126b81ca4cb3a977aa04f1a437d6545bf3dbee07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 22:59:16 +0100 Subject: [PATCH 176/467] build(deps-dev): bump org.junit.jupiter:junit-jupiter from 5.10.0 to 5.10.1 (#153) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/pom.xml b/core/pom.xml index f4cc6cc8..be0a9260 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -35,7 +35,7 @@ org.junit.jupiter junit-jupiter - 5.10.0 + 5.10.1 test From a1fd479ad58f33d6a936479f11d444577dfc85dd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 22:59:26 +0100 Subject: [PATCH 177/467] build(deps): bump org.apache.maven.plugins:maven-javadoc-plugin from 3.6.0 to 3.6.2 (#152) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e947c5b3..d5c9cc53 100644 --- a/pom.xml +++ b/pom.xml @@ -196,7 +196,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.6.0 + 3.6.2 true ${delombok.output} From fd0ed2d9a19cb0d9f42687f17179cfb08c6ab213 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 22:59:34 +0100 Subject: [PATCH 178/467] build(deps): bump org.apache.maven.plugins:maven-failsafe-plugin from 3.2.1 to 3.2.2 (#151) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d5c9cc53..3798ae4f 100644 --- a/pom.xml +++ b/pom.xml @@ -145,7 +145,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.2.1 + 3.2.2 From eb466080f83f0c0564e20bc492b20172ac037005 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Sat, 11 Nov 2023 17:50:16 +0100 Subject: [PATCH 179/467] Add java8, java17 and spring modules --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 2 +- README.md | 134 ++++- core/pom.xml | 28 +- .../org/matomo/java/tracking/BulkRequest.java | 43 ++ .../java/tracking/DaemonThreadFactory.java | 9 +- .../matomo/java/tracking/MatomoRequest.java | 27 +- .../matomo/java/tracking/MatomoTracker.java | 67 ++- .../matomo/java/tracking/QueryCreator.java | 10 +- .../java/tracking/RequestValidator.java | 5 +- .../java/org/matomo/java/tracking/Sender.java | 266 +-------- .../matomo/java/tracking/SenderFactory.java | 10 + .../matomo/java/tracking/SenderProvider.java | 7 + .../tracking/ServiceLoaderSenderFactory.java | 28 + .../ServletMatomoRequestCustomizer.java | 58 ++ .../java/tracking/TrackerConfiguration.java | 48 +- .../org/piwik/java/tracking/PiwikTracker.java | 10 +- .../matomo/java/tracking/BulkRequestTest.java | 35 ++ .../tracking/DaemonThreadFactoryTest.java | 27 + .../matomo/java/tracking/MatomoTrackerIT.java | 527 ++++-------------- .../java/tracking/MockHttpServletRequest.java | 384 +++++++++++++ .../java/tracking/QueryCreatorTest.java | 8 +- .../java/tracking/RequestValidatorTest.java | 16 + .../org/matomo/java/tracking/SenderIT.java | 148 ----- .../ServiceLoaderSenderFactoryTest.java | 24 + .../ServletMatomoRequestCustomizerTest.java | 38 ++ .../org/matomo/java/tracking/TestSender.java | 72 +++ .../java/tracking/TestSenderFactory.java | 16 + .../tracking/TrackerConfigurationTest.java | 393 ++++++++++++- .../piwik/java/tracking/PiwikTrackerIT.java | 47 ++ java17/pom.xml | 74 +++ .../matomo/java/tracking/Java17Sender.java | 164 ++++++ .../java/tracking/Java17SenderProvider.java | 63 +++ .../org.matomo.java.tracking.SenderProvider | 1 + .../matomo/java/tracking/Java17SenderIT.java | 332 +++++++++++ .../matomo/java/tracking/MatomoTrackerIT.java | 498 +++++++++++++++++ .../matomo/java/tracking/PiwikTrackerIT.java | 249 +++++++++ java8/pom.xml | 61 ++ .../org/matomo/java/tracking/Java8Sender.java | 292 ++++++++++ .../java/tracking/Java8SenderProvider.java | 20 + .../org.matomo.java.tracking.SenderProvider | 1 + .../matomo/java/tracking/Java8SenderIT.java | 223 ++++++++ .../matomo/java/tracking/MatomoTrackerIT.java | 516 +++++++++++++++++ .../matomo/java/tracking/PiwikTrackerIT.java | 8 +- pom.xml | 46 +- spring/pom.xml | 63 +++ .../MatomoTrackerAutoConfiguration.java | 101 ++++ .../spring/MatomoTrackerProperties.java | 148 +++++ ...TrackerConfigurationBuilderCustomizer.java | 50 ++ ...TrackerConfigurationBuilderCustomizer.java | 34 ++ .../java/tracking/spring/package-info.java | 10 + ...ot.autoconfigure.AutoConfiguration.imports | 1 + .../MatomoTrackerAutoConfigurationIT.java | 70 +++ ...ackerConfigurationBuilderCustomizerIT.java | 55 ++ test/pom.xml | 8 +- .../java/tracking/MatomoJavaTrackerTest.java | 7 +- 56 files changed, 4622 insertions(+), 962 deletions(-) create mode 100644 core/src/main/java/org/matomo/java/tracking/BulkRequest.java create mode 100644 core/src/main/java/org/matomo/java/tracking/SenderFactory.java create mode 100644 core/src/main/java/org/matomo/java/tracking/SenderProvider.java create mode 100644 core/src/main/java/org/matomo/java/tracking/ServiceLoaderSenderFactory.java create mode 100644 core/src/main/java/org/matomo/java/tracking/ServletMatomoRequestCustomizer.java create mode 100644 core/src/test/java/org/matomo/java/tracking/BulkRequestTest.java create mode 100644 core/src/test/java/org/matomo/java/tracking/DaemonThreadFactoryTest.java create mode 100644 core/src/test/java/org/matomo/java/tracking/MockHttpServletRequest.java delete mode 100644 core/src/test/java/org/matomo/java/tracking/SenderIT.java create mode 100644 core/src/test/java/org/matomo/java/tracking/ServiceLoaderSenderFactoryTest.java create mode 100644 core/src/test/java/org/matomo/java/tracking/ServletMatomoRequestCustomizerTest.java create mode 100644 core/src/test/java/org/matomo/java/tracking/TestSender.java create mode 100644 core/src/test/java/org/matomo/java/tracking/TestSenderFactory.java create mode 100644 core/src/test/java/org/piwik/java/tracking/PiwikTrackerIT.java create mode 100644 java17/pom.xml create mode 100644 java17/src/main/java/org/matomo/java/tracking/Java17Sender.java create mode 100644 java17/src/main/java/org/matomo/java/tracking/Java17SenderProvider.java create mode 100644 java17/src/main/resources/META-INF/services/org.matomo.java.tracking.SenderProvider create mode 100644 java17/src/test/java/org/matomo/java/tracking/Java17SenderIT.java create mode 100644 java17/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java create mode 100644 java17/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java create mode 100644 java8/pom.xml create mode 100644 java8/src/main/java/org/matomo/java/tracking/Java8Sender.java create mode 100644 java8/src/main/java/org/matomo/java/tracking/Java8SenderProvider.java create mode 100644 java8/src/main/resources/META-INF/services/org.matomo.java.tracking.SenderProvider create mode 100644 java8/src/test/java/org/matomo/java/tracking/Java8SenderIT.java create mode 100644 java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java rename {core => java8}/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java (97%) create mode 100644 spring/pom.xml create mode 100644 spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java create mode 100644 spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerProperties.java create mode 100644 spring/src/main/java/org/matomo/java/tracking/spring/StandardTrackerConfigurationBuilderCustomizer.java create mode 100644 spring/src/main/java/org/matomo/java/tracking/spring/TrackerConfigurationBuilderCustomizer.java create mode 100644 spring/src/main/java/org/matomo/java/tracking/spring/package-info.java create mode 100644 spring/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 spring/src/test/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfigurationIT.java create mode 100644 spring/src/test/java/org/matomo/java/tracking/spring/StandardTrackerConfigurationBuilderCustomizerIT.java diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4c6c89fc..20a6c625 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: - java-version: '8' + java-version: '17' distribution: 'temurin' cache: maven - run: mvn -B verify diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e1b43070..82bdd82f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,7 +8,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: - java-version: '8' + java-version: '17' distribution: 'temurin' cache: maven server-id: ossrh diff --git a/README.md b/README.md index 40f4e110..36b3199b 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Features include: * Track page views, goals, ecommerce transactions and items * Supports custom dimensions and custom variables * Includes tracking parameters for campaigns, events, downloads, outlinks, site search, devices, visitors -* Supports Java 8 and higher +* Supports Java 8 and higher (if you use Java 17, please use artifact matomo-java-tracker-java17) * Allows you to skip SSL certificate validation (not recommended for production) * Contains nearly no runtime dependencies (only SLF4J) * Allows asynchronous requests @@ -26,6 +26,8 @@ Features include: * Includes debug and error logging * Easy to integrate in frameworks, e.g. Spring: Just create the MatomoTracker Spring bean and use it in other beans +Please prefer the Java 17 version as the Java 8 will become obsolete in the future. + Further information on Matomo and Matomo HTTP tracking: * [Matomo PHP Tracker](https://github.com/matomo-org/matomo-php-tracker) @@ -70,6 +72,7 @@ Here are the most important changes: * less dependencies * new dimension parameter * special types allow to provide valid parameters now +* a new implementation for Java 17 uses the HttpClient available since Java 11 ## Javadoc @@ -89,21 +92,71 @@ You can also build the Javadoc yourself. See the section [Building](#building-an ## Using this API See the following sections for information on how to use this API. For more information, see the Javadoc. We also -recommend -to read the [Tracking API User Guide](https://matomo.org/guide/apis/tracking-api/). +recommend to read the [Tracking API User Guide](https://matomo.org/guide/apis/tracking-api/). The [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) is well documented and contains many examples. ### Add library to your build -Add a dependency on Matomo Java Tracker using Maven: +Add a dependency on Matomo Java Tracker using Maven. For Java 8: ```xml - org.piwik.java.tracking matomo-java-tracker - 3.0.0-rc1 + 3.0.0 + +``` + +For Java 17: + +```xml + + org.piwik.java.tracking + matomo-java-tracker-java17 + 3.0.0 + +``` + +or Gradle (Java 8): + +```groovy +dependencies { + implementation("org.piwik.java.tracking:matomo-java-tracker:3.0.0") +} +``` + +or Gradle (Java 17): + +```groovy +dependencies { + implementation("org.piwik.java.tracking:matomo-java-tracker-java17:3.0.0") +} +``` + +or Gradle with Kotlin DSL (Java 8) + +```kotlin +implementation("org.piwik.java.tracking:matomo-java-tracker:3.0.0") +``` + +or Gradle with Kotlin DSL (Java 17) + +```kotlin +implementation("org.piwik.java.tracking:matomo-java-tracker-java17:3.0.0") +``` + +### Spring Boot Module + +If you use Spring Boot, you can use the Spring Boot Autoconfigure artifact. It will create a MatomoTracker bean for you +and allows you to configure the tracker via application properties. Add the following dependency to your build: + +```xml + + + org.piwik.java.tracking + matomo-java-tracker-spring-boot-starter + 3.0.0 ``` @@ -111,14 +164,49 @@ or Gradle: ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker:3.0.0-rc1") + implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.0.0") } ``` -or Gradle with Kotlin DSL: +or Gradle with Kotlin DSL ```kotlin -implementation("org.piwik.java.tracking:matomo-java-tracker:3.0.0-rc1") +implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.0.0") +``` + +The following properties are supported: + +| Property Name | Description | +|----------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------| +| matomo.tracker.api-endpoint (required) | The URL to the Matomo Tracking API endpoint. Must be set. | +| matomo.tracker.default-site-id | If you provide a default site id, it will be taken if the action does not contain a site id. | +| matomo.tracker.default-token-auth | If you provide a default token auth, it will be taken if the action does not contain a token auth. | +| matomo.tracker.enabled | The tracker is enabled per default. You can disable it per configuration with this flag. | +| matomo.tracker.log-failed-tracking | Will send errors to the log if the Matomo Tracking API responds with an erroneous HTTP code | +| matomo.tracker.connect-timeout | allows you to change the default connection timeout of 10 seconds. 0 is interpreted as infinite, null uses the system default | +| matomo.tracker.socket-timeout | allows you to change the default socket timeout of 10 seconds. 0 is interpreted as infinite, null uses the system default | +| matomo.tracker.user-agent | used by the request made to the endpoint is `MatomoJavaClient` per default. You can change it by using this builder method. | +| matomo.tracker.proxy-host | The hostname or IP address of an optional HTTP proxy. `proxyPort` must be configured as well | +| matomo.tracker.proxy-port | The port of an HTTP proxy. `proxyHost` must be configured as well. | +| matomo.tracker.proxy-username | If the HTTP proxy requires a username for basic authentication, it can be configured with this method. Proxy host, port and password must also be set. | +| matomo.tracker.proxy-password | The corresponding password for the basic auth proxy user. The proxy host, port and username must be set as well. | +| matomo.tracker.disable-ssl-cert-validation | If set to true, the SSL certificate of the Matomo server will not be validated. This should only be used for testing purposes. Default: false | +| matomo.tracker.disable-ssl-host-verification | If set to true, the SSL host of the Matomo server will not be validated. This should only be used for testing purposes. Default: false | +| matomo.tracker.thread-pool-size | The number of threads that will be used to asynchronously send requests. Default: 2 | + +To ensure the `MatomoTracker` bean is created by the autoconfigure module, you have to add the following property to +your `application.properties` file: + +```properties +matomo.tracker.api-endpoint=https://your-matomo-domain.tld/matomo.php +``` + +Or if you use YAML: + +```yaml +matomo: + tracker: + api-endpoint: https://your-matomo-domain.tld/matomo.php ``` ### Create a Request @@ -183,7 +271,8 @@ public class YourImplementation { See [Tracking Campaigns](https://matomo.org/docs/tracking-campaigns/) for more information. All HTTP query parameters denoted on the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate -getters and setters. See [MatomoRequest](core/src/main/java/org/matomo/java/tracking/MatomoRequest.java) for the mappings of +getters and setters. See [MatomoRequest](core/src/main/java/org/matomo/java/tracking/MatomoRequest.java) for the +mappings of the parameters to their corresponding attributes. @@ -221,8 +310,6 @@ The Matomo Tracker currently supports the following builder methods: * `.defaultSiteId(...)` If you provide a default site id, it will be taken if the action does not contain a site id. * `.defaultTokenAuth(...)` If you provide a default token auth, it will be taken if the action does not contain a token auth. -* `.delay(...)` The duration on how long the tracker collects actions until they will be sent out as a bulk request. - Default: 1 seconds * `.enabled(...)` The tracker is enabled per default. You can disable it per configuration with this flag. * `.logFailedTracking(...)` Will send errors to the log if the Matomo Tracking API responds with an erroneous HTTP code * `.connectTimeout(...)` allows you to change the default connection timeout of 10 seconds. 0 is @@ -234,15 +321,15 @@ The Matomo Tracker currently supports the following builder methods: * `.proxyHost(...)` The hostname or IP address of an optional HTTP proxy. `proxyPort` must be configured as well * `.proxyPort(...)` The port of an HTTP proxy. `proxyHost` must be configured as well. -* `.proxyUserName(...)` If the HTTP proxy requires a username for basic authentication, it can be +* `.proxyUsername(...)` If the HTTP proxy requires a username for basic authentication, it can be configured with this method. Proxy host, port and password must also be set. * `.proxyPassword(...)` The corresponding password for the basic auth proxy user. The proxy host, port and username must be set as well. * `.disableSslCertValidation(...)` If set to true, the SSL certificate of the Matomo server will not be validated. This should only be used for testing purposes. Default: false * `.disableSslHostVerification(...)` If set to true, the SSL host of the Matomo server will not be validated. This - should - only be used for testing purposes. Default: false + should only be used for testing purposes. Default: false +* `.threadPoolSize(...)` The number of threads that will be used to asynchronously send requests. Default: 2 To send a single request synchronously via GET, call @@ -446,7 +533,18 @@ This project can be tested and built by calling mvn install ``` -This repository contains the modules `core` and `test`. The built jars and javadoc can be found in `target`. By using the Maven goal `install, a snapshot +This project contains the following modules: + +* `core` contains the core functionality of the Matomo Java Tracker +* `java8` contains the Java 8 implementation of the Matomo Java Tracker +* `java17` contains the Java 17 implementation of the Matomo Java Tracker using the HttpClient available since Java 11 + (recommended) +* `spring-boot-autoconfigure` contains the Spring Boot autoconfigure module +* `test` contains tools for manual test against a local Matomo instance created with Docker (see below) + + +The built jars and javadoc can be found in `target`. By using +the Maven goal `install, a snapshot version can be used in your local Maven repository for testing purposes, e.g. ```xml @@ -466,6 +564,7 @@ Start the docker containers with ```shell docker-compose up -d ``` + You need to adapt your config.ini.php file and change the following line: @@ -491,7 +590,8 @@ The following snippets helps you to do this quickly: docker-compose exec matomo sed -i 's/localhost/localhost:8080/g' /var/www/html/config/config.ini.php ``` -After the installation you can run `MatomoJavaTrackerTest` in the module `test` to test the tracker. It will send multiple randomized +After the installation you can run `MatomoJavaTrackerTest` in the module `test` to test the tracker. It will send +multiple randomized requests to the local Matomo instance. To enable debug logging, you append the following line to the `config.ini.php` file: diff --git a/core/pom.xml b/core/pom.xml index be0a9260..dbf78e4f 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,59 +4,47 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0-rc3-SNAPSHOT + 3.0.0-SNAPSHOT ../pom.xml - matomo-java-tracker + matomo-java-tracker-core jar - Matomo Java Tracker - Official Java implementation of the Matomo Tracking HTTP API. + Matomo Java Tracker Core com.github.spotbugs spotbugs-annotations - 4.8.0 - provided org.slf4j slf4j-api - ${slf4j.version} org.projectlombok lombok - ${lombok.version} + + + jakarta.servlet + jakarta.servlet-api + 6.0.0 provided org.junit.jupiter junit-jupiter - 5.10.1 test org.assertj assertj-core - 3.24.2 - test org.slf4j slf4j-simple - ${slf4j.version} test - - com.github.tomakehurst - wiremock - 2.27.2 - test - - - diff --git a/core/src/main/java/org/matomo/java/tracking/BulkRequest.java b/core/src/main/java/org/matomo/java/tracking/BulkRequest.java new file mode 100644 index 00000000..10483dc5 --- /dev/null +++ b/core/src/main/java/org/matomo/java/tracking/BulkRequest.java @@ -0,0 +1,43 @@ +package org.matomo.java.tracking; + +import edu.umd.cs.findbugs.annotations.Nullable; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Iterator; +import lombok.Builder; +import lombok.NonNull; +import lombok.Value; + +@Builder +@Value +class BulkRequest { + + @NonNull + Collection queries; + + @Nullable + String authToken; + + byte[] toBytes( + + ) { + if (queries.isEmpty()) { + throw new IllegalArgumentException("Queries must not be empty"); + } + StringBuilder payload = new StringBuilder("{\"requests\":["); + Iterator iterator = queries.iterator(); + while (iterator.hasNext()) { + String query = iterator.next(); + payload.append("\"?").append(query).append('"'); + if (iterator.hasNext()) { + payload.append(','); + } + } + payload.append(']'); + if (authToken != null) { + payload.append(",\"token_auth\":\"").append(authToken).append('"'); + } + return payload.append('}').toString().getBytes(StandardCharsets.UTF_8); + } + +} diff --git a/core/src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java b/core/src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java index 75366390..9dc93b45 100644 --- a/core/src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java +++ b/core/src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java @@ -1,11 +1,16 @@ package org.matomo.java.tracking; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; class DaemonThreadFactory implements ThreadFactory { + + private final AtomicInteger count = new AtomicInteger(); + @Override - public Thread newThread(Runnable r) { - Thread thread = new Thread(r); + public Thread newThread(@NonNull Runnable r) { + Thread thread = new Thread(null, r, String.format("MatomoJavaTracker-%d", count.getAndIncrement())); thread.setDaemon(true); return thread; } diff --git a/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java index d38a941c..19095931 100644 --- a/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -8,7 +8,6 @@ package org.matomo.java.tracking; import static java.util.Collections.singleton; -import static java.util.Objects.requireNonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.nio.charset.Charset; @@ -590,10 +589,28 @@ public class MatomoRequest { @TrackingParameter(name = "debug") private Boolean debug; - private Iterable dimensions; + /** + * Custom Dimension values for specific Custom Dimension IDs. + * + *

Custom Dimensions plugin must be installed. See the + * Custom Dimensions guide. Requires Matomo at least 2.15.1 + */ + private Map dimensions; + /** + * Allows you to specify additional HTTP request parameters that will be sent to Matomo. + * + *

For example, you can use this to set the Accept-Language header, or to set the Content-Type. + */ private Map> additionalParameters; + /** + * You can set additional HTTP headers for the request sent to Matomo. + * + *

For example, you can use this to set the Accept-Language header, or to set the Content-Type. + */ + private Map headers; + /** * Create a new request from the id of the site being tracked and the full * url for the current action. This constructor also sets: @@ -800,9 +817,8 @@ private static CustomVariable getCustomVariable(CustomVariables customVariables, */ @Deprecated public void setPageCustomVariable( - @edu.umd.cs.findbugs.annotations.NonNull String key, @Nullable String value + @NonNull String key, @Nullable String value ) { - requireNonNull(key, "Key must not be null"); if (value == null) { if (pageCustomVariables == null) { return; @@ -923,9 +939,8 @@ public CustomVariable getVisitCustomVariable(int index) { */ @Deprecated public void setUserCustomVariable( - @edu.umd.cs.findbugs.annotations.NonNull String key, @Nullable String value + @NonNull String key, @Nullable String value ) { - requireNonNull(key, "Key must not be null"); if (value == null) { if (visitCustomVariables == null) { return; diff --git a/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java index cdc501fa..a06de566 100644 --- a/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java +++ b/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -7,16 +7,15 @@ package org.matomo.java.tracking; -import static java.util.Objects.requireNonNull; - import edu.umd.cs.findbugs.annotations.Nullable; import java.net.URI; import java.time.Duration; import java.util.Arrays; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executors; import java.util.function.Consumer; +import lombok.AccessLevel; import lombok.NonNull; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; /** @@ -39,7 +38,10 @@ public class MatomoTracker { private final TrackerConfiguration trackerConfiguration; - private final Sender sender; + @Setter(AccessLevel.PROTECTED) + private SenderFactory senderFactory = new ServiceLoaderSenderFactory(); + + private Sender sender; /** * Creates a tracker that will send {@link MatomoRequest}s to the specified @@ -51,9 +53,9 @@ public class MatomoTracker { */ @Deprecated public MatomoTracker( - @edu.umd.cs.findbugs.annotations.NonNull String hostUrl + @NonNull String hostUrl ) { - this(requireNonNull(hostUrl, "Host URL must not be null"), 0); + this(hostUrl, 0); } /** @@ -67,9 +69,9 @@ public MatomoTracker( */ @Deprecated public MatomoTracker( - @edu.umd.cs.findbugs.annotations.NonNull String hostUrl, int timeout + @NonNull String hostUrl, int timeout ) { - this(requireNonNull(hostUrl, "Host URL must not be null"), null, 0, timeout); + this(hostUrl, null, 0, timeout); } /** @@ -85,12 +87,12 @@ public MatomoTracker( */ @Deprecated public MatomoTracker( - @edu.umd.cs.findbugs.annotations.NonNull String hostUrl, @Nullable String proxyHost, int proxyPort, int timeout + @NonNull String hostUrl, @Nullable String proxyHost, int proxyPort, int timeout ) { this(TrackerConfiguration .builder() .enabled(true) - .apiEndpoint(URI.create(requireNonNull(hostUrl, "Host URL must not be null"))) + .apiEndpoint(URI.create(hostUrl)) .proxyHost(proxyHost) .proxyPort(proxyPort) .connectTimeout(timeout == -1 ? Duration.ofSeconds(5L) : Duration.ofSeconds(timeout)) @@ -104,16 +106,10 @@ public MatomoTracker( * @param trackerConfiguration Configurations parameters (you can use a builder) */ public MatomoTracker( - @edu.umd.cs.findbugs.annotations.NonNull TrackerConfiguration trackerConfiguration + @NonNull TrackerConfiguration trackerConfiguration ) { - requireNonNull(trackerConfiguration, "Tracker configuration must not be null"); trackerConfiguration.validate(); this.trackerConfiguration = trackerConfiguration; - sender = new Sender( - trackerConfiguration, - new QueryCreator(trackerConfiguration), - Executors.newFixedThreadPool(trackerConfiguration.getThreadPoolSize(), new DaemonThreadFactory()) - ); } /** @@ -128,7 +124,7 @@ public MatomoTracker( */ @Deprecated public MatomoTracker( - @edu.umd.cs.findbugs.annotations.NonNull String hostUrl, @Nullable String proxyHost, int proxyPort + @NonNull String hostUrl, @Nullable String proxyHost, int proxyPort ) { this(hostUrl, proxyHost, proxyPort, -1); } @@ -145,12 +141,20 @@ public MatomoTracker( public void sendRequest(@NonNull MatomoRequest request) { if (trackerConfiguration.isEnabled()) { log.debug("Sending request via GET: {}", request); + validate(request); + initializeSender(); sender.sendSingle(request); } else { log.warn("Not sending request, because tracker is disabled"); } } + private void initializeSender() { + if (sender == null) { + sender = senderFactory.createSender(trackerConfiguration, new QueryCreator(trackerConfiguration)); + } + } + /** * Send a request asynchronously via HTTP GET. * @@ -161,8 +165,8 @@ public void sendRequest(@NonNull MatomoRequest request) { * @param request request to send * @return completable future to let you know when the request is done */ - public CompletableFuture sendRequestAsync( - @edu.umd.cs.findbugs.annotations.NonNull MatomoRequest request + public CompletableFuture sendRequestAsync( + @NonNull MatomoRequest request ) { return sendRequestAsync(request, null); } @@ -176,18 +180,21 @@ public CompletableFuture sendRequestAsync( * * @param request request to send * @param callback callback that gets executed when response arrives, null allowed - * @return a completable future to let you know when the request is done + * @return a completable future to let you know when the request is done. The future contains either the request (if + * no callback is specified) or null (if a callback is specified) * @deprecated Please use {@link MatomoTracker#sendRequestAsync(MatomoRequest)} in combination with * {@link CompletableFuture#thenAccept(Consumer)} instead */ @Deprecated - public CompletableFuture sendRequestAsync( - @edu.umd.cs.findbugs.annotations.NonNull MatomoRequest request, @Nullable Consumer callback + public CompletableFuture sendRequestAsync( + @NonNull MatomoRequest request, + @Nullable Consumer callback ) { if (trackerConfiguration.isEnabled()) { validate(request); log.debug("Sending async request via GET: {}", request); - CompletableFuture future = sender.sendSingleAsync(request); + initializeSender(); + CompletableFuture future = sender.sendSingleAsync(request); if (callback != null) { return future.thenAccept(callback); } @@ -198,7 +205,7 @@ public CompletableFuture sendRequestAsync( } private void validate( - @edu.umd.cs.findbugs.annotations.NonNull MatomoRequest request + @NonNull MatomoRequest request ) { if (trackerConfiguration.getDefaultSiteId() == null && request.getSiteId() == null) { throw new IllegalArgumentException("No default site ID and no request site ID is given"); @@ -254,6 +261,7 @@ public void sendBulkRequest( validate(request); } log.debug("Sending requests via POST: {}", requests); + initializeSender(); sender.sendBulk(requests, authToken); } else { log.warn("Not sending request, because tracker is disabled"); @@ -279,7 +287,7 @@ public CompletableFuture sendBulkRequestAsync(MatomoRequest... requests) { * @return completable future to let you know when the request is done */ public CompletableFuture sendBulkRequestAsync( - @edu.umd.cs.findbugs.annotations.NonNull Iterable requests + @NonNull Iterable requests ) { return sendBulkRequestAsync(requests, null, null); } @@ -299,7 +307,7 @@ public CompletableFuture sendBulkRequestAsync( */ @Deprecated public CompletableFuture sendBulkRequestAsync( - @edu.umd.cs.findbugs.annotations.NonNull Iterable requests, + @NonNull Iterable requests, @Nullable String authToken, @Nullable Consumer callback ) { @@ -308,6 +316,7 @@ public CompletableFuture sendBulkRequestAsync( validate(request); } log.debug("Sending async requests via POST: {}", requests); + initializeSender(); CompletableFuture future = sender.sendBulkAsync(requests, authToken); if (callback != null) { return future.thenAccept(callback); @@ -327,7 +336,7 @@ public CompletableFuture sendBulkRequestAsync( * @return completable future to let you know when the request is done */ public CompletableFuture sendBulkRequestAsync( - @edu.umd.cs.findbugs.annotations.NonNull Iterable requests, + @NonNull Iterable requests, @Nullable Consumer callback ) { return sendBulkRequestAsync(requests, null, callback); @@ -345,7 +354,7 @@ public CompletableFuture sendBulkRequestAsync( * {@link #sendBulkRequestAsync(Iterable)} instead. */ public CompletableFuture sendBulkRequestAsync( - @edu.umd.cs.findbugs.annotations.NonNull Iterable requests, @Nullable String authToken + @NonNull Iterable requests, @Nullable String authToken ) { return sendBulkRequestAsync(requests, authToken, null); } diff --git a/core/src/main/java/org/matomo/java/tracking/QueryCreator.java b/core/src/main/java/org/matomo/java/tracking/QueryCreator.java index f324219e..55211fea 100644 --- a/core/src/main/java/org/matomo/java/tracking/QueryCreator.java +++ b/core/src/main/java/org/matomo/java/tracking/QueryCreator.java @@ -94,11 +94,11 @@ String createQuery( } } if (request.getDimensions() != null) { - int i = 0; - for (Object dimension : request.getDimensions()) { - appendAmpersand(query); - query.append("dimension").append(i + 1).append('=').append(encode(dimension.toString())); - i++; + for (Entry entry : request.getDimensions().entrySet()) { + if (entry.getKey() != null && entry.getValue() != null) { + appendAmpersand(query); + query.append("dimension").append(entry.getKey()).append('=').append(encode(entry.getValue().toString())); + } } } return query.toString(); diff --git a/core/src/main/java/org/matomo/java/tracking/RequestValidator.java b/core/src/main/java/org/matomo/java/tracking/RequestValidator.java index c802e061..a9233c4e 100644 --- a/core/src/main/java/org/matomo/java/tracking/RequestValidator.java +++ b/core/src/main/java/org/matomo/java/tracking/RequestValidator.java @@ -8,12 +8,10 @@ package org.matomo.java.tracking; -import static java.util.Objects.requireNonNull; - -import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.time.Instant; import java.time.temporal.ChronoUnit; +import lombok.NonNull; final class RequestValidator { @@ -27,7 +25,6 @@ static void validate( @Nullable CharSequence authToken ) { - requireNonNull(request, "Request must not be null"); if (request.getSiteId() != null && request.getSiteId() < 0) { throw new IllegalArgumentException("Site ID must not be negative"); } diff --git a/core/src/main/java/org/matomo/java/tracking/Sender.java b/core/src/main/java/org/matomo/java/tracking/Sender.java index 44940e67..acb82280 100644 --- a/core/src/main/java/org/matomo/java/tracking/Sender.java +++ b/core/src/main/java/org/matomo/java/tracking/Sender.java @@ -1,279 +1,25 @@ -/* - * Matomo Java Tracker - * - * @link https://github.com/matomo/matomo-java-tracker - * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause - */ - package org.matomo.java.tracking; -import static java.util.Collections.singleton; -import static java.util.Objects.requireNonNull; - import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; -import java.io.IOException; -import java.io.OutputStream; -import java.net.Authenticator; -import java.net.HttpURLConnection; -import java.net.InetSocketAddress; -import java.net.MalformedURLException; -import java.net.ProtocolException; -import java.net.Proxy; -import java.net.URI; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@RequiredArgsConstructor -class Sender { - - private static final TrustManager[] TRUST_ALL_MANAGERS = {new TrustingX509TrustManager()}; - private static final HostnameVerifier TRUSTING_HOSTNAME_VERIFIER = new TrustingHostnameVerifier(); - - private final TrackerConfiguration trackerConfiguration; - - private final QueryCreator queryCreator; - - private final Collection queries = new ArrayList<>(16); - - private final Executor executor; +interface Sender { @NonNull - CompletableFuture sendSingleAsync( + CompletableFuture sendSingleAsync( @NonNull MatomoRequest request - ) { - return CompletableFuture.supplyAsync(() -> { - sendSingle(request); - return null; - }, executor); - } + ); void sendSingle( @NonNull MatomoRequest request - ) { - String authToken = AuthToken.determineAuthToken(null, singleton(request), trackerConfiguration); - RequestValidator.validate(request, authToken); - HttpURLConnection connection; - URI apiEndpoint = trackerConfiguration.getApiEndpoint(); - try { - connection = openConnection(apiEndpoint - .resolve(String.format("%s?%s", apiEndpoint.getPath(), queryCreator.createQuery(request, authToken))) - .toURL()); - } catch (MalformedURLException e) { - throw new InvalidUrlException(e); - } - configureAgentsAndTimeouts(connection); - log.debug("Sending single request using URI {} asynchronously", apiEndpoint); - try { - connection.connect(); - checkResponse(connection); - } catch (IOException e) { - throw new MatomoException("Could not send request via GET", e); - } finally { - connection.disconnect(); - } - } - - private HttpURLConnection openConnection(URL url) { - HttpURLConnection connection; - try { - if (isEmpty(trackerConfiguration.getProxyHost()) || trackerConfiguration.getProxyPort() <= 0) { - log.debug("Proxy host or proxy port not configured. Will create connection without proxy"); - connection = (HttpURLConnection) url.openConnection(); - } else { - connection = openProxiedConnection(url); - } - } catch (IOException e) { - throw new MatomoException("Could not open connection", e); - } - if (connection instanceof HttpsURLConnection) { - applySslConfiguration((HttpsURLConnection) connection); - } - return connection; - } - - private void applySslConfiguration( - @NonNull HttpsURLConnection connection - ) { - requireNonNull(connection, "Connection must not be null"); - if (trackerConfiguration.isDisableSslCertValidation()) { - try { - SSLContext sslContext = SSLContext.getInstance("SSL"); - sslContext.init(null, TRUST_ALL_MANAGERS, new SecureRandom()); - connection.setSSLSocketFactory(sslContext.getSocketFactory()); - } catch (Exception e) { - throw new MatomoException("Could not disable SSL certification validation", e); - } - } - if (trackerConfiguration.isDisableSslHostVerification()) { - connection.setHostnameVerifier(TRUSTING_HOSTNAME_VERIFIER); - } - } - - private HttpURLConnection openProxiedConnection( - @NonNull URL url - ) throws IOException { - requireNonNull(url, "URL must not be null"); - requireNonNull(trackerConfiguration.getProxyHost(), "Proxy host must not be null"); - if (trackerConfiguration.getProxyPort() <= 0) { - throw new IllegalArgumentException("Proxy port must be configured"); - } - InetSocketAddress proxyAddress = - new InetSocketAddress(trackerConfiguration.getProxyHost(), trackerConfiguration.getProxyPort()); - Proxy proxy = new Proxy(Proxy.Type.HTTP, proxyAddress); - if (!isEmpty(trackerConfiguration.getProxyUserName()) && !isEmpty(trackerConfiguration.getProxyPassword())) { - Authenticator.setDefault(new ProxyAuthenticator(trackerConfiguration.getProxyUserName(), - trackerConfiguration.getProxyPassword() - )); - } - log.debug("Using proxy {} on port {}", trackerConfiguration.getProxyHost(), trackerConfiguration.getProxyPort()); - return (HttpURLConnection) url.openConnection(proxy); - } - - private void configureAgentsAndTimeouts(HttpURLConnection connection) { - connection.setUseCaches(false); - connection.setRequestProperty("User-Agent", trackerConfiguration.getUserAgent()); - if (trackerConfiguration.getConnectTimeout() != null) { - connection.setConnectTimeout((int) trackerConfiguration.getConnectTimeout().toMillis()); - } - if (trackerConfiguration.getSocketTimeout() != null) { - connection.setReadTimeout((int) trackerConfiguration.getSocketTimeout().toMillis()); - } - } - - private void checkResponse(HttpURLConnection connection) throws IOException { - int responseCode = connection.getResponseCode(); - if (responseCode > 399) { - if (trackerConfiguration.isLogFailedTracking()) { - log.error("Received HTTP error code {} for URL {}", responseCode, connection.getURL()); - } - throw new MatomoException(String.format("Tracking endpoint responded with code %d", responseCode)); - } - } - - private static boolean isEmpty( - @Nullable String str - ) { - return str == null || str.isEmpty() || str.trim().isEmpty(); - } + ); void sendBulk( @NonNull Iterable requests, @Nullable String overrideAuthToken - ) { - String authToken = AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); - sendBulk(StreamSupport.stream(requests.spliterator(), false).map(request -> { - RequestValidator.validate(request, authToken); - return queryCreator.createQuery(request, null); - }).collect(Collectors.toList()), authToken); - } - - private void sendBulk( - @NonNull Collection queries, @Nullable String authToken - ) { - requireNonNull(queries, "Queries must not be null"); - HttpURLConnection connection; - try { - connection = openConnection(trackerConfiguration.getApiEndpoint().toURL()); - } catch (MalformedURLException e) { - throw new InvalidUrlException(e); - } - preparePostConnection(connection); - configureAgentsAndTimeouts(connection); - log.debug("Sending bulk request using URI {} asynchronously", trackerConfiguration.getApiEndpoint()); - OutputStream outputStream = null; - try { - connection.connect(); - outputStream = connection.getOutputStream(); - outputStream.write(createPayload(queries, authToken)); - outputStream.flush(); - checkResponse(connection); - } catch (IOException e) { - throw new MatomoException("Could not send requests via POST", e); - } finally { - if (outputStream != null) { - try { - outputStream.close(); - } catch (IOException e) { - // ignore - } - } - connection.disconnect(); - } - } - - private static void preparePostConnection(HttpURLConnection connection) { - try { - connection.setRequestMethod("POST"); - } catch (ProtocolException e) { - throw new MatomoException("Could not set request method", e); - } - connection.setDoOutput(true); - connection.setRequestProperty("Accept", "*/*"); - connection.setRequestProperty("Content-Type", "application/json"); - - } - - private static byte[] createPayload( - @NonNull Collection queries, @Nullable String authToken - ) { - requireNonNull(queries, "Queries must not be null"); - StringBuilder payload = new StringBuilder("{\"requests\":["); - Iterator iterator = queries.iterator(); - while (iterator.hasNext()) { - String query = iterator.next(); - payload.append("\"?").append(query).append('"'); - if (iterator.hasNext()) { - payload.append(','); - } - } - payload.append(']'); - if (authToken != null) { - payload.append(",\"token_auth\":\"").append(authToken).append('"'); - } - return payload.append('}').toString().getBytes(StandardCharsets.UTF_8); - } + ); @NonNull CompletableFuture sendBulkAsync( @NonNull Iterable requests, @Nullable String overrideAuthToken - ) { - String authToken = AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); - synchronized (queries) { - for (MatomoRequest request : requests) { - RequestValidator.validate(request, authToken); - String query = queryCreator.createQuery(request, null); - queries.add(query); - } - } - return CompletableFuture.supplyAsync(() -> sendBulkAsync(authToken), executor); - } - - @Nullable - private Void sendBulkAsync( - @Nullable String authToken - ) { - synchronized (queries) { - if (!queries.isEmpty()) { - sendBulk(queries, authToken); - queries.clear(); - } - return null; - } - } - + ); } diff --git a/core/src/main/java/org/matomo/java/tracking/SenderFactory.java b/core/src/main/java/org/matomo/java/tracking/SenderFactory.java new file mode 100644 index 00000000..c4bfa561 --- /dev/null +++ b/core/src/main/java/org/matomo/java/tracking/SenderFactory.java @@ -0,0 +1,10 @@ +package org.matomo.java.tracking; + +/** + * A factory for {@link Sender} instances. + */ +public interface SenderFactory { + + Sender createSender(TrackerConfiguration trackerConfiguration, QueryCreator queryCreator); + +} diff --git a/core/src/main/java/org/matomo/java/tracking/SenderProvider.java b/core/src/main/java/org/matomo/java/tracking/SenderProvider.java new file mode 100644 index 00000000..12b80291 --- /dev/null +++ b/core/src/main/java/org/matomo/java/tracking/SenderProvider.java @@ -0,0 +1,7 @@ +package org.matomo.java.tracking; + +interface SenderProvider { + + Sender provideSender(TrackerConfiguration trackerConfiguration, QueryCreator queryCreator); + +} diff --git a/core/src/main/java/org/matomo/java/tracking/ServiceLoaderSenderFactory.java b/core/src/main/java/org/matomo/java/tracking/ServiceLoaderSenderFactory.java new file mode 100644 index 00000000..08968e0c --- /dev/null +++ b/core/src/main/java/org/matomo/java/tracking/ServiceLoaderSenderFactory.java @@ -0,0 +1,28 @@ +package org.matomo.java.tracking; + +import static java.util.stream.Collectors.toMap; + +import java.util.Map; +import java.util.ServiceLoader; +import java.util.function.Function; +import java.util.stream.StreamSupport; + +class ServiceLoaderSenderFactory implements SenderFactory { + + @Override + public Sender createSender(TrackerConfiguration trackerConfiguration, QueryCreator queryCreator) { + ServiceLoader serviceLoader = ServiceLoader.load(SenderProvider.class); + Map senderProviders = StreamSupport + .stream(serviceLoader.spliterator(), false) + .collect(toMap(senderProvider -> senderProvider.getClass().getName(), Function.identity())); + SenderProvider senderProvider = senderProviders.get("org.matomo.java.tracking.Java17SenderProvider"); + if (senderProvider == null) { + senderProvider = senderProviders.get("org.matomo.java.tracking.Java8SenderProvider"); + } + if (senderProvider == null) { + throw new MatomoException("No SenderProvider found"); + } + return senderProvider.provideSender(trackerConfiguration, new QueryCreator(trackerConfiguration)); + } + +} diff --git a/core/src/main/java/org/matomo/java/tracking/ServletMatomoRequestCustomizer.java b/core/src/main/java/org/matomo/java/tracking/ServletMatomoRequestCustomizer.java new file mode 100644 index 00000000..2b556d59 --- /dev/null +++ b/core/src/main/java/org/matomo/java/tracking/ServletMatomoRequestCustomizer.java @@ -0,0 +1,58 @@ +package org.matomo.java.tracking; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import lombok.NonNull; + + +/** + * Adds the headers from a {@link HttpServletRequest} to a {@link MatomoRequest.MatomoRequestBuilder}. + * + *

Use #fromServletRequest(HttpServletRequest) to create a new builder with the headers from the request or + * #addServletRequestHeaders(MatomoRequest.MatomoRequestBuilder, HttpServletRequest) to add the headers to an existing + * builder. + */ +public final class ServletMatomoRequestCustomizer { + + private ServletMatomoRequestCustomizer() { + // should not be instantiated + } + + /** + * Creates a new builder with the headers from the request. + * + *

Use #addServletRequestHeaders(MatomoRequest.MatomoRequestBuilder, HttpServletRequest) to add the headers to an + * existing builder. + * + * @param request the request to get the headers from (must not be null) + * @return a new builder with the headers from the request (never null) + */ + @edu.umd.cs.findbugs.annotations.NonNull + public static MatomoRequest.MatomoRequestBuilder fromServletRequest(@NonNull HttpServletRequest request) { + return addServletRequestHeaders(MatomoRequest.request(), request); + } + + /** + * Adds the headers from the request to an existing builder. + * + *

Use #fromServletRequest(HttpServletRequest) to create a new builder with the headers from the request. + * + * @param builder the builder to add the headers to (must not be null) + * @param request the request to get the headers from (must not be null) + * @return the builder with the headers added (never null) + */ + @edu.umd.cs.findbugs.annotations.NonNull + public static MatomoRequest.MatomoRequestBuilder addServletRequestHeaders( + @NonNull MatomoRequest.MatomoRequestBuilder builder, @NonNull HttpServletRequest request + ) { + Map headers = new HashMap<>(10); + Enumeration headerNames = request.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String headerName = headerNames.nextElement(); + headers.put(headerName, request.getHeader(headerName)); + } + return builder.headers(headers); + } +} \ No newline at end of file diff --git a/core/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java b/core/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java index 31d4129b..8e99e710 100644 --- a/core/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java +++ b/core/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java @@ -12,7 +12,6 @@ import java.time.Duration; import java.util.regex.Pattern; import lombok.Builder; -import lombok.NonNull; import lombok.Value; /** @@ -23,10 +22,11 @@ public class TrackerConfiguration { private static final Pattern AUTH_TOKEN_PATTERN = Pattern.compile("[a-z0-9]+"); + /** * The Matomo Tracking HTTP API endpoint, for example https://your-matomo-domain.example/matomo.php */ - @NonNull URI apiEndpoint; + URI apiEndpoint; /** * The default ID of the website that will be used if not specified explicitly. @@ -83,7 +83,7 @@ public class TrackerConfiguration { * password must also be set. */ @Nullable - String proxyUserName; + String proxyUsername; /** * The corresponding password for the basic auth proxy user. The proxy host, port and username must be set as well. @@ -95,7 +95,7 @@ public class TrackerConfiguration { * A custom user agent to be set. Defaults to "MatomoJavaClient" */ @Builder.Default - @NonNull String userAgent = "MatomoJavaClient"; + String userAgent = "MatomoJavaClient"; /** * Logs if the Matomo Tracking API endpoint responds with an erroneous HTTP code. Defaults to @@ -117,7 +117,11 @@ public class TrackerConfiguration { * Disables SSL host verification. This is useful for testing with self-signed certificates. Do * not use in production environments. Defaults to false. * - *

Attention: This slows down performance + *

If you use the Java 17 of the Matomo Java Tracker, this setting is ignored. Instead, you + * have to set the system property {@code jdk.internal.httpclient.disableHostnameVerification} as + * described in the + * Module + * java.net.http. * * @see #disableSslCertValidation */ @@ -130,12 +134,16 @@ public class TrackerConfiguration { * does not exceed the thread pool of the web application. Otherwise, you might run into * problems. */ + @Builder.Default int threadPoolSize = 2; /** * Validates the auth token. The auth token must be exactly 32 characters long. */ public void validate() { + if (apiEndpoint == null) { + throw new IllegalArgumentException("API endpoint must not be null"); + } if (defaultAuthToken != null) { if (defaultAuthToken.trim().length() != 32) { throw new IllegalArgumentException("Auth token must be exactly 32 characters long"); @@ -145,5 +153,35 @@ public void validate() { "Auth token must contain only lowercase letters and numbers"); } } + if (defaultSiteId != null && defaultSiteId < 0) { + throw new IllegalArgumentException("Default site ID must not be negative"); + } + if (proxyHost != null && proxyPort < 1) { + throw new IllegalArgumentException("Proxy port must be greater than 0"); + } + if (proxyPort > 0 && proxyHost == null) { + throw new IllegalArgumentException("Proxy host must be set if port is set"); + } + if (proxyUsername != null && proxyHost == null) { + throw new IllegalArgumentException("Proxy host must be set if username is set"); + } + if (proxyPassword != null && proxyHost == null) { + throw new IllegalArgumentException("Proxy host must be set if password is set"); + } + if (proxyUsername != null && proxyPassword == null) { + throw new IllegalArgumentException("Proxy password must be set if username is set"); + } + if (proxyPassword != null && proxyUsername == null) { + throw new IllegalArgumentException("Proxy username must be set if password is set"); + } + if (socketTimeout != null && socketTimeout.isNegative()) { + throw new IllegalArgumentException("Socket timeout must not be negative"); + } + if (connectTimeout != null && connectTimeout.isNegative()) { + throw new IllegalArgumentException("Connect timeout must not be negative"); + } + if (threadPoolSize < 1) { + throw new IllegalArgumentException("Thread pool size must be greater than 0"); + } } } diff --git a/core/src/main/java/org/piwik/java/tracking/PiwikTracker.java b/core/src/main/java/org/piwik/java/tracking/PiwikTracker.java index 1c9ed2f0..02e18b13 100644 --- a/core/src/main/java/org/piwik/java/tracking/PiwikTracker.java +++ b/core/src/main/java/org/piwik/java/tracking/PiwikTracker.java @@ -7,6 +7,8 @@ package org.piwik.java.tracking; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import org.matomo.java.tracking.MatomoTracker; /** @@ -25,7 +27,7 @@ public class PiwikTracker extends MatomoTracker { * @deprecated Use {@link MatomoTracker} instead. */ @Deprecated - public PiwikTracker(String hostUrl) { + public PiwikTracker(@NonNull String hostUrl) { super(hostUrl); } @@ -37,7 +39,7 @@ public PiwikTracker(String hostUrl) { * @deprecated Use {@link MatomoTracker} instead. */ @Deprecated - public PiwikTracker(String hostUrl, int timeout) { + public PiwikTracker(@NonNull String hostUrl, int timeout) { super(hostUrl, timeout); } @@ -50,7 +52,7 @@ public PiwikTracker(String hostUrl, int timeout) { * @deprecated Use {@link MatomoTracker} instead. */ @Deprecated - public PiwikTracker(String hostUrl, String proxyHost, int proxyPort) { + public PiwikTracker(@NonNull String hostUrl, @Nullable String proxyHost, int proxyPort) { super(hostUrl, proxyHost, proxyPort); } @@ -65,7 +67,7 @@ public PiwikTracker(String hostUrl, String proxyHost, int proxyPort) { * @deprecated Use {@link MatomoTracker} instead. */ @Deprecated - public PiwikTracker(String hostUrl, String proxyHost, int proxyPort, int timeout) { + public PiwikTracker(@NonNull String hostUrl, @Nullable String proxyHost, int proxyPort, int timeout) { super(hostUrl, proxyHost, proxyPort, timeout); } diff --git a/core/src/test/java/org/matomo/java/tracking/BulkRequestTest.java b/core/src/test/java/org/matomo/java/tracking/BulkRequestTest.java new file mode 100644 index 00000000..535986a0 --- /dev/null +++ b/core/src/test/java/org/matomo/java/tracking/BulkRequestTest.java @@ -0,0 +1,35 @@ +package org.matomo.java.tracking; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singleton; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.Test; + +class BulkRequestTest { + + @Test + void formatsQueriesAsJson() { + BulkRequest bulkRequest = BulkRequest.builder() + .queries(singleton("idsite=1&rec=1&action_name=TestBulkRequest")) + .authToken("token") + .build(); + + byte[] bytes = bulkRequest.toBytes(); + + assertThat(new String(bytes)).isEqualTo("{\"requests\":[\"?idsite=1&rec=1&action_name=TestBulkRequest\"],\"token_auth\":\"token\"}"); + } + + @Test + void failsIfQueriesAreEmpty() { + + BulkRequest bulkRequest = BulkRequest.builder().queries(emptyList()).build(); + + assertThatThrownBy(bulkRequest::toBytes) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Queries must not be empty"); + + } + +} \ No newline at end of file diff --git a/core/src/test/java/org/matomo/java/tracking/DaemonThreadFactoryTest.java b/core/src/test/java/org/matomo/java/tracking/DaemonThreadFactoryTest.java new file mode 100644 index 00000000..787693db --- /dev/null +++ b/core/src/test/java/org/matomo/java/tracking/DaemonThreadFactoryTest.java @@ -0,0 +1,27 @@ +package org.matomo.java.tracking; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class DaemonThreadFactoryTest { + + private final DaemonThreadFactory daemonThreadFactory = new DaemonThreadFactory(); + + @Test + void createsNewThreadAsDaemonThread() { + Thread thread = daemonThreadFactory.newThread(() -> { + // do nothing + }); + assertThat(thread.isDaemon()).isTrue(); + } + + @Test + void createsNewThreadWithMatomoJavaTrackerName() { + Thread thread = daemonThreadFactory.newThread(() -> { + // do nothing + }); + assertThat(thread.getName()).startsWith("MatomoJavaTracker-"); + } + +} \ No newline at end of file diff --git a/core/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/core/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java index ab55026c..51c361b2 100644 --- a/core/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java +++ b/core/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java @@ -1,525 +1,218 @@ package org.matomo.java.tracking; -import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; -import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.status; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static java.util.Collections.singleton; -import static java.util.concurrent.TimeUnit.MINUTES; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import com.github.tomakehurst.wiremock.WireMockServer; -import com.github.tomakehurst.wiremock.core.WireMockConfiguration; import java.net.URI; -import java.time.LocalDateTime; -import java.time.ZoneOffset; -import java.util.Arrays; -import java.util.Locale.LanguageRange; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.matomo.java.tracking.MatomoRequest.MatomoRequestBuilder; -import org.matomo.java.tracking.TrackerConfiguration.TrackerConfigurationBuilder; -import org.matomo.java.tracking.parameters.AcceptLanguage; -import org.matomo.java.tracking.parameters.Country; -import org.matomo.java.tracking.parameters.CustomVariable; -import org.matomo.java.tracking.parameters.CustomVariables; -import org.matomo.java.tracking.parameters.DeviceResolution; -import org.matomo.java.tracking.parameters.EcommerceItem; -import org.matomo.java.tracking.parameters.EcommerceItems; import org.matomo.java.tracking.parameters.RandomValue; -import org.matomo.java.tracking.parameters.UniqueId; import org.matomo.java.tracking.parameters.VisitorId; class MatomoTrackerIT { - private static final WireMockServer wireMockServer = - new WireMockServer(WireMockConfiguration.options().dynamicPort()); - - private static final int SITE_ID = 42; - - private final TrackerConfigurationBuilder trackerConfigurationBuilder = TrackerConfiguration.builder(); - - private final MatomoRequestBuilder requestBuilder = MatomoRequest - .builder() - .visitorId(VisitorId.fromHex("bbccddeeff1122")) - .randomValue(RandomValue.fromString("someRandom")); - - private CompletableFuture future; - + private static final String HOST_URL = "http://localhost:8080/matomo.php"; + public static final String QUERY = + "rec=1&idsite=1&action_name=test&apiv=1&_id=00000000343efaf5&send_image=0&rand=test-random"; private MatomoTracker matomoTracker; - - @BeforeAll - static void beforeAll() { - wireMockServer.start(); - } - - @BeforeEach - void givenStub() { - wireMockServer.resetRequests(); - wireMockServer.stubFor(post(urlPathEqualTo("/matomo.php")).willReturn(status(204))); - wireMockServer.stubFor(get(urlPathEqualTo("/matomo.php")).willReturn(status(204))); - } + private final TestSenderFactory senderFactory = new TestSenderFactory(); + private final MatomoRequest request = MatomoRequest + .request() + .siteId(1) + .visitorId(VisitorId.fromString("test-visitor-id")) + .randomValue(RandomValue.fromString("test-random")) + .actionName("test") + .build(); @Test - void requiresApiEndpoint() { - - assertThatThrownBy(() -> trackerConfigurationBuilder.defaultSiteId(SITE_ID).build()) - .isInstanceOf(NullPointerException.class) - .hasMessage("apiEndpoint is marked non-null but is null"); + void sendsRequest() { - } + matomoTracker = new MatomoTracker(HOST_URL); + matomoTracker.setSenderFactory(senderFactory); - @Test - void requiresSiteId() { + matomoTracker.sendRequest(request); - trackerConfigurationBuilder.apiEndpoint(URI.create(wireMockServer.baseUrl() + "/matomo.php")).build(); - - assertThatThrownBy(this::whenSendsRequestAsync) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("No default site ID and no request site ID is given"); + TestSender testSender = senderFactory.getTestSender(); + thenContainsRequest(testSender, QUERY); + assertThat(testSender.getTrackerConfiguration().getApiEndpoint()).hasToString(HOST_URL); } - private void whenSendsRequestAsync() { - matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); - future = matomoTracker.sendRequestAsync(requestBuilder.build()); - } - - @Test - void usesDefaultSiteId() { - - givenTrackerConfigurationWithDefaultSiteId(); - - whenSendsRequestAsync(); - - thenGetsRequest("idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom"); - - } - - private void givenTrackerConfigurationWithDefaultSiteId() { - trackerConfigurationBuilder - .apiEndpoint(URI.create(wireMockServer.baseUrl() + "/matomo.php")) - .defaultSiteId(SITE_ID); - } - - private void thenGetsRequest(String expectedQuery) { - assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { - wireMockServer.verify(getRequestedFor(urlEqualTo( - String.format("/matomo.php?%s", expectedQuery))).withHeader("User-Agent", - equalTo("MatomoJavaClient") - )); - }); - } - @Test - void overridesDefaultSiteId() { - - givenTrackerConfigurationWithDefaultSiteId(); - requestBuilder.siteId(123); + void validatesRequest() { - whenSendsRequestAsync(); + matomoTracker = new MatomoTracker(HOST_URL); + matomoTracker.setSenderFactory(senderFactory); + request.setSiteId(null); - thenGetsRequest("rec=1&idsite=123&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom"); - - } - - @Test - void validatesTokenAuth() { - - givenTrackerConfigurationWithDefaultSiteId(); - requestBuilder.authToken("invalid-token-auth"); - - whenSendsRequestAsync(); - - assertThat(future) - .failsWithin(1, MINUTES) - .withThrowableThat() - .havingRootCause() + assertThatThrownBy(() -> matomoTracker.sendRequest(request)) .isInstanceOf(IllegalArgumentException.class) - .withMessage("Auth token must be exactly 32 characters long"); - - } - - @Test - void convertsTrueBooleanTo1() { - - givenTrackerConfigurationWithDefaultSiteId(); - requestBuilder.pluginFlash(true); - - whenSendsRequestAsync(); - - thenGetsRequest("idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&fla=1&send_image=0&rand=someRandom"); + .hasMessageContaining("No default site ID and no request site ID is given"); - } - - @Test - void convertsFalseBooleanTo0() { - - givenTrackerConfigurationWithDefaultSiteId(); - requestBuilder.pluginJava(false); - - whenSendsRequestAsync(); - - thenGetsRequest("idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&java=0&send_image=0&rand=someRandom"); + assertThat(senderFactory.getTestSender()).isNull(); } @Test - void encodesUrl() { + void doesNotSendRequestIfNotEnabled() { - givenTrackerConfigurationWithDefaultSiteId(); - requestBuilder.actionUrl("https://www.daniel-heid.de/some/page?foo=bar"); + matomoTracker = + new MatomoTracker(TrackerConfiguration.builder().apiEndpoint(URI.create(HOST_URL)).enabled(false).build()); + matomoTracker.setSenderFactory(senderFactory); - whenSendsRequestAsync(); + matomoTracker.sendRequest(request); - thenGetsRequest( - "idsite=42&rec=1&url=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fpage%3Ffoo%3Dbar&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom"); + assertThat(senderFactory.getTestSender()).isNull(); } @Test - void encodesReferrerUrl() { + void sendsRequestUsingProxy() { - givenTrackerConfigurationWithDefaultSiteId(); - requestBuilder.referrerUrl("https://www.daniel-heid.de/some/referrer?foo2=bar2"); + matomoTracker = new MatomoTracker(HOST_URL, "localhost", 8081); + matomoTracker.setSenderFactory(senderFactory); - whenSendsRequestAsync(); + matomoTracker.sendRequest(request); - thenGetsRequest( - "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Freferrer%3Ffoo2%3Dbar2&send_image=0&rand=someRandom"); + TestSender testSender = senderFactory.getTestSender(); + thenContainsRequest(testSender, QUERY); + TrackerConfiguration trackerConfiguration = testSender.getTrackerConfiguration(); + assertThat(trackerConfiguration.getProxyHost()).isEqualTo("localhost"); + assertThat(trackerConfiguration.getProxyPort()).isEqualTo(8081); } @Test - void encodesLink() { + void sendsRequestAsync() { - givenTrackerConfigurationWithDefaultSiteId(); - requestBuilder.outlinkUrl("https://www.daniel-heid.de/some/external/link#"); + matomoTracker = new MatomoTracker(HOST_URL, 1000); + matomoTracker.setSenderFactory(senderFactory); - whenSendsBulkRequestAsync(); + matomoTracker.sendRequestAsync(request); - thenPostsRequestWithoutAuthToken( - "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&link=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fexternal%2Flink%23&send_image=0&rand=someRandom", - "156" - ); + TestSender testSender = senderFactory.getTestSender(); + thenContainsRequest(testSender, QUERY); + assertThat(testSender.getTrackerConfiguration().getApiEndpoint()).hasToString(HOST_URL); } @Test - void sendsRequestsBulkAsynchronously() { - - givenTrackerConfigurationWithDefaultSiteId(); - matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); + void sendsRequestAsyncWithCallback() { - future = matomoTracker.sendBulkRequestAsync(requestBuilder.build()); - - thenPostsRequestWithoutAuthToken("idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom", "90"); - - } - - private void whenSendsBulkRequestAsync() { - future = - new MatomoTracker(trackerConfigurationBuilder.build()).sendBulkRequestAsync(singleton(requestBuilder.build())); - } - - private void thenPostsRequestWithoutAuthToken(String expectedQuery, String contentLength) { - assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { - wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) - .withHeader("Content-Length", equalTo(contentLength)) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient")) - .withRequestBody(equalToJson("{\"requests\":[\"?" + expectedQuery + "\"]}"))); + matomoTracker = new MatomoTracker(HOST_URL, 1000); + matomoTracker.setSenderFactory(senderFactory); + AtomicBoolean callbackCalled = new AtomicBoolean(); + matomoTracker.sendRequestAsync(request, request -> { + assertThat(request).isEqualTo(request); + callbackCalled.set(true); }); - } - - @Test - void encodesDownloadUrl() { - - givenTrackerConfigurationWithDefaultSiteId(); - requestBuilder.downloadUrl("https://www.daniel-heid.de/some/download.pdf"); - - whenSendsBulkRequestAsync(); - - thenPostsRequestWithoutAuthToken( - "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&download=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fdownload.pdf&send_image=0&rand=someRandom", - "154" - ); - - } - - @Test - void getContainsHeaders() { - - givenTrackerConfigurationWithDefaultSiteId(); - - whenSendsRequestAsync(); - - assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { - wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php")).withHeader("User-Agent", - equalTo("MatomoJavaClient") - )); - }); - - } - - @Test - void postContainsHeaders() { - - givenTrackerConfigurationWithDefaultSiteId(); - - whenSendsBulkRequestAsync(); - - assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { - wireMockServer.verify(postRequestedFor(urlPathEqualTo("/matomo.php")) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Length", equalTo("90")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient"))); - }); - - } - - @Test - void allowsToOverrideUserAgent() { - - givenTrackerConfigurationWithDefaultSiteId(); - trackerConfigurationBuilder.userAgent("Mozilla/5.0"); - - whenSendsRequestAsync(); - - assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { - wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php")).withHeader("User-Agent", - equalTo("Mozilla/5.0") - )); - }); - - } - - @Test - void tracksMinimalRequest() { - - givenTrackerConfigurationWithDefaultSiteId(); - requestBuilder - .actionName("Help / Feedback") - .actionUrl("https://www.daniel-heid.de/portfolio") - .visitorId(VisitorId.fromHash(3434343434343434343L)) - .referrerUrl("https://www.daniel-heid.de/referrer") - .visitCustomVariables(new CustomVariables() - .add(new CustomVariable("customVariable1Key", "customVariable1Value"), 4) - .add(new CustomVariable("customVariable2Key", "customVariable2Value"), 5)) - .visitorVisitCount(2) - .visitorFirstVisitTimestamp(LocalDateTime.of(2022, 8, 9, 18, 34, 12).toInstant(ZoneOffset.UTC)) - .deviceResolution(DeviceResolution.builder().width(1024).height(768).build()) - .headerAcceptLanguage(AcceptLanguage - .builder() - .languageRange(new LanguageRange("de")) - .languageRange(new LanguageRange("de-DE", 0.9)) - .languageRange(new LanguageRange("en", 0.8)) - .build()) - .pageViewId(UniqueId.fromValue(999999999999999999L)) - .goalId(0) - .ecommerceRevenue(12.34) - .ecommerceItems(EcommerceItems - .builder() - .item(org.matomo.java.tracking.parameters.EcommerceItem.builder().sku("SKU").build()) - .item(EcommerceItem.builder().sku("SKU").name("NAME").category("CATEGORY").price(123.4).build()) - .build()) - .authToken("fdf6e8461ea9de33176b222519627f78") - .visitorCountry(Country.fromLanguageRanges("en-GB;q=0.7,de,de-DE;q=0.9,en;q=0.8,en-US;q=0.6")); - - whenSendsBulkRequestAsync(); - - assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { - wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) - .withHeader("Content-Length", equalTo("711")) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient")) - .withRequestBody(equalToJson("{\"requests\":[\"?" - + "idsite=42&rec=1&action_name=Help+%2F+Feedback&url=https%3A%2F%2Fwww.daniel-heid.de%2Fportfolio&apiv=1&_id=2fa93d2858bc4867&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Freferrer&_cvar=%7B%224%22%3A%5B%22customVariable1Key%22%2C%22customVariable1Value%22%5D%2C%225%22%3A%5B%22customVariable2Key%22%2C%22customVariable2Value%22%5D%7D&_idvc=2&_idts=1660070052&res=1024x768&lang=de%2Cde-de%3Bq%3D0.9%2Cen%3Bq%3D0.8&pv_id=lbBbxG&idgoal=0&revenue=12.34&ec_items=%5B%5B%22SKU%22%2C%22%22%2C%22%22%2C0.0%2C0%5D%2C%5B%22SKU%22%2C%22NAME%22%2C%22CATEGORY%22%2C123.4%2C0%5D%5D&token_auth=fdf6e8461ea9de33176b222519627f78&country=de&send_image=0&rand=someRandom" - + "\"],\"token_auth\" : \"" + "fdf6e8461ea9de33176b222519627f78" + "\"}"))); - - }); + TestSender testSender = senderFactory.getTestSender(); + thenContainsRequest(testSender, QUERY); + assertThat(testSender.getTrackerConfiguration().getApiEndpoint()).hasToString(HOST_URL); + assertThat(callbackCalled).isTrue(); } @Test - void doesNothingIfNotEnabled() throws Exception { + void doesNotSendRequestAsyncIfNotEnabled() { - wireMockServer.resetRequests(); - givenTrackerConfigurationWithDefaultSiteId(); - trackerConfigurationBuilder.enabled(false); + matomoTracker = + new MatomoTracker(TrackerConfiguration.builder().apiEndpoint(URI.create(HOST_URL)).enabled(false).build()); + matomoTracker.setSenderFactory(senderFactory); - whenSendsRequestAsync(); - future.get(); + matomoTracker.sendRequestAsync(request); - wireMockServer.verify(0, postRequestedFor(urlPathEqualTo("/matomo.php"))); + assertThat(senderFactory.getTestSender()).isNull(); } @Test - void reportsErrors() { + void sendsBulkRequests() { - wireMockServer.stubFor(get(urlPathEqualTo("/failing")).willReturn(status(500))); - trackerConfigurationBuilder.apiEndpoint(URI.create(wireMockServer.baseUrl() + "/failing")).defaultSiteId(SITE_ID); + matomoTracker = new MatomoTracker(HOST_URL); + matomoTracker.setSenderFactory(senderFactory); - whenSendsRequestAsync(); + matomoTracker.sendBulkRequest(request); - assertThat(future) - .failsWithin(1, MINUTES) - .withThrowableThat() - .havingRootCause() - .isInstanceOf(MatomoException.class) - .withMessage("Tracking endpoint responded with code 500"); + TestSender testSender = senderFactory.getTestSender(); + thenContainsRequest(testSender, QUERY); + assertThat(testSender.getTrackerConfiguration().getApiEndpoint()).hasToString(HOST_URL); } @Test - void includesDefaultTokenAuth() { + void doesNotSendBulkRequestsIfNotEnabled() { - givenTrackerConfigurationWithDefaultSiteId(); - trackerConfigurationBuilder.defaultAuthToken("fdf6e8461ea9de33176b222519627f78"); + matomoTracker = + new MatomoTracker(TrackerConfiguration.builder().apiEndpoint(URI.create(HOST_URL)).enabled(false).build()); + matomoTracker.setSenderFactory(senderFactory); - whenSendsRequestAsync(); + matomoTracker.sendBulkRequest(request); - assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { - wireMockServer.verify(getRequestedFor(urlEqualTo( - "/matomo.php?idsite=42&token_auth=fdf6e8461ea9de33176b222519627f78&rec=1&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom")).withHeader("User-Agent", - equalTo("MatomoJavaClient") - )); - }); + assertThat(senderFactory.getTestSender()).isNull(); } @Test - void includesMultipleQueriesInBulkRequest() throws Exception { - - givenTrackerConfigurationWithDefaultSiteId(); - matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); - - future = matomoTracker.sendBulkRequestAsync(Arrays.asList(requestBuilder.actionName("First").build(), - requestBuilder.actionName("Second").build(), - requestBuilder.actionName("Third").build() - )); - - assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { - wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) - .withHeader("Content-Length", equalTo("297")) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient")) - .withRequestBody(equalToJson( - "{\"requests\" : [ \"?idsite=42&rec=1&action_name=First&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\", \"?idsite=42&rec=1&action_name=Second&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\", \"?idsite=42&rec=1&action_name=Third&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\" ]}"))); - }); + void sendsBulkRequestsAsync() { - } + matomoTracker = new MatomoTracker(HOST_URL, 1000); + matomoTracker.setSenderFactory(senderFactory); - @Test - void failsOnNegativeSiteId() { + matomoTracker.sendBulkRequestAsync(request); - givenTrackerConfigurationWithDefaultSiteId(); - requestBuilder.siteId(-1); - - whenSendsRequestAsync(); - - assertThat(future) - .failsWithin(1, MINUTES) - .withThrowableThat() - .havingRootCause() - .isInstanceOf(IllegalArgumentException.class) - .withMessage("Site ID must not be negative"); + TestSender testSender = senderFactory.getTestSender(); + thenContainsRequest(testSender, QUERY); + assertThat(testSender.getTrackerConfiguration().getApiEndpoint()).hasToString(HOST_URL); - ; } @Test - void doesNotSendRequestAsyncIfTrackerConfigurationIsDisabled() throws Exception { - givenTrackerConfigurationWithDefaultSiteId(); - trackerConfigurationBuilder.enabled(false); + void doesNotSendBulkRequestsAsyncIfNotEnabled() { - whenSendsRequestAsync(); - future.get(); + matomoTracker = + new MatomoTracker(TrackerConfiguration.builder().apiEndpoint(URI.create(HOST_URL)).enabled(false).build()); + matomoTracker.setSenderFactory(senderFactory); - wireMockServer.verify(0, getRequestedFor(urlPathEqualTo("/matomo.php"))); + matomoTracker.sendBulkRequestAsync(request); - } - - @Test - void doesNotSendRequestIfTrackerConfigurationIsDisabled() { - givenTrackerConfigurationWithDefaultSiteId(); - trackerConfigurationBuilder.enabled(false); - - whenSendsSingleRequest(); + assertThat(senderFactory.getTestSender()).isNull(); - wireMockServer.verify(0, getRequestedFor(urlPathEqualTo("/matomo.php"))); - } - - private void whenSendsSingleRequest() { - new MatomoTracker(trackerConfigurationBuilder.build()).sendRequest(requestBuilder.build()); } @Test - void doesNotSendBulkRequestIfTrackerConfigurationIsDisabled() { - givenTrackerConfigurationWithDefaultSiteId(); - trackerConfigurationBuilder.enabled(false); + void sendsBulkRequestAsyncWithCallback() { - whenSendsBulkRequest(); + matomoTracker = new MatomoTracker(HOST_URL, 1000); + matomoTracker.setSenderFactory(senderFactory); + AtomicBoolean callbackCalled = new AtomicBoolean(); + matomoTracker.sendBulkRequestAsync(singleton(request), v -> callbackCalled.set(true)); - wireMockServer.verify(0, postRequestedFor(urlPathEqualTo("/matomo.php"))); - } + TestSender testSender = senderFactory.getTestSender(); + thenContainsRequest(testSender, QUERY); + assertThat(testSender.getTrackerConfiguration().getApiEndpoint()).hasToString(HOST_URL); + assertThat(callbackCalled).isTrue(); - private void whenSendsBulkRequest() { - new MatomoTracker(trackerConfigurationBuilder.build()).sendBulkRequest(singleton(requestBuilder.build())); } @Test - void doesNotSendBulkRequestAsyncIfTrackerConfigurationIsDisabled() throws Exception { - givenTrackerConfigurationWithDefaultSiteId(); - trackerConfigurationBuilder.enabled(false); + void sendsBulkRequestAsyncWithAuthToken() { - whenSendsBulkRequestAsync(); + matomoTracker = new MatomoTracker(HOST_URL, 1000); + matomoTracker.setSenderFactory(senderFactory); + matomoTracker.sendBulkRequestAsync(singleton(request), "abc123def456abc123def456abc123de"); - future.get(); - wireMockServer.verify(0, postRequestedFor(urlPathEqualTo("/matomo.php"))); - } + TestSender testSender = senderFactory.getTestSender(); + thenContainsRequest(testSender, "token_auth=abc123def456abc123def456abc123de&rec=1&idsite=1&action_name=test&apiv=1&_id=00000000343efaf5&send_image=0&rand=test-random"); + assertThat(testSender.getTrackerConfiguration().getApiEndpoint()).hasToString(HOST_URL); - @Test - void sendsRequestAsyncAndAcceptsCallback() throws Exception { - givenTrackerConfigurationWithDefaultSiteId(); - matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); - AtomicBoolean success = new AtomicBoolean(); - future = matomoTracker.sendRequestAsync(requestBuilder.build(), v -> { - success.set(true); - }); - assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { - wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php"))); - }); - assertThat(success).isTrue(); } - @Test - void sendsRequestsAsyncAndAcceptsCallback() throws Exception { - givenTrackerConfigurationWithDefaultSiteId(); - matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); - AtomicBoolean success = new AtomicBoolean(); - future = matomoTracker.sendBulkRequestAsync(singleton(requestBuilder.build()), v -> { - success.set(true); - }); - assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { - wireMockServer.verify(postRequestedFor(urlPathEqualTo("/matomo.php"))); - }); - assertThat(success).isTrue(); + private void thenContainsRequest(TestSender testSender, String query) { + assertThat(testSender.getRequests()).containsExactly(request); + assertThat(testSender.getQueries()).containsExactly(query); } -} +} \ No newline at end of file diff --git a/core/src/test/java/org/matomo/java/tracking/MockHttpServletRequest.java b/core/src/test/java/org/matomo/java/tracking/MockHttpServletRequest.java new file mode 100644 index 00000000..bd73d135 --- /dev/null +++ b/core/src/test/java/org/matomo/java/tracking/MockHttpServletRequest.java @@ -0,0 +1,384 @@ +package org.matomo.java.tracking; + +import jakarta.servlet.AsyncContext; +import jakarta.servlet.DispatcherType; +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletConnection; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import jakarta.servlet.http.HttpUpgradeHandler; +import jakarta.servlet.http.Part; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Locale; +import java.util.Map; +import lombok.Setter; + +@Setter +class MockHttpServletRequest implements HttpServletRequest { + + private Map headers; + + @Override + public String getAuthType() { + return null; + } + + @Override + public Cookie[] getCookies() { + return new Cookie[0]; + } + + @Override + public long getDateHeader(String name) { + return 0; + } + + @Override + public String getHeader(String name) { + return headers.get(name); + } + + @Override + public Enumeration getHeaders(String name) { + return null; + } + + @Override + public Enumeration getHeaderNames() { + return Collections.enumeration(headers.keySet()); + } + + @Override + public int getIntHeader(String name) { + return 0; + } + + @Override + public String getMethod() { + return null; + } + + @Override + public String getPathInfo() { + return null; + } + + @Override + public String getPathTranslated() { + return null; + } + + @Override + public String getContextPath() { + return null; + } + + @Override + public String getQueryString() { + return null; + } + + @Override + public String getRemoteUser() { + return null; + } + + @Override + public boolean isUserInRole(String role) { + return false; + } + + @Override + public Principal getUserPrincipal() { + return null; + } + + @Override + public String getRequestedSessionId() { + return null; + } + + @Override + public String getRequestURI() { + return null; + } + + @Override + public StringBuffer getRequestURL() { + return null; + } + + @Override + public String getServletPath() { + return null; + } + + @Override + public HttpSession getSession(boolean create) { + return null; + } + + @Override + public HttpSession getSession() { + return null; + } + + @Override + public String changeSessionId() { + return null; + } + + @Override + public boolean isRequestedSessionIdValid() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromCookie() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromURL() { + return false; + } + + @Override + public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { + return false; + } + + @Override + public void login(String username, String password) throws ServletException { + + } + + @Override + public void logout() throws ServletException { + + } + + @Override + public Collection getParts() throws IOException, ServletException { + return null; + } + + @Override + public Part getPart(String name) throws IOException, ServletException { + return null; + } + + @Override + public T upgrade(Class handlerClass) throws IOException, ServletException { + return null; + } + + @Override + public Object getAttribute(String name) { + return null; + } + + @Override + public Enumeration getAttributeNames() { + return null; + } + + @Override + public String getCharacterEncoding() { + return null; + } + + @Override + public void setCharacterEncoding(String env) throws UnsupportedEncodingException { + + } + + @Override + public int getContentLength() { + return 0; + } + + @Override + public long getContentLengthLong() { + return 0; + } + + @Override + public String getContentType() { + return null; + } + + @Override + public ServletInputStream getInputStream() throws IOException { + return null; + } + + @Override + public String getParameter(String name) { + return null; + } + + @Override + public Enumeration getParameterNames() { + return null; + } + + @Override + public String[] getParameterValues(String name) { + return new String[0]; + } + + @Override + public Map getParameterMap() { + return null; + } + + @Override + public String getProtocol() { + return null; + } + + @Override + public String getScheme() { + return null; + } + + @Override + public String getServerName() { + return null; + } + + @Override + public int getServerPort() { + return 0; + } + + @Override + public BufferedReader getReader() throws IOException { + return null; + } + + @Override + public String getRemoteAddr() { + return null; + } + + @Override + public String getRemoteHost() { + return null; + } + + @Override + public void setAttribute(String name, Object o) { + + } + + @Override + public void removeAttribute(String name) { + + } + + @Override + public Locale getLocale() { + return null; + } + + @Override + public Enumeration getLocales() { + return null; + } + + @Override + public boolean isSecure() { + return false; + } + + @Override + public RequestDispatcher getRequestDispatcher(String path) { + return null; + } + + @Override + public int getRemotePort() { + return 0; + } + + @Override + public String getLocalName() { + return null; + } + + @Override + public String getLocalAddr() { + return null; + } + + @Override + public int getLocalPort() { + return 0; + } + + @Override + public ServletContext getServletContext() { + return null; + } + + @Override + public AsyncContext startAsync() throws IllegalStateException { + return null; + } + + @Override + public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) + throws IllegalStateException { + return null; + } + + @Override + public boolean isAsyncStarted() { + return false; + } + + @Override + public boolean isAsyncSupported() { + return false; + } + + @Override + public AsyncContext getAsyncContext() { + return null; + } + + @Override + public DispatcherType getDispatcherType() { + return null; + } + + @Override + public String getRequestId() { + return null; + } + + @Override + public String getProtocolRequestId() { + return null; + } + + @Override + public ServletConnection getServletConnection() { + return null; + } +} diff --git a/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java b/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java index f31c5837..54d21373 100644 --- a/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java +++ b/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java @@ -12,6 +12,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Locale.LanguageRange; import java.util.Map; import org.junit.jupiter.api.Test; @@ -388,12 +389,15 @@ void testAuthTokenTT() { @Test void createsQueryWithDimensions() { - matomoRequestBuilder.dimensions(asList("firstDimension", "secondDimension")); + Map dimensions = new LinkedHashMap<>(); + dimensions.put(1L, "firstDimension"); + dimensions.put(3L, "thirdDimension"); + matomoRequestBuilder.dimensions(dimensions); whenCreatesQuery(); assertThat(query).isEqualTo( - "idsite=42&token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value&dimension1=firstDimension&dimension2=secondDimension"); + "idsite=42&token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&send_image=0&rand=random-value&dimension1=firstDimension&dimension3=thirdDimension"); } @Test diff --git a/core/src/test/java/org/matomo/java/tracking/RequestValidatorTest.java b/core/src/test/java/org/matomo/java/tracking/RequestValidatorTest.java index b6aec7ab..cdb8e90c 100644 --- a/core/src/test/java/org/matomo/java/tracking/RequestValidatorTest.java +++ b/core/src/test/java/org/matomo/java/tracking/RequestValidatorTest.java @@ -163,4 +163,20 @@ void testRequestDatetime() { } + @Test + void failsIfSiteIdIsNegative() { + request.setSiteId(-1); + + assertThatThrownBy(() -> RequestValidator.validate(request, null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Site ID must not be negative"); + } + + @Test + void failsIfAuthTokenIsNot32CharactersLong() { + assertThatThrownBy(() -> RequestValidator.validate(request, "123456789012345678901234567890")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Auth token must be exactly 32 characters long"); + } + } diff --git a/core/src/test/java/org/matomo/java/tracking/SenderIT.java b/core/src/test/java/org/matomo/java/tracking/SenderIT.java deleted file mode 100644 index c571ddaa..00000000 --- a/core/src/test/java/org/matomo/java/tracking/SenderIT.java +++ /dev/null @@ -1,148 +0,0 @@ -package org.matomo.java.tracking; - -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.status; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import com.github.tomakehurst.wiremock.WireMockServer; -import com.github.tomakehurst.wiremock.core.WireMockConfiguration; -import java.net.MalformedURLException; -import java.net.URI; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -class SenderIT { - - private static final WireMockServer wireMockServer = - new WireMockServer(WireMockConfiguration.options().dynamicPort().dynamicHttpsPort()); - - @BeforeAll - static void beforeAll() { - wireMockServer.start(); - } - - @Test - void sendSingleFailsIfQueryIsMalformed() { - TrackerConfiguration trackerConfiguration = - TrackerConfiguration.builder().apiEndpoint(URI.create("telnet://localhost")).build(); - Sender sender = - new Sender(trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); - - assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) - .isInstanceOf(InvalidUrlException.class) - .hasRootCause(new MalformedURLException("unknown protocol: telnet")); - } - - @Test - void failsIfEndpointReturnsNotFound() { - TrackerConfiguration trackerConfiguration = TrackerConfiguration - .builder() - .apiEndpoint(URI.create(wireMockServer.baseUrl())) - .disableSslHostVerification(true) - .disableSslCertValidation(true) - .build(); - - Sender sender = - new Sender(trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); - - assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) - .isInstanceOf(MatomoException.class) - .hasMessage("Tracking endpoint responded with code 404"); - } - - @Test - void failsIfCouldNotConnectToEndpoint() { - TrackerConfiguration trackerConfiguration = - TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost:1234")).build(); - - Sender sender = - new Sender(trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); - - assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) - .isInstanceOf(MatomoException.class) - .hasMessage("Could not send request via GET"); - } - - @Test - void connectsViaProxy() throws Exception { - TrackerConfiguration trackerConfiguration = TrackerConfiguration - .builder() - .apiEndpoint(URI.create(wireMockServer.baseUrl())) - .disableSslCertValidation(true) - .disableSslHostVerification(true) - .proxyHost("localhost") - .proxyPort(wireMockServer.port()) - .build(); - - Sender sender = - new Sender(trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); - - assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) - .isInstanceOf(MatomoException.class) - .hasMessage("Could not send request via GET"); - } - - @Test - void connectsViaProxyWithProxyUserNameAndPassword() throws Exception { - TrackerConfiguration trackerConfiguration = TrackerConfiguration - .builder() - .apiEndpoint(URI.create(wireMockServer.baseUrl())) - .disableSslCertValidation(true) - .disableSslHostVerification(true) - .proxyHost("localhost") - .proxyPort(wireMockServer.port()) - .proxyUserName("user") - .proxyPassword("password") - .build(); - - Sender sender = - new Sender(trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); - - assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) - .isInstanceOf(MatomoException.class) - .hasMessage("Could not send request via GET"); - } - - @Test - void logsFailedTracking() throws Exception { - TrackerConfiguration trackerConfiguration = TrackerConfiguration - .builder() - .apiEndpoint(URI.create(wireMockServer.baseUrl())) - .disableSslCertValidation(true) - .disableSslHostVerification(true) - .logFailedTracking(true) - .build(); - - Sender sender = - new Sender(trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); - - assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) - .isInstanceOf(MatomoException.class) - .hasMessage("Tracking endpoint responded with code 404"); - } - - @Test - void skipSslCertificationValidation() { - wireMockServer.stubFor(get(urlPathEqualTo("/matomo_ssl.php")).willReturn(status(204))); - TrackerConfiguration trackerConfiguration = - TrackerConfiguration - .builder() - .apiEndpoint(URI.create(String.format("https://localhost:%d/matomo_ssl.php", - wireMockServer.httpsPort() - ))) - .disableSslCertValidation(true) - .disableSslHostVerification(true) - .build(); - - Sender sender = - new Sender(trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); - - sender.sendSingle(new MatomoRequest()); - - wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo_ssl.php"))); - - } - -} diff --git a/core/src/test/java/org/matomo/java/tracking/ServiceLoaderSenderFactoryTest.java b/core/src/test/java/org/matomo/java/tracking/ServiceLoaderSenderFactoryTest.java new file mode 100644 index 00000000..0a5d2253 --- /dev/null +++ b/core/src/test/java/org/matomo/java/tracking/ServiceLoaderSenderFactoryTest.java @@ -0,0 +1,24 @@ +package org.matomo.java.tracking; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.net.URI; +import org.junit.jupiter.api.Test; + +class ServiceLoaderSenderFactoryTest { + + @Test + void failsIfNoImplementationFound() { + ServiceLoaderSenderFactory serviceLoaderSenderFactory = new ServiceLoaderSenderFactory(); + + TrackerConfiguration trackerConfiguration = + TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost/matomo.php")).build(); + + assertThatThrownBy(() -> serviceLoaderSenderFactory.createSender(trackerConfiguration, + new QueryCreator(trackerConfiguration) + )) + .isInstanceOf(MatomoException.class) + .hasMessage("No SenderProvider found"); + } + +} \ No newline at end of file diff --git a/core/src/test/java/org/matomo/java/tracking/ServletMatomoRequestCustomizerTest.java b/core/src/test/java/org/matomo/java/tracking/ServletMatomoRequestCustomizerTest.java new file mode 100644 index 00000000..3994a083 --- /dev/null +++ b/core/src/test/java/org/matomo/java/tracking/ServletMatomoRequestCustomizerTest.java @@ -0,0 +1,38 @@ +package org.matomo.java.tracking; + +import static java.util.Collections.singletonMap; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.Test; + +class ServletMatomoRequestCustomizerTest { + + @Test + void addsServletRequestHeaders() { + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setHeaders(singletonMap("headerName", "headerValue")); + + MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequestCustomizer.fromServletRequest(request); + + MatomoRequest matomoRequest = builder.build(); + assertThat(matomoRequest.getHeaders()).hasSize(1).containsEntry("headerName", "headerValue"); + } + + @Test + void failsIfServletRequestIsNull() { + assertThatThrownBy(() -> ServletMatomoRequestCustomizer.fromServletRequest(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("request is marked non-null but is null"); + } + + @Test + void failsIfBuilderIsNull() { + assertThatThrownBy(() -> ServletMatomoRequestCustomizer.addServletRequestHeaders( + null, + new MockHttpServletRequest() + )).isInstanceOf(NullPointerException.class).hasMessage("builder is marked non-null but is null"); + } + +} \ No newline at end of file diff --git a/core/src/test/java/org/matomo/java/tracking/TestSender.java b/core/src/test/java/org/matomo/java/tracking/TestSender.java new file mode 100644 index 00000000..4d4c7465 --- /dev/null +++ b/core/src/test/java/org/matomo/java/tracking/TestSender.java @@ -0,0 +1,72 @@ +package org.matomo.java.tracking; + +import static java.util.Collections.singleton; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * A {@link Sender} implementation that does not send anything but stores the requests and queries. + * + *

This class is intended for testing purposes only. It does not send anything to the Matomo server. Instead, it + * stores the requests and queries in collections that can be accessed via {@link #getRequests()} and {@link + * #getQueries()}. + */ +@RequiredArgsConstructor +@Getter +class TestSender implements Sender { + + private final Collection requests = new ArrayList<>(); + + private final Collection queries = new ArrayList<>(); + + private final TrackerConfiguration trackerConfiguration; + + private final QueryCreator queryCreator; + + @NonNull + @Override + public CompletableFuture sendSingleAsync(@NonNull MatomoRequest request) { + createQueryAndAddRequest(request, null); + return CompletableFuture.completedFuture(request); + } + + @Override + public void sendSingle(@NonNull MatomoRequest request) { + createQueryAndAddRequest(request, null); + } + + @Override + public void sendBulk( + @NonNull Iterable requests, @Nullable String overrideAuthToken + ) { + for (MatomoRequest request : requests) { + createQueryAndAddRequest(request, overrideAuthToken); + } + } + + @NonNull + @Override + public CompletableFuture sendBulkAsync( + @NonNull Iterable requests, @Nullable String overrideAuthToken + ) { + for (MatomoRequest request : requests) { + createQueryAndAddRequest(request, overrideAuthToken); + } + return CompletableFuture.completedFuture(null); + } + + + + private void createQueryAndAddRequest(@lombok.NonNull MatomoRequest request, @Nullable String overrideAuthToken) { + String authToken = AuthToken.determineAuthToken(overrideAuthToken, singleton(request), trackerConfiguration); + queries.add(queryCreator.createQuery(request, authToken)); + requests.add(request); + } + +} diff --git a/core/src/test/java/org/matomo/java/tracking/TestSenderFactory.java b/core/src/test/java/org/matomo/java/tracking/TestSenderFactory.java new file mode 100644 index 00000000..fafafa90 --- /dev/null +++ b/core/src/test/java/org/matomo/java/tracking/TestSenderFactory.java @@ -0,0 +1,16 @@ +package org.matomo.java.tracking; + +import lombok.Getter; + +class TestSenderFactory implements SenderFactory { + + @Getter + private TestSender testSender; + + @Override + public Sender createSender(TrackerConfiguration trackerConfiguration, QueryCreator queryCreator) { + TestSender testSender = new TestSender(trackerConfiguration, queryCreator); + this.testSender = testSender; + return testSender; + } +} diff --git a/core/src/test/java/org/matomo/java/tracking/TrackerConfigurationTest.java b/core/src/test/java/org/matomo/java/tracking/TrackerConfigurationTest.java index 11a7a9ba..593aa7b7 100644 --- a/core/src/test/java/org/matomo/java/tracking/TrackerConfigurationTest.java +++ b/core/src/test/java/org/matomo/java/tracking/TrackerConfigurationTest.java @@ -7,71 +7,406 @@ class TrackerConfigurationTest { + private final TrackerConfiguration.TrackerConfigurationBuilder trackerConfigurationBuilder + = TrackerConfiguration.builder(); + @Test - void validateDoesNotFailIfDefaultAuthTokenIsNull() throws Exception { - TrackerConfiguration trackerConfiguration = TrackerConfiguration - .builder() + void validateDoesNotFailIfDefaultAuthTokenIsNull() { + trackerConfigurationBuilder .apiEndpoint(URI.create("https://matomo.example/matomo.php")) .defaultSiteId(1) .defaultAuthToken(null) .build(); - trackerConfiguration.validate(); + whenValidates(); } @Test - void validateFailsIfDefaultAuthTokenIsEmpty() throws Exception { - TrackerConfiguration trackerConfiguration = TrackerConfiguration - .builder() + void validateFailsIfDefaultAuthTokenIsEmpty() { + trackerConfigurationBuilder .apiEndpoint(URI.create("https://matomo.example/matomo.php")) .defaultSiteId(1) .defaultAuthToken("") .build(); - assertThatThrownBy(trackerConfiguration::validate) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Auth token must be exactly 32 characters long"); + thenFailsOnValidation("Auth token must be exactly 32 characters long"); } @Test - void validateFailsIfDefaultAuthTokenIsTooLong() throws Exception { - TrackerConfiguration trackerConfiguration = TrackerConfiguration - .builder() + void validateFailsIfDefaultAuthTokenIsTooLong() { + trackerConfigurationBuilder .apiEndpoint(URI.create("https://matomo.example/matomo.php")) .defaultSiteId(1) .defaultAuthToken("123456789012345678901234567890123") .build(); - assertThatThrownBy(trackerConfiguration::validate) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Auth token must be exactly 32 characters long"); + thenFailsOnValidation("Auth token must be exactly 32 characters long"); } @Test - void validateFailsIfDefaultAuthTokenIsTooShort() throws Exception { - TrackerConfiguration trackerConfiguration = TrackerConfiguration - .builder() + void validateFailsIfDefaultAuthTokenIsTooShort() { + trackerConfigurationBuilder .apiEndpoint(URI.create("https://matomo.example/matomo.php")) .defaultSiteId(1) .defaultAuthToken("1234567890123456789012345678901") .build(); - assertThatThrownBy(trackerConfiguration::validate) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Auth token must be exactly 32 characters long"); + thenFailsOnValidation("Auth token must be exactly 32 characters long"); } @Test - void validateFailsIfDefaultAuthTokenContainsInvalidCharacters() throws Exception { - TrackerConfiguration trackerConfiguration = TrackerConfiguration - .builder() + void validateFailsIfDefaultAuthTokenContainsInvalidCharacters() { + trackerConfigurationBuilder .apiEndpoint(URI.create("https://matomo.example/matomo.php")) .defaultSiteId(1) .defaultAuthToken("1234567890123456789012345678901!") .build(); - assertThatThrownBy(trackerConfiguration::validate) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Auth token must contain only lowercase letters and numbers"); + thenFailsOnValidation("Auth token must contain only lowercase letters and numbers"); + } + + @Test + void validateDoesNotFailIfDefaultSiteIdIsNull() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .defaultSiteId(null) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + whenValidates(); + } + + + @Test + void validateDoesNotFailIfDefaultSiteIdIsPositive() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + whenValidates(); + } + + @Test + void validateDoesNotFailIfDefaultSiteIdIsZero() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .defaultSiteId(0) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + + whenValidates(); + } + + @Test + void validateFailsIfDefaultSiteIdIsNegative() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .defaultSiteId(-1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + + thenFailsOnValidation("Default site ID must not be negative"); + } + + @Test + void validateDoesNotFailIfApiEndpointIsSet() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + whenValidates(); + } + + @Test + void validateFailsIfApiEndpointIsNotSet() { + trackerConfigurationBuilder + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + + thenFailsOnValidation("API endpoint must not be null"); + } + + @Test + void validateDoesNotFailIfProxyHostIsSetAndProxyPortIsPositive() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .proxyHost("proxy.example") + .proxyPort(1234) + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + whenValidates(); + } + + @Test + void validateFailsIfProxyPortIsSetAndProxyHostIsNotSet() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .proxyPort(1234) + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + thenFailsOnValidation("Proxy host must be set if port is set"); + } + + @Test + void validateFailsIfProxyPasswordIsSetAndProxyHostIsNotSet() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .proxyPassword("password") + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + thenFailsOnValidation("Proxy host must be set if password is set"); + } + + @Test + void validateFailsIfProxyHostIsSetAndProxyPortIsZero() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .proxyHost("proxy.example") + .proxyPort(0) + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + + thenFailsOnValidation("Proxy port must be greater than 0"); + } + + @Test + void validateFailsIfProxyHostIsSetAndProxyPortIsNegative() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .proxyHost("proxy.example") + .proxyPort(-1) + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + + thenFailsOnValidation("Proxy port must be greater than 0"); + } + + @Test + void validateFailsIfProxyUsernameIsSetAndProxyPasswordIsNotSet() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .proxyHost("proxy.example") + .proxyPort(1234) + .proxyUsername("user") + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + + thenFailsOnValidation("Proxy password must be set if username is set"); + } + + @Test + void validateFailsIfProxyPasswordIsSetAndProxyUsernameIsNotSet() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .proxyHost("proxy.example") + .proxyPort(1234) + .proxyPassword("password") + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + + thenFailsOnValidation("Proxy username must be set if password is set"); + } + + @Test + void validateDoesNotFailIfProxyUsernameAndProxyPasswordAreSet() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .proxyHost("proxy.example") + .proxyPort(1234) + .proxyUsername("user") + .proxyPassword("password") + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + whenValidates(); + } + + @Test + void validateDoesNotFailIfProxyUsernameAndProxyPasswordAreNotSet() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .proxyHost("proxy.example") + .proxyPort(1234) + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + whenValidates(); + } + + @Test + void validateDoesNotFailIfProxyUsernameIsSetAndProxyPasswordIsSet() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .proxyHost("proxy.example") + .proxyPort(1234) + .proxyUsername("user") + .proxyPassword("password") + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + whenValidates(); + } + + @Test + void validateDoesNotFailIfProxyUsernameIsNotSetAndProxyPasswordIsNotSet() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .proxyHost("proxy.example") + .proxyPort(1234) + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + whenValidates(); + } + + @Test + void validateFailsIfProxyUsernameIsSetAndProxyPasswordIsNotSetAndProxyHostIsNotSetAndProxyPortIsNotSet() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .proxyUsername("user") + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + thenFailsOnValidation("Proxy host must be set if username is set"); + } + + @Test + void validateDoesNotFailIfProxyUsernameIsNotSetAndProxyPasswordIsNotSetAndProxyHostIsNotSetAndProxyPortIsNotSet() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + whenValidates(); + } + + @Test + void validateFailsIfProxyUsernameIsSetAndProxyPasswordIsSetAndProxyHostIsNotSetAndProxyPortIsNotSet() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .proxyUsername("user") + .proxyPassword("password") + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + thenFailsOnValidation("Proxy host must be set if username is set"); + } + + @Test + void validateDoesNotFailIfSocketTimeoutIsNotSet() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .socketTimeout(null) + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + whenValidates(); + } + + @Test + void validateFailsIfSocketTimeoutIsNegative() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .socketTimeout(java.time.Duration.ofSeconds(-1)) + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + + thenFailsOnValidation("Socket timeout must not be negative"); + } + + @Test + void validateDoesNotFailIfConnectTimeoutIsNotSet() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .connectTimeout(null) + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + whenValidates(); + } + + @Test + void validateFailsIfConnectTimeoutIsNegative() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .connectTimeout(java.time.Duration.ofSeconds(-1)) + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + + thenFailsOnValidation("Connect timeout must not be negative"); + } + + @Test + void validateDoesNotFailIfThreadPoolSizeIsOne() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .threadPoolSize(1) + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + whenValidates(); + } + + @Test + void validateFailsIfThreadPoolSizeIsLessThanOne() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .threadPoolSize(0) + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + + thenFailsOnValidation("Thread pool size must be greater than 0"); + } + + @Test + void validateDoesNotFailIfThreadPoolSizeIsGreaterThanOne() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .threadPoolSize(2) + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + whenValidates(); + } + + @Test + void validateDoesNotFailIfThreadPoolSizeIsNotSet() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + whenValidates(); + } + + @Test + void validateDoesNotFailIfThreadPoolSizeIsGreaterThanOneAndSet() { + trackerConfigurationBuilder + .apiEndpoint(URI.create("https://matomo.example/matomo.php")) + .threadPoolSize(2) + .defaultSiteId(1) + .defaultAuthToken("12345678901234567890123456789012") + .build(); + whenValidates(); + } + + void whenValidates() { + trackerConfigurationBuilder.build().validate(); + } + + private void thenFailsOnValidation(String message) { + assertThatThrownBy(this::whenValidates).isInstanceOf(IllegalArgumentException.class).hasMessage(message); } -} +} \ No newline at end of file diff --git a/core/src/test/java/org/piwik/java/tracking/PiwikTrackerIT.java b/core/src/test/java/org/piwik/java/tracking/PiwikTrackerIT.java new file mode 100644 index 00000000..c4ce73e6 --- /dev/null +++ b/core/src/test/java/org/piwik/java/tracking/PiwikTrackerIT.java @@ -0,0 +1,47 @@ +package org.piwik.java.tracking; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class PiwikTrackerIT { + + private PiwikTracker piwikTracker; + + @Test + void createsNewPiwikTrackerInstanceWithHostUrl() { + + piwikTracker = new PiwikTracker("http://localhost:8080"); + + assertThat(piwikTracker).isNotNull(); + + } + + @Test + void createsNewPiwikTrackerInstanceWithHostUrlAndTimeout() { + + piwikTracker = new PiwikTracker("http://localhost:8080", 1000); + + assertThat(piwikTracker).isNotNull(); + + } + + @Test + void createsNewPiwikTrackerInstanceWithHostUrlAndProxySettings() { + + piwikTracker = new PiwikTracker("http://localhost:8080", "localhost", 8080); + + assertThat(piwikTracker).isNotNull(); + + } + + @Test + void createsNewPiwikTrackerInstanceWithHostUrlAndProxySettingsAndTimeout() { + + piwikTracker = new PiwikTracker("http://localhost:8080", "localhost", 8080, 1000); + + assertThat(piwikTracker).isNotNull(); + + } + +} \ No newline at end of file diff --git a/java17/pom.xml b/java17/pom.xml new file mode 100644 index 00000000..d213d563 --- /dev/null +++ b/java17/pom.xml @@ -0,0 +1,74 @@ + + 4.0.0 + + + org.piwik.java.tracking + matomo-java-tracker-parent + 3.0.0-SNAPSHOT + ../pom.xml + + + matomo-java-tracker-java17 + 3.0.0-SNAPSHOT + jar + + Matomo Java Tracker Java 17 + Official Java implementation of the Matomo Tracking HTTP API for Java 17. + + + 17 + 17 + 3.1.5 + + + + + org.piwik.java.tracking + matomo-java-tracker-core + 3.0.0-SNAPSHOT + + + org.projectlombok + lombok + + + com.github.spotbugs + spotbugs-annotations + + + org.junit.jupiter + junit-jupiter + + + org.assertj + assertj-core + + + org.wiremock + wiremock + 3.3.1 + test + + + org.slf4j + slf4j-simple + test + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + true + + + + + + + diff --git a/java17/src/main/java/org/matomo/java/tracking/Java17Sender.java b/java17/src/main/java/org/matomo/java/tracking/Java17Sender.java new file mode 100644 index 00000000..7a11f4d3 --- /dev/null +++ b/java17/src/main/java/org/matomo/java/tracking/Java17Sender.java @@ -0,0 +1,164 @@ +package org.matomo.java.tracking; + +import static java.util.Collections.singleton; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * A {@link Sender} implementation that uses the Java 17 HTTP client. + */ +@RequiredArgsConstructor +@Slf4j +public class Java17Sender implements Sender { + + @lombok.NonNull + private final TrackerConfiguration trackerConfiguration; + + @lombok.NonNull + private final QueryCreator queryCreator; + + @lombok.NonNull + private final HttpClient httpClient; + + @NonNull + @Override + public CompletableFuture sendSingleAsync(@NonNull @lombok.NonNull MatomoRequest request) { + return sendAsyncAndCheckResponse(buildHttpGetRequest(request), request); + } + + @Override + public void sendSingle(@NonNull @lombok.NonNull MatomoRequest request) { + sendAndCheckResponse(buildHttpGetRequest(request)); + } + + private void sendAndCheckResponse(@NonNull HttpRequest httpRequest) { + checkResponse(send(httpRequest, () -> httpClient.send(httpRequest, HttpResponse.BodyHandlers.discarding())), + httpRequest + ); + } + + @Override + public void sendBulk( + @NonNull @lombok.NonNull Iterable requests, @Nullable String overrideAuthToken + ) { + sendAndCheckResponse(buildHttpPostRequest(requests, overrideAuthToken)); + } + + @NonNull + private HttpRequest buildHttpPostRequest( + @NonNull Iterable requests, @Nullable String overrideAuthToken + ) { + String authToken = AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); + Collection queries = new ArrayList<>(); + Map headers = new LinkedHashMap<>(10); + for (MatomoRequest request : requests) { + RequestValidator.validate(request, authToken); + if (request.getHeaders() != null && !request.getHeaders().isEmpty()) { + headers.putAll(request.getHeaders()); + } + queries.add(queryCreator.createQuery(request, null)); + } + HttpRequest.Builder builder = HttpRequest + .newBuilder() + .uri(trackerConfiguration.getApiEndpoint()) + .header("Accept", "*/*") + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofByteArray(BulkRequest + .builder() + .queries(queries) + .authToken(authToken) + .build() + .toBytes())); + applyTrackerConfiguration(builder); + addHeaders(builder, headers); + return builder.build(); + } + + @NonNull + @Override + public CompletableFuture sendBulkAsync( + @NonNull @lombok.NonNull Iterable requests, @Nullable String overrideAuthToken + ) { + return sendAsyncAndCheckResponse(buildHttpPostRequest(requests, overrideAuthToken), null); + } + + @NonNull + private HttpRequest buildHttpGetRequest(@NonNull MatomoRequest request) { + String authToken = AuthToken.determineAuthToken(null, singleton(request), trackerConfiguration); + RequestValidator.validate(request, authToken); + URI apiEndpoint = trackerConfiguration.getApiEndpoint(); + HttpRequest.Builder builder = HttpRequest + .newBuilder() + .uri(apiEndpoint.resolve(String.format("%s?%s", + apiEndpoint.getPath(), + queryCreator.createQuery(request, authToken) + ))); + applyTrackerConfiguration(builder); + addHeaders(builder, request.getHeaders()); + return builder.build(); + } + + private void addHeaders(@NonNull HttpRequest.Builder builder, @Nullable Map headers) { + if (headers != null) { + for (Map.Entry header : headers.entrySet()) { + builder.header(header.getKey(), header.getValue()); + } + } + } + + @NonNull + private CompletableFuture sendAsyncAndCheckResponse( + @NonNull HttpRequest httpRequest, @Nullable T result + ) { + return send(httpRequest, + () -> httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.discarding()).thenApply(response -> { + checkResponse(response, httpRequest); + return result; + }) + ); + } + + private void applyTrackerConfiguration(@NonNull HttpRequest.Builder builder) { + if (trackerConfiguration.getSocketTimeout() != null && trackerConfiguration.getSocketTimeout().toMillis() > 0L) { + builder.timeout(trackerConfiguration.getSocketTimeout()); + } + if (trackerConfiguration.getUserAgent() != null && !trackerConfiguration.getUserAgent().isEmpty()) { + builder.header("User-Agent", trackerConfiguration.getUserAgent()); + } + } + + private T send( + @NonNull HttpRequest httpRequest, @NonNull Callable callable + ) { + try { + return callable.call(); + } catch (Exception e) { + if (trackerConfiguration.isLogFailedTracking()) { + log.error("Could not send request to Matomo: {}", httpRequest.uri(), e); + } + throw new MatomoException("Could not send request to Matomo", e); + } + } + + private void checkResponse(@NonNull HttpResponse response, @NonNull HttpRequest httpRequest) { + if (response.statusCode() > 399) { + if (trackerConfiguration.isLogFailedTracking()) { + log.error("Received HTTP error code {} for URL {}", response.statusCode(), httpRequest.uri()); + } + throw new MatomoException(String.format("Tracking endpoint responded with code %d", response.statusCode())); + } + } +} diff --git a/java17/src/main/java/org/matomo/java/tracking/Java17SenderProvider.java b/java17/src/main/java/org/matomo/java/tracking/Java17SenderProvider.java new file mode 100644 index 00000000..405061d4 --- /dev/null +++ b/java17/src/main/java/org/matomo/java/tracking/Java17SenderProvider.java @@ -0,0 +1,63 @@ +package org.matomo.java.tracking; + +import edu.umd.cs.findbugs.annotations.Nullable; +import java.net.InetSocketAddress; +import java.net.ProxySelector; +import java.net.http.HttpClient; +import java.security.SecureRandom; +import java.util.concurrent.Executors; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; + +/** + * Provides a {@link Sender} implementation based on Java 17. + */ +public class Java17SenderProvider implements SenderProvider { + + private static final TrustManager[] TRUST_ALL_MANAGERS = {new TrustingX509TrustManager()}; + + @Override + public Sender provideSender( + TrackerConfiguration trackerConfiguration, QueryCreator queryCreator + ) { + HttpClient.Builder builder = HttpClient + .newBuilder() + .executor(Executors.newFixedThreadPool(trackerConfiguration.getThreadPoolSize(), new DaemonThreadFactory())) + ; + if (trackerConfiguration.getConnectTimeout() != null && trackerConfiguration.getConnectTimeout().toMillis() > 0L) { + builder.connectTimeout(trackerConfiguration.getConnectTimeout()); + } + if (!isEmpty(trackerConfiguration.getProxyHost()) && trackerConfiguration.getProxyPort() > 0) { + builder.proxy(ProxySelector.of(new InetSocketAddress(trackerConfiguration.getProxyHost(), + trackerConfiguration.getProxyPort() + ))); + if (!isEmpty(trackerConfiguration.getProxyUsername()) && !isEmpty(trackerConfiguration.getProxyPassword())) { + builder.authenticator(new ProxyAuthenticator( + trackerConfiguration.getProxyUsername(), + trackerConfiguration.getProxyPassword() + )); + } + } + if (trackerConfiguration.isDisableSslCertValidation()) { + try { + SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, TRUST_ALL_MANAGERS, new SecureRandom()); + builder.sslContext(sslContext); + } catch (Exception e) { + throw new MatomoException("Could not disable SSL certification validation", e); + } + } + if (trackerConfiguration.isDisableSslHostVerification()) { + throw new MatomoException("Please disable SSL hostname verification manually using the system parameter -Djdk.internal.httpclient.disableHostnameVerification=true"); + } + + return new Java17Sender(trackerConfiguration, queryCreator, builder.build()); + } + + private static boolean isEmpty( + @Nullable String str + ) { + return str == null || str.isEmpty() || str.trim().isEmpty(); + } + +} diff --git a/java17/src/main/resources/META-INF/services/org.matomo.java.tracking.SenderProvider b/java17/src/main/resources/META-INF/services/org.matomo.java.tracking.SenderProvider new file mode 100644 index 00000000..faed872c --- /dev/null +++ b/java17/src/main/resources/META-INF/services/org.matomo.java.tracking.SenderProvider @@ -0,0 +1 @@ +org.matomo.java.tracking.Java17SenderProvider \ No newline at end of file diff --git a/java17/src/test/java/org/matomo/java/tracking/Java17SenderIT.java b/java17/src/test/java/org/matomo/java/tracking/Java17SenderIT.java new file mode 100644 index 00000000..60a71fa8 --- /dev/null +++ b/java17/src/test/java/org/matomo/java/tracking/Java17SenderIT.java @@ -0,0 +1,332 @@ +package org.matomo.java.tracking; + +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.status; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static com.github.tomakehurst.wiremock.client.WireMock.verify; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonMap; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; +import java.net.URI; +import java.net.http.HttpClient; +import java.time.Duration; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +@WireMockTest(httpsEnabled = true) +class Java17SenderIT { + + private Sender sender; + + private TrackerConfiguration trackerConfiguration; + + @BeforeEach + void disableSslHostnameVerification() { + System.setProperty("jdk.internal.httpclient.disableHostnameVerification", "true"); + } + + @Test + void failsIfTrackerConfigurationIsNotSet() { + assertThatThrownBy(() -> new Java17Sender( + null, + new QueryCreator(TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build()), + HttpClient.newHttpClient() + )).isInstanceOf(NullPointerException.class).hasMessage("trackerConfiguration is marked non-null but is null"); + } + + @Test + void failsIfQueryCreatorIsNotSet() { + assertThatThrownBy(() -> new Java17Sender( + TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build(), + null, + HttpClient.newHttpClient() + )).isInstanceOf(NullPointerException.class).hasMessage("queryCreator is marked non-null but is null"); + } + + @Test + void failsIfHttpClientIsNotSet() { + assertThatThrownBy(() -> new Java17Sender( + TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build(), + new QueryCreator(TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build()), + null + )).isInstanceOf(NullPointerException.class).hasMessage("httpClient is marked non-null but is null"); + } + + @Test + void sendSingleFailsIfQueryIsMalformedWithSocketTimeout() { + trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("telnet://localhost")) + .socketTimeout(Duration.ofSeconds(30L)) + .build(); + givenSender(); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("invalid URI scheme telnet"); + } + + @Test + void sendSingleFailsIfQueryIsMalformedWithoutSocketTimeout() { + trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("telnet://localhost")) + .build(); + givenSender(); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("invalid URI scheme telnet"); + } + + private void givenSender() { + sender = new Java17SenderProvider().provideSender(trackerConfiguration, new QueryCreator(trackerConfiguration)); + } + + @Test + void failsIfEndpointReturnsNotFound(WireMockRuntimeInfo wireMockRuntimeInfo) { + trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create(wireMockRuntimeInfo.getHttpBaseUrl())) + .disableSslCertValidation(true) + .socketTimeout(Duration.ofSeconds(0L)) + .build(); + + givenSender(); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) + .isInstanceOf(MatomoException.class) + .hasMessage("Tracking endpoint responded with code 404"); + } + + @Test + void failsAndLogsIfCouldNotConnectToEndpoint() { + trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("http://localhost:1234")) + .userAgent("") + .logFailedTracking(true) + .build(); + + givenSender(); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not send request to Matomo"); + } + + @Test + void failsAndDoesNotLogIfCouldNotConnectToEndpoint() { + trackerConfiguration = + TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost:1234")).userAgent("").build(); + + givenSender(); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not send request to Matomo"); + } + + @Test + void connectsViaProxy(WireMockRuntimeInfo wireMockRuntimeInfo) { + trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create(wireMockRuntimeInfo.getHttpBaseUrl())) + .disableSslCertValidation(true) + .proxyHost("localhost") + .proxyPort(wireMockRuntimeInfo.getHttpPort()) + .userAgent(null) + .build(); + + givenSender(); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) + .isInstanceOf(MatomoException.class) + .hasMessage("Tracking endpoint responded with code 400"); + } + + @Test + void connectsViaProxyWithProxyUserNameAndPassword(WireMockRuntimeInfo wireMockRuntimeInfo) { + trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create(wireMockRuntimeInfo.getHttpBaseUrl())) + .disableSslCertValidation(true) + .proxyHost("localhost") + .proxyPort(wireMockRuntimeInfo.getHttpPort()) + .proxyUsername("user") + .proxyPassword("password") + .build(); + + givenSender(); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) + .isInstanceOf(MatomoException.class) + .hasMessage("Tracking endpoint responded with code 400"); + } + + @Test + void logsFailedTracking(WireMockRuntimeInfo wireMockRuntimeInfo) { + trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create(wireMockRuntimeInfo.getHttpsBaseUrl())) + .disableSslCertValidation(true) + .logFailedTracking(true) + .build(); + + givenSender(); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) + .isInstanceOf(MatomoException.class) + .hasMessage("Tracking endpoint responded with code 404"); + } + + @Test + void skipSslCertificationValidation(WireMockRuntimeInfo wireMockRuntimeInfo) { + stubFor(get(urlPathEqualTo("/matomo_ssl.php")).willReturn(status(204))); + trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create(String.format("https://localhost:%d/matomo_ssl.php", + wireMockRuntimeInfo.getHttpsPort() + ))) + .disableSslCertValidation(true) + .build(); + + givenSender(); + + sender.sendSingle(new MatomoRequest()); + + verify(getRequestedFor(urlPathEqualTo("/matomo_ssl.php"))); + + } + + @Test + void addsHeadersToSingleRequest(WireMockRuntimeInfo wireMockRuntimeInfo) { + stubFor(get(urlPathEqualTo("/matomo.php")).willReturn(status(204))); + trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create(wireMockRuntimeInfo.getHttpBaseUrl() + "/matomo.php")) + .build(); + + givenSender(); + + sender.sendSingle(MatomoRequest.request().headers(singletonMap("headerName", "headerValue")).build()); + + verify(getRequestedFor(urlPathEqualTo("/matomo.php")).withHeader("headerName", equalTo("headerValue"))); + } + + @Test + void addsHeadersToBulkRequest(WireMockRuntimeInfo wireMockRuntimeInfo) { + stubFor(post(urlPathEqualTo("/matomo.php")).willReturn(status(204))); + trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create(wireMockRuntimeInfo.getHttpBaseUrl() + "/matomo.php")) + .build(); + + givenSender(); + + sender.sendBulk(List.of(MatomoRequest.request().headers(singletonMap("headerName", "headerValue")).build()), null); + + verify(postRequestedFor(urlPathEqualTo("/matomo.php")).withHeader("headerName", equalTo("headerValue"))); + } + + @Test + void doesNotAddEmptyHeaders(WireMockRuntimeInfo wireMockRuntimeInfo) { + stubFor(post(urlPathEqualTo("/matomo.php")).willReturn(status(204))); + trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create(wireMockRuntimeInfo.getHttpBaseUrl() + "/matomo.php")) + .build(); + + givenSender(); + + sender.sendBulk(List.of(MatomoRequest.request().headers(emptyMap()).build()), null); + + verify(postRequestedFor(urlPathEqualTo("/matomo.php")).withoutHeader("headerName")); + } + + @Test + void addsHeadersToBulkAsyncRequest(WireMockRuntimeInfo wireMockRuntimeInfo) { + stubFor(post(urlPathEqualTo("/matomo.php")).willReturn(status(204))); + trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create(wireMockRuntimeInfo.getHttpBaseUrl() + "/matomo.php")) + .build(); + + givenSender(); + + CompletableFuture future = sender.sendBulkAsync(List.of(MatomoRequest + .request() + .headers(singletonMap("headerName", "headerValue")) + .build()), null); + + future.join(); + verify(postRequestedFor(urlPathEqualTo("/matomo.php")).withHeader("headerName", equalTo("headerValue"))); + } + + @Test + void failsOnSendSingleAsyncIfRequestIsNull() { + trackerConfiguration = TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost:1234")).build(); + + givenSender(); + + assertThatThrownBy(() -> sender.sendSingleAsync(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("request is marked non-null but is null"); + } + + @Test + void failsOnSendSingleIfRequestIsNull() { + trackerConfiguration = TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost:1234")).build(); + + givenSender(); + + assertThatThrownBy(() -> sender.sendSingle(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("request is marked non-null but is null"); + } + + @Test + void failsOnSendBulkAsyncIfRequestsIsNull() { + trackerConfiguration = TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost:1234")).build(); + + givenSender(); + + assertThatThrownBy(() -> sender.sendBulkAsync(null, null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("requests is marked non-null but is null"); + } + + @Test + void failsOnSendBulkAsyncIfRequestIsNull() { + trackerConfiguration = TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost:1234")).build(); + + givenSender(); + + assertThatThrownBy(() -> sender.sendBulk(null, null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("requests is marked non-null but is null"); + } + + @Test + void failsOnSendBulkAsyncIfOverrideAuthTokenIsMalformed() { + trackerConfiguration = TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost:1234")).build(); + + givenSender(); + + assertThatThrownBy(() -> sender.sendBulkAsync(List.of(MatomoRequest.request().build()), "telnet://localhost")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Auth token must be exactly 32 characters long"); + } + + +} diff --git a/java17/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/java17/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java new file mode 100644 index 00000000..0f0f9c56 --- /dev/null +++ b/java17/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java @@ -0,0 +1,498 @@ +package org.matomo.java.tracking; + +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.resetAllRequests; +import static com.github.tomakehurst.wiremock.client.WireMock.status; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static com.github.tomakehurst.wiremock.client.WireMock.verify; +import static java.util.Collections.singleton; +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; +import java.net.URI; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Arrays; +import java.util.Locale.LanguageRange; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.matomo.java.tracking.MatomoRequest.MatomoRequestBuilder; +import org.matomo.java.tracking.TrackerConfiguration.TrackerConfigurationBuilder; +import org.matomo.java.tracking.parameters.AcceptLanguage; +import org.matomo.java.tracking.parameters.Country; +import org.matomo.java.tracking.parameters.CustomVariable; +import org.matomo.java.tracking.parameters.CustomVariables; +import org.matomo.java.tracking.parameters.DeviceResolution; +import org.matomo.java.tracking.parameters.EcommerceItem; +import org.matomo.java.tracking.parameters.EcommerceItems; +import org.matomo.java.tracking.parameters.RandomValue; +import org.matomo.java.tracking.parameters.UniqueId; +import org.matomo.java.tracking.parameters.VisitorId; + +@WireMockTest +class MatomoTrackerIT { + + private static final int SITE_ID = 42; + + private final TrackerConfigurationBuilder trackerConfigurationBuilder = TrackerConfiguration.builder(); + + private final MatomoRequestBuilder requestBuilder = MatomoRequest + .builder() + .visitorId(VisitorId.fromHex("bbccddeeff1122")) + .randomValue(RandomValue.fromString("someRandom")); + + private CompletableFuture future; + + private MatomoTracker matomoTracker; + + @BeforeEach + void givenStub() { + resetAllRequests(); + stubFor(post(urlPathEqualTo("/matomo.php")).willReturn(status(204))); + stubFor(get(urlPathEqualTo("/matomo.php")).willReturn(status(204))); + } + + @Test + void requiresSiteId(WireMockRuntimeInfo wireMockRuntimeInfo) { + + trackerConfigurationBuilder.apiEndpoint(URI.create(wireMockRuntimeInfo.getHttpBaseUrl() + "/matomo.php")).build(); + + assertThatThrownBy(this::whenSendsRequestAsync) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("No default site ID and no request site ID is given"); + + } + + private void whenSendsRequestAsync() { + matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); + future = matomoTracker.sendRequestAsync(requestBuilder.build()); + } + + @Test + void usesDefaultSiteId(WireMockRuntimeInfo wireMockRuntimeInfo) { + + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + + whenSendsRequestAsync(); + + thenGetsRequest("idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom"); + + } + + private void givenTrackerConfigurationWithDefaultSiteId(WireMockRuntimeInfo wireMockRuntimeInfo) { + trackerConfigurationBuilder + .apiEndpoint(URI.create(wireMockRuntimeInfo.getHttpBaseUrl() + "/matomo.php")) + .defaultSiteId(SITE_ID); + } + + private void thenGetsRequest(String expectedQuery) { + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + verify(getRequestedFor(urlEqualTo(String.format("/matomo.php?%s", expectedQuery))).withHeader("User-Agent", + equalTo("MatomoJavaClient") + )); + }); + } + + @Test + void overridesDefaultSiteId(WireMockRuntimeInfo wireMockRuntimeInfo) { + + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + requestBuilder.siteId(123); + + whenSendsRequestAsync(); + + thenGetsRequest("rec=1&idsite=123&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom"); + + } + + @Test + void validatesTokenAuth(WireMockRuntimeInfo wireMockRuntimeInfo) { + + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + requestBuilder.authToken("invalid-token-auth"); + + assertThatThrownBy(this::whenSendsRequestAsync) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Auth token must be exactly 32 characters long"); + + } + + @Test + void convertsTrueBooleanTo1(WireMockRuntimeInfo wireMockRuntimeInfo) { + + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + requestBuilder.pluginFlash(true); + + whenSendsRequestAsync(); + + thenGetsRequest("idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&fla=1&send_image=0&rand=someRandom"); + + } + + @Test + void convertsFalseBooleanTo0(WireMockRuntimeInfo wireMockRuntimeInfo) { + + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + requestBuilder.pluginJava(false); + + whenSendsRequestAsync(); + + thenGetsRequest("idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&java=0&send_image=0&rand=someRandom"); + + } + + @Test + void encodesUrl(WireMockRuntimeInfo wireMockRuntimeInfo) { + + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + requestBuilder.actionUrl("https://www.daniel-heid.de/some/page?foo=bar"); + + whenSendsRequestAsync(); + + thenGetsRequest( + "idsite=42&rec=1&url=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fpage%3Ffoo%3Dbar&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom"); + + } + + @Test + void encodesReferrerUrl(WireMockRuntimeInfo wireMockRuntimeInfo) { + + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + requestBuilder.referrerUrl("https://www.daniel-heid.de/some/referrer?foo2=bar2"); + + whenSendsRequestAsync(); + + thenGetsRequest( + "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Freferrer%3Ffoo2%3Dbar2&send_image=0&rand=someRandom"); + + } + + @Test + void encodesLink(WireMockRuntimeInfo wireMockRuntimeInfo) { + + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + requestBuilder.outlinkUrl("https://www.daniel-heid.de/some/external/link#"); + + whenSendsBulkRequestAsync(); + + thenPostsRequestWithoutAuthToken( + "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&link=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fexternal%2Flink%23&send_image=0&rand=someRandom", + "156" + ); + + } + + @Test + void sendsRequestsBulkAsynchronously(WireMockRuntimeInfo wireMockRuntimeInfo) { + + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); + + future = matomoTracker.sendBulkRequestAsync(requestBuilder.build()); + + thenPostsRequestWithoutAuthToken("idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom", "90"); + + } + + private void whenSendsBulkRequestAsync() { + future = + new MatomoTracker(trackerConfigurationBuilder.build()).sendBulkRequestAsync(singleton(requestBuilder.build())); + } + + private void thenPostsRequestWithoutAuthToken(String expectedQuery, String contentLength) { + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo(contentLength)) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson("{\"requests\":[\"?" + expectedQuery + "\"]}"))); + }); + + } + + @Test + void encodesDownloadUrl(WireMockRuntimeInfo wireMockRuntimeInfo) { + + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + requestBuilder.downloadUrl("https://www.daniel-heid.de/some/download.pdf"); + + whenSendsBulkRequestAsync(); + + thenPostsRequestWithoutAuthToken( + "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&download=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fdownload.pdf&send_image=0&rand=someRandom", + "154" + ); + + } + + @Test + void getContainsHeaders(WireMockRuntimeInfo wireMockRuntimeInfo) { + + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + + whenSendsRequestAsync(); + + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + verify(getRequestedFor(urlPathEqualTo("/matomo.php")).withHeader("User-Agent", equalTo("MatomoJavaClient"))); + }); + + } + + @Test + void postContainsHeaders(WireMockRuntimeInfo wireMockRuntimeInfo) { + + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + + whenSendsBulkRequestAsync(); + + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + verify(postRequestedFor(urlPathEqualTo("/matomo.php")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Length", equalTo("90")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient"))); + }); + + } + + @Test + void allowsToOverrideUserAgent(WireMockRuntimeInfo wireMockRuntimeInfo) { + + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + trackerConfigurationBuilder.userAgent("Mozilla/5.0"); + + whenSendsRequestAsync(); + + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + verify(getRequestedFor(urlPathEqualTo("/matomo.php")).withHeader("User-Agent", equalTo("Mozilla/5.0"))); + }); + + } + + @Test + void tracksMinimalRequest(WireMockRuntimeInfo wireMockRuntimeInfo) { + + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + requestBuilder + .actionName("Help / Feedback") + .actionUrl("https://www.daniel-heid.de/portfolio") + .visitorId(VisitorId.fromHash(3434343434343434343L)) + .referrerUrl("https://www.daniel-heid.de/referrer") + .visitCustomVariables(new CustomVariables() + .add(new CustomVariable("customVariable1Key", "customVariable1Value"), 4) + .add(new CustomVariable("customVariable2Key", "customVariable2Value"), 5)) + .visitorVisitCount(2) + .visitorFirstVisitTimestamp(LocalDateTime.of(2022, 8, 9, 18, 34, 12).toInstant(ZoneOffset.UTC)) + .deviceResolution(DeviceResolution.builder().width(1024).height(768).build()) + .headerAcceptLanguage(AcceptLanguage + .builder() + .languageRange(new LanguageRange("de")) + .languageRange(new LanguageRange("de-DE", 0.9)) + .languageRange(new LanguageRange("en", 0.8)) + .build()) + .pageViewId(UniqueId.fromValue(999999999999999999L)) + .goalId(0) + .ecommerceRevenue(12.34) + .ecommerceItems(EcommerceItems + .builder() + .item(EcommerceItem.builder().sku("SKU").build()) + .item(EcommerceItem.builder().sku("SKU").name("NAME").category("CATEGORY").price(123.4).build()) + .build()) + .authToken("fdf6e8461ea9de33176b222519627f78") + .visitorCountry(Country.fromLanguageRanges("en-GB;q=0.7,de,de-DE;q=0.9,en;q=0.8,en-US;q=0.6")); + + whenSendsBulkRequestAsync(); + + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("711")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson("{\"requests\":[\"?" + + "idsite=42&rec=1&action_name=Help+%2F+Feedback&url=https%3A%2F%2Fwww.daniel-heid.de%2Fportfolio&apiv=1&_id=2fa93d2858bc4867&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Freferrer&_cvar=%7B%224%22%3A%5B%22customVariable1Key%22%2C%22customVariable1Value%22%5D%2C%225%22%3A%5B%22customVariable2Key%22%2C%22customVariable2Value%22%5D%7D&_idvc=2&_idts=1660070052&res=1024x768&lang=de%2Cde-de%3Bq%3D0.9%2Cen%3Bq%3D0.8&pv_id=lbBbxG&idgoal=0&revenue=12.34&ec_items=%5B%5B%22SKU%22%2C%22%22%2C%22%22%2C0.0%2C0%5D%2C%5B%22SKU%22%2C%22NAME%22%2C%22CATEGORY%22%2C123.4%2C0%5D%5D&token_auth=fdf6e8461ea9de33176b222519627f78&country=de&send_image=0&rand=someRandom" + + "\"],\"token_auth\" : \"" + "fdf6e8461ea9de33176b222519627f78" + "\"}"))); + + }); + + } + + @Test + void doesNothingIfNotEnabled(WireMockRuntimeInfo wireMockRuntimeInfo) throws Exception { + + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + trackerConfigurationBuilder.enabled(false); + + whenSendsRequestAsync(); + future.get(); + + verify(0, postRequestedFor(urlPathEqualTo("/matomo.php"))); + + } + + @Test + void reportsErrors(WireMockRuntimeInfo wireMockRuntimeInfo) { + + stubFor(get(urlPathEqualTo("/failing")).willReturn(status(500))); + trackerConfigurationBuilder + .apiEndpoint(URI.create(wireMockRuntimeInfo.getHttpBaseUrl() + "/failing")) + .defaultSiteId(SITE_ID); + + whenSendsRequestAsync(); + + assertThat(future) + .failsWithin(1, MINUTES) + .withThrowableThat() + .havingRootCause() + .isInstanceOf(MatomoException.class) + .withMessage("Tracking endpoint responded with code 500"); + + } + + @Test + void includesDefaultTokenAuth(WireMockRuntimeInfo wireMockRuntimeInfo) { + + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + trackerConfigurationBuilder.defaultAuthToken("fdf6e8461ea9de33176b222519627f78"); + + whenSendsRequestAsync(); + + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + verify(getRequestedFor(urlEqualTo( + "/matomo.php?idsite=42&token_auth=fdf6e8461ea9de33176b222519627f78&rec=1&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom")).withHeader("User-Agent", + equalTo("MatomoJavaClient") + )); + }); + + } + + @Test + void includesMultipleQueriesInBulkRequest(WireMockRuntimeInfo wireMockRuntimeInfo) { + + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); + + future = matomoTracker.sendBulkRequestAsync(Arrays.asList(requestBuilder.actionName("First").build(), + requestBuilder.actionName("Second").build(), + requestBuilder.actionName("Third").build() + )); + + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("297")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{\"requests\" : [ \"?idsite=42&rec=1&action_name=First&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\", \"?idsite=42&rec=1&action_name=Second&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\", \"?idsite=42&rec=1&action_name=Third&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\" ]}"))); + }); + + } + + @Test + void failsOnNegativeSiteId(WireMockRuntimeInfo wireMockRuntimeInfo) { + + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + requestBuilder.siteId(-1); + + assertThatThrownBy(this::whenSendsRequestAsync) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Site ID must not be negative"); + + } + + @Test + void doesNotSendRequestAsyncIfTrackerConfigurationIsDisabled(WireMockRuntimeInfo wireMockRuntimeInfo) + throws Exception { + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + trackerConfigurationBuilder.enabled(false); + + whenSendsRequestAsync(); + future.get(); + + verify(0, getRequestedFor(urlPathEqualTo("/matomo.php"))); + + } + + @Test + void doesNotSendRequestIfTrackerConfigurationIsDisabled(WireMockRuntimeInfo wireMockRuntimeInfo) { + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + trackerConfigurationBuilder.enabled(false); + + whenSendsSingleRequest(); + + verify(0, getRequestedFor(urlPathEqualTo("/matomo.php"))); + } + + private void whenSendsSingleRequest() { + new MatomoTracker(trackerConfigurationBuilder.build()).sendRequest(requestBuilder.build()); + } + + @Test + void doesNotSendBulkRequestIfTrackerConfigurationIsDisabled(WireMockRuntimeInfo wireMockRuntimeInfo) { + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + trackerConfigurationBuilder.enabled(false); + + whenSendsBulkRequest(); + + verify(0, postRequestedFor(urlPathEqualTo("/matomo.php"))); + } + + private void whenSendsBulkRequest() { + new MatomoTracker(trackerConfigurationBuilder.build()).sendBulkRequest(singleton(requestBuilder.build())); + } + + @Test + void doesNotSendBulkRequestAsyncIfTrackerConfigurationIsDisabled(WireMockRuntimeInfo wireMockRuntimeInfo) + throws Exception { + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + trackerConfigurationBuilder.enabled(false); + + whenSendsBulkRequestAsync(); + + future.get(); + verify(0, postRequestedFor(urlPathEqualTo("/matomo.php"))); + } + + @Test + void sendsRequestAsyncAndAcceptsCallback(WireMockRuntimeInfo wireMockRuntimeInfo) { + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); + AtomicBoolean success = new AtomicBoolean(); + future = matomoTracker.sendRequestAsync(requestBuilder.build(), request -> { + success.set(true); + }); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + verify(getRequestedFor(urlPathEqualTo("/matomo.php"))); + }); + assertThat(success).isTrue(); + } + + @Test + void sendsRequestsAsyncAndAcceptsCallback(WireMockRuntimeInfo wireMockRuntimeInfo) { + givenTrackerConfigurationWithDefaultSiteId(wireMockRuntimeInfo); + matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); + AtomicBoolean success = new AtomicBoolean(); + future = matomoTracker.sendBulkRequestAsync(singleton(requestBuilder.build()), v -> { + success.set(true); + }); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + verify(postRequestedFor(urlPathEqualTo("/matomo.php"))); + }); + assertThat(success).isTrue(); + } + +} diff --git a/java17/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java b/java17/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java new file mode 100644 index 00000000..799af605 --- /dev/null +++ b/java17/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java @@ -0,0 +1,249 @@ +package org.matomo.java.tracking; + +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.resetAllRequests; +import static com.github.tomakehurst.wiremock.client.WireMock.status; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static com.github.tomakehurst.wiremock.client.WireMock.verify; +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.matomo.java.tracking.parameters.RandomValue; +import org.matomo.java.tracking.parameters.VisitorId; +import org.piwik.java.tracking.PiwikRequest; +import org.piwik.java.tracking.PiwikTracker; + +@WireMockTest +class PiwikTrackerIT { + + private static final int SITE_ID = 42; + + private PiwikTracker piwikTracker; + + private PiwikRequest request; + + @BeforeEach + void setUp(WireMockRuntimeInfo wireMockRuntimeInfo) throws MalformedURLException { + piwikTracker = new PiwikTracker(wireMockRuntimeInfo.getHttpBaseUrl() + "/matomo.php", -1); + resetAllRequests(); + stubFor(post(urlPathEqualTo("/matomo.php")).willReturn(status(204))); + stubFor(get(urlPathEqualTo("/matomo.php")).willReturn(status(204))); + request = new PiwikRequest(SITE_ID, new URL("https://test.local/test/path?id=123")); + request.setRandomValue(RandomValue.fromString("rand")); + request.setVisitorId(VisitorId.fromHash(999999999999999999L)); + } + + /** + * Test of sendRequest method, of class PiwikTracker. + */ + @Test + void testSendRequest() { + request.setCustomTrackingParameter("parameterName", "parameterValue"); + + piwikTracker.sendRequest(request); + + verify(getRequestedFor(urlEqualTo( + "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue")).withHeader("User-Agent", + equalTo("MatomoJavaClient") + )); + } + + /** + * Test of sendRequestAsync method, of class PiwikTracker. + */ + @Test + void testSendRequestAsync() { + request.setCustomTrackingParameter("parameterName", "parameterValue"); + + CompletableFuture future = piwikTracker.sendRequestAsync(request); + + assertThat(future).isNotCompletedExceptionally(); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + verify(getRequestedFor(urlEqualTo( + "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue")) + .withHeader("User-Agent", equalTo("MatomoJavaClient") + )); + }); + + } + + + /** + * Test of sendBulkRequest method, of class PiwikTracker. + */ + @Test + void testSendBulkRequest_Iterable() { + List requests = Collections.singletonList(request); + request.setCustomTrackingParameter("parameterName", "parameterValue"); + + piwikTracker.sendBulkRequest(requests); + + verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("167")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{ \"requests\" : [ \"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\" ]}"))); + + } + + /** + * Test of sendBulkRequest method, of class PiwikTracker. + */ + @Test + void testSendBulkRequest_Iterable_StringTT() { + List requests = Collections.singletonList(request); + request.setCustomTrackingParameter("parameterName", "parameterValue"); + + assertThatThrownBy(() -> piwikTracker.sendBulkRequest(requests, "1")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Auth token must be exactly 32 characters long"); + } + + @Test + void testSendBulkRequest_Iterable_StringFF() { + List requests = Collections.singletonList(request); + request.setCustomTrackingParameter("parameterName", "parameterValue"); + + piwikTracker.sendBulkRequest(requests, null); + + verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("167")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{\"requests\":[\"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\"]}"))); + + } + + @Test + void testSendBulkRequest_Iterable_StringFT() { + List requests = Collections.singletonList(request); + request.setCustomTrackingParameter("parameterName", "parameterValue"); + + piwikTracker.sendBulkRequest(requests, "12345678901234567890123456789012"); + + verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("215")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{\"requests\":[\"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\" ],\"token_auth\":\"12345678901234567890123456789012\"}"))); + + } + + /** + * Test of sendBulkRequestAsync method, of class PiwikTracker. + */ + @Test + void testSendBulkRequestAsync_Iterable() { + List requests = Collections.singletonList(request); + request.setCustomTrackingParameter("parameterName", "parameterValue"); + + CompletableFuture future = piwikTracker.sendBulkRequestAsync(requests); + + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("167")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{\"requests\" : [ \"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\" ]}"))); + + }); + + } + + /** + * Test of sendBulkRequestAsync method, of class PiwikTracker. + */ + @Test + void testSendBulkRequestAsync_Iterable_StringTT() { + + List requests = Collections.singletonList(request); + request.setCustomTrackingParameter("parameterName", "parameterValue"); + + assertThatThrownBy(() -> piwikTracker.sendBulkRequestAsync(requests, "1").get()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Auth token must be exactly 32 characters long"); + + } + + + @Test + void testSendBulkRequestAsync_Iterable_String() { + + List requests = Collections.singletonList(request); + request.setCustomTrackingParameter("parameterName", "parameterValue"); + + CompletableFuture future = + piwikTracker.sendBulkRequestAsync(requests, "12345678901234567890123456789012"); + + assertThat(future).isNotCompletedExceptionally(); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("215")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{\"requests\":[ \"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\"],\"token_auth\":\"12345678901234567890123456789012\"}"))); + + }); + } + + @Test + void createsPiwikTrackerWithHostUrl(WireMockRuntimeInfo wireMockRuntimeInfo) { + PiwikTracker piwikTracker = new PiwikTracker(wireMockRuntimeInfo.getHttpBaseUrl() + "/matomo.php"); + + piwikTracker.sendRequest(request); + + verify(getRequestedFor(urlEqualTo( + "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand")).withHeader("User-Agent", + equalTo("MatomoJavaClient") + )); + } + + @Test + void createPiwikTrackerWithHostUrlAndProxyHostAndPort(WireMockRuntimeInfo wireMockRuntimeInfo) { + PiwikTracker piwikTracker = + new PiwikTracker(wireMockRuntimeInfo.getHttpBaseUrl() + "/matomo.php", "localhost", 8080); + + assertThatThrownBy(() -> piwikTracker.sendRequest(request)) + .isInstanceOf(MatomoException.class) + ; + + } + + @Test + void createPiwikTrackerWithHostUrlAndProxyHostAndPortAndTimeout(WireMockRuntimeInfo wireMockRuntimeInfo) { + PiwikTracker piwikTracker = + new PiwikTracker(wireMockRuntimeInfo.getHttpBaseUrl() + "/matomo.php", "localhost", 8080, 1000); + + assertThatThrownBy(() -> piwikTracker.sendRequest(request)) + .isInstanceOf(MatomoException.class) + ; + } + +} diff --git a/java8/pom.xml b/java8/pom.xml new file mode 100644 index 00000000..f3e3f4ff --- /dev/null +++ b/java8/pom.xml @@ -0,0 +1,61 @@ + + 4.0.0 + + + org.piwik.java.tracking + matomo-java-tracker-parent + 3.0.0-SNAPSHOT + ../pom.xml + + + matomo-java-tracker + 3.0.0-SNAPSHOT + jar + + Matomo Java Tracker Java 8 + Official Java implementation of the Matomo Tracking HTTP API for Java 8. + + + 2.7.17 + + + + + org.piwik.java.tracking + matomo-java-tracker-core + 3.0.0-SNAPSHOT + + + org.slf4j + slf4j-api + + + com.github.spotbugs + spotbugs-annotations + + + org.projectlombok + lombok + + + org.junit.jupiter + junit-jupiter + + + org.assertj + assertj-core + + + com.github.tomakehurst + wiremock + 2.27.2 + test + + + org.slf4j + slf4j-simple + test + + + + diff --git a/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java b/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java new file mode 100644 index 00000000..ca9b8c93 --- /dev/null +++ b/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java @@ -0,0 +1,292 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking; + +import static java.util.Collections.singleton; +import static java.util.Objects.requireNonNull; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.io.IOException; +import java.io.OutputStream; +import java.net.Authenticator; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.MalformedURLException; +import java.net.ProtocolException; +import java.net.Proxy; +import java.net.URI; +import java.net.URL; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * A {@link Sender} implementation that uses Java 8 {@link HttpURLConnection} to send requests to the Matomo. + * + *

This implementation uses a thread pool to send requests asynchronously. The thread pool is configured using + * {@link TrackerConfiguration#getThreadPoolSize()}. The thread pool uses daemon threads. This means that the JVM will + * exit even if the thread pool is not shut down. + * + *

If you use a newer Java version, please use the newer Java implementation from the Matomo Java Tracker for + * Java 17. + */ +@Slf4j +@RequiredArgsConstructor +class Java8Sender implements Sender { + + private static final TrustManager[] TRUST_ALL_MANAGERS = {new TrustingX509TrustManager()}; + private static final HostnameVerifier TRUSTING_HOSTNAME_VERIFIER = new TrustingHostnameVerifier(); + + private final TrackerConfiguration trackerConfiguration; + + private final QueryCreator queryCreator; + + private final Executor executor; + private final Collection queries = new ArrayList<>(16); + + @Override + @NonNull + public CompletableFuture sendSingleAsync( + @NonNull MatomoRequest request + ) { + return CompletableFuture.supplyAsync(() -> { + sendSingle(request); + return request; + }, executor); + } + + @Override + public void sendSingle( + @NonNull MatomoRequest request + ) { + String authToken = AuthToken.determineAuthToken(null, singleton(request), trackerConfiguration); + RequestValidator.validate(request, authToken); + HttpURLConnection connection; + URI apiEndpoint = trackerConfiguration.getApiEndpoint(); + try { + connection = openConnection(apiEndpoint + .resolve(String.format("%s?%s", apiEndpoint.getPath(), queryCreator.createQuery(request, authToken))) + .toURL()); + } catch (MalformedURLException e) { + throw new InvalidUrlException(e); + } + applyTrackerConfiguration(connection); + addHeaders(connection, request.getHeaders()); + log.debug("Sending single request using URI {} asynchronously", apiEndpoint); + try { + connection.connect(); + checkResponse(connection); + } catch (IOException e) { + throw new MatomoException("Could not send request via GET", e); + } finally { + connection.disconnect(); + } + } + + private void addHeaders(@NonNull HttpURLConnection connection, @Nullable Map headers) { + if (headers != null) { + for (Map.Entry header : headers.entrySet()) { + connection.setRequestProperty(header.getKey(), header.getValue()); + } + } + } + + private HttpURLConnection openConnection(URL url) { + HttpURLConnection connection; + try { + if (isEmpty(trackerConfiguration.getProxyHost()) || trackerConfiguration.getProxyPort() <= 0) { + log.debug("Proxy host or proxy port not configured. Will create connection without proxy"); + connection = (HttpURLConnection) url.openConnection(); + } else { + connection = openProxiedConnection(url); + } + } catch (IOException e) { + throw new MatomoException("Could not open connection", e); + } + if (connection instanceof HttpsURLConnection) { + applySslConfiguration((HttpsURLConnection) connection); + } + return connection; + } + + private void applySslConfiguration( + @NonNull @lombok.NonNull HttpsURLConnection connection + ) { + if (trackerConfiguration.isDisableSslCertValidation()) { + try { + SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, TRUST_ALL_MANAGERS, new SecureRandom()); + connection.setSSLSocketFactory(sslContext.getSocketFactory()); + } catch (Exception e) { + throw new MatomoException("Could not disable SSL certification validation", e); + } + } + if (trackerConfiguration.isDisableSslHostVerification()) { + connection.setHostnameVerifier(TRUSTING_HOSTNAME_VERIFIER); + } + } + + private HttpURLConnection openProxiedConnection( + @NonNull @lombok.NonNull URL url + ) throws IOException { + requireNonNull(trackerConfiguration.getProxyHost(), "Proxy host must not be null"); + if (trackerConfiguration.getProxyPort() <= 0) { + throw new IllegalArgumentException("Proxy port must be configured"); + } + InetSocketAddress proxyAddress = + new InetSocketAddress(trackerConfiguration.getProxyHost(), trackerConfiguration.getProxyPort()); + Proxy proxy = new Proxy(Proxy.Type.HTTP, proxyAddress); + if (!isEmpty(trackerConfiguration.getProxyUsername()) && !isEmpty(trackerConfiguration.getProxyPassword())) { + Authenticator.setDefault(new ProxyAuthenticator(trackerConfiguration.getProxyUsername(), + trackerConfiguration.getProxyPassword() + )); + } + log.debug("Using proxy {} on port {}", trackerConfiguration.getProxyHost(), trackerConfiguration.getProxyPort()); + return (HttpURLConnection) url.openConnection(proxy); + } + + private void applyTrackerConfiguration(HttpURLConnection connection) { + connection.setUseCaches(false); + if (trackerConfiguration.getUserAgent() != null && !trackerConfiguration.getUserAgent().isEmpty()) { + connection.setRequestProperty("User-Agent", trackerConfiguration.getUserAgent()); + } + if (trackerConfiguration.getConnectTimeout() != null) { + connection.setConnectTimeout((int) trackerConfiguration.getConnectTimeout().toMillis()); + } + if (trackerConfiguration.getSocketTimeout() != null) { + connection.setReadTimeout((int) trackerConfiguration.getSocketTimeout().toMillis()); + } + } + + private void checkResponse(HttpURLConnection connection) throws IOException { + int responseCode = connection.getResponseCode(); + if (responseCode > 399) { + if (trackerConfiguration.isLogFailedTracking()) { + log.error("Received HTTP error code {} for URL {}", responseCode, connection.getURL()); + } + throw new MatomoException(String.format("Tracking endpoint responded with code %d", responseCode)); + } + } + + private static boolean isEmpty( + @Nullable String str + ) { + return str == null || str.isEmpty() || str.trim().isEmpty(); + } + + @Override + public void sendBulk( + @NonNull @lombok.NonNull Iterable requests, @Nullable String overrideAuthToken + ) { + String authToken = AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); + Map headers = new LinkedHashMap<>(); + sendBulk(StreamSupport.stream(requests.spliterator(), false).map(request -> { + RequestValidator.validate(request, authToken); + if (request.getHeaders() != null && !request.getHeaders().isEmpty()) { + headers.putAll(request.getHeaders()); + } + return queryCreator.createQuery(request, null); + }).collect(Collectors.toList()), authToken, headers); + } + + private void sendBulk( + @NonNull @lombok.NonNull Collection queries, @Nullable String authToken, Map headers + ) { + if (queries.isEmpty()) { + throw new IllegalArgumentException("Queries must not be empty"); + } + HttpURLConnection connection; + try { + connection = openConnection(trackerConfiguration.getApiEndpoint().toURL()); + } catch (MalformedURLException e) { + throw new InvalidUrlException(e); + } + preparePostConnection(connection); + applyTrackerConfiguration(connection); + addHeaders(connection, headers); + log.debug("Sending bulk request using URI {} asynchronously", trackerConfiguration.getApiEndpoint()); + OutputStream outputStream = null; + try { + connection.connect(); + outputStream = connection.getOutputStream(); + outputStream.write(BulkRequest.builder().queries(queries).authToken(authToken).build().toBytes()); + outputStream.flush(); + checkResponse(connection); + } catch (IOException e) { + throw new MatomoException("Could not send requests via POST", e); + } finally { + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException e) { + // ignore + } + } + connection.disconnect(); + } + } + + private static void preparePostConnection(HttpURLConnection connection) { + try { + connection.setRequestMethod("POST"); + } catch (ProtocolException e) { + throw new MatomoException("Could not set request method", e); + } + connection.setDoOutput(true); + connection.setRequestProperty("Accept", "*/*"); + connection.setRequestProperty("Content-Type", "application/json"); + + } + + @Override + @NonNull + public CompletableFuture sendBulkAsync( + @NonNull Iterable requests, @Nullable String overrideAuthToken + ) { + String authToken = AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); + final Map headers = new LinkedHashMap<>(); + synchronized (queries) { + for (MatomoRequest request : requests) { + RequestValidator.validate(request, authToken); + if (request.getHeaders() != null && !request.getHeaders().isEmpty()) { + headers.putAll(request.getHeaders()); + } + String query = queryCreator.createQuery(request, null); + queries.add(query); + } + } + return CompletableFuture.supplyAsync(() -> sendBulkAsync(authToken, headers), executor); + } + + @Nullable + private Void sendBulkAsync( + @Nullable String authToken, Map headers + ) { + synchronized (queries) { + if (!queries.isEmpty()) { + sendBulk(queries, authToken, headers); + queries.clear(); + } + return null; + } + } + +} diff --git a/java8/src/main/java/org/matomo/java/tracking/Java8SenderProvider.java b/java8/src/main/java/org/matomo/java/tracking/Java8SenderProvider.java new file mode 100644 index 00000000..0a3b40bf --- /dev/null +++ b/java8/src/main/java/org/matomo/java/tracking/Java8SenderProvider.java @@ -0,0 +1,20 @@ +package org.matomo.java.tracking; + +import java.util.concurrent.Executors; + +/** + * Provides a {@link Sender} implementation based on Java 8. + */ +public class Java8SenderProvider implements SenderProvider { + + @Override + public Sender provideSender( + TrackerConfiguration trackerConfiguration, QueryCreator queryCreator + ) { + return new Java8Sender( + trackerConfiguration, + queryCreator, + Executors.newFixedThreadPool(trackerConfiguration.getThreadPoolSize(), new DaemonThreadFactory()) + ); + } +} diff --git a/java8/src/main/resources/META-INF/services/org.matomo.java.tracking.SenderProvider b/java8/src/main/resources/META-INF/services/org.matomo.java.tracking.SenderProvider new file mode 100644 index 00000000..25145902 --- /dev/null +++ b/java8/src/main/resources/META-INF/services/org.matomo.java.tracking.SenderProvider @@ -0,0 +1 @@ +org.matomo.java.tracking.Java8SenderProvider \ No newline at end of file diff --git a/java8/src/test/java/org/matomo/java/tracking/Java8SenderIT.java b/java8/src/test/java/org/matomo/java/tracking/Java8SenderIT.java new file mode 100644 index 00000000..df80df07 --- /dev/null +++ b/java8/src/test/java/org/matomo/java/tracking/Java8SenderIT.java @@ -0,0 +1,223 @@ +package org.matomo.java.tracking; + +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.status; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static java.util.Collections.singleton; +import static java.util.Collections.singletonMap; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import java.net.MalformedURLException; +import java.net.URI; +import java.util.concurrent.CompletableFuture; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +class Java8SenderIT { + + private static final WireMockServer wireMockServer = + new WireMockServer(WireMockConfiguration.options().dynamicPort().dynamicHttpsPort()); + + private Sender sender; + + private TrackerConfiguration trackerConfiguration; + + @BeforeAll + static void beforeAll() { + wireMockServer.start(); + } + + @Test + void sendSingleFailsIfQueryIsMalformed() { + trackerConfiguration = TrackerConfiguration.builder().apiEndpoint(URI.create("telnet://localhost")).build(); + givenSender(); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) + .isInstanceOf(InvalidUrlException.class) + .hasRootCause(new MalformedURLException("unknown protocol: telnet")); + } + + + @Test + void failsIfEndpointReturnsNotFound() { + trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create(wireMockServer.baseUrl())) + .disableSslHostVerification(true) + .disableSslCertValidation(true) + .build(); + + givenSender(); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) + .isInstanceOf(MatomoException.class) + .hasMessage("Tracking endpoint responded with code 404"); + } + + @Test + void failsIfCouldNotConnectToEndpoint() { + trackerConfiguration = TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost:1234")).build(); + + givenSender(); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not send request via GET"); + } + + @Test + void connectsViaProxy() { + trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create(wireMockServer.baseUrl())) + .disableSslCertValidation(true) + .disableSslHostVerification(true) + .proxyHost("localhost") + .proxyPort(wireMockServer.port()) + .build(); + + givenSender(); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not send request via GET"); + } + + @Test + void connectsViaProxyWithProxyUserNameAndPassword() { + trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create(wireMockServer.baseUrl())) + .disableSslCertValidation(true) + .disableSslHostVerification(true) + .proxyHost("localhost") + .proxyPort(wireMockServer.port()) + .proxyUsername("user") + .proxyPassword("password") + .build(); + + givenSender(); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not send request via GET"); + } + + @Test + void logsFailedTracking() { + trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create(wireMockServer.baseUrl())) + .disableSslCertValidation(true) + .disableSslHostVerification(true) + .logFailedTracking(true) + .build(); + + givenSender(); + + assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) + .isInstanceOf(MatomoException.class) + .hasMessage("Tracking endpoint responded with code 404"); + } + + @Test + void skipSslCertificationValidation() { + wireMockServer.stubFor(get(urlPathEqualTo("/matomo_ssl.php")).willReturn(status(204))); + trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create(String.format("https://localhost:%d/matomo_ssl.php", wireMockServer.httpsPort()))) + .disableSslCertValidation(true) + .disableSslHostVerification(true) + .build(); + + givenSender(); + + sender.sendSingle(new MatomoRequest()); + + wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo_ssl.php"))); + + } + + @Test + void addsHeadersToSingleRequest() { + wireMockServer.stubFor(get(urlPathEqualTo("/matomo.php")).willReturn(status(204))); + trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create(String.format("http://localhost:%d/matomo.php", wireMockServer.port()))) + .disableSslCertValidation(true) + .disableSslHostVerification(true) + .build(); + + givenSender(); + + sender.sendSingle(MatomoRequest.request().headers(singletonMap("headerName", "headerValue")).build()); + + wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php")).withHeader( + "headerName", + equalTo("headerValue") + )); + + } + + @Test + void addsHeadersToBulkRequest() { + wireMockServer.stubFor(post(urlPathEqualTo("/matomo.php")).willReturn(status(204))); + trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create(String.format("http://localhost:%d/matomo.php", wireMockServer.port()))) + .disableSslCertValidation(true) + .disableSslHostVerification(true) + .build(); + + givenSender(); + + sender.sendBulk( + singleton(MatomoRequest.request().headers(singletonMap("headerName", "headerValue")).build()), + null + ); + + wireMockServer.verify(postRequestedFor(urlPathEqualTo("/matomo.php")).withHeader( + "headerName", + equalTo("headerValue") + )); + + } + + + @Test + void addsHeadersToBulkAsyncRequest() { + wireMockServer.stubFor(post(urlPathEqualTo("/matomo.php")).willReturn(status(204))); + trackerConfiguration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create(String.format("http://localhost:%d/matomo.php", wireMockServer.port()))) + .disableSslCertValidation(true) + .disableSslHostVerification(true) + .build(); + + givenSender(); + + CompletableFuture future = sender.sendBulkAsync(singleton(MatomoRequest + .request() + .headers(singletonMap("headerName", "headerValue")) + .build()), null); + + future.join(); + wireMockServer.verify(postRequestedFor(urlPathEqualTo("/matomo.php")).withHeader( + "headerName", + equalTo("headerValue") + )); + + } + + + private void givenSender() { + sender = new Java8Sender(trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); + } + +} diff --git a/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java new file mode 100644 index 00000000..6d935c8b --- /dev/null +++ b/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java @@ -0,0 +1,516 @@ +package org.matomo.java.tracking; + +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.status; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static java.util.Collections.singleton; +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import java.net.URI; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Arrays; +import java.util.Locale.LanguageRange; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.matomo.java.tracking.MatomoRequest.MatomoRequestBuilder; +import org.matomo.java.tracking.TrackerConfiguration.TrackerConfigurationBuilder; +import org.matomo.java.tracking.parameters.AcceptLanguage; +import org.matomo.java.tracking.parameters.Country; +import org.matomo.java.tracking.parameters.CustomVariable; +import org.matomo.java.tracking.parameters.CustomVariables; +import org.matomo.java.tracking.parameters.DeviceResolution; +import org.matomo.java.tracking.parameters.EcommerceItem; +import org.matomo.java.tracking.parameters.EcommerceItems; +import org.matomo.java.tracking.parameters.RandomValue; +import org.matomo.java.tracking.parameters.UniqueId; +import org.matomo.java.tracking.parameters.VisitorId; + +class MatomoTrackerIT { + + private static final WireMockServer wireMockServer = + new WireMockServer(WireMockConfiguration.options().dynamicPort()); + + private static final int SITE_ID = 42; + + private final TrackerConfigurationBuilder trackerConfigurationBuilder = TrackerConfiguration.builder(); + + private final MatomoRequestBuilder requestBuilder = MatomoRequest + .builder() + .visitorId(VisitorId.fromHex("bbccddeeff1122")) + .randomValue(RandomValue.fromString("someRandom")); + + private CompletableFuture future; + + private MatomoTracker matomoTracker; + + @BeforeAll + static void beforeAll() { + wireMockServer.start(); + } + + @BeforeEach + void givenStub() { + wireMockServer.resetRequests(); + wireMockServer.stubFor(post(urlPathEqualTo("/matomo.php")).willReturn(status(204))); + wireMockServer.stubFor(get(urlPathEqualTo("/matomo.php")).willReturn(status(204))); + } + + @Test + void requiresSiteId() { + + trackerConfigurationBuilder.apiEndpoint(URI.create(wireMockServer.baseUrl() + "/matomo.php")).build(); + + assertThatThrownBy(this::whenSendsRequestAsync) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("No default site ID and no request site ID is given"); + + } + + private void whenSendsRequestAsync() { + matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); + future = matomoTracker.sendRequestAsync(requestBuilder.build()); + } + + @Test + void usesDefaultSiteId() { + + givenTrackerConfigurationWithDefaultSiteId(); + + whenSendsRequestAsync(); + + thenGetsRequest("idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom"); + + } + + private void givenTrackerConfigurationWithDefaultSiteId() { + trackerConfigurationBuilder + .apiEndpoint(URI.create(wireMockServer.baseUrl() + "/matomo.php")) + .defaultSiteId(SITE_ID); + } + + private void thenGetsRequest(String expectedQuery) { + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(getRequestedFor(urlEqualTo( + String.format("/matomo.php?%s", expectedQuery))).withHeader("User-Agent", + equalTo("MatomoJavaClient") + )); + }); + } + + @Test + void overridesDefaultSiteId() { + + givenTrackerConfigurationWithDefaultSiteId(); + requestBuilder.siteId(123); + + whenSendsRequestAsync(); + + thenGetsRequest("rec=1&idsite=123&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom"); + + } + + @Test + void validatesTokenAuth() { + + givenTrackerConfigurationWithDefaultSiteId(); + requestBuilder.authToken("invalid-token-auth"); + + whenSendsRequestAsync(); + + assertThat(future) + .failsWithin(1, MINUTES) + .withThrowableThat() + .havingRootCause() + .isInstanceOf(IllegalArgumentException.class) + .withMessage("Auth token must be exactly 32 characters long"); + + } + + @Test + void convertsTrueBooleanTo1() { + + givenTrackerConfigurationWithDefaultSiteId(); + requestBuilder.pluginFlash(true); + + whenSendsRequestAsync(); + + thenGetsRequest("idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&fla=1&send_image=0&rand=someRandom"); + + } + + @Test + void convertsFalseBooleanTo0() { + + givenTrackerConfigurationWithDefaultSiteId(); + requestBuilder.pluginJava(false); + + whenSendsRequestAsync(); + + thenGetsRequest("idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&java=0&send_image=0&rand=someRandom"); + + } + + @Test + void encodesUrl() { + + givenTrackerConfigurationWithDefaultSiteId(); + requestBuilder.actionUrl("https://www.daniel-heid.de/some/page?foo=bar"); + + whenSendsRequestAsync(); + + thenGetsRequest( + "idsite=42&rec=1&url=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fpage%3Ffoo%3Dbar&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom"); + + } + + @Test + void encodesReferrerUrl() { + + givenTrackerConfigurationWithDefaultSiteId(); + requestBuilder.referrerUrl("https://www.daniel-heid.de/some/referrer?foo2=bar2"); + + whenSendsRequestAsync(); + + thenGetsRequest( + "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Freferrer%3Ffoo2%3Dbar2&send_image=0&rand=someRandom"); + + } + + @Test + void encodesLink() { + + givenTrackerConfigurationWithDefaultSiteId(); + requestBuilder.outlinkUrl("https://www.daniel-heid.de/some/external/link#"); + + whenSendsBulkRequestAsync(); + + thenPostsRequestWithoutAuthToken( + "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&link=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fexternal%2Flink%23&send_image=0&rand=someRandom", + "156" + ); + + } + + @Test + void sendsRequestsBulkAsynchronously() { + + givenTrackerConfigurationWithDefaultSiteId(); + matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); + + future = matomoTracker.sendBulkRequestAsync(requestBuilder.build()); + + thenPostsRequestWithoutAuthToken("idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom", "90"); + + } + + private void whenSendsBulkRequestAsync() { + future = + new MatomoTracker(trackerConfigurationBuilder.build()).sendBulkRequestAsync(singleton(requestBuilder.build())); + } + + private void thenPostsRequestWithoutAuthToken(String expectedQuery, String contentLength) { + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo(contentLength)) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson("{\"requests\":[\"?" + expectedQuery + "\"]}"))); + }); + + } + + @Test + void encodesDownloadUrl() { + + givenTrackerConfigurationWithDefaultSiteId(); + requestBuilder.downloadUrl("https://www.daniel-heid.de/some/download.pdf"); + + whenSendsBulkRequestAsync(); + + thenPostsRequestWithoutAuthToken( + "idsite=42&rec=1&apiv=1&_id=00bbccddeeff1122&download=https%3A%2F%2Fwww.daniel-heid.de%2Fsome%2Fdownload.pdf&send_image=0&rand=someRandom", + "154" + ); + + } + + @Test + void getContainsHeaders() { + + givenTrackerConfigurationWithDefaultSiteId(); + + whenSendsRequestAsync(); + + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php")).withHeader("User-Agent", + equalTo("MatomoJavaClient") + )); + }); + + } + + @Test + void postContainsHeaders() { + + givenTrackerConfigurationWithDefaultSiteId(); + + whenSendsBulkRequestAsync(); + + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(postRequestedFor(urlPathEqualTo("/matomo.php")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Length", equalTo("90")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient"))); + }); + + } + + @Test + void allowsToOverrideUserAgent() { + + givenTrackerConfigurationWithDefaultSiteId(); + trackerConfigurationBuilder.userAgent("Mozilla/5.0"); + + whenSendsRequestAsync(); + + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php")).withHeader("User-Agent", + equalTo("Mozilla/5.0") + )); + }); + + } + + @Test + void tracksMinimalRequest() { + + givenTrackerConfigurationWithDefaultSiteId(); + requestBuilder + .actionName("Help / Feedback") + .actionUrl("https://www.daniel-heid.de/portfolio") + .visitorId(VisitorId.fromHash(3434343434343434343L)) + .referrerUrl("https://www.daniel-heid.de/referrer") + .visitCustomVariables(new CustomVariables() + .add(new CustomVariable("customVariable1Key", "customVariable1Value"), 4) + .add(new CustomVariable("customVariable2Key", "customVariable2Value"), 5)) + .visitorVisitCount(2) + .visitorFirstVisitTimestamp(LocalDateTime.of(2022, 8, 9, 18, 34, 12).toInstant(ZoneOffset.UTC)) + .deviceResolution(DeviceResolution.builder().width(1024).height(768).build()) + .headerAcceptLanguage(AcceptLanguage + .builder() + .languageRange(new LanguageRange("de")) + .languageRange(new LanguageRange("de-DE", 0.9)) + .languageRange(new LanguageRange("en", 0.8)) + .build()) + .pageViewId(UniqueId.fromValue(999999999999999999L)) + .goalId(0) + .ecommerceRevenue(12.34) + .ecommerceItems(EcommerceItems + .builder() + .item(org.matomo.java.tracking.parameters.EcommerceItem.builder().sku("SKU").build()) + .item(EcommerceItem.builder().sku("SKU").name("NAME").category("CATEGORY").price(123.4).build()) + .build()) + .authToken("fdf6e8461ea9de33176b222519627f78") + .visitorCountry(Country.fromLanguageRanges("en-GB;q=0.7,de,de-DE;q=0.9,en;q=0.8,en-US;q=0.6")); + + whenSendsBulkRequestAsync(); + + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("711")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson("{\"requests\":[\"?" + + "idsite=42&rec=1&action_name=Help+%2F+Feedback&url=https%3A%2F%2Fwww.daniel-heid.de%2Fportfolio&apiv=1&_id=2fa93d2858bc4867&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Freferrer&_cvar=%7B%224%22%3A%5B%22customVariable1Key%22%2C%22customVariable1Value%22%5D%2C%225%22%3A%5B%22customVariable2Key%22%2C%22customVariable2Value%22%5D%7D&_idvc=2&_idts=1660070052&res=1024x768&lang=de%2Cde-de%3Bq%3D0.9%2Cen%3Bq%3D0.8&pv_id=lbBbxG&idgoal=0&revenue=12.34&ec_items=%5B%5B%22SKU%22%2C%22%22%2C%22%22%2C0.0%2C0%5D%2C%5B%22SKU%22%2C%22NAME%22%2C%22CATEGORY%22%2C123.4%2C0%5D%5D&token_auth=fdf6e8461ea9de33176b222519627f78&country=de&send_image=0&rand=someRandom" + + "\"],\"token_auth\" : \"" + "fdf6e8461ea9de33176b222519627f78" + "\"}"))); + + }); + + } + + @Test + void doesNothingIfNotEnabled() throws Exception { + + wireMockServer.resetRequests(); + givenTrackerConfigurationWithDefaultSiteId(); + trackerConfigurationBuilder.enabled(false); + + whenSendsRequestAsync(); + future.get(); + + wireMockServer.verify(0, postRequestedFor(urlPathEqualTo("/matomo.php"))); + + } + + @Test + void reportsErrors() { + + wireMockServer.stubFor(get(urlPathEqualTo("/failing")).willReturn(status(500))); + trackerConfigurationBuilder.apiEndpoint(URI.create(wireMockServer.baseUrl() + "/failing")).defaultSiteId(SITE_ID); + + whenSendsRequestAsync(); + + assertThat(future) + .failsWithin(1, MINUTES) + .withThrowableThat() + .havingRootCause() + .isInstanceOf(MatomoException.class) + .withMessage("Tracking endpoint responded with code 500"); + + } + + @Test + void includesDefaultTokenAuth() { + + givenTrackerConfigurationWithDefaultSiteId(); + trackerConfigurationBuilder.defaultAuthToken("fdf6e8461ea9de33176b222519627f78"); + + whenSendsRequestAsync(); + + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(getRequestedFor(urlEqualTo( + "/matomo.php?idsite=42&token_auth=fdf6e8461ea9de33176b222519627f78&rec=1&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom")).withHeader("User-Agent", + equalTo("MatomoJavaClient") + )); + }); + + } + + @Test + void includesMultipleQueriesInBulkRequest() { + + givenTrackerConfigurationWithDefaultSiteId(); + matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); + + future = matomoTracker.sendBulkRequestAsync(Arrays.asList(requestBuilder.actionName("First").build(), + requestBuilder.actionName("Second").build(), + requestBuilder.actionName("Third").build() + )); + + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("297")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{\"requests\" : [ \"?idsite=42&rec=1&action_name=First&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\", \"?idsite=42&rec=1&action_name=Second&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\", \"?idsite=42&rec=1&action_name=Third&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\" ]}"))); + }); + + } + + @Test + void failsOnNegativeSiteId() { + + givenTrackerConfigurationWithDefaultSiteId(); + requestBuilder.siteId(-1); + + whenSendsRequestAsync(); + + assertThat(future) + .failsWithin(1, MINUTES) + .withThrowableThat() + .havingRootCause() + .isInstanceOf(IllegalArgumentException.class) + .withMessage("Site ID must not be negative"); + + ; + } + + @Test + void doesNotSendRequestAsyncIfTrackerConfigurationIsDisabled() throws Exception { + givenTrackerConfigurationWithDefaultSiteId(); + trackerConfigurationBuilder.enabled(false); + + whenSendsRequestAsync(); + future.get(); + + wireMockServer.verify(0, getRequestedFor(urlPathEqualTo("/matomo.php"))); + + } + + @Test + void doesNotSendRequestIfTrackerConfigurationIsDisabled() { + givenTrackerConfigurationWithDefaultSiteId(); + trackerConfigurationBuilder.enabled(false); + + whenSendsSingleRequest(); + + wireMockServer.verify(0, getRequestedFor(urlPathEqualTo("/matomo.php"))); + } + + private void whenSendsSingleRequest() { + new MatomoTracker(trackerConfigurationBuilder.build()).sendRequest(requestBuilder.build()); + } + + @Test + void doesNotSendBulkRequestIfTrackerConfigurationIsDisabled() { + givenTrackerConfigurationWithDefaultSiteId(); + trackerConfigurationBuilder.enabled(false); + + whenSendsBulkRequest(); + + wireMockServer.verify(0, postRequestedFor(urlPathEqualTo("/matomo.php"))); + } + + private void whenSendsBulkRequest() { + new MatomoTracker(trackerConfigurationBuilder.build()).sendBulkRequest(singleton(requestBuilder.build())); + } + + @Test + void doesNotSendBulkRequestAsyncIfTrackerConfigurationIsDisabled() throws Exception { + givenTrackerConfigurationWithDefaultSiteId(); + trackerConfigurationBuilder.enabled(false); + + whenSendsBulkRequestAsync(); + + future.get(); + wireMockServer.verify(0, postRequestedFor(urlPathEqualTo("/matomo.php"))); + } + + @Test + void sendsRequestAsyncAndAcceptsCallback() { + givenTrackerConfigurationWithDefaultSiteId(); + matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); + AtomicBoolean success = new AtomicBoolean(); + future = matomoTracker.sendRequestAsync(requestBuilder.build(), request -> { + success.set(true); + }); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php"))); + }); + assertThat(success).isTrue(); + } + + @Test + void sendsRequestsAsyncAndAcceptsCallback() { + givenTrackerConfigurationWithDefaultSiteId(); + matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); + AtomicBoolean success = new AtomicBoolean(); + future = matomoTracker.sendBulkRequestAsync(singleton(requestBuilder.build()), v -> { + success.set(true); + }); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(postRequestedFor(urlPathEqualTo("/matomo.php"))); + }); + assertThat(success).isTrue(); + } + +} diff --git a/core/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java b/java8/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java similarity index 97% rename from core/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java rename to java8/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java index bc28ec22..523b9f4a 100644 --- a/core/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java +++ b/java8/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java @@ -75,10 +75,10 @@ void testSendRequest() { * Test of sendRequestAsync method, of class PiwikTracker. */ @Test - void testSendRequestAsync() throws Exception { + void testSendRequestAsync() { request.setCustomTrackingParameter("parameterName", "parameterValue"); - CompletableFuture future = piwikTracker.sendRequestAsync(request); + CompletableFuture future = piwikTracker.sendRequestAsync(request); assertThat(future).isNotCompletedExceptionally(); assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { @@ -162,7 +162,7 @@ void testSendBulkRequest_Iterable_StringFT() { * Test of sendBulkRequestAsync method, of class PiwikTracker. */ @Test - void testSendBulkRequestAsync_Iterable() throws Exception { + void testSendBulkRequestAsync_Iterable() { List requests = Collections.singletonList(request); request.setCustomTrackingParameter("parameterName", "parameterValue"); @@ -198,7 +198,7 @@ void testSendBulkRequestAsync_Iterable_StringTT() { @Test - void testSendBulkRequestAsync_Iterable_String() throws Exception { + void testSendBulkRequestAsync_Iterable_String() { List requests = Collections.singletonList(request); request.setCustomTrackingParameter("parameterName", "parameterValue"); diff --git a/pom.xml b/pom.xml index 3798ae4f..9e21cacf 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0-rc3-SNAPSHOT + 3.0.0-SNAPSHOT pom Matomo Java Tracker Parent @@ -66,6 +66,45 @@ 1.18.30 + + + + org.projectlombok + lombok + ${lombok.version} + provided + + + org.slf4j + slf4j-api + ${slf4j.version} + + + com.github.spotbugs + spotbugs-annotations + 4.8.0 + provided + + + org.assertj + assertj-core + 3.24.2 + test + + + org.junit.jupiter + junit-jupiter + 5.10.1 + test + + + org.slf4j + slf4j-simple + ${slf4j.version} + + + + @@ -261,7 +300,7 @@ LINE COVEREDRATIO - 0.9 + 0.8 BRANCH @@ -331,6 +370,9 @@ core + java8 + java17 + spring test diff --git a/spring/pom.xml b/spring/pom.xml new file mode 100644 index 00000000..1a8a4700 --- /dev/null +++ b/spring/pom.xml @@ -0,0 +1,63 @@ + + 4.0.0 + + + org.piwik.java.tracking + matomo-java-tracker-parent + 3.0.0-SNAPSHOT + ../pom.xml + + + matomo-java-tracker-spring-boot-starter + jar + + Matomo Java Tracker Spring Boot Starter + Spring integration of Matomo Java Tracker + + + 17 + 17 + 3.1.5 + + + + + org.piwik.java.tracking + matomo-java-tracker-java17 + 3.0.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter + ${spring-boot.version} + + + org.springframework.boot + spring-boot-autoconfigure-processor + ${spring-boot.version} + true + + + org.springframework.boot + spring-boot-configuration-processor + ${spring-boot.version} + true + + + org.projectlombok + lombok + + + org.springframework.boot + spring-boot-starter-test + ${spring-boot.version} + test + + + org.slf4j + slf4j-simple + test + + + + diff --git a/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java b/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java new file mode 100644 index 00000000..ad758bd6 --- /dev/null +++ b/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java @@ -0,0 +1,101 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking.spring; + +import java.net.URI; +import java.util.List; +import org.matomo.java.tracking.MatomoTracker; +import org.matomo.java.tracking.TrackerConfiguration; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + +/** + * {@link AutoConfiguration Auto configuration} for Matomo Tracker. + * + * @see MatomoTrackerProperties + * @see TrackerConfiguration + * @see MatomoTracker + */ +@AutoConfiguration +@ConditionalOnProperty(prefix = "matomo.tracker", name = "api-endpoint") +@EnableConfigurationProperties(MatomoTrackerProperties.class) +public class MatomoTrackerAutoConfiguration { + + /** + * Creates a {@link TrackerConfiguration.TrackerConfigurationBuilder} and applies all + * {@link TrackerConfigurationBuilderCustomizer}s. Can be overridden by custom beans. + * + * @param customizers the customizers to apply to the builder instance (never {@code null}) + * @return the {@link TrackerConfiguration.TrackerConfigurationBuilder} instance (never {@code null}) + * @see TrackerConfiguration#builder() + */ + @Bean + @ConditionalOnMissingBean + public TrackerConfiguration.TrackerConfigurationBuilder trackerConfigurationBuilder( + List customizers + ) { + TrackerConfiguration.TrackerConfigurationBuilder builder = TrackerConfiguration.builder(); + customizers.forEach(customizer -> customizer.customize(builder)); + return builder; + } + + /** + * Creates a {@link TrackerConfiguration} instance based on the current configuration. Can be + * overridden by custom beans. + * + *

If you define your own {@link TrackerConfiguration} bean, please don't forget to set the + * API endpoint. + * + * @param builder the {@link TrackerConfiguration.TrackerConfigurationBuilder} instance (never {@code null}) + * @return the {@link TrackerConfiguration} instance (never {@code null}) + * @see TrackerConfiguration#builder() + * @see TrackerConfiguration.TrackerConfigurationBuilder#apiEndpoint(URI) + */ + @Bean + @ConditionalOnMissingBean + public TrackerConfiguration trackerConfiguration( + TrackerConfiguration.TrackerConfigurationBuilder builder + ) { + return builder.build(); + } + + /** + * Configures the {@link TrackerConfiguration.TrackerConfigurationBuilder} with the properties from + * {@link MatomoTrackerProperties}. + * + * @param properties the {@link MatomoTrackerProperties} instance (never {@code null}) + * @return the {@link StandardTrackerConfigurationBuilderCustomizer} instance (never {@code null}) + * @see MatomoTrackerProperties + * @see TrackerConfiguration.TrackerConfigurationBuilder + */ + @Bean + public StandardTrackerConfigurationBuilderCustomizer standardTrackerConfigurationBuilderCustomizer( + MatomoTrackerProperties properties + ) { + return new StandardTrackerConfigurationBuilderCustomizer(properties); + } + + /** + * A {@link MatomoTracker} instance based on the current configuration. Only created if a bean of the same type is not + * already configured. + * + * @param trackerConfiguration the {@link TrackerConfiguration} instance (never {@code null}) + * @return the {@link MatomoTracker} instance (never {@code null}) + * @see MatomoTracker + * @see TrackerConfiguration + */ + @Bean + @ConditionalOnMissingBean + public MatomoTracker matomoTracker(TrackerConfiguration trackerConfiguration) { + return new MatomoTracker(trackerConfiguration); + } + +} diff --git a/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerProperties.java b/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerProperties.java new file mode 100644 index 00000000..8be4023d --- /dev/null +++ b/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerProperties.java @@ -0,0 +1,148 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking.spring; + +import java.time.Duration; +import lombok.Getter; +import lombok.Setter; +import org.matomo.java.tracking.TrackerConfiguration; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Configuration properties for the Matomo Tracker. + * + *

These properties can be configured in the application.properties file. For example: + *

+ *   matomo.tracker.api-endpoint=https://your-matomo-domain.example/matomo.php
+ *   matomo.tracker.default-site-id=1
+ *   matomo.tracker.default-auth-token=1234567890abcdef1234567890abcdef
+ *   matomo.tracker.enabled=true
+ *   matomo.tracker.connect-timeout=10s
+ *   matomo.tracker.socket-timeout=30s
+ *   matomo.tracker.proxy-host=proxy.example.com
+ *   matomo.tracker.proxy-port=8080
+ *   matomo.tracker.proxy-username=proxyuser
+ *   matomo.tracker.proxy-password=proxypassword
+ *   matomo.tracker.user-agent=MatomoJavaClient
+ *   matomo.tracker.log-failed-tracking=true
+ *   matomo.tracker.disable-ssl-cert-validation=true
+ *   matomo.tracker.disable-ssl-host-validation=true
+ *   matomo.tracker.thread-pool-size=2
+ * 
+ * + * @see MatomoTrackerAutoConfiguration + * @see TrackerConfiguration + */ +@ConfigurationProperties(prefix = "matomo.tracker") +@Getter +@Setter +public class MatomoTrackerProperties { + + /** + * The Matomo Tracking HTTP API endpoint, for example https://your-matomo-domain.example/matomo.php + */ + private String apiEndpoint; + + /** + * The default ID of the website that will be used if not specified explicitly. + */ + private Integer defaultSiteId; + + /** + * The authorization token (parameter token_auth) to use if not specified explicitly. + */ + private String defaultAuthToken; + + /** + * Allows to stop the tracker to send requests to the Matomo endpoint. + */ + private Boolean enabled = true; + + /** + * The timeout until a connection is established. + * + *

A timeout value of zero is interpreted as an infinite timeout. + * A `null` value is interpreted as undefined (system default if applicable).

+ * + *

Default: 10 seconds

+ */ + private Duration connectTimeout = Duration.ofSeconds(5L); + + /** + * The socket timeout ({@code SO_TIMEOUT}), which is the timeout for waiting for data or, put differently, a maximum + * period inactivity between two consecutive data packets. + * + *

A timeout value of zero is interpreted as an infinite timeout. + * A `null value is interpreted as undefined (system default if applicable).

+ * + *

Default: 30 seconds

+ */ + private Duration socketTimeout = Duration.ofSeconds(5L); + + /** + * The hostname or IP address of an optional HTTP proxy. {@code proxyPort} must be configured as well + */ + private String proxyHost; + + /** + * The port of an HTTP proxy. {@code proxyHost} must be configured as well. + */ + private Integer proxyPort; + + /** + * If the HTTP proxy requires a username for basic authentication, it can be configured here. Proxy host, port and + * password must also be set. + */ + private String proxyUsername; + + /** + * The corresponding password for the basic auth proxy user. The proxy host, port and username must be set as well. + */ + private String proxyPassword; + + /** + * A custom user agent to be set. Defaults to "MatomoJavaClient" + */ + private String userAgent = "MatomoJavaClient"; + + /** + * Logs if the Matomo Tracking API endpoint responds with an erroneous HTTP code. Defaults to + * false. + */ + private Boolean logFailedTracking; + + /** + * Disables SSL certificate validation. This is useful for testing with self-signed certificates. + * Do not use in production environments. Defaults to false. + * + *

Attention: This slows down performance + + * @see #disableSslHostVerification + */ + private Boolean disableSslCertValidation; + + /** + * Disables SSL host verification. This is useful for testing with self-signed certificates. Do + * not use in production environments. Defaults to false. + * + *

Attention: This slows down performance + * + * @see #disableSslCertValidation + */ + private Boolean disableSslHostVerification; + + /** + * The thread pool size for the async sender. Defaults to 2. + * + *

Attention: If you use this library in a web application, make sure that this thread pool + * does not exceed the thread pool of the web application. Otherwise, you might run into + * problems. + */ + private Integer threadPoolSize = 2; + +} diff --git a/spring/src/main/java/org/matomo/java/tracking/spring/StandardTrackerConfigurationBuilderCustomizer.java b/spring/src/main/java/org/matomo/java/tracking/spring/StandardTrackerConfigurationBuilderCustomizer.java new file mode 100644 index 00000000..e9f1acd2 --- /dev/null +++ b/spring/src/main/java/org/matomo/java/tracking/spring/StandardTrackerConfigurationBuilderCustomizer.java @@ -0,0 +1,50 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking.spring; + +import java.net.URI; +import org.matomo.java.tracking.TrackerConfiguration; +import org.springframework.boot.context.properties.PropertyMapper; +import org.springframework.core.Ordered; +import org.springframework.lang.NonNull; + +class StandardTrackerConfigurationBuilderCustomizer implements TrackerConfigurationBuilderCustomizer, Ordered { + + private final MatomoTrackerProperties properties; + + StandardTrackerConfigurationBuilderCustomizer(MatomoTrackerProperties properties) { + this.properties = properties; + } + + @Override + public int getOrder() { + return 0; + } + + @Override + public void customize(@NonNull TrackerConfiguration.TrackerConfigurationBuilder builder) { + PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); + map.from(properties::getApiEndpoint).as(URI::create).to(builder::apiEndpoint); + map.from(properties::getDefaultSiteId).to(builder::defaultSiteId); + map.from(properties::getDefaultAuthToken).to(builder::defaultAuthToken); + map.from(properties::getEnabled).to(builder::enabled); + map.from(properties::getConnectTimeout).to(builder::connectTimeout); + map.from(properties::getSocketTimeout).to(builder::socketTimeout); + map.from(properties::getProxyHost).to(builder::proxyHost); + map.from(properties::getProxyPort).to(builder::proxyPort); + map.from(properties::getProxyUsername).to(builder::proxyUsername); + map.from(properties::getProxyPassword).to(builder::proxyPassword); + map.from(properties::getUserAgent).to(builder::userAgent); + map.from(properties::getLogFailedTracking).to(builder::logFailedTracking); + map.from(properties::getDisableSslCertValidation).to(builder::disableSslCertValidation); + map.from(properties::getDisableSslHostVerification).to(builder::disableSslHostVerification); + map.from(properties::getThreadPoolSize).to(builder::threadPoolSize); + } + + +} \ No newline at end of file diff --git a/spring/src/main/java/org/matomo/java/tracking/spring/TrackerConfigurationBuilderCustomizer.java b/spring/src/main/java/org/matomo/java/tracking/spring/TrackerConfigurationBuilderCustomizer.java new file mode 100644 index 00000000..e147a64f --- /dev/null +++ b/spring/src/main/java/org/matomo/java/tracking/spring/TrackerConfigurationBuilderCustomizer.java @@ -0,0 +1,34 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ + +package org.matomo.java.tracking.spring; + +import org.matomo.java.tracking.TrackerConfiguration; +import org.springframework.lang.NonNull; + +/** + * Allows to customize the {@link TrackerConfiguration.TrackerConfigurationBuilder} with additional properties. + * + *

Implementations of this interface are detected automatically by the {@link MatomoTrackerAutoConfiguration}. + * + * @see MatomoTrackerAutoConfiguration + * @see TrackerConfiguration + * @see TrackerConfiguration.TrackerConfigurationBuilder + */ +@FunctionalInterface +public interface TrackerConfigurationBuilderCustomizer { + + /** + * Customize the {@link TrackerConfiguration.TrackerConfigurationBuilder}. + * + * @param builder the {@link TrackerConfiguration.TrackerConfigurationBuilder} instance (never {@code null}) + * @see TrackerConfiguration#builder() + * @see MatomoTrackerProperties + */ + void customize(@NonNull TrackerConfiguration.TrackerConfigurationBuilder builder); + +} diff --git a/spring/src/main/java/org/matomo/java/tracking/spring/package-info.java b/spring/src/main/java/org/matomo/java/tracking/spring/package-info.java new file mode 100644 index 00000000..784c9d72 --- /dev/null +++ b/spring/src/main/java/org/matomo/java/tracking/spring/package-info.java @@ -0,0 +1,10 @@ +/** + * Provides Spring specific classes to integrate Matomo tracking into Spring applications. + * + *

See {@link org.matomo.java.tracking.spring.MatomoTrackerProperties} for the available configuration properties. + * + *

See {@link org.matomo.java.tracking.spring.MatomoTrackerAutoConfiguration} for the available configuration + * options. + */ + +package org.matomo.java.tracking.spring; \ No newline at end of file diff --git a/spring/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..dd6d4697 --- /dev/null +++ b/spring/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +org.matomo.java.tracking.spring.MatomoTrackerAutoConfiguration diff --git a/spring/src/test/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfigurationIT.java b/spring/src/test/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfigurationIT.java new file mode 100644 index 00000000..c008dbc8 --- /dev/null +++ b/spring/src/test/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfigurationIT.java @@ -0,0 +1,70 @@ +package org.matomo.java.tracking.spring; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.URI; +import java.time.Duration; +import org.junit.jupiter.api.Test; +import org.matomo.java.tracking.MatomoTracker; +import org.matomo.java.tracking.TrackerConfiguration; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +class MatomoTrackerAutoConfigurationIT { + + private final ApplicationContextRunner contextRunner = + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(MatomoTrackerAutoConfiguration.class)); + + + @Test + void matomoTrackerRegistration() { + contextRunner.withPropertyValues("matomo.tracker.api-endpoint:https://test.com/matomo.php").run(context -> { + assertThat(context).hasSingleBean(MatomoTracker.class).hasBean("matomoTracker"); + }); + } + + @Test + void additionalTrackerConfigurationBuilderCustomization() { + this.contextRunner + .withPropertyValues("matomo.tracker.api-endpoint:https://test.com/matomo.php") + .withUserConfiguration(TrackerConfigurationBuilderCustomizerConfig.class) + .run(context -> { + TrackerConfiguration trackerConfiguration = context.getBean(TrackerConfiguration.class); + assertThat(trackerConfiguration.getConnectTimeout()).isEqualTo(Duration.ofMinutes(1L)); + }); + } + + @Test + void customTrackerConfigurationBuilder() { + this.contextRunner + .withPropertyValues("matomo.tracker.api-endpoint:https://test.com/matomo.php") + .withUserConfiguration(TrackerConfigurationBuilderConfig.class) + .run(context -> { + TrackerConfiguration trackerConfiguration = context.getBean(TrackerConfiguration.class); + assertThat(trackerConfiguration.isDisableSslHostVerification()).isTrue(); + }); + } + + @Configuration + static class TrackerConfigurationBuilderCustomizerConfig { + + @Bean + TrackerConfigurationBuilderCustomizer customConnectTimeout() { + return configurationBuilder -> configurationBuilder.connectTimeout(Duration.ofMinutes(1L)); + } + + } + + @Configuration + static class TrackerConfigurationBuilderConfig { + + @Bean + TrackerConfiguration.TrackerConfigurationBuilder customTrackerConfigurationBuilder() { + return TrackerConfiguration.builder().apiEndpoint(URI.create("https://test.com/matomo.php")).disableSslHostVerification(true); + } + + } + +} \ No newline at end of file diff --git a/spring/src/test/java/org/matomo/java/tracking/spring/StandardTrackerConfigurationBuilderCustomizerIT.java b/spring/src/test/java/org/matomo/java/tracking/spring/StandardTrackerConfigurationBuilderCustomizerIT.java new file mode 100644 index 00000000..b62eafec --- /dev/null +++ b/spring/src/test/java/org/matomo/java/tracking/spring/StandardTrackerConfigurationBuilderCustomizerIT.java @@ -0,0 +1,55 @@ +package org.matomo.java.tracking.spring; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Duration; +import org.junit.jupiter.api.Test; +import org.matomo.java.tracking.TrackerConfiguration; + +class StandardTrackerConfigurationBuilderCustomizerIT { + + @Test + void createsStandardTrackerConfigurationBuilderCustomizer() { + MatomoTrackerProperties properties = new MatomoTrackerProperties(); + properties.setApiEndpoint("https://test.com/matomo.php"); + properties.setDefaultSiteId(1); + properties.setDefaultAuthToken("abc123def4563123abc123def4563123"); + properties.setEnabled(true); + properties.setConnectTimeout(Duration.ofMinutes(1L)); + properties.setSocketTimeout(Duration.ofMinutes(2L)); + properties.setProxyHost("proxy.example.com"); + properties.setProxyPort(8080); + properties.setProxyUsername("user"); + properties.setProxyPassword("password"); + properties.setUserAgent("Mozilla/5.0 (compatible; AcmeInc/1.0; +https://example.com/bot.html)"); + properties.setLogFailedTracking(true); + properties.setDisableSslCertValidation(true); + properties.setDisableSslHostVerification(true); + properties.setThreadPoolSize(10); + StandardTrackerConfigurationBuilderCustomizer customizer = + new StandardTrackerConfigurationBuilderCustomizer(properties); + TrackerConfiguration.TrackerConfigurationBuilder builder = TrackerConfiguration.builder(); + + customizer.customize(builder); + + assertThat(customizer.getOrder()).isZero(); + TrackerConfiguration configuration = builder.build(); + assertThat(configuration.getApiEndpoint()).hasToString("https://test.com/matomo.php"); + assertThat(configuration.getDefaultSiteId()).isEqualTo(1); + assertThat(configuration.getDefaultAuthToken()).isEqualTo("abc123def4563123abc123def4563123"); + assertThat(configuration.isEnabled()).isTrue(); + assertThat(configuration.getConnectTimeout()).hasSeconds(60L); + assertThat(configuration.getSocketTimeout()).hasSeconds(120L); + assertThat(configuration.getProxyHost()).isEqualTo("proxy.example.com"); + assertThat(configuration.getProxyPort()).isEqualTo(8080); + assertThat(configuration.getProxyUsername()).isEqualTo("user"); + assertThat(configuration.getProxyPassword()).isEqualTo("password"); + assertThat(configuration.getUserAgent()).isEqualTo( + "Mozilla/5.0 (compatible; AcmeInc/1.0; +https://example.com/bot.html)"); + assertThat(configuration.isLogFailedTracking()).isTrue(); + assertThat(configuration.isDisableSslCertValidation()).isTrue(); + assertThat(configuration.isDisableSslHostVerification()).isTrue(); + assertThat(configuration.getThreadPoolSize()).isEqualTo(10); + } + +} \ No newline at end of file diff --git a/test/pom.xml b/test/pom.xml index 6027ddbb..4f012104 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -5,7 +5,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0-rc3-SNAPSHOT + 3.0.0-SNAPSHOT ../pom.xml @@ -15,13 +15,17 @@ Test application for Matomo Java Tracker + 17 + 17 + true + true true org.piwik.java.tracking - matomo-java-tracker + matomo-java-tracker-java17 ${project.version} diff --git a/test/src/main/java/org/matomo/java/tracking/MatomoJavaTrackerTest.java b/test/src/main/java/org/matomo/java/tracking/MatomoJavaTrackerTest.java index fdb0f040..50f6f648 100644 --- a/test/src/main/java/org/matomo/java/tracking/MatomoJavaTrackerTest.java +++ b/test/src/main/java/org/matomo/java/tracking/MatomoJavaTrackerTest.java @@ -1,7 +1,5 @@ package org.matomo.java.tracking; -import static java.util.Arrays.asList; - import com.github.javafaker.Country; import com.github.javafaker.Faker; import java.net.URI; @@ -9,6 +7,7 @@ import java.time.Instant; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -68,7 +67,7 @@ private void sendBulkRequests() { private void sendRequestAsync() { MatomoRequest request = randomRequest(); - CompletableFuture future = tracker.sendRequestAsync(request); + CompletableFuture future = tracker.sendRequestAsync(request); future.thenAccept(v -> System.out.printf("Successfully sent async single request to Matomo server: %s%n", request)); } @@ -179,7 +178,7 @@ private MatomoRequest randomRequest() { .contentPiece(faker.ancient().god()) .contentTarget("https://" + faker.internet().url()) .contentInteraction(faker.app().name()) - .dimensions(asList(faker.artist().name(), faker.dog().name())) + .dimensions(Map.of(1L, faker.artist().name(), 2L, faker.dog().name())) .debug(true) .build(); } From cefeb1a7bf69e24c6db3dac914ba95fb4d781b67 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Sat, 11 Nov 2023 18:13:47 +0100 Subject: [PATCH 180/467] Do not add restricted headers from HTTP servlet request --- ...tomizer.java => ServletMatomoRequest.java} | 21 +++++++++++++++--- ...est.java => ServletMatomoRequestTest.java} | 22 +++++++++++++++---- 2 files changed, 36 insertions(+), 7 deletions(-) rename core/src/main/java/org/matomo/java/tracking/{ServletMatomoRequestCustomizer.java => ServletMatomoRequest.java} (75%) rename core/src/test/java/org/matomo/java/tracking/{ServletMatomoRequestCustomizerTest.java => ServletMatomoRequestTest.java} (54%) diff --git a/core/src/main/java/org/matomo/java/tracking/ServletMatomoRequestCustomizer.java b/core/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java similarity index 75% rename from core/src/main/java/org/matomo/java/tracking/ServletMatomoRequestCustomizer.java rename to core/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java index 2b556d59..b4c8220e 100644 --- a/core/src/main/java/org/matomo/java/tracking/ServletMatomoRequestCustomizer.java +++ b/core/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java @@ -1,11 +1,17 @@ package org.matomo.java.tracking; import jakarta.servlet.http.HttpServletRequest; +import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; import java.util.Map; +import java.util.Set; import lombok.NonNull; +import static java.util.Arrays.asList; + /** * Adds the headers from a {@link HttpServletRequest} to a {@link MatomoRequest.MatomoRequestBuilder}. @@ -14,9 +20,16 @@ * #addServletRequestHeaders(MatomoRequest.MatomoRequestBuilder, HttpServletRequest) to add the headers to an existing * builder. */ -public final class ServletMatomoRequestCustomizer { +public final class ServletMatomoRequest { + + /** + * Please ensure these values are always lower case. + */ + private static final Set RESTRICTED_HEADERS = Collections.unmodifiableSet(new HashSet<>( + asList("connection", "content-length", "expect", "host", "upgrade") + )); - private ServletMatomoRequestCustomizer() { + private ServletMatomoRequest() { // should not be instantiated } @@ -51,7 +64,9 @@ public static MatomoRequest.MatomoRequestBuilder addServletRequestHeaders( Enumeration headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement(); - headers.put(headerName, request.getHeader(headerName)); + if (headerName != null && !headerName.trim().isEmpty() && !RESTRICTED_HEADERS.contains(headerName.toLowerCase(Locale.ROOT))) { + headers.put(headerName, request.getHeader(headerName)); + } } return builder.headers(headers); } diff --git a/core/src/test/java/org/matomo/java/tracking/ServletMatomoRequestCustomizerTest.java b/core/src/test/java/org/matomo/java/tracking/ServletMatomoRequestTest.java similarity index 54% rename from core/src/test/java/org/matomo/java/tracking/ServletMatomoRequestCustomizerTest.java rename to core/src/test/java/org/matomo/java/tracking/ServletMatomoRequestTest.java index 3994a083..5d52df92 100644 --- a/core/src/test/java/org/matomo/java/tracking/ServletMatomoRequestCustomizerTest.java +++ b/core/src/test/java/org/matomo/java/tracking/ServletMatomoRequestTest.java @@ -5,8 +5,10 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; -class ServletMatomoRequestCustomizerTest { +class ServletMatomoRequestTest { @Test void addsServletRequestHeaders() { @@ -14,22 +16,34 @@ void addsServletRequestHeaders() { MockHttpServletRequest request = new MockHttpServletRequest(); request.setHeaders(singletonMap("headerName", "headerValue")); - MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequestCustomizer.fromServletRequest(request); + MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); MatomoRequest matomoRequest = builder.build(); assertThat(matomoRequest.getHeaders()).hasSize(1).containsEntry("headerName", "headerValue"); } + @ParameterizedTest + @ValueSource(strings = {"connection", "content-length", "expect", "host", "upgrade"}) + void doesNotAddRestrictedHeaders(String restrictedHeader) { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setHeaders(singletonMap(restrictedHeader, "headerValue")); + + MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); + + MatomoRequest matomoRequest = builder.build(); + assertThat(matomoRequest.getHeaders()).isEmpty(); + } + @Test void failsIfServletRequestIsNull() { - assertThatThrownBy(() -> ServletMatomoRequestCustomizer.fromServletRequest(null)) + assertThatThrownBy(() -> ServletMatomoRequest.fromServletRequest(null)) .isInstanceOf(NullPointerException.class) .hasMessage("request is marked non-null but is null"); } @Test void failsIfBuilderIsNull() { - assertThatThrownBy(() -> ServletMatomoRequestCustomizer.addServletRequestHeaders( + assertThatThrownBy(() -> ServletMatomoRequest.addServletRequestHeaders( null, new MockHttpServletRequest() )).isInstanceOf(NullPointerException.class).hasMessage("builder is marked non-null but is null"); From 778b405a4704a6563d8ab71a3ab81ea849beee2e Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Sat, 11 Nov 2023 18:16:16 +0100 Subject: [PATCH 181/467] Fix checkstyle --- .../java/org/matomo/java/tracking/ServletMatomoRequest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java b/core/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java index b4c8220e..e8c5f48c 100644 --- a/core/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java +++ b/core/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java @@ -64,7 +64,8 @@ public static MatomoRequest.MatomoRequestBuilder addServletRequestHeaders( Enumeration headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement(); - if (headerName != null && !headerName.trim().isEmpty() && !RESTRICTED_HEADERS.contains(headerName.toLowerCase(Locale.ROOT))) { + if (headerName != null && !headerName.trim().isEmpty() + && !RESTRICTED_HEADERS.contains(headerName.toLowerCase(Locale.ROOT))) { headers.put(headerName, request.getHeader(headerName)); } } From c4573ff6992328a02c18353f4662d3a27ee3d916 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Sat, 11 Nov 2023 18:55:32 +0100 Subject: [PATCH 182/467] Do not overwrite user agent header --- .../java/tracking/ServletMatomoRequest.java | 2 +- .../matomo/java/tracking/Java17Sender.java | 24 +++++++- .../org/matomo/java/tracking/Java8Sender.java | 59 +++++++++++++++---- 3 files changed, 71 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java b/core/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java index e8c5f48c..dd534e72 100644 --- a/core/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java +++ b/core/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java @@ -69,6 +69,6 @@ public static MatomoRequest.MatomoRequestBuilder addServletRequestHeaders( headers.put(headerName, request.getHeader(headerName)); } } - return builder.headers(headers); + return builder.headers(headers).visitorIp(request.getRemoteAddr()); } } \ No newline at end of file diff --git a/java17/src/main/java/org/matomo/java/tracking/Java17Sender.java b/java17/src/main/java/org/matomo/java/tracking/Java17Sender.java index 7a11f4d3..a4a34da3 100644 --- a/java17/src/main/java/org/matomo/java/tracking/Java17Sender.java +++ b/java17/src/main/java/org/matomo/java/tracking/Java17Sender.java @@ -12,6 +12,7 @@ import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; +import java.util.TreeMap; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import lombok.RequiredArgsConstructor; @@ -64,11 +65,15 @@ private HttpRequest buildHttpPostRequest( String authToken = AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); Collection queries = new ArrayList<>(); Map headers = new LinkedHashMap<>(10); + String headerUserAgent = null; for (MatomoRequest request : requests) { RequestValidator.validate(request, authToken); if (request.getHeaders() != null && !request.getHeaders().isEmpty()) { headers.putAll(request.getHeaders()); } + if (request.getHeaderUserAgent() != null && !request.getHeaderUserAgent().trim().isEmpty()) { + headerUserAgent = request.getHeaderUserAgent(); + } queries.add(queryCreator.createQuery(request, null)); } HttpRequest.Builder builder = HttpRequest @@ -83,6 +88,7 @@ private HttpRequest buildHttpPostRequest( .build() .toBytes())); applyTrackerConfiguration(builder); + setUserAgentHeader(builder, headerUserAgent, headers); addHeaders(builder, headers); return builder.build(); } @@ -107,6 +113,7 @@ private HttpRequest buildHttpGetRequest(@NonNull MatomoRequest request) { queryCreator.createQuery(request, authToken) ))); applyTrackerConfiguration(builder); + setUserAgentHeader(builder, request.getHeaderUserAgent(), request.getHeaders()); addHeaders(builder, request.getHeaders()); return builder.build(); } @@ -135,7 +142,22 @@ private void applyTrackerConfiguration(@NonNull HttpRequest.Builder builder) { if (trackerConfiguration.getSocketTimeout() != null && trackerConfiguration.getSocketTimeout().toMillis() > 0L) { builder.timeout(trackerConfiguration.getSocketTimeout()); } - if (trackerConfiguration.getUserAgent() != null && !trackerConfiguration.getUserAgent().isEmpty()) { + } + + private void setUserAgentHeader( + HttpRequest.Builder builder, + @Nullable String headerUserAgent, + @Nullable Map headers + ) { + String userAgentHeader = null; + if ((headerUserAgent == null || headerUserAgent.trim().isEmpty()) && headers != null) { + TreeMap caseInsensitiveMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + caseInsensitiveMap.putAll(headers); + userAgentHeader = caseInsensitiveMap.get("User-Agent"); + } + if ((userAgentHeader == null || userAgentHeader.trim().isEmpty()) + && (headerUserAgent == null || headerUserAgent.trim().isEmpty()) + && trackerConfiguration.getUserAgent() != null && !trackerConfiguration.getUserAgent().isEmpty()) { builder.header("User-Agent", trackerConfiguration.getUserAgent()); } } diff --git a/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java b/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java index ca9b8c93..62a051c7 100644 --- a/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java +++ b/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java @@ -27,10 +27,9 @@ import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; +import java.util.TreeMap; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; @@ -89,6 +88,7 @@ public void sendSingle( throw new InvalidUrlException(e); } applyTrackerConfiguration(connection); + setUserAgentProperty(connection, request.getHeaderUserAgent(), request.getHeaders()); addHeaders(connection, request.getHeaders()); log.debug("Sending single request using URI {} asynchronously", apiEndpoint); try { @@ -165,9 +165,6 @@ private HttpURLConnection openProxiedConnection( private void applyTrackerConfiguration(HttpURLConnection connection) { connection.setUseCaches(false); - if (trackerConfiguration.getUserAgent() != null && !trackerConfiguration.getUserAgent().isEmpty()) { - connection.setRequestProperty("User-Agent", trackerConfiguration.getUserAgent()); - } if (trackerConfiguration.getConnectTimeout() != null) { connection.setConnectTimeout((int) trackerConfiguration.getConnectTimeout().toMillis()); } @@ -176,6 +173,22 @@ private void applyTrackerConfiguration(HttpURLConnection connection) { } } + private void setUserAgentProperty( + @NonNull HttpURLConnection connection, @Nullable String headerUserAgent, @Nullable Map headers + ) { + String userAgentHeader = null; + if ((headerUserAgent == null || headerUserAgent.trim().isEmpty()) && headers != null) { + TreeMap caseInsensitiveMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + caseInsensitiveMap.putAll(headers); + userAgentHeader = caseInsensitiveMap.get("User-Agent"); + } + if ((userAgentHeader == null || userAgentHeader.trim().isEmpty()) + && (headerUserAgent == null || headerUserAgent.trim().isEmpty()) + && trackerConfiguration.getUserAgent() != null && !trackerConfiguration.getUserAgent().isEmpty()) { + connection.setRequestProperty("User-Agent", trackerConfiguration.getUserAgent()); + } + } + private void checkResponse(HttpURLConnection connection) throws IOException { int responseCode = connection.getResponseCode(); if (responseCode > 399) { @@ -197,18 +210,28 @@ public void sendBulk( @NonNull @lombok.NonNull Iterable requests, @Nullable String overrideAuthToken ) { String authToken = AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); + Collection queries = new ArrayList<>(); Map headers = new LinkedHashMap<>(); - sendBulk(StreamSupport.stream(requests.spliterator(), false).map(request -> { + String headerUserAgent = null; + for (MatomoRequest request : requests) { RequestValidator.validate(request, authToken); if (request.getHeaders() != null && !request.getHeaders().isEmpty()) { headers.putAll(request.getHeaders()); } - return queryCreator.createQuery(request, null); - }).collect(Collectors.toList()), authToken, headers); + if (headerUserAgent == null && request.getHeaderUserAgent() != null + && !request.getHeaderUserAgent().trim().isEmpty()) { + headerUserAgent = request.getHeaderUserAgent(); + } + queries.add(queryCreator.createQuery(request, null)); + } + sendBulk(queries, authToken, headers, headerUserAgent); } private void sendBulk( - @NonNull @lombok.NonNull Collection queries, @Nullable String authToken, Map headers + @NonNull @lombok.NonNull Collection queries, + @Nullable String authToken, + Map headers, + String headerUserAgent ) { if (queries.isEmpty()) { throw new IllegalArgumentException("Queries must not be empty"); @@ -221,6 +244,7 @@ private void sendBulk( } preparePostConnection(connection); applyTrackerConfiguration(connection); + setUserAgentProperty(connection, headerUserAgent, headers); addHeaders(connection, headers); log.debug("Sending bulk request using URI {} asynchronously", trackerConfiguration.getApiEndpoint()); OutputStream outputStream = null; @@ -263,6 +287,7 @@ public CompletableFuture sendBulkAsync( ) { String authToken = AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); final Map headers = new LinkedHashMap<>(); + String headerUserAgent = findHeaderUserAgent(requests); synchronized (queries) { for (MatomoRequest request : requests) { RequestValidator.validate(request, authToken); @@ -273,20 +298,30 @@ public CompletableFuture sendBulkAsync( queries.add(query); } } - return CompletableFuture.supplyAsync(() -> sendBulkAsync(authToken, headers), executor); + return CompletableFuture.supplyAsync(() -> sendBulkAsync(authToken, headers, headerUserAgent), executor); } @Nullable private Void sendBulkAsync( - @Nullable String authToken, Map headers + @Nullable String authToken, Map headers, String headerUserAgent ) { synchronized (queries) { if (!queries.isEmpty()) { - sendBulk(queries, authToken, headers); + sendBulk(queries, authToken, headers, headerUserAgent); queries.clear(); } return null; } } + @Nullable + private static String findHeaderUserAgent(@NonNull Iterable requests) { + for (MatomoRequest request : requests) { + if (request.getHeaderUserAgent() != null && !request.getHeaderUserAgent().trim().isEmpty()) { + return request.getHeaderUserAgent(); + } + } + return null; + } + } From c1392a5c09a184076f75aa7bb8528aa8f6d0e5d7 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Sat, 11 Nov 2023 19:03:40 +0100 Subject: [PATCH 183/467] Setup Java 17 in CodeQL --- .github/workflows/codeql-analysis.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 45a7fb5d..25627e1e 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -23,13 +23,14 @@ jobs: language: [ 'java' ] steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + - uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + - uses: github/codeql-action/autobuild@v2 + - uses: github/codeql-action/analyze@v2 From 4c0a9f97921f4cec0a7e5367e56c05b5de827c7e Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Sun, 12 Nov 2023 10:19:36 +0100 Subject: [PATCH 184/467] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 36b3199b..66f52a9d 100644 --- a/README.md +++ b/README.md @@ -637,6 +637,7 @@ provide tests for your changes. We use JUnit 5 for testing. Coverage should be a ## Other Java Matomo Tracker Implementations * [Matomo SDK for Android](https://github.com/matomo-org/matomo-sdk-android) +* [Piwik SDK Android]( https://github.com/lkogler/piwik-sdk-android) * [piwik-tracking](https://github.com/ralscha/piwik-tracking) * [Matomo Tracking API Java Client](https://github.com/dheid/matomo-tracker) -> Most of the code was integrated in the official Matomo Java Tracker From f49450878aebad531366e657c6be94b22d00b2c6 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 13 Nov 2023 00:29:47 +0100 Subject: [PATCH 185/467] Add servlet module --- .editorconfig | 887 +++++++++++++----- README.md | 324 +++---- core/pom.xml | 6 +- .../matomo/java/tracking/MatomoRequest.java | 23 + .../java/tracking/ServletMatomoRequest.java | 74 -- .../java/tracking/TrackerConfiguration.java | 4 +- .../tracking/parameters/CustomVariables.java | 47 + .../tracking/ServletMatomoRequestTest.java | 52 - .../parameters/CustomVariablesTest.java | 17 +- java17/pom.xml | 6 +- .../matomo/java/tracking/Java17Sender.java | 95 +- .../java/tracking/Java17SenderProvider.java | 5 +- .../matomo/java/tracking/Java17SenderIT.java | 24 +- java8/pom.xml | 11 +- .../org/matomo/java/tracking/Java8Sender.java | 169 ++-- .../tracking/TrustingHostnameVerifier.java | 0 .../TrustingHostnameVerifierTest.java | 0 pom.xml | 37 +- servlet/pom.xml | 70 ++ .../java/tracking/MatomoTrackerFilter.java | 30 + .../java/tracking/ServletMatomoRequest.java | 140 +++ .../java/tracking/MatomoTrackerFilterIT.java | 62 ++ .../java/tracking/MockHttpServletRequest.java | 6 +- .../tracking/ServletMatomoRequestTest.java | 134 +++ .../org/matomo/java/tracking/TestSender.java | 54 ++ .../java/tracking/TestSenderFactory.java | 16 + spring/pom.xml | 18 +- .../MatomoTrackerAutoConfiguration.java | 32 +- test/pom.xml | 15 +- .../java/tracking/test/BulkExample.java | 49 + .../tracking/test/MatomoServletTester.java | 42 + .../MatomoTrackerTester.java} | 29 +- .../java/tracking/test/SendExample.java | 41 + .../test/ServletMatomoRequestExample.java | 34 + .../main/resources/simplelogger.properties | 2 + test/src/main/resources/web/track.html | 11 + 36 files changed, 1861 insertions(+), 705 deletions(-) delete mode 100644 core/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java delete mode 100644 core/src/test/java/org/matomo/java/tracking/ServletMatomoRequestTest.java rename {core => java8}/src/main/java/org/matomo/java/tracking/TrustingHostnameVerifier.java (100%) rename {core => java8}/src/test/java/org/matomo/java/tracking/TrustingHostnameVerifierTest.java (100%) create mode 100644 servlet/pom.xml create mode 100644 servlet/src/main/java/org/matomo/java/tracking/MatomoTrackerFilter.java create mode 100644 servlet/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java create mode 100644 servlet/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java rename {core => servlet}/src/test/java/org/matomo/java/tracking/MockHttpServletRequest.java (96%) create mode 100644 servlet/src/test/java/org/matomo/java/tracking/ServletMatomoRequestTest.java create mode 100644 servlet/src/test/java/org/matomo/java/tracking/TestSender.java create mode 100644 servlet/src/test/java/org/matomo/java/tracking/TestSenderFactory.java create mode 100644 test/src/main/java/org/matomo/java/tracking/test/BulkExample.java create mode 100644 test/src/main/java/org/matomo/java/tracking/test/MatomoServletTester.java rename test/src/main/java/org/matomo/java/tracking/{MatomoJavaTrackerTest.java => test/MatomoTrackerTester.java} (87%) create mode 100644 test/src/main/java/org/matomo/java/tracking/test/SendExample.java create mode 100644 test/src/main/java/org/matomo/java/tracking/test/ServletMatomoRequestExample.java create mode 100644 test/src/main/resources/simplelogger.properties create mode 100644 test/src/main/resources/web/track.html diff --git a/.editorconfig b/.editorconfig index f472ee82..e391ae95 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,9 +14,31 @@ ij_smart_tabs = false ij_visual_guides = ij_wrap_on_typing = false +[*.css] +ij_css_align_closing_brace_with_properties = false +ij_css_blank_lines_around_nested_selector = 1 +ij_css_blank_lines_between_blocks = 1 +ij_css_block_comment_add_space = false +ij_css_brace_placement = end_of_line +ij_css_enforce_quotes_on_format = false +ij_css_hex_color_long_format = false +ij_css_hex_color_lower_case = false +ij_css_hex_color_short_format = false +ij_css_hex_color_upper_case = false +ij_css_keep_blank_lines_in_code = 2 +ij_css_keep_indents_on_empty_lines = false +ij_css_keep_single_line_blocks = false +ij_css_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_css_space_after_colon = true +ij_css_space_before_opening_brace = true +ij_css_use_double_quotes = true +ij_css_value_alignment = do_not_align + [*.java] indent_size = 2 +max_line_length = 100 ij_continuation_indent_size = 4 +ij_wrap_on_typing = true ij_java_align_consecutive_assignments = false ij_java_align_consecutive_variable_declarations = false ij_java_align_group_field_declarations = false @@ -82,7 +104,7 @@ ij_java_do_not_wrap_after_single_annotation = false ij_java_do_not_wrap_after_single_annotation_in_parameter = false ij_java_do_while_brace_force = always ij_java_doc_add_blank_line_after_description = true -ij_java_doc_add_blank_line_after_param_comments = false +ij_java_doc_add_blank_line_after_param_comments = true ij_java_doc_add_blank_line_after_return = false ij_java_doc_add_p_tag_on_empty_lines = true ij_java_doc_align_exception_comments = true @@ -95,17 +117,36 @@ ij_java_doc_keep_empty_lines = true ij_java_doc_keep_empty_parameter_tag = true ij_java_doc_keep_empty_return_tag = true ij_java_doc_keep_empty_throws_tag = true -ij_java_doc_keep_invalid_tags = true +ij_java_doc_keep_invalid_tags = false ij_java_doc_param_description_on_new_line = false ij_java_doc_preserve_line_breaks = false ij_java_doc_use_throws_not_exception_tag = true ij_java_else_on_new_line = false +ij_java_entity_dd_prefix = +ij_java_entity_dd_suffix = EJB +ij_java_entity_eb_prefix = +ij_java_entity_eb_suffix = Bean +ij_java_entity_hi_prefix = +ij_java_entity_hi_suffix = Home +ij_java_entity_lhi_prefix = Local +ij_java_entity_lhi_suffix = Home +ij_java_entity_li_prefix = Local +ij_java_entity_li_suffix = +ij_java_entity_pk_class = java.lang.String +ij_java_entity_ri_prefix = +ij_java_entity_ri_suffix = +ij_java_entity_vo_prefix = +ij_java_entity_vo_suffix = VO ij_java_enum_constants_wrap = split_into_lines ij_java_extends_keyword_wrap = normal ij_java_extends_list_wrap = on_every_item ij_java_field_annotation_wrap = split_into_lines ij_java_field_name_prefix = ij_java_field_name_suffix = +ij_java_filter_class_prefix = +ij_java_filter_class_suffix = +ij_java_filter_dd_prefix = +ij_java_filter_dd_suffix = ij_java_finally_on_new_line = false ij_java_for_brace_force = always ij_java_for_statement_new_line_after_left_paren = false @@ -122,11 +163,11 @@ ij_java_keep_blank_lines_before_right_brace = 2 ij_java_keep_blank_lines_between_package_declaration_and_header = 2 ij_java_keep_blank_lines_in_code = 2 ij_java_keep_blank_lines_in_declarations = 2 -ij_java_keep_builder_methods_indents = false -ij_java_keep_control_statement_in_one_line = true +ij_java_keep_builder_methods_indents = true +ij_java_keep_control_statement_in_one_line = false ij_java_keep_first_column_comment = false ij_java_keep_indents_on_empty_lines = false -ij_java_keep_line_breaks = false +ij_java_keep_line_breaks = true ij_java_keep_multiple_expressions_in_one_line = false ij_java_keep_simple_blocks_in_one_line = false ij_java_keep_simple_classes_in_one_line = false @@ -139,8 +180,14 @@ ij_java_layout_static_imports_separately = true ij_java_line_comment_add_space = false ij_java_line_comment_add_space_on_reformat = false ij_java_line_comment_at_first_column = true +ij_java_listener_class_prefix = +ij_java_listener_class_suffix = ij_java_local_variable_name_prefix = ij_java_local_variable_name_suffix = +ij_java_message_dd_prefix = +ij_java_message_dd_suffix = EJB +ij_java_message_eb_prefix = +ij_java_message_eb_suffix = Bean ij_java_method_annotation_wrap = split_into_lines ij_java_method_brace_style = end_of_line ij_java_method_call_chain_wrap = on_every_item @@ -174,6 +221,24 @@ ij_java_resource_list_wrap = on_every_item ij_java_rparen_on_new_line_in_annotation = true ij_java_rparen_on_new_line_in_deconstruction_pattern = true ij_java_rparen_on_new_line_in_record_header = true +ij_java_servlet_class_prefix = +ij_java_servlet_class_suffix = +ij_java_servlet_dd_prefix = +ij_java_servlet_dd_suffix = +ij_java_session_dd_prefix = +ij_java_session_dd_suffix = EJB +ij_java_session_eb_prefix = +ij_java_session_eb_suffix = Bean +ij_java_session_hi_prefix = +ij_java_session_hi_suffix = Home +ij_java_session_lhi_prefix = Local +ij_java_session_lhi_suffix = Home +ij_java_session_li_prefix = Local +ij_java_session_li_suffix = +ij_java_session_ri_prefix = +ij_java_session_ri_suffix = +ij_java_session_si_prefix = +ij_java_session_si_suffix = Service ij_java_space_after_closing_angle_bracket_in_type_argument = false ij_java_space_after_colon = true ij_java_space_after_comma = true @@ -271,29 +336,10 @@ ij_java_variable_annotation_wrap = split_into_lines ij_java_visibility = public ij_java_while_brace_force = always ij_java_while_on_new_line = false -ij_java_wrap_comments = false -ij_java_wrap_first_method_in_call_chain = true +ij_java_wrap_comments = true +ij_java_wrap_first_method_in_call_chain = false ij_java_wrap_long_lines = false -[*.properties] -ij_properties_align_group_field_declarations = false -ij_properties_keep_blank_lines = false -ij_properties_key_value_delimiter = equals -ij_properties_spaces_around_key_value_delimiter = false - -[*.proto] -indent_size = 2 -tab_width = 2 -ij_continuation_indent_size = 4 -ij_protobuf_keep_blank_lines_in_code = 2 -ij_protobuf_keep_indents_on_empty_lines = false -ij_protobuf_keep_line_breaks = true -ij_protobuf_space_after_comma = true -ij_protobuf_space_before_comma = false -ij_protobuf_spaces_around_assignment_operators = true -ij_protobuf_spaces_within_braces = false -ij_protobuf_spaces_within_brackets = false - [.editorconfig] ij_editorconfig_align_group_field_declarations = false ij_editorconfig_space_after_colon = false @@ -302,7 +348,7 @@ ij_editorconfig_space_before_colon = false ij_editorconfig_space_before_comma = false ij_editorconfig_spaces_around_assignment_operators = true -[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.jspx,*.pom,*.rng,*.tagx,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}] +[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.pom,*.rng,*.tld,*.wadl,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul,phpunit.xml.dist}] ij_xml_align_attributes = true ij_xml_align_text = false ij_xml_attribute_wrap = normal @@ -320,7 +366,179 @@ ij_xml_space_after_tag_name = false ij_xml_space_around_equals_in_attribute = false ij_xml_space_inside_empty_tag = false ij_xml_text_wrap = normal -ij_xml_use_custom_settings = false + +[{*.ats,*.cts,*.mts,*.ts}] +ij_continuation_indent_size = 4 +ij_typescript_align_imports = false +ij_typescript_align_multiline_array_initializer_expression = false +ij_typescript_align_multiline_binary_operation = false +ij_typescript_align_multiline_chained_methods = false +ij_typescript_align_multiline_extends_list = false +ij_typescript_align_multiline_for = true +ij_typescript_align_multiline_parameters = true +ij_typescript_align_multiline_parameters_in_calls = false +ij_typescript_align_multiline_ternary_operation = false +ij_typescript_align_object_properties = 0 +ij_typescript_align_union_types = false +ij_typescript_align_var_statements = 0 +ij_typescript_array_initializer_new_line_after_left_brace = false +ij_typescript_array_initializer_right_brace_on_new_line = false +ij_typescript_array_initializer_wrap = off +ij_typescript_assignment_wrap = off +ij_typescript_binary_operation_sign_on_next_line = false +ij_typescript_binary_operation_wrap = off +ij_typescript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/** +ij_typescript_blank_lines_after_imports = 1 +ij_typescript_blank_lines_around_class = 1 +ij_typescript_blank_lines_around_field = 0 +ij_typescript_blank_lines_around_field_in_interface = 0 +ij_typescript_blank_lines_around_function = 1 +ij_typescript_blank_lines_around_method = 1 +ij_typescript_blank_lines_around_method_in_interface = 1 +ij_typescript_block_brace_style = end_of_line +ij_typescript_block_comment_add_space = false +ij_typescript_block_comment_at_first_column = true +ij_typescript_call_parameters_new_line_after_left_paren = false +ij_typescript_call_parameters_right_paren_on_new_line = false +ij_typescript_call_parameters_wrap = off +ij_typescript_catch_on_new_line = false +ij_typescript_chained_call_dot_on_new_line = true +ij_typescript_class_brace_style = end_of_line +ij_typescript_comma_on_new_line = false +ij_typescript_do_while_brace_force = never +ij_typescript_else_on_new_line = false +ij_typescript_enforce_trailing_comma = keep +ij_typescript_enum_constants_wrap = on_every_item +ij_typescript_extends_keyword_wrap = off +ij_typescript_extends_list_wrap = off +ij_typescript_field_prefix = _ +ij_typescript_file_name_style = relaxed +ij_typescript_finally_on_new_line = false +ij_typescript_for_brace_force = never +ij_typescript_for_statement_new_line_after_left_paren = false +ij_typescript_for_statement_right_paren_on_new_line = false +ij_typescript_for_statement_wrap = off +ij_typescript_force_quote_style = false +ij_typescript_force_semicolon_style = false +ij_typescript_function_expression_brace_style = end_of_line +ij_typescript_if_brace_force = never +ij_typescript_import_merge_members = global +ij_typescript_import_prefer_absolute_path = global +ij_typescript_import_sort_members = true +ij_typescript_import_sort_module_name = false +ij_typescript_import_use_node_resolution = true +ij_typescript_imports_wrap = on_every_item +ij_typescript_indent_case_from_switch = true +ij_typescript_indent_chained_calls = true +ij_typescript_indent_package_children = 0 +ij_typescript_jsdoc_include_types = false +ij_typescript_jsx_attribute_value = braces +ij_typescript_keep_blank_lines_in_code = 2 +ij_typescript_keep_first_column_comment = true +ij_typescript_keep_indents_on_empty_lines = false +ij_typescript_keep_line_breaks = true +ij_typescript_keep_simple_blocks_in_one_line = false +ij_typescript_keep_simple_methods_in_one_line = false +ij_typescript_line_comment_add_space = true +ij_typescript_line_comment_at_first_column = false +ij_typescript_method_brace_style = end_of_line +ij_typescript_method_call_chain_wrap = off +ij_typescript_method_parameters_new_line_after_left_paren = false +ij_typescript_method_parameters_right_paren_on_new_line = false +ij_typescript_method_parameters_wrap = off +ij_typescript_object_literal_wrap = on_every_item +ij_typescript_object_types_wrap = on_every_item +ij_typescript_parentheses_expression_new_line_after_left_paren = false +ij_typescript_parentheses_expression_right_paren_on_new_line = false +ij_typescript_place_assignment_sign_on_next_line = false +ij_typescript_prefer_as_type_cast = false +ij_typescript_prefer_explicit_types_function_expression_returns = false +ij_typescript_prefer_explicit_types_function_returns = false +ij_typescript_prefer_explicit_types_vars_fields = false +ij_typescript_prefer_parameters_wrap = false +ij_typescript_property_prefix = +ij_typescript_reformat_c_style_comments = false +ij_typescript_space_after_colon = true +ij_typescript_space_after_comma = true +ij_typescript_space_after_dots_in_rest_parameter = false +ij_typescript_space_after_generator_mult = true +ij_typescript_space_after_property_colon = true +ij_typescript_space_after_quest = true +ij_typescript_space_after_type_colon = true +ij_typescript_space_after_unary_not = false +ij_typescript_space_before_async_arrow_lparen = true +ij_typescript_space_before_catch_keyword = true +ij_typescript_space_before_catch_left_brace = true +ij_typescript_space_before_catch_parentheses = true +ij_typescript_space_before_class_lbrace = true +ij_typescript_space_before_class_left_brace = true +ij_typescript_space_before_colon = true +ij_typescript_space_before_comma = false +ij_typescript_space_before_do_left_brace = true +ij_typescript_space_before_else_keyword = true +ij_typescript_space_before_else_left_brace = true +ij_typescript_space_before_finally_keyword = true +ij_typescript_space_before_finally_left_brace = true +ij_typescript_space_before_for_left_brace = true +ij_typescript_space_before_for_parentheses = true +ij_typescript_space_before_for_semicolon = false +ij_typescript_space_before_function_left_parenth = true +ij_typescript_space_before_generator_mult = false +ij_typescript_space_before_if_left_brace = true +ij_typescript_space_before_if_parentheses = true +ij_typescript_space_before_method_call_parentheses = false +ij_typescript_space_before_method_left_brace = true +ij_typescript_space_before_method_parentheses = false +ij_typescript_space_before_property_colon = false +ij_typescript_space_before_quest = true +ij_typescript_space_before_switch_left_brace = true +ij_typescript_space_before_switch_parentheses = true +ij_typescript_space_before_try_left_brace = true +ij_typescript_space_before_type_colon = false +ij_typescript_space_before_unary_not = false +ij_typescript_space_before_while_keyword = true +ij_typescript_space_before_while_left_brace = true +ij_typescript_space_before_while_parentheses = true +ij_typescript_spaces_around_additive_operators = true +ij_typescript_spaces_around_arrow_function_operator = true +ij_typescript_spaces_around_assignment_operators = true +ij_typescript_spaces_around_bitwise_operators = true +ij_typescript_spaces_around_equality_operators = true +ij_typescript_spaces_around_logical_operators = true +ij_typescript_spaces_around_multiplicative_operators = true +ij_typescript_spaces_around_relational_operators = true +ij_typescript_spaces_around_shift_operators = true +ij_typescript_spaces_around_unary_operator = false +ij_typescript_spaces_within_array_initializer_brackets = false +ij_typescript_spaces_within_brackets = false +ij_typescript_spaces_within_catch_parentheses = false +ij_typescript_spaces_within_for_parentheses = false +ij_typescript_spaces_within_if_parentheses = false +ij_typescript_spaces_within_imports = false +ij_typescript_spaces_within_interpolation_expressions = false +ij_typescript_spaces_within_method_call_parentheses = false +ij_typescript_spaces_within_method_parentheses = false +ij_typescript_spaces_within_object_literal_braces = false +ij_typescript_spaces_within_object_type_braces = true +ij_typescript_spaces_within_parentheses = false +ij_typescript_spaces_within_switch_parentheses = false +ij_typescript_spaces_within_type_assertion = false +ij_typescript_spaces_within_union_types = true +ij_typescript_spaces_within_while_parentheses = false +ij_typescript_special_else_if_treatment = true +ij_typescript_ternary_operation_signs_on_next_line = false +ij_typescript_ternary_operation_wrap = off +ij_typescript_union_types_wrap = on_every_item +ij_typescript_use_chained_calls_group_indents = false +ij_typescript_use_double_quotes = true +ij_typescript_use_explicit_js_extension = auto +ij_typescript_use_path_mapping = always +ij_typescript_use_public_modifier = false +ij_typescript_use_semicolon_after_statement = true +ij_typescript_var_declaration_wrap = normal +ij_typescript_while_brace_force = never +ij_typescript_while_on_new_line = false +ij_typescript_wrap_comments = false [{*.bash,*.sh,*.zsh}] indent_size = 2 @@ -332,198 +550,403 @@ ij_shell_redirect_followed_by_space = false ij_shell_switch_cases_indented = false ij_shell_use_unix_line_separator = true -[{*.gant,*.groovy,*.gy}] -ij_groovy_align_group_field_declarations = false -ij_groovy_align_multiline_array_initializer_expression = false -ij_groovy_align_multiline_assignment = false -ij_groovy_align_multiline_binary_operation = false -ij_groovy_align_multiline_chained_methods = false -ij_groovy_align_multiline_extends_list = false -ij_groovy_align_multiline_for = true -ij_groovy_align_multiline_list_or_map = true -ij_groovy_align_multiline_method_parentheses = false -ij_groovy_align_multiline_parameters = true -ij_groovy_align_multiline_parameters_in_calls = false -ij_groovy_align_multiline_resources = true -ij_groovy_align_multiline_ternary_operation = false -ij_groovy_align_multiline_throws_list = false -ij_groovy_align_named_args_in_map = true -ij_groovy_align_throws_keyword = false -ij_groovy_array_initializer_new_line_after_left_brace = false -ij_groovy_array_initializer_right_brace_on_new_line = false -ij_groovy_array_initializer_wrap = off -ij_groovy_assert_statement_wrap = off -ij_groovy_assignment_wrap = off -ij_groovy_binary_operation_wrap = off -ij_groovy_blank_lines_after_class_header = 0 -ij_groovy_blank_lines_after_imports = 1 -ij_groovy_blank_lines_after_package = 1 -ij_groovy_blank_lines_around_class = 1 -ij_groovy_blank_lines_around_field = 0 -ij_groovy_blank_lines_around_field_in_interface = 0 -ij_groovy_blank_lines_around_method = 1 -ij_groovy_blank_lines_around_method_in_interface = 1 -ij_groovy_blank_lines_before_imports = 1 -ij_groovy_blank_lines_before_method_body = 0 -ij_groovy_blank_lines_before_package = 0 -ij_groovy_block_brace_style = end_of_line -ij_groovy_block_comment_add_space = false -ij_groovy_block_comment_at_first_column = true -ij_groovy_call_parameters_new_line_after_left_paren = false -ij_groovy_call_parameters_right_paren_on_new_line = false -ij_groovy_call_parameters_wrap = off -ij_groovy_catch_on_new_line = false -ij_groovy_class_annotation_wrap = split_into_lines -ij_groovy_class_brace_style = end_of_line -ij_groovy_class_count_to_use_import_on_demand = 5 -ij_groovy_do_while_brace_force = never -ij_groovy_else_on_new_line = false -ij_groovy_enable_groovydoc_formatting = true -ij_groovy_enum_constants_wrap = off -ij_groovy_extends_keyword_wrap = off -ij_groovy_extends_list_wrap = off -ij_groovy_field_annotation_wrap = split_into_lines -ij_groovy_finally_on_new_line = false -ij_groovy_for_brace_force = never -ij_groovy_for_statement_new_line_after_left_paren = false -ij_groovy_for_statement_right_paren_on_new_line = false -ij_groovy_for_statement_wrap = off -ij_groovy_ginq_general_clause_wrap_policy = 2 -ij_groovy_ginq_having_wrap_policy = 1 -ij_groovy_ginq_indent_having_clause = true -ij_groovy_ginq_indent_on_clause = true -ij_groovy_ginq_on_wrap_policy = 1 -ij_groovy_ginq_space_after_keyword = true -ij_groovy_if_brace_force = never -ij_groovy_import_annotation_wrap = 2 -ij_groovy_imports_layout = *,|,javax.**,java.**,|,$* -ij_groovy_indent_case_from_switch = true -ij_groovy_indent_label_blocks = true -ij_groovy_insert_inner_class_imports = false -ij_groovy_keep_blank_lines_before_right_brace = 2 -ij_groovy_keep_blank_lines_in_code = 2 -ij_groovy_keep_blank_lines_in_declarations = 2 -ij_groovy_keep_control_statement_in_one_line = true -ij_groovy_keep_first_column_comment = true -ij_groovy_keep_indents_on_empty_lines = false -ij_groovy_keep_line_breaks = true -ij_groovy_keep_multiple_expressions_in_one_line = false -ij_groovy_keep_simple_blocks_in_one_line = false -ij_groovy_keep_simple_classes_in_one_line = true -ij_groovy_keep_simple_lambdas_in_one_line = true -ij_groovy_keep_simple_methods_in_one_line = true -ij_groovy_label_indent_absolute = false -ij_groovy_label_indent_size = 0 -ij_groovy_lambda_brace_style = end_of_line -ij_groovy_layout_static_imports_separately = true -ij_groovy_line_comment_add_space = false -ij_groovy_line_comment_add_space_on_reformat = false -ij_groovy_line_comment_at_first_column = true -ij_groovy_method_annotation_wrap = split_into_lines -ij_groovy_method_brace_style = end_of_line -ij_groovy_method_call_chain_wrap = off -ij_groovy_method_parameters_new_line_after_left_paren = false -ij_groovy_method_parameters_right_paren_on_new_line = false -ij_groovy_method_parameters_wrap = off -ij_groovy_modifier_list_wrap = false -ij_groovy_names_count_to_use_import_on_demand = 3 -ij_groovy_packages_to_use_import_on_demand = java.awt.*,javax.swing.* -ij_groovy_parameter_annotation_wrap = off -ij_groovy_parentheses_expression_new_line_after_left_paren = false -ij_groovy_parentheses_expression_right_paren_on_new_line = false -ij_groovy_prefer_parameters_wrap = false -ij_groovy_resource_list_new_line_after_left_paren = false -ij_groovy_resource_list_right_paren_on_new_line = false -ij_groovy_resource_list_wrap = off -ij_groovy_space_after_assert_separator = true -ij_groovy_space_after_colon = true -ij_groovy_space_after_comma = true -ij_groovy_space_after_comma_in_type_arguments = true -ij_groovy_space_after_for_semicolon = true -ij_groovy_space_after_quest = true -ij_groovy_space_after_type_cast = true -ij_groovy_space_before_annotation_parameter_list = false -ij_groovy_space_before_array_initializer_left_brace = false -ij_groovy_space_before_assert_separator = false -ij_groovy_space_before_catch_keyword = true -ij_groovy_space_before_catch_left_brace = true -ij_groovy_space_before_catch_parentheses = true -ij_groovy_space_before_class_left_brace = true -ij_groovy_space_before_closure_left_brace = true -ij_groovy_space_before_colon = true -ij_groovy_space_before_comma = false -ij_groovy_space_before_do_left_brace = true -ij_groovy_space_before_else_keyword = true -ij_groovy_space_before_else_left_brace = true -ij_groovy_space_before_finally_keyword = true -ij_groovy_space_before_finally_left_brace = true -ij_groovy_space_before_for_left_brace = true -ij_groovy_space_before_for_parentheses = true -ij_groovy_space_before_for_semicolon = false -ij_groovy_space_before_if_left_brace = true -ij_groovy_space_before_if_parentheses = true -ij_groovy_space_before_method_call_parentheses = false -ij_groovy_space_before_method_left_brace = true -ij_groovy_space_before_method_parentheses = false -ij_groovy_space_before_quest = true -ij_groovy_space_before_record_parentheses = false -ij_groovy_space_before_switch_left_brace = true -ij_groovy_space_before_switch_parentheses = true -ij_groovy_space_before_synchronized_left_brace = true -ij_groovy_space_before_synchronized_parentheses = true -ij_groovy_space_before_try_left_brace = true -ij_groovy_space_before_try_parentheses = true -ij_groovy_space_before_while_keyword = true -ij_groovy_space_before_while_left_brace = true -ij_groovy_space_before_while_parentheses = true -ij_groovy_space_in_named_argument = true -ij_groovy_space_in_named_argument_before_colon = false -ij_groovy_space_within_empty_array_initializer_braces = false -ij_groovy_space_within_empty_method_call_parentheses = false -ij_groovy_spaces_around_additive_operators = true -ij_groovy_spaces_around_assignment_operators = true -ij_groovy_spaces_around_bitwise_operators = true -ij_groovy_spaces_around_equality_operators = true -ij_groovy_spaces_around_lambda_arrow = true -ij_groovy_spaces_around_logical_operators = true -ij_groovy_spaces_around_multiplicative_operators = true -ij_groovy_spaces_around_regex_operators = true -ij_groovy_spaces_around_relational_operators = true -ij_groovy_spaces_around_shift_operators = true -ij_groovy_spaces_within_annotation_parentheses = false -ij_groovy_spaces_within_array_initializer_braces = false -ij_groovy_spaces_within_braces = true -ij_groovy_spaces_within_brackets = false -ij_groovy_spaces_within_cast_parentheses = false -ij_groovy_spaces_within_catch_parentheses = false -ij_groovy_spaces_within_for_parentheses = false -ij_groovy_spaces_within_gstring_injection_braces = false -ij_groovy_spaces_within_if_parentheses = false -ij_groovy_spaces_within_list_or_map = false -ij_groovy_spaces_within_method_call_parentheses = false -ij_groovy_spaces_within_method_parentheses = false -ij_groovy_spaces_within_parentheses = false -ij_groovy_spaces_within_switch_parentheses = false -ij_groovy_spaces_within_synchronized_parentheses = false -ij_groovy_spaces_within_try_parentheses = false -ij_groovy_spaces_within_tuple_expression = false -ij_groovy_spaces_within_while_parentheses = false -ij_groovy_special_else_if_treatment = true -ij_groovy_ternary_operation_wrap = off -ij_groovy_throws_keyword_wrap = off -ij_groovy_throws_list_wrap = off -ij_groovy_use_flying_geese_braces = false -ij_groovy_use_fq_class_names = false -ij_groovy_use_fq_class_names_in_javadoc = true -ij_groovy_use_relative_indents = false -ij_groovy_use_single_class_imports = true -ij_groovy_variable_annotation_wrap = off -ij_groovy_while_brace_force = never -ij_groovy_while_on_new_line = false -ij_groovy_wrap_chain_calls_after_dot = false -ij_groovy_wrap_long_lines = false +[{*.cjs,*.js}] +ij_continuation_indent_size = 4 +ij_javascript_align_imports = false +ij_javascript_align_multiline_array_initializer_expression = false +ij_javascript_align_multiline_binary_operation = false +ij_javascript_align_multiline_chained_methods = false +ij_javascript_align_multiline_extends_list = false +ij_javascript_align_multiline_for = true +ij_javascript_align_multiline_parameters = true +ij_javascript_align_multiline_parameters_in_calls = false +ij_javascript_align_multiline_ternary_operation = false +ij_javascript_align_object_properties = 0 +ij_javascript_align_union_types = false +ij_javascript_align_var_statements = 0 +ij_javascript_array_initializer_new_line_after_left_brace = false +ij_javascript_array_initializer_right_brace_on_new_line = false +ij_javascript_array_initializer_wrap = off +ij_javascript_assignment_wrap = off +ij_javascript_binary_operation_sign_on_next_line = false +ij_javascript_binary_operation_wrap = off +ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/** +ij_javascript_blank_lines_after_imports = 1 +ij_javascript_blank_lines_around_class = 1 +ij_javascript_blank_lines_around_field = 0 +ij_javascript_blank_lines_around_function = 1 +ij_javascript_blank_lines_around_method = 1 +ij_javascript_block_brace_style = end_of_line +ij_javascript_block_comment_add_space = false +ij_javascript_block_comment_at_first_column = true +ij_javascript_call_parameters_new_line_after_left_paren = false +ij_javascript_call_parameters_right_paren_on_new_line = false +ij_javascript_call_parameters_wrap = off +ij_javascript_catch_on_new_line = false +ij_javascript_chained_call_dot_on_new_line = true +ij_javascript_class_brace_style = end_of_line +ij_javascript_comma_on_new_line = false +ij_javascript_do_while_brace_force = never +ij_javascript_else_on_new_line = false +ij_javascript_enforce_trailing_comma = keep +ij_javascript_extends_keyword_wrap = off +ij_javascript_extends_list_wrap = off +ij_javascript_field_prefix = _ +ij_javascript_file_name_style = relaxed +ij_javascript_finally_on_new_line = false +ij_javascript_for_brace_force = never +ij_javascript_for_statement_new_line_after_left_paren = false +ij_javascript_for_statement_right_paren_on_new_line = false +ij_javascript_for_statement_wrap = off +ij_javascript_force_quote_style = false +ij_javascript_force_semicolon_style = false +ij_javascript_function_expression_brace_style = end_of_line +ij_javascript_if_brace_force = never +ij_javascript_import_merge_members = global +ij_javascript_import_prefer_absolute_path = global +ij_javascript_import_sort_members = true +ij_javascript_import_sort_module_name = false +ij_javascript_import_use_node_resolution = true +ij_javascript_imports_wrap = on_every_item +ij_javascript_indent_case_from_switch = true +ij_javascript_indent_chained_calls = true +ij_javascript_indent_package_children = 0 +ij_javascript_jsx_attribute_value = braces +ij_javascript_keep_blank_lines_in_code = 2 +ij_javascript_keep_first_column_comment = true +ij_javascript_keep_indents_on_empty_lines = false +ij_javascript_keep_line_breaks = true +ij_javascript_keep_simple_blocks_in_one_line = false +ij_javascript_keep_simple_methods_in_one_line = false +ij_javascript_line_comment_add_space = true +ij_javascript_line_comment_at_first_column = false +ij_javascript_method_brace_style = end_of_line +ij_javascript_method_call_chain_wrap = off +ij_javascript_method_parameters_new_line_after_left_paren = false +ij_javascript_method_parameters_right_paren_on_new_line = false +ij_javascript_method_parameters_wrap = off +ij_javascript_object_literal_wrap = on_every_item +ij_javascript_object_types_wrap = on_every_item +ij_javascript_parentheses_expression_new_line_after_left_paren = false +ij_javascript_parentheses_expression_right_paren_on_new_line = false +ij_javascript_place_assignment_sign_on_next_line = false +ij_javascript_prefer_as_type_cast = false +ij_javascript_prefer_explicit_types_function_expression_returns = false +ij_javascript_prefer_explicit_types_function_returns = false +ij_javascript_prefer_explicit_types_vars_fields = false +ij_javascript_prefer_parameters_wrap = false +ij_javascript_property_prefix = +ij_javascript_reformat_c_style_comments = false +ij_javascript_space_after_colon = true +ij_javascript_space_after_comma = true +ij_javascript_space_after_dots_in_rest_parameter = false +ij_javascript_space_after_generator_mult = true +ij_javascript_space_after_property_colon = true +ij_javascript_space_after_quest = true +ij_javascript_space_after_type_colon = true +ij_javascript_space_after_unary_not = false +ij_javascript_space_before_async_arrow_lparen = true +ij_javascript_space_before_catch_keyword = true +ij_javascript_space_before_catch_left_brace = true +ij_javascript_space_before_catch_parentheses = true +ij_javascript_space_before_class_lbrace = true +ij_javascript_space_before_class_left_brace = true +ij_javascript_space_before_colon = true +ij_javascript_space_before_comma = false +ij_javascript_space_before_do_left_brace = true +ij_javascript_space_before_else_keyword = true +ij_javascript_space_before_else_left_brace = true +ij_javascript_space_before_finally_keyword = true +ij_javascript_space_before_finally_left_brace = true +ij_javascript_space_before_for_left_brace = true +ij_javascript_space_before_for_parentheses = true +ij_javascript_space_before_for_semicolon = false +ij_javascript_space_before_function_left_parenth = true +ij_javascript_space_before_generator_mult = false +ij_javascript_space_before_if_left_brace = true +ij_javascript_space_before_if_parentheses = true +ij_javascript_space_before_method_call_parentheses = false +ij_javascript_space_before_method_left_brace = true +ij_javascript_space_before_method_parentheses = false +ij_javascript_space_before_property_colon = false +ij_javascript_space_before_quest = true +ij_javascript_space_before_switch_left_brace = true +ij_javascript_space_before_switch_parentheses = true +ij_javascript_space_before_try_left_brace = true +ij_javascript_space_before_type_colon = false +ij_javascript_space_before_unary_not = false +ij_javascript_space_before_while_keyword = true +ij_javascript_space_before_while_left_brace = true +ij_javascript_space_before_while_parentheses = true +ij_javascript_spaces_around_additive_operators = true +ij_javascript_spaces_around_arrow_function_operator = true +ij_javascript_spaces_around_assignment_operators = true +ij_javascript_spaces_around_bitwise_operators = true +ij_javascript_spaces_around_equality_operators = true +ij_javascript_spaces_around_logical_operators = true +ij_javascript_spaces_around_multiplicative_operators = true +ij_javascript_spaces_around_relational_operators = true +ij_javascript_spaces_around_shift_operators = true +ij_javascript_spaces_around_unary_operator = false +ij_javascript_spaces_within_array_initializer_brackets = false +ij_javascript_spaces_within_brackets = false +ij_javascript_spaces_within_catch_parentheses = false +ij_javascript_spaces_within_for_parentheses = false +ij_javascript_spaces_within_if_parentheses = false +ij_javascript_spaces_within_imports = false +ij_javascript_spaces_within_interpolation_expressions = false +ij_javascript_spaces_within_method_call_parentheses = false +ij_javascript_spaces_within_method_parentheses = false +ij_javascript_spaces_within_object_literal_braces = false +ij_javascript_spaces_within_object_type_braces = true +ij_javascript_spaces_within_parentheses = false +ij_javascript_spaces_within_switch_parentheses = false +ij_javascript_spaces_within_type_assertion = false +ij_javascript_spaces_within_union_types = true +ij_javascript_spaces_within_while_parentheses = false +ij_javascript_special_else_if_treatment = true +ij_javascript_ternary_operation_signs_on_next_line = false +ij_javascript_ternary_operation_wrap = off +ij_javascript_union_types_wrap = on_every_item +ij_javascript_use_chained_calls_group_indents = false +ij_javascript_use_double_quotes = true +ij_javascript_use_explicit_js_extension = auto +ij_javascript_use_path_mapping = always +ij_javascript_use_public_modifier = false +ij_javascript_use_semicolon_after_statement = true +ij_javascript_var_declaration_wrap = normal +ij_javascript_while_brace_force = never +ij_javascript_while_on_new_line = false +ij_javascript_wrap_comments = false + +[{*.ctp,*.hphp,*.inc,*.module,*.php,*.php4,*.php5,*.phtml}] +ij_continuation_indent_size = 4 +ij_php_align_assignments = false +ij_php_align_class_constants = false +ij_php_align_enum_cases = false +ij_php_align_group_field_declarations = false +ij_php_align_inline_comments = false +ij_php_align_key_value_pairs = false +ij_php_align_match_arm_bodies = false +ij_php_align_multiline_array_initializer_expression = false +ij_php_align_multiline_binary_operation = false +ij_php_align_multiline_chained_methods = false +ij_php_align_multiline_extends_list = false +ij_php_align_multiline_for = true +ij_php_align_multiline_parameters = true +ij_php_align_multiline_parameters_in_calls = false +ij_php_align_multiline_ternary_operation = false +ij_php_align_named_arguments = false +ij_php_align_phpdoc_comments = false +ij_php_align_phpdoc_param_names = false +ij_php_anonymous_brace_style = end_of_line +ij_php_api_weight = 28 +ij_php_array_initializer_new_line_after_left_brace = false +ij_php_array_initializer_right_brace_on_new_line = false +ij_php_array_initializer_wrap = off +ij_php_assignment_wrap = off +ij_php_attributes_wrap = off +ij_php_author_weight = 28 +ij_php_binary_operation_sign_on_next_line = false +ij_php_binary_operation_wrap = off +ij_php_blank_lines_after_class_header = 0 +ij_php_blank_lines_after_function = 1 +ij_php_blank_lines_after_imports = 1 +ij_php_blank_lines_after_opening_tag = 0 +ij_php_blank_lines_after_package = 0 +ij_php_blank_lines_around_class = 1 +ij_php_blank_lines_around_constants = 0 +ij_php_blank_lines_around_enum_cases = 0 +ij_php_blank_lines_around_field = 0 +ij_php_blank_lines_around_method = 1 +ij_php_blank_lines_before_class_end = 0 +ij_php_blank_lines_before_imports = 1 +ij_php_blank_lines_before_method_body = 0 +ij_php_blank_lines_before_package = 1 +ij_php_blank_lines_before_return_statement = 0 +ij_php_blank_lines_between_imports = 0 +ij_php_block_brace_style = end_of_line +ij_php_call_parameters_new_line_after_left_paren = false +ij_php_call_parameters_right_paren_on_new_line = false +ij_php_call_parameters_wrap = off +ij_php_catch_on_new_line = false +ij_php_category_weight = 28 +ij_php_class_brace_style = next_line +ij_php_comma_after_last_argument = false +ij_php_comma_after_last_array_element = false +ij_php_comma_after_last_closure_use_var = false +ij_php_comma_after_last_match_arm = false +ij_php_comma_after_last_parameter = false +ij_php_concat_spaces = true +ij_php_copyright_weight = 28 +ij_php_deprecated_weight = 28 +ij_php_do_while_brace_force = never +ij_php_else_if_style = as_is +ij_php_else_on_new_line = false +ij_php_example_weight = 28 +ij_php_extends_keyword_wrap = off +ij_php_extends_list_wrap = off +ij_php_fields_default_visibility = private +ij_php_filesource_weight = 28 +ij_php_finally_on_new_line = false +ij_php_for_brace_force = never +ij_php_for_statement_new_line_after_left_paren = false +ij_php_for_statement_right_paren_on_new_line = false +ij_php_for_statement_wrap = off +ij_php_force_empty_methods_in_one_line = false +ij_php_force_short_declaration_array_style = false +ij_php_getters_setters_naming_style = camel_case +ij_php_getters_setters_order_style = getters_first +ij_php_global_weight = 28 +ij_php_group_use_wrap = on_every_item +ij_php_if_brace_force = never +ij_php_if_lparen_on_next_line = false +ij_php_if_rparen_on_next_line = false +ij_php_ignore_weight = 28 +ij_php_import_sorting = alphabetic +ij_php_indent_break_from_case = true +ij_php_indent_case_from_switch = true +ij_php_indent_code_in_php_tags = false +ij_php_internal_weight = 28 +ij_php_keep_blank_lines_after_lbrace = 2 +ij_php_keep_blank_lines_before_right_brace = 2 +ij_php_keep_blank_lines_in_code = 2 +ij_php_keep_blank_lines_in_declarations = 2 +ij_php_keep_control_statement_in_one_line = true +ij_php_keep_first_column_comment = true +ij_php_keep_indents_on_empty_lines = false +ij_php_keep_line_breaks = true +ij_php_keep_rparen_and_lbrace_on_one_line = false +ij_php_keep_simple_classes_in_one_line = false +ij_php_keep_simple_methods_in_one_line = false +ij_php_lambda_brace_style = end_of_line +ij_php_license_weight = 28 +ij_php_line_comment_add_space = false +ij_php_line_comment_at_first_column = true +ij_php_link_weight = 28 +ij_php_lower_case_boolean_const = false +ij_php_lower_case_keywords = true +ij_php_lower_case_null_const = false +ij_php_method_brace_style = next_line +ij_php_method_call_chain_wrap = off +ij_php_method_parameters_new_line_after_left_paren = false +ij_php_method_parameters_right_paren_on_new_line = false +ij_php_method_parameters_wrap = off +ij_php_method_weight = 28 +ij_php_modifier_list_wrap = false +ij_php_multiline_chained_calls_semicolon_on_new_line = false +ij_php_namespace_brace_style = 1 +ij_php_new_line_after_php_opening_tag = false +ij_php_null_type_position = in_the_end +ij_php_package_weight = 28 +ij_php_param_weight = 0 +ij_php_parameters_attributes_wrap = off +ij_php_parentheses_expression_new_line_after_left_paren = false +ij_php_parentheses_expression_right_paren_on_new_line = false +ij_php_phpdoc_blank_line_before_tags = false +ij_php_phpdoc_blank_lines_around_parameters = false +ij_php_phpdoc_keep_blank_lines = true +ij_php_phpdoc_param_spaces_between_name_and_description = 1 +ij_php_phpdoc_param_spaces_between_tag_and_type = 1 +ij_php_phpdoc_param_spaces_between_type_and_name = 1 +ij_php_phpdoc_use_fqcn = false +ij_php_phpdoc_wrap_long_lines = false +ij_php_place_assignment_sign_on_next_line = false +ij_php_place_parens_for_constructor = 0 +ij_php_property_read_weight = 28 +ij_php_property_weight = 28 +ij_php_property_write_weight = 28 +ij_php_return_type_on_new_line = false +ij_php_return_weight = 1 +ij_php_see_weight = 28 +ij_php_since_weight = 28 +ij_php_sort_phpdoc_elements = true +ij_php_space_after_colon = true +ij_php_space_after_colon_in_enum_backed_type = true +ij_php_space_after_colon_in_named_argument = true +ij_php_space_after_colon_in_return_type = true +ij_php_space_after_comma = true +ij_php_space_after_for_semicolon = true +ij_php_space_after_quest = true +ij_php_space_after_type_cast = false +ij_php_space_after_unary_not = false +ij_php_space_before_array_initializer_left_brace = false +ij_php_space_before_catch_keyword = true +ij_php_space_before_catch_left_brace = true +ij_php_space_before_catch_parentheses = true +ij_php_space_before_class_left_brace = true +ij_php_space_before_closure_left_parenthesis = true +ij_php_space_before_colon = true +ij_php_space_before_colon_in_enum_backed_type = false +ij_php_space_before_colon_in_named_argument = false +ij_php_space_before_colon_in_return_type = false +ij_php_space_before_comma = false +ij_php_space_before_do_left_brace = true +ij_php_space_before_else_keyword = true +ij_php_space_before_else_left_brace = true +ij_php_space_before_finally_keyword = true +ij_php_space_before_finally_left_brace = true +ij_php_space_before_for_left_brace = true +ij_php_space_before_for_parentheses = true +ij_php_space_before_for_semicolon = false +ij_php_space_before_if_left_brace = true +ij_php_space_before_if_parentheses = true +ij_php_space_before_method_call_parentheses = false +ij_php_space_before_method_left_brace = true +ij_php_space_before_method_parentheses = false +ij_php_space_before_quest = true +ij_php_space_before_short_closure_left_parenthesis = false +ij_php_space_before_switch_left_brace = true +ij_php_space_before_switch_parentheses = true +ij_php_space_before_try_left_brace = true +ij_php_space_before_unary_not = false +ij_php_space_before_while_keyword = true +ij_php_space_before_while_left_brace = true +ij_php_space_before_while_parentheses = true +ij_php_space_between_ternary_quest_and_colon = false +ij_php_spaces_around_additive_operators = true +ij_php_spaces_around_arrow = false +ij_php_spaces_around_assignment_in_declare = false +ij_php_spaces_around_assignment_operators = true +ij_php_spaces_around_bitwise_operators = true +ij_php_spaces_around_equality_operators = true +ij_php_spaces_around_logical_operators = true +ij_php_spaces_around_multiplicative_operators = true +ij_php_spaces_around_null_coalesce_operator = true +ij_php_spaces_around_pipe_in_union_type = false +ij_php_spaces_around_relational_operators = true +ij_php_spaces_around_shift_operators = true +ij_php_spaces_around_unary_operator = false +ij_php_spaces_around_var_within_brackets = false +ij_php_spaces_within_array_initializer_braces = false +ij_php_spaces_within_brackets = false +ij_php_spaces_within_catch_parentheses = false +ij_php_spaces_within_for_parentheses = false +ij_php_spaces_within_if_parentheses = false +ij_php_spaces_within_method_call_parentheses = false +ij_php_spaces_within_method_parentheses = false +ij_php_spaces_within_parentheses = false +ij_php_spaces_within_short_echo_tags = true +ij_php_spaces_within_switch_parentheses = false +ij_php_spaces_within_while_parentheses = false +ij_php_special_else_if_treatment = false +ij_php_subpackage_weight = 28 +ij_php_ternary_operation_signs_on_next_line = false +ij_php_ternary_operation_wrap = off +ij_php_throws_weight = 2 +ij_php_todo_weight = 28 +ij_php_treat_multiline_arrays_and_lambdas_multiline = false +ij_php_unknown_tag_weight = 28 +ij_php_upper_case_boolean_const = false +ij_php_upper_case_null_const = false +ij_php_uses_weight = 28 +ij_php_var_weight = 28 +ij_php_variable_naming_style = mixed +ij_php_version_weight = 28 +ij_php_while_brace_force = never +ij_php_while_on_new_line = false -[{*.har,*.json}] +[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,composer.lock,jest.config}] indent_size = 2 ij_json_array_wrapping = split_into_lines ij_json_keep_blank_lines_in_code = 0 @@ -568,6 +991,14 @@ ij_html_space_around_equality_in_attribute = false ij_html_space_inside_empty_tag = false ij_html_text_wrap = normal +[{*.http,*.rest}] +indent_size = 0 +ij_continuation_indent_size = 4 +ij_http-request_call_parameters_wrap = normal +ij_http-request_method_parameters_wrap = split_into_lines +ij_http-request_space_before_comma = true +ij_http-request_spaces_around_assignment_operators = true + [{*.kt,*.kts}] ij_kotlin_align_in_columns_case_branch = false ij_kotlin_align_multiline_binary_operation = false @@ -670,19 +1101,11 @@ ij_markdown_min_lines_between_paragraphs = 1 ij_markdown_wrap_text_if_long = true ij_markdown_wrap_text_inside_blockquotes = true -[{*.pb,*.textproto}] -indent_size = 2 -tab_width = 2 -ij_continuation_indent_size = 4 -ij_prototext_keep_blank_lines_in_code = 2 -ij_prototext_keep_indents_on_empty_lines = false -ij_prototext_keep_line_breaks = true -ij_prototext_space_after_colon = true -ij_prototext_space_after_comma = true -ij_prototext_space_before_colon = false -ij_prototext_space_before_comma = false -ij_prototext_spaces_within_braces = true -ij_prototext_spaces_within_brackets = false +[{*.properties,spring.handlers,spring.schemas}] +ij_properties_align_group_field_declarations = false +ij_properties_keep_blank_lines = false +ij_properties_key_value_delimiter = equals +ij_properties_spaces_around_key_value_delimiter = false [{*.toml,Cargo.lock,Cargo.toml.orig,Gopkg.lock,Pipfile,poetry.lock}] ij_toml_keep_indents_on_empty_lines = false diff --git a/README.md b/README.md index 66f52a9d..4e5e71e9 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ implementation("org.piwik.java.tracking:matomo-java-tracker-java17:3.0.0") ### Spring Boot Module -If you use Spring Boot, you can use the Spring Boot Autoconfigure artifact. It will create a MatomoTracker bean for you +If you use Spring Boot 3, you can use the Spring Boot Starter artifact. It will create a MatomoTracker bean for you and allows you to configure the tracker via application properties. Add the following dependency to your build: ```xml @@ -194,7 +194,7 @@ The following properties are supported: | matomo.tracker.disable-ssl-host-verification | If set to true, the SSL host of the Matomo server will not be validated. This should only be used for testing purposes. Default: false | | matomo.tracker.thread-pool-size | The number of threads that will be used to asynchronously send requests. Default: 2 | -To ensure the `MatomoTracker` bean is created by the autoconfigure module, you have to add the following property to +To ensure the `MatomoTracker` bean is created by the auto configuration, you have to add the following property to your `application.properties` file: ```properties @@ -209,29 +209,114 @@ matomo: api-endpoint: https://your-matomo-domain.tld/matomo.php ``` -### Create a Request +You can automatically add the `MatomoTrackerFilter` to your Spring Boot application if you add the following property: -Each MatomoRequest represents an action the user has taken that you want tracked by your Matomo server. Create a -MatomoRequest through +```properties +matomo.tracker.filter.enabled=true +``` -```java +Or if you use YAML: +```yaml +matomo: + tracker: + filter: + enabled: true +``` + +The filter uses `ServletMatomoRequest` to create a `MatomoRequest` from a `HttpServletRequest` on every filter call. + +### Sending a Tracking Request + +To let the Matomo Java Tracker send a request to the Matomo instance, you need the following minimal code: + +```java +import java.net.URI; import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoTracker; +import org.matomo.java.tracking.TrackerConfiguration; +import org.matomo.java.tracking.parameters.VisitorId; -public class YourImplementation { +public class SendExample { - public void yourMethod() { - MatomoRequest request = MatomoRequest.builder() - .siteId(42) - .actionUrl("https://www.mydomain.com/signup") - .actionName("Signup") - .build(); - } + public static void main(String[] args) { + + TrackerConfiguration configuration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://www.yourdomain.com/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("ee6e3dd9ed1b61f5328cf5978b5a8c71") + .logFailedTracking(true) + .build(); + + MatomoTracker tracker = new MatomoTracker(configuration); + + tracker.sendRequestAsync(MatomoRequest + .request() + .actionName("Checkout") + .actionUrl("https://www.yourdomain.com/checkout") + .visitorId(VisitorId.fromString("customer@mail.com")) + .build() + ); + + } } +``` + +This will send a request to the Matomo instance at https://www.yourdomain.com/matomo.php and track a page view for the +visitor customer@mail.com with the action name "Checkout" and action URL "https://www.yourdomain.com/checkout" for +the site with id 1. The request will be sent asynchronously, that means the method will return immediately and your +application will not wait for the response of the Matomo server. In the configuration we set the default site id to 1 +and configure the default auth token. With `logFailedTracking` we enable logging of failed tracking requests. +If you have multiple requests to wish to track, it may be more efficient to send them in a single HTTP call. To do this, +send a bulk request. Place your requests in an _Iterable_ data structure and call + +```java +import java.net.URI; +import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoTracker; +import org.matomo.java.tracking.TrackerConfiguration; +import org.matomo.java.tracking.parameters.VisitorId; + +public class BulkExample { + + public static void main(String[] args) { + + TrackerConfiguration configuration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://www.yourdomain.com/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("ee6e3dd9ed1b61f5328cf5978b5a8c71") + .logFailedTracking(true) + .build(); + + MatomoTracker tracker = new MatomoTracker(configuration); + + VisitorId visitorId = VisitorId.fromString("customer@mail.com"); + tracker.sendBulkRequestAsync( + MatomoRequest + .request() + .actionName("Checkout") + .actionUrl("https://www.yourdomain.com/checkout") + .visitorId(visitorId) + .build(), + MatomoRequest + .request() + .actionName("Payment") + .actionUrl("https://www.yourdomain.com/checkout") + .visitorId(visitorId) + .build() + ); + + } + +} ``` +This will send two requests in a single HTTP call. The requests will be sent asynchronously. + Per default every request has the following default parameters: | Parameter Name | Default Value | @@ -242,68 +327,55 @@ Per default every request has the following default parameters: | apiVersion | 1 | | responseAsImage | false | -Overwrite these properties as desired. +Overwrite these properties as desired. We strongly recommend your to determine the visitor id for every user using +a unique identifier, e.g. an email address. If you do not provide a visitor id, a random visitor id will be generated. Note that if you want to be able to track campaigns using *Referrers > Campaigns*, you must add the correct -URL parameters to your actionUrl. For matomoJavaTrackerTest, - -```java - -package matomoJavaTrackerTest; - -import org.matomo.java.tracking.MatomoRequest; - -public class YourImplementation { - - public void yourMethod() { - - MatomoRequest request = MatomoRequest.builder() - .siteId(42) - .actionUrl( - "https://matomoJavaTrackerTest.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore") // include the query parameters to the url - .actionName("LearnMore") - .build(); - } - -} -``` - -See [Tracking Campaigns](https://matomo.org/docs/tracking-campaigns/) for more information. All HTTP query parameters -denoted on -the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate -getters and setters. See [MatomoRequest](core/src/main/java/org/matomo/java/tracking/MatomoRequest.java) for the -mappings of -the -parameters to their corresponding attributes. +URL parameters to your actionUrl. See [Tracking Campaigns](https://matomo.org/docs/tracking-campaigns/) for more information. All HTTP query parameters +denoted on the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate getters and setters. See +[MatomoRequest](core/src/main/java/org/matomo/java/tracking/MatomoRequest.java) for the mappings of the parameters to their corresponding attributes. Requests are validated prior to sending. If a request is invalid, a `MatomoException` will be thrown. -### Sending Requests - -Create a MatomoTracker using the constructor +In a Servlet environment, it might be easier to use the `ServletMatomoRequest` class to create a `MatomoRequest` from a +`HttpServletRequest`: ```java -package matomoJavaTrackerTest; - -import java.net.URI; +import jakarta.servlet.http.HttpServletRequest; +import org.matomo.java.tracking.MatomoRequest; import org.matomo.java.tracking.MatomoTracker; -import org.matomo.java.tracking.TrackerConfiguration; - -public class YourImplementation { +import org.matomo.java.tracking.parameters.DeviceResolution; +import org.matomo.java.tracking.ServletMatomoRequest; - public void yourMethod() { +public class ServletMatomoRequestExample { - MatomoTracker tracker = new MatomoTracker(TrackerConfiguration.builder() - .apiEndpoint(URI.create( - "https://your-matomo-domain.tld/matomo.php")) - .build()); + private final MatomoTracker tracker; + public ServletMatomoRequestExample(MatomoTracker tracker) { + this.tracker = tracker; + } + + public void someControllerMethod(HttpServletRequest req) { + MatomoRequest matomoRequest = ServletMatomoRequest + .fromServletRequest(req) + .actionName("Some Controller Action") + // ... + .build(); + tracker.sendRequestAsync(matomoRequest); + // ... } } ``` -The Matomo Tracker currently supports the following builder methods: +The `ServletMatomoRequest` automatically sets the action URL, applies browser request headers, corresponding Matomo +cookies and the visitor IP address. It sets the visitor ID, Matomo session ID, custom variables and heatmap +if Matomo cookies are present. + +### Tracking Configuration + +The `MatomoTracker` can be configured using the `TrackerConfiguration` object. The following configuration options are +available: * `.apiEndpoint(...)` An `URI` object that points to the Matomo Tracking API endpoint of your Matomo installation. Must be set. @@ -331,123 +403,6 @@ The Matomo Tracker currently supports the following builder methods: should only be used for testing purposes. Default: false * `.threadPoolSize(...)` The number of threads that will be used to asynchronously send requests. Default: 2 -To send a single request synchronously via GET, call - -```java -package matomoJavaTrackerTest; - -import org.apache.http.HttpResponse; -import org.matomo.java.tracking.MatomoRequest; -import org.matomo.java.tracking.MatomoTracker; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; - -public class YourImplementation { - - public void yourMethod() { - - MatomoRequest request = MatomoRequest.builder() - .siteId(42) - .actionUrl("https://www.mydomain.com/some/page") - .actionName("Signup") - .build(); - - MatomoTracker tracker = new MatomoTracker(TrackerConfiguration.builder() - .apiEndpoint(URI.create( - "https://your-matomo-domain.tld/matomo.php")) - .build()); - - tracker.sendRequestAsync(request); - - } - -} -``` - -If you have multiple requests to wish to track, it may be more efficient to send them in a single HTTP call. To do this, -send a bulk request. Place your requests in an _Iterable_ data structure and call - -```java -package matomoJavaTrackerTest; - -import org.matomo.java.tracking.MatomoRequest; -import org.matomo.java.tracking.MatomoTracker; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.concurrent.ExecutionException; - -public class YourImplementation { - - public void yourMethod() { - - Collection requests = new ArrayList<>(); - MatomoRequestBuilder builder = MatomoRequest.request().siteId(42); - requests.add(builder.actionUrl("https://www.mydomain.com/some/page").actionName("Some Page").build()); - requests.add(builder.actionUrl("https://www.mydomain.com/another/page").actionName("Another Page").build()); - - MatomoTracker tracker = new MatomoTracker(TrackerConfiguration.builder() - .apiEndpoint(URI.create( - "https://your-matomo-domain.tld/matomo.php")) - .build()); - - tracker.sendBulkRequestAsync(requests); - - } - -} - -``` - -If some of the parameters that you've specified in the bulk request require AuthToken to be set, this can also be set in -the bulk request through - -```java -package matomoJavaTrackerTest; - -import org.apache.http.HttpResponse; -import org.matomo.java.tracking.MatomoLocale; -import org.matomo.java.tracking.MatomoRequest; -import org.matomo.java.tracking.MatomoTracker; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Locale; -import java.util.concurrent.ExecutionException; - -public class YourImplementation { - - public void yourMethod() { - - Collection requests = new ArrayList<>(); - MatomoRequestBuilder builder = MatomoRequest.request().siteId(42); - requests.add(builder.actionUrl("https://www.mydomain.com/some/page").actionName("Some Page").build()); - requests.add(builder.actionUrl("https://www.mydomain.com/another/page") - .actionName("Another Page") - .visitorCountry(new MatomoLocale(Locale.GERMANY)) - .build()); - - MatomoTracker tracker = new MatomoTracker(TrackerConfiguration.builder() - .apiEndpoint(URI.create( - "https://your-matomo-domain.tld/matomo.php")) - .build()); - - // second parameter is authentication token need for country override - tracker.sendBulkRequestAsync(requests, "33dc3f2536d3025974cccb4b4d2d98f4"); - - - } - -} -``` - ## Migration from Version 2 to 3 We improved this library by adding the dimension parameter and removing outdated parameters in Matomo version 5, @@ -520,7 +475,7 @@ following breaking changes: * `customTrackingParameters` in `MatomoRequestBuilder` requires a `Map>` instead of `Map` * `pageCustomVariables` and `visitCustomVariables` are of type `CustomVariables` instead of collections. Create them - with `CustomVariables.builder().variable(customVariable)` + with `new CustomVariables().add(customVariable)` * `setPageCustomVariable` and `getPageCustomVariable` no longer accept a string as an index. Please use integers instead. * Custom variables will be sent URL encoded @@ -539,7 +494,9 @@ This project contains the following modules: * `java8` contains the Java 8 implementation of the Matomo Java Tracker * `java17` contains the Java 17 implementation of the Matomo Java Tracker using the HttpClient available since Java 11 (recommended) -* `spring-boot-autoconfigure` contains the Spring Boot autoconfigure module +* `servlet` contains `SerlvetMatomoRequest` to create a `MatomoRequest` from a `HttpServletRequest` and a filter + `MatomoTrackingFilter` that can be used to track requests to a servlet +* `spring` contains the Spring Boot Starter * `test` contains tools for manual test against a local Matomo instance created with Docker (see below) @@ -590,7 +547,7 @@ The following snippets helps you to do this quickly: docker-compose exec matomo sed -i 's/localhost/localhost:8080/g' /var/www/html/config/config.ini.php ``` -After the installation you can run `MatomoJavaTrackerTest` in the module `test` to test the tracker. It will send +After the installation you can run `MatomoTrackerTester` in the module `test` to test the tracker. It will send multiple randomized requests to the local Matomo instance. @@ -607,6 +564,13 @@ Use the following snippet to do this: docker-compose exec matomo sh -c 'echo -e "\n\n[Tracker]\ndebug = 1\n" >> /var/www/html/config/config.ini.php' ``` +To test the servlet integration, run `MatomoServletTester` in your favorite IDE. It starts an embedded Jetty server +that serves a simple servlet. The servlet sends a request to the local Matomo instance if you call the URL +http://localhost:8090/track.html. Maybe you need to disable support for the Do Not Track preference in Matomo to get the +request tracked: Go to _Administration > Privacy > Do Not Track_ and disable the checkbox _Respect Do Not Track. +We also recommend to install the Custom Variables plugin from Marketplace to the test custom variables feature and +setup some dimensions. + ## Versioning We use [SemVer](https://semver.org/) for versioning. For the versions available, see diff --git a/core/pom.xml b/core/pom.xml index dbf78e4f..e18418bb 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -25,11 +25,6 @@ org.projectlombok lombok - - - jakarta.servlet - jakarta.servlet-api - 6.0.0 provided @@ -40,6 +35,7 @@ org.assertj assertj-core + test org.slf4j diff --git a/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java index 19095931..9bced43e 100644 --- a/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -99,6 +99,14 @@ public class MatomoRequest { @Default private VisitorId visitorId = VisitorId.random(); + /** + * Tracks if the visitor is a returning visitor. + * + *

This is done by storing a visitor ID in a 1st party cookie. + */ + @TrackingParameter(name = "_idn") + private Boolean newVisitor; + /** * The full HTTP Referrer URL. This value is used to determine how someone got to your website (ie, through a website, * search engine or campaign) @@ -589,6 +597,13 @@ public class MatomoRequest { @TrackingParameter(name = "debug") private Boolean debug; + /** + * The Matomo session ID sent as a cookie {@code MATOMO_SESSID}. + * + *

If not null a cookie with the name {@code MATOMO_SESSID} will be sent with the value of this parameter. + */ + private String sessionId; + /** * Custom Dimension values for specific Custom Dimension IDs. * @@ -611,6 +626,14 @@ public class MatomoRequest { */ private Map headers; + /** + * Appends additional cookies to the request. + * + *

This allows you to add Matomo specific cookies, like {@code _pk_id} or {@code _pk_sess} coming from Matomo + * responses to the request. + */ + private Map cookies; + /** * Create a new request from the id of the site being tracked and the full * url for the current action. This constructor also sets: diff --git a/core/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java b/core/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java deleted file mode 100644 index dd534e72..00000000 --- a/core/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.matomo.java.tracking; - -import jakarta.servlet.http.HttpServletRequest; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import lombok.NonNull; - -import static java.util.Arrays.asList; - - -/** - * Adds the headers from a {@link HttpServletRequest} to a {@link MatomoRequest.MatomoRequestBuilder}. - * - *

Use #fromServletRequest(HttpServletRequest) to create a new builder with the headers from the request or - * #addServletRequestHeaders(MatomoRequest.MatomoRequestBuilder, HttpServletRequest) to add the headers to an existing - * builder. - */ -public final class ServletMatomoRequest { - - /** - * Please ensure these values are always lower case. - */ - private static final Set RESTRICTED_HEADERS = Collections.unmodifiableSet(new HashSet<>( - asList("connection", "content-length", "expect", "host", "upgrade") - )); - - private ServletMatomoRequest() { - // should not be instantiated - } - - /** - * Creates a new builder with the headers from the request. - * - *

Use #addServletRequestHeaders(MatomoRequest.MatomoRequestBuilder, HttpServletRequest) to add the headers to an - * existing builder. - * - * @param request the request to get the headers from (must not be null) - * @return a new builder with the headers from the request (never null) - */ - @edu.umd.cs.findbugs.annotations.NonNull - public static MatomoRequest.MatomoRequestBuilder fromServletRequest(@NonNull HttpServletRequest request) { - return addServletRequestHeaders(MatomoRequest.request(), request); - } - - /** - * Adds the headers from the request to an existing builder. - * - *

Use #fromServletRequest(HttpServletRequest) to create a new builder with the headers from the request. - * - * @param builder the builder to add the headers to (must not be null) - * @param request the request to get the headers from (must not be null) - * @return the builder with the headers added (never null) - */ - @edu.umd.cs.findbugs.annotations.NonNull - public static MatomoRequest.MatomoRequestBuilder addServletRequestHeaders( - @NonNull MatomoRequest.MatomoRequestBuilder builder, @NonNull HttpServletRequest request - ) { - Map headers = new HashMap<>(10); - Enumeration headerNames = request.getHeaderNames(); - while (headerNames.hasMoreElements()) { - String headerName = headerNames.nextElement(); - if (headerName != null && !headerName.trim().isEmpty() - && !RESTRICTED_HEADERS.contains(headerName.toLowerCase(Locale.ROOT))) { - headers.put(headerName, request.getHeader(headerName)); - } - } - return builder.headers(headers).visitorIp(request.getRemoteAddr()); - } -} \ No newline at end of file diff --git a/core/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java b/core/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java index 8e99e710..51408558 100644 --- a/core/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java +++ b/core/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java @@ -50,7 +50,7 @@ public class TrackerConfiguration { *

A timeout value of zero is interpreted as an infinite timeout. * A `null` value is interpreted as undefined (system default if applicable).

* - *

Default: 10 seconds

+ *

Default: 5 seconds

*/ @Builder.Default Duration connectTimeout = Duration.ofSeconds(5L); @@ -62,7 +62,7 @@ public class TrackerConfiguration { *

A timeout value of zero is interpreted as an infinite timeout. * A `null value is interpreted as undefined (system default if applicable).

* - *

Default: 30 seconds

+ *

Default: 5 seconds

*/ @Builder.Default Duration socketTimeout = Duration.ofSeconds(5L); diff --git a/core/src/main/java/org/matomo/java/tracking/parameters/CustomVariables.java b/core/src/main/java/org/matomo/java/tracking/parameters/CustomVariables.java index c887e7d8..4fee72df 100644 --- a/core/src/main/java/org/matomo/java/tracking/parameters/CustomVariables.java +++ b/core/src/main/java/org/matomo/java/tracking/parameters/CustomVariables.java @@ -12,6 +12,7 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; +import java.util.StringTokenizer; import lombok.EqualsAndHashCode; import lombok.NonNull; @@ -131,6 +132,52 @@ boolean isEmpty() { return variables.isEmpty(); } + + /** + * Parses a JSON representation of custom variables. + * + *

The format is as follows: {@code {"1":["key1","value1"],"2":["key2","value2"]}} + * + *

Example: {@code {"1":["OS","Windows"],"2":["Browser","Firefox"]}} + * + *

This is mainly used to parse the custom variables from the cookie. + * + * @param value The JSON representation of the custom variables to parse or null + * @return The parsed custom variables or null if the given value is null or empty + */ + @Nullable + public static CustomVariables parse(@Nullable String value) { + if (value == null || value.isEmpty()) { + return null; + } + + CustomVariables customVariables = new CustomVariables(); + StringTokenizer tokenizer = new StringTokenizer(value, ":{}\""); + + Integer key = null; + String customVariableKey = null; + String customVariableValue = null; + while (tokenizer.hasMoreTokens()) { + String token = tokenizer.nextToken().trim(); + if (!token.isEmpty()) { + if (token.matches("\\d+")) { + key = Integer.parseInt(token); + } else if (token.startsWith("[") && key != null) { + customVariableKey = tokenizer.nextToken(); + tokenizer.nextToken(); + customVariableValue = tokenizer.nextToken(); + } else if (key != null && customVariableKey != null && customVariableValue != null) { + customVariables.add(new CustomVariable(customVariableKey, customVariableValue), key); + } else if (token.equals(",")) { + key = null; + customVariableKey = null; + customVariableValue = null; + } + } + } + return customVariables; + } + /** * Creates a JSON representation of the custom variables. The format is as follows: * {@code {"1":["key1","value1"],"2":["key2","value2"]}} diff --git a/core/src/test/java/org/matomo/java/tracking/ServletMatomoRequestTest.java b/core/src/test/java/org/matomo/java/tracking/ServletMatomoRequestTest.java deleted file mode 100644 index 5d52df92..00000000 --- a/core/src/test/java/org/matomo/java/tracking/ServletMatomoRequestTest.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.matomo.java.tracking; - -import static java.util.Collections.singletonMap; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -class ServletMatomoRequestTest { - - @Test - void addsServletRequestHeaders() { - - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setHeaders(singletonMap("headerName", "headerValue")); - - MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); - - MatomoRequest matomoRequest = builder.build(); - assertThat(matomoRequest.getHeaders()).hasSize(1).containsEntry("headerName", "headerValue"); - } - - @ParameterizedTest - @ValueSource(strings = {"connection", "content-length", "expect", "host", "upgrade"}) - void doesNotAddRestrictedHeaders(String restrictedHeader) { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setHeaders(singletonMap(restrictedHeader, "headerValue")); - - MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); - - MatomoRequest matomoRequest = builder.build(); - assertThat(matomoRequest.getHeaders()).isEmpty(); - } - - @Test - void failsIfServletRequestIsNull() { - assertThatThrownBy(() -> ServletMatomoRequest.fromServletRequest(null)) - .isInstanceOf(NullPointerException.class) - .hasMessage("request is marked non-null but is null"); - } - - @Test - void failsIfBuilderIsNull() { - assertThatThrownBy(() -> ServletMatomoRequest.addServletRequestHeaders( - null, - new MockHttpServletRequest() - )).isInstanceOf(NullPointerException.class).hasMessage("builder is marked non-null but is null"); - } - -} \ No newline at end of file diff --git a/core/src/test/java/org/matomo/java/tracking/parameters/CustomVariablesTest.java b/core/src/test/java/org/matomo/java/tracking/parameters/CustomVariablesTest.java index c31e2bdc..0cf25d80 100644 --- a/core/src/test/java/org/matomo/java/tracking/parameters/CustomVariablesTest.java +++ b/core/src/test/java/org/matomo/java/tracking/parameters/CustomVariablesTest.java @@ -36,15 +36,13 @@ void testAdd_CustomVariable() { assertThat(customVariables.get("a")).isEqualTo("b"); assertThat(customVariables.get(5)).isEqualTo(c); assertThat(customVariables.get(3)).isNull(); - assertThat(customVariables).hasToString( - "{\"1\":[\"a\",\"b\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"e\"]}"); + assertThat(customVariables).hasToString("{\"1\":[\"a\",\"b\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"e\"]}"); CustomVariable d = new CustomVariable("a", "f"); customVariables.add(d); assertThat(customVariables.get("a")).isEqualTo("f"); assertThat(customVariables.get(1)).isEqualTo(d); assertThat(customVariables.get(5)).isEqualTo(d); - assertThat(customVariables).hasToString( - "{\"1\":[\"a\",\"f\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"f\"]}"); + assertThat(customVariables).hasToString("{\"1\":[\"a\",\"f\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"f\"]}"); customVariables.remove("a"); assertThat(customVariables.get("a")).isNull(); assertThat(customVariables.get(1)).isNull(); @@ -166,4 +164,15 @@ void testRemoveCustomVariableKeyNull() { .hasNoCause(); } + @Test + void parseCustomVariables() { + CustomVariables customVariables = + CustomVariables.parse( + "{\"1\":[\"VAR 1 set, var 2 not set\",\"yes\"],\"3\":[\"var 3 set\",\"yes!!!!\"]}"); + assertThat(customVariables.get(1).getKey()).isEqualTo("VAR 1 set, var 2 not set"); + assertThat(customVariables.get(1).getValue()).isEqualTo("yes"); + assertThat(customVariables.get(3).getKey()).isEqualTo("var 3 set"); + assertThat(customVariables.get(3).getValue()).isEqualTo("yes!!!!"); + } + } diff --git a/java17/pom.xml b/java17/pom.xml index d213d563..c08e3eeb 100644 --- a/java17/pom.xml +++ b/java17/pom.xml @@ -19,18 +19,18 @@ 17 17 - 3.1.5 org.piwik.java.tracking matomo-java-tracker-core - 3.0.0-SNAPSHOT + ${project.version} org.projectlombok lombok + provided com.github.spotbugs @@ -39,10 +39,12 @@ org.junit.jupiter junit-jupiter + test org.assertj assertj-core + test org.wiremock diff --git a/java17/src/main/java/org/matomo/java/tracking/Java17Sender.java b/java17/src/main/java/org/matomo/java/tracking/Java17Sender.java index a4a34da3..c1e92432 100644 --- a/java17/src/main/java/org/matomo/java/tracking/Java17Sender.java +++ b/java17/src/main/java/org/matomo/java/tracking/Java17Sender.java @@ -4,6 +4,8 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; +import java.net.CookieStore; +import java.net.HttpCookie; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; @@ -34,6 +36,9 @@ public class Java17Sender implements Sender { @lombok.NonNull private final HttpClient httpClient; + @lombok.NonNull + private final CookieStore cookieStore; + @NonNull @Override public CompletableFuture sendSingleAsync(@NonNull @lombok.NonNull MatomoRequest request) { @@ -66,6 +71,7 @@ private HttpRequest buildHttpPostRequest( Collection queries = new ArrayList<>(); Map headers = new LinkedHashMap<>(10); String headerUserAgent = null; + cookieStore.removeAll(); for (MatomoRequest request : requests) { RequestValidator.validate(request, authToken); if (request.getHeaders() != null && !request.getHeaders().isEmpty()) { @@ -75,6 +81,7 @@ private HttpRequest buildHttpPostRequest( headerUserAgent = request.getHeaderUserAgent(); } queries.add(queryCreator.createQuery(request, null)); + addCookies(request); } HttpRequest.Builder builder = HttpRequest .newBuilder() @@ -101,10 +108,24 @@ public CompletableFuture sendBulkAsync( return sendAsyncAndCheckResponse(buildHttpPostRequest(requests, overrideAuthToken), null); } + @NonNull + private CompletableFuture sendAsyncAndCheckResponse( + @NonNull HttpRequest httpRequest, @Nullable T result + ) { + return send(httpRequest, + () -> httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.discarding()).thenApply(response -> { + checkResponse(response, httpRequest); + return result; + }) + ); + } + @NonNull private HttpRequest buildHttpGetRequest(@NonNull MatomoRequest request) { String authToken = AuthToken.determineAuthToken(null, singleton(request), trackerConfiguration); RequestValidator.validate(request, authToken); + cookieStore.removeAll(); + addCookies(request); URI apiEndpoint = trackerConfiguration.getApiEndpoint(); HttpRequest.Builder builder = HttpRequest .newBuilder() @@ -118,24 +139,40 @@ private HttpRequest buildHttpGetRequest(@NonNull MatomoRequest request) { return builder.build(); } - private void addHeaders(@NonNull HttpRequest.Builder builder, @Nullable Map headers) { - if (headers != null) { - for (Map.Entry header : headers.entrySet()) { - builder.header(header.getKey(), header.getValue()); + private T send( + @NonNull HttpRequest httpRequest, @NonNull Callable callable + ) { + try { + log.debug("Sending request to Matomo: {}", httpRequest); + log.debug("Headers: {}", httpRequest.headers()); + log.debug("Cookies: {}", cookieStore.getCookies()); + return callable.call(); + } catch (Exception e) { + if (trackerConfiguration.isLogFailedTracking()) { + log.error("Could not send request to Matomo: {}", httpRequest.uri(), e); } + throw new MatomoException("Could not send request to Matomo", e); } } - @NonNull - private CompletableFuture sendAsyncAndCheckResponse( - @NonNull HttpRequest httpRequest, @Nullable T result - ) { - return send(httpRequest, - () -> httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.discarding()).thenApply(response -> { - checkResponse(response, httpRequest); - return result; - }) - ); + private void checkResponse(@NonNull HttpResponse response, @NonNull HttpRequest httpRequest) { + if (response.statusCode() > 399) { + if (trackerConfiguration.isLogFailedTracking()) { + log.error("Received HTTP error code {} for URL {}", response.statusCode(), httpRequest.uri()); + } + throw new MatomoException(String.format("Tracking endpoint responded with code %d", response.statusCode())); + } + } + + private void addCookies(MatomoRequest request) { + if (request.getSessionId() != null && !request.getSessionId().isEmpty()) { + cookieStore.add(null, new HttpCookie("MATOMO_SESSID", request.getSessionId())); + } + if (request.getCookies() != null) { + for (Map.Entry entry : request.getCookies().entrySet()) { + cookieStore.add(null, new HttpCookie(entry.getKey(), entry.getValue())); + } + } } private void applyTrackerConfiguration(@NonNull HttpRequest.Builder builder) { @@ -145,9 +182,7 @@ private void applyTrackerConfiguration(@NonNull HttpRequest.Builder builder) { } private void setUserAgentHeader( - HttpRequest.Builder builder, - @Nullable String headerUserAgent, - @Nullable Map headers + HttpRequest.Builder builder, @Nullable String headerUserAgent, @Nullable Map headers ) { String userAgentHeader = null; if ((headerUserAgent == null || headerUserAgent.trim().isEmpty()) && headers != null) { @@ -155,32 +190,18 @@ private void setUserAgentHeader( caseInsensitiveMap.putAll(headers); userAgentHeader = caseInsensitiveMap.get("User-Agent"); } - if ((userAgentHeader == null || userAgentHeader.trim().isEmpty()) - && (headerUserAgent == null || headerUserAgent.trim().isEmpty()) + if ((userAgentHeader == null || userAgentHeader.trim().isEmpty()) && ( + headerUserAgent == null || headerUserAgent.trim().isEmpty()) && trackerConfiguration.getUserAgent() != null && !trackerConfiguration.getUserAgent().isEmpty()) { builder.header("User-Agent", trackerConfiguration.getUserAgent()); } } - private T send( - @NonNull HttpRequest httpRequest, @NonNull Callable callable - ) { - try { - return callable.call(); - } catch (Exception e) { - if (trackerConfiguration.isLogFailedTracking()) { - log.error("Could not send request to Matomo: {}", httpRequest.uri(), e); - } - throw new MatomoException("Could not send request to Matomo", e); - } - } - - private void checkResponse(@NonNull HttpResponse response, @NonNull HttpRequest httpRequest) { - if (response.statusCode() > 399) { - if (trackerConfiguration.isLogFailedTracking()) { - log.error("Received HTTP error code {} for URL {}", response.statusCode(), httpRequest.uri()); + private void addHeaders(@NonNull HttpRequest.Builder builder, @Nullable Map headers) { + if (headers != null) { + for (Map.Entry header : headers.entrySet()) { + builder.header(header.getKey(), header.getValue()); } - throw new MatomoException(String.format("Tracking endpoint responded with code %d", response.statusCode())); } } } diff --git a/java17/src/main/java/org/matomo/java/tracking/Java17SenderProvider.java b/java17/src/main/java/org/matomo/java/tracking/Java17SenderProvider.java index 405061d4..babc2b66 100644 --- a/java17/src/main/java/org/matomo/java/tracking/Java17SenderProvider.java +++ b/java17/src/main/java/org/matomo/java/tracking/Java17SenderProvider.java @@ -1,6 +1,7 @@ package org.matomo.java.tracking; import edu.umd.cs.findbugs.annotations.Nullable; +import java.net.CookieManager; import java.net.InetSocketAddress; import java.net.ProxySelector; import java.net.http.HttpClient; @@ -20,8 +21,10 @@ public class Java17SenderProvider implements SenderProvider { public Sender provideSender( TrackerConfiguration trackerConfiguration, QueryCreator queryCreator ) { + CookieManager cookieManager = new CookieManager(); HttpClient.Builder builder = HttpClient .newBuilder() + .cookieHandler(cookieManager) .executor(Executors.newFixedThreadPool(trackerConfiguration.getThreadPoolSize(), new DaemonThreadFactory())) ; if (trackerConfiguration.getConnectTimeout() != null && trackerConfiguration.getConnectTimeout().toMillis() > 0L) { @@ -51,7 +54,7 @@ public Sender provideSender( throw new MatomoException("Please disable SSL hostname verification manually using the system parameter -Djdk.internal.httpclient.disableHostnameVerification=true"); } - return new Java17Sender(trackerConfiguration, queryCreator, builder.build()); + return new Java17Sender(trackerConfiguration, queryCreator, builder.build(), cookieManager.getCookieStore()); } private static boolean isEmpty( diff --git a/java17/src/test/java/org/matomo/java/tracking/Java17SenderIT.java b/java17/src/test/java/org/matomo/java/tracking/Java17SenderIT.java index 60a71fa8..bad936d5 100644 --- a/java17/src/test/java/org/matomo/java/tracking/Java17SenderIT.java +++ b/java17/src/test/java/org/matomo/java/tracking/Java17SenderIT.java @@ -15,6 +15,7 @@ import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; import com.github.tomakehurst.wiremock.junit5.WireMockTest; +import java.net.CookieManager; import java.net.URI; import java.net.http.HttpClient; import java.time.Duration; @@ -37,31 +38,48 @@ void disableSslHostnameVerification() { @Test void failsIfTrackerConfigurationIsNotSet() { + CookieManager cookieManager = new CookieManager(); assertThatThrownBy(() -> new Java17Sender( null, new QueryCreator(TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build()), - HttpClient.newHttpClient() + HttpClient.newBuilder().cookieHandler(cookieManager).build(), + cookieManager.getCookieStore() )).isInstanceOf(NullPointerException.class).hasMessage("trackerConfiguration is marked non-null but is null"); } @Test void failsIfQueryCreatorIsNotSet() { + CookieManager cookieManager = new CookieManager(); assertThatThrownBy(() -> new Java17Sender( TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build(), null, - HttpClient.newHttpClient() + HttpClient.newBuilder().cookieHandler(cookieManager).build(), + cookieManager.getCookieStore() )).isInstanceOf(NullPointerException.class).hasMessage("queryCreator is marked non-null but is null"); } @Test void failsIfHttpClientIsNotSet() { + CookieManager cookieManager = new CookieManager(); assertThatThrownBy(() -> new Java17Sender( TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build(), new QueryCreator(TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build()), - null + null, + cookieManager.getCookieStore() )).isInstanceOf(NullPointerException.class).hasMessage("httpClient is marked non-null but is null"); } + @Test + void failsIfCookieStoreIsNotSet() { + CookieManager cookieManager = new CookieManager(); + assertThatThrownBy(() -> new Java17Sender( + TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build(), + new QueryCreator(TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build()), + HttpClient.newBuilder().cookieHandler(cookieManager).build(), + null + )).isInstanceOf(NullPointerException.class).hasMessage("cookieStore is marked non-null but is null"); + } + @Test void sendSingleFailsIfQueryIsMalformedWithSocketTimeout() { trackerConfiguration = TrackerConfiguration diff --git a/java8/pom.xml b/java8/pom.xml index f3e3f4ff..79d03105 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -15,15 +15,11 @@ Matomo Java Tracker Java 8 Official Java implementation of the Matomo Tracking HTTP API for Java 8. - - 2.7.17 - - org.piwik.java.tracking matomo-java-tracker-core - 3.0.0-SNAPSHOT + ${project.version} org.slf4j @@ -36,18 +32,21 @@ org.projectlombok lombok + provided org.junit.jupiter junit-jupiter + test org.assertj assertj-core + test com.github.tomakehurst - wiremock + wiremock-standalone 2.27.2 test diff --git a/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java b/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java index 62a051c7..e35fac9d 100644 --- a/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java +++ b/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java @@ -25,6 +25,7 @@ import java.security.SecureRandom; import java.util.ArrayList; import java.util.Collection; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.TreeMap; @@ -90,6 +91,7 @@ public void sendSingle( applyTrackerConfiguration(connection); setUserAgentProperty(connection, request.getHeaderUserAgent(), request.getHeaders()); addHeaders(connection, request.getHeaders()); + addCookies(connection, request.getSessionId(), request.getCookies()); log.debug("Sending single request using URI {} asynchronously", apiEndpoint); try { connection.connect(); @@ -101,14 +103,6 @@ public void sendSingle( } } - private void addHeaders(@NonNull HttpURLConnection connection, @Nullable Map headers) { - if (headers != null) { - for (Map.Entry header : headers.entrySet()) { - connection.setRequestProperty(header.getKey(), header.getValue()); - } - } - } - private HttpURLConnection openConnection(URL url) { HttpURLConnection connection; try { @@ -127,42 +121,6 @@ private HttpURLConnection openConnection(URL url) { return connection; } - private void applySslConfiguration( - @NonNull @lombok.NonNull HttpsURLConnection connection - ) { - if (trackerConfiguration.isDisableSslCertValidation()) { - try { - SSLContext sslContext = SSLContext.getInstance("SSL"); - sslContext.init(null, TRUST_ALL_MANAGERS, new SecureRandom()); - connection.setSSLSocketFactory(sslContext.getSocketFactory()); - } catch (Exception e) { - throw new MatomoException("Could not disable SSL certification validation", e); - } - } - if (trackerConfiguration.isDisableSslHostVerification()) { - connection.setHostnameVerifier(TRUSTING_HOSTNAME_VERIFIER); - } - } - - private HttpURLConnection openProxiedConnection( - @NonNull @lombok.NonNull URL url - ) throws IOException { - requireNonNull(trackerConfiguration.getProxyHost(), "Proxy host must not be null"); - if (trackerConfiguration.getProxyPort() <= 0) { - throw new IllegalArgumentException("Proxy port must be configured"); - } - InetSocketAddress proxyAddress = - new InetSocketAddress(trackerConfiguration.getProxyHost(), trackerConfiguration.getProxyPort()); - Proxy proxy = new Proxy(Proxy.Type.HTTP, proxyAddress); - if (!isEmpty(trackerConfiguration.getProxyUsername()) && !isEmpty(trackerConfiguration.getProxyPassword())) { - Authenticator.setDefault(new ProxyAuthenticator(trackerConfiguration.getProxyUsername(), - trackerConfiguration.getProxyPassword() - )); - } - log.debug("Using proxy {} on port {}", trackerConfiguration.getProxyHost(), trackerConfiguration.getProxyPort()); - return (HttpURLConnection) url.openConnection(proxy); - } - private void applyTrackerConfiguration(HttpURLConnection connection) { connection.setUseCaches(false); if (trackerConfiguration.getConnectTimeout() != null) { @@ -182,13 +140,45 @@ private void setUserAgentProperty( caseInsensitiveMap.putAll(headers); userAgentHeader = caseInsensitiveMap.get("User-Agent"); } - if ((userAgentHeader == null || userAgentHeader.trim().isEmpty()) - && (headerUserAgent == null || headerUserAgent.trim().isEmpty()) + if ((userAgentHeader == null || userAgentHeader.trim().isEmpty()) && ( + headerUserAgent == null || headerUserAgent.trim().isEmpty()) && trackerConfiguration.getUserAgent() != null && !trackerConfiguration.getUserAgent().isEmpty()) { connection.setRequestProperty("User-Agent", trackerConfiguration.getUserAgent()); } } + private void addHeaders(@NonNull HttpURLConnection connection, @Nullable Map headers) { + if (headers != null) { + for (Map.Entry header : headers.entrySet()) { + connection.setRequestProperty(header.getKey(), header.getValue()); + } + } + } + + private static void addCookies( + HttpURLConnection connection, String sessionId, Map cookies + ) { + StringBuilder cookiesValue = new StringBuilder(); + if (sessionId != null && !sessionId.isEmpty()) { + cookiesValue.append("MATOMO_SESSID=").append(sessionId); + if (cookies != null && !cookies.isEmpty()) { + cookiesValue.append("; "); + } + } + if (cookies != null) { + for (Iterator> iterator = cookies.entrySet().iterator(); iterator.hasNext(); ) { + Map.Entry entry = iterator.next(); + cookiesValue.append(entry.getKey()).append("=").append(entry.getValue()); + if (iterator.hasNext()) { + cookiesValue.append("; "); + } + } + } + if (cookiesValue.length() > 0) { + connection.setRequestProperty("Cookie", cookiesValue.toString()); + } + } + private void checkResponse(HttpURLConnection connection) throws IOException { int responseCode = connection.getResponseCode(); if (responseCode > 399) { @@ -205,6 +195,42 @@ private static boolean isEmpty( return str == null || str.isEmpty() || str.trim().isEmpty(); } + private HttpURLConnection openProxiedConnection( + @NonNull @lombok.NonNull URL url + ) throws IOException { + requireNonNull(trackerConfiguration.getProxyHost(), "Proxy host must not be null"); + if (trackerConfiguration.getProxyPort() <= 0) { + throw new IllegalArgumentException("Proxy port must be configured"); + } + InetSocketAddress proxyAddress = + new InetSocketAddress(trackerConfiguration.getProxyHost(), trackerConfiguration.getProxyPort()); + Proxy proxy = new Proxy(Proxy.Type.HTTP, proxyAddress); + if (!isEmpty(trackerConfiguration.getProxyUsername()) && !isEmpty(trackerConfiguration.getProxyPassword())) { + Authenticator.setDefault(new ProxyAuthenticator(trackerConfiguration.getProxyUsername(), + trackerConfiguration.getProxyPassword() + )); + } + log.debug("Using proxy {} on port {}", trackerConfiguration.getProxyHost(), trackerConfiguration.getProxyPort()); + return (HttpURLConnection) url.openConnection(proxy); + } + + private void applySslConfiguration( + @NonNull @lombok.NonNull HttpsURLConnection connection + ) { + if (trackerConfiguration.isDisableSslCertValidation()) { + try { + SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, TRUST_ALL_MANAGERS, new SecureRandom()); + connection.setSSLSocketFactory(sslContext.getSocketFactory()); + } catch (Exception e) { + throw new MatomoException("Could not disable SSL certification validation", e); + } + } + if (trackerConfiguration.isDisableSslHostVerification()) { + connection.setHostnameVerifier(TRUSTING_HOSTNAME_VERIFIER); + } + } + @Override public void sendBulk( @NonNull @lombok.NonNull Iterable requests, @Nullable String overrideAuthToken @@ -213,25 +239,37 @@ public void sendBulk( Collection queries = new ArrayList<>(); Map headers = new LinkedHashMap<>(); String headerUserAgent = null; + String sessionId = null; + Map cookies = null; for (MatomoRequest request : requests) { RequestValidator.validate(request, authToken); if (request.getHeaders() != null && !request.getHeaders().isEmpty()) { headers.putAll(request.getHeaders()); } - if (headerUserAgent == null && request.getHeaderUserAgent() != null - && !request.getHeaderUserAgent().trim().isEmpty()) { + if (headerUserAgent == null && request.getHeaderUserAgent() != null && !request + .getHeaderUserAgent() + .trim() + .isEmpty()) { headerUserAgent = request.getHeaderUserAgent(); } queries.add(queryCreator.createQuery(request, null)); + if (request.getSessionId() != null && !request.getSessionId().isEmpty()) { + sessionId = request.getSessionId(); + } + if (request.getCookies() != null && !request.getCookies().isEmpty()) { + cookies = request.getCookies(); + } } - sendBulk(queries, authToken, headers, headerUserAgent); + sendBulk(queries, authToken, headers, headerUserAgent, sessionId, cookies); } private void sendBulk( @NonNull @lombok.NonNull Collection queries, @Nullable String authToken, Map headers, - String headerUserAgent + String headerUserAgent, + String sessionId, + Map cookies ) { if (queries.isEmpty()) { throw new IllegalArgumentException("Queries must not be empty"); @@ -246,6 +284,7 @@ private void sendBulk( applyTrackerConfiguration(connection); setUserAgentProperty(connection, headerUserAgent, headers); addHeaders(connection, headers); + addCookies(connection, sessionId, cookies); log.debug("Sending bulk request using URI {} asynchronously", trackerConfiguration.getApiEndpoint()); OutputStream outputStream = null; try { @@ -286,8 +325,10 @@ public CompletableFuture sendBulkAsync( @NonNull Iterable requests, @Nullable String overrideAuthToken ) { String authToken = AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); - final Map headers = new LinkedHashMap<>(); + Map headers = new LinkedHashMap<>(); String headerUserAgent = findHeaderUserAgent(requests); + String sessionId = findSessionId(requests); + Map cookies = findCookies(requests); synchronized (queries) { for (MatomoRequest request : requests) { RequestValidator.validate(request, authToken); @@ -298,16 +339,18 @@ public CompletableFuture sendBulkAsync( queries.add(query); } } - return CompletableFuture.supplyAsync(() -> sendBulkAsync(authToken, headers, headerUserAgent), executor); + return CompletableFuture.supplyAsync(() -> + sendBulkAsync(authToken, headers, headerUserAgent, sessionId, cookies), executor); } @Nullable private Void sendBulkAsync( - @Nullable String authToken, Map headers, String headerUserAgent + @Nullable String authToken, Map headers, String headerUserAgent, String sessionId, + Map cookies ) { synchronized (queries) { if (!queries.isEmpty()) { - sendBulk(queries, authToken, headers, headerUserAgent); + sendBulk(queries, authToken, headers, headerUserAgent, sessionId, cookies); queries.clear(); } return null; @@ -324,4 +367,22 @@ private static String findHeaderUserAgent(@NonNull Iterable requests) { + for (MatomoRequest request : requests) { + if (request.getHeaderUserAgent() != null && !request.getHeaderUserAgent().trim().isEmpty()) { + return request.getHeaderUserAgent(); + } + } + return null; + } + + private Map findCookies(Iterable requests) { + for (MatomoRequest request : requests) { + if (request.getCookies() != null && !request.getCookies().isEmpty()) { + return request.getCookies(); + } + } + return null; + } + } diff --git a/core/src/main/java/org/matomo/java/tracking/TrustingHostnameVerifier.java b/java8/src/main/java/org/matomo/java/tracking/TrustingHostnameVerifier.java similarity index 100% rename from core/src/main/java/org/matomo/java/tracking/TrustingHostnameVerifier.java rename to java8/src/main/java/org/matomo/java/tracking/TrustingHostnameVerifier.java diff --git a/core/src/test/java/org/matomo/java/tracking/TrustingHostnameVerifierTest.java b/java8/src/test/java/org/matomo/java/tracking/TrustingHostnameVerifierTest.java similarity index 100% rename from core/src/test/java/org/matomo/java/tracking/TrustingHostnameVerifierTest.java rename to java8/src/test/java/org/matomo/java/tracking/TrustingHostnameVerifierTest.java diff --git a/pom.xml b/pom.xml index 9e21cacf..bac4ee05 100644 --- a/pom.xml +++ b/pom.xml @@ -61,23 +61,18 @@ UTF-8 1.8 1.8 - 2.0.9 ${project.build.outputDirectory}/delombok - 1.18.30 + 3.1.5 - org.projectlombok - lombok - ${lombok.version} - provided - - - org.slf4j - slf4j-api - ${slf4j.version} + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import com.github.spotbugs @@ -85,23 +80,6 @@ 4.8.0 provided - - org.assertj - assertj-core - 3.24.2 - test - - - org.junit.jupiter - junit-jupiter - 5.10.1 - test - - - org.slf4j - slf4j-simple - ${slf4j.version} - @@ -215,7 +193,7 @@ org.projectlombok lombok - ${lombok.version} + 1.18.30 @@ -372,6 +350,7 @@ core java8 java17 + servlet spring test diff --git a/servlet/pom.xml b/servlet/pom.xml new file mode 100644 index 00000000..d750ca64 --- /dev/null +++ b/servlet/pom.xml @@ -0,0 +1,70 @@ + + 4.0.0 + + + org.piwik.java.tracking + matomo-java-tracker-parent + 3.0.0-SNAPSHOT + ../pom.xml + + + matomo-java-tracker-servlet + 3.0.0-SNAPSHOT + jar + + Matomo Java Tracker Servlet + Integrates Matomo Java Tracker into your servlet based application + + + 17 + 17 + + + + + org.piwik.java.tracking + matomo-java-tracker-core + ${project.version} + + + jakarta.servlet + jakarta.servlet-api + provided + + + org.slf4j + slf4j-api + + + com.github.spotbugs + spotbugs-annotations + + + org.projectlombok + lombok + provided + + + org.junit.jupiter + junit-jupiter + test + + + org.eclipse.jetty + jetty-servlet + test + + + org.assertj + assertj-core + test + + + org.slf4j + slf4j-simple + test + + + + diff --git a/servlet/src/main/java/org/matomo/java/tracking/MatomoTrackerFilter.java b/servlet/src/main/java/org/matomo/java/tracking/MatomoTrackerFilter.java new file mode 100644 index 00000000..82ebd557 --- /dev/null +++ b/servlet/src/main/java/org/matomo/java/tracking/MatomoTrackerFilter.java @@ -0,0 +1,30 @@ +package org.matomo.java.tracking; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpFilter; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * This filter can be used to automatically send a request to the Matomo server for every request + * that is received by the servlet container. + */ +@RequiredArgsConstructor +@Slf4j +public class MatomoTrackerFilter extends HttpFilter { + + private final MatomoTracker tracker; + + @Override + protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) + throws IOException, ServletException { + MatomoRequest matomoRequest = ServletMatomoRequest.fromServletRequest(req).build(); + log.debug("Sending request {}", matomoRequest); + tracker.sendRequestAsync(matomoRequest); + super.doFilter(req, res, chain); + } +} diff --git a/servlet/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java b/servlet/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java new file mode 100644 index 00000000..27aca706 --- /dev/null +++ b/servlet/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java @@ -0,0 +1,140 @@ +package org.matomo.java.tracking; + +import static java.util.Arrays.asList; + +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import lombok.NonNull; +import org.matomo.java.tracking.parameters.CustomVariables; +import org.matomo.java.tracking.parameters.VisitorId; + + +/** + * Adds the headers from a {@link HttpServletRequest} to a {@link MatomoRequest.MatomoRequestBuilder}. + * + *

Use #fromServletRequest(HttpServletRequest) to create a new builder with the headers from the request or + * #addServletRequestHeaders(MatomoRequest.MatomoRequestBuilder, HttpServletRequest) to add the headers to an existing + * builder. + */ +public final class ServletMatomoRequest { + + /** + * Please ensure these values are always lower case. + */ + private static final Set RESTRICTED_HEADERS = + Collections.unmodifiableSet(new HashSet<>(asList("connection", "content-length", "expect", "host", "upgrade"))); + + private ServletMatomoRequest() { + // should not be instantiated + } + + /** + * Creates a new builder with the headers from the request. + * + *

Use #addServletRequestHeaders(MatomoRequest.MatomoRequestBuilder, HttpServletRequest) to add the headers to an + * existing builder. + * + * @param request the request to get the headers from (must not be null) + * @return a new builder with the headers from the request (never null) + */ + @edu.umd.cs.findbugs.annotations.NonNull + public static MatomoRequest.MatomoRequestBuilder fromServletRequest(@NonNull HttpServletRequest request) { + return addServletRequestHeaders(MatomoRequest.request(), request); + } + + /** + * Adds the headers from the request to an existing builder. + * + *

Use #fromServletRequest(HttpServletRequest) to create a new builder with the headers from the request. + * + * @param builder the builder to add the headers to (must not be null) + * @param request the request to get the headers from (must not be null) + * @return the builder with the headers added (never null) + */ + @edu.umd.cs.findbugs.annotations.NonNull + public static MatomoRequest.MatomoRequestBuilder addServletRequestHeaders( + @NonNull MatomoRequest.MatomoRequestBuilder builder, @NonNull HttpServletRequest request + ) { + return builder + .actionUrl(request.getRequestURL() == null ? null : request.getRequestURL().toString()) + .headers(collectHeaders(request)) + .visitorIp(request.getRemoteAddr()) + .cookies(processCookies(builder, request)); + } + + @edu.umd.cs.findbugs.annotations.NonNull + private static Map processCookies( + @edu.umd.cs.findbugs.annotations.NonNull MatomoRequest.MatomoRequestBuilder builder, + @edu.umd.cs.findbugs.annotations.NonNull HttpServletRequest request + ) { + Map cookies = new LinkedHashMap<>(3); + if (request.getCookies() != null) { + builder.supportsCookies(Boolean.TRUE); + for (Cookie cookie : request.getCookies()) { + if (cookie.getValue() != null && !cookie.getValue().trim().isEmpty()) { + processCookie(builder, cookies, cookie.getName(), cookie.getValue()); + } + } + } + return cookies; + } + + private static void processCookie( + @edu.umd.cs.findbugs.annotations.NonNull MatomoRequest.MatomoRequestBuilder builder, + @edu.umd.cs.findbugs.annotations.NonNull Map cookies, + @edu.umd.cs.findbugs.annotations.NonNull String cookieName, + @edu.umd.cs.findbugs.annotations.NonNull String cookieValue + ) { + if (cookieName.toLowerCase(Locale.ROOT).startsWith("_pk_id")) { + extractVisitorId(builder, cookies, cookieValue, cookieName); + } + if (cookieName.toLowerCase(Locale.ROOT).equalsIgnoreCase("MATOMO_SESSID")) { + builder.sessionId(cookieValue); + } + if (cookieName.toLowerCase(Locale.ROOT).startsWith("_pk_ses") + || cookieName.toLowerCase(Locale.ROOT).startsWith("_pk_ref") + || cookieName.toLowerCase(Locale.ROOT).startsWith("_pk_hsr")) { + cookies.put(cookieName, cookieValue); + } + if (cookieName.toLowerCase(Locale.ROOT).startsWith("_pk_cvar")) { + builder.visitCustomVariables(CustomVariables.parse(cookieValue)); + } + } + + private static void extractVisitorId( + @edu.umd.cs.findbugs.annotations.NonNull MatomoRequest.MatomoRequestBuilder builder, + @edu.umd.cs.findbugs.annotations.NonNull Map cookies, + @edu.umd.cs.findbugs.annotations.NonNull String cookieValue, + @edu.umd.cs.findbugs.annotations.NonNull String cookieName + ) { + String[] cookieValues = cookieValue.split("\\."); + if (cookieValues.length > 0) { + builder.visitorId(VisitorId.fromHex(cookieValues[0])).newVisitor(Boolean.FALSE); + cookies.put(cookieName, cookieValue); + } + } + + @edu.umd.cs.findbugs.annotations.NonNull + private static Map collectHeaders( + @edu.umd.cs.findbugs.annotations.NonNull HttpServletRequest request + ) { + Map headers = new HashMap<>(10); + Enumeration headerNames = request.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String headerName = headerNames.nextElement(); + if (headerName != null && !headerName.trim().isEmpty() && !RESTRICTED_HEADERS.contains(headerName.toLowerCase( + Locale.ROOT))) { + headers.put(headerName, request.getHeader(headerName)); + } + } + return headers; + } +} \ No newline at end of file diff --git a/servlet/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java b/servlet/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java new file mode 100644 index 00000000..c33ae2ba --- /dev/null +++ b/servlet/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java @@ -0,0 +1,62 @@ +package org.matomo.java.tracking; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.FilterHolder; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.junit.jupiter.api.Test; + +class MatomoTrackerFilterIT { + + @Test + void sendsAnAsyncRequestOnFilter() throws Exception { + + + TestSenderFactory senderFactory = new TestSenderFactory(); + + MatomoTracker tracker = new MatomoTracker( + TrackerConfiguration + .builder() + .apiEndpoint(URI.create("http://localhost:8080/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("ee6e3dd9ed1b61f5328cf5978b5a8c71") + .logFailedTracking(true) + .build()); + tracker.setSenderFactory(senderFactory); + + ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/"); + context.addFilter(new FilterHolder(new MatomoTrackerFilter(tracker)), "/*", null); + Server server = new Server(0); + server.setHandler(context); + + server.start(); + URI uri = server.getURI(); + HttpClient.newHttpClient().send( + HttpRequest.newBuilder() + .header("Accept-Language", "en-US,en;q=0.9,de;q=0.8") + .uri(uri) + .build(), + HttpResponse.BodyHandlers.discarding() + ); + server.stop(); + + TestSender testSender = senderFactory.getTestSender(); + assertThat(testSender.getRequests()).hasSize(1).satisfiesExactly(matomoRequest -> { + assertThat(matomoRequest.getActionUrl()).isEqualTo(uri.toString()); + assertThat(matomoRequest.getVisitorId()).isNotNull(); + assertThat(matomoRequest.getVisitorIp()).isNotNull(); + assertThat(matomoRequest.getHeaders()).containsEntry( + "Accept-Language", + "en-US,en;q=0.9,de;q=0.8" + ); + }); + + } + +} \ No newline at end of file diff --git a/core/src/test/java/org/matomo/java/tracking/MockHttpServletRequest.java b/servlet/src/test/java/org/matomo/java/tracking/MockHttpServletRequest.java similarity index 96% rename from core/src/test/java/org/matomo/java/tracking/MockHttpServletRequest.java rename to servlet/src/test/java/org/matomo/java/tracking/MockHttpServletRequest.java index bd73d135..fff6be6a 100644 --- a/core/src/test/java/org/matomo/java/tracking/MockHttpServletRequest.java +++ b/servlet/src/test/java/org/matomo/java/tracking/MockHttpServletRequest.java @@ -31,6 +31,8 @@ class MockHttpServletRequest implements HttpServletRequest { private Map headers; + private Collection cookies; + @Override public String getAuthType() { return null; @@ -38,7 +40,7 @@ public String getAuthType() { @Override public Cookie[] getCookies() { - return new Cookie[0]; + return cookies == null ? null : cookies.toArray(new Cookie[0]); } @Override @@ -58,7 +60,7 @@ public Enumeration getHeaders(String name) { @Override public Enumeration getHeaderNames() { - return Collections.enumeration(headers.keySet()); + return headers == null ? Collections.emptyEnumeration() : Collections.enumeration(headers.keySet()); } @Override diff --git a/servlet/src/test/java/org/matomo/java/tracking/ServletMatomoRequestTest.java b/servlet/src/test/java/org/matomo/java/tracking/ServletMatomoRequestTest.java new file mode 100644 index 00000000..c19d42a2 --- /dev/null +++ b/servlet/src/test/java/org/matomo/java/tracking/ServletMatomoRequestTest.java @@ -0,0 +1,134 @@ +package org.matomo.java.tracking; + +import static java.util.Collections.singleton; +import static java.util.Collections.singletonMap; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import jakarta.servlet.http.Cookie; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class ServletMatomoRequestTest { + + @Test + void addsServletRequestHeaders() { + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setHeaders(singletonMap("headerName", "headerValue")); + + MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); + + MatomoRequest matomoRequest = builder.build(); + assertThat(matomoRequest.getHeaders()).hasSize(1).containsEntry("headerName", "headerValue"); + } + + @Test + void skipsEmptyHeaderNames() { + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setHeaders(singletonMap("", "headerValue")); + + MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); + + MatomoRequest matomoRequest = builder.build(); + assertThat(matomoRequest.getHeaders()).isEmpty(); + + } + + @Test + void skipsBlankHeaderNames() { + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setHeaders(singletonMap(" ", "headerValue")); + + MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); + + MatomoRequest matomoRequest = builder.build(); + assertThat(matomoRequest.getHeaders()).isEmpty(); + + } + + @ParameterizedTest + @ValueSource(strings = {"connection", "content-length", "expect", "host", "upgrade"}) + void doesNotAddRestrictedHeaders(String restrictedHeader) { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setHeaders(singletonMap(restrictedHeader, "headerValue")); + + MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); + + MatomoRequest matomoRequest = builder.build(); + assertThat(matomoRequest.getHeaders()).isEmpty(); + } + + @Test + void failsIfServletRequestIsNull() { + assertThatThrownBy(() -> ServletMatomoRequest.fromServletRequest(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("request is marked non-null but is null"); + } + + @Test + void failsIfBuilderIsNull() { + assertThatThrownBy(() -> ServletMatomoRequest.addServletRequestHeaders(null, new MockHttpServletRequest())) + .isInstanceOf(NullPointerException.class) + .hasMessage("builder is marked non-null but is null"); + } + + @Test + void extractsVisitorIdFromCookie() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setCookies(singleton(new Cookie("_pk_id.1.1fff", "be40d677d6c7270b.1699801331."))); + + MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); + + MatomoRequest matomoRequest = builder.build(); + assertThat(matomoRequest.getVisitorId()).hasToString("be40d677d6c7270b"); + assertThat(matomoRequest.getCookies()).hasSize(1).containsEntry("_pk_id.1.1fff", "be40d677d6c7270b.1699801331."); + } + + @ParameterizedTest + @ValueSource( + strings = {"_pk_ses.1.1fff", "_pk_ref.1.1fff", "_pk_hsr.1.1fff"} + ) + void extractsMatomoCookies(String cookieName) { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setCookies(singleton(new Cookie(cookieName, "anything"))); + + MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); + + MatomoRequest matomoRequest = builder.build(); + assertThat(matomoRequest.getCookies()).hasSize(1).containsEntry(cookieName, "anything"); + } + + @Test + void extractsSessionIdFromMatomoSessIdCookie() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setCookies(singleton(new Cookie("MATOMO_SESSID", "2cbf8b5ba00fbf9ba70853308cd0944a"))); + + MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); + + MatomoRequest matomoRequest = builder.build(); + assertThat(matomoRequest.getSessionId()).isEqualTo("2cbf8b5ba00fbf9ba70853308cd0944a"); + } + + @Test + void parsesVisitCustomVariablesFromCookie() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setCookies(singleton(new Cookie( + "_pk_cvar.1.1fff", + "{\"1\":[\"VAR 1 set, var 2 not set\",\"yes\"],\"3\":[\"var 3 set\",\"yes!!!!\"]}" + ))); + + MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); + + MatomoRequest matomoRequest = builder.build(); + assertThat(matomoRequest.getVisitCustomVariables().get(1).getKey()).isEqualTo("VAR 1 set, var 2 not set"); + assertThat(matomoRequest.getVisitCustomVariables().get(1).getValue()).isEqualTo("yes"); + assertThat(matomoRequest.getVisitCustomVariables().get(3).getKey()).isEqualTo("var 3 set"); + assertThat(matomoRequest.getVisitCustomVariables().get(3).getValue()).isEqualTo("yes!!!!"); + + } + +} \ No newline at end of file diff --git a/servlet/src/test/java/org/matomo/java/tracking/TestSender.java b/servlet/src/test/java/org/matomo/java/tracking/TestSender.java new file mode 100644 index 00000000..d6c03833 --- /dev/null +++ b/servlet/src/test/java/org/matomo/java/tracking/TestSender.java @@ -0,0 +1,54 @@ +package org.matomo.java.tracking; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * A {@link Sender} implementation that does not send anything but stores the requests. + * + *

This class is intended for testing purposes only. It does not send anything to the Matomo server. Instead, it + * stores the requests and queries in collections that can be accessed via {@link #getRequests()}. + */ +@RequiredArgsConstructor +@Getter +class TestSender implements Sender { + + private final Collection requests = new ArrayList<>(); + + private final TrackerConfiguration trackerConfiguration; + + private final QueryCreator queryCreator; + + @NonNull + @Override + public CompletableFuture sendSingleAsync(@NonNull MatomoRequest request) { + requests.add(request); + return CompletableFuture.completedFuture(request); + } + + @Override + public void sendSingle(@NonNull MatomoRequest request) { + throw new UnsupportedOperationException(); + } + + @Override + public void sendBulk( + @NonNull Iterable requests, @Nullable String overrideAuthToken + ) { + throw new UnsupportedOperationException(); + } + + @NonNull + @Override + public CompletableFuture sendBulkAsync( + @NonNull Iterable requests, @Nullable String overrideAuthToken + ) { + throw new UnsupportedOperationException(); + } + +} diff --git a/servlet/src/test/java/org/matomo/java/tracking/TestSenderFactory.java b/servlet/src/test/java/org/matomo/java/tracking/TestSenderFactory.java new file mode 100644 index 00000000..fafafa90 --- /dev/null +++ b/servlet/src/test/java/org/matomo/java/tracking/TestSenderFactory.java @@ -0,0 +1,16 @@ +package org.matomo.java.tracking; + +import lombok.Getter; + +class TestSenderFactory implements SenderFactory { + + @Getter + private TestSender testSender; + + @Override + public Sender createSender(TrackerConfiguration trackerConfiguration, QueryCreator queryCreator) { + TestSender testSender = new TestSender(trackerConfiguration, queryCreator); + this.testSender = testSender; + return testSender; + } +} diff --git a/spring/pom.xml b/spring/pom.xml index 1a8a4700..0f2d3c67 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -17,40 +17,46 @@ 17 17 - 3.1.5 org.piwik.java.tracking matomo-java-tracker-java17 - 3.0.0-SNAPSHOT + ${project.version} + + + org.piwik.java.tracking + matomo-java-tracker-servlet + ${project.version} + + + jakarta.servlet + jakarta.servlet-api + provided org.springframework.boot spring-boot-starter - ${spring-boot.version} org.springframework.boot spring-boot-autoconfigure-processor - ${spring-boot.version} true org.springframework.boot spring-boot-configuration-processor - ${spring-boot.version} true org.projectlombok lombok + provided org.springframework.boot spring-boot-starter-test - ${spring-boot.version} test diff --git a/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java b/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java index ad758bd6..3529e537 100644 --- a/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java +++ b/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java @@ -10,12 +10,15 @@ import java.net.URI; import java.util.List; import org.matomo.java.tracking.MatomoTracker; +import org.matomo.java.tracking.MatomoTrackerFilter; import org.matomo.java.tracking.TrackerConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; +import org.springframework.lang.NonNull; /** * {@link AutoConfiguration Auto configuration} for Matomo Tracker. @@ -39,8 +42,9 @@ public class MatomoTrackerAutoConfiguration { */ @Bean @ConditionalOnMissingBean + @NonNull public TrackerConfiguration.TrackerConfigurationBuilder trackerConfigurationBuilder( - List customizers + @NonNull List customizers ) { TrackerConfiguration.TrackerConfigurationBuilder builder = TrackerConfiguration.builder(); customizers.forEach(customizer -> customizer.customize(builder)); @@ -61,6 +65,7 @@ public TrackerConfiguration.TrackerConfigurationBuilder trackerConfigurationBuil */ @Bean @ConditionalOnMissingBean + @NonNull public TrackerConfiguration trackerConfiguration( TrackerConfiguration.TrackerConfigurationBuilder builder ) { @@ -77,8 +82,9 @@ public TrackerConfiguration trackerConfiguration( * @see TrackerConfiguration.TrackerConfigurationBuilder */ @Bean + @NonNull public StandardTrackerConfigurationBuilderCustomizer standardTrackerConfigurationBuilderCustomizer( - MatomoTrackerProperties properties + @NonNull MatomoTrackerProperties properties ) { return new StandardTrackerConfigurationBuilderCustomizer(properties); } @@ -94,8 +100,28 @@ public StandardTrackerConfigurationBuilderCustomizer standardTrackerConfiguratio */ @Bean @ConditionalOnMissingBean - public MatomoTracker matomoTracker(TrackerConfiguration trackerConfiguration) { + @NonNull + public MatomoTracker matomoTracker(@NonNull TrackerConfiguration trackerConfiguration) { return new MatomoTracker(trackerConfiguration); } + /** + * A {@link FilterRegistrationBean} for the {@link MatomoTrackerFilter}. + * + *

Only created if a bean of the same type is not already configured. The filter is only registered if + * {@code matomo.filter.enabled} is set to {@code true}. + * + * @param matomoTracker the {@link MatomoTracker} instance (never {@code null}) + * @return the {@link FilterRegistrationBean} instance (never {@code null}) + */ + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty(value = "matomo.filter.enabled", havingValue = "true") + @NonNull + public FilterRegistrationBean matomoTrackerSpringFilter( + @NonNull MatomoTracker matomoTracker + ) { + return new FilterRegistrationBean<>(new MatomoTrackerFilter(matomoTracker)); + } + } diff --git a/test/pom.xml b/test/pom.xml index 4f012104..1aff5130 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,15 +28,28 @@ matomo-java-tracker-java17 ${project.version} + + org.piwik.java.tracking + matomo-java-tracker-servlet + ${project.version} + com.github.javafaker javafaker 1.0.2 + + org.projectlombok + lombok + provided + org.slf4j slf4j-simple - ${slf4j.version} + + + org.eclipse.jetty + jetty-servlet diff --git a/test/src/main/java/org/matomo/java/tracking/test/BulkExample.java b/test/src/main/java/org/matomo/java/tracking/test/BulkExample.java new file mode 100644 index 00000000..104ac611 --- /dev/null +++ b/test/src/main/java/org/matomo/java/tracking/test/BulkExample.java @@ -0,0 +1,49 @@ +package org.matomo.java.tracking.test; + +import java.net.URI; +import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoTracker; +import org.matomo.java.tracking.TrackerConfiguration; +import org.matomo.java.tracking.parameters.VisitorId; + +/** + * Example for sending multiple requests in one bulk request. + */ +public class BulkExample { + + /** + * Example for sending multiple requests in one bulk request. + * + * @param args ignored + */ + public static void main(String[] args) { + + TrackerConfiguration configuration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://www.yourdomain.com/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("ee6e3dd9ed1b61f5328cf5978b5a8c71") + .logFailedTracking(true) + .build(); + + MatomoTracker tracker = new MatomoTracker(configuration); + + VisitorId visitorId = VisitorId.fromString("customer@mail.com"); + tracker.sendBulkRequestAsync( + MatomoRequest + .request() + .actionName("Checkout") + .actionUrl("https://www.yourdomain.com/checkout") + .visitorId(visitorId) + .build(), + MatomoRequest + .request() + .actionName("Payment") + .actionUrl("https://www.yourdomain.com/checkout") + .visitorId(visitorId) + .build() + ); + + } + +} diff --git a/test/src/main/java/org/matomo/java/tracking/test/MatomoServletTester.java b/test/src/main/java/org/matomo/java/tracking/test/MatomoServletTester.java new file mode 100644 index 00000000..5681c5c4 --- /dev/null +++ b/test/src/main/java/org/matomo/java/tracking/test/MatomoServletTester.java @@ -0,0 +1,42 @@ +package org.matomo.java.tracking.test; + +import java.net.URI; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.DefaultServlet; +import org.eclipse.jetty.servlet.FilterHolder; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.matomo.java.tracking.MatomoTracker; +import org.matomo.java.tracking.MatomoTrackerFilter; +import org.matomo.java.tracking.TrackerConfiguration; + +@Slf4j +class MatomoServletTester { + public static void main(String[] args) throws Exception { + + ServletHolder servletHolder = new ServletHolder("default", new DefaultServlet()); + servletHolder.setInitParameter( + "resourceBase", + MatomoServletTester.class.getClassLoader().getResource("web").toExternalForm() + ); + + ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/"); + context.addServlet(servletHolder, "/"); + context.addFilter(new FilterHolder(new MatomoTrackerFilter(new MatomoTracker( + TrackerConfiguration + .builder() + .apiEndpoint(URI.create("http://localhost:8080/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("ee6e3dd9ed1b61f5328cf5978b5a8c71") + .logFailedTracking(true) + .build()))), "/*", null); + + Server server = new Server(8090); + server.setHandler(context); + server.start(); + server.join(); + + } +} \ No newline at end of file diff --git a/test/src/main/java/org/matomo/java/tracking/MatomoJavaTrackerTest.java b/test/src/main/java/org/matomo/java/tracking/test/MatomoTrackerTester.java similarity index 87% rename from test/src/main/java/org/matomo/java/tracking/MatomoJavaTrackerTest.java rename to test/src/main/java/org/matomo/java/tracking/test/MatomoTrackerTester.java index 50f6f648..8255d0ed 100644 --- a/test/src/main/java/org/matomo/java/tracking/MatomoJavaTrackerTest.java +++ b/test/src/main/java/org/matomo/java/tracking/test/MatomoTrackerTester.java @@ -1,4 +1,4 @@ -package org.matomo.java.tracking; +package org.matomo.java.tracking.test; import com.github.javafaker.Country; import com.github.javafaker.Faker; @@ -11,6 +11,10 @@ import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import java.util.stream.IntStream; +import lombok.extern.slf4j.Slf4j; +import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoTracker; +import org.matomo.java.tracking.TrackerConfiguration; import org.matomo.java.tracking.parameters.AcceptLanguage; import org.matomo.java.tracking.parameters.CustomVariable; import org.matomo.java.tracking.parameters.CustomVariables; @@ -20,14 +24,15 @@ import org.matomo.java.tracking.parameters.UniqueId; import org.matomo.java.tracking.parameters.VisitorId; -class MatomoJavaTrackerTest { +@Slf4j +class MatomoTrackerTester { private final MatomoTracker tracker; private final Faker faker = new Faker(); private final List vistors = new ArrayList<>(5); - MatomoJavaTrackerTest(TrackerConfiguration configuration) { + MatomoTrackerTester(TrackerConfiguration configuration) { tracker = new MatomoTracker(configuration); for (int i = 0; i < 5; i++) { vistors.add(VisitorId.random()); @@ -44,38 +49,38 @@ public static void main(String[] args) { .logFailedTracking(true) .build(); - MatomoJavaTrackerTest matomoJavaTrackerTest = new MatomoJavaTrackerTest(configuration); + MatomoTrackerTester matomoTrackerTester = new MatomoTrackerTester(configuration); - matomoJavaTrackerTest.sendRequestAsync(); - matomoJavaTrackerTest.sendBulkRequestsAsync(); - matomoJavaTrackerTest.sendRequest(); - matomoJavaTrackerTest.sendBulkRequests(); + matomoTrackerTester.sendRequestAsync(); + matomoTrackerTester.sendBulkRequestsAsync(); + matomoTrackerTester.sendRequest(); + matomoTrackerTester.sendBulkRequests(); } private void sendRequest() { MatomoRequest request = randomRequest(); tracker.sendRequest(request); - System.out.printf("Successfully sent single request to Matomo server: %s%n", request); + log.info("Successfully sent single request to Matomo server: {}", request); } private void sendBulkRequests() { List requests = randomRequests(); tracker.sendBulkRequest(requests); - System.out.printf("Successfully sent bulk requests to Matomo server: %s%n", requests); + log.info("Successfully sent bulk requests to Matomo server: {}", requests); } private void sendRequestAsync() { MatomoRequest request = randomRequest(); CompletableFuture future = tracker.sendRequestAsync(request); - future.thenAccept(v -> System.out.printf("Successfully sent async single request to Matomo server: %s%n", request)); + future.thenAccept(v -> log.info("Successfully sent async single request to Matomo server: {}", request)); } private void sendBulkRequestsAsync() { List requests = randomRequests(); tracker .sendBulkRequestAsync(requests) - .thenAccept(v -> System.out.printf("Successfully sent async bulk requests to Matomo server: %s%n", requests)); + .thenAccept(v -> log.info("Successfully sent async bulk requests to Matomo server: {}", requests)); } private List randomRequests() { diff --git a/test/src/main/java/org/matomo/java/tracking/test/SendExample.java b/test/src/main/java/org/matomo/java/tracking/test/SendExample.java new file mode 100644 index 00000000..99e316d7 --- /dev/null +++ b/test/src/main/java/org/matomo/java/tracking/test/SendExample.java @@ -0,0 +1,41 @@ +package org.matomo.java.tracking.test; + +import java.net.URI; +import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoTracker; +import org.matomo.java.tracking.TrackerConfiguration; +import org.matomo.java.tracking.parameters.VisitorId; + +/** + * Example for sending a request. + */ +public class SendExample { + + /** + * Example for sending a request. + * + * @param args ignored + */ + public static void main(String[] args) { + + TrackerConfiguration configuration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://www.yourdomain.com/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("ee6e3dd9ed1b61f5328cf5978b5a8c71") + .logFailedTracking(true) + .build(); + + MatomoTracker tracker = new MatomoTracker(configuration); + + tracker.sendRequestAsync(MatomoRequest + .request() + .actionName("Checkout") + .actionUrl("https://www.yourdomain.com/checkout") + .visitorId(VisitorId.fromString("customer@mail.com")) + .build() + ); + + } + +} diff --git a/test/src/main/java/org/matomo/java/tracking/test/ServletMatomoRequestExample.java b/test/src/main/java/org/matomo/java/tracking/test/ServletMatomoRequestExample.java new file mode 100644 index 00000000..8e1c9547 --- /dev/null +++ b/test/src/main/java/org/matomo/java/tracking/test/ServletMatomoRequestExample.java @@ -0,0 +1,34 @@ +package org.matomo.java.tracking.test; + +import jakarta.servlet.http.HttpServletRequest; +import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoTracker; +import org.matomo.java.tracking.ServletMatomoRequest; + +/** + * This is an example of how to use the ServletMatomoRequest class. + */ +public class ServletMatomoRequestExample { + + private final MatomoTracker tracker; + + public ServletMatomoRequestExample(MatomoTracker tracker) { + this.tracker = tracker; + } + + /** + * Example for sending a request from a servlet request. + * + * @param req the servlet request + */ + public void someControllerMethod(HttpServletRequest req) { + MatomoRequest matomoRequest = ServletMatomoRequest + .fromServletRequest(req) + .actionName("Some Controller Action") + // ... + .build(); + tracker.sendRequestAsync(matomoRequest); + // ... + } + +} diff --git a/test/src/main/resources/simplelogger.properties b/test/src/main/resources/simplelogger.properties new file mode 100644 index 00000000..835f39d5 --- /dev/null +++ b/test/src/main/resources/simplelogger.properties @@ -0,0 +1,2 @@ +org.slf4j.simpleLogger.defaultLogLevel=debug +org.slf4j.simpleLogger.log.org.eclipse.jetty=info \ No newline at end of file diff --git a/test/src/main/resources/web/track.html b/test/src/main/resources/web/track.html new file mode 100644 index 00000000..508b59ac --- /dev/null +++ b/test/src/main/resources/web/track.html @@ -0,0 +1,11 @@ + + + + + + Matomo Java Tracker Servlet Test + + +Thank you! Your request was sent to Matomo at http://localhost:8080. + + \ No newline at end of file From fac4272f712c5f49f9fb89c3ade408cc94615ab5 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 13 Nov 2023 00:49:36 +0100 Subject: [PATCH 186/467] Closes #131 --- .../matomo/java/tracking/MatomoRequest.java | 360 ++++++++++++------ 1 file changed, 236 insertions(+), 124 deletions(-) diff --git a/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java index 9bced43e..bd3be8f4 100644 --- a/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -54,22 +54,25 @@ public class MatomoRequest { /** - * The ID of the website we're tracking a visit/action for. Only needed, if no default site id is configured + * The ID of the website we're tracking a visit/action for. Only needed, if no default site id is + * configured */ @TrackingParameter(name = "rec") @Default private Boolean required = true; /** - * The ID of the website we're tracking a visit/action for. Only needed, if no default site id is configured + * The ID of the website we're tracking a visit/action for. Only needed, if no default site id is + * configured */ @TrackingParameter(name = "idsite") private Integer siteId; /** - * The title of the action being tracked. For page tracks this is used as page title. If enabled in your installation - * you may use the category tree structure in this field. For example, "game / register new user" would then create a - * group "game" and add the item "register new user" in it. + * The title of the action being tracked. For page tracks this is used as page title. If enabled + * in your installation you may use the category tree structure in this field. For example, "game + * / register new user" would then create a group "game" and add the item "register new user" in + * it. */ @TrackingParameter(name = "action_name") private String actionName; @@ -108,36 +111,38 @@ public class MatomoRequest { private Boolean newVisitor; /** - * The full HTTP Referrer URL. This value is used to determine how someone got to your website (ie, through a website, - * search engine or campaign) + * The full HTTP Referrer URL. This value is used to determine how someone got to your website + * (ie, through a website, search engine or campaign) */ @TrackingParameter(name = "urlref") private String referrerUrl; /** - * Custom variables are custom name-value pairs that you can assign to your visitors (or page views). + * Custom variables are custom name-value pairs that you can assign to your visitors (or page + * views). */ @TrackingParameter(name = "_cvar") private CustomVariables visitCustomVariables; /** - * The current count of visits for this visitor. To set this value correctly, it would be required to store the value - * for each visitor in your application (using sessions or persisting in a database). Then you would manually - * increment the counts by one on each new visit or "session", depending on how you choose to define a visit. + * The current count of visits for this visitor. To set this value correctly, it would be required + * to store the value for each visitor in your application (using sessions or persisting in a + * database). Then you would manually increment the counts by one on each new visit or "session", + * depending on how you choose to define a visit. */ @TrackingParameter(name = "_idvc") private Integer visitorVisitCount; /** - * The UNIX timestamp of this visitor's previous visit. This parameter is used to populate the report Visitors > - * Engagement > Visits by days since last visit. + * The UNIX timestamp of this visitor's previous visit. This parameter is used to populate the + * report Visitors > Engagement > Visits by days since last visit. */ @TrackingParameter(name = "_viewts") private Instant visitorPreviousVisitTimestamp; /** - * The UNIX timestamp of this visitor's first visit. This could be set to the date where the user first started using - * your software/app, or when he/she created an account. + * The UNIX timestamp of this visitor's first visit. This could be set to the date where the user + * first started using your software/app, or when he/she created an account. */ @TrackingParameter(name = "_idts") private Instant visitorFirstVisitTimestamp; @@ -150,8 +155,10 @@ public class MatomoRequest { /** * The campaign keyword (see - * Tracking Campaigns). Used to populate the Referrers > Campaigns report (clicking on a - * campaign loads all keywords for this campaign). This parameter will only be used for the first pageview of a visit. + * Tracking Campaigns). Used to + * populate + * the Referrers > Campaigns report (clicking on a campaign loads all keywords for + * this campaign). This parameter will only be used for the first pageview of a visit. */ @TrackingParameter(name = "_rck") private String campaignKeyword; @@ -247,17 +254,18 @@ public class MatomoRequest { private String headerUserAgent; /** - * An override value for the Accept-Language HTTP header field. This value is used to detect the visitor's country if - * GeoIP is not enabled. + * An override value for the Accept-Language HTTP header field. This value is used to detect the + * visitor's country if GeoIP is not enabled. */ @TrackingParameter(name = "lang") private AcceptLanguage headerAcceptLanguage; /** - * Defines the User ID for this request. User ID is any non-empty unique string identifying the user (such as an email - * address or a username). When specified, the User ID will be "enforced". This means that if there is no recent - * visit with this User ID, a new one will be created. If a visit is found in the last 30 minutes with your specified - * User ID, then the new action will be recorded to this existing visit. + * Defines the User ID for this request. User ID is any non-empty unique string identifying the + * user (such as an email address or a username). When specified, the User ID will be "enforced". + * This means that if there is no recent visit with this User ID, a new one will be created. If a + * visit is found in the last 30 minutes with your specified User ID, then the new action will be + * recorded to this existing visit. */ @TrackingParameter(name = "uid") private String userId; @@ -275,28 +283,29 @@ public class MatomoRequest { private Boolean newVisit; /** - * Custom variables are custom name-value pairs that you can assign to your visitors (or page views). + * Custom variables are custom name-value pairs that you can assign to your visitors (or page + * views). */ @TrackingParameter(name = "cvar") private CustomVariables pageCustomVariables; /** - * An external URL the user has opened. Used for tracking outlink clicks. We recommend to also set the url parameter - * to this same value. + * An external URL the user has opened. Used for tracking outlink clicks. We recommend to also set + * the url parameter to this same value. */ @TrackingParameter(name = "link") private String outlinkUrl; /** - * URL of a file the user has downloaded. Used for tracking downloads. We recommend to also set the url parameter to - * this same value. + * URL of a file the user has downloaded. Used for tracking downloads. We recommend to also set + * the url parameter to this same value. */ @TrackingParameter(name = "download") private String downloadUrl; /** - * The Site Search keyword. When specified, the request will not be tracked as a normal pageview but will instead be - * tracked as a Site Search request + * The Site Search keyword. When specified, the request will not be tracked as a normal pageview + * but will instead be tracked as a Site Search request */ @TrackingParameter(name = "search") private String searchQuery; @@ -308,25 +317,25 @@ public class MatomoRequest { private String searchCategory; /** - * When search is specified, we also recommend setting the search_count to the number of search results displayed on - * the results page. When keywords are tracked with &search_count=0 they will appear in the "No Result Search Keyword" - * report. + * When search is specified, we also recommend setting the search_count to the number of search + * results displayed on the results page. When keywords are tracked with &search_count=0 they will + * appear in the "No Result Search Keyword" report. */ @TrackingParameter(name = "search_count") private Long searchResultsCount; /** - * Accepts a six character unique ID that identifies which actions were performed on a specific page view. When a page - * was viewed, all following tracking requests (such as events) during that page view should use the same pageview ID. - * Once another page was viewed a new unique ID should be generated. Use [0-9a-Z] as possible characters for the - * unique ID. + * Accepts a six character unique ID that identifies which actions were performed on a specific + * page view. When a page was viewed, all following tracking requests (such as events) during that + * page view should use the same pageview ID. Once another page was viewed a new unique ID should + * be generated. Use [0-9a-Z] as possible characters for the unique ID. */ @TrackingParameter(name = "pv_id") private UniqueId pageViewId; /** - * If specified, the tracking request will trigger a conversion for the goal of the website being tracked with this - * ID. + * If specified, the tracking request will trigger a conversion for the goal of the website being + * tracked with this ID. */ @TrackingParameter(name = "idgoal") private Integer goalId; @@ -338,17 +347,17 @@ public class MatomoRequest { private Double ecommerceRevenue; /** - * The charset of the page being tracked. Specify the charset if the data you send to Matomo is encoded in a different - * character set than the default utf-8 + * The charset of the page being tracked. Specify the charset if the data you send to Matomo is + * encoded in a different character set than the default utf-8 */ @TrackingParameter(name = "cs") private Charset characterSet; /** - * can be optionally sent along any tracking request that isn't a page view. For example, it can be sent together with - * an event tracking request. The advantage being that should you ever disable the event plugin, then the event - * tracking requests will be ignored vs if the parameter is not set, a page view would be tracked even though it isn't - * a page view. + * can be optionally sent along any tracking request that isn't a page view. For example, it can + * be sent together with an event tracking request. The advantage being that should you ever + * disable the event plugin, then the event tracking requests will be ignored vs if the parameter + * is not set, a page view would be tracked even though it isn't a page view. */ @TrackingParameter(name = "ca") private Boolean customAction; @@ -372,15 +381,15 @@ public class MatomoRequest { private Long transferTime; /** - * How long the browser spends loading the webpage after the response was fully received until the user can start - * interacting with it. + * How long the browser spends loading the webpage after the response was fully received until the + * user can start interacting with it. */ @TrackingParameter(name = "pf_dm1") private Long domProcessingTime; /** - * How long it takes for the browser to load media and execute any Javascript code listening for the DOMContentLoaded - * event. + * How long it takes for the browser to load media and execute any Javascript code listening for + * the DOMContentLoaded event. */ @TrackingParameter(name = "pf_dm2") private Long domCompletionTime; @@ -440,8 +449,9 @@ public class MatomoRequest { private String contentInteraction; /** - * he unique string identifier for the ecommerce order (required when tracking an ecommerce order). you must set - * &idgoal=0 in the request to track an ecommerce interaction: cart update or an ecommerce order. + * he unique string identifier for the ecommerce order (required when tracking an ecommerce + * order). you must set &idgoal=0 in the request to track an ecommerce interaction: cart update or + * an ecommerce order. */ @TrackingParameter(name = "ec_id") private String ecommerceId; @@ -477,15 +487,16 @@ public class MatomoRequest { private Double ecommerceDiscount; /** - * The UNIX timestamp of this customer's last ecommerce order. This value is used to process the "Days since last - * order" report. + * The UNIX timestamp of this customer's last ecommerce order. This value is used to process the + * "Days since last order" report. */ @TrackingParameter(name = "_ects") private Instant ecommerceLastOrderTimestamp; /** - * 32 character authorization key used to authenticate the API request. We recommend to create a user specifically for - * accessing the Tracking API, and give the user only write permission on the website(s). + * 32 character authorization key used to authenticate the API request. We recommend to create a + * user specifically for accessing the Tracking API, and give the user only write permission on + * the website(s). */ @TrackingParameter( name = "token_auth", @@ -501,8 +512,8 @@ public class MatomoRequest { private String visitorIp; /** - * Override for the datetime of the request (normally the current time is used). This can be used to record visits and - * page views in the past. + * Override for the datetime of the request (normally the current time is used). This can be used + * to record visits and page views in the past. */ @TrackingParameter(name = "cdt") private Instant requestTimestamp; @@ -517,8 +528,8 @@ public class MatomoRequest { private Country visitorCountry; /** - * An override value for the region. Should be set to a ISO 3166-2 region code, which are used by MaxMind's and - * DB-IP's GeoIP2 databases. See here for a list of them for every country. + * An override value for the region. Should be set to a ISO 3166-2 region code, which are used by + * MaxMind's and DB-IP's GeoIP2 databases. See here for a list of them for every country. */ @TrackingParameter( name = "region", @@ -545,17 +556,17 @@ public class MatomoRequest { private Double visitorLongitude; /** - * When set to false, the queued tracking handler won't be used and instead the tracking request will be executed - * directly. This can be useful when you need to debug a tracking problem or want to test that the tracking works in - * general. + * When set to false, the queued tracking handler won't be used and instead the tracking request + * will be executed directly. This can be useful when you need to debug a tracking problem or want + * to test that the tracking works in general. */ @TrackingParameter(name = "queuedtracking") private Boolean queuedTracking; /** - * If set to 0 (send_image=0) Matomo will respond with an HTTP 204 response code instead of a GIF image. This improves - * performance and can fix errors if images are not allowed to be obtained directly (like Chrome Apps). Available - * since Matomo 2.10.0 + * If set to 0 (send_image=0) Matomo will respond with an HTTP 204 response code instead of a GIF + * image. This improves performance and can fix errors if images are not allowed to be obtained + * directly (like Chrome Apps). Available since Matomo 2.10.0 * *

Default is {@code false} */ @@ -564,79 +575,158 @@ public class MatomoRequest { private Boolean responseAsImage = false; /** - * If set to true, the request will be a Heartbeat request which will not track any new activity (such as a new visit, - * new action or new goal). The heartbeat request will only update the visit's total time to provide accurate "Visit - * duration" metric when this parameter is set. It won't record any other data. This means by sending an additional - * tracking request when the user leaves your site or app with &ping=1, you fix the issue where the time spent of the - * last page visited is reported as 0 seconds. + * If set to true, the request will be a Heartbeat request which will not track any new activity + * (such as a new visit, new action or new goal). The heartbeat request will only update the + * visit's total time to provide accurate "Visit duration" metric when this parameter is set. It + * won't record any other data. This means by sending an additional tracking request when the user + * leaves your site or app with &ping=1, you fix the issue where the time spent of the last page + * visited is reported as 0 seconds. */ @TrackingParameter(name = "ping") private Boolean ping; /** - * By default, Matomo does not track bots. If you use the Tracking HTTP API directly, you may be interested in - * tracking bot requests. + * By default, Matomo does not track bots. If you use the Tracking HTTP API directly, you may be + * interested in tracking bot requests. */ @TrackingParameter(name = "bots") private Boolean trackBotRequests; /** - * Meant to hold a random value that is generated before each request. Using it helps avoid the tracking request - * being cached by the browser or a proxy. + * Meant to hold a random value that is generated before each request. Using it helps avoid the + * tracking request being cached by the browser or a proxy. */ @TrackingParameter(name = "rand") @Default private RandomValue randomValue = RandomValue.random(); - /** - * Meant to hold a random value that is generated before each request. Using it helps avoid the tracking request - * being cached by the browser or a proxy. + * Meant to hold a random value that is generated before each request. Using it helps avoid the + * tracking request being cached by the browser or a proxy. */ @TrackingParameter(name = "debug") private Boolean debug; + /** + * Contains an error message describing the error that occurred during the last tracking request. + * + *

Custom action must be enabled for this. + * + *

Required for crash analytics + */ + @TrackingParameter(name = "cra") + private String crashMessage; + + /** + * The type of exception that occurred during the last tracking request. + * + *

Custom action must be enabled for this. + * + *

Typically a fully qualified class name of the exception, e.g. + * {@code java.lang.NullPointerException}. + * + *

Optional for crash analytics + */ + @TrackingParameter(name = "cra_tp") + private String crashType; + + /** + * Category of a crash to group crashes by. + * + *

Custom action must be enabled for this. + * + *

Optional for crash analytics + */ + @TrackingParameter(name = "cra_ct") + private String crashCategory; + + /** + * A stack trace of the exception that occurred during the last tracking request. + * + *

Custom action must be enabled for this. + * + *

Optional for crash analytics + */ + @TrackingParameter(name = "cra_st") + private String crashStackTrace; + + /** + * The originating source of the crash. + * + *

Could be a source file URI or something similar + * + *

Custom action must be enabled for this. + * + *

Optional for crash analytics + */ + @TrackingParameter(name = "cra_ru") + private String crashLocation; + + /** + * The line number of the crash source, where the crash occurred. + * + *

Custom action must be enabled for this. + * + *

Optional for crash analytics + */ + @TrackingParameter(name = "cra_rl") + private Integer crashLine; + + /** + * The column within the line where the crash occurred. + * + *

Optional for crash analytics + */ + @TrackingParameter(name = "cra_rc") + private Integer crashColumn; + /** * The Matomo session ID sent as a cookie {@code MATOMO_SESSID}. * - *

If not null a cookie with the name {@code MATOMO_SESSID} will be sent with the value of this parameter. + *

If not null a cookie with the name {@code MATOMO_SESSID} will be sent with the value of + * this + * parameter. */ private String sessionId; /** * Custom Dimension values for specific Custom Dimension IDs. * - *

Custom Dimensions plugin must be installed. See the - * Custom Dimensions guide. Requires Matomo at least 2.15.1 + *

Custom Dimensions plugin must be + * installed. See the + * Custom Dimensions guide. Requires + * Matomo at least 2.15.1 */ private Map dimensions; /** * Allows you to specify additional HTTP request parameters that will be sent to Matomo. * - *

For example, you can use this to set the Accept-Language header, or to set the Content-Type. + *

For example, you can use this to set the Accept-Language header, or to set the + * Content-Type. */ private Map> additionalParameters; /** * You can set additional HTTP headers for the request sent to Matomo. * - *

For example, you can use this to set the Accept-Language header, or to set the Content-Type. + *

For example, you can use this to set the Accept-Language header, or to set the + * Content-Type. */ private Map headers; /** * Appends additional cookies to the request. * - *

This allows you to add Matomo specific cookies, like {@code _pk_id} or {@code _pk_sess} coming from Matomo - * responses to the request. + *

This allows you to add Matomo specific cookies, like {@code _pk_id} or {@code _pk_sess} + * coming from Matomo responses to the request. */ private Map cookies; /** - * Create a new request from the id of the site being tracked and the full - * url for the current action. This constructor also sets: + * Create a new request from the id of the site being tracked and the full url for the current + * action. This constructor also sets: *

    * {@code
    * Required = true
@@ -650,6 +740,7 @@ public class MatomoRequest {
    *
    * @param siteId    the id of the website we're tracking a visit/action for
    * @param actionUrl the full URL for the current action
+   *
    * @deprecated Please use {@link MatomoRequest#request()}
    */
   @Deprecated
@@ -664,11 +755,11 @@ public MatomoRequest(int siteId, String actionUrl) {
   }
 
   /**
-   * Gets the list of objects currently stored at the specified custom tracking
-   * parameter.  An empty list will be returned if there are no objects set at
-   * that key.
+   * Gets the list of objects currently stored at the specified custom tracking parameter.  An empty
+   * list will be returned if there are no objects set at that key.
    *
    * @param key the key of the parameter whose list of objects to get.  Cannot be null
+   *
    * @return the list of objects currently stored at the specified key
    */
   public List getCustomTrackingParameter(@NonNull String key) {
@@ -683,14 +774,14 @@ public List getCustomTrackingParameter(@NonNull String key) {
   }
 
   /**
-   * Set a custom tracking parameter whose toString() value will be sent to
-   * the Matomo server. These parameters are stored separately from named Matomo
-   * parameters, meaning it is not possible to overwrite or clear named Matomo
-   * parameters with this method. A custom parameter that has the same name
-   * as a named Matomo parameter will be sent in addition to that named parameter.
+   * Set a custom tracking parameter whose toString() value will be sent to the Matomo server. These
+   * parameters are stored separately from named Matomo parameters, meaning it is not possible to
+   * overwrite or clear named Matomo parameters with this method. A custom parameter that has the
+   * same name as a named Matomo parameter will be sent in addition to that named parameter.
    *
    * @param key   the parameter's key.  Cannot be null
    * @param value the parameter's value.  Removes the parameter if null
+   *
    * @deprecated Use {@link MatomoRequest.MatomoRequestBuilder#additionalParameters(Map)} instead.
    */
   @Deprecated
@@ -713,12 +804,13 @@ public void setCustomTrackingParameter(
   }
 
   /**
-   * Add a custom tracking parameter to the specified key.  This allows users
-   * to have multiple parameters with the same name and different values,
-   * commonly used during situations where list parameters are needed
+   * Add a custom tracking parameter to the specified key.  This allows users to have multiple
+   * parameters with the same name and different values, commonly used during situations where list
+   * parameters are needed
    *
    * @param key   the parameter's key.  Cannot be null
    * @param value the parameter's value.  Cannot be null
+   *
    * @deprecated Use {@link MatomoRequest.MatomoRequestBuilder#additionalParameters(Map)} instead.
    */
   @Deprecated
@@ -732,8 +824,8 @@ public void addCustomTrackingParameter(@NonNull String key, @NonNull Object valu
   /**
    * Removes all custom tracking parameters.
    *
-   * @deprecated Please use {@link MatomoRequest.MatomoRequestBuilder#additionalParameters(Map)} instead so that you can
-   * manage the map yourself.
+   * @deprecated Please use {@link MatomoRequest.MatomoRequestBuilder#additionalParameters(Map)}
+   * instead so that you can manage the map yourself.
    */
   @Deprecated
   public void clearCustomTrackingParameter() {
@@ -741,8 +833,8 @@ public void clearCustomTrackingParameter() {
   }
 
   /**
-   * Sets idgoal=0 in the request to track an ecommerce interaction:
-   * cart update or an ecommerce order.
+   * Sets idgoal=0 in the request to track an ecommerce interaction: cart update or an
+   * ecommerce order.
    *
    * @deprecated Please use {@link MatomoRequest#setGoalId(Integer)} instead
    */
@@ -755,8 +847,10 @@ public void enableEcommerce() {
    * Get the {@link EcommerceItem} at the specified index.
    *
    * @param index the index of the {@link EcommerceItem} to return
+   *
    * @return the {@link EcommerceItem} at the specified index
-   * @deprecated Use @link {@link MatomoRequest.MatomoRequestBuilder#ecommerceItems(EcommerceItems)} instead
+   * @deprecated Use @link {@link MatomoRequest.MatomoRequestBuilder#ecommerceItems(EcommerceItems)}
+   * instead
    */
   @Nullable
   @Deprecated
@@ -768,11 +862,13 @@ public EcommerceItem getEcommerceItem(int index) {
   }
 
   /**
-   * Add an {@link EcommerceItem} to this order.  Ecommerce must be enabled,
-   * and EcommerceId and EcommerceRevenue must first be set.
+   * Add an {@link EcommerceItem} to this order.  Ecommerce must be enabled, and EcommerceId and
+   * EcommerceRevenue must first be set.
    *
    * @param item the {@link EcommerceItem} to add.  Cannot be null
-   * @deprecated Use @link {@link MatomoRequest.MatomoRequestBuilder#ecommerceItems(EcommerceItems)} instead
+   *
+   * @deprecated Use @link {@link MatomoRequest.MatomoRequestBuilder#ecommerceItems(EcommerceItems)}
+   * instead
    */
   @Deprecated
   public void addEcommerceItem(@NonNull EcommerceItem item) {
@@ -785,7 +881,8 @@ public void addEcommerceItem(@NonNull EcommerceItem item) {
   /**
    * Clears all {@link EcommerceItem} from this order.
    *
-   * @deprecated Use @link {@link MatomoRequest.MatomoRequestBuilder#ecommerceItems(EcommerceItems)} instead
+   * @deprecated Use @link {@link MatomoRequest.MatomoRequestBuilder#ecommerceItems(EcommerceItems)}
+   * instead
    */
   @Deprecated
   public void clearEcommerceItems() {
@@ -796,6 +893,7 @@ public void clearEcommerceItems() {
    * Get the page custom variable at the specified key.
    *
    * @param key the key of the variable to get
+   *
    * @return the variable at the specified key, null if key is not present
    * @deprecated Use the {@link #getPageCustomVariables()} method instead.
    */
@@ -812,6 +910,7 @@ public String getPageCustomVariable(String key) {
    * Get the page custom variable at the specified index.
    *
    * @param index the index of the variable to get.  Must be greater than 0
+   *
    * @return the variable at the specified key, null if nothing at this index
    * @deprecated Use {@link MatomoRequest#getPageCustomVariables()} instead
    */
@@ -831,11 +930,13 @@ private static CustomVariable getCustomVariable(CustomVariables customVariables,
   }
 
   /**
-   * Set a page custom variable with the specified key and value at the first available index.
-   * All page custom variables with this key will be overwritten or deleted
+   * Set a page custom variable with the specified key and value at the first available index. All
+   * page custom variables with this key will be overwritten or deleted
    *
    * @param key   the key of the variable to set
-   * @param value the value of the variable to set at the specified key.  A null value will remove this custom variable
+   * @param value the value of the variable to set at the specified key.  A null value will remove
+   *              this custom variable
+   *
    * @deprecated Use {@link MatomoRequest#getPageCustomVariables()} instead
    */
   @Deprecated
@@ -859,9 +960,10 @@ public void setPageCustomVariable(
   /**
    * Set a page custom variable at the specified index.
    *
-   * @param customVariable the CustomVariable to set.  A null value will remove the CustomVariable at the specified
-   *                       index
+   * @param customVariable the CustomVariable to set.  A null value will remove the CustomVariable
+   *                       at the specified index
    * @param index          the index of he CustomVariable to set
+   *
    * @deprecated Use {@link #getPageCustomVariables()} instead
    */
   @Deprecated
@@ -901,16 +1003,17 @@ public MatomoDate getRequestDatetime() {
   }
 
   /**
-   * Set the datetime of the request (normally the current time is used).
-   * This can be used to record visits and page views in the past. The datetime
-   * must be sent in UTC timezone. Note: if you record data in the past, you will
-   * need to force Matomo to re-process
-   * reports for the past dates. If you set the Request Datetime to a datetime
-   * older than four hours then Auth Token must be set. If you set
+   * Set the datetime of the request (normally the current time is used). This can be used to record
+   * visits and page views in the past. The datetime must be sent in UTC timezone. Note: if you
+   * record data in the past, you will need to force
+   * Matomo to re-process reports for the past dates. If you set the Request
+   * Datetime to a datetime older than four hours then Auth Token must be set. If you
+   * set
    * Request Datetime with a datetime in the last four hours then you
    * don't need to pass Auth Token.
    *
    * @param matomoDate the datetime of the request to set.  A null value will remove this parameter
+   *
    * @deprecated Use {@link #setRequestTimestamp(Instant)} instead
    */
   @Deprecated
@@ -927,6 +1030,7 @@ public void setRequestDatetime(MatomoDate matomoDate) {
    * Get the visit custom variable at the specified key.
    *
    * @param key the key of the variable to get
+   *
    * @return the variable at the specified key, null if key is not present
    * @deprecated Use the {@link #getVisitCustomVariables()} method instead.
    */
@@ -943,6 +1047,7 @@ public String getUserCustomVariable(String key) {
    * Get the visit custom variable at the specified index.
    *
    * @param index the index of the variable to get
+   *
    * @return the variable at the specified index, null if nothing at this index
    * @deprecated Use {@link #getVisitCustomVariables()} instead
    */
@@ -953,11 +1058,13 @@ public CustomVariable getVisitCustomVariable(int index) {
   }
 
   /**
-   * Set a visit custom variable with the specified key and value at the first available index.
-   * All visit custom variables with this key will be overwritten or deleted
+   * Set a visit custom variable with the specified key and value at the first available index. All
+   * visit custom variables with this key will be overwritten or deleted
    *
    * @param key   the key of the variable to set
-   * @param value the value of the variable to set at the specified key.  A null value will remove this parameter
+   * @param value the value of the variable to set at the specified key.  A null value will remove
+   *              this parameter
+   *
    * @deprecated Use {@link #setVisitCustomVariables(CustomVariables)} instead
    */
   @Deprecated
@@ -981,9 +1088,10 @@ public void setUserCustomVariable(
   /**
    * Set a user custom variable at the specified key.
    *
-   * @param customVariable the CustomVariable to set.  A null value will remove the custom variable at the specified
-   *                       index
+   * @param customVariable the CustomVariable to set.  A null value will remove the custom variable
+   *                       at the specified index
    * @param index          the index to set the customVariable at.
+   *
    * @deprecated Use {@link #setVisitCustomVariables(CustomVariables)} instead
    */
   @Deprecated
@@ -1005,8 +1113,11 @@ public void setVisitCustomVariable(
    * 

Attention: If a parameter with the same name already exists, it will be appended twice! * * @param parameterName The name of the query parameter to append. Must not be null or empty. - * @param value The value of the query parameter to append. To remove the parameter, pass null. - * @deprecated Use @link {@link MatomoRequest.MatomoRequestBuilder#additionalParameters(Map)} instead + * @param value The value of the query parameter to append. To remove the parameter, pass + * null. + * + * @deprecated Use @link {@link MatomoRequest.MatomoRequestBuilder#additionalParameters(Map)} + * instead */ @Deprecated public void setParameter(@NonNull String parameterName, Object value) { @@ -1040,6 +1151,7 @@ public static org.matomo.java.tracking.MatomoRequestBuilder builder() { * Parses the given device resolution string and sets the {@link #deviceResolution} field. * * @param deviceResolution the device resolution string to parse. Format: "WIDTHxHEIGHT" + * * @deprecated Use {@link #setDeviceResolution(DeviceResolution)} instead. */ @Tolerate From c99003f624971e5b74712703449e804ed39a4d6a Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 13 Nov 2023 07:20:53 +0000 Subject: [PATCH 187/467] [maven-release-plugin] prepare release matomo-java-tracker-parent-3.0.0 --- core/pom.xml | 2 +- java17/pom.xml | 7 +++---- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet/pom.xml | 7 +++---- spring/pom.xml | 2 +- test/pom.xml | 5 ++--- 7 files changed, 14 insertions(+), 17 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index e18418bb..fd5b7fdb 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0-SNAPSHOT + 3.0.0 ../pom.xml diff --git a/java17/pom.xml b/java17/pom.xml index c08e3eeb..b30250dd 100644 --- a/java17/pom.xml +++ b/java17/pom.xml @@ -1,16 +1,15 @@ - + 4.0.0 org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0-SNAPSHOT + 3.0.0 ../pom.xml matomo-java-tracker-java17 - 3.0.0-SNAPSHOT + 3.0.0 jar Matomo Java Tracker Java 17 diff --git a/java8/pom.xml b/java8/pom.xml index 79d03105..52e2bfe4 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0-SNAPSHOT + 3.0.0 ../pom.xml matomo-java-tracker - 3.0.0-SNAPSHOT + 3.0.0 jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index bac4ee05..8ec7fa68 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0-SNAPSHOT + 3.0.0 pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - HEAD + matomo-java-tracker-parent-3.0.0 diff --git a/servlet/pom.xml b/servlet/pom.xml index d750ca64..502dc061 100644 --- a/servlet/pom.xml +++ b/servlet/pom.xml @@ -1,16 +1,15 @@ - + 4.0.0 org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0-SNAPSHOT + 3.0.0 ../pom.xml matomo-java-tracker-servlet - 3.0.0-SNAPSHOT + 3.0.0 jar Matomo Java Tracker Servlet diff --git a/spring/pom.xml b/spring/pom.xml index 0f2d3c67..5967e94e 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0-SNAPSHOT + 3.0.0 ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 1aff5130..f95135f2 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -1,11 +1,10 @@ - + 4.0.0 org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0-SNAPSHOT + 3.0.0 ../pom.xml From 5c5d5d0afa05fbfeb299234258ad67d590dcfb44 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 13 Nov 2023 07:20:54 +0000 Subject: [PATCH 188/467] [maven-release-plugin] prepare for next development iteration --- core/pom.xml | 2 +- java17/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index fd5b7fdb..5a957b03 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0 + 3.0.1-SNAPSHOT ../pom.xml diff --git a/java17/pom.xml b/java17/pom.xml index b30250dd..96c2c332 100644 --- a/java17/pom.xml +++ b/java17/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0 + 3.0.1-SNAPSHOT ../pom.xml matomo-java-tracker-java17 - 3.0.0 + 3.0.1-SNAPSHOT jar Matomo Java Tracker Java 17 diff --git a/java8/pom.xml b/java8/pom.xml index 52e2bfe4..f413fecd 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0 + 3.0.1-SNAPSHOT ../pom.xml matomo-java-tracker - 3.0.0 + 3.0.1-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index 8ec7fa68..38a32b59 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0 + 3.0.1-SNAPSHOT pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - matomo-java-tracker-parent-3.0.0 + HEAD diff --git a/servlet/pom.xml b/servlet/pom.xml index 502dc061..2e3e7a3f 100644 --- a/servlet/pom.xml +++ b/servlet/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0 + 3.0.1-SNAPSHOT ../pom.xml matomo-java-tracker-servlet - 3.0.0 + 3.0.1-SNAPSHOT jar Matomo Java Tracker Servlet diff --git a/spring/pom.xml b/spring/pom.xml index 5967e94e..4cb7df9f 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0 + 3.0.1-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index f95135f2..49edaf1c 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0 + 3.0.1-SNAPSHOT ../pom.xml From 9ab07c2659b26b4cf718da67e80b02570651afda Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 13 Nov 2023 08:32:09 +0100 Subject: [PATCH 189/467] Update Java version in Javadoc publisher --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 82bdd82f..9ee782b1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -38,6 +38,6 @@ jobs: with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} javadoc-branch: javadoc - java-version: 8 + java-version: 17 target-folder: javadoc project: maven From f8601837fc90c9fda7a8ff07ea2f225af05aa150 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 13 Nov 2023 07:39:20 +0000 Subject: [PATCH 190/467] [maven-release-plugin] prepare release matomo-java-tracker-parent-3.0.1 --- core/pom.xml | 2 +- java17/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 5a957b03..9746921e 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1-SNAPSHOT + 3.0.1 ../pom.xml diff --git a/java17/pom.xml b/java17/pom.xml index 96c2c332..3707e982 100644 --- a/java17/pom.xml +++ b/java17/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1-SNAPSHOT + 3.0.1 ../pom.xml matomo-java-tracker-java17 - 3.0.1-SNAPSHOT + 3.0.1 jar Matomo Java Tracker Java 17 diff --git a/java8/pom.xml b/java8/pom.xml index f413fecd..5507d4d3 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1-SNAPSHOT + 3.0.1 ../pom.xml matomo-java-tracker - 3.0.1-SNAPSHOT + 3.0.1 jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index 38a32b59..acd3ec5f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1-SNAPSHOT + 3.0.1 pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - HEAD + matomo-java-tracker-parent-3.0.1 diff --git a/servlet/pom.xml b/servlet/pom.xml index 2e3e7a3f..34d10af3 100644 --- a/servlet/pom.xml +++ b/servlet/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1-SNAPSHOT + 3.0.1 ../pom.xml matomo-java-tracker-servlet - 3.0.1-SNAPSHOT + 3.0.1 jar Matomo Java Tracker Servlet diff --git a/spring/pom.xml b/spring/pom.xml index 4cb7df9f..e27cc82a 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1-SNAPSHOT + 3.0.1 ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 49edaf1c..61b3492d 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1-SNAPSHOT + 3.0.1 ../pom.xml From 570da5a8627797f89dac02be42b940d5c8cec217 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 13 Nov 2023 07:39:22 +0000 Subject: [PATCH 191/467] [maven-release-plugin] prepare for next development iteration --- core/pom.xml | 2 +- java17/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 9746921e..8458daf4 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1 + 3.0.2-SNAPSHOT ../pom.xml diff --git a/java17/pom.xml b/java17/pom.xml index 3707e982..cdeb6b0d 100644 --- a/java17/pom.xml +++ b/java17/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1 + 3.0.2-SNAPSHOT ../pom.xml matomo-java-tracker-java17 - 3.0.1 + 3.0.2-SNAPSHOT jar Matomo Java Tracker Java 17 diff --git a/java8/pom.xml b/java8/pom.xml index 5507d4d3..c03c494f 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1 + 3.0.2-SNAPSHOT ../pom.xml matomo-java-tracker - 3.0.1 + 3.0.2-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index acd3ec5f..071a97a4 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1 + 3.0.2-SNAPSHOT pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - matomo-java-tracker-parent-3.0.1 + HEAD diff --git a/servlet/pom.xml b/servlet/pom.xml index 34d10af3..96fa9a31 100644 --- a/servlet/pom.xml +++ b/servlet/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1 + 3.0.2-SNAPSHOT ../pom.xml matomo-java-tracker-servlet - 3.0.1 + 3.0.2-SNAPSHOT jar Matomo Java Tracker Servlet diff --git a/spring/pom.xml b/spring/pom.xml index e27cc82a..7044c910 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1 + 3.0.2-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 61b3492d..f392f0fe 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1 + 3.0.2-SNAPSHOT ../pom.xml From d692155414a2a6c5522c56ee79cb09662c8c617f Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 13 Nov 2023 08:47:10 +0100 Subject: [PATCH 192/467] Fix version extraction in release script --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9ee782b1..4d2398b4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,7 +22,7 @@ jobs: - id: version run: | VERSION=$( mvn -B help:evaluate -Dexpression=project.version -q -DforceStdout ) - echo "::set-output name=version::${VERSION#-SNAPSHOT}" + echo "::set-output name=version::${VERSION%-SNAPSHOT}" - run: mvn -B release:prepare release:perform env: OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} From 13cf2b5dd5bb747a5408e86d36f4b1aff470c75c Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 13 Nov 2023 08:57:31 +0100 Subject: [PATCH 193/467] Update name template --- .github/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index d43ca132..6830782a 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,4 +1,4 @@ -name-template: 'v$RESOLVED_VERSION 🌈' +name-template: 'v$RESOLVED_VERSION' tag-template: 'v$RESOLVED_VERSION' categories: From 536a893f27e19261dcab4f2b2c12915a614d3837 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 13 Nov 2023 09:57:35 +0100 Subject: [PATCH 194/467] Add release plugin configuration --- pom.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pom.xml b/pom.xml index 071a97a4..437801cc 100644 --- a/pom.xml +++ b/pom.xml @@ -131,6 +131,18 @@ maven-surefire-plugin 3.2.2 + + org.apache.maven.plugins + maven-release-plugin + 3.0.1 + + true + false + release + deploy + [ci skip] + + org.apache.maven.plugins maven-gpg-plugin From 5f5df747c154bfe01c1a0b9bc135dc44c66e7bbb Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 13 Nov 2023 10:13:36 +0100 Subject: [PATCH 195/467] Add slack notification --- .github/workflows/release.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4d2398b4..cd8ac75b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -41,3 +41,12 @@ jobs: java-version: 17 target-folder: javadoc project: maven + - uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + SLACK_USERNAME: github-actions + SLACK_ICON: https://m-img.org/spai/w_147+q_lossless+ret_img+to_webp/matomo.org/wp-content/uploads/2020/03/matomo-logo-winner-mobile3.png + SLACK_ICON_EMOJI: ':tada:' + SLACK_TITLE: Matomo Java Tracker ${{ steps.version.outputs.version }} released + SLACK_MESSAGE: "Exciting news! :star2: We're thrilled to share that a new version of the Matomo Java Tracker was staged and will become available soon, packed with awesome features and improvements. :rocket: See https://github.com/matomo-org/matomo-java-tracker/releases for release notes." + SLACK_FOOTER: "Thanks for being awesome! :heart:" \ No newline at end of file From 8869074b86e5e6e7bd2a98cdc3102d5f0fc0f406 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 13 Nov 2023 09:16:37 +0000 Subject: [PATCH 196/467] [ci skip] prepare release matomo-java-tracker-parent-3.0.2 --- core/pom.xml | 2 +- java17/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 8458daf4..b151e54e 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2-SNAPSHOT + 3.0.2 ../pom.xml diff --git a/java17/pom.xml b/java17/pom.xml index cdeb6b0d..31dc17f3 100644 --- a/java17/pom.xml +++ b/java17/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2-SNAPSHOT + 3.0.2 ../pom.xml matomo-java-tracker-java17 - 3.0.2-SNAPSHOT + 3.0.2 jar Matomo Java Tracker Java 17 diff --git a/java8/pom.xml b/java8/pom.xml index c03c494f..4f906d1a 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2-SNAPSHOT + 3.0.2 ../pom.xml matomo-java-tracker - 3.0.2-SNAPSHOT + 3.0.2 jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index 437801cc..d460af84 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2-SNAPSHOT + 3.0.2 pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - HEAD + matomo-java-tracker-parent-3.0.2 diff --git a/servlet/pom.xml b/servlet/pom.xml index 96fa9a31..23543dc8 100644 --- a/servlet/pom.xml +++ b/servlet/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2-SNAPSHOT + 3.0.2 ../pom.xml matomo-java-tracker-servlet - 3.0.2-SNAPSHOT + 3.0.2 jar Matomo Java Tracker Servlet diff --git a/spring/pom.xml b/spring/pom.xml index 7044c910..5c127649 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2-SNAPSHOT + 3.0.2 ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index f392f0fe..dbc4baad 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2-SNAPSHOT + 3.0.2 ../pom.xml From abe2ab140d95669dda1846fc08fb196138d9d948 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 13 Nov 2023 09:16:39 +0000 Subject: [PATCH 197/467] [ci skip] prepare for next development iteration --- core/pom.xml | 2 +- java17/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index b151e54e..4c22b327 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2 + 3.0.3-SNAPSHOT ../pom.xml diff --git a/java17/pom.xml b/java17/pom.xml index 31dc17f3..f06a83c9 100644 --- a/java17/pom.xml +++ b/java17/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2 + 3.0.3-SNAPSHOT ../pom.xml matomo-java-tracker-java17 - 3.0.2 + 3.0.3-SNAPSHOT jar Matomo Java Tracker Java 17 diff --git a/java8/pom.xml b/java8/pom.xml index 4f906d1a..3b277968 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2 + 3.0.3-SNAPSHOT ../pom.xml matomo-java-tracker - 3.0.2 + 3.0.3-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index d460af84..2057dec7 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2 + 3.0.3-SNAPSHOT pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - matomo-java-tracker-parent-3.0.2 + HEAD diff --git a/servlet/pom.xml b/servlet/pom.xml index 23543dc8..5030b009 100644 --- a/servlet/pom.xml +++ b/servlet/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2 + 3.0.3-SNAPSHOT ../pom.xml matomo-java-tracker-servlet - 3.0.2 + 3.0.3-SNAPSHOT jar Matomo Java Tracker Servlet diff --git a/spring/pom.xml b/spring/pom.xml index 5c127649..4eeccd3d 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2 + 3.0.3-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index dbc4baad..1970dab5 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2 + 3.0.3-SNAPSHOT ../pom.xml From 6de6b22527be46a90f2011a580d195385fdd7e02 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 13 Nov 2023 10:39:37 +0100 Subject: [PATCH 198/467] Fix Javadoc generation --- pom.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2057dec7..b22adf3f 100644 --- a/pom.xml +++ b/pom.xml @@ -209,7 +209,7 @@ - core/src/main/java + src/main/java ${delombok.output} false @@ -227,7 +227,6 @@ maven-javadoc-plugin 3.6.2 - true ${delombok.output} none From 3e90f8f25662ec83a4c62a3899dc7b6e89ff199d Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 13 Nov 2023 09:59:07 +0000 Subject: [PATCH 199/467] [ci skip] prepare release matomo-java-tracker-parent-3.0.3 --- core/pom.xml | 2 +- java17/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 4c22b327..fdd89ca4 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3-SNAPSHOT + 3.0.3 ../pom.xml diff --git a/java17/pom.xml b/java17/pom.xml index f06a83c9..689529fa 100644 --- a/java17/pom.xml +++ b/java17/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3-SNAPSHOT + 3.0.3 ../pom.xml matomo-java-tracker-java17 - 3.0.3-SNAPSHOT + 3.0.3 jar Matomo Java Tracker Java 17 diff --git a/java8/pom.xml b/java8/pom.xml index 3b277968..49792d4a 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3-SNAPSHOT + 3.0.3 ../pom.xml matomo-java-tracker - 3.0.3-SNAPSHOT + 3.0.3 jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index b22adf3f..b57a3091 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3-SNAPSHOT + 3.0.3 pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - HEAD + matomo-java-tracker-parent-3.0.3 diff --git a/servlet/pom.xml b/servlet/pom.xml index 5030b009..f80f4b8b 100644 --- a/servlet/pom.xml +++ b/servlet/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3-SNAPSHOT + 3.0.3 ../pom.xml matomo-java-tracker-servlet - 3.0.3-SNAPSHOT + 3.0.3 jar Matomo Java Tracker Servlet diff --git a/spring/pom.xml b/spring/pom.xml index 4eeccd3d..d0775360 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3-SNAPSHOT + 3.0.3 ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 1970dab5..d351c89c 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3-SNAPSHOT + 3.0.3 ../pom.xml From 05a1cb5d29200ba88c619f83ecc804654275df4b Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 13 Nov 2023 09:59:08 +0000 Subject: [PATCH 200/467] [ci skip] prepare for next development iteration --- core/pom.xml | 2 +- java17/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index fdd89ca4..2fdb9bcf 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3 + 3.0.4-SNAPSHOT ../pom.xml diff --git a/java17/pom.xml b/java17/pom.xml index 689529fa..331312f8 100644 --- a/java17/pom.xml +++ b/java17/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3 + 3.0.4-SNAPSHOT ../pom.xml matomo-java-tracker-java17 - 3.0.3 + 3.0.4-SNAPSHOT jar Matomo Java Tracker Java 17 diff --git a/java8/pom.xml b/java8/pom.xml index 49792d4a..86d6e02e 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3 + 3.0.4-SNAPSHOT ../pom.xml matomo-java-tracker - 3.0.3 + 3.0.4-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index b57a3091..429c7f49 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3 + 3.0.4-SNAPSHOT pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - matomo-java-tracker-parent-3.0.3 + HEAD diff --git a/servlet/pom.xml b/servlet/pom.xml index f80f4b8b..801ab279 100644 --- a/servlet/pom.xml +++ b/servlet/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3 + 3.0.4-SNAPSHOT ../pom.xml matomo-java-tracker-servlet - 3.0.3 + 3.0.4-SNAPSHOT jar Matomo Java Tracker Servlet diff --git a/spring/pom.xml b/spring/pom.xml index d0775360..a08d8f2a 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3 + 3.0.4-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index d351c89c..1b0672e1 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3 + 3.0.4-SNAPSHOT ../pom.xml From 2bb3791342928ff121c1f2ac8c66cdacd6bd27ea Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 13 Nov 2023 11:08:49 +0100 Subject: [PATCH 201/467] Remove Javadoc publisher (can't handle multi-module projects) --- .github/workflows/release.yml | 7 ------- README.md | 7 +++---- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cd8ac75b..7db3300c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,13 +34,6 @@ jobs: publish: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: MathieuSoysal/Javadoc-publisher.yml@v2.4.0 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - javadoc-branch: javadoc - java-version: 17 - target-folder: javadoc - project: maven - uses: rtCamp/action-slack-notify@v2 env: SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} diff --git a/README.md b/README.md index 4e5e71e9..4148eebe 100644 --- a/README.md +++ b/README.md @@ -76,10 +76,9 @@ Here are the most important changes: ## Javadoc -The Javadoc for this project is hosted as a GitHub page for this repo. The latest Javadoc can be -found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/index.html). Javadoc folder older versions can be -found here: https://javadoc.io/doc/org.piwik.java.tracking/matomo-java-tracker -You can also build the Javadoc yourself. See the section [Building](#building-and-testing) below. +The Javadoc for all versions can be found +[at javadoc.io](https://javadoc.io/doc/org.piwik.java.tracking/matomo-java-tracker/latest/index.html). Thanks to +[javadoc.io](https://javadoc.io) for hosting it. ## Need help? From 7c04eb074d3330da559350b7d30ea895b6dff519 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 13 Nov 2023 11:11:50 +0100 Subject: [PATCH 202/467] Reset version --- core/pom.xml | 2 +- java17/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 2 +- servlet/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 2fdb9bcf..e18418bb 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4-SNAPSHOT + 3.0.0-SNAPSHOT ../pom.xml diff --git a/java17/pom.xml b/java17/pom.xml index 331312f8..ec4e2391 100644 --- a/java17/pom.xml +++ b/java17/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4-SNAPSHOT + 3.0.0-SNAPSHOT ../pom.xml matomo-java-tracker-java17 - 3.0.4-SNAPSHOT + 3.0.0-SNAPSHOT jar Matomo Java Tracker Java 17 diff --git a/java8/pom.xml b/java8/pom.xml index 86d6e02e..79d03105 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4-SNAPSHOT + 3.0.0-SNAPSHOT ../pom.xml matomo-java-tracker - 3.0.4-SNAPSHOT + 3.0.0-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index 429c7f49..52fe2289 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4-SNAPSHOT + 3.0.0-SNAPSHOT pom Matomo Java Tracker Parent diff --git a/servlet/pom.xml b/servlet/pom.xml index 801ab279..22aa5458 100644 --- a/servlet/pom.xml +++ b/servlet/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4-SNAPSHOT + 3.0.0-SNAPSHOT ../pom.xml matomo-java-tracker-servlet - 3.0.4-SNAPSHOT + 3.0.0-SNAPSHOT jar Matomo Java Tracker Servlet diff --git a/spring/pom.xml b/spring/pom.xml index a08d8f2a..0f2d3c67 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4-SNAPSHOT + 3.0.0-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 1b0672e1..9faed807 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4-SNAPSHOT + 3.0.0-SNAPSHOT ../pom.xml From 27607580d7a99e420ce110a768947d38af30624c Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 13 Nov 2023 10:18:08 +0000 Subject: [PATCH 203/467] [ci skip] prepare release matomo-java-tracker-parent-3.0.0 --- core/pom.xml | 2 +- java17/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index e18418bb..fd5b7fdb 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0-SNAPSHOT + 3.0.0 ../pom.xml diff --git a/java17/pom.xml b/java17/pom.xml index ec4e2391..b30250dd 100644 --- a/java17/pom.xml +++ b/java17/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0-SNAPSHOT + 3.0.0 ../pom.xml matomo-java-tracker-java17 - 3.0.0-SNAPSHOT + 3.0.0 jar Matomo Java Tracker Java 17 diff --git a/java8/pom.xml b/java8/pom.xml index 79d03105..52e2bfe4 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0-SNAPSHOT + 3.0.0 ../pom.xml matomo-java-tracker - 3.0.0-SNAPSHOT + 3.0.0 jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index 52fe2289..2d337fbc 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0-SNAPSHOT + 3.0.0 pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - HEAD + matomo-java-tracker-parent-3.0.0 diff --git a/servlet/pom.xml b/servlet/pom.xml index 22aa5458..502dc061 100644 --- a/servlet/pom.xml +++ b/servlet/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0-SNAPSHOT + 3.0.0 ../pom.xml matomo-java-tracker-servlet - 3.0.0-SNAPSHOT + 3.0.0 jar Matomo Java Tracker Servlet diff --git a/spring/pom.xml b/spring/pom.xml index 0f2d3c67..5967e94e 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0-SNAPSHOT + 3.0.0 ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 9faed807..f95135f2 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0-SNAPSHOT + 3.0.0 ../pom.xml From 52bdabcdaf17e8479296dd450fd1db8a352caf0b Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 13 Nov 2023 10:18:10 +0000 Subject: [PATCH 204/467] [ci skip] prepare for next development iteration --- core/pom.xml | 2 +- java17/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index fd5b7fdb..5a957b03 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0 + 3.0.1-SNAPSHOT ../pom.xml diff --git a/java17/pom.xml b/java17/pom.xml index b30250dd..96c2c332 100644 --- a/java17/pom.xml +++ b/java17/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0 + 3.0.1-SNAPSHOT ../pom.xml matomo-java-tracker-java17 - 3.0.0 + 3.0.1-SNAPSHOT jar Matomo Java Tracker Java 17 diff --git a/java8/pom.xml b/java8/pom.xml index 52e2bfe4..f413fecd 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0 + 3.0.1-SNAPSHOT ../pom.xml matomo-java-tracker - 3.0.0 + 3.0.1-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index 2d337fbc..c6bb006d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0 + 3.0.1-SNAPSHOT pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - matomo-java-tracker-parent-3.0.0 + HEAD diff --git a/servlet/pom.xml b/servlet/pom.xml index 502dc061..2e3e7a3f 100644 --- a/servlet/pom.xml +++ b/servlet/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0 + 3.0.1-SNAPSHOT ../pom.xml matomo-java-tracker-servlet - 3.0.0 + 3.0.1-SNAPSHOT jar Matomo Java Tracker Servlet diff --git a/spring/pom.xml b/spring/pom.xml index 5967e94e..4cb7df9f 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0 + 3.0.1-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index f95135f2..49edaf1c 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.0 + 3.0.1-SNAPSHOT ../pom.xml From 27b3dcc9aebfc82aca63ac8f892e3ce738a09dba Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 13 Nov 2023 11:23:30 +0100 Subject: [PATCH 205/467] Create gh-pages.yml --- .github/workflows/gh-pages.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/gh-pages.yml diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml new file mode 100644 index 00000000..14952d88 --- /dev/null +++ b/.github/workflows/gh-pages.yml @@ -0,0 +1,32 @@ +name: Deploy GitHub Pages +on: + push: + branches: ["main"] + workflow_dispatch: +permissions: + contents: read + pages: write + id-token: write +concurrency: + group: "pages" + cancel-in-progress: false +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/configure-pages@v3 + - uses: actions/jekyll-build-pages@v1 + with: + source: ./ + destination: ./_site + - uses: actions/upload-pages-artifact@v2 + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - id: deployment + uses: actions/deploy-pages@v2 From 30b2e631ab6d265b69a9d1b6dc23cbaef2f85554 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 13 Nov 2023 11:54:36 +0100 Subject: [PATCH 206/467] Determine visitor IP from headers --- .../java/tracking/ServletMatomoRequest.java | 86 +++++++++++++------ .../java/tracking/MockHttpServletRequest.java | 3 +- .../tracking/ServletMatomoRequestTest.java | 23 ++++- 3 files changed, 82 insertions(+), 30 deletions(-) diff --git a/servlet/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java b/servlet/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java index 27aca706..5d8be191 100644 --- a/servlet/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java +++ b/servlet/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java @@ -2,6 +2,7 @@ import static java.util.Arrays.asList; +import edu.umd.cs.findbugs.annotations.Nullable; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import java.util.Collections; @@ -18,11 +19,12 @@ /** - * Adds the headers from a {@link HttpServletRequest} to a {@link MatomoRequest.MatomoRequestBuilder}. + * Adds the headers from a {@link HttpServletRequest} to a + * {@link MatomoRequest.MatomoRequestBuilder}. * - *

Use #fromServletRequest(HttpServletRequest) to create a new builder with the headers from the request or - * #addServletRequestHeaders(MatomoRequest.MatomoRequestBuilder, HttpServletRequest) to add the headers to an existing - * builder. + *

Use #fromServletRequest(HttpServletRequest) to create a new builder with the headers from the + * request or #addServletRequestHeaders(MatomoRequest.MatomoRequestBuilder, HttpServletRequest) to + * add the headers to an existing builder. */ public final class ServletMatomoRequest { @@ -30,7 +32,13 @@ public final class ServletMatomoRequest { * Please ensure these values are always lower case. */ private static final Set RESTRICTED_HEADERS = - Collections.unmodifiableSet(new HashSet<>(asList("connection", "content-length", "expect", "host", "upgrade"))); + Collections.unmodifiableSet(new HashSet<>(asList( + "connection", + "content-length", + "expect", + "host", + "upgrade" + ))); private ServletMatomoRequest() { // should not be instantiated @@ -39,10 +47,11 @@ private ServletMatomoRequest() { /** * Creates a new builder with the headers from the request. * - *

Use #addServletRequestHeaders(MatomoRequest.MatomoRequestBuilder, HttpServletRequest) to add the headers to an - * existing builder. + *

Use #addServletRequestHeaders(MatomoRequest.MatomoRequestBuilder, HttpServletRequest) to + * add the headers to an existing builder. * * @param request the request to get the headers from (must not be null) + * * @return a new builder with the headers from the request (never null) */ @edu.umd.cs.findbugs.annotations.NonNull @@ -53,10 +62,12 @@ public static MatomoRequest.MatomoRequestBuilder fromServletRequest(@NonNull Htt /** * Adds the headers from the request to an existing builder. * - *

Use #fromServletRequest(HttpServletRequest) to create a new builder with the headers from the request. + *

Use #fromServletRequest(HttpServletRequest) to create a new builder with the headers from + * the request. * * @param builder the builder to add the headers to (must not be null) * @param request the request to get the headers from (must not be null) + * * @return the builder with the headers added (never null) */ @edu.umd.cs.findbugs.annotations.NonNull @@ -66,10 +77,45 @@ public static MatomoRequest.MatomoRequestBuilder addServletRequestHeaders( return builder .actionUrl(request.getRequestURL() == null ? null : request.getRequestURL().toString()) .headers(collectHeaders(request)) - .visitorIp(request.getRemoteAddr()) + .visitorIp(determineVisitorIp(request)) .cookies(processCookies(builder, request)); } + @edu.umd.cs.findbugs.annotations.NonNull + private static Map collectHeaders( + @edu.umd.cs.findbugs.annotations.NonNull HttpServletRequest request + ) { + Map headers = new HashMap<>(10); + Enumeration headerNames = request.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String headerName = headerNames.nextElement(); + if (headerName != null && !headerName.trim().isEmpty() && !RESTRICTED_HEADERS.contains( + headerName.toLowerCase( + Locale.ROOT))) { + headers.put(headerName, request.getHeader(headerName)); + } + } + return headers; + } + + @Nullable + private static String determineVisitorIp( + @edu.umd.cs.findbugs.annotations.NonNull HttpServletRequest request + ) { + String realIpHeader = request.getHeader("X-Real-Ip"); + if (isNotEmpty(realIpHeader)) { + return realIpHeader; + } + String forwardedForHeader = request.getHeader("X-Forwarded-For"); + if (isNotEmpty(forwardedForHeader)) { + return forwardedForHeader; + } + if (isNotEmpty(request.getRemoteAddr())) { + return request.getRemoteAddr(); + } + return null; + } + @edu.umd.cs.findbugs.annotations.NonNull private static Map processCookies( @edu.umd.cs.findbugs.annotations.NonNull MatomoRequest.MatomoRequestBuilder builder, @@ -79,7 +125,7 @@ private static Map processCookies( if (request.getCookies() != null) { builder.supportsCookies(Boolean.TRUE); for (Cookie cookie : request.getCookies()) { - if (cookie.getValue() != null && !cookie.getValue().trim().isEmpty()) { + if (isNotEmpty(cookie.getValue())) { processCookie(builder, cookies, cookie.getName(), cookie.getValue()); } } @@ -87,6 +133,10 @@ private static Map processCookies( return cookies; } + private static boolean isNotEmpty(String forwardedForHeader) { + return forwardedForHeader != null && !forwardedForHeader.trim().isEmpty(); + } + private static void processCookie( @edu.umd.cs.findbugs.annotations.NonNull MatomoRequest.MatomoRequestBuilder builder, @edu.umd.cs.findbugs.annotations.NonNull Map cookies, @@ -121,20 +171,4 @@ private static void extractVisitorId( cookies.put(cookieName, cookieValue); } } - - @edu.umd.cs.findbugs.annotations.NonNull - private static Map collectHeaders( - @edu.umd.cs.findbugs.annotations.NonNull HttpServletRequest request - ) { - Map headers = new HashMap<>(10); - Enumeration headerNames = request.getHeaderNames(); - while (headerNames.hasMoreElements()) { - String headerName = headerNames.nextElement(); - if (headerName != null && !headerName.trim().isEmpty() && !RESTRICTED_HEADERS.contains(headerName.toLowerCase( - Locale.ROOT))) { - headers.put(headerName, request.getHeader(headerName)); - } - } - return headers; - } } \ No newline at end of file diff --git a/servlet/src/test/java/org/matomo/java/tracking/MockHttpServletRequest.java b/servlet/src/test/java/org/matomo/java/tracking/MockHttpServletRequest.java index fff6be6a..f0f6341a 100644 --- a/servlet/src/test/java/org/matomo/java/tracking/MockHttpServletRequest.java +++ b/servlet/src/test/java/org/matomo/java/tracking/MockHttpServletRequest.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Enumeration; +import java.util.LinkedHashMap; import java.util.Locale; import java.util.Map; import lombok.Setter; @@ -29,7 +30,7 @@ @Setter class MockHttpServletRequest implements HttpServletRequest { - private Map headers; + private Map headers = new LinkedHashMap<>(); private Collection cookies; diff --git a/servlet/src/test/java/org/matomo/java/tracking/ServletMatomoRequestTest.java b/servlet/src/test/java/org/matomo/java/tracking/ServletMatomoRequestTest.java index c19d42a2..a79cda66 100644 --- a/servlet/src/test/java/org/matomo/java/tracking/ServletMatomoRequestTest.java +++ b/servlet/src/test/java/org/matomo/java/tracking/ServletMatomoRequestTest.java @@ -71,7 +71,10 @@ void failsIfServletRequestIsNull() { @Test void failsIfBuilderIsNull() { - assertThatThrownBy(() -> ServletMatomoRequest.addServletRequestHeaders(null, new MockHttpServletRequest())) + assertThatThrownBy(() -> ServletMatomoRequest.addServletRequestHeaders( + null, + new MockHttpServletRequest() + )) .isInstanceOf(NullPointerException.class) .hasMessage("builder is marked non-null but is null"); } @@ -85,7 +88,9 @@ void extractsVisitorIdFromCookie() { MatomoRequest matomoRequest = builder.build(); assertThat(matomoRequest.getVisitorId()).hasToString("be40d677d6c7270b"); - assertThat(matomoRequest.getCookies()).hasSize(1).containsEntry("_pk_id.1.1fff", "be40d677d6c7270b.1699801331."); + assertThat(matomoRequest.getCookies()) + .hasSize(1) + .containsEntry("_pk_id.1.1fff", "be40d677d6c7270b.1699801331."); } @ParameterizedTest @@ -124,11 +129,23 @@ void parsesVisitCustomVariablesFromCookie() { MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); MatomoRequest matomoRequest = builder.build(); - assertThat(matomoRequest.getVisitCustomVariables().get(1).getKey()).isEqualTo("VAR 1 set, var 2 not set"); + assertThat(matomoRequest.getVisitCustomVariables().get(1).getKey()).isEqualTo( + "VAR 1 set, var 2 not set"); assertThat(matomoRequest.getVisitCustomVariables().get(1).getValue()).isEqualTo("yes"); assertThat(matomoRequest.getVisitCustomVariables().get(3).getKey()).isEqualTo("var 3 set"); assertThat(matomoRequest.getVisitCustomVariables().get(3).getValue()).isEqualTo("yes!!!!"); } + @Test + void determinerVisitorIpFromXForwardedForHeader() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setHeaders(singletonMap("X-Forwarded-For", "44.55.66.77")); + + MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); + + MatomoRequest matomoRequest = builder.build(); + assertThat(matomoRequest.getVisitorIp()).isEqualTo("44.55.66.77"); + } + } \ No newline at end of file From 6491c837c803e50bee9fa82539dfc403241155a1 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 13 Nov 2023 12:08:20 +0100 Subject: [PATCH 207/467] Be compatible to Java 11 as well --- README.md | 20 +++++++++---------- .../tracking/ServiceLoaderSenderFactory.java | 2 +- .../java/tracking/TrackerConfiguration.java | 2 +- {java17 => java11}/pom.xml | 10 +++++----- .../matomo/java/tracking/Java11Sender.java | 4 ++-- .../java/tracking/Java11SenderProvider.java | 6 +++--- .../org.matomo.java.tracking.SenderProvider | 1 + .../matomo/java/tracking/Java11SenderIT.java | 12 +++++------ .../matomo/java/tracking/MatomoTrackerIT.java | 0 .../matomo/java/tracking/PiwikTrackerIT.java | 0 .../org.matomo.java.tracking.SenderProvider | 1 - .../org/matomo/java/tracking/Java8Sender.java | 2 +- pom.xml | 2 +- servlet/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 6 +++--- 16 files changed, 37 insertions(+), 37 deletions(-) rename {java17 => java11}/pom.xml (90%) rename java17/src/main/java/org/matomo/java/tracking/Java17Sender.java => java11/src/main/java/org/matomo/java/tracking/Java11Sender.java (98%) rename java17/src/main/java/org/matomo/java/tracking/Java17SenderProvider.java => java11/src/main/java/org/matomo/java/tracking/Java11SenderProvider.java (92%) create mode 100644 java11/src/main/resources/META-INF/services/org.matomo.java.tracking.SenderProvider rename java17/src/test/java/org/matomo/java/tracking/Java17SenderIT.java => java11/src/test/java/org/matomo/java/tracking/Java11SenderIT.java (97%) rename {java17 => java11}/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java (100%) rename {java17 => java11}/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java (100%) delete mode 100644 java17/src/main/resources/META-INF/services/org.matomo.java.tracking.SenderProvider diff --git a/README.md b/README.md index 4148eebe..b9433b33 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Features include: * Track page views, goals, ecommerce transactions and items * Supports custom dimensions and custom variables * Includes tracking parameters for campaigns, events, downloads, outlinks, site search, devices, visitors -* Supports Java 8 and higher (if you use Java 17, please use artifact matomo-java-tracker-java17) +* Supports Java 8 and higher (if you use Java 11, please use artifact matomo-java-tracker-java11) * Allows you to skip SSL certificate validation (not recommended for production) * Contains nearly no runtime dependencies (only SLF4J) * Allows asynchronous requests @@ -26,7 +26,7 @@ Features include: * Includes debug and error logging * Easy to integrate in frameworks, e.g. Spring: Just create the MatomoTracker Spring bean and use it in other beans -Please prefer the Java 17 version as the Java 8 will become obsolete in the future. +Please prefer the Java 11 version as the Java 8 will become obsolete in the future. Further information on Matomo and Matomo HTTP tracking: @@ -72,7 +72,7 @@ Here are the most important changes: * less dependencies * new dimension parameter * special types allow to provide valid parameters now -* a new implementation for Java 17 uses the HttpClient available since Java 11 +* a new implementation for Java 11 uses the HttpClient available since Java 11 ## Javadoc @@ -107,12 +107,12 @@ Add a dependency on Matomo Java Tracker using Maven. For Java 8: ``` -For Java 17: +For Java 11: ```xml org.piwik.java.tracking - matomo-java-tracker-java17 + matomo-java-tracker-java11 3.0.0 ``` @@ -125,11 +125,11 @@ dependencies { } ``` -or Gradle (Java 17): +or Gradle (Java 11): ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker-java17:3.0.0") + implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.0.0") } ``` @@ -139,10 +139,10 @@ or Gradle with Kotlin DSL (Java 8) implementation("org.piwik.java.tracking:matomo-java-tracker:3.0.0") ``` -or Gradle with Kotlin DSL (Java 17) +or Gradle with Kotlin DSL (Java 11) ```kotlin -implementation("org.piwik.java.tracking:matomo-java-tracker-java17:3.0.0") +implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.0.0") ``` ### Spring Boot Module @@ -491,7 +491,7 @@ This project contains the following modules: * `core` contains the core functionality of the Matomo Java Tracker * `java8` contains the Java 8 implementation of the Matomo Java Tracker -* `java17` contains the Java 17 implementation of the Matomo Java Tracker using the HttpClient available since Java 11 +* `java11` contains the Java 11 implementation of the Matomo Java Tracker using the HttpClient available since Java 11 (recommended) * `servlet` contains `SerlvetMatomoRequest` to create a `MatomoRequest` from a `HttpServletRequest` and a filter `MatomoTrackingFilter` that can be used to track requests to a servlet diff --git a/core/src/main/java/org/matomo/java/tracking/ServiceLoaderSenderFactory.java b/core/src/main/java/org/matomo/java/tracking/ServiceLoaderSenderFactory.java index 08968e0c..7cafe302 100644 --- a/core/src/main/java/org/matomo/java/tracking/ServiceLoaderSenderFactory.java +++ b/core/src/main/java/org/matomo/java/tracking/ServiceLoaderSenderFactory.java @@ -15,7 +15,7 @@ public Sender createSender(TrackerConfiguration trackerConfiguration, QueryCreat Map senderProviders = StreamSupport .stream(serviceLoader.spliterator(), false) .collect(toMap(senderProvider -> senderProvider.getClass().getName(), Function.identity())); - SenderProvider senderProvider = senderProviders.get("org.matomo.java.tracking.Java17SenderProvider"); + SenderProvider senderProvider = senderProviders.get("org.matomo.java.tracking.Java11SenderProvider"); if (senderProvider == null) { senderProvider = senderProviders.get("org.matomo.java.tracking.Java8SenderProvider"); } diff --git a/core/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java b/core/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java index 51408558..424ce82d 100644 --- a/core/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java +++ b/core/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java @@ -117,7 +117,7 @@ public class TrackerConfiguration { * Disables SSL host verification. This is useful for testing with self-signed certificates. Do * not use in production environments. Defaults to false. * - *

If you use the Java 17 of the Matomo Java Tracker, this setting is ignored. Instead, you + *

If you use the Java 11 of the Matomo Java Tracker, this setting is ignored. Instead, you * have to set the system property {@code jdk.internal.httpclient.disableHostnameVerification} as * described in the * Module diff --git a/java17/pom.xml b/java11/pom.xml similarity index 90% rename from java17/pom.xml rename to java11/pom.xml index 96c2c332..76bb4419 100644 --- a/java17/pom.xml +++ b/java11/pom.xml @@ -8,16 +8,16 @@ ../pom.xml - matomo-java-tracker-java17 + matomo-java-tracker-java11 3.0.1-SNAPSHOT jar - Matomo Java Tracker Java 17 - Official Java implementation of the Matomo Tracking HTTP API for Java 17. + Matomo Java Tracker Java 11 + Official Java implementation of the Matomo Tracking HTTP API for Java 11. - 17 - 17 + 11 + 11 diff --git a/java17/src/main/java/org/matomo/java/tracking/Java17Sender.java b/java11/src/main/java/org/matomo/java/tracking/Java11Sender.java similarity index 98% rename from java17/src/main/java/org/matomo/java/tracking/Java17Sender.java rename to java11/src/main/java/org/matomo/java/tracking/Java11Sender.java index c1e92432..3daf434f 100644 --- a/java17/src/main/java/org/matomo/java/tracking/Java17Sender.java +++ b/java11/src/main/java/org/matomo/java/tracking/Java11Sender.java @@ -21,11 +21,11 @@ import lombok.extern.slf4j.Slf4j; /** - * A {@link Sender} implementation that uses the Java 17 HTTP client. + * A {@link Sender} implementation that uses the Java 11 HTTP client. */ @RequiredArgsConstructor @Slf4j -public class Java17Sender implements Sender { +public class Java11Sender implements Sender { @lombok.NonNull private final TrackerConfiguration trackerConfiguration; diff --git a/java17/src/main/java/org/matomo/java/tracking/Java17SenderProvider.java b/java11/src/main/java/org/matomo/java/tracking/Java11SenderProvider.java similarity index 92% rename from java17/src/main/java/org/matomo/java/tracking/Java17SenderProvider.java rename to java11/src/main/java/org/matomo/java/tracking/Java11SenderProvider.java index babc2b66..273ca19f 100644 --- a/java17/src/main/java/org/matomo/java/tracking/Java17SenderProvider.java +++ b/java11/src/main/java/org/matomo/java/tracking/Java11SenderProvider.java @@ -11,9 +11,9 @@ import javax.net.ssl.TrustManager; /** - * Provides a {@link Sender} implementation based on Java 17. + * Provides a {@link Sender} implementation based on Java 11. */ -public class Java17SenderProvider implements SenderProvider { +public class Java11SenderProvider implements SenderProvider { private static final TrustManager[] TRUST_ALL_MANAGERS = {new TrustingX509TrustManager()}; @@ -54,7 +54,7 @@ public Sender provideSender( throw new MatomoException("Please disable SSL hostname verification manually using the system parameter -Djdk.internal.httpclient.disableHostnameVerification=true"); } - return new Java17Sender(trackerConfiguration, queryCreator, builder.build(), cookieManager.getCookieStore()); + return new Java11Sender(trackerConfiguration, queryCreator, builder.build(), cookieManager.getCookieStore()); } private static boolean isEmpty( diff --git a/java11/src/main/resources/META-INF/services/org.matomo.java.tracking.SenderProvider b/java11/src/main/resources/META-INF/services/org.matomo.java.tracking.SenderProvider new file mode 100644 index 00000000..55c8186e --- /dev/null +++ b/java11/src/main/resources/META-INF/services/org.matomo.java.tracking.SenderProvider @@ -0,0 +1 @@ +org.matomo.java.tracking.Java11SenderProvider \ No newline at end of file diff --git a/java17/src/test/java/org/matomo/java/tracking/Java17SenderIT.java b/java11/src/test/java/org/matomo/java/tracking/Java11SenderIT.java similarity index 97% rename from java17/src/test/java/org/matomo/java/tracking/Java17SenderIT.java rename to java11/src/test/java/org/matomo/java/tracking/Java11SenderIT.java index bad936d5..4026e19e 100644 --- a/java17/src/test/java/org/matomo/java/tracking/Java17SenderIT.java +++ b/java11/src/test/java/org/matomo/java/tracking/Java11SenderIT.java @@ -25,7 +25,7 @@ import org.junit.jupiter.api.Test; @WireMockTest(httpsEnabled = true) -class Java17SenderIT { +class Java11SenderIT { private Sender sender; @@ -39,7 +39,7 @@ void disableSslHostnameVerification() { @Test void failsIfTrackerConfigurationIsNotSet() { CookieManager cookieManager = new CookieManager(); - assertThatThrownBy(() -> new Java17Sender( + assertThatThrownBy(() -> new Java11Sender( null, new QueryCreator(TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build()), HttpClient.newBuilder().cookieHandler(cookieManager).build(), @@ -50,7 +50,7 @@ void failsIfTrackerConfigurationIsNotSet() { @Test void failsIfQueryCreatorIsNotSet() { CookieManager cookieManager = new CookieManager(); - assertThatThrownBy(() -> new Java17Sender( + assertThatThrownBy(() -> new Java11Sender( TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build(), null, HttpClient.newBuilder().cookieHandler(cookieManager).build(), @@ -61,7 +61,7 @@ void failsIfQueryCreatorIsNotSet() { @Test void failsIfHttpClientIsNotSet() { CookieManager cookieManager = new CookieManager(); - assertThatThrownBy(() -> new Java17Sender( + assertThatThrownBy(() -> new Java11Sender( TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build(), new QueryCreator(TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build()), null, @@ -72,7 +72,7 @@ void failsIfHttpClientIsNotSet() { @Test void failsIfCookieStoreIsNotSet() { CookieManager cookieManager = new CookieManager(); - assertThatThrownBy(() -> new Java17Sender( + assertThatThrownBy(() -> new Java11Sender( TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build(), new QueryCreator(TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build()), HttpClient.newBuilder().cookieHandler(cookieManager).build(), @@ -108,7 +108,7 @@ void sendSingleFailsIfQueryIsMalformedWithoutSocketTimeout() { } private void givenSender() { - sender = new Java17SenderProvider().provideSender(trackerConfiguration, new QueryCreator(trackerConfiguration)); + sender = new Java11SenderProvider().provideSender(trackerConfiguration, new QueryCreator(trackerConfiguration)); } @Test diff --git a/java17/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/java11/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java similarity index 100% rename from java17/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java rename to java11/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java diff --git a/java17/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java b/java11/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java similarity index 100% rename from java17/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java rename to java11/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java diff --git a/java17/src/main/resources/META-INF/services/org.matomo.java.tracking.SenderProvider b/java17/src/main/resources/META-INF/services/org.matomo.java.tracking.SenderProvider deleted file mode 100644 index faed872c..00000000 --- a/java17/src/main/resources/META-INF/services/org.matomo.java.tracking.SenderProvider +++ /dev/null @@ -1 +0,0 @@ -org.matomo.java.tracking.Java17SenderProvider \ No newline at end of file diff --git a/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java b/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java index e35fac9d..9b386f89 100644 --- a/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java +++ b/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java @@ -46,7 +46,7 @@ * exit even if the thread pool is not shut down. * *

If you use a newer Java version, please use the newer Java implementation from the Matomo Java Tracker for - * Java 17. + * Java 11. */ @Slf4j @RequiredArgsConstructor diff --git a/pom.xml b/pom.xml index c6bb006d..1eb0332a 100644 --- a/pom.xml +++ b/pom.xml @@ -360,7 +360,7 @@ core java8 - java17 + java11 servlet spring test diff --git a/servlet/pom.xml b/servlet/pom.xml index 2e3e7a3f..d74c0f59 100644 --- a/servlet/pom.xml +++ b/servlet/pom.xml @@ -16,8 +16,8 @@ Integrates Matomo Java Tracker into your servlet based application - 17 - 17 + 11 + 11 diff --git a/spring/pom.xml b/spring/pom.xml index 4cb7df9f..126e3f82 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -22,7 +22,7 @@ org.piwik.java.tracking - matomo-java-tracker-java17 + matomo-java-tracker-java11 ${project.version} diff --git a/test/pom.xml b/test/pom.xml index 49edaf1c..4d0c7ab2 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -14,8 +14,8 @@ Test application for Matomo Java Tracker - 17 - 17 + 11 + 11 true true true @@ -24,7 +24,7 @@ org.piwik.java.tracking - matomo-java-tracker-java17 + matomo-java-tracker-java11 ${project.version} From 57601e84766c960fc257e81986aad91b26544c11 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 13 Nov 2023 11:22:18 +0000 Subject: [PATCH 208/467] [ci skip] prepare release matomo-java-tracker-parent-3.0.1 --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 5a957b03..9746921e 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1-SNAPSHOT + 3.0.1 ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index 76bb4419..9e234e97 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1-SNAPSHOT + 3.0.1 ../pom.xml matomo-java-tracker-java11 - 3.0.1-SNAPSHOT + 3.0.1 jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index f413fecd..5507d4d3 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1-SNAPSHOT + 3.0.1 ../pom.xml matomo-java-tracker - 3.0.1-SNAPSHOT + 3.0.1 jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index 1eb0332a..d25cd104 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1-SNAPSHOT + 3.0.1 pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - HEAD + matomo-java-tracker-parent-3.0.1 diff --git a/servlet/pom.xml b/servlet/pom.xml index d74c0f59..751ae390 100644 --- a/servlet/pom.xml +++ b/servlet/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1-SNAPSHOT + 3.0.1 ../pom.xml matomo-java-tracker-servlet - 3.0.1-SNAPSHOT + 3.0.1 jar Matomo Java Tracker Servlet diff --git a/spring/pom.xml b/spring/pom.xml index 126e3f82..dc7eabd1 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1-SNAPSHOT + 3.0.1 ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 4d0c7ab2..5f98ad9e 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1-SNAPSHOT + 3.0.1 ../pom.xml From 0841d057b90d0df4e35df96e2df228a7d4294a39 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 13 Nov 2023 11:22:20 +0000 Subject: [PATCH 209/467] [ci skip] prepare for next development iteration --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 9746921e..8458daf4 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1 + 3.0.2-SNAPSHOT ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index 9e234e97..2969603c 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1 + 3.0.2-SNAPSHOT ../pom.xml matomo-java-tracker-java11 - 3.0.1 + 3.0.2-SNAPSHOT jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index 5507d4d3..c03c494f 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1 + 3.0.2-SNAPSHOT ../pom.xml matomo-java-tracker - 3.0.1 + 3.0.2-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index d25cd104..2c4f5cf8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1 + 3.0.2-SNAPSHOT pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - matomo-java-tracker-parent-3.0.1 + HEAD diff --git a/servlet/pom.xml b/servlet/pom.xml index 751ae390..d9cfe71d 100644 --- a/servlet/pom.xml +++ b/servlet/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1 + 3.0.2-SNAPSHOT ../pom.xml matomo-java-tracker-servlet - 3.0.1 + 3.0.2-SNAPSHOT jar Matomo Java Tracker Servlet diff --git a/spring/pom.xml b/spring/pom.xml index dc7eabd1..555af204 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1 + 3.0.2-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 5f98ad9e..22bc9205 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.1 + 3.0.2-SNAPSHOT ../pom.xml From 5b2447a8f28b9c744495bd15b2d7b84f6746a809 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 13 Nov 2023 14:30:17 +0100 Subject: [PATCH 210/467] Provide wrappers for Jakarta and Java EE --- README.md | 11 +- .../java/tracking/servlet/CookieWrapper.java | 15 + .../servlet/HttpServletRequestWrapper.java | 53 +++ .../servlet}/ServletMatomoRequest.java | 27 +- .../servlet}/ServletMatomoRequestTest.java | 91 +++-- pom.xml | 52 ++- servlet-jakarta/pom.xml | 71 ++++ .../servlet/JakartaHttpServletWrapper.java | 49 +++ .../servlet}/MatomoTrackerFilter.java | 7 +- .../java/tracking/MatomoTrackerFilterIT.java | 63 +++ .../org/matomo/java/tracking/TestSender.java | 0 .../java/tracking/TestSenderFactory.java | 0 .../JakartaHttpServletWrapperTest.java | 35 ++ .../servlet}/MockHttpServletRequest.java | 11 +- {servlet => servlet-javax}/pom.xml | 12 +- .../servlet/JavaxHttpServletWrapper.java | 49 +++ .../tracking/servlet/MatomoTrackerFilter.java | 35 ++ .../java/tracking/MatomoTrackerFilterIT.java | 3 +- .../org/matomo/java/tracking/TestSender.java | 54 +++ .../java/tracking/TestSenderFactory.java | 16 + .../servlet/JavaxHttpServletWrapperTest.java | 35 ++ .../servlet/MockHttpServletRequest.java | 381 ++++++++++++++++++ spring/pom.xml | 15 +- .../MatomoTrackerAutoConfiguration.java | 2 +- test/pom.xml | 7 +- .../tracking/test/MatomoServletTester.java | 10 +- .../test/ServletMatomoRequestExample.java | 5 +- 27 files changed, 1027 insertions(+), 82 deletions(-) create mode 100644 core/src/main/java/org/matomo/java/tracking/servlet/CookieWrapper.java create mode 100644 core/src/main/java/org/matomo/java/tracking/servlet/HttpServletRequestWrapper.java rename {servlet/src/main/java/org/matomo/java/tracking => core/src/main/java/org/matomo/java/tracking/servlet}/ServletMatomoRequest.java (89%) rename {servlet/src/test/java/org/matomo/java/tracking => core/src/test/java/org/matomo/java/tracking/servlet}/ServletMatomoRequestTest.java (62%) create mode 100644 servlet-jakarta/pom.xml create mode 100644 servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/JakartaHttpServletWrapper.java rename {servlet/src/main/java/org/matomo/java/tracking => servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet}/MatomoTrackerFilter.java (75%) create mode 100644 servlet-jakarta/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java rename {servlet => servlet-jakarta}/src/test/java/org/matomo/java/tracking/TestSender.java (100%) rename {servlet => servlet-jakarta}/src/test/java/org/matomo/java/tracking/TestSenderFactory.java (100%) create mode 100644 servlet-jakarta/src/test/java/org/matomo/java/tracking/servlet/JakartaHttpServletWrapperTest.java rename {servlet/src/test/java/org/matomo/java/tracking => servlet-jakarta/src/test/java/org/matomo/java/tracking/servlet}/MockHttpServletRequest.java (98%) rename {servlet => servlet-javax}/pom.xml (84%) create mode 100644 servlet-javax/src/main/java/org/matomo/java/tracking/servlet/JavaxHttpServletWrapper.java create mode 100644 servlet-javax/src/main/java/org/matomo/java/tracking/servlet/MatomoTrackerFilter.java rename {servlet => servlet-javax}/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java (95%) create mode 100644 servlet-javax/src/test/java/org/matomo/java/tracking/TestSender.java create mode 100644 servlet-javax/src/test/java/org/matomo/java/tracking/TestSenderFactory.java create mode 100644 servlet-javax/src/test/java/org/matomo/java/tracking/servlet/JavaxHttpServletWrapperTest.java create mode 100644 servlet-javax/src/test/java/org/matomo/java/tracking/servlet/MockHttpServletRequest.java diff --git a/README.md b/README.md index b9433b33..4db00548 100644 --- a/README.md +++ b/README.md @@ -343,8 +343,8 @@ In a Servlet environment, it might be easier to use the `ServletMatomoRequest` c import jakarta.servlet.http.HttpServletRequest; import org.matomo.java.tracking.MatomoRequest; import org.matomo.java.tracking.MatomoTracker; -import org.matomo.java.tracking.parameters.DeviceResolution; -import org.matomo.java.tracking.ServletMatomoRequest; +import org.matomo.java.tracking.servlet.JakartaHttpServletWrapper; +import org.matomo.java.tracking.servlet.ServletMatomoRequest; public class ServletMatomoRequestExample { @@ -353,10 +353,10 @@ public class ServletMatomoRequestExample { public ServletMatomoRequestExample(MatomoTracker tracker) { this.tracker = tracker; } - + public void someControllerMethod(HttpServletRequest req) { MatomoRequest matomoRequest = ServletMatomoRequest - .fromServletRequest(req) + .fromServletRequest(JakartaHttpServletWrapper.fromHttpServletRequest(req)) .actionName("Some Controller Action") // ... .build(); @@ -369,7 +369,8 @@ public class ServletMatomoRequestExample { The `ServletMatomoRequest` automatically sets the action URL, applies browser request headers, corresponding Matomo cookies and the visitor IP address. It sets the visitor ID, Matomo session ID, custom variables and heatmap -if Matomo cookies are present. +if Matomo cookies are present. Since there was a renaming from Java EE (javax) to Jakarta EE (jakarta), we provide a +wrapper class `JakartaHttpServletWrapper` for Jakarta and `JavaxHttpServletWrapper` for javax. ### Tracking Configuration diff --git a/core/src/main/java/org/matomo/java/tracking/servlet/CookieWrapper.java b/core/src/main/java/org/matomo/java/tracking/servlet/CookieWrapper.java new file mode 100644 index 00000000..4c29ae3d --- /dev/null +++ b/core/src/main/java/org/matomo/java/tracking/servlet/CookieWrapper.java @@ -0,0 +1,15 @@ +package org.matomo.java.tracking.servlet; + +import lombok.Value; + +/** + * Wrapper for the cookie name and value. + */ +@Value +public class CookieWrapper { + + String name; + + String value; + +} diff --git a/core/src/main/java/org/matomo/java/tracking/servlet/HttpServletRequestWrapper.java b/core/src/main/java/org/matomo/java/tracking/servlet/HttpServletRequestWrapper.java new file mode 100644 index 00000000..51dfa0d3 --- /dev/null +++ b/core/src/main/java/org/matomo/java/tracking/servlet/HttpServletRequestWrapper.java @@ -0,0 +1,53 @@ +package org.matomo.java.tracking.servlet; + +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Locale; +import java.util.Map; +import lombok.Builder; +import lombok.NonNull; +import lombok.Value; + +/** + * Wraps a HttpServletRequest to be compatible with both the Jakarta and the Java EE API. + */ +@Builder +@Value +public class HttpServletRequestWrapper { + + StringBuffer requestURL; + + String remoteAddr; + + Map headers; + + CookieWrapper[] cookies; + + /** + * Returns an enumeration of all the header names this request contains. If the request has no + * headers, this method returns an empty enumeration. + * + * @return an enumeration of all the header names sent with this request + */ + public Enumeration getHeaderNames() { + return headers == null ? Collections.emptyEnumeration() : + Collections.enumeration(headers.keySet()); + } + + /** + * Returns the value of the specified request header as a String. If the request did not include a + * header of the specified name, this method returns null. If there are multiple headers with the + * same name, this method returns the last header in the request. The header name is case + * insensitive. You can use this method with any request header. + * + * @param name a String specifying the header name (case insensitive) - must not be {@code null}. + * @return a String containing the value of the requested header, or null if the request does not + * have a header of that name + */ + @Nullable + public String getHeader(@NonNull String name) { + return headers == null ? null : headers.get(name.toLowerCase(Locale.ROOT)); + } + +} diff --git a/servlet/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java b/core/src/main/java/org/matomo/java/tracking/servlet/ServletMatomoRequest.java similarity index 89% rename from servlet/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java rename to core/src/main/java/org/matomo/java/tracking/servlet/ServletMatomoRequest.java index 5d8be191..6b2125d3 100644 --- a/servlet/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java +++ b/core/src/main/java/org/matomo/java/tracking/servlet/ServletMatomoRequest.java @@ -1,10 +1,8 @@ -package org.matomo.java.tracking; +package org.matomo.java.tracking.servlet; import static java.util.Arrays.asList; import edu.umd.cs.findbugs.annotations.Nullable; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; @@ -14,16 +12,17 @@ import java.util.Map; import java.util.Set; import lombok.NonNull; +import org.matomo.java.tracking.MatomoRequest; import org.matomo.java.tracking.parameters.CustomVariables; import org.matomo.java.tracking.parameters.VisitorId; /** - * Adds the headers from a {@link HttpServletRequest} to a + * Adds the headers from a {@link HttpServletRequestWrapper} to a * {@link MatomoRequest.MatomoRequestBuilder}. * - *

Use #fromServletRequest(HttpServletRequest) to create a new builder with the headers from the - * request or #addServletRequestHeaders(MatomoRequest.MatomoRequestBuilder, HttpServletRequest) to + *

Use #fromServletRequest(HttpServletRequestWrapper) to create a new builder with the headers from the + * request or #addServletRequestHeaders(MatomoRequest.MatomoRequestBuilder, HttpServletRequestWrapper) to * add the headers to an existing builder. */ public final class ServletMatomoRequest { @@ -47,7 +46,7 @@ private ServletMatomoRequest() { /** * Creates a new builder with the headers from the request. * - *

Use #addServletRequestHeaders(MatomoRequest.MatomoRequestBuilder, HttpServletRequest) to + *

Use #addServletRequestHeaders(MatomoRequest.MatomoRequestBuilder, HttpServletRequestWrapper) to * add the headers to an existing builder. * * @param request the request to get the headers from (must not be null) @@ -55,14 +54,14 @@ private ServletMatomoRequest() { * @return a new builder with the headers from the request (never null) */ @edu.umd.cs.findbugs.annotations.NonNull - public static MatomoRequest.MatomoRequestBuilder fromServletRequest(@NonNull HttpServletRequest request) { + public static MatomoRequest.MatomoRequestBuilder fromServletRequest(@NonNull HttpServletRequestWrapper request) { return addServletRequestHeaders(MatomoRequest.request(), request); } /** * Adds the headers from the request to an existing builder. * - *

Use #fromServletRequest(HttpServletRequest) to create a new builder with the headers from + *

Use #fromServletRequest(HttpServletRequestWrapper) to create a new builder with the headers from * the request. * * @param builder the builder to add the headers to (must not be null) @@ -72,7 +71,7 @@ public static MatomoRequest.MatomoRequestBuilder fromServletRequest(@NonNull Htt */ @edu.umd.cs.findbugs.annotations.NonNull public static MatomoRequest.MatomoRequestBuilder addServletRequestHeaders( - @NonNull MatomoRequest.MatomoRequestBuilder builder, @NonNull HttpServletRequest request + @NonNull MatomoRequest.MatomoRequestBuilder builder, @NonNull HttpServletRequestWrapper request ) { return builder .actionUrl(request.getRequestURL() == null ? null : request.getRequestURL().toString()) @@ -83,7 +82,7 @@ public static MatomoRequest.MatomoRequestBuilder addServletRequestHeaders( @edu.umd.cs.findbugs.annotations.NonNull private static Map collectHeaders( - @edu.umd.cs.findbugs.annotations.NonNull HttpServletRequest request + @edu.umd.cs.findbugs.annotations.NonNull HttpServletRequestWrapper request ) { Map headers = new HashMap<>(10); Enumeration headerNames = request.getHeaderNames(); @@ -100,7 +99,7 @@ private static Map collectHeaders( @Nullable private static String determineVisitorIp( - @edu.umd.cs.findbugs.annotations.NonNull HttpServletRequest request + @edu.umd.cs.findbugs.annotations.NonNull HttpServletRequestWrapper request ) { String realIpHeader = request.getHeader("X-Real-Ip"); if (isNotEmpty(realIpHeader)) { @@ -119,12 +118,12 @@ private static String determineVisitorIp( @edu.umd.cs.findbugs.annotations.NonNull private static Map processCookies( @edu.umd.cs.findbugs.annotations.NonNull MatomoRequest.MatomoRequestBuilder builder, - @edu.umd.cs.findbugs.annotations.NonNull HttpServletRequest request + @edu.umd.cs.findbugs.annotations.NonNull HttpServletRequestWrapper request ) { Map cookies = new LinkedHashMap<>(3); if (request.getCookies() != null) { builder.supportsCookies(Boolean.TRUE); - for (Cookie cookie : request.getCookies()) { + for (CookieWrapper cookie : request.getCookies()) { if (isNotEmpty(cookie.getValue())) { processCookie(builder, cookies, cookie.getName(), cookie.getValue()); } diff --git a/servlet/src/test/java/org/matomo/java/tracking/ServletMatomoRequestTest.java b/core/src/test/java/org/matomo/java/tracking/servlet/ServletMatomoRequestTest.java similarity index 62% rename from servlet/src/test/java/org/matomo/java/tracking/ServletMatomoRequestTest.java rename to core/src/test/java/org/matomo/java/tracking/servlet/ServletMatomoRequestTest.java index a79cda66..65eabd2c 100644 --- a/servlet/src/test/java/org/matomo/java/tracking/ServletMatomoRequestTest.java +++ b/core/src/test/java/org/matomo/java/tracking/servlet/ServletMatomoRequestTest.java @@ -1,34 +1,37 @@ -package org.matomo.java.tracking; +package org.matomo.java.tracking.servlet; -import static java.util.Collections.singleton; import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import jakarta.servlet.http.Cookie; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import org.matomo.java.tracking.MatomoRequest; class ServletMatomoRequestTest { @Test void addsServletRequestHeaders() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setHeaders(singletonMap("headerName", "headerValue")); + HttpServletRequestWrapper request = HttpServletRequestWrapper + .builder() + .headers(singletonMap("headername", "headerValue")) + .build(); MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); MatomoRequest matomoRequest = builder.build(); - assertThat(matomoRequest.getHeaders()).hasSize(1).containsEntry("headerName", "headerValue"); + assertThat(matomoRequest.getHeaders()).hasSize(1).containsEntry("headername", "headerValue"); } @Test void skipsEmptyHeaderNames() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setHeaders(singletonMap("", "headerValue")); + HttpServletRequestWrapper request = HttpServletRequestWrapper + .builder() + .headers(singletonMap("", "headerValue")) + .build(); MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); @@ -40,8 +43,10 @@ void skipsEmptyHeaderNames() { @Test void skipsBlankHeaderNames() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setHeaders(singletonMap(" ", "headerValue")); + HttpServletRequestWrapper request = HttpServletRequestWrapper + .builder() + .headers(singletonMap(" ", "headerValue")) + .build(); MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); @@ -53,8 +58,10 @@ void skipsBlankHeaderNames() { @ParameterizedTest @ValueSource(strings = {"connection", "content-length", "expect", "host", "upgrade"}) void doesNotAddRestrictedHeaders(String restrictedHeader) { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setHeaders(singletonMap(restrictedHeader, "headerValue")); + HttpServletRequestWrapper request = HttpServletRequestWrapper + .builder() + .headers(singletonMap(restrictedHeader, "headerValue")) + .build(); MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); @@ -73,7 +80,7 @@ void failsIfServletRequestIsNull() { void failsIfBuilderIsNull() { assertThatThrownBy(() -> ServletMatomoRequest.addServletRequestHeaders( null, - new MockHttpServletRequest() + HttpServletRequestWrapper.builder().build() )) .isInstanceOf(NullPointerException.class) .hasMessage("builder is marked non-null but is null"); @@ -81,8 +88,12 @@ void failsIfBuilderIsNull() { @Test void extractsVisitorIdFromCookie() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setCookies(singleton(new Cookie("_pk_id.1.1fff", "be40d677d6c7270b.1699801331."))); + HttpServletRequestWrapper request = HttpServletRequestWrapper + .builder() + .cookies(new CookieWrapper[] { + new CookieWrapper("_pk_id.1.1fff", "be40d677d6c7270b.1699801331.") + }) + .build(); MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); @@ -98,8 +109,10 @@ void extractsVisitorIdFromCookie() { strings = {"_pk_ses.1.1fff", "_pk_ref.1.1fff", "_pk_hsr.1.1fff"} ) void extractsMatomoCookies(String cookieName) { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setCookies(singleton(new Cookie(cookieName, "anything"))); + HttpServletRequestWrapper request = HttpServletRequestWrapper + .builder() + .cookies(new CookieWrapper[] {new CookieWrapper(cookieName, "anything")}) + .build(); MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); @@ -109,8 +122,15 @@ void extractsMatomoCookies(String cookieName) { @Test void extractsSessionIdFromMatomoSessIdCookie() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setCookies(singleton(new Cookie("MATOMO_SESSID", "2cbf8b5ba00fbf9ba70853308cd0944a"))); + HttpServletRequestWrapper request = HttpServletRequestWrapper + .builder() + .cookies(new CookieWrapper[] { + new CookieWrapper( + "MATOMO_SESSID", + "2cbf8b5ba00fbf9ba70853308cd0944a" + ) + }) + .build(); MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); @@ -120,11 +140,15 @@ void extractsSessionIdFromMatomoSessIdCookie() { @Test void parsesVisitCustomVariablesFromCookie() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setCookies(singleton(new Cookie( - "_pk_cvar.1.1fff", - "{\"1\":[\"VAR 1 set, var 2 not set\",\"yes\"],\"3\":[\"var 3 set\",\"yes!!!!\"]}" - ))); + HttpServletRequestWrapper request = HttpServletRequestWrapper + .builder() + .cookies(new CookieWrapper[] { + new CookieWrapper( + "_pk_cvar.1.1fff", + "{\"1\":[\"VAR 1 set, var 2 not set\",\"yes\"],\"3\":[\"var 3 set\",\"yes!!!!\"]}" + ) + }) + .build(); MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); @@ -139,8 +163,23 @@ void parsesVisitCustomVariablesFromCookie() { @Test void determinerVisitorIpFromXForwardedForHeader() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setHeaders(singletonMap("X-Forwarded-For", "44.55.66.77")); + HttpServletRequestWrapper request = HttpServletRequestWrapper + .builder() + .headers(singletonMap("x-forwarded-for", "44.55.66.77")) + .build(); + + MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); + + MatomoRequest matomoRequest = builder.build(); + assertThat(matomoRequest.getVisitorIp()).isEqualTo("44.55.66.77"); + } + + @Test + void determinerVisitorIpFromXRealIpHeader() { + HttpServletRequestWrapper request = HttpServletRequestWrapper + .builder() + .headers(singletonMap("x-real-ip", "44.55.66.77")) + .build(); MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); diff --git a/pom.xml b/pom.xml index 2c4f5cf8..4f8ee12c 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 org.piwik.java.tracking @@ -62,24 +63,52 @@ 1.8 1.8 ${project.build.outputDirectory}/delombok - 3.1.5 + 1.18.30 + 2.0.9 - - org.springframework.boot - spring-boot-dependencies - ${spring-boot.version} - pom - import - com.github.spotbugs spotbugs-annotations 4.8.0 provided + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.projectlombok + lombok + ${lombok.version} + provided + + + org.junit.jupiter + junit-jupiter + 5.10.1 + test + + + org.assertj + assertj-core + 3.24.2 + test + + + org.slf4j + slf4j-simple + ${slf4j.version} + test + + + org.eclipse.jetty.ee10 + jetty-ee10-servlet + 12.0.3 + @@ -205,7 +234,7 @@ org.projectlombok lombok - 1.18.30 + ${lombok.version} @@ -361,7 +390,8 @@ core java8 java11 - servlet + servlet-jakarta + servlet-javax spring test diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml new file mode 100644 index 00000000..e290d21b --- /dev/null +++ b/servlet-jakarta/pom.xml @@ -0,0 +1,71 @@ + + 4.0.0 + + + org.piwik.java.tracking + matomo-java-tracker-parent + 3.0.2-SNAPSHOT + ../pom.xml + + + matomo-java-tracker-servlet-jakarta + 3.0.2-SNAPSHOT + jar + + Matomo Java Tracker Servlet Jakarta + Integrates Matomo Java Tracker into your Jakarta servlet based application + + + 11 + 11 + + + + + org.piwik.java.tracking + matomo-java-tracker-core + ${project.version} + + + jakarta.servlet + jakarta.servlet-api + 6.0.0 + provided + + + org.slf4j + slf4j-api + + + com.github.spotbugs + spotbugs-annotations + + + org.projectlombok + lombok + provided + + + org.junit.jupiter + junit-jupiter + test + + + org.eclipse.jetty.ee10 + jetty-ee10-servlet + 12.0.3 + test + + + org.assertj + assertj-core + test + + + org.slf4j + slf4j-simple + test + + + + diff --git a/servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/JakartaHttpServletWrapper.java b/servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/JakartaHttpServletWrapper.java new file mode 100644 index 00000000..51b85554 --- /dev/null +++ b/servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/JakartaHttpServletWrapper.java @@ -0,0 +1,49 @@ +package org.matomo.java.tracking.servlet; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import lombok.NonNull; + +/** + * Converts a Jakarta {@link HttpServletRequest} to a {@link HttpServletRequestWrapper}. + */ +public final class JakartaHttpServletWrapper { + + private JakartaHttpServletWrapper() { + // utility + } + + /** + * Takes a Jakarta {@link HttpServletRequest} and converts it to a + * {@link HttpServletRequestWrapper}. + * + * @param request the request to convert to a wrapper object (must not be {@code null}). + * @return the wrapper object (never {@code null}). + */ + @edu.umd.cs.findbugs.annotations.NonNull + public static HttpServletRequestWrapper fromHttpServletRequest(@NonNull HttpServletRequest request) { + Map headers = new LinkedHashMap<>(); + request.getHeaderNames() + .asIterator() + .forEachRemaining(name -> headers.put(name.toLowerCase(Locale.ROOT), request.getHeader(name))); + List cookies = null; + if (request.getCookies() != null) { + cookies = Stream.of(request.getCookies()) + .map(cookie -> new CookieWrapper(cookie.getName(), cookie.getValue())) + .collect(Collectors.toList()); + } + return HttpServletRequestWrapper + .builder() + .requestURL(request.getRequestURL()) + .remoteAddr(request.getRemoteAddr()) + .headers(headers) + .cookies(cookies == null ? null : cookies.toArray(new CookieWrapper[0])) + .build(); + } + +} diff --git a/servlet/src/main/java/org/matomo/java/tracking/MatomoTrackerFilter.java b/servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/MatomoTrackerFilter.java similarity index 75% rename from servlet/src/main/java/org/matomo/java/tracking/MatomoTrackerFilter.java rename to servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/MatomoTrackerFilter.java index 82ebd557..664ce209 100644 --- a/servlet/src/main/java/org/matomo/java/tracking/MatomoTrackerFilter.java +++ b/servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/MatomoTrackerFilter.java @@ -1,4 +1,4 @@ -package org.matomo.java.tracking; +package org.matomo.java.tracking.servlet; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; @@ -8,6 +8,8 @@ import java.io.IOException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoTracker; /** * This filter can be used to automatically send a request to the Matomo server for every request @@ -22,7 +24,8 @@ public class MatomoTrackerFilter extends HttpFilter { @Override protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException { - MatomoRequest matomoRequest = ServletMatomoRequest.fromServletRequest(req).build(); + MatomoRequest matomoRequest = ServletMatomoRequest + .fromServletRequest(JakartaHttpServletWrapper.fromHttpServletRequest(req)).build(); log.debug("Sending request {}", matomoRequest); tracker.sendRequestAsync(matomoRequest); super.doFilter(req, res, chain); diff --git a/servlet-jakarta/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java b/servlet-jakarta/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java new file mode 100644 index 00000000..fac0d472 --- /dev/null +++ b/servlet-jakarta/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java @@ -0,0 +1,63 @@ +package org.matomo.java.tracking; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import org.eclipse.jetty.ee10.servlet.FilterHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.Test; +import org.matomo.java.tracking.servlet.MatomoTrackerFilter; + +class MatomoTrackerFilterIT { + + @Test + void sendsAnAsyncRequestOnFilter() throws Exception { + + + TestSenderFactory senderFactory = new TestSenderFactory(); + + MatomoTracker tracker = new MatomoTracker( + TrackerConfiguration + .builder() + .apiEndpoint(URI.create("http://localhost:8080/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("ee6e3dd9ed1b61f5328cf5978b5a8c71") + .logFailedTracking(true) + .build()); + tracker.setSenderFactory(senderFactory); + + ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/"); + context.addFilter(new FilterHolder(new MatomoTrackerFilter(tracker)), "/*", null); + Server server = new Server(0); + server.setHandler(context); + + server.start(); + URI uri = server.getURI(); + HttpClient.newHttpClient().send( + HttpRequest.newBuilder() + .header("Accept-Language", "en-US,en;q=0.9,de;q=0.8") + .uri(uri) + .build(), + HttpResponse.BodyHandlers.discarding() + ); + server.stop(); + + TestSender testSender = senderFactory.getTestSender(); + assertThat(testSender.getRequests()).hasSize(1).satisfiesExactly(matomoRequest -> { + assertThat(matomoRequest.getActionUrl()).isEqualTo(uri.toString()); + assertThat(matomoRequest.getVisitorId()).isNotNull(); + assertThat(matomoRequest.getVisitorIp()).isNotNull(); + assertThat(matomoRequest.getHeaders()).containsEntry( + "accept-language", + "en-US,en;q=0.9,de;q=0.8" + ); + }); + + } + +} \ No newline at end of file diff --git a/servlet/src/test/java/org/matomo/java/tracking/TestSender.java b/servlet-jakarta/src/test/java/org/matomo/java/tracking/TestSender.java similarity index 100% rename from servlet/src/test/java/org/matomo/java/tracking/TestSender.java rename to servlet-jakarta/src/test/java/org/matomo/java/tracking/TestSender.java diff --git a/servlet/src/test/java/org/matomo/java/tracking/TestSenderFactory.java b/servlet-jakarta/src/test/java/org/matomo/java/tracking/TestSenderFactory.java similarity index 100% rename from servlet/src/test/java/org/matomo/java/tracking/TestSenderFactory.java rename to servlet-jakarta/src/test/java/org/matomo/java/tracking/TestSenderFactory.java diff --git a/servlet-jakarta/src/test/java/org/matomo/java/tracking/servlet/JakartaHttpServletWrapperTest.java b/servlet-jakarta/src/test/java/org/matomo/java/tracking/servlet/JakartaHttpServletWrapperTest.java new file mode 100644 index 00000000..3d2699a7 --- /dev/null +++ b/servlet-jakarta/src/test/java/org/matomo/java/tracking/servlet/JakartaHttpServletWrapperTest.java @@ -0,0 +1,35 @@ +package org.matomo.java.tracking.servlet; + +import static java.util.Collections.singletonMap; +import static org.assertj.core.api.Assertions.assertThat; + +import jakarta.servlet.http.Cookie; +import java.util.List; +import org.junit.jupiter.api.Test; + +class JakartaHttpServletWrapperTest { + + @Test + void wrapsHttpServletRequest() { + + MockHttpServletRequest servlet = new MockHttpServletRequest(); + servlet.setRequestURL(new StringBuffer("http://localhost")); + servlet.setHeaders(singletonMap("Accept-Language", "en-US,en;q=0.9,de;q=0.8")); + servlet.setCookies(List.of(new Cookie("foo", "bar"))); + + HttpServletRequestWrapper httpServletRequestWrapper = + JakartaHttpServletWrapper.fromHttpServletRequest(servlet); + + assertThat(httpServletRequestWrapper.getRequestURL()).hasToString("http://localhost"); + assertThat(httpServletRequestWrapper.getHeaders()) + .containsEntry("accept-language", "en-US,en;q=0.9,de;q=0.8"); + assertThat(httpServletRequestWrapper.getCookies()) + .hasSize(1) + .satisfiesExactly(cookieWrapper -> { + assertThat(cookieWrapper.getName()).isEqualTo("foo"); + assertThat(cookieWrapper.getValue()).isEqualTo("bar"); + }); + } + +} + diff --git a/servlet/src/test/java/org/matomo/java/tracking/MockHttpServletRequest.java b/servlet-jakarta/src/test/java/org/matomo/java/tracking/servlet/MockHttpServletRequest.java similarity index 98% rename from servlet/src/test/java/org/matomo/java/tracking/MockHttpServletRequest.java rename to servlet-jakarta/src/test/java/org/matomo/java/tracking/servlet/MockHttpServletRequest.java index f0f6341a..d13398a6 100644 --- a/servlet/src/test/java/org/matomo/java/tracking/MockHttpServletRequest.java +++ b/servlet-jakarta/src/test/java/org/matomo/java/tracking/servlet/MockHttpServletRequest.java @@ -1,4 +1,4 @@ -package org.matomo.java.tracking; +package org.matomo.java.tracking.servlet; import jakarta.servlet.AsyncContext; import jakarta.servlet.DispatcherType; @@ -25,11 +25,15 @@ import java.util.LinkedHashMap; import java.util.Locale; import java.util.Map; +import lombok.Getter; import lombok.Setter; @Setter +@Getter class MockHttpServletRequest implements HttpServletRequest { + private StringBuffer requestURL; + private Map headers = new LinkedHashMap<>(); private Collection cookies; @@ -119,11 +123,6 @@ public String getRequestURI() { return null; } - @Override - public StringBuffer getRequestURL() { - return null; - } - @Override public String getServletPath() { return null; diff --git a/servlet/pom.xml b/servlet-javax/pom.xml similarity index 84% rename from servlet/pom.xml rename to servlet-javax/pom.xml index d9cfe71d..f907b9c5 100644 --- a/servlet/pom.xml +++ b/servlet-javax/pom.xml @@ -8,12 +8,12 @@ ../pom.xml - matomo-java-tracker-servlet + matomo-java-tracker-servlet-javax 3.0.2-SNAPSHOT jar - Matomo Java Tracker Servlet - Integrates Matomo Java Tracker into your servlet based application + Matomo Java Tracker Servlet Javax + Integrates Matomo Java Tracker into your javax servlet based application 11 @@ -27,8 +27,9 @@ ${project.version} - jakarta.servlet - jakarta.servlet-api + javax.servlet + javax.servlet-api + 4.0.1 provided @@ -52,6 +53,7 @@ org.eclipse.jetty jetty-servlet + 9.4.53.v20231009 test diff --git a/servlet-javax/src/main/java/org/matomo/java/tracking/servlet/JavaxHttpServletWrapper.java b/servlet-javax/src/main/java/org/matomo/java/tracking/servlet/JavaxHttpServletWrapper.java new file mode 100644 index 00000000..cee1a5ac --- /dev/null +++ b/servlet-javax/src/main/java/org/matomo/java/tracking/servlet/JavaxHttpServletWrapper.java @@ -0,0 +1,49 @@ +package org.matomo.java.tracking.servlet; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.servlet.http.HttpServletRequest; +import lombok.NonNull; + +/** + * Converts a javax {@link HttpServletRequest} to a {@link HttpServletRequestWrapper}. + */ +public final class JavaxHttpServletWrapper { + + private JavaxHttpServletWrapper() { + // utility + } + + /** + * Takes a javax {@link HttpServletRequest} and converts it to a + * {@link HttpServletRequestWrapper}. + * + * @param request the request to convert to a wrapper object (must not be {@code null}). + * @return the wrapper object (never {@code null}). + */ + @edu.umd.cs.findbugs.annotations.NonNull + public static HttpServletRequestWrapper fromHttpServletRequest(@NonNull HttpServletRequest request) { + Map headers = new LinkedHashMap<>(); + request.getHeaderNames() + .asIterator() + .forEachRemaining(name -> headers.put(name.toLowerCase(Locale.ROOT), request.getHeader(name))); + List cookies = null; + if (request.getCookies() != null) { + cookies = Stream.of(request.getCookies()) + .map(cookie -> new CookieWrapper(cookie.getName(), cookie.getValue())) + .collect(Collectors.toList()); + } + return HttpServletRequestWrapper + .builder() + .requestURL(request.getRequestURL()) + .remoteAddr(request.getRemoteAddr()) + .headers(headers) + .cookies(cookies == null ? null : cookies.toArray(new CookieWrapper[0])) + .build(); + } + +} diff --git a/servlet-javax/src/main/java/org/matomo/java/tracking/servlet/MatomoTrackerFilter.java b/servlet-javax/src/main/java/org/matomo/java/tracking/servlet/MatomoTrackerFilter.java new file mode 100644 index 00000000..abf0849d --- /dev/null +++ b/servlet-javax/src/main/java/org/matomo/java/tracking/servlet/MatomoTrackerFilter.java @@ -0,0 +1,35 @@ +package org.matomo.java.tracking.servlet; + +import edu.umd.cs.findbugs.annotations.NonNull; +import java.io.IOException; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpFilter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoTracker; + +/** + * This filter can be used to automatically send a request to the Matomo server for every request + * that is received by the servlet container. + */ +@RequiredArgsConstructor +@Slf4j +public class MatomoTrackerFilter extends HttpFilter { + + private final MatomoTracker tracker; + + @Override + protected void doFilter(@NonNull HttpServletRequest req, @NonNull HttpServletResponse res, + @NonNull FilterChain chain) + throws IOException, ServletException { + MatomoRequest matomoRequest = ServletMatomoRequest + .fromServletRequest(JavaxHttpServletWrapper.fromHttpServletRequest(req)).build(); + log.debug("Sending request {}", matomoRequest); + tracker.sendRequestAsync(matomoRequest); + super.doFilter(req, res, chain); + } +} diff --git a/servlet/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java b/servlet-javax/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java similarity index 95% rename from servlet/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java rename to servlet-javax/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java index c33ae2ba..62979d9b 100644 --- a/servlet/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java +++ b/servlet-javax/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java @@ -10,6 +10,7 @@ import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletContextHandler; import org.junit.jupiter.api.Test; +import org.matomo.java.tracking.servlet.MatomoTrackerFilter; class MatomoTrackerFilterIT { @@ -52,7 +53,7 @@ void sendsAnAsyncRequestOnFilter() throws Exception { assertThat(matomoRequest.getVisitorId()).isNotNull(); assertThat(matomoRequest.getVisitorIp()).isNotNull(); assertThat(matomoRequest.getHeaders()).containsEntry( - "Accept-Language", + "accept-language", "en-US,en;q=0.9,de;q=0.8" ); }); diff --git a/servlet-javax/src/test/java/org/matomo/java/tracking/TestSender.java b/servlet-javax/src/test/java/org/matomo/java/tracking/TestSender.java new file mode 100644 index 00000000..d6c03833 --- /dev/null +++ b/servlet-javax/src/test/java/org/matomo/java/tracking/TestSender.java @@ -0,0 +1,54 @@ +package org.matomo.java.tracking; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * A {@link Sender} implementation that does not send anything but stores the requests. + * + *

This class is intended for testing purposes only. It does not send anything to the Matomo server. Instead, it + * stores the requests and queries in collections that can be accessed via {@link #getRequests()}. + */ +@RequiredArgsConstructor +@Getter +class TestSender implements Sender { + + private final Collection requests = new ArrayList<>(); + + private final TrackerConfiguration trackerConfiguration; + + private final QueryCreator queryCreator; + + @NonNull + @Override + public CompletableFuture sendSingleAsync(@NonNull MatomoRequest request) { + requests.add(request); + return CompletableFuture.completedFuture(request); + } + + @Override + public void sendSingle(@NonNull MatomoRequest request) { + throw new UnsupportedOperationException(); + } + + @Override + public void sendBulk( + @NonNull Iterable requests, @Nullable String overrideAuthToken + ) { + throw new UnsupportedOperationException(); + } + + @NonNull + @Override + public CompletableFuture sendBulkAsync( + @NonNull Iterable requests, @Nullable String overrideAuthToken + ) { + throw new UnsupportedOperationException(); + } + +} diff --git a/servlet-javax/src/test/java/org/matomo/java/tracking/TestSenderFactory.java b/servlet-javax/src/test/java/org/matomo/java/tracking/TestSenderFactory.java new file mode 100644 index 00000000..fafafa90 --- /dev/null +++ b/servlet-javax/src/test/java/org/matomo/java/tracking/TestSenderFactory.java @@ -0,0 +1,16 @@ +package org.matomo.java.tracking; + +import lombok.Getter; + +class TestSenderFactory implements SenderFactory { + + @Getter + private TestSender testSender; + + @Override + public Sender createSender(TrackerConfiguration trackerConfiguration, QueryCreator queryCreator) { + TestSender testSender = new TestSender(trackerConfiguration, queryCreator); + this.testSender = testSender; + return testSender; + } +} diff --git a/servlet-javax/src/test/java/org/matomo/java/tracking/servlet/JavaxHttpServletWrapperTest.java b/servlet-javax/src/test/java/org/matomo/java/tracking/servlet/JavaxHttpServletWrapperTest.java new file mode 100644 index 00000000..411a3919 --- /dev/null +++ b/servlet-javax/src/test/java/org/matomo/java/tracking/servlet/JavaxHttpServletWrapperTest.java @@ -0,0 +1,35 @@ +package org.matomo.java.tracking.servlet; + +import static java.util.Collections.singletonMap; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import javax.servlet.http.Cookie; +import org.junit.jupiter.api.Test; + +class JavaxHttpServletWrapperTest { + + @Test + void wrapsHttpServletRequest() { + + MockHttpServletRequest servlet = new MockHttpServletRequest(); + servlet.setRequestURL(new StringBuffer("http://localhost")); + servlet.setHeaders(singletonMap("Accept-Language", "en-US,en;q=0.9,de;q=0.8")); + servlet.setCookies(List.of(new Cookie("foo", "bar"))); + + HttpServletRequestWrapper httpServletRequestWrapper = + JavaxHttpServletWrapper.fromHttpServletRequest(servlet); + + assertThat(httpServletRequestWrapper.getRequestURL()).hasToString("http://localhost"); + assertThat(httpServletRequestWrapper.getHeaders()) + .containsEntry("accept-language", "en-US,en;q=0.9,de;q=0.8"); + assertThat(httpServletRequestWrapper.getCookies()) + .hasSize(1) + .satisfiesExactly(cookieWrapper -> { + assertThat(cookieWrapper.getName()).isEqualTo("foo"); + assertThat(cookieWrapper.getValue()).isEqualTo("bar"); + }); + } + +} + diff --git a/servlet-javax/src/test/java/org/matomo/java/tracking/servlet/MockHttpServletRequest.java b/servlet-javax/src/test/java/org/matomo/java/tracking/servlet/MockHttpServletRequest.java new file mode 100644 index 00000000..cb0924b3 --- /dev/null +++ b/servlet-javax/src/test/java/org/matomo/java/tracking/servlet/MockHttpServletRequest.java @@ -0,0 +1,381 @@ +package org.matomo.java.tracking.servlet; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.Locale; +import java.util.Map; +import javax.servlet.AsyncContext; +import javax.servlet.DispatcherType; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpUpgradeHandler; +import javax.servlet.http.Part; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +class MockHttpServletRequest implements HttpServletRequest { + + private StringBuffer requestURL; + + private Map headers = new LinkedHashMap<>(); + + private Collection cookies; + + @Override + public String getAuthType() { + return null; + } + + @Override + public Cookie[] getCookies() { + return cookies == null ? null : cookies.toArray(new Cookie[0]); + } + + @Override + public long getDateHeader(String name) { + return 0; + } + + @Override + public String getHeader(String name) { + return headers.get(name); + } + + @Override + public Enumeration getHeaders(String name) { + return null; + } + + @Override + public Enumeration getHeaderNames() { + return headers == null ? Collections.emptyEnumeration() : Collections.enumeration(headers.keySet()); + } + + @Override + public int getIntHeader(String name) { + return 0; + } + + @Override + public String getMethod() { + return null; + } + + @Override + public String getPathInfo() { + return null; + } + + @Override + public String getPathTranslated() { + return null; + } + + @Override + public String getContextPath() { + return null; + } + + @Override + public String getQueryString() { + return null; + } + + @Override + public String getRemoteUser() { + return null; + } + + @Override + public boolean isUserInRole(String role) { + return false; + } + + @Override + public Principal getUserPrincipal() { + return null; + } + + @Override + public String getRequestedSessionId() { + return null; + } + + @Override + public String getRequestURI() { + return null; + } + + @Override + public String getServletPath() { + return null; + } + + @Override + public HttpSession getSession(boolean create) { + return null; + } + + @Override + public HttpSession getSession() { + return null; + } + + @Override + public String changeSessionId() { + return null; + } + + @Override + public boolean isRequestedSessionIdValid() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromCookie() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromURL() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromUrl() { + return false; + } + + @Override + public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { + return false; + } + + @Override + public void login(String username, String password) throws ServletException { + + } + + @Override + public void logout() throws ServletException { + + } + + @Override + public Collection getParts() throws IOException, ServletException { + return null; + } + + @Override + public Part getPart(String name) throws IOException, ServletException { + return null; + } + + @Override + public T upgrade(Class handlerClass) throws IOException, ServletException { + return null; + } + + @Override + public Object getAttribute(String name) { + return null; + } + + @Override + public Enumeration getAttributeNames() { + return null; + } + + @Override + public String getCharacterEncoding() { + return null; + } + + @Override + public void setCharacterEncoding(String env) throws UnsupportedEncodingException { + + } + + @Override + public int getContentLength() { + return 0; + } + + @Override + public long getContentLengthLong() { + return 0; + } + + @Override + public String getContentType() { + return null; + } + + @Override + public ServletInputStream getInputStream() throws IOException { + return null; + } + + @Override + public String getParameter(String name) { + return null; + } + + @Override + public Enumeration getParameterNames() { + return null; + } + + @Override + public String[] getParameterValues(String name) { + return new String[0]; + } + + @Override + public Map getParameterMap() { + return null; + } + + @Override + public String getProtocol() { + return null; + } + + @Override + public String getScheme() { + return null; + } + + @Override + public String getServerName() { + return null; + } + + @Override + public int getServerPort() { + return 0; + } + + @Override + public BufferedReader getReader() throws IOException { + return null; + } + + @Override + public String getRemoteAddr() { + return null; + } + + @Override + public String getRemoteHost() { + return null; + } + + @Override + public void setAttribute(String name, Object o) { + + } + + @Override + public void removeAttribute(String name) { + + } + + @Override + public Locale getLocale() { + return null; + } + + @Override + public Enumeration getLocales() { + return null; + } + + @Override + public boolean isSecure() { + return false; + } + + @Override + public RequestDispatcher getRequestDispatcher(String path) { + return null; + } + + @Override + public String getRealPath(String s) { + return null; + } + + @Override + public int getRemotePort() { + return 0; + } + + @Override + public String getLocalName() { + return null; + } + + @Override + public String getLocalAddr() { + return null; + } + + @Override + public int getLocalPort() { + return 0; + } + + @Override + public ServletContext getServletContext() { + return null; + } + + @Override + public AsyncContext startAsync() throws IllegalStateException { + return null; + } + + @Override + public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) + throws IllegalStateException { + return null; + } + + @Override + public boolean isAsyncStarted() { + return false; + } + + @Override + public boolean isAsyncSupported() { + return false; + } + + @Override + public AsyncContext getAsyncContext() { + return null; + } + + @Override + public DispatcherType getDispatcherType() { + return null; + } + +} diff --git a/spring/pom.xml b/spring/pom.xml index 555af204..5647910a 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -17,8 +17,21 @@ 17 17 + 3.1.5 + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + org.piwik.java.tracking @@ -27,7 +40,7 @@ org.piwik.java.tracking - matomo-java-tracker-servlet + matomo-java-tracker-servlet-jakarta ${project.version} diff --git a/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java b/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java index 3529e537..e79bc8f8 100644 --- a/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java +++ b/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java @@ -10,8 +10,8 @@ import java.net.URI; import java.util.List; import org.matomo.java.tracking.MatomoTracker; -import org.matomo.java.tracking.MatomoTrackerFilter; import org.matomo.java.tracking.TrackerConfiguration; +import org.matomo.java.tracking.servlet.MatomoTrackerFilter; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; diff --git a/test/pom.xml b/test/pom.xml index 22bc9205..e841a196 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -29,7 +29,7 @@ org.piwik.java.tracking - matomo-java-tracker-servlet + matomo-java-tracker-servlet-jakarta ${project.version} @@ -47,8 +47,9 @@ slf4j-simple - org.eclipse.jetty - jetty-servlet + org.eclipse.jetty.ee10 + jetty-ee10-servlet + 12.0.3 diff --git a/test/src/main/java/org/matomo/java/tracking/test/MatomoServletTester.java b/test/src/main/java/org/matomo/java/tracking/test/MatomoServletTester.java index 5681c5c4..0b4d9174 100644 --- a/test/src/main/java/org/matomo/java/tracking/test/MatomoServletTester.java +++ b/test/src/main/java/org/matomo/java/tracking/test/MatomoServletTester.java @@ -2,14 +2,14 @@ import java.net.URI; import lombok.extern.slf4j.Slf4j; +import org.eclipse.jetty.ee10.servlet.DefaultServlet; +import org.eclipse.jetty.ee10.servlet.FilterHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.DefaultServlet; -import org.eclipse.jetty.servlet.FilterHolder; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.matomo.java.tracking.MatomoTracker; -import org.matomo.java.tracking.MatomoTrackerFilter; import org.matomo.java.tracking.TrackerConfiguration; +import org.matomo.java.tracking.servlet.MatomoTrackerFilter; @Slf4j class MatomoServletTester { diff --git a/test/src/main/java/org/matomo/java/tracking/test/ServletMatomoRequestExample.java b/test/src/main/java/org/matomo/java/tracking/test/ServletMatomoRequestExample.java index 8e1c9547..29990973 100644 --- a/test/src/main/java/org/matomo/java/tracking/test/ServletMatomoRequestExample.java +++ b/test/src/main/java/org/matomo/java/tracking/test/ServletMatomoRequestExample.java @@ -3,7 +3,8 @@ import jakarta.servlet.http.HttpServletRequest; import org.matomo.java.tracking.MatomoRequest; import org.matomo.java.tracking.MatomoTracker; -import org.matomo.java.tracking.ServletMatomoRequest; +import org.matomo.java.tracking.servlet.JakartaHttpServletWrapper; +import org.matomo.java.tracking.servlet.ServletMatomoRequest; /** * This is an example of how to use the ServletMatomoRequest class. @@ -23,7 +24,7 @@ public ServletMatomoRequestExample(MatomoTracker tracker) { */ public void someControllerMethod(HttpServletRequest req) { MatomoRequest matomoRequest = ServletMatomoRequest - .fromServletRequest(req) + .fromServletRequest(JakartaHttpServletWrapper.fromHttpServletRequest(req)) .actionName("Some Controller Action") // ... .build(); From a0bcffbc985458acdb1932307923646e2b796e83 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 13 Nov 2023 13:38:22 +0000 Subject: [PATCH 211/467] [ci skip] prepare release matomo-java-tracker-parent-3.0.2 --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 7 +++---- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 14 insertions(+), 15 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 8458daf4..b151e54e 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2-SNAPSHOT + 3.0.2 ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index 2969603c..d2f2dd74 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2-SNAPSHOT + 3.0.2 ../pom.xml matomo-java-tracker-java11 - 3.0.2-SNAPSHOT + 3.0.2 jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index c03c494f..4f906d1a 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2-SNAPSHOT + 3.0.2 ../pom.xml matomo-java-tracker - 3.0.2-SNAPSHOT + 3.0.2 jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index 4f8ee12c..e0e52efc 100644 --- a/pom.xml +++ b/pom.xml @@ -1,10 +1,9 @@ - + 4.0.0 org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2-SNAPSHOT + 3.0.2 pom Matomo Java Tracker Parent @@ -42,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - HEAD + matomo-java-tracker-parent-3.0.2 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index e290d21b..69a1286b 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2-SNAPSHOT + 3.0.2 ../pom.xml matomo-java-tracker-servlet-jakarta - 3.0.2-SNAPSHOT + 3.0.2 jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index f907b9c5..caa4037d 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2-SNAPSHOT + 3.0.2 ../pom.xml matomo-java-tracker-servlet-javax - 3.0.2-SNAPSHOT + 3.0.2 jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index 5647910a..b269b2c2 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2-SNAPSHOT + 3.0.2 ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index e841a196..52e99e9a 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2-SNAPSHOT + 3.0.2 ../pom.xml From 4f5ace1011019a658eecdc77e66df8719e508fda Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 13 Nov 2023 13:38:24 +0000 Subject: [PATCH 212/467] [ci skip] prepare for next development iteration --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index b151e54e..4c22b327 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2 + 3.0.3-SNAPSHOT ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index d2f2dd74..63147021 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2 + 3.0.3-SNAPSHOT ../pom.xml matomo-java-tracker-java11 - 3.0.2 + 3.0.3-SNAPSHOT jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index 4f906d1a..3b277968 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2 + 3.0.3-SNAPSHOT ../pom.xml matomo-java-tracker - 3.0.2 + 3.0.3-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index e0e52efc..5a83b300 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2 + 3.0.3-SNAPSHOT pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - matomo-java-tracker-parent-3.0.2 + HEAD diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 69a1286b..7caa6656 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2 + 3.0.3-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-jakarta - 3.0.2 + 3.0.3-SNAPSHOT jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index caa4037d..189bc2dd 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2 + 3.0.3-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-javax - 3.0.2 + 3.0.3-SNAPSHOT jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index b269b2c2..8babf1c8 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2 + 3.0.3-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 52e99e9a..27d1cea5 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.2 + 3.0.3-SNAPSHOT ../pom.xml From c62dbaa3baf29741cb5f6d5cb2ac250b356d7cd9 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 13 Nov 2023 15:18:06 +0100 Subject: [PATCH 213/467] Fix Matomo tracker filter property name --- README.md | 1 + .../java/tracking/spring/MatomoTrackerAutoConfiguration.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4db00548..661d3e5e 100644 --- a/README.md +++ b/README.md @@ -192,6 +192,7 @@ The following properties are supported: | matomo.tracker.disable-ssl-cert-validation | If set to true, the SSL certificate of the Matomo server will not be validated. This should only be used for testing purposes. Default: false | | matomo.tracker.disable-ssl-host-verification | If set to true, the SSL host of the Matomo server will not be validated. This should only be used for testing purposes. Default: false | | matomo.tracker.thread-pool-size | The number of threads that will be used to asynchronously send requests. Default: 2 | +| matomo.tracker.filter.enabled | Enables a servlet filter that tracks every request within the application | To ensure the `MatomoTracker` bean is created by the auto configuration, you have to add the following property to your `application.properties` file: diff --git a/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java b/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java index e79bc8f8..e330056b 100644 --- a/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java +++ b/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java @@ -109,14 +109,14 @@ public MatomoTracker matomoTracker(@NonNull TrackerConfiguration trackerConfigur * A {@link FilterRegistrationBean} for the {@link MatomoTrackerFilter}. * *

Only created if a bean of the same type is not already configured. The filter is only registered if - * {@code matomo.filter.enabled} is set to {@code true}. + * {@code matomo.tracker.filter.enabled} is set to {@code true}. * * @param matomoTracker the {@link MatomoTracker} instance (never {@code null}) * @return the {@link FilterRegistrationBean} instance (never {@code null}) */ @Bean @ConditionalOnMissingBean - @ConditionalOnProperty(value = "matomo.filter.enabled", havingValue = "true") + @ConditionalOnProperty(value = "matomo.tracker.filter.enabled", havingValue = "true") @NonNull public FilterRegistrationBean matomoTrackerSpringFilter( @NonNull MatomoTracker matomoTracker From 5ec49fc2986e618b90f69f34de4c1c7329f26f5a Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 13 Nov 2023 15:20:04 +0100 Subject: [PATCH 214/467] Remove ConditionalOnMissingBean for filter --- .../java/tracking/spring/MatomoTrackerAutoConfiguration.java | 1 - 1 file changed, 1 deletion(-) diff --git a/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java b/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java index e330056b..ae7e0376 100644 --- a/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java +++ b/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java @@ -115,7 +115,6 @@ public MatomoTracker matomoTracker(@NonNull TrackerConfiguration trackerConfigur * @return the {@link FilterRegistrationBean} instance (never {@code null}) */ @Bean - @ConditionalOnMissingBean @ConditionalOnProperty(value = "matomo.tracker.filter.enabled", havingValue = "true") @NonNull public FilterRegistrationBean matomoTrackerSpringFilter( From f4f15e1cc68e0c38d6ec96d711146bb775b2b484 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 13 Nov 2023 14:28:45 +0000 Subject: [PATCH 215/467] [ci skip] prepare release matomo-java-tracker-parent-3.0.3 --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 4c22b327..fdd89ca4 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3-SNAPSHOT + 3.0.3 ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index 63147021..55580791 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3-SNAPSHOT + 3.0.3 ../pom.xml matomo-java-tracker-java11 - 3.0.3-SNAPSHOT + 3.0.3 jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index 3b277968..49792d4a 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3-SNAPSHOT + 3.0.3 ../pom.xml matomo-java-tracker - 3.0.3-SNAPSHOT + 3.0.3 jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index 5a83b300..8793158e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3-SNAPSHOT + 3.0.3 pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - HEAD + matomo-java-tracker-parent-3.0.3 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 7caa6656..53b4474c 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3-SNAPSHOT + 3.0.3 ../pom.xml matomo-java-tracker-servlet-jakarta - 3.0.3-SNAPSHOT + 3.0.3 jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 189bc2dd..077a2cf9 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3-SNAPSHOT + 3.0.3 ../pom.xml matomo-java-tracker-servlet-javax - 3.0.3-SNAPSHOT + 3.0.3 jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index 8babf1c8..d157860e 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3-SNAPSHOT + 3.0.3 ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 27d1cea5..5b0e35e1 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3-SNAPSHOT + 3.0.3 ../pom.xml From 33afcd317c5e16885d878b5b136ef07975f8041c Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 13 Nov 2023 14:28:46 +0000 Subject: [PATCH 216/467] [ci skip] prepare for next development iteration --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index fdd89ca4..2fdb9bcf 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3 + 3.0.4-SNAPSHOT ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index 55580791..815b9da1 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3 + 3.0.4-SNAPSHOT ../pom.xml matomo-java-tracker-java11 - 3.0.3 + 3.0.4-SNAPSHOT jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index 49792d4a..86d6e02e 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3 + 3.0.4-SNAPSHOT ../pom.xml matomo-java-tracker - 3.0.3 + 3.0.4-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index 8793158e..3ea0795b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3 + 3.0.4-SNAPSHOT pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - matomo-java-tracker-parent-3.0.3 + HEAD diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 53b4474c..2c0857ca 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3 + 3.0.4-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-jakarta - 3.0.3 + 3.0.4-SNAPSHOT jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 077a2cf9..6618f2ee 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3 + 3.0.4-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-javax - 3.0.3 + 3.0.4-SNAPSHOT jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index d157860e..0324812d 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3 + 3.0.4-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 5b0e35e1..ea9a1c8c 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.3 + 3.0.4-SNAPSHOT ../pom.xml From 7de47f4678fbb77301268975c63b7e13c65269aa Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 13 Nov 2023 16:11:27 +0100 Subject: [PATCH 217/467] Remove X-Real-IP --- .../java/tracking/servlet/ServletMatomoRequest.java | 4 ---- .../tracking/servlet/ServletMatomoRequestTest.java | 13 ------------- 2 files changed, 17 deletions(-) diff --git a/core/src/main/java/org/matomo/java/tracking/servlet/ServletMatomoRequest.java b/core/src/main/java/org/matomo/java/tracking/servlet/ServletMatomoRequest.java index 6b2125d3..b654f3cf 100644 --- a/core/src/main/java/org/matomo/java/tracking/servlet/ServletMatomoRequest.java +++ b/core/src/main/java/org/matomo/java/tracking/servlet/ServletMatomoRequest.java @@ -101,10 +101,6 @@ private static Map collectHeaders( private static String determineVisitorIp( @edu.umd.cs.findbugs.annotations.NonNull HttpServletRequestWrapper request ) { - String realIpHeader = request.getHeader("X-Real-Ip"); - if (isNotEmpty(realIpHeader)) { - return realIpHeader; - } String forwardedForHeader = request.getHeader("X-Forwarded-For"); if (isNotEmpty(forwardedForHeader)) { return forwardedForHeader; diff --git a/core/src/test/java/org/matomo/java/tracking/servlet/ServletMatomoRequestTest.java b/core/src/test/java/org/matomo/java/tracking/servlet/ServletMatomoRequestTest.java index 65eabd2c..2b83b664 100644 --- a/core/src/test/java/org/matomo/java/tracking/servlet/ServletMatomoRequestTest.java +++ b/core/src/test/java/org/matomo/java/tracking/servlet/ServletMatomoRequestTest.java @@ -174,17 +174,4 @@ void determinerVisitorIpFromXForwardedForHeader() { assertThat(matomoRequest.getVisitorIp()).isEqualTo("44.55.66.77"); } - @Test - void determinerVisitorIpFromXRealIpHeader() { - HttpServletRequestWrapper request = HttpServletRequestWrapper - .builder() - .headers(singletonMap("x-real-ip", "44.55.66.77")) - .build(); - - MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); - - MatomoRequest matomoRequest = builder.build(); - assertThat(matomoRequest.getVisitorIp()).isEqualTo("44.55.66.77"); - } - } \ No newline at end of file From e3ca63bee034acafc88f06600365e90eb0d6746d Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 13 Nov 2023 15:17:27 +0000 Subject: [PATCH 218/467] [ci skip] prepare release matomo-java-tracker-parent-3.0.4 --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 2fdb9bcf..88436661 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4-SNAPSHOT + 3.0.4 ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index 815b9da1..f357608d 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4-SNAPSHOT + 3.0.4 ../pom.xml matomo-java-tracker-java11 - 3.0.4-SNAPSHOT + 3.0.4 jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index 86d6e02e..e8bb151e 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4-SNAPSHOT + 3.0.4 ../pom.xml matomo-java-tracker - 3.0.4-SNAPSHOT + 3.0.4 jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index 3ea0795b..aec2df51 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4-SNAPSHOT + 3.0.4 pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - HEAD + matomo-java-tracker-parent-3.0.4 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 2c0857ca..0fb8d7c2 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4-SNAPSHOT + 3.0.4 ../pom.xml matomo-java-tracker-servlet-jakarta - 3.0.4-SNAPSHOT + 3.0.4 jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 6618f2ee..37438887 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4-SNAPSHOT + 3.0.4 ../pom.xml matomo-java-tracker-servlet-javax - 3.0.4-SNAPSHOT + 3.0.4 jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index 0324812d..adfaa64d 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4-SNAPSHOT + 3.0.4 ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index ea9a1c8c..a4af22f5 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4-SNAPSHOT + 3.0.4 ../pom.xml From 033b1678ad112605dcc5328956a08b7c2e7f60d5 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 13 Nov 2023 15:17:28 +0000 Subject: [PATCH 219/467] [ci skip] prepare for next development iteration --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 88436661..457a7231 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4 + 3.0.5-SNAPSHOT ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index f357608d..ca3e836f 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4 + 3.0.5-SNAPSHOT ../pom.xml matomo-java-tracker-java11 - 3.0.4 + 3.0.5-SNAPSHOT jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index e8bb151e..3d04c96d 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4 + 3.0.5-SNAPSHOT ../pom.xml matomo-java-tracker - 3.0.4 + 3.0.5-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index aec2df51..5034a880 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4 + 3.0.5-SNAPSHOT pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - matomo-java-tracker-parent-3.0.4 + HEAD diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 0fb8d7c2..db67380a 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4 + 3.0.5-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-jakarta - 3.0.4 + 3.0.5-SNAPSHOT jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 37438887..c5b50388 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4 + 3.0.5-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-javax - 3.0.4 + 3.0.5-SNAPSHOT jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index adfaa64d..a11462cd 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4 + 3.0.5-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index a4af22f5..78b0239b 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.4 + 3.0.5-SNAPSHOT ../pom.xml From 69290a9b9f6434d26d441420e8c60ca629b7ca70 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 13 Nov 2023 15:48:32 +0000 Subject: [PATCH 220/467] [ci skip] prepare release matomo-java-tracker-parent-3.0.5 --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 457a7231..b0087235 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.5-SNAPSHOT + 3.0.5 ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index ca3e836f..857a7b15 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.5-SNAPSHOT + 3.0.5 ../pom.xml matomo-java-tracker-java11 - 3.0.5-SNAPSHOT + 3.0.5 jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index 3d04c96d..9d2fc986 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.5-SNAPSHOT + 3.0.5 ../pom.xml matomo-java-tracker - 3.0.5-SNAPSHOT + 3.0.5 jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index 5034a880..565a0df0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.5-SNAPSHOT + 3.0.5 pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - HEAD + matomo-java-tracker-parent-3.0.5 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index db67380a..ae662fa1 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.5-SNAPSHOT + 3.0.5 ../pom.xml matomo-java-tracker-servlet-jakarta - 3.0.5-SNAPSHOT + 3.0.5 jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index c5b50388..038284ad 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.5-SNAPSHOT + 3.0.5 ../pom.xml matomo-java-tracker-servlet-javax - 3.0.5-SNAPSHOT + 3.0.5 jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index a11462cd..f7c396da 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.5-SNAPSHOT + 3.0.5 ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 78b0239b..6344067b 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.5-SNAPSHOT + 3.0.5 ../pom.xml From 6f808ccee2611b3d35ac837219982cfe43ec9876 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 13 Nov 2023 15:48:33 +0000 Subject: [PATCH 221/467] [ci skip] prepare for next development iteration --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index b0087235..56312b7c 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.5 + 3.0.6-SNAPSHOT ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index 857a7b15..c20d411a 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.5 + 3.0.6-SNAPSHOT ../pom.xml matomo-java-tracker-java11 - 3.0.5 + 3.0.6-SNAPSHOT jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index 9d2fc986..d4c2083f 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.5 + 3.0.6-SNAPSHOT ../pom.xml matomo-java-tracker - 3.0.5 + 3.0.6-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index 565a0df0..2ff2606f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.5 + 3.0.6-SNAPSHOT pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - matomo-java-tracker-parent-3.0.5 + HEAD diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index ae662fa1..0ca161fd 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.5 + 3.0.6-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-jakarta - 3.0.5 + 3.0.6-SNAPSHOT jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 038284ad..c4119e9a 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.5 + 3.0.6-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-javax - 3.0.5 + 3.0.6-SNAPSHOT jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index f7c396da..cccfcf2f 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.5 + 3.0.6-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 6344067b..6ded240f 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.5 + 3.0.6-SNAPSHOT ../pom.xml From 569e0c15655f3b7e1cb773041ebc5bf39047f2d0 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 13 Nov 2023 16:55:42 +0100 Subject: [PATCH 222/467] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 661d3e5e..0c6b6834 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ Here are the most important changes: ## Javadoc The Javadoc for all versions can be found -[at javadoc.io](https://javadoc.io/doc/org.piwik.java.tracking/matomo-java-tracker/latest/index.html). Thanks to +[at javadoc.io](https://javadoc.io/doc/org.piwik.java.tracking/matomo-java-tracker-core/latest/index.html). Thanks to [javadoc.io](https://javadoc.io) for hosting it. ## Need help? From 4cf15a79fc0b68ce34994e0e16d2c36d5c5a4f59 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 13 Nov 2023 17:12:20 +0100 Subject: [PATCH 223/467] Downgrade Java EE Servlet to Java 8 and use Java 11 in Jakarta EE Servlet --- servlet-jakarta/pom.xml | 2 +- servlet-javax/pom.xml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 0ca161fd..305081a1 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -23,7 +23,7 @@ org.piwik.java.tracking - matomo-java-tracker-core + matomo-java-tracker-java11 ${project.version} diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index c4119e9a..cb84ef9e 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -16,14 +16,14 @@ Integrates Matomo Java Tracker into your javax servlet based application - 11 - 11 + 1.8 + 1.8 org.piwik.java.tracking - matomo-java-tracker-core + matomo-java-tracker ${project.version} From e6274c7398b4cf1714b9f2591a0ed93eac1d5c5a Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 13 Nov 2023 16:21:38 +0000 Subject: [PATCH 224/467] [ci skip] prepare release matomo-java-tracker-parent-3.0.6 --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 56312b7c..024c4a2e 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.6-SNAPSHOT + 3.0.6 ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index c20d411a..dd7d71f1 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.6-SNAPSHOT + 3.0.6 ../pom.xml matomo-java-tracker-java11 - 3.0.6-SNAPSHOT + 3.0.6 jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index d4c2083f..09c48763 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.6-SNAPSHOT + 3.0.6 ../pom.xml matomo-java-tracker - 3.0.6-SNAPSHOT + 3.0.6 jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index 2ff2606f..0e7788de 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.6-SNAPSHOT + 3.0.6 pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - HEAD + matomo-java-tracker-parent-3.0.6 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 305081a1..b4fdc33a 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.6-SNAPSHOT + 3.0.6 ../pom.xml matomo-java-tracker-servlet-jakarta - 3.0.6-SNAPSHOT + 3.0.6 jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index cb84ef9e..3caf58fb 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.6-SNAPSHOT + 3.0.6 ../pom.xml matomo-java-tracker-servlet-javax - 3.0.6-SNAPSHOT + 3.0.6 jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index cccfcf2f..b2fee8c3 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.6-SNAPSHOT + 3.0.6 ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 6ded240f..e257d969 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.6-SNAPSHOT + 3.0.6 ../pom.xml From a11555758ba56f82e9cb8dd540d046693a5ccb00 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 13 Nov 2023 16:21:40 +0000 Subject: [PATCH 225/467] [ci skip] prepare for next development iteration --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 024c4a2e..52437a49 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.6 + 3.0.7-SNAPSHOT ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index dd7d71f1..eb8450a0 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.6 + 3.0.7-SNAPSHOT ../pom.xml matomo-java-tracker-java11 - 3.0.6 + 3.0.7-SNAPSHOT jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index 09c48763..49c176df 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.6 + 3.0.7-SNAPSHOT ../pom.xml matomo-java-tracker - 3.0.6 + 3.0.7-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index 0e7788de..4bafaf39 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.6 + 3.0.7-SNAPSHOT pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - matomo-java-tracker-parent-3.0.6 + HEAD diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index b4fdc33a..3b369a66 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.6 + 3.0.7-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-jakarta - 3.0.6 + 3.0.7-SNAPSHOT jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 3caf58fb..a85ef826 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.6 + 3.0.7-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-javax - 3.0.6 + 3.0.7-SNAPSHOT jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index b2fee8c3..008bb051 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.6 + 3.0.7-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index e257d969..baf0b3c1 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.6 + 3.0.7-SNAPSHOT ../pom.xml From f5932469484ea31217e84712f60f16831b232572 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 22:45:25 +0100 Subject: [PATCH 226/467] build(deps): bump com.github.spotbugs:spotbugs-maven-plugin from 4.7.3.6 to 4.8.1.0 (#159) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4bafaf39..d3fac5b1 100644 --- a/pom.xml +++ b/pom.xml @@ -354,7 +354,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.7.3.6 + 4.8.1.0 org.owasp From 5d63b4139e1f72ac8e9163757914c74cf8953c9c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 22:47:00 +0100 Subject: [PATCH 227/467] build(deps): bump com.github.spotbugs:spotbugs-annotations from 4.8.0 to 4.8.1 (#157) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d3fac5b1..d8f1a6a5 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ com.github.spotbugs spotbugs-annotations - 4.8.0 + 4.8.1 provided From 1d72d89ad0bdd84924cf968c7e3163fceb08d972 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 22:48:58 +0100 Subject: [PATCH 228/467] build(deps): bump actions/checkout from 3 to 4 (#160) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 14952d88..4b99e6e7 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -14,7 +14,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/configure-pages@v3 - uses: actions/jekyll-build-pages@v1 with: From adf8a6a1068551cfc1031324c130174109effd2b Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 14 Nov 2023 10:25:27 +0100 Subject: [PATCH 229/467] Read user ID from remote user servlet attribute (#161) --- .../servlet/HttpServletRequestWrapper.java | 7 ++ .../servlet/ServletMatomoRequest.java | 1 + .../servlet/ServletMatomoRequestTest.java | 96 ++++++++++++------- .../servlet/JakartaHttpServletWrapper.java | 1 + .../JakartaHttpServletWrapperTest.java | 2 + .../servlet/MockHttpServletRequest.java | 7 +- .../servlet/JavaxHttpServletWrapper.java | 1 + .../servlet/JavaxHttpServletWrapperTest.java | 5 +- .../servlet/MockHttpServletRequest.java | 7 +- test/pom.xml | 6 ++ 10 files changed, 86 insertions(+), 47 deletions(-) diff --git a/core/src/main/java/org/matomo/java/tracking/servlet/HttpServletRequestWrapper.java b/core/src/main/java/org/matomo/java/tracking/servlet/HttpServletRequestWrapper.java index 51dfa0d3..b72c087b 100644 --- a/core/src/main/java/org/matomo/java/tracking/servlet/HttpServletRequestWrapper.java +++ b/core/src/main/java/org/matomo/java/tracking/servlet/HttpServletRequestWrapper.java @@ -16,12 +16,19 @@ @Value public class HttpServletRequestWrapper { + @Nullable StringBuffer requestURL; + @Nullable String remoteAddr; + @Nullable + String remoteUser; + + @Nullable Map headers; + @Nullable CookieWrapper[] cookies; /** diff --git a/core/src/main/java/org/matomo/java/tracking/servlet/ServletMatomoRequest.java b/core/src/main/java/org/matomo/java/tracking/servlet/ServletMatomoRequest.java index b654f3cf..ef81608f 100644 --- a/core/src/main/java/org/matomo/java/tracking/servlet/ServletMatomoRequest.java +++ b/core/src/main/java/org/matomo/java/tracking/servlet/ServletMatomoRequest.java @@ -77,6 +77,7 @@ public static MatomoRequest.MatomoRequestBuilder addServletRequestHeaders( .actionUrl(request.getRequestURL() == null ? null : request.getRequestURL().toString()) .headers(collectHeaders(request)) .visitorIp(determineVisitorIp(request)) + .userId(request.getRemoteUser()) .cookies(processCookies(builder, request)); } diff --git a/core/src/test/java/org/matomo/java/tracking/servlet/ServletMatomoRequestTest.java b/core/src/test/java/org/matomo/java/tracking/servlet/ServletMatomoRequestTest.java index 2b83b664..8f11d935 100644 --- a/core/src/test/java/org/matomo/java/tracking/servlet/ServletMatomoRequestTest.java +++ b/core/src/test/java/org/matomo/java/tracking/servlet/ServletMatomoRequestTest.java @@ -11,31 +11,34 @@ class ServletMatomoRequestTest { + private MatomoRequest.MatomoRequestBuilder requestBuilder; + + private HttpServletRequestWrapper.HttpServletRequestWrapperBuilder wrapperBuilder = + HttpServletRequestWrapper.builder(); + @Test void addsServletRequestHeaders() { - HttpServletRequestWrapper request = HttpServletRequestWrapper - .builder() + wrapperBuilder .headers(singletonMap("headername", "headerValue")) .build(); - MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); + whenBuildsRequest(); - MatomoRequest matomoRequest = builder.build(); + MatomoRequest matomoRequest = requestBuilder.build(); assertThat(matomoRequest.getHeaders()).hasSize(1).containsEntry("headername", "headerValue"); } @Test void skipsEmptyHeaderNames() { - HttpServletRequestWrapper request = HttpServletRequestWrapper - .builder() + wrapperBuilder .headers(singletonMap("", "headerValue")) .build(); - MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); + whenBuildsRequest(); - MatomoRequest matomoRequest = builder.build(); + MatomoRequest matomoRequest = requestBuilder.build(); assertThat(matomoRequest.getHeaders()).isEmpty(); } @@ -43,14 +46,13 @@ void skipsEmptyHeaderNames() { @Test void skipsBlankHeaderNames() { - HttpServletRequestWrapper request = HttpServletRequestWrapper - .builder() + wrapperBuilder .headers(singletonMap(" ", "headerValue")) .build(); - MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); + whenBuildsRequest(); - MatomoRequest matomoRequest = builder.build(); + MatomoRequest matomoRequest = requestBuilder.build(); assertThat(matomoRequest.getHeaders()).isEmpty(); } @@ -58,14 +60,13 @@ void skipsBlankHeaderNames() { @ParameterizedTest @ValueSource(strings = {"connection", "content-length", "expect", "host", "upgrade"}) void doesNotAddRestrictedHeaders(String restrictedHeader) { - HttpServletRequestWrapper request = HttpServletRequestWrapper - .builder() + wrapperBuilder .headers(singletonMap(restrictedHeader, "headerValue")) .build(); - MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); + whenBuildsRequest(); - MatomoRequest matomoRequest = builder.build(); + MatomoRequest matomoRequest = requestBuilder.build(); assertThat(matomoRequest.getHeaders()).isEmpty(); } @@ -88,16 +89,15 @@ void failsIfBuilderIsNull() { @Test void extractsVisitorIdFromCookie() { - HttpServletRequestWrapper request = HttpServletRequestWrapper - .builder() + wrapperBuilder .cookies(new CookieWrapper[] { new CookieWrapper("_pk_id.1.1fff", "be40d677d6c7270b.1699801331.") }) .build(); - MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); + whenBuildsRequest(); - MatomoRequest matomoRequest = builder.build(); + MatomoRequest matomoRequest = requestBuilder.build(); assertThat(matomoRequest.getVisitorId()).hasToString("be40d677d6c7270b"); assertThat(matomoRequest.getCookies()) .hasSize(1) @@ -109,21 +109,19 @@ void extractsVisitorIdFromCookie() { strings = {"_pk_ses.1.1fff", "_pk_ref.1.1fff", "_pk_hsr.1.1fff"} ) void extractsMatomoCookies(String cookieName) { - HttpServletRequestWrapper request = HttpServletRequestWrapper - .builder() + wrapperBuilder .cookies(new CookieWrapper[] {new CookieWrapper(cookieName, "anything")}) .build(); - MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); + whenBuildsRequest(); - MatomoRequest matomoRequest = builder.build(); + MatomoRequest matomoRequest = requestBuilder.build(); assertThat(matomoRequest.getCookies()).hasSize(1).containsEntry(cookieName, "anything"); } @Test void extractsSessionIdFromMatomoSessIdCookie() { - HttpServletRequestWrapper request = HttpServletRequestWrapper - .builder() + wrapperBuilder .cookies(new CookieWrapper[] { new CookieWrapper( "MATOMO_SESSID", @@ -132,16 +130,15 @@ void extractsSessionIdFromMatomoSessIdCookie() { }) .build(); - MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); + whenBuildsRequest(); - MatomoRequest matomoRequest = builder.build(); + MatomoRequest matomoRequest = requestBuilder.build(); assertThat(matomoRequest.getSessionId()).isEqualTo("2cbf8b5ba00fbf9ba70853308cd0944a"); } @Test void parsesVisitCustomVariablesFromCookie() { - HttpServletRequestWrapper request = HttpServletRequestWrapper - .builder() + wrapperBuilder .cookies(new CookieWrapper[] { new CookieWrapper( "_pk_cvar.1.1fff", @@ -150,9 +147,9 @@ void parsesVisitCustomVariablesFromCookie() { }) .build(); - MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); + whenBuildsRequest(); - MatomoRequest matomoRequest = builder.build(); + MatomoRequest matomoRequest = requestBuilder.build(); assertThat(matomoRequest.getVisitCustomVariables().get(1).getKey()).isEqualTo( "VAR 1 set, var 2 not set"); assertThat(matomoRequest.getVisitCustomVariables().get(1).getValue()).isEqualTo("yes"); @@ -163,15 +160,42 @@ void parsesVisitCustomVariablesFromCookie() { @Test void determinerVisitorIpFromXForwardedForHeader() { - HttpServletRequestWrapper request = HttpServletRequestWrapper - .builder() + wrapperBuilder .headers(singletonMap("x-forwarded-for", "44.55.66.77")) .build(); - MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); + whenBuildsRequest(); - MatomoRequest matomoRequest = builder.build(); + MatomoRequest matomoRequest = requestBuilder.build(); assertThat(matomoRequest.getVisitorIp()).isEqualTo("44.55.66.77"); } + @Test + void setsActionUrlFromRequestURL() { + wrapperBuilder + .requestURL(new StringBuffer("https://localhost/test")) + .build(); + + whenBuildsRequest(); + + MatomoRequest matomoRequest = requestBuilder.build(); + assertThat(matomoRequest.getActionUrl()).isEqualTo("https://localhost/test"); + } + + @Test + void setsUserIdFromRemoteUser() { + wrapperBuilder + .remoteUser("remote-user") + .build(); + + whenBuildsRequest(); + + MatomoRequest matomoRequest = requestBuilder.build(); + assertThat(matomoRequest.getUserId()).isEqualTo("remote-user"); + } + + private void whenBuildsRequest() { + requestBuilder = ServletMatomoRequest.fromServletRequest(wrapperBuilder.build()); + } + } \ No newline at end of file diff --git a/servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/JakartaHttpServletWrapper.java b/servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/JakartaHttpServletWrapper.java index 51b85554..11a89de7 100644 --- a/servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/JakartaHttpServletWrapper.java +++ b/servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/JakartaHttpServletWrapper.java @@ -41,6 +41,7 @@ public static HttpServletRequestWrapper fromHttpServletRequest(@NonNull HttpServ .builder() .requestURL(request.getRequestURL()) .remoteAddr(request.getRemoteAddr()) + .remoteUser(request.getRemoteUser()) .headers(headers) .cookies(cookies == null ? null : cookies.toArray(new CookieWrapper[0])) .build(); diff --git a/servlet-jakarta/src/test/java/org/matomo/java/tracking/servlet/JakartaHttpServletWrapperTest.java b/servlet-jakarta/src/test/java/org/matomo/java/tracking/servlet/JakartaHttpServletWrapperTest.java index 3d2699a7..d15dbcbf 100644 --- a/servlet-jakarta/src/test/java/org/matomo/java/tracking/servlet/JakartaHttpServletWrapperTest.java +++ b/servlet-jakarta/src/test/java/org/matomo/java/tracking/servlet/JakartaHttpServletWrapperTest.java @@ -14,6 +14,7 @@ void wrapsHttpServletRequest() { MockHttpServletRequest servlet = new MockHttpServletRequest(); servlet.setRequestURL(new StringBuffer("http://localhost")); + servlet.setRemoteUser("remote-user"); servlet.setHeaders(singletonMap("Accept-Language", "en-US,en;q=0.9,de;q=0.8")); servlet.setCookies(List.of(new Cookie("foo", "bar"))); @@ -21,6 +22,7 @@ void wrapsHttpServletRequest() { JakartaHttpServletWrapper.fromHttpServletRequest(servlet); assertThat(httpServletRequestWrapper.getRequestURL()).hasToString("http://localhost"); + assertThat(httpServletRequestWrapper.getRemoteUser()).hasToString("remote-user"); assertThat(httpServletRequestWrapper.getHeaders()) .containsEntry("accept-language", "en-US,en;q=0.9,de;q=0.8"); assertThat(httpServletRequestWrapper.getCookies()) diff --git a/servlet-jakarta/src/test/java/org/matomo/java/tracking/servlet/MockHttpServletRequest.java b/servlet-jakarta/src/test/java/org/matomo/java/tracking/servlet/MockHttpServletRequest.java index d13398a6..856c76db 100644 --- a/servlet-jakarta/src/test/java/org/matomo/java/tracking/servlet/MockHttpServletRequest.java +++ b/servlet-jakarta/src/test/java/org/matomo/java/tracking/servlet/MockHttpServletRequest.java @@ -34,6 +34,8 @@ class MockHttpServletRequest implements HttpServletRequest { private StringBuffer requestURL; + private String remoteUser; + private Map headers = new LinkedHashMap<>(); private Collection cookies; @@ -98,11 +100,6 @@ public String getQueryString() { return null; } - @Override - public String getRemoteUser() { - return null; - } - @Override public boolean isUserInRole(String role) { return false; diff --git a/servlet-javax/src/main/java/org/matomo/java/tracking/servlet/JavaxHttpServletWrapper.java b/servlet-javax/src/main/java/org/matomo/java/tracking/servlet/JavaxHttpServletWrapper.java index cee1a5ac..77c9838b 100644 --- a/servlet-javax/src/main/java/org/matomo/java/tracking/servlet/JavaxHttpServletWrapper.java +++ b/servlet-javax/src/main/java/org/matomo/java/tracking/servlet/JavaxHttpServletWrapper.java @@ -41,6 +41,7 @@ public static HttpServletRequestWrapper fromHttpServletRequest(@NonNull HttpServ .builder() .requestURL(request.getRequestURL()) .remoteAddr(request.getRemoteAddr()) + .remoteUser(request.getRemoteUser()) .headers(headers) .cookies(cookies == null ? null : cookies.toArray(new CookieWrapper[0])) .build(); diff --git a/servlet-javax/src/test/java/org/matomo/java/tracking/servlet/JavaxHttpServletWrapperTest.java b/servlet-javax/src/test/java/org/matomo/java/tracking/servlet/JavaxHttpServletWrapperTest.java index 411a3919..e631d818 100644 --- a/servlet-javax/src/test/java/org/matomo/java/tracking/servlet/JavaxHttpServletWrapperTest.java +++ b/servlet-javax/src/test/java/org/matomo/java/tracking/servlet/JavaxHttpServletWrapperTest.java @@ -1,5 +1,6 @@ package org.matomo.java.tracking.servlet; +import static java.util.Collections.singleton; import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; @@ -14,13 +15,15 @@ void wrapsHttpServletRequest() { MockHttpServletRequest servlet = new MockHttpServletRequest(); servlet.setRequestURL(new StringBuffer("http://localhost")); + servlet.setRemoteUser("remote-user"); servlet.setHeaders(singletonMap("Accept-Language", "en-US,en;q=0.9,de;q=0.8")); - servlet.setCookies(List.of(new Cookie("foo", "bar"))); + servlet.setCookies(singleton(new Cookie("foo", "bar"))); HttpServletRequestWrapper httpServletRequestWrapper = JavaxHttpServletWrapper.fromHttpServletRequest(servlet); assertThat(httpServletRequestWrapper.getRequestURL()).hasToString("http://localhost"); + assertThat(httpServletRequestWrapper.getRemoteUser()).hasToString("remote-user"); assertThat(httpServletRequestWrapper.getHeaders()) .containsEntry("accept-language", "en-US,en;q=0.9,de;q=0.8"); assertThat(httpServletRequestWrapper.getCookies()) diff --git a/servlet-javax/src/test/java/org/matomo/java/tracking/servlet/MockHttpServletRequest.java b/servlet-javax/src/test/java/org/matomo/java/tracking/servlet/MockHttpServletRequest.java index cb0924b3..e0de55b9 100644 --- a/servlet-javax/src/test/java/org/matomo/java/tracking/servlet/MockHttpServletRequest.java +++ b/servlet-javax/src/test/java/org/matomo/java/tracking/servlet/MockHttpServletRequest.java @@ -33,6 +33,8 @@ class MockHttpServletRequest implements HttpServletRequest { private StringBuffer requestURL; + private String remoteUser; + private Map headers = new LinkedHashMap<>(); private Collection cookies; @@ -97,11 +99,6 @@ public String getQueryString() { return null; } - @Override - public String getRemoteUser() { - return null; - } - @Override public boolean isUserInRole(String role) { return false; diff --git a/test/pom.xml b/test/pom.xml index baf0b3c1..9dacc9ae 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -16,6 +16,12 @@ 11 11 + true + true + true + true + true + true true true true From 21784e92483b13f63f2686454d8917f426501c5f Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 14 Nov 2023 13:01:10 +0100 Subject: [PATCH 230/467] Remove Slack notification --- .github/workflows/release.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7db3300c..87ebee84 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,12 +34,4 @@ jobs: publish: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: rtCamp/action-slack-notify@v2 - env: - SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} - SLACK_USERNAME: github-actions - SLACK_ICON: https://m-img.org/spai/w_147+q_lossless+ret_img+to_webp/matomo.org/wp-content/uploads/2020/03/matomo-logo-winner-mobile3.png - SLACK_ICON_EMOJI: ':tada:' - SLACK_TITLE: Matomo Java Tracker ${{ steps.version.outputs.version }} released - SLACK_MESSAGE: "Exciting news! :star2: We're thrilled to share that a new version of the Matomo Java Tracker was staged and will become available soon, packed with awesome features and improvements. :rocket: See https://github.com/matomo-org/matomo-java-tracker/releases for release notes." - SLACK_FOOTER: "Thanks for being awesome! :heart:" \ No newline at end of file + From 514867d50b594897c6343d885964a7622f077c1a Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 14 Nov 2023 15:45:56 +0100 Subject: [PATCH 231/467] Add Digitale Ehrenamtskarte to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0c6b6834..5c1de301 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ Further information on Matomo and Matomo HTTP tracking: Projects that use Matomo Java Tracker: +* [Digitale Ehrenamtskarte](https://github.com/digitalfabrik/entitlementcard) * [Box-c - supports the UNC Libraries' Digital Collections Repository](https://github.com/UNC-Libraries/box-c) * [DSpace - provide durable access to digital resources](https://github.com/thanvanlong/dspace) * [Identifiers.org satellite Web SPA](https://github.com/identifiers-org/cloud-satellite-web-spa) From 94182d1634faeac2b38e791ab1d96df3824accbc Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 14 Nov 2023 18:58:42 +0100 Subject: [PATCH 232/467] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5c1de301..f6f44f71 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,6 @@ Further information on Matomo and Matomo HTTP tracking: Projects that use Matomo Java Tracker: -* [Digitale Ehrenamtskarte](https://github.com/digitalfabrik/entitlementcard) * [Box-c - supports the UNC Libraries' Digital Collections Repository](https://github.com/UNC-Libraries/box-c) * [DSpace - provide durable access to digital resources](https://github.com/thanvanlong/dspace) * [Identifiers.org satellite Web SPA](https://github.com/identifiers-org/cloud-satellite-web-spa) @@ -47,7 +46,9 @@ Projects that use Matomo Java Tracker: * [Resource Catalogue](https://github.com/madgeek-arc/resource-catalogue) * [INCEpTION - A semantic annotation platform offering intelligent assistance and knowledge management](https://github.com/inception-project/inception) * [QualiChain Analytics Intelligent Profiling](https://github.com/JoaoCabrita95/IP) -* And many closed source projects that we are not aware of :smile: +* [Digitale Ehrenamtskarte](https://github.com/digitalfabrik/entitlementcard) +* [skidfuscator-java-obfuscator](https://github.com/skidfuscatordev/skidfuscator-java-obfuscator) +* And many closed source projects that we are not aware of :smile: ## Table of Contents From 78061c98797cef4a591e5ad03eb04872015ec97c Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 14 Nov 2023 19:12:42 +0100 Subject: [PATCH 233/467] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f6f44f71..9c73f141 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ Projects that use Matomo Java Tracker: * [QualiChain Analytics Intelligent Profiling](https://github.com/JoaoCabrita95/IP) * [Digitale Ehrenamtskarte](https://github.com/digitalfabrik/entitlementcard) * [skidfuscator-java-obfuscator](https://github.com/skidfuscatordev/skidfuscator-java-obfuscator) +* [DnA](https://github.com/mercedes-benz/DnA) * And many closed source projects that we are not aware of :smile: ## Table of Contents From 68a7d5be331d53ea52e637759930dae4b7229157 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 14 Nov 2023 23:18:23 +0100 Subject: [PATCH 234/467] Add convenience methods to track typical scenarios (#156) --- .github/workflows/release.yml | 3 +- README.md | 70 ++-- .../org/matomo/java/tracking/ActionType.java | 33 ++ .../matomo/java/tracking/MatomoRequest.java | 2 +- .../matomo/java/tracking/MatomoRequests.java | 359 ++++++++++++++++++ .../matomo/java/tracking/AuthTokenTest.java | 8 +- .../java/tracking/MatomoRequestsTest.java | 318 ++++++++++++++++ .../matomo/java/tracking/TestThrowable.java | 9 + .../matomo/java/tracking/Java11SenderIT.java | 89 +++-- .../matomo/java/tracking/Java8SenderIT.java | 46 ++- .../java/tracking/test/BulkExample.java | 21 +- .../java/tracking/test/SendExample.java | 8 +- .../test/ServletMatomoRequestExample.java | 16 +- 13 files changed, 887 insertions(+), 95 deletions(-) create mode 100644 core/src/main/java/org/matomo/java/tracking/ActionType.java create mode 100644 core/src/main/java/org/matomo/java/tracking/MatomoRequests.java create mode 100644 core/src/test/java/org/matomo/java/tracking/MatomoRequestsTest.java create mode 100644 core/src/test/java/org/matomo/java/tracking/TestThrowable.java diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 87ebee84..a60b8676 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,5 +33,4 @@ jobs: version: ${{ steps.version.outputs.version }} publish: true env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/README.md b/README.md index 9c73f141..d47af883 100644 --- a/README.md +++ b/README.md @@ -236,33 +236,32 @@ To let the Matomo Java Tracker send a request to the Matomo instance, you need t ```java import java.net.URI; import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoRequests; import org.matomo.java.tracking.MatomoTracker; import org.matomo.java.tracking.TrackerConfiguration; import org.matomo.java.tracking.parameters.VisitorId; public class SendExample { - public static void main(String[] args) { + public static void main(String[] args) { + + TrackerConfiguration configuration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://www.yourdomain.com/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("ee6e3dd9ed1b61f5328cf5978b5a8c71") + .logFailedTracking(true) + .build(); - TrackerConfiguration configuration = TrackerConfiguration - .builder() - .apiEndpoint(URI.create("https://www.yourdomain.com/matomo.php")) - .defaultSiteId(1) - .defaultAuthToken("ee6e3dd9ed1b61f5328cf5978b5a8c71") - .logFailedTracking(true) - .build(); + MatomoTracker tracker = new MatomoTracker(configuration); - MatomoTracker tracker = new MatomoTracker(configuration); + tracker.sendRequestAsync(MatomoRequests + .event("Training", "Workout completed", "Bench press", 60.0) + .visitorId(VisitorId.fromString("customer@mail.com")) + .build() + ); - tracker.sendRequestAsync(MatomoRequest - .request() - .actionName("Checkout") - .actionUrl("https://www.yourdomain.com/checkout") - .visitorId(VisitorId.fromString("customer@mail.com")) - .build() - ); - - } + } } ``` @@ -279,6 +278,7 @@ send a bulk request. Place your requests in an _Iterable_ data structure and cal ```java import java.net.URI; import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoRequests; import org.matomo.java.tracking.MatomoTracker; import org.matomo.java.tracking.TrackerConfiguration; import org.matomo.java.tracking.parameters.VisitorId; @@ -299,23 +299,19 @@ public class BulkExample { VisitorId visitorId = VisitorId.fromString("customer@mail.com"); tracker.sendBulkRequestAsync( - MatomoRequest - .request() - .actionName("Checkout") - .actionUrl("https://www.yourdomain.com/checkout") - .visitorId(visitorId) - .build(), - MatomoRequest - .request() - .actionName("Payment") - .actionUrl("https://www.yourdomain.com/checkout") - .visitorId(visitorId) - .build() + MatomoRequests.siteSearch("Running shoes", "Running", 120L) + .visitorId(visitorId).build(), + MatomoRequests.pageView("VelocityStride ProX Running Shoes") + .visitorId(visitorId).build(), + MatomoRequests.ecommerceOrder("QXZ-789LMP", 100.0, 124.0, 19.0, 10.0, 5.0) + .visitorId(visitorId) + .build() ); } } + ``` This will send two requests in a single HTTP call. The requests will be sent asynchronously. @@ -346,7 +342,9 @@ In a Servlet environment, it might be easier to use the `ServletMatomoRequest` c ```java import jakarta.servlet.http.HttpServletRequest; import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoRequests; import org.matomo.java.tracking.MatomoTracker; +import org.matomo.java.tracking.parameters.VisitorId; import org.matomo.java.tracking.servlet.JakartaHttpServletWrapper; import org.matomo.java.tracking.servlet.ServletMatomoRequest; @@ -358,10 +356,16 @@ public class ServletMatomoRequestExample { this.tracker = tracker; } - public void someControllerMethod(HttpServletRequest req) { + public void someControllerMethod(HttpServletRequest request) { MatomoRequest matomoRequest = ServletMatomoRequest - .fromServletRequest(JakartaHttpServletWrapper.fromHttpServletRequest(req)) - .actionName("Some Controller Action") + .addServletRequestHeaders( + MatomoRequests.contentImpression( + "Latest Product Announced", + "Main Blog Text", + "https://www.yourdomain.com/blog/2018/10/01/new-product-launches" + ), + JakartaHttpServletWrapper.fromHttpServletRequest(request) + ).visitorId(VisitorId.fromString("customer@mail.com")) // ... .build(); tracker.sendRequestAsync(matomoRequest); diff --git a/core/src/main/java/org/matomo/java/tracking/ActionType.java b/core/src/main/java/org/matomo/java/tracking/ActionType.java new file mode 100644 index 00000000..5033a776 --- /dev/null +++ b/core/src/main/java/org/matomo/java/tracking/ActionType.java @@ -0,0 +1,33 @@ +package org.matomo.java.tracking; + +import java.util.function.BiConsumer; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +/** + * The type of action performed (download or outlink). + */ +@RequiredArgsConstructor +public enum ActionType { + DOWNLOAD(MatomoRequest.MatomoRequestBuilder::downloadUrl), + LINK(MatomoRequest.MatomoRequestBuilder::outlinkUrl); + + @NonNull + private final BiConsumer consumer; + + /** + * Applies the action URL to the given builder. + * + * @param builder The builder to apply the action URL to. + * @param actionUrl The action URL to apply. + * + * @return The builder with the action URL applied. + */ + public MatomoRequest.MatomoRequestBuilder applyUrl( + @NonNull MatomoRequest.MatomoRequestBuilder builder, + @NonNull String actionUrl + ) { + consumer.accept(builder, actionUrl); + return builder; + } +} diff --git a/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java index bd3be8f4..620ce4fe 100644 --- a/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -449,7 +449,7 @@ public class MatomoRequest { private String contentInteraction; /** - * he unique string identifier for the ecommerce order (required when tracking an ecommerce + * The unique string identifier for the ecommerce order (required when tracking an ecommerce * order). you must set &idgoal=0 in the request to track an ecommerce interaction: cart update or * an ecommerce order. */ diff --git a/core/src/main/java/org/matomo/java/tracking/MatomoRequests.java b/core/src/main/java/org/matomo/java/tracking/MatomoRequests.java new file mode 100644 index 00000000..c5b57c09 --- /dev/null +++ b/core/src/main/java/org/matomo/java/tracking/MatomoRequests.java @@ -0,0 +1,359 @@ +package org.matomo.java.tracking; + +import edu.umd.cs.findbugs.annotations.Nullable; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Optional; +import lombok.NonNull; + +/** + * This class contains static methods for common tracking items to create {@link MatomoRequest} + * objects. + * + *

The intention of this class is to bundle common tracking items in a single place to make + * tracking easier. The methods contain the typical parameters for the tracking item and return a + * {@link MatomoRequest.MatomoRequestBuilder} object to add additional parameters, like the visitor + * ID, a user ID or custom dimensions. + */ +public class MatomoRequests { + + /** + * Creates a {@link MatomoRequest} object for a download or a link action. + * + * @param url The URL of the download or link. Must not be null. + * @param type The type of the action. Either {@link ActionType#DOWNLOAD} or + * {@link ActionType#LINK}. + * + * @return A {@link MatomoRequest.MatomoRequestBuilder} object to add additional parameters. + */ + @edu.umd.cs.findbugs.annotations.NonNull + public static MatomoRequest.MatomoRequestBuilder action( + @NonNull String url, @NonNull ActionType type + ) { + return type.applyUrl(MatomoRequest.request(), url); + } + + /** + * Creates a {@link MatomoRequest} object for a content impression. + * + *

A content impression is a view of a content piece. The content piece can be a product, an + * article, a video, a banner, etc. The content piece can be specified by the parameters + * {@code piece} and {@code target}. The {@code name} parameter is required and should be a + * descriptive name of the content piece. + * + * @param name The name of the content piece, like the name of a product or an article. Must not + * be null. Example: "SuperPhone". + * @param piece The content piece. Can be null. Example: "Smartphone". + * @param target The target of the content piece, like the URL of a product or an article. Can be + * null. Example: "https://example.com/superphone". + * + * @return A {@link MatomoRequest.MatomoRequestBuilder} object to add additional parameters. + */ + @edu.umd.cs.findbugs.annotations.NonNull + public static MatomoRequest.MatomoRequestBuilder contentImpression( + @NonNull String name, @Nullable String piece, @Nullable String target + ) { + return MatomoRequest.request().contentName(name).contentPiece(piece).contentTarget(target); + } + + /** + * Creates a {@link MatomoRequest} object for a content interaction. + * + *

Make sure you have tracked a content impression using the same content name and + * content piece, otherwise it will not count. + * + *

A content interaction is an interaction with a content piece. The content piece can be a + * product, an article, a video, a banner, etc. The content piece can be specified by the + * parameters {@code piece} and {@code target}. The {@code name} parameter is required and should + * be a descriptive name of the content piece. The {@code interaction} parameter is required and + * should be the type of the interaction, like "click" or "add-to-cart". + * + * @param interaction The type of the interaction. Must not be null. Example: "click". + * @param name The name of the content piece, like the name of a product or an article. + * @param piece The content piece. Can be null. Example: "Blog Article XYZ". + * @param target The target of the content piece, like the URL of a product or an article. + * Can be null. Example: "https://example.com/blog/article-xyz". + * + * @return A {@link MatomoRequest.MatomoRequestBuilder} object to add additional parameters. + */ + @edu.umd.cs.findbugs.annotations.NonNull + public static MatomoRequest.MatomoRequestBuilder contentInteraction( + @NonNull String interaction, + @NonNull String name, + @Nullable String piece, + @Nullable String target + ) { + return MatomoRequest + .request() + .contentInteraction(interaction) + .contentName(name) + .contentPiece(piece) + .contentTarget(target); + } + + /** + * Creates a {@link MatomoRequest} object for a crash. + * + *

Requires Crash Analytics plugin to be enabled in the target Matomo instance. + * + *

A crash is an error that causes the application to stop working. The parameters {@code + * message} and {@code stackTrace} are required. The other parameters are optional. The + * {@code type} parameter can be used to specify the type of the crash, like + * {@code NullPointerException}. The {@code category} parameter can be used to specify the + * category of the crash, like payment failure. The {@code location}, {@code line} and + * {@code column} can be used to specify the location of the crash. The {@code location} parameter + * should be the name of the file where the crash occurred. The {@code line} and {@code column} + * parameters should be the line and column number of the crash. + * + * @param message The message of the crash. Must not be null. + * @param type The type of the crash. Can be null. Example: + * {@code java.lang.NullPointerException} + * @param category The category of the crash. Can be null. Example: "payment failure". + * @param stackTrace The stack trace of the crash. Must not be null. + * @param location The location of the crash. Can be null. Example: "MainActivity.java". + * @param line The line number of the crash. Can be null. Example: 42. + * @param column The column number of the crash. Can be null. Example: 23. + * + * @return A {@link MatomoRequest.MatomoRequestBuilder} object to add additional parameters. + */ + @edu.umd.cs.findbugs.annotations.NonNull + public static MatomoRequest.MatomoRequestBuilder crash( + @NonNull String message, + @Nullable String type, + @Nullable String category, + @Nullable String stackTrace, + @Nullable String location, + @Nullable Integer line, + @Nullable Integer column + ) { + return MatomoRequest.request() + .crashMessage(message) + .crashType(type) + .crashCategory(category) + .crashStackTrace(stackTrace) + .crashLocation(location) + .crashLine(line) + .crashColumn(column); + } + + /** + * Creates a {@link MatomoRequest} object for a crash with information from a {@link Throwable}. + * + *

Requires Crash Analytics plugin to be enabled in the target Matomo instance. + * + *

The {@code category} parameter can be used to specify the category of the crash, like + * payment failure. + * + * @param throwable The throwable that caused the crash. Must not be null. + * @param category The category of the crash. Can be null. Example: "payment failure". + * + * @return A {@link MatomoRequest.MatomoRequestBuilder} object to add additional parameters. + */ + @edu.umd.cs.findbugs.annotations.NonNull + public static MatomoRequest.MatomoRequestBuilder crash( + @NonNull Throwable throwable, @Nullable String category + ) { + return MatomoRequest + .request() + .crashMessage(throwable.getMessage()) + .crashCategory(category) + .crashStackTrace(formatStackTrace(throwable)) + .crashType(throwable.getClass().getName()) + .crashLocation( + getFirstStackTraceElement(throwable) + .map(StackTraceElement::getFileName) + .orElse(null)) + .crashLine( + getFirstStackTraceElement(throwable) + .map(StackTraceElement::getLineNumber) + .orElse(null)); + } + + @edu.umd.cs.findbugs.annotations.NonNull + private static String formatStackTrace(@Nullable Throwable throwable) { + StringWriter writer = new StringWriter(); + throwable.printStackTrace(new PrintWriter(writer)); + return writer.toString().trim(); + } + + @edu.umd.cs.findbugs.annotations.NonNull + private static Optional getFirstStackTraceElement( + @edu.umd.cs.findbugs.annotations.NonNull Throwable throwable + ) { + StackTraceElement[] stackTrace = throwable.getStackTrace(); + if (stackTrace == null || stackTrace.length == 0) { + return Optional.empty(); + } + return Optional.of(stackTrace[0]); + } + + /** + * Creates a {@link MatomoRequest} object for a ecommerce cart update (add item, remove item, + * update item). + * + *

The {@code revenue} parameter is required and should be the total revenue of the cart. + * + * @param revenue The total revenue of the cart. Must not be null. + * + * @return A {@link MatomoRequest.MatomoRequestBuilder} object to add additional parameters. + */ + @edu.umd.cs.findbugs.annotations.NonNull + public static MatomoRequest.MatomoRequestBuilder ecommerceCartUpdate( + @NonNull Double revenue + ) { + return MatomoRequest.request().ecommerceRevenue(revenue); + } + + /** + * Creates a {@link MatomoRequest} object for a ecommerce order. + * + *

All revenues (revenue, subtotal, tax, shippingCost, discount) will be individually summed + * and reported in Matomo reports. + * + *

The {@code id} and {@code revenue} parameters are required and should be the order ID and + * the total revenue of the order. The other parameters are optional. The {@code subtotal}, + * {@code tax}, {@code shippingCost} and {@code discount} parameters should be the subtotal, tax, + * shipping cost and discount of the order. + * + *

If the Ecommerce order contains items (products), you must call + * {@link MatomoRequest.MatomoRequestBuilder#ecommerceItems(EcommerceItems)} to add the items to + * the request. + * + * @param id An order ID. Can be a stock keeping unit (SKU) or a unique ID. Must not be + * null. + * @param revenue The total revenue of the order. Must not be null. + * @param subtotal The subtotal of the order. Can be null. + * @param tax The tax of the order. Can be null. + * @param shippingCost The shipping cost of the order. Can be null. + * @param discount The discount of the order. Can be null. + * + * @return A {@link MatomoRequest.MatomoRequestBuilder} object to add additional parameters. + */ + @edu.umd.cs.findbugs.annotations.NonNull + public static MatomoRequest.MatomoRequestBuilder ecommerceOrder( + @NonNull String id, + @NonNull Double revenue, + @Nullable Double subtotal, + @Nullable Double tax, + @Nullable Double shippingCost, + @Nullable Double discount + ) { + return MatomoRequest.request() + .ecommerceId(id) + .ecommerceRevenue(revenue) + .ecommerceSubtotal(subtotal) + .ecommerceTax(tax) + .ecommerceShippingCost(shippingCost) + .ecommerceDiscount(discount); + } + + /** + * Creates a {@link MatomoRequest} object for an event. + * + *

The {@code category} and {@code action} parameters are required and should be the category + * and action of the event. The {@code name} and {@code value} parameters are optional. The + * {@code category} parameter should be a category of the event, like "Travel". The {@code action} + * parameter should be an action of the event, like "Book flight". The {@code name} parameter + * should be the name of the event, like "Flight to Berlin". The {@code value} parameter should be + * the value of the event, like the price of the flight. + * + * @param category The category of the event. Must not be null. Example: "Music" + * @param action The action of the event. Must not be null. Example: "Play" + * @param name The name of the event. Can be null. Example: "Edvard Grieg - The Death of Ase" + * @param value The value of the event. Can be null. Example: 9.99 + * + * @return A {@link MatomoRequest.MatomoRequestBuilder} object to add additional parameters. + */ + @edu.umd.cs.findbugs.annotations.NonNull + public static MatomoRequest.MatomoRequestBuilder event( + @NonNull String category, + @NonNull String action, + @Nullable String name, + @Nullable Double value + ) { + return MatomoRequest.request() + .eventCategory(category) + .eventAction(action) + .eventName(name) + .eventValue(value); + } + + /** + * Creates a {@link MatomoRequest} object for a conversion of a goal of the website. + * + *

The {@code id} parameter is required and should be the ID of the goal. The {@code revenue}, + * {@code name} and {@code value} parameters are optional. The {@code revenue} parameter should be + * the revenue of the conversion. The {@code name} parameter should be the name of the conversion. + * The {@code value} parameter should be the value of the conversion. + * + * @param id The ID of the goal. Must not be null. Example: 1 + * @param revenue The revenue of the conversion. Can be null. Example: 9.99 + * + * @return A {@link MatomoRequest.MatomoRequestBuilder} object to add additional parameters. + */ + @edu.umd.cs.findbugs.annotations.NonNull + public static MatomoRequest.MatomoRequestBuilder goal( + int id, @Nullable Double revenue + ) { + return MatomoRequest.request().goalId(id).ecommerceRevenue(revenue); + } + + /** + * Creates a {@link MatomoRequest} object for a page view. + * + *

The {@code name} parameter is required and should be the name of the page. + * + * @param name The name of the page. Must not be null. Example: "Home" + * + * @return A {@link MatomoRequest.MatomoRequestBuilder} object to add additional parameters. + */ + @edu.umd.cs.findbugs.annotations.NonNull + public static MatomoRequest.MatomoRequestBuilder pageView( + @NonNull String name + ) { + return MatomoRequest.request().actionName(name); + } + + /** + * Creates a {@link MatomoRequest} object for a search. + * + *

These are used to populate reports in Actions > Site Search. + * + *

The {@code query} parameter is required and should be the search query. The {@code + * category} and {@code resultsCount} parameters are optional. The {@code category} parameter + * should be the category of the search, like "Music". The {@code resultsCount} parameter should + * be the number of results of the search. + * + * @param query The search query. Must not be null. Example: "Edvard Grieg" + * @param category The category of the search. Can be null. Example: "Music" + * @param resultsCount The number of results of the search. Can be null. Example: 42 + * + * @return A {@link MatomoRequest.MatomoRequestBuilder} object to add additional parameters. + */ + @edu.umd.cs.findbugs.annotations.NonNull + public static MatomoRequest.MatomoRequestBuilder siteSearch( + @NonNull String query, @Nullable String category, @Nullable Long resultsCount + ) { + return MatomoRequest.request() + .searchQuery(query) + .searchCategory(category) + .searchResultsCount(resultsCount); + } + + /** + * Creates a {@link MatomoRequest} object for a ping. + * + *

Ping requests do not track new actions. If they are sent within the standard visit + * length (see global.ini.php), they will extend the existing visit and the current last action + * for the visit. If after the standard visit length, ping requests will create a new visit using + * the last action in the last known visit. + * + * @return A {@link MatomoRequest.MatomoRequestBuilder} object to add additional parameters. + */ + @edu.umd.cs.findbugs.annotations.NonNull + public static MatomoRequest.MatomoRequestBuilder ping() { + return MatomoRequest.request().ping(true); + } + + +} diff --git a/core/src/test/java/org/matomo/java/tracking/AuthTokenTest.java b/core/src/test/java/org/matomo/java/tracking/AuthTokenTest.java index ab6819ef..169a83e6 100644 --- a/core/src/test/java/org/matomo/java/tracking/AuthTokenTest.java +++ b/core/src/test/java/org/matomo/java/tracking/AuthTokenTest.java @@ -12,7 +12,9 @@ class AuthTokenTest { void determineAuthTokenReturnsAuthTokenFromRequest() { MatomoRequest request = - MatomoRequest.builder().authToken("bdeca231a312ab12cde124131bedfa23").build(); + MatomoRequests + .event("Inbox", "Open", null, null) + .authToken("bdeca231a312ab12cde124131bedfa23").build(); String authToken = AuthToken.determineAuthToken(null, singleton(request), null); @@ -37,7 +39,7 @@ void determineAuthTokenReturnsAuthTokenFromTrackerConfiguration() { @Test void determineAuthTokenFromTrackerConfigurationIfRequestTokenIsEmpty() { - MatomoRequest request = MatomoRequest.builder().authToken("").build(); + MatomoRequest request = MatomoRequests.ping().authToken("").build(); TrackerConfiguration trackerConfiguration = TrackerConfiguration .builder() @@ -54,7 +56,7 @@ void determineAuthTokenFromTrackerConfigurationIfRequestTokenIsEmpty() { @Test void determineAuthTokenFromTrackerConfigurationIfRequestTokenIsBlank() { - MatomoRequest request = MatomoRequest.builder().authToken(" ").build(); + MatomoRequest request = MatomoRequests.pageView("Help").authToken(" ").build(); TrackerConfiguration trackerConfiguration = TrackerConfiguration .builder() diff --git a/core/src/test/java/org/matomo/java/tracking/MatomoRequestsTest.java b/core/src/test/java/org/matomo/java/tracking/MatomoRequestsTest.java new file mode 100644 index 00000000..3aa058d2 --- /dev/null +++ b/core/src/test/java/org/matomo/java/tracking/MatomoRequestsTest.java @@ -0,0 +1,318 @@ +package org.matomo.java.tracking; + + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; + +import org.junit.jupiter.api.Test; + +class MatomoRequestsTest { + + @Test + void actionRequestBuilderContainsDownloadUrl() { + MatomoRequest.MatomoRequestBuilder builder = + MatomoRequests.action("https://example.com", ActionType.DOWNLOAD); + MatomoRequest request = builder.build(); + assertThat(request.getDownloadUrl()) + .isEqualTo("https://example.com"); + } + + @Test + void actionRequestBuilderContainsOutlinkUrl() { + MatomoRequest.MatomoRequestBuilder builder = + MatomoRequests.action("https://example.com", ActionType.LINK); + MatomoRequest request = builder.build(); + assertThat(request.getOutlinkUrl()) + .isEqualTo("https://example.com"); + } + + @Test + void contentImpressionRequestBuilderContainsContentInformation() { + MatomoRequest.MatomoRequestBuilder builder = + MatomoRequests.contentImpression("Product", "Smartphone", "https://example.com/product"); + MatomoRequest request = builder.build(); + assertThat(request) + .isNotNull() + .extracting( + MatomoRequest::getContentName, + MatomoRequest::getContentPiece, + MatomoRequest::getContentTarget + ) + .containsExactly("Product", "Smartphone", "https://example.com/product"); + } + + @Test + void contentInteractionRequestBuilderContainsInteractionAndContentInformation() { + MatomoRequest.MatomoRequestBuilder builder = MatomoRequests.contentInteraction( + "click", + "Product", + "Smartphone", + "https://example.com/product" + ); + MatomoRequest request = builder.build(); + assertThat(request) + .isNotNull() + .extracting( + MatomoRequest::getContentInteraction, + MatomoRequest::getContentName, + MatomoRequest::getContentPiece, + MatomoRequest::getContentTarget + ) + .containsExactly("click", "Product", "Smartphone", "https://example.com/product"); + } + + @Test + void crashRequestBuilderContainsCrashInformation() { + MatomoRequest.MatomoRequestBuilder builder = MatomoRequests.crash( + "Error", + "NullPointerException", + "payment failure", + "stackTrace", + "MainActivity.java", + 42, + 23 + ); + MatomoRequest request = builder.build(); + assertThat(request) + .isNotNull() + .extracting( + MatomoRequest::getCrashMessage, + MatomoRequest::getCrashType, + MatomoRequest::getCrashCategory, + MatomoRequest::getCrashStackTrace, + MatomoRequest::getCrashLocation, + MatomoRequest::getCrashLine, + MatomoRequest::getCrashColumn + ) + .containsExactly( + "Error", + "NullPointerException", + "payment failure", + "stackTrace", + "MainActivity.java", + 42, + 23 + ); + } + + @Test + void crashWithThrowableRequestBuilderContainsCrashInformationFromThrowable() { + Throwable throwable = new NullPointerException("Test NullPointerException"); + MatomoRequest.MatomoRequestBuilder builder = MatomoRequests.crash(throwable, "payment failure"); + MatomoRequest request = builder.build(); + assertThat(request) + .isNotNull() + .extracting(MatomoRequest::getCrashMessage) // Additional assertions for other properties + .isEqualTo("Test NullPointerException"); + } + + @Test + void ecommerceCartUpdateRequestBuilderContainsEcommerceRevenue() { + MatomoRequest.MatomoRequestBuilder builder = MatomoRequests.ecommerceCartUpdate(100.0); + MatomoRequest request = builder.build(); + assertThat(request) + .isNotNull() + .extracting(MatomoRequest::getEcommerceRevenue) + .isEqualTo(100.0); + } + + @Test + void ecommerceOrderRequestBuilderContainsEcommerceOrderInformation() { + MatomoRequest.MatomoRequestBuilder builder = + MatomoRequests.ecommerceOrder("123", 200.0, 180.0, 10.0, 5.0, 5.0); + MatomoRequest request = builder.build(); + assertThat(request) + .isNotNull() + .extracting( + MatomoRequest::getEcommerceId, + MatomoRequest::getEcommerceRevenue, + MatomoRequest::getEcommerceSubtotal, + MatomoRequest::getEcommerceTax, + MatomoRequest::getEcommerceShippingCost, + MatomoRequest::getEcommerceDiscount + ) + .containsExactly("123", 200.0, 180.0, 10.0, 5.0, 5.0); + } + + @Test + void eventRequestBuilderContainsEventInformation() { + MatomoRequest.MatomoRequestBuilder builder = MatomoRequests.event( + "Music", + "Play", + "Edvard Grieg - The Death of Ase", + 9.99 + ); + MatomoRequest request = builder.build(); + assertThat(request) + .isNotNull() + .extracting( + MatomoRequest::getEventCategory, + MatomoRequest::getEventAction, + MatomoRequest::getEventName, + MatomoRequest::getEventValue + ) + .containsExactly("Music", "Play", "Edvard Grieg - The Death of Ase", 9.99); + } + + @Test + void goalRequestBuilderContainsGoalInformation() { + MatomoRequest.MatomoRequestBuilder builder = MatomoRequests.goal( + 1, + 9.99 + ); + MatomoRequest request = builder.build(); + assertThat(request) + .isNotNull() + .extracting( + MatomoRequest::getGoalId, + MatomoRequest::getEcommerceRevenue + ) + .containsExactly(1, 9.99); + } + + @Test + void pageViewRequestBuilderContainsPageViewInformation() { + MatomoRequest.MatomoRequestBuilder builder = MatomoRequests.pageView("About"); + MatomoRequest request = builder.build(); + assertThat(request.getActionName()) + .isEqualTo("About"); + } + + @Test + void searchRequestBuilderContainsSearchInformation() { + MatomoRequest.MatomoRequestBuilder builder = + MatomoRequests.siteSearch("Matomo", "Download", 42L); + MatomoRequest request = builder.build(); + assertThat(request) + .isNotNull() + .extracting( + MatomoRequest::getSearchQuery, + MatomoRequest::getSearchCategory, + MatomoRequest::getSearchResultsCount + ) + .containsExactly("Matomo", "Download", 42L); + } + + @Test + void pingRequestBuilderContainsPingInformation() { + MatomoRequest.MatomoRequestBuilder builder = MatomoRequests.ping(); + MatomoRequest request = builder.build(); + assertThat(request.getPing()).isTrue(); + } + + @Test + void nullParametersThrowNullPointerExceptionForInvalidInput() { + assertThatNullPointerException() + .isThrownBy(() -> MatomoRequests.action(null, ActionType.DOWNLOAD)) + .withMessage("url is marked non-null but is null"); + assertThatNullPointerException() + .isThrownBy(() -> MatomoRequests.contentImpression(null, null, null)) + .withMessage("name is marked non-null but is null"); + // Add similar checks for other methods + } + + @Test + void actionNullUrlThrowsNullPointerException() { + assertThatNullPointerException() + .isThrownBy(() -> MatomoRequests.action(null, ActionType.DOWNLOAD)) + .withMessage("url is marked non-null but is null"); + } + + @Test + void actionNullTypeThrowsNullPointerException() { + assertThatNullPointerException() + .isThrownBy(() -> MatomoRequests.action("https://example.com", null)) + .withMessage("type is marked non-null but is null"); + } + + @Test + void contentImpressionNullNameThrowsNullPointerException() { + assertThatNullPointerException() + .isThrownBy(() -> MatomoRequests.contentImpression( + null, + "Smartphone", + "https://example.com/product" + )) + .withMessage("name is marked non-null but is null"); + } + + // Add similar null checks for other methods... + + @Test + void crashNullMessageThrowsNullPointerException() { + assertThatNullPointerException() + .isThrownBy(() -> MatomoRequests.crash( + null, + "NullPointerException", + "payment failure", + "stackTrace", + "MainActivity.java", + 42, + 23 + )) + .withMessage("message is marked non-null but is null"); + } + + @Test + void crashWithThrowableNullThrowableThrowsNullPointerException() { + assertThatNullPointerException() + .isThrownBy(() -> MatomoRequests.crash(null, "payment failure")) + .withMessage("throwable is marked non-null but is null"); + } + + @Test + void ecommerceCartUpdateNullRevenueThrowsNullPointerException() { + assertThatNullPointerException() + .isThrownBy(() -> MatomoRequests.ecommerceCartUpdate(null)) + .withMessage("revenue is marked non-null but is null"); + } + + @Test + void ecommerceOrderNullIdThrowsNullPointerException() { + assertThatNullPointerException() + .isThrownBy(() -> MatomoRequests.ecommerceOrder(null, 200.0, 180.0, 10.0, 5.0, 5.0)) + .withMessage("id is marked non-null but is null"); + } + + @Test + void eventNullCategoryThrowsNullPointerException() { + assertThatNullPointerException() + .isThrownBy(() -> MatomoRequests.event( + null, + "Play", + "Edvard Grieg - The Death of Ase", + 9.99 + )) + .withMessage("category is marked non-null but is null"); + } + + @Test + void pageViewNullNameThrowsNullPointerException() { + assertThatNullPointerException() + .isThrownBy(() -> MatomoRequests.pageView(null)) + .withMessage("name is marked non-null but is null"); + } + + @Test + void siteSearchNullQueryThrowsNullPointerException() { + assertThatNullPointerException() + .isThrownBy(() -> MatomoRequests.siteSearch(null, "Music", 42L)) + .withMessage("query is marked non-null but is null"); + } + + @Test + void crashDoesNotIncludeStackTraceIfStackTraceOfThrowableIsEmpty() { + MatomoRequest.MatomoRequestBuilder builder = + MatomoRequests.crash(new TestThrowable(), "payment failure"); + MatomoRequest request = builder.build(); + assertThat(request.getCrashMessage()).isEqualTo("message"); + assertThat(request.getCrashType()).isEqualTo("org.matomo.java.tracking.TestThrowable"); + assertThat(request.getCrashCategory()).isEqualTo("payment failure"); + assertThat(request.getCrashStackTrace()).isEqualTo( + "org.matomo.java.tracking.TestThrowable: message"); + assertThat(request.getCrashLocation()).isNull(); + assertThat(request.getCrashLine()).isNull(); + assertThat(request.getCrashColumn()).isNull(); + } + +} \ No newline at end of file diff --git a/core/src/test/java/org/matomo/java/tracking/TestThrowable.java b/core/src/test/java/org/matomo/java/tracking/TestThrowable.java new file mode 100644 index 00000000..daf4a69a --- /dev/null +++ b/core/src/test/java/org/matomo/java/tracking/TestThrowable.java @@ -0,0 +1,9 @@ +package org.matomo.java.tracking; + +class TestThrowable extends Throwable { + + TestThrowable() { + super("message", null, false, false); + } + +} diff --git a/java11/src/test/java/org/matomo/java/tracking/Java11SenderIT.java b/java11/src/test/java/org/matomo/java/tracking/Java11SenderIT.java index 4026e19e..cdfef1df 100644 --- a/java11/src/test/java/org/matomo/java/tracking/Java11SenderIT.java +++ b/java11/src/test/java/org/matomo/java/tracking/Java11SenderIT.java @@ -41,10 +41,13 @@ void failsIfTrackerConfigurationIsNotSet() { CookieManager cookieManager = new CookieManager(); assertThatThrownBy(() -> new Java11Sender( null, - new QueryCreator(TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build()), + new QueryCreator(TrackerConfiguration.builder() + .apiEndpoint(URI.create("http://localhost")) + .build()), HttpClient.newBuilder().cookieHandler(cookieManager).build(), cookieManager.getCookieStore() - )).isInstanceOf(NullPointerException.class).hasMessage("trackerConfiguration is marked non-null but is null"); + )).isInstanceOf(NullPointerException.class) + .hasMessage("trackerConfiguration is marked non-null but is null"); } @Test @@ -55,7 +58,8 @@ void failsIfQueryCreatorIsNotSet() { null, HttpClient.newBuilder().cookieHandler(cookieManager).build(), cookieManager.getCookieStore() - )).isInstanceOf(NullPointerException.class).hasMessage("queryCreator is marked non-null but is null"); + )).isInstanceOf(NullPointerException.class) + .hasMessage("queryCreator is marked non-null but is null"); } @Test @@ -63,10 +67,13 @@ void failsIfHttpClientIsNotSet() { CookieManager cookieManager = new CookieManager(); assertThatThrownBy(() -> new Java11Sender( TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build(), - new QueryCreator(TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build()), + new QueryCreator(TrackerConfiguration.builder() + .apiEndpoint(URI.create("http://localhost")) + .build()), null, cookieManager.getCookieStore() - )).isInstanceOf(NullPointerException.class).hasMessage("httpClient is marked non-null but is null"); + )).isInstanceOf(NullPointerException.class) + .hasMessage("httpClient is marked non-null but is null"); } @Test @@ -74,10 +81,13 @@ void failsIfCookieStoreIsNotSet() { CookieManager cookieManager = new CookieManager(); assertThatThrownBy(() -> new Java11Sender( TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build(), - new QueryCreator(TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build()), + new QueryCreator(TrackerConfiguration.builder() + .apiEndpoint(URI.create("http://localhost")) + .build()), HttpClient.newBuilder().cookieHandler(cookieManager).build(), null - )).isInstanceOf(NullPointerException.class).hasMessage("cookieStore is marked non-null but is null"); + )).isInstanceOf(NullPointerException.class) + .hasMessage("cookieStore is marked non-null but is null"); } @Test @@ -89,7 +99,8 @@ void sendSingleFailsIfQueryIsMalformedWithSocketTimeout() { .build(); givenSender(); - assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) + assertThatThrownBy(() -> sender.sendSingle(MatomoRequests.ecommerceCartUpdate(50.0) + .goalId(0).build())) .isInstanceOf(IllegalArgumentException.class) .hasMessage("invalid URI scheme telnet"); } @@ -108,7 +119,10 @@ void sendSingleFailsIfQueryIsMalformedWithoutSocketTimeout() { } private void givenSender() { - sender = new Java11SenderProvider().provideSender(trackerConfiguration, new QueryCreator(trackerConfiguration)); + sender = new Java11SenderProvider().provideSender( + trackerConfiguration, + new QueryCreator(trackerConfiguration) + ); } @Test @@ -146,7 +160,10 @@ void failsAndLogsIfCouldNotConnectToEndpoint() { @Test void failsAndDoesNotLogIfCouldNotConnectToEndpoint() { trackerConfiguration = - TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost:1234")).userAgent("").build(); + TrackerConfiguration.builder() + .apiEndpoint(URI.create("http://localhost:1234")) + .userAgent("") + .build(); givenSender(); @@ -213,7 +230,8 @@ void skipSslCertificationValidation(WireMockRuntimeInfo wireMockRuntimeInfo) { stubFor(get(urlPathEqualTo("/matomo_ssl.php")).willReturn(status(204))); trackerConfiguration = TrackerConfiguration .builder() - .apiEndpoint(URI.create(String.format("https://localhost:%d/matomo_ssl.php", + .apiEndpoint(URI.create(String.format( + "https://localhost:%d/matomo_ssl.php", wireMockRuntimeInfo.getHttpsPort() ))) .disableSslCertValidation(true) @@ -221,7 +239,7 @@ void skipSslCertificationValidation(WireMockRuntimeInfo wireMockRuntimeInfo) { givenSender(); - sender.sendSingle(new MatomoRequest()); + sender.sendSingle(MatomoRequests.goal(2, 60.0).build()); verify(getRequestedFor(urlPathEqualTo("/matomo_ssl.php"))); @@ -237,9 +255,14 @@ void addsHeadersToSingleRequest(WireMockRuntimeInfo wireMockRuntimeInfo) { givenSender(); - sender.sendSingle(MatomoRequest.request().headers(singletonMap("headerName", "headerValue")).build()); + sender.sendSingle(MatomoRequests.ping() + .headers(singletonMap("headerName", "headerValue")) + .build()); - verify(getRequestedFor(urlPathEqualTo("/matomo.php")).withHeader("headerName", equalTo("headerValue"))); + verify(getRequestedFor(urlPathEqualTo("/matomo.php")).withHeader( + "headerName", + equalTo("headerValue") + )); } @Test @@ -252,9 +275,15 @@ void addsHeadersToBulkRequest(WireMockRuntimeInfo wireMockRuntimeInfo) { givenSender(); - sender.sendBulk(List.of(MatomoRequest.request().headers(singletonMap("headerName", "headerValue")).build()), null); + sender.sendBulk(List.of(MatomoRequests.goal(1, 23.50).headers(singletonMap( + "headerName", + "headerValue" + )).build()), null); - verify(postRequestedFor(urlPathEqualTo("/matomo.php")).withHeader("headerName", equalTo("headerValue"))); + verify(postRequestedFor(urlPathEqualTo("/matomo.php")).withHeader( + "headerName", + equalTo("headerValue") + )); } @Test @@ -267,7 +296,7 @@ void doesNotAddEmptyHeaders(WireMockRuntimeInfo wireMockRuntimeInfo) { givenSender(); - sender.sendBulk(List.of(MatomoRequest.request().headers(emptyMap()).build()), null); + sender.sendBulk(List.of(MatomoRequests.pageView("Contact").headers(emptyMap()).build()), null); verify(postRequestedFor(urlPathEqualTo("/matomo.php")).withoutHeader("headerName")); } @@ -288,12 +317,16 @@ void addsHeadersToBulkAsyncRequest(WireMockRuntimeInfo wireMockRuntimeInfo) { .build()), null); future.join(); - verify(postRequestedFor(urlPathEqualTo("/matomo.php")).withHeader("headerName", equalTo("headerValue"))); + verify(postRequestedFor(urlPathEqualTo("/matomo.php")).withHeader( + "headerName", + equalTo("headerValue") + )); } @Test void failsOnSendSingleAsyncIfRequestIsNull() { - trackerConfiguration = TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost:1234")).build(); + trackerConfiguration = + TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost:1234")).build(); givenSender(); @@ -304,7 +337,8 @@ void failsOnSendSingleAsyncIfRequestIsNull() { @Test void failsOnSendSingleIfRequestIsNull() { - trackerConfiguration = TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost:1234")).build(); + trackerConfiguration = + TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost:1234")).build(); givenSender(); @@ -315,7 +349,8 @@ void failsOnSendSingleIfRequestIsNull() { @Test void failsOnSendBulkAsyncIfRequestsIsNull() { - trackerConfiguration = TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost:1234")).build(); + trackerConfiguration = + TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost:1234")).build(); givenSender(); @@ -326,7 +361,8 @@ void failsOnSendBulkAsyncIfRequestsIsNull() { @Test void failsOnSendBulkAsyncIfRequestIsNull() { - trackerConfiguration = TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost:1234")).build(); + trackerConfiguration = + TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost:1234")).build(); givenSender(); @@ -337,11 +373,16 @@ void failsOnSendBulkAsyncIfRequestIsNull() { @Test void failsOnSendBulkAsyncIfOverrideAuthTokenIsMalformed() { - trackerConfiguration = TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost:1234")).build(); + trackerConfiguration = + TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost:1234")).build(); givenSender(); - assertThatThrownBy(() -> sender.sendBulkAsync(List.of(MatomoRequest.request().build()), "telnet://localhost")) + assertThatThrownBy(() -> sender.sendBulkAsync( + List.of(MatomoRequests + .siteSearch("Special offers", "Products", 5L).build()), + "telnet://localhost" + )) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Auth token must be exactly 32 characters long"); } diff --git a/java8/src/test/java/org/matomo/java/tracking/Java8SenderIT.java b/java8/src/test/java/org/matomo/java/tracking/Java8SenderIT.java index df80df07..4b92535f 100644 --- a/java8/src/test/java/org/matomo/java/tracking/Java8SenderIT.java +++ b/java8/src/test/java/org/matomo/java/tracking/Java8SenderIT.java @@ -35,7 +35,8 @@ static void beforeAll() { @Test void sendSingleFailsIfQueryIsMalformed() { - trackerConfiguration = TrackerConfiguration.builder().apiEndpoint(URI.create("telnet://localhost")).build(); + trackerConfiguration = + TrackerConfiguration.builder().apiEndpoint(URI.create("telnet://localhost")).build(); givenSender(); assertThatThrownBy(() -> sender.sendSingle(new MatomoRequest())) @@ -62,7 +63,8 @@ void failsIfEndpointReturnsNotFound() { @Test void failsIfCouldNotConnectToEndpoint() { - trackerConfiguration = TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost:1234")).build(); + trackerConfiguration = + TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost:1234")).build(); givenSender(); @@ -131,14 +133,17 @@ void skipSslCertificationValidation() { wireMockServer.stubFor(get(urlPathEqualTo("/matomo_ssl.php")).willReturn(status(204))); trackerConfiguration = TrackerConfiguration .builder() - .apiEndpoint(URI.create(String.format("https://localhost:%d/matomo_ssl.php", wireMockServer.httpsPort()))) + .apiEndpoint(URI.create(String.format( + "https://localhost:%d/matomo_ssl.php", + wireMockServer.httpsPort() + ))) .disableSslCertValidation(true) .disableSslHostVerification(true) .build(); givenSender(); - sender.sendSingle(new MatomoRequest()); + sender.sendSingle(MatomoRequests.pageView("Join Us").build()); wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo_ssl.php"))); @@ -149,14 +154,23 @@ void addsHeadersToSingleRequest() { wireMockServer.stubFor(get(urlPathEqualTo("/matomo.php")).willReturn(status(204))); trackerConfiguration = TrackerConfiguration .builder() - .apiEndpoint(URI.create(String.format("http://localhost:%d/matomo.php", wireMockServer.port()))) + .apiEndpoint(URI.create(String.format( + "http://localhost:%d/matomo.php", + wireMockServer.port() + ))) .disableSslCertValidation(true) .disableSslHostVerification(true) .build(); givenSender(); - sender.sendSingle(MatomoRequest.request().headers(singletonMap("headerName", "headerValue")).build()); + sender.sendSingle(MatomoRequests + .action("http://localhost/example", ActionType.LINK) + .headers(singletonMap( + "headerName", + "headerValue" + )) + .build()); wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php")).withHeader( "headerName", @@ -170,7 +184,10 @@ void addsHeadersToBulkRequest() { wireMockServer.stubFor(post(urlPathEqualTo("/matomo.php")).willReturn(status(204))); trackerConfiguration = TrackerConfiguration .builder() - .apiEndpoint(URI.create(String.format("http://localhost:%d/matomo.php", wireMockServer.port()))) + .apiEndpoint(URI.create(String.format( + "http://localhost:%d/matomo.php", + wireMockServer.port() + ))) .disableSslCertValidation(true) .disableSslHostVerification(true) .build(); @@ -178,7 +195,9 @@ void addsHeadersToBulkRequest() { givenSender(); sender.sendBulk( - singleton(MatomoRequest.request().headers(singletonMap("headerName", "headerValue")).build()), + singleton(MatomoRequests.ecommerceCartUpdate(50.0).goalId(0) + .headers(singletonMap("headerName", "headerValue")) + .build()), null ); @@ -195,7 +214,10 @@ void addsHeadersToBulkAsyncRequest() { wireMockServer.stubFor(post(urlPathEqualTo("/matomo.php")).willReturn(status(204))); trackerConfiguration = TrackerConfiguration .builder() - .apiEndpoint(URI.create(String.format("http://localhost:%d/matomo.php", wireMockServer.port()))) + .apiEndpoint(URI.create(String.format( + "http://localhost:%d/matomo.php", + wireMockServer.port() + ))) .disableSslCertValidation(true) .disableSslHostVerification(true) .build(); @@ -217,7 +239,11 @@ void addsHeadersToBulkAsyncRequest() { private void givenSender() { - sender = new Java8Sender(trackerConfiguration, new QueryCreator(trackerConfiguration), Runnable::run); + sender = new Java8Sender( + trackerConfiguration, + new QueryCreator(trackerConfiguration), + Runnable::run + ); } } diff --git a/test/src/main/java/org/matomo/java/tracking/test/BulkExample.java b/test/src/main/java/org/matomo/java/tracking/test/BulkExample.java index 104ac611..d674acf2 100644 --- a/test/src/main/java/org/matomo/java/tracking/test/BulkExample.java +++ b/test/src/main/java/org/matomo/java/tracking/test/BulkExample.java @@ -1,7 +1,7 @@ package org.matomo.java.tracking.test; import java.net.URI; -import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoRequests; import org.matomo.java.tracking.MatomoTracker; import org.matomo.java.tracking.TrackerConfiguration; import org.matomo.java.tracking.parameters.VisitorId; @@ -30,18 +30,13 @@ public static void main(String[] args) { VisitorId visitorId = VisitorId.fromString("customer@mail.com"); tracker.sendBulkRequestAsync( - MatomoRequest - .request() - .actionName("Checkout") - .actionUrl("https://www.yourdomain.com/checkout") - .visitorId(visitorId) - .build(), - MatomoRequest - .request() - .actionName("Payment") - .actionUrl("https://www.yourdomain.com/checkout") - .visitorId(visitorId) - .build() + MatomoRequests.siteSearch("Running shoes", "Running", 120L) + .visitorId(visitorId).build(), + MatomoRequests.pageView("VelocityStride ProX Running Shoes") + .visitorId(visitorId).build(), + MatomoRequests.ecommerceOrder("QXZ-789LMP", 100.0, 124.0, 19.0, 10.0, 5.0) + .visitorId(visitorId) + .build() ); } diff --git a/test/src/main/java/org/matomo/java/tracking/test/SendExample.java b/test/src/main/java/org/matomo/java/tracking/test/SendExample.java index 99e316d7..03e4af5d 100644 --- a/test/src/main/java/org/matomo/java/tracking/test/SendExample.java +++ b/test/src/main/java/org/matomo/java/tracking/test/SendExample.java @@ -1,7 +1,7 @@ package org.matomo.java.tracking.test; import java.net.URI; -import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoRequests; import org.matomo.java.tracking.MatomoTracker; import org.matomo.java.tracking.TrackerConfiguration; import org.matomo.java.tracking.parameters.VisitorId; @@ -28,10 +28,8 @@ public static void main(String[] args) { MatomoTracker tracker = new MatomoTracker(configuration); - tracker.sendRequestAsync(MatomoRequest - .request() - .actionName("Checkout") - .actionUrl("https://www.yourdomain.com/checkout") + tracker.sendRequestAsync(MatomoRequests + .event("Training", "Workout completed", "Bench press", 60.0) .visitorId(VisitorId.fromString("customer@mail.com")) .build() ); diff --git a/test/src/main/java/org/matomo/java/tracking/test/ServletMatomoRequestExample.java b/test/src/main/java/org/matomo/java/tracking/test/ServletMatomoRequestExample.java index 29990973..3a2be7a1 100644 --- a/test/src/main/java/org/matomo/java/tracking/test/ServletMatomoRequestExample.java +++ b/test/src/main/java/org/matomo/java/tracking/test/ServletMatomoRequestExample.java @@ -2,7 +2,9 @@ import jakarta.servlet.http.HttpServletRequest; import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoRequests; import org.matomo.java.tracking.MatomoTracker; +import org.matomo.java.tracking.parameters.VisitorId; import org.matomo.java.tracking.servlet.JakartaHttpServletWrapper; import org.matomo.java.tracking.servlet.ServletMatomoRequest; @@ -20,12 +22,18 @@ public ServletMatomoRequestExample(MatomoTracker tracker) { /** * Example for sending a request from a servlet request. * - * @param req the servlet request + * @param request the servlet request */ - public void someControllerMethod(HttpServletRequest req) { + public void someControllerMethod(HttpServletRequest request) { MatomoRequest matomoRequest = ServletMatomoRequest - .fromServletRequest(JakartaHttpServletWrapper.fromHttpServletRequest(req)) - .actionName("Some Controller Action") + .addServletRequestHeaders( + MatomoRequests.contentImpression( + "Latest Product Announced", + "Main Blog Text", + "https://www.yourdomain.com/blog/2018/10/01/new-product-launches" + ), + JakartaHttpServletWrapper.fromHttpServletRequest(request) + ).visitorId(VisitorId.fromString("customer@mail.com")) // ... .build(); tracker.sendRequestAsync(matomoRequest); From 8cb5afdfa0f7395566288092028d0a8fc193334e Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 14 Nov 2023 23:19:45 +0100 Subject: [PATCH 235/467] Increase minor version to 3.1.0 --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 2 +- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 52437a49..2ff06f6d 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.7-SNAPSHOT + 3.1.0-SNAPSHOT ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index eb8450a0..e7e5d208 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.7-SNAPSHOT + 3.1.0-SNAPSHOT ../pom.xml matomo-java-tracker-java11 - 3.0.7-SNAPSHOT + 3.1.0-SNAPSHOT jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index 49c176df..d6a64764 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.7-SNAPSHOT + 3.1.0-SNAPSHOT ../pom.xml matomo-java-tracker - 3.0.7-SNAPSHOT + 3.1.0-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index d8f1a6a5..11a56c15 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.7-SNAPSHOT + 3.1.0-SNAPSHOT pom Matomo Java Tracker Parent diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 3b369a66..fa334b3b 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.7-SNAPSHOT + 3.1.0-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-jakarta - 3.0.7-SNAPSHOT + 3.1.0-SNAPSHOT jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index a85ef826..8abbc918 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.7-SNAPSHOT + 3.1.0-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-javax - 3.0.7-SNAPSHOT + 3.1.0-SNAPSHOT jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index 008bb051..529e6c73 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.7-SNAPSHOT + 3.1.0-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 9dacc9ae..75bf65ab 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.0.7-SNAPSHOT + 3.1.0-SNAPSHOT ../pom.xml From 8b5981f629425186db6f76e7650dd8798e90f508 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Tue, 14 Nov 2023 22:22:51 +0000 Subject: [PATCH 236/467] [ci skip] prepare release matomo-java-tracker-parent-3.1.0 --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 2ff06f6d..6ad6445d 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.0-SNAPSHOT + 3.1.0 ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index e7e5d208..ac6ca9af 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.0-SNAPSHOT + 3.1.0 ../pom.xml matomo-java-tracker-java11 - 3.1.0-SNAPSHOT + 3.1.0 jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index d6a64764..89e8b4a3 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.0-SNAPSHOT + 3.1.0 ../pom.xml matomo-java-tracker - 3.1.0-SNAPSHOT + 3.1.0 jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index 11a56c15..e4ea991c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.0-SNAPSHOT + 3.1.0 pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - HEAD + matomo-java-tracker-parent-3.1.0 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index fa334b3b..86600ffc 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.0-SNAPSHOT + 3.1.0 ../pom.xml matomo-java-tracker-servlet-jakarta - 3.1.0-SNAPSHOT + 3.1.0 jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 8abbc918..9a7d26b6 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.0-SNAPSHOT + 3.1.0 ../pom.xml matomo-java-tracker-servlet-javax - 3.1.0-SNAPSHOT + 3.1.0 jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index 529e6c73..ccc53088 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.0-SNAPSHOT + 3.1.0 ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 75bf65ab..9d7ab5ba 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.0-SNAPSHOT + 3.1.0 ../pom.xml From 7bf008898514513e4e2da2f81998ea2ed62fbd6e Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Tue, 14 Nov 2023 22:22:53 +0000 Subject: [PATCH 237/467] [ci skip] prepare for next development iteration --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 6ad6445d..f7a2d341 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.0 + 3.1.1-SNAPSHOT ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index ac6ca9af..632ccb97 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.0 + 3.1.1-SNAPSHOT ../pom.xml matomo-java-tracker-java11 - 3.1.0 + 3.1.1-SNAPSHOT jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index 89e8b4a3..9d547052 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.0 + 3.1.1-SNAPSHOT ../pom.xml matomo-java-tracker - 3.1.0 + 3.1.1-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index e4ea991c..d84a9d06 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.0 + 3.1.1-SNAPSHOT pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - matomo-java-tracker-parent-3.1.0 + HEAD diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 86600ffc..9e297e96 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.0 + 3.1.1-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-jakarta - 3.1.0 + 3.1.1-SNAPSHOT jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 9a7d26b6..5a476be0 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.0 + 3.1.1-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-javax - 3.1.0 + 3.1.1-SNAPSHOT jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index ccc53088..34fae83c 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.0 + 3.1.1-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 9d7ab5ba..a87f8df2 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.0 + 3.1.1-SNAPSHOT ../pom.xml From 1da8190d2b47034d018a7741e4e613ec24b109ad Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 14 Nov 2023 23:33:43 +0100 Subject: [PATCH 238/467] Update pom.xml --- test/pom.xml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/test/pom.xml b/test/pom.xml index a87f8df2..89b63e3f 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -16,15 +16,6 @@ 11 11 - true - true - true - true - true - true - true - true - true From 737a169d4b2247d004170f0d03d68e1c6ddbafc3 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Tue, 14 Nov 2023 22:36:14 +0000 Subject: [PATCH 239/467] [ci skip] prepare release matomo-java-tracker-parent-3.1.1 --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index f7a2d341..971970fe 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.1-SNAPSHOT + 3.1.1 ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index 632ccb97..253ec40b 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.1-SNAPSHOT + 3.1.1 ../pom.xml matomo-java-tracker-java11 - 3.1.1-SNAPSHOT + 3.1.1 jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index 9d547052..9beab38f 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.1-SNAPSHOT + 3.1.1 ../pom.xml matomo-java-tracker - 3.1.1-SNAPSHOT + 3.1.1 jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index d84a9d06..8836f87a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.1-SNAPSHOT + 3.1.1 pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - HEAD + matomo-java-tracker-parent-3.1.1 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 9e297e96..5eabf1f0 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.1-SNAPSHOT + 3.1.1 ../pom.xml matomo-java-tracker-servlet-jakarta - 3.1.1-SNAPSHOT + 3.1.1 jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 5a476be0..a3202ba6 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.1-SNAPSHOT + 3.1.1 ../pom.xml matomo-java-tracker-servlet-javax - 3.1.1-SNAPSHOT + 3.1.1 jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index 34fae83c..15f75bda 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.1-SNAPSHOT + 3.1.1 ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 89b63e3f..ee68fdc1 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.1-SNAPSHOT + 3.1.1 ../pom.xml From 4dfab1d8535e47000db5c76d1a1870d0bb488877 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Tue, 14 Nov 2023 22:36:16 +0000 Subject: [PATCH 240/467] [ci skip] prepare for next development iteration --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 971970fe..dedb5618 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.1 + 3.1.2-SNAPSHOT ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index 253ec40b..18bf2b64 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.1 + 3.1.2-SNAPSHOT ../pom.xml matomo-java-tracker-java11 - 3.1.1 + 3.1.2-SNAPSHOT jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index 9beab38f..e4c9a001 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.1 + 3.1.2-SNAPSHOT ../pom.xml matomo-java-tracker - 3.1.1 + 3.1.2-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index 8836f87a..482dd802 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.1 + 3.1.2-SNAPSHOT pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - matomo-java-tracker-parent-3.1.1 + HEAD diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 5eabf1f0..92ae57d2 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.1 + 3.1.2-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-jakarta - 3.1.1 + 3.1.2-SNAPSHOT jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index a3202ba6..7e63d4a4 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.1 + 3.1.2-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-javax - 3.1.1 + 3.1.2-SNAPSHOT jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index 15f75bda..0932c7cd 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.1 + 3.1.2-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index ee68fdc1..41c7fc6c 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.1 + 3.1.2-SNAPSHOT ../pom.xml From be5f02fc936a610b5934ccf628e593ae4823d95d Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Thu, 16 Nov 2023 10:33:33 +0100 Subject: [PATCH 241/467] Update README.md --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index d47af883..11287962 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ dependencies. Release notes can be found here: https://github.com/matomo-org/mat Here are the most important changes: -* Matomo Java Tracker 3.x is compatible with Matomo 4 and 5 +* Matomo Java Tracker 3.1.1 is compatible with Matomo 4 and 5 * less dependencies * new dimension parameter * special types allow to provide valid parameters now @@ -106,7 +106,7 @@ Add a dependency on Matomo Java Tracker using Maven. For Java 8: org.piwik.java.tracking matomo-java-tracker - 3.0.0 + 3.1.1 ``` @@ -116,7 +116,7 @@ For Java 11: org.piwik.java.tracking matomo-java-tracker-java11 - 3.0.0 + 3.1.1 ``` @@ -124,7 +124,7 @@ or Gradle (Java 8): ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker:3.0.0") + implementation("org.piwik.java.tracking:matomo-java-tracker:3.1.1") } ``` @@ -132,20 +132,20 @@ or Gradle (Java 11): ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.0.0") + implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.1.1") } ``` or Gradle with Kotlin DSL (Java 8) ```kotlin -implementation("org.piwik.java.tracking:matomo-java-tracker:3.0.0") +implementation("org.piwik.java.tracking:matomo-java-tracker:3.1.1") ``` or Gradle with Kotlin DSL (Java 11) ```kotlin -implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.0.0") +implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.1.1") ``` ### Spring Boot Module @@ -158,7 +158,7 @@ and allows you to configure the tracker via application properties. Add the foll org.piwik.java.tracking matomo-java-tracker-spring-boot-starter - 3.0.0 + 3.1.1 ``` @@ -166,14 +166,14 @@ or Gradle: ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.0.0") + implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.1.1") } ``` or Gradle with Kotlin DSL ```kotlin -implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.0.0") +implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.1.1") ``` The following properties are supported: @@ -517,7 +517,7 @@ version can be used in your local Maven repository for testing purposes, e.g. org.piwik.java.tracking matomo-java-tracker - 3.0.0-rc2-SNAPSHOT + 3.1.1-rc2-SNAPSHOT ``` From 1e98c8cb8dbcf0f2b6fbfaa68351818f8ea56cad Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Thu, 16 Nov 2023 16:45:53 +0100 Subject: [PATCH 242/467] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 11287962..993b433c 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ Features include: Please prefer the Java 11 version as the Java 8 will become obsolete in the future. +You can find our [Developer Guide here](https://developer.matomo.org/api-reference/tracking-java) + Further information on Matomo and Matomo HTTP tracking: * [Matomo PHP Tracker](https://github.com/matomo-org/matomo-php-tracker) @@ -77,6 +79,8 @@ Here are the most important changes: * special types allow to provide valid parameters now * a new implementation for Java 11 uses the HttpClient available since Java 11 +See also the [Developer Guide here](https://developer.matomo.org/api-reference/tracking-java) + ## Javadoc The Javadoc for all versions can be found @@ -85,6 +89,7 @@ The Javadoc for all versions can be found ## Need help? +* Check the [Developer Guide](https://developer.matomo.org/api-reference/tracking-java) * Open an issue in the [Issue Tracker](https://github.com/matomo-org/matomo-java-tracker/issues) * Use [our GitHub discussions](https://github.com/matomo-org/matomo-java-tracker/discussions) * Ask your question on [Stackoverflow with the tag `matomo`](https://stackoverflow.com/questions/tagged/matomo) From 521fd070eb51838309248c6b7d910d821dc77a06 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Thu, 16 Nov 2023 17:14:20 +0100 Subject: [PATCH 243/467] Add more examples and job to deploy Javadoc --- .editorconfig | 262 +++++++++++++++++- .github/workflows/gh-pages.yml | 32 --- .github/workflows/javadoc.yml | 15 + README.md | 101 +++++++ .../matomo/java/tracking/MatomoTracker.java | 22 +- .../java/tracking/test/ConsumerExample.java | 46 +++ .../java/tracking/test/EcommerceExample.java | 60 ++++ .../java/tracking/test/SendExample.java | 1 - 8 files changed, 491 insertions(+), 48 deletions(-) delete mode 100644 .github/workflows/gh-pages.yml create mode 100644 .github/workflows/javadoc.yml create mode 100644 test/src/main/java/org/matomo/java/tracking/test/ConsumerExample.java create mode 100644 test/src/main/java/org/matomo/java/tracking/test/EcommerceExample.java diff --git a/.editorconfig b/.editorconfig index e391ae95..322b027f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -34,6 +34,10 @@ ij_css_space_before_opening_brace = true ij_css_use_double_quotes = true ij_css_value_alignment = do_not_align +[*.feature] +indent_size = 2 +ij_gherkin_keep_indents_on_empty_lines = false + [*.java] indent_size = 2 max_line_length = 100 @@ -340,6 +344,53 @@ ij_java_wrap_comments = true ij_java_wrap_first_method_in_call_chain = false ij_java_wrap_long_lines = false +[*.less] +indent_size = 2 +ij_less_align_closing_brace_with_properties = false +ij_less_blank_lines_around_nested_selector = 1 +ij_less_blank_lines_between_blocks = 1 +ij_less_block_comment_add_space = false +ij_less_brace_placement = 0 +ij_less_enforce_quotes_on_format = false +ij_less_hex_color_long_format = false +ij_less_hex_color_lower_case = false +ij_less_hex_color_short_format = false +ij_less_hex_color_upper_case = false +ij_less_keep_blank_lines_in_code = 2 +ij_less_keep_indents_on_empty_lines = false +ij_less_keep_single_line_blocks = false +ij_less_line_comment_add_space = false +ij_less_line_comment_at_first_column = false +ij_less_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_less_space_after_colon = true +ij_less_space_before_opening_brace = true +ij_less_use_double_quotes = true +ij_less_value_alignment = 0 + +[*.proto] +indent_size = 2 +tab_width = 2 +ij_continuation_indent_size = 4 +ij_protobuf_keep_blank_lines_in_code = 2 +ij_protobuf_keep_indents_on_empty_lines = false +ij_protobuf_keep_line_breaks = true +ij_protobuf_space_after_comma = true +ij_protobuf_space_before_comma = false +ij_protobuf_spaces_around_assignment_operators = true +ij_protobuf_spaces_within_braces = false +ij_protobuf_spaces_within_brackets = false + +[*.vue] +indent_size = 2 +tab_width = 2 +ij_continuation_indent_size = 4 +ij_vue_indent_children_of_top_level = template +ij_vue_interpolation_new_line_after_start_delimiter = true +ij_vue_interpolation_new_line_before_end_delimiter = true +ij_vue_interpolation_wrap = off +ij_vue_keep_indents_on_empty_lines = false +ij_vue_spaces_within_interpolation_expressions = true + [.editorconfig] ij_editorconfig_align_group_field_declarations = false ij_editorconfig_space_after_colon = false @@ -348,7 +399,7 @@ ij_editorconfig_space_before_colon = false ij_editorconfig_space_before_comma = false ij_editorconfig_spaces_around_assignment_operators = true -[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.pom,*.rng,*.tld,*.wadl,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul,phpunit.xml.dist}] +[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.pom,*.rng,*.tld,*.wadl,*.wsdd,*.wsdl,*.xjb,*.xml,*.xsd,*.xsl,*.xslt,*.xul,phpunit.xml.dist}] ij_xml_align_attributes = true ij_xml_align_text = false ij_xml_attribute_wrap = normal @@ -946,7 +997,198 @@ ij_php_version_weight = 28 ij_php_while_brace_force = never ij_php_while_on_new_line = false -[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,composer.lock,jest.config}] +[{*.gant,*.groovy,*.gy}] +ij_groovy_align_group_field_declarations = false +ij_groovy_align_multiline_array_initializer_expression = false +ij_groovy_align_multiline_assignment = false +ij_groovy_align_multiline_binary_operation = false +ij_groovy_align_multiline_chained_methods = false +ij_groovy_align_multiline_extends_list = false +ij_groovy_align_multiline_for = true +ij_groovy_align_multiline_list_or_map = true +ij_groovy_align_multiline_method_parentheses = false +ij_groovy_align_multiline_parameters = true +ij_groovy_align_multiline_parameters_in_calls = false +ij_groovy_align_multiline_resources = true +ij_groovy_align_multiline_ternary_operation = false +ij_groovy_align_multiline_throws_list = false +ij_groovy_align_named_args_in_map = true +ij_groovy_align_throws_keyword = false +ij_groovy_array_initializer_new_line_after_left_brace = false +ij_groovy_array_initializer_right_brace_on_new_line = false +ij_groovy_array_initializer_wrap = off +ij_groovy_assert_statement_wrap = off +ij_groovy_assignment_wrap = off +ij_groovy_binary_operation_wrap = off +ij_groovy_blank_lines_after_class_header = 0 +ij_groovy_blank_lines_after_imports = 1 +ij_groovy_blank_lines_after_package = 1 +ij_groovy_blank_lines_around_class = 1 +ij_groovy_blank_lines_around_field = 0 +ij_groovy_blank_lines_around_field_in_interface = 0 +ij_groovy_blank_lines_around_method = 1 +ij_groovy_blank_lines_around_method_in_interface = 1 +ij_groovy_blank_lines_before_imports = 1 +ij_groovy_blank_lines_before_method_body = 0 +ij_groovy_blank_lines_before_package = 0 +ij_groovy_block_brace_style = end_of_line +ij_groovy_block_comment_add_space = false +ij_groovy_block_comment_at_first_column = true +ij_groovy_call_parameters_new_line_after_left_paren = false +ij_groovy_call_parameters_right_paren_on_new_line = false +ij_groovy_call_parameters_wrap = off +ij_groovy_catch_on_new_line = false +ij_groovy_class_annotation_wrap = split_into_lines +ij_groovy_class_brace_style = end_of_line +ij_groovy_class_count_to_use_import_on_demand = 5 +ij_groovy_do_while_brace_force = never +ij_groovy_else_on_new_line = false +ij_groovy_enable_groovydoc_formatting = true +ij_groovy_enum_constants_wrap = off +ij_groovy_extends_keyword_wrap = off +ij_groovy_extends_list_wrap = off +ij_groovy_field_annotation_wrap = split_into_lines +ij_groovy_finally_on_new_line = false +ij_groovy_for_brace_force = never +ij_groovy_for_statement_new_line_after_left_paren = false +ij_groovy_for_statement_right_paren_on_new_line = false +ij_groovy_for_statement_wrap = off +ij_groovy_ginq_general_clause_wrap_policy = 2 +ij_groovy_ginq_having_wrap_policy = 1 +ij_groovy_ginq_indent_having_clause = true +ij_groovy_ginq_indent_on_clause = true +ij_groovy_ginq_on_wrap_policy = 1 +ij_groovy_ginq_space_after_keyword = true +ij_groovy_if_brace_force = never +ij_groovy_import_annotation_wrap = 2 +ij_groovy_imports_layout = *,|,javax.**,java.**,|,$* +ij_groovy_indent_case_from_switch = true +ij_groovy_indent_label_blocks = true +ij_groovy_insert_inner_class_imports = false +ij_groovy_keep_blank_lines_before_right_brace = 2 +ij_groovy_keep_blank_lines_in_code = 2 +ij_groovy_keep_blank_lines_in_declarations = 2 +ij_groovy_keep_control_statement_in_one_line = true +ij_groovy_keep_first_column_comment = true +ij_groovy_keep_indents_on_empty_lines = false +ij_groovy_keep_line_breaks = true +ij_groovy_keep_multiple_expressions_in_one_line = false +ij_groovy_keep_simple_blocks_in_one_line = false +ij_groovy_keep_simple_classes_in_one_line = true +ij_groovy_keep_simple_lambdas_in_one_line = true +ij_groovy_keep_simple_methods_in_one_line = true +ij_groovy_label_indent_absolute = false +ij_groovy_label_indent_size = 0 +ij_groovy_lambda_brace_style = end_of_line +ij_groovy_layout_static_imports_separately = true +ij_groovy_line_comment_add_space = false +ij_groovy_line_comment_add_space_on_reformat = false +ij_groovy_line_comment_at_first_column = true +ij_groovy_method_annotation_wrap = split_into_lines +ij_groovy_method_brace_style = end_of_line +ij_groovy_method_call_chain_wrap = off +ij_groovy_method_parameters_new_line_after_left_paren = false +ij_groovy_method_parameters_right_paren_on_new_line = false +ij_groovy_method_parameters_wrap = off +ij_groovy_modifier_list_wrap = false +ij_groovy_names_count_to_use_import_on_demand = 3 +ij_groovy_packages_to_use_import_on_demand = java.awt.*,javax.swing.* +ij_groovy_parameter_annotation_wrap = off +ij_groovy_parentheses_expression_new_line_after_left_paren = false +ij_groovy_parentheses_expression_right_paren_on_new_line = false +ij_groovy_prefer_parameters_wrap = false +ij_groovy_resource_list_new_line_after_left_paren = false +ij_groovy_resource_list_right_paren_on_new_line = false +ij_groovy_resource_list_wrap = off +ij_groovy_space_after_assert_separator = true +ij_groovy_space_after_colon = true +ij_groovy_space_after_comma = true +ij_groovy_space_after_comma_in_type_arguments = true +ij_groovy_space_after_for_semicolon = true +ij_groovy_space_after_quest = true +ij_groovy_space_after_type_cast = true +ij_groovy_space_before_annotation_parameter_list = false +ij_groovy_space_before_array_initializer_left_brace = false +ij_groovy_space_before_assert_separator = false +ij_groovy_space_before_catch_keyword = true +ij_groovy_space_before_catch_left_brace = true +ij_groovy_space_before_catch_parentheses = true +ij_groovy_space_before_class_left_brace = true +ij_groovy_space_before_closure_left_brace = true +ij_groovy_space_before_colon = true +ij_groovy_space_before_comma = false +ij_groovy_space_before_do_left_brace = true +ij_groovy_space_before_else_keyword = true +ij_groovy_space_before_else_left_brace = true +ij_groovy_space_before_finally_keyword = true +ij_groovy_space_before_finally_left_brace = true +ij_groovy_space_before_for_left_brace = true +ij_groovy_space_before_for_parentheses = true +ij_groovy_space_before_for_semicolon = false +ij_groovy_space_before_if_left_brace = true +ij_groovy_space_before_if_parentheses = true +ij_groovy_space_before_method_call_parentheses = false +ij_groovy_space_before_method_left_brace = true +ij_groovy_space_before_method_parentheses = false +ij_groovy_space_before_quest = true +ij_groovy_space_before_record_parentheses = false +ij_groovy_space_before_switch_left_brace = true +ij_groovy_space_before_switch_parentheses = true +ij_groovy_space_before_synchronized_left_brace = true +ij_groovy_space_before_synchronized_parentheses = true +ij_groovy_space_before_try_left_brace = true +ij_groovy_space_before_try_parentheses = true +ij_groovy_space_before_while_keyword = true +ij_groovy_space_before_while_left_brace = true +ij_groovy_space_before_while_parentheses = true +ij_groovy_space_in_named_argument = true +ij_groovy_space_in_named_argument_before_colon = false +ij_groovy_space_within_empty_array_initializer_braces = false +ij_groovy_space_within_empty_method_call_parentheses = false +ij_groovy_spaces_around_additive_operators = true +ij_groovy_spaces_around_assignment_operators = true +ij_groovy_spaces_around_bitwise_operators = true +ij_groovy_spaces_around_equality_operators = true +ij_groovy_spaces_around_lambda_arrow = true +ij_groovy_spaces_around_logical_operators = true +ij_groovy_spaces_around_multiplicative_operators = true +ij_groovy_spaces_around_regex_operators = true +ij_groovy_spaces_around_relational_operators = true +ij_groovy_spaces_around_shift_operators = true +ij_groovy_spaces_within_annotation_parentheses = false +ij_groovy_spaces_within_array_initializer_braces = false +ij_groovy_spaces_within_braces = true +ij_groovy_spaces_within_brackets = false +ij_groovy_spaces_within_cast_parentheses = false +ij_groovy_spaces_within_catch_parentheses = false +ij_groovy_spaces_within_for_parentheses = false +ij_groovy_spaces_within_gstring_injection_braces = false +ij_groovy_spaces_within_if_parentheses = false +ij_groovy_spaces_within_list_or_map = false +ij_groovy_spaces_within_method_call_parentheses = false +ij_groovy_spaces_within_method_parentheses = false +ij_groovy_spaces_within_parentheses = false +ij_groovy_spaces_within_switch_parentheses = false +ij_groovy_spaces_within_synchronized_parentheses = false +ij_groovy_spaces_within_try_parentheses = false +ij_groovy_spaces_within_tuple_expression = false +ij_groovy_spaces_within_while_parentheses = false +ij_groovy_special_else_if_treatment = true +ij_groovy_ternary_operation_wrap = off +ij_groovy_throws_keyword_wrap = off +ij_groovy_throws_list_wrap = off +ij_groovy_use_flying_geese_braces = false +ij_groovy_use_fq_class_names = false +ij_groovy_use_fq_class_names_in_javadoc = true +ij_groovy_use_relative_indents = false +ij_groovy_use_single_class_imports = true +ij_groovy_variable_annotation_wrap = off +ij_groovy_while_brace_force = never +ij_groovy_while_on_new_line = false +ij_groovy_wrap_chain_calls_after_dot = false +ij_groovy_wrap_long_lines = false + +[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.prettierrc,.stylelintrc,bowerrc,composer.lock,jest.config}] indent_size = 2 ij_json_array_wrapping = split_into_lines ij_json_keep_blank_lines_in_code = 0 @@ -963,7 +1205,7 @@ ij_json_spaces_within_braces = false ij_json_spaces_within_brackets = false ij_json_wrap_long_lines = false -[{*.htm,*.html,*.sht,*.shtm,*.shtml}] +[{*.htm,*.html,*.ng,*.sht,*.shtm,*.shtml}] ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3 ij_html_align_attributes = true ij_html_align_text = false @@ -1101,6 +1343,20 @@ ij_markdown_min_lines_between_paragraphs = 1 ij_markdown_wrap_text_if_long = true ij_markdown_wrap_text_inside_blockquotes = true +[{*.pb,*.textproto}] +indent_size = 2 +tab_width = 2 +ij_continuation_indent_size = 4 +ij_prototext_keep_blank_lines_in_code = 2 +ij_prototext_keep_indents_on_empty_lines = false +ij_prototext_keep_line_breaks = true +ij_prototext_space_after_colon = true +ij_prototext_space_after_comma = true +ij_prototext_space_before_colon = false +ij_prototext_space_before_comma = false +ij_prototext_spaces_within_braces = true +ij_prototext_spaces_within_brackets = false + [{*.properties,spring.handlers,spring.schemas}] ij_properties_align_group_field_declarations = false ij_properties_keep_blank_lines = false diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml deleted file mode 100644 index 4b99e6e7..00000000 --- a/.github/workflows/gh-pages.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Deploy GitHub Pages -on: - push: - branches: ["main"] - workflow_dispatch: -permissions: - contents: read - pages: write - id-token: write -concurrency: - group: "pages" - cancel-in-progress: false -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/configure-pages@v3 - - uses: actions/jekyll-build-pages@v1 - with: - source: ./ - destination: ./_site - - uses: actions/upload-pages-artifact@v2 - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: build - steps: - - id: deployment - uses: actions/deploy-pages@v2 diff --git a/.github/workflows/javadoc.yml b/.github/workflows/javadoc.yml new file mode 100644 index 00000000..cfef7c4c --- /dev/null +++ b/.github/workflows/javadoc.yml @@ -0,0 +1,15 @@ +name: Deploy Javadoc +on: + workflow_dispatch: +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: MathieuSoysal/Javadoc-publisher.yml@v2.4.0 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + javadoc-branch: javadoc + java-version: 17 + target-folder: javadoc + project: maven + subdirectories: core java8 java11 servlet-jakarta servlet-javax spring \ No newline at end of file diff --git a/README.md b/README.md index 993b433c..b6fcacf9 100644 --- a/README.md +++ b/README.md @@ -277,6 +277,49 @@ the site with id 1. The request will be sent asynchronously, that means the meth application will not wait for the response of the Matomo server. In the configuration we set the default site id to 1 and configure the default auth token. With `logFailedTracking` we enable logging of failed tracking requests. +If you want to perform an operation after a successful asynchronous call to Matomo, you can use the completable future +result like this: + +```java + +import java.net.URI; +import org.matomo.java.tracking.MatomoRequests; +import org.matomo.java.tracking.MatomoTracker; +import org.matomo.java.tracking.TrackerConfiguration; +import org.matomo.java.tracking.parameters.VisitorId; + +public class ConsumerExample { + + public static void main(String[] args) { + + TrackerConfiguration configuration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://www.yourdomain.com/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("ee6e3dd9ed1b61f5328cf5978b5a8c71") + .logFailedTracking(true) + .build(); + + MatomoTracker tracker = new MatomoTracker(configuration); + + MatomoRequest request = MatomoRequests + .event("Training", "Workout completed", "Bench press", 60.0) + .visitorId(VisitorId.fromString("customer@mail.com")) + .build(); + + tracker.sendRequestAsync(request) + .thenAccept(req -> System.out.printf("Sent request %s%n", req)) + .exceptionally(throwable -> { + System.err.printf("Failed to send request: %s%n", throwable.getMessage()); + return null; + }); + + } + +} + +``` + If you have multiple requests to wish to track, it may be more efficient to send them in a single HTTP call. To do this, send a bulk request. Place your requests in an _Iterable_ data structure and call @@ -334,6 +377,64 @@ Per default every request has the following default parameters: Overwrite these properties as desired. We strongly recommend your to determine the visitor id for every user using a unique identifier, e.g. an email address. If you do not provide a visitor id, a random visitor id will be generated. +Ecommerce requests contain ecommerce items, that can be fluently build: + +```java + +import java.net.URI; +import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoRequests; +import org.matomo.java.tracking.MatomoTracker; +import org.matomo.java.tracking.TrackerConfiguration; +import org.matomo.java.tracking.parameters.EcommerceItem; +import org.matomo.java.tracking.parameters.EcommerceItems; +import org.matomo.java.tracking.parameters.VisitorId; + +public class EcommerceExample { + + public static void main(String[] args) { + + TrackerConfiguration configuration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://www.yourdomain.com/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("ee6e3dd9ed1b61f5328cf5978b5a8c71") + .logFailedTracking(true) + .build(); + + MatomoTracker tracker = new MatomoTracker(configuration); + + tracker.sendBulkRequestAsync(MatomoRequests + .ecommerceCartUpdate(50.0) + .ecommerceItems(EcommerceItems + .builder() + .item(EcommerceItem + .builder() + .sku("XYZ12345") + .name("Matomo - The big book about web analytics") + .category("Education & Teaching") + .price(23.1) + .quantity(2) + .build()) + .item(EcommerceItem + .builder() + .sku("B0C2WV3MRJ") + .name("Matomo for data visualization") + .category("Education & Teaching") + .price(15.0) + .quantity(1) + .build()) + .build()) + .visitorId(VisitorId.fromString("customer@mail.com")) + .build() + ); + + } + +} + +``` + Note that if you want to be able to track campaigns using *Referrers > Campaigns*, you must add the correct URL parameters to your actionUrl. See [Tracking Campaigns](https://matomo.org/docs/tracking-campaigns/) for more information. All HTTP query parameters denoted on the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate getters and setters. See diff --git a/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java index a06de566..a6da2241 100644 --- a/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java +++ b/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -13,6 +13,7 @@ import java.util.Arrays; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; +import java.util.function.Function; import lombok.AccessLevel; import lombok.NonNull; import lombok.Setter; @@ -163,12 +164,12 @@ private void initializeSender() { * {@link #sendRequest(MatomoRequest)} or {@link #sendBulkRequest(Iterable)} instead. * * @param request request to send - * @return completable future to let you know when the request is done + * @return completable future to let you know when the request is done. Contains the request. */ - public CompletableFuture sendRequestAsync( + public CompletableFuture sendRequestAsync( @NonNull MatomoRequest request ) { - return sendRequestAsync(request, null); + return sendRequestAsync(request, Function.identity()); } /** @@ -179,26 +180,23 @@ public CompletableFuture sendRequestAsync( * {@link #sendRequest(MatomoRequest)} or {@link #sendBulkRequest(Iterable)} instead. * * @param request request to send - * @param callback callback that gets executed when response arrives, null allowed - * @return a completable future to let you know when the request is done. The future contains either the request (if - * no callback is specified) or null (if a callback is specified) + * @param callback callback that gets executed when response arrives, must not be null + * @return a completable future to let you know when the request is done. The future contains + * the callback result. * @deprecated Please use {@link MatomoTracker#sendRequestAsync(MatomoRequest)} in combination with * {@link CompletableFuture#thenAccept(Consumer)} instead */ @Deprecated - public CompletableFuture sendRequestAsync( + public CompletableFuture sendRequestAsync( @NonNull MatomoRequest request, - @Nullable Consumer callback + @NonNull Function callback ) { if (trackerConfiguration.isEnabled()) { validate(request); log.debug("Sending async request via GET: {}", request); initializeSender(); CompletableFuture future = sender.sendSingleAsync(request); - if (callback != null) { - return future.thenAccept(callback); - } - return future; + return future.thenApply(callback); } log.warn("Not sending request, because tracker is disabled"); return CompletableFuture.completedFuture(null); diff --git a/test/src/main/java/org/matomo/java/tracking/test/ConsumerExample.java b/test/src/main/java/org/matomo/java/tracking/test/ConsumerExample.java new file mode 100644 index 00000000..3c49b87a --- /dev/null +++ b/test/src/main/java/org/matomo/java/tracking/test/ConsumerExample.java @@ -0,0 +1,46 @@ +package org.matomo.java.tracking.test; + +import java.net.URI; +import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoRequests; +import org.matomo.java.tracking.MatomoTracker; +import org.matomo.java.tracking.TrackerConfiguration; +import org.matomo.java.tracking.parameters.VisitorId; + +/** + * Example for sending a request and performing an action when the request was sent successfully. + */ +public class ConsumerExample { + + /** + * Example for sending a request and performing an action when the request was sent successfully. + * + * @param args ignored + */ + public static void main(String[] args) { + + TrackerConfiguration configuration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://www.yourdomain.com/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("ee6e3dd9ed1b61f5328cf5978b5a8c71") + .logFailedTracking(true) + .build(); + + MatomoTracker tracker = new MatomoTracker(configuration); + + MatomoRequest request = MatomoRequests + .event("Training", "Workout completed", "Bench press", 60.0) + .visitorId(VisitorId.fromString("customer@mail.com")) + .build(); + + tracker.sendRequestAsync(request) + .thenAccept(req -> System.out.printf("Sent request %s%n", req)) + .exceptionally(throwable -> { + System.err.printf("Failed to send request: %s%n", throwable.getMessage()); + return null; + }); + + } + +} diff --git a/test/src/main/java/org/matomo/java/tracking/test/EcommerceExample.java b/test/src/main/java/org/matomo/java/tracking/test/EcommerceExample.java new file mode 100644 index 00000000..584aa32d --- /dev/null +++ b/test/src/main/java/org/matomo/java/tracking/test/EcommerceExample.java @@ -0,0 +1,60 @@ +package org.matomo.java.tracking.test; + +import java.net.URI; +import org.matomo.java.tracking.MatomoRequests; +import org.matomo.java.tracking.MatomoTracker; +import org.matomo.java.tracking.TrackerConfiguration; +import org.matomo.java.tracking.parameters.EcommerceItem; +import org.matomo.java.tracking.parameters.EcommerceItems; +import org.matomo.java.tracking.parameters.VisitorId; + +/** + * Example for sending an ecommerce request. + */ +public class EcommerceExample { + + /** + * Example for sending an ecommerce request. + * + * @param args ignored + */ + public static void main(String[] args) { + + TrackerConfiguration configuration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://www.yourdomain.com/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("ee6e3dd9ed1b61f5328cf5978b5a8c71") + .logFailedTracking(true) + .build(); + + MatomoTracker tracker = new MatomoTracker(configuration); + + tracker.sendBulkRequestAsync(MatomoRequests + .ecommerceCartUpdate(50.0) + .ecommerceItems(EcommerceItems + .builder() + .item(EcommerceItem + .builder() + .sku("XYZ12345") + .name("Matomo - The big book about web analytics") + .category("Education & Teaching") + .price(23.1) + .quantity(2) + .build()) + .item(EcommerceItem + .builder() + .sku("B0C2WV3MRJ") + .name("Matomo for data visualization") + .category("Education & Teaching") + .price(15.0) + .quantity(1) + .build()) + .build()) + .visitorId(VisitorId.fromString("customer@mail.com")) + .build() + ); + + } + +} diff --git a/test/src/main/java/org/matomo/java/tracking/test/SendExample.java b/test/src/main/java/org/matomo/java/tracking/test/SendExample.java index 03e4af5d..1fe55b3e 100644 --- a/test/src/main/java/org/matomo/java/tracking/test/SendExample.java +++ b/test/src/main/java/org/matomo/java/tracking/test/SendExample.java @@ -33,7 +33,6 @@ public static void main(String[] args) { .visitorId(VisitorId.fromString("customer@mail.com")) .build() ); - } } From bd0c06ff0cf945948d0a2ed0bb0ce4437e44e1fe Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Thu, 16 Nov 2023 17:29:35 +0100 Subject: [PATCH 244/467] Update tests --- core/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java | 1 + .../src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java | 1 + .../src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java | 1 + 3 files changed, 3 insertions(+) diff --git a/core/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/core/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java index 51c361b2..04c67f70 100644 --- a/core/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java +++ b/core/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java @@ -106,6 +106,7 @@ void sendsRequestAsyncWithCallback() { matomoTracker.sendRequestAsync(request, request -> { assertThat(request).isEqualTo(request); callbackCalled.set(true); + return null; }); TestSender testSender = senderFactory.getTestSender(); diff --git a/java11/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/java11/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java index 0f0f9c56..bc8fdef0 100644 --- a/java11/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java +++ b/java11/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java @@ -474,6 +474,7 @@ void sendsRequestAsyncAndAcceptsCallback(WireMockRuntimeInfo wireMockRuntimeInfo AtomicBoolean success = new AtomicBoolean(); future = matomoTracker.sendRequestAsync(requestBuilder.build(), request -> { success.set(true); + return null; }); assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { verify(getRequestedFor(urlPathEqualTo("/matomo.php"))); diff --git a/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java index 6d935c8b..f9993f94 100644 --- a/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java +++ b/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java @@ -492,6 +492,7 @@ void sendsRequestAsyncAndAcceptsCallback() { AtomicBoolean success = new AtomicBoolean(); future = matomoTracker.sendRequestAsync(requestBuilder.build(), request -> { success.set(true); + return null; }); assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php"))); From 35eae02b67b999e4ddd6362059d49c17650c8030 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Thu, 16 Nov 2023 17:34:54 +0100 Subject: [PATCH 245/467] Remove subdirectories from Javadoc publisher --- .github/workflows/javadoc.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/javadoc.yml b/.github/workflows/javadoc.yml index cfef7c4c..9f0ab726 100644 --- a/.github/workflows/javadoc.yml +++ b/.github/workflows/javadoc.yml @@ -12,4 +12,3 @@ jobs: java-version: 17 target-folder: javadoc project: maven - subdirectories: core java8 java11 servlet-jakarta servlet-javax spring \ No newline at end of file From 9554c9e75f3b53cc61e662da7cb1e8991f621753 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Thu, 16 Nov 2023 17:42:40 +0100 Subject: [PATCH 246/467] Set custom command in Javadoc publisher --- .github/workflows/javadoc.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/javadoc.yml b/.github/workflows/javadoc.yml index 9f0ab726..bc4b6f2a 100644 --- a/.github/workflows/javadoc.yml +++ b/.github/workflows/javadoc.yml @@ -12,3 +12,5 @@ jobs: java-version: 17 target-folder: javadoc project: maven + custom-command: mvn -B javadoc:javadoc + subdirectories: core java8 java11 servlet-jakarta servlet-javax spring \ No newline at end of file From 37b2ac38559a5f026a135f686f6522355bc51888 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Thu, 16 Nov 2023 17:50:16 +0100 Subject: [PATCH 247/467] Use install target in Javadoc publisher --- .github/workflows/javadoc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/javadoc.yml b/.github/workflows/javadoc.yml index bc4b6f2a..c69865fa 100644 --- a/.github/workflows/javadoc.yml +++ b/.github/workflows/javadoc.yml @@ -12,5 +12,5 @@ jobs: java-version: 17 target-folder: javadoc project: maven - custom-command: mvn -B javadoc:javadoc + custom-command: mvn -B install subdirectories: core java8 java11 servlet-jakarta servlet-javax spring \ No newline at end of file From b8c6333344ea19da4b8e68dacf7c584ece54ff94 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Thu, 16 Nov 2023 17:57:20 +0100 Subject: [PATCH 248/467] Execute Javadoc target in Javadoc publisher --- .github/workflows/javadoc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/javadoc.yml b/.github/workflows/javadoc.yml index c69865fa..c5ac044a 100644 --- a/.github/workflows/javadoc.yml +++ b/.github/workflows/javadoc.yml @@ -12,5 +12,5 @@ jobs: java-version: 17 target-folder: javadoc project: maven - custom-command: mvn -B install + custom-command: mvn -B install javadoc:javadoc subdirectories: core java8 java11 servlet-jakarta servlet-javax spring \ No newline at end of file From f145cd79abc8c8d606d574d86e56071e54cae5a5 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Thu, 16 Nov 2023 18:32:54 +0100 Subject: [PATCH 249/467] Generate GitHub Pages again --- .github/workflows/gh-pages.yml | 32 ++++++++++++++++++++++++++++++++ .github/workflows/javadoc.yml | 16 ---------------- 2 files changed, 32 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/gh-pages.yml delete mode 100644 .github/workflows/javadoc.yml diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml new file mode 100644 index 00000000..4b99e6e7 --- /dev/null +++ b/.github/workflows/gh-pages.yml @@ -0,0 +1,32 @@ +name: Deploy GitHub Pages +on: + push: + branches: ["main"] + workflow_dispatch: +permissions: + contents: read + pages: write + id-token: write +concurrency: + group: "pages" + cancel-in-progress: false +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/configure-pages@v3 + - uses: actions/jekyll-build-pages@v1 + with: + source: ./ + destination: ./_site + - uses: actions/upload-pages-artifact@v2 + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - id: deployment + uses: actions/deploy-pages@v2 diff --git a/.github/workflows/javadoc.yml b/.github/workflows/javadoc.yml deleted file mode 100644 index c5ac044a..00000000 --- a/.github/workflows/javadoc.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Deploy Javadoc -on: - workflow_dispatch: -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: MathieuSoysal/Javadoc-publisher.yml@v2.4.0 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - javadoc-branch: javadoc - java-version: 17 - target-folder: javadoc - project: maven - custom-command: mvn -B install javadoc:javadoc - subdirectories: core java8 java11 servlet-jakarta servlet-javax spring \ No newline at end of file From c370643fe1d9cce0e2dc411944facb92c31b2d0f Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Thu, 16 Nov 2023 23:35:32 +0100 Subject: [PATCH 250/467] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b6fcacf9..2e0849b9 100644 --- a/README.md +++ b/README.md @@ -714,6 +714,7 @@ provide tests for your changes. We use JUnit 5 for testing. Coverage should be a ## Other Java Matomo Tracker Implementations +* [The original Piwik Java Tracker Implementation](https://github.com/summitsystemsinc/piwik-java-tracking) * [Matomo SDK for Android](https://github.com/matomo-org/matomo-sdk-android) * [Piwik SDK Android]( https://github.com/lkogler/piwik-sdk-android) * [piwik-tracking](https://github.com/ralscha/piwik-tracking) From aa2923a526f5ed1a5f36cd68cff2b37afba7946a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Nov 2023 22:14:25 +0100 Subject: [PATCH 251/467] build(deps): bump org.owasp:dependency-check-maven from 8.4.2 to 8.4.3 (#166) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 482dd802..ced31caa 100644 --- a/pom.xml +++ b/pom.xml @@ -359,7 +359,7 @@ org.owasp dependency-check-maven - 8.4.2 + 8.4.3 true From 7f250e855f8439d8f63ac2fca0f60e76a5346d98 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Nov 2023 22:20:39 +0100 Subject: [PATCH 252/467] build(deps-dev): bump org.eclipse.jetty:jetty-servlet from 9.4.53.v20231009 to 10.0.18 (#167) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- servlet-javax/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 7e63d4a4..794fb084 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jetty jetty-servlet - 9.4.53.v20231009 + 10.0.18 test From d4f835704dcdc2a6be143b52354562d018839802 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 21 Nov 2023 00:02:28 +0100 Subject: [PATCH 253/467] Set idgoal to 0 on ecommerce parameters (#162) --- .../matomo/java/tracking/MatomoRequest.java | 72 ++++++---- .../matomo/java/tracking/MatomoTracker.java | 18 ++- .../matomo/java/tracking/QueryCreator.java | 17 ++- .../java/tracking/RequestValidator.java | 16 +-- .../java/tracking/TrackingParameter.java | 4 + .../tracking/TrackingParameterMethod.java | 24 +++- .../matomo/java/tracking/MatomoTrackerIT.java | 15 ++ .../java/tracking/QueryCreatorTest.java | 132 +++++++++++++++++- .../java/tracking/RequestValidatorTest.java | 95 +------------ .../tracking/TrackingParameterMethodTest.java | 22 +++ .../matomo/java/tracking/MatomoTrackerIT.java | 6 +- .../matomo/java/tracking/MatomoTrackerIT.java | 4 +- 12 files changed, 279 insertions(+), 146 deletions(-) diff --git a/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java index 620ce4fe..860593eb 100644 --- a/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -41,7 +41,7 @@ /** * A class that implements the - * Matomo Tracking HTTP API. These requests can be sent using {@link MatomoTracker}. + * Matomo Tracking HTTP API. These requests can be sent using {@link MatomoTracker}. * * @author brettcsorba */ @@ -55,7 +55,7 @@ public class MatomoRequest { /** * The ID of the website we're tracking a visit/action for. Only needed, if no default site id is - * configured + * configured. */ @TrackingParameter(name = "rec") @Default @@ -63,9 +63,12 @@ public class MatomoRequest { /** * The ID of the website we're tracking a visit/action for. Only needed, if no default site id is - * configured + * configured. */ - @TrackingParameter(name = "idsite") + @TrackingParameter( + name = "idsite", + min = 1 + ) private Integer siteId; /** @@ -130,7 +133,7 @@ public class MatomoRequest { * database). Then you would manually increment the counts by one on each new visit or "session", * depending on how you choose to define a visit. */ - @TrackingParameter(name = "_idvc") + @TrackingParameter(name = "_idvc", min = 0) private Integer visitorVisitCount; /** @@ -156,9 +159,9 @@ public class MatomoRequest { /** * The campaign keyword (see * Tracking Campaigns). Used to - * populate - * the Referrers > Campaigns report (clicking on a campaign loads all keywords for - * this campaign). This parameter will only be used for the first pageview of a visit. + * populate the Referrers > Campaigns report (clicking on a campaign loads all + * keywords for this campaign). This parameter will only be used for the first pageview of a + * visit. */ @TrackingParameter(name = "_rck") private String campaignKeyword; @@ -172,19 +175,31 @@ public class MatomoRequest { /** * The current hour (local time). */ - @TrackingParameter(name = "h") + @TrackingParameter( + name = "h", + min = 0, + max = 23 + ) private Integer currentHour; /** * The current minute (local time). */ - @TrackingParameter(name = "m") + @TrackingParameter( + name = "m", + min = 0, + max = 59 + ) private Integer currentMinute; /** * The current second (local time). */ - @TrackingParameter(name = "s") + @TrackingParameter( + name = "s", + min = 0, + max = 59 + ) private Integer currentSecond; /** @@ -321,7 +336,10 @@ public class MatomoRequest { * results displayed on the results page. When keywords are tracked with &search_count=0 they will * appear in the "No Result Search Keyword" report. */ - @TrackingParameter(name = "search_count") + @TrackingParameter( + name = "search_count", + min = 0 + ) private Long searchResultsCount; /** @@ -335,9 +353,9 @@ public class MatomoRequest { /** * If specified, the tracking request will trigger a conversion for the goal of the website being - * tracked with this ID. + * tracked with this ID. The value 0 tracks an ecommerce interaction. */ - @TrackingParameter(name = "idgoal") + @TrackingParameter(name = "idgoal", min = 0) private Integer goalId; /** @@ -365,39 +383,39 @@ public class MatomoRequest { /** * How long it took to connect to server. */ - @TrackingParameter(name = "pf_net") + @TrackingParameter(name = "pf_net", min = 0) private Long networkTime; /** * How long it took the server to generate page. */ - @TrackingParameter(name = "pf_srv") + @TrackingParameter(name = "pf_srv", min = 0) private Long serverTime; /** * How long it takes the browser to download the response from the server. */ - @TrackingParameter(name = "pf_tfr") + @TrackingParameter(name = "pf_tfr", min = 0) private Long transferTime; /** * How long the browser spends loading the webpage after the response was fully received until the * user can start interacting with it. */ - @TrackingParameter(name = "pf_dm1") + @TrackingParameter(name = "pf_dm1", min = 0) private Long domProcessingTime; /** * How long it takes for the browser to load media and execute any Javascript code listening for * the DOMContentLoaded event. */ - @TrackingParameter(name = "pf_dm2") + @TrackingParameter(name = "pf_dm2", min = 0) private Long domCompletionTime; /** * How long it takes the browser to execute Javascript code waiting for the window.load event. */ - @TrackingParameter(name = "pf_onl") + @TrackingParameter(name = "pf_onl", min = 0) private Long onloadTime; /** @@ -450,8 +468,7 @@ public class MatomoRequest { /** * The unique string identifier for the ecommerce order (required when tracking an ecommerce - * order). you must set &idgoal=0 in the request to track an ecommerce interaction: cart update or - * an ecommerce order. + * order). */ @TrackingParameter(name = "ec_id") private String ecommerceId; @@ -546,13 +563,13 @@ public class MatomoRequest { /** * An override value for the visitor's latitude, eg 22.456. */ - @TrackingParameter(name = "lat") + @TrackingParameter(name = "lat", min = -90, max = 90) private Double visitorLatitude; /** * An override value for the visitor's longitude, eg 22.456. */ - @TrackingParameter(name = "long") + @TrackingParameter(name = "long", min = -180, max = 180) private Double visitorLongitude; /** @@ -670,7 +687,7 @@ public class MatomoRequest { * *

Optional for crash analytics */ - @TrackingParameter(name = "cra_rl") + @TrackingParameter(name = "cra_rl", min = 0) private Integer crashLine; /** @@ -678,15 +695,14 @@ public class MatomoRequest { * *

Optional for crash analytics */ - @TrackingParameter(name = "cra_rc") + @TrackingParameter(name = "cra_rc", min = 0) private Integer crashColumn; /** * The Matomo session ID sent as a cookie {@code MATOMO_SESSID}. * *

If not null a cookie with the name {@code MATOMO_SESSID} will be sent with the value of - * this - * parameter. + * this parameter. */ private String sessionId; diff --git a/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java index a6da2241..51a585d5 100644 --- a/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java +++ b/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -142,7 +142,7 @@ public MatomoTracker( public void sendRequest(@NonNull MatomoRequest request) { if (trackerConfiguration.isEnabled()) { log.debug("Sending request via GET: {}", request); - validate(request); + applyGoalIdAndCheckSiteId(request); initializeSender(); sender.sendSingle(request); } else { @@ -192,7 +192,7 @@ public CompletableFuture sendRequestAsync( @NonNull Function callback ) { if (trackerConfiguration.isEnabled()) { - validate(request); + applyGoalIdAndCheckSiteId(request); log.debug("Sending async request via GET: {}", request); initializeSender(); CompletableFuture future = sender.sendSingleAsync(request); @@ -202,9 +202,17 @@ public CompletableFuture sendRequestAsync( return CompletableFuture.completedFuture(null); } - private void validate( + private void applyGoalIdAndCheckSiteId( @NonNull MatomoRequest request ) { + if (request.getGoalId() == null && ( + request.getEcommerceId() != null || request.getEcommerceRevenue() != null + || request.getEcommerceDiscount() != null || request.getEcommerceItems() != null + || request.getEcommerceLastOrderTimestamp() != null + || request.getEcommerceShippingCost() != null || request.getEcommerceSubtotal() != null + || request.getEcommerceTax() != null)) { + request.setGoalId(0); + } if (trackerConfiguration.getDefaultSiteId() == null && request.getSiteId() == null) { throw new IllegalArgumentException("No default site ID and no request site ID is given"); } @@ -256,7 +264,7 @@ public void sendBulkRequest( ) { if (trackerConfiguration.isEnabled()) { for (MatomoRequest request : requests) { - validate(request); + applyGoalIdAndCheckSiteId(request); } log.debug("Sending requests via POST: {}", requests); initializeSender(); @@ -311,7 +319,7 @@ public CompletableFuture sendBulkRequestAsync( ) { if (trackerConfiguration.isEnabled()) { for (MatomoRequest request : requests) { - validate(request); + applyGoalIdAndCheckSiteId(request); } log.debug("Sending async requests via POST: {}", requests); initializeSender(); diff --git a/core/src/main/java/org/matomo/java/tracking/QueryCreator.java b/core/src/main/java/org/matomo/java/tracking/QueryCreator.java index 55211fea..3bfbd0b2 100644 --- a/core/src/main/java/org/matomo/java/tracking/QueryCreator.java +++ b/core/src/main/java/org/matomo/java/tracking/QueryCreator.java @@ -28,7 +28,8 @@ @RequiredArgsConstructor class QueryCreator { - private static final TrackingParameterMethod[] TRACKING_PARAMETER_METHODS = initializeTrackingParameterMethods(); + private static final TrackingParameterMethod[] TRACKING_PARAMETER_METHODS = + initializeTrackingParameterMethods(); private final TrackerConfiguration trackerConfiguration; @@ -44,15 +45,20 @@ private static TrackingParameterMethod[] initializeTrackingParameterMethods() { } private static void addMethods( - Collection methods, Member member, TrackingParameter trackingParameter + Collection methods, + Member member, + TrackingParameter trackingParameter ) { try { - for (PropertyDescriptor pd : Introspector.getBeanInfo(MatomoRequest.class).getPropertyDescriptors()) { + for (PropertyDescriptor pd : Introspector.getBeanInfo(MatomoRequest.class) + .getPropertyDescriptors()) { if (member.getName().equals(pd.getName())) { String regex = trackingParameter.regex(); methods.add(TrackingParameterMethod .builder() .parameterName(trackingParameter.name()) + .min(trackingParameter.min()) + .max(trackingParameter.max()) .maxLength(trackingParameter.maxLength()) .method(pd.getReadMethod()) .pattern(regex == null || regex.isEmpty() || regex.trim().isEmpty() ? null : @@ -97,7 +103,10 @@ String createQuery( for (Entry entry : request.getDimensions().entrySet()) { if (entry.getKey() != null && entry.getValue() != null) { appendAmpersand(query); - query.append("dimension").append(entry.getKey()).append('=').append(encode(entry.getValue().toString())); + query.append("dimension") + .append(entry.getKey()) + .append('=') + .append(encode(entry.getValue().toString())); } } } diff --git a/core/src/main/java/org/matomo/java/tracking/RequestValidator.java b/core/src/main/java/org/matomo/java/tracking/RequestValidator.java index a9233c4e..f604e1c5 100644 --- a/core/src/main/java/org/matomo/java/tracking/RequestValidator.java +++ b/core/src/main/java/org/matomo/java/tracking/RequestValidator.java @@ -25,26 +25,16 @@ static void validate( @Nullable CharSequence authToken ) { - if (request.getSiteId() != null && request.getSiteId() < 0) { - throw new IllegalArgumentException("Site ID must not be negative"); - } - if (request.getGoalId() == null && ( - request.getEcommerceId() != null || request.getEcommerceRevenue() != null - || request.getEcommerceDiscount() != null || request.getEcommerceItems() != null - || request.getEcommerceLastOrderTimestamp() != null - || request.getEcommerceShippingCost() != null || request.getEcommerceSubtotal() != null - || request.getEcommerceTax() != null)) { - throw new MatomoException("Goal ID must be set if ecommerce parameters are used"); - } + if (request.getSearchResultsCount() != null && request.getSearchQuery() == null) { throw new MatomoException("Search query must be set if search results count is set"); } if (authToken == null) { if (request.getVisitorLongitude() != null || request.getVisitorLatitude() != null || request.getVisitorRegion() != null || request.getVisitorCity() != null - || request.getVisitorCountry() != null) { + || request.getVisitorCountry() != null || request.getVisitorIp() != null) { throw new MatomoException( - "Auth token must be present if longitude, latitude, region, city or country are set"); + "Auth token must be present if visitor longitude, latitude, region, city, country or IP are set"); } if (request.getRequestTimestamp() != null && request .getRequestTimestamp() diff --git a/core/src/main/java/org/matomo/java/tracking/TrackingParameter.java b/core/src/main/java/org/matomo/java/tracking/TrackingParameter.java index f94e226e..0b9d82bc 100644 --- a/core/src/main/java/org/matomo/java/tracking/TrackingParameter.java +++ b/core/src/main/java/org/matomo/java/tracking/TrackingParameter.java @@ -20,6 +20,10 @@ String regex() default ""; + double min() default Double.MIN_VALUE; + + double max() default Double.MAX_VALUE; + int maxLength() default Integer.MAX_VALUE; } diff --git a/core/src/main/java/org/matomo/java/tracking/TrackingParameterMethod.java b/core/src/main/java/org/matomo/java/tracking/TrackingParameterMethod.java index bbfca14c..2540ea0c 100644 --- a/core/src/main/java/org/matomo/java/tracking/TrackingParameterMethod.java +++ b/core/src/main/java/org/matomo/java/tracking/TrackingParameterMethod.java @@ -23,6 +23,10 @@ class TrackingParameterMethod { Pattern pattern; + double min; + + double max; + int maxLength; void validateParameterValue(@NonNull Object parameterValue) { @@ -34,12 +38,30 @@ void validateParameterValue(@NonNull Object parameterValue) { pattern )); } - if (parameterValue.toString().length() > maxLength) { + if (maxLength != 0 && parameterValue.toString().length() > maxLength) { throw new MatomoException(String.format("Invalid value for %s. Must be less or equal than %d characters", parameterName, maxLength )); } + if (parameterValue instanceof Number) { + Number number = (Number) parameterValue; + if (number.doubleValue() < min) { + throw new MatomoException(String.format( + "Invalid value for %s. Must be greater or equal than %s", + parameterName, + min % 1 == 0 ? Long.toString((long) min) : min + )); + + } + if (number.doubleValue() > max) { + throw new MatomoException(String.format( + "Invalid value for %s. Must be less or equal than %s", + parameterName, + max % 1 == 0 ? Long.toString((long) max) : max + )); + } + } } } diff --git a/core/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/core/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java index 04c67f70..c312c717 100644 --- a/core/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java +++ b/core/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java @@ -211,6 +211,21 @@ void sendsBulkRequestAsyncWithAuthToken() { } + @Test + void appliesGoalId() { + + matomoTracker = new MatomoTracker(HOST_URL); + matomoTracker.setSenderFactory(senderFactory); + request.setEcommerceId("some-id"); + + matomoTracker.sendRequest(request); + + TestSender testSender = senderFactory.getTestSender(); + thenContainsRequest(testSender, "rec=1&idsite=1&action_name=test&apiv=1&_id=00000000343efaf5&idgoal=0&ec_id=some-id&send_image=0&rand=test-random"); + assertThat(testSender.getTrackerConfiguration().getApiEndpoint()).hasToString(HOST_URL); + + } + private void thenContainsRequest(TestSender testSender, String query) { assertThat(testSender.getRequests()).containsExactly(request); assertThat(testSender.getQueries()).containsExactly(query); diff --git a/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java b/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java index 54d21373..668e4b46 100644 --- a/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java +++ b/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java @@ -30,7 +30,7 @@ class QueryCreatorTest { private final MatomoRequest.MatomoRequestBuilder matomoRequestBuilder = MatomoRequest - .builder() + .request() .visitorId(VisitorId.fromHash(1234567890123456789L)) .randomValue(RandomValue.fromString("random-value")); @@ -410,4 +410,134 @@ void appendsCharsetParameters() { "idsite=42&token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&cs=ISO-8859-1&send_image=0&rand=random-value"); } + @Test + void failsIfIdSiteIsNegative() { + matomoRequestBuilder.siteId(-1); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Invalid value for idsite. Must be greater or equal than 1"); + } + + @Test + void failsIfIdSiteIsZero() { + matomoRequestBuilder.siteId(0); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Invalid value for idsite. Must be greater or equal than 1"); + } + + @Test + void failsIfCurrentHourIsNegative() { + matomoRequestBuilder.currentHour(-1); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Invalid value for h. Must be greater or equal than 0"); + } + + @Test + void failsIfCurrentHourIsGreaterThan23() { + matomoRequestBuilder.currentHour(24); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Invalid value for h. Must be less or equal than 23"); + } + + @Test + void failsIfCurrentMinuteIsNegative() { + matomoRequestBuilder.currentMinute(-1); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Invalid value for m. Must be greater or equal than 0"); + } + + @Test + void failsIfCurrentMinuteIsGreaterThan59() { + matomoRequestBuilder.currentMinute(60); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Invalid value for m. Must be less or equal than 59"); + } + + @Test + void failsIfCurrentSecondIsNegative() { + matomoRequestBuilder.currentSecond(-1); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Invalid value for s. Must be greater or equal than 0"); + } + + @Test + void failsIfCurrentSecondIsGreaterThan59() { + matomoRequestBuilder.currentSecond(60); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Invalid value for s. Must be less or equal than 59"); + } + + @Test + void failsIfLatitudeIsLessThanMinus90() { + matomoRequestBuilder.visitorLatitude(-90.1); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Invalid value for lat. Must be greater or equal than -90"); + } + + @Test + void failsIfLatitudeIsGreaterThan90() { + matomoRequestBuilder.visitorLatitude(90.1); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Invalid value for lat. Must be less or equal than 90"); + } + + @Test + void failsIfLongitudeIsLessThanMinus180() { + matomoRequestBuilder.visitorLongitude(-180.1); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseMessage("Invalid value for long. Must be greater or equal than -180"); + } + + @Test + void failsIfLongitudeIsGreaterThan180() { + matomoRequestBuilder.visitorLongitude(180.1); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseMessage("Invalid value for long. Must be less or equal than 180"); + } + } diff --git a/core/src/test/java/org/matomo/java/tracking/RequestValidatorTest.java b/core/src/test/java/org/matomo/java/tracking/RequestValidatorTest.java index cdb8e90c..6d1ab348 100644 --- a/core/src/test/java/org/matomo/java/tracking/RequestValidatorTest.java +++ b/core/src/test/java/org/matomo/java/tracking/RequestValidatorTest.java @@ -2,7 +2,6 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; -import java.time.Instant; import java.util.Locale; import org.junit.jupiter.api.Test; import org.piwik.java.tracking.PiwikDate; @@ -12,81 +11,6 @@ class RequestValidatorTest { private final MatomoRequest request = new MatomoRequest(); - @Test - void testEcommerceRevenue() { - - request.setEcommerceRevenue(20.0); - - assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); - - } - - @Test - void testEcommerceDiscount() { - request.setEcommerceDiscount(1.0); - - assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); - - } - - @Test - void testEcommerceId() { - request.setEcommerceId("1"); - - assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); - } - - @Test - void testEcommerceSubtotal() { - request.setEcommerceSubtotal(20.0); - - assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); - } - - @Test - void testEcommerceShippingCost() { - request.setEcommerceShippingCost(20.0); - - assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); - } - - @Test - void testEcommerceLastOrderTimestamp() { - request.setEcommerceLastOrderTimestamp(Instant.ofEpochSecond(1000L)); - - assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); - } - - @Test - void testEcommerceTax() { - request.setEcommerceTax(20.0); - - assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); - } - - @Test - void testEcommerceItemE() { - - request.addEcommerceItem(new EcommerceItem("sku", "name", "category", 1.0, 2)); - - assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); - } @Test void testSearchResultsCount() { @@ -106,7 +30,7 @@ void testVisitorLongitude() { assertThatThrownBy(() -> RequestValidator.validate(request, null)) .isInstanceOf(MatomoException.class) .hasMessage( - "Auth token must be present if longitude, latitude, region, city or country are set"); + "Auth token must be present if visitor longitude, latitude, region, city, country or IP are set"); } @Test @@ -116,7 +40,7 @@ void testVisitorLatitude() { assertThatThrownBy(() -> RequestValidator.validate(request, null)) .isInstanceOf(MatomoException.class) .hasMessage( - "Auth token must be present if longitude, latitude, region, city or country are set"); + "Auth token must be present if visitor longitude, latitude, region, city, country or IP are set"); } @Test @@ -126,7 +50,7 @@ void testVisitorCity() { assertThatThrownBy(() -> RequestValidator.validate(request, null)) .isInstanceOf(MatomoException.class) .hasMessage( - "Auth token must be present if longitude, latitude, region, city or country are set"); + "Auth token must be present if visitor longitude, latitude, region, city, country or IP are set"); } @Test @@ -136,7 +60,7 @@ void testVisitorRegion() { assertThatThrownBy(() -> RequestValidator.validate(request, null)) .isInstanceOf(MatomoException.class) .hasMessage( - "Auth token must be present if longitude, latitude, region, city or country are set"); + "Auth token must be present if visitor longitude, latitude, region, city, country or IP are set"); } @Test @@ -148,7 +72,7 @@ void testVisitorCountryTE() { assertThatThrownBy(() -> RequestValidator.validate(request, null)) .isInstanceOf(MatomoException.class) .hasMessage( - "Auth token must be present if longitude, latitude, region, city or country are set"); + "Auth token must be present if visitor longitude, latitude, region, city, country or IP are set"); } @Test @@ -163,15 +87,6 @@ void testRequestDatetime() { } - @Test - void failsIfSiteIdIsNegative() { - request.setSiteId(-1); - - assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Site ID must not be negative"); - } - @Test void failsIfAuthTokenIsNot32CharactersLong() { assertThatThrownBy(() -> RequestValidator.validate(request, "123456789012345678901234567890")) diff --git a/core/src/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java b/core/src/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java index 4458568f..8b68c950 100644 --- a/core/src/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java +++ b/core/src/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java @@ -35,6 +35,8 @@ void doNothingIfParameterValueIsNotCharSequence() { .parameterName("foo") .pattern(Pattern.compile("bar")) .maxLength(255) + .min(1) + .max(1) .build(); trackingParameterMethod.validateParameterValue(1); @@ -64,4 +66,24 @@ void validateParameterValueFailsIfMaxLengthIsExceeded() { .hasMessage("Invalid value for foo. Must be less or equal than 3 characters"); } + @Test + void failIfParameterValueIsLessThanMin() { + TrackingParameterMethod trackingParameterMethod = + TrackingParameterMethod.builder().parameterName("foo").min(3.0).build(); + + assertThatThrownBy(() -> trackingParameterMethod.validateParameterValue(1)) + .isInstanceOf(MatomoException.class) + .hasMessage("Invalid value for foo. Must be greater or equal than 3"); + } + + @Test + void failIfParameterValueIsGreaterThanMax() { + TrackingParameterMethod trackingParameterMethod = + TrackingParameterMethod.builder().parameterName("foo").max(3.0).build(); + + assertThatThrownBy(() -> trackingParameterMethod.validateParameterValue(4)) + .isInstanceOf(MatomoException.class) + .hasMessage("Invalid value for foo. Must be less or equal than 3"); + } + } diff --git a/java11/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/java11/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java index bc8fdef0..734d69e0 100644 --- a/java11/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java +++ b/java11/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java @@ -409,8 +409,10 @@ void failsOnNegativeSiteId(WireMockRuntimeInfo wireMockRuntimeInfo) { requestBuilder.siteId(-1); assertThatThrownBy(this::whenSendsRequestAsync) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Site ID must not be negative"); + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Invalid value for idsite. Must be greater or equal than 1"); } diff --git a/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java index f9993f94..fe8eb7c1 100644 --- a/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java +++ b/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java @@ -428,8 +428,8 @@ void failsOnNegativeSiteId() { .failsWithin(1, MINUTES) .withThrowableThat() .havingRootCause() - .isInstanceOf(IllegalArgumentException.class) - .withMessage("Site ID must not be negative"); + .isInstanceOf(MatomoException.class) + .withMessage("Invalid value for idsite. Must be greater or equal than 1"); ; } From 8e5de9aa7baf78a2853ba72472e1ae60d22f1acd Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 21 Nov 2023 00:26:05 +0100 Subject: [PATCH 254/467] Only allow singular additional parameters (#163) --- README.md | 22 +++++----- core/pom.xml | 2 +- .../matomo/java/tracking/MatomoRequest.java | 38 ++++++----------- .../java/tracking/MatomoRequestBuilder.java | 22 +--------- .../matomo/java/tracking/QueryCreator.java | 11 +++-- .../tracking/MatomoRequestBuilderTest.java | 11 +++-- .../java/tracking/MatomoRequestTest.java | 6 +-- .../java/tracking/PiwikRequestTest.java | 42 +++++++------------ .../java/tracking/QueryCreatorTest.java | 34 +++++++-------- java11/pom.xml | 4 +- java8/pom.xml | 4 +- pom.xml | 2 +- servlet-jakarta/pom.xml | 4 +- servlet-javax/pom.xml | 4 +- spring/pom.xml | 2 +- test/pom.xml | 2 +- 16 files changed, 80 insertions(+), 130 deletions(-) diff --git a/README.md b/README.md index 2e0849b9..fa22bf5d 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ dependencies. Release notes can be found here: https://github.com/matomo-org/mat Here are the most important changes: -* Matomo Java Tracker 3.1.1 is compatible with Matomo 4 and 5 +* Matomo Java Tracker 3.2.0 is compatible with Matomo 4 and 5 * less dependencies * new dimension parameter * special types allow to provide valid parameters now @@ -111,7 +111,7 @@ Add a dependency on Matomo Java Tracker using Maven. For Java 8: org.piwik.java.tracking matomo-java-tracker - 3.1.1 + 3.2.0 ``` @@ -121,7 +121,7 @@ For Java 11: org.piwik.java.tracking matomo-java-tracker-java11 - 3.1.1 + 3.2.0 ``` @@ -129,7 +129,7 @@ or Gradle (Java 8): ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker:3.1.1") + implementation("org.piwik.java.tracking:matomo-java-tracker:3.2.0") } ``` @@ -137,20 +137,20 @@ or Gradle (Java 11): ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.1.1") + implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.2.0") } ``` or Gradle with Kotlin DSL (Java 8) ```kotlin -implementation("org.piwik.java.tracking:matomo-java-tracker:3.1.1") +implementation("org.piwik.java.tracking:matomo-java-tracker:3.2.0") ``` or Gradle with Kotlin DSL (Java 11) ```kotlin -implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.1.1") +implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.2.0") ``` ### Spring Boot Module @@ -163,7 +163,7 @@ and allows you to configure the tracker via application properties. Add the foll org.piwik.java.tracking matomo-java-tracker-spring-boot-starter - 3.1.1 + 3.2.0 ``` @@ -171,14 +171,14 @@ or Gradle: ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.1.1") + implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.2.0") } ``` or Gradle with Kotlin DSL ```kotlin -implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.1.1") +implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.2.0") ``` The following properties are supported: @@ -623,7 +623,7 @@ version can be used in your local Maven repository for testing purposes, e.g. org.piwik.java.tracking matomo-java-tracker - 3.1.1-rc2-SNAPSHOT + 3.2.1-SNAPSHOT ``` diff --git a/core/pom.xml b/core/pom.xml index dedb5618..2e1ae75e 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.2-SNAPSHOT + 3.2.0-SNAPSHOT ../pom.xml diff --git a/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java index 860593eb..ea2ddbfc 100644 --- a/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -7,16 +7,10 @@ package org.matomo.java.tracking; -import static java.util.Collections.singleton; - import edu.umd.cs.findbugs.annotations.Nullable; import java.nio.charset.Charset; import java.time.Instant; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import java.util.UUID; import lombok.AllArgsConstructor; @@ -722,7 +716,7 @@ public class MatomoRequest { *

For example, you can use this to set the Accept-Language header, or to set the * Content-Type. */ - private Map> additionalParameters; + private Map additionalParameters; /** * You can set additional HTTP headers for the request sent to Matomo. @@ -776,17 +770,14 @@ public MatomoRequest(int siteId, String actionUrl) { * * @param key the key of the parameter whose list of objects to get. Cannot be null * - * @return the list of objects currently stored at the specified key + * @return the parameter at the specified key, null if nothing at this key */ - public List getCustomTrackingParameter(@NonNull String key) { + @Nullable + public Object getCustomTrackingParameter(@NonNull String key) { if (additionalParameters == null || additionalParameters.isEmpty()) { - return Collections.emptyList(); - } - Collection parameterValues = additionalParameters.get(key); - if (parameterValues == null || parameterValues.isEmpty()) { - return Collections.emptyList(); + return null; } - return Collections.unmodifiableList(new ArrayList<>(parameterValues)); + return additionalParameters.get(key); } /** @@ -813,28 +804,25 @@ public void setCustomTrackingParameter( if (additionalParameters == null) { additionalParameters = new LinkedHashMap<>(); } - Collection values = additionalParameters.computeIfAbsent(key, k -> new ArrayList<>()); - values.clear(); - values.add(value); + additionalParameters.put(key, value); } } /** - * Add a custom tracking parameter to the specified key. This allows users to have multiple - * parameters with the same name and different values, commonly used during situations where list - * parameters are needed + * Add a custom tracking parameter to the specified key. If there is already a parameter at this + * key, the new value replaces the old value. * * @param key the parameter's key. Cannot be null - * @param value the parameter's value. Cannot be null + * @param value the parameter's value. May be null * * @deprecated Use {@link MatomoRequest.MatomoRequestBuilder#additionalParameters(Map)} instead. */ @Deprecated - public void addCustomTrackingParameter(@NonNull String key, @NonNull Object value) { + public void addCustomTrackingParameter(@NonNull String key, @Nullable Object value) { if (additionalParameters == null) { additionalParameters = new LinkedHashMap<>(); } - additionalParameters.computeIfAbsent(key, k -> new ArrayList<>()).add(value); + additionalParameters.put(key, value); } /** @@ -1149,7 +1137,7 @@ public void setParameter(@NonNull String parameterName, Object value) { if (value == null) { additionalParameters.remove(parameterName); } else { - additionalParameters.put(parameterName, singleton(value)); + additionalParameters.put(parameterName, value); } } diff --git a/core/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java b/core/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java index 1dd8f9db..01c43cd2 100644 --- a/core/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java +++ b/core/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java @@ -1,13 +1,7 @@ package org.matomo.java.tracking; -import static java.util.Collections.singleton; - import edu.umd.cs.findbugs.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collection; import java.util.Map; -import java.util.stream.Collectors; -import lombok.NonNull; import org.matomo.java.tracking.parameters.AcceptLanguage; /** @@ -48,22 +42,8 @@ public MatomoRequestBuilder headerAcceptLanguage(@Nullable String headerAcceptLa */ @Deprecated public MatomoRequestBuilder customTrackingParameters(@Nullable Map parameters) { - if (parameters == null || parameters.isEmpty()) { - additionalParameters(null); - } else { - additionalParameters(parameters.entrySet().stream().collect(Collectors.toMap( - e -> e.getKey(), - e -> toCollection(e) - ))); - } + additionalParameters(parameters); return this; } - private static Collection toCollection(@NonNull Map.Entry e) { - if (e.getValue() instanceof Collection) { - return (Collection) e.getValue(); - } - return new ArrayList<>(singleton(e.getValue())); - } - } diff --git a/core/src/main/java/org/matomo/java/tracking/QueryCreator.java b/core/src/main/java/org/matomo/java/tracking/QueryCreator.java index 3bfbd0b2..63a2b9d2 100644 --- a/core/src/main/java/org/matomo/java/tracking/QueryCreator.java +++ b/core/src/main/java/org/matomo/java/tracking/QueryCreator.java @@ -90,12 +90,11 @@ String createQuery( appendParameter(method, request, query); } if (request.getAdditionalParameters() != null) { - for (Entry> entry : request.getAdditionalParameters().entrySet()) { - for (Object value : entry.getValue()) { - if (value != null && !value.toString().trim().isEmpty()) { - appendAmpersand(query); - query.append(encode(entry.getKey())).append('=').append(encode(value.toString())); - } + for (Entry entry : request.getAdditionalParameters().entrySet()) { + Object value = entry.getValue(); + if (value != null && !value.toString().trim().isEmpty()) { + appendAmpersand(query); + query.append(encode(entry.getKey())).append('=').append(encode(value.toString())); } } } diff --git a/core/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java b/core/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java index 18eae8cb..87dab6d5 100644 --- a/core/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java +++ b/core/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java @@ -1,6 +1,5 @@ package org.matomo.java.tracking; -import static java.util.Collections.singleton; import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; @@ -24,7 +23,7 @@ void buildsRequest() { .referrerUrl("https://referrer.com") .additionalParameters(singletonMap( "trackingParameterName", - singleton("trackingParameterValue") + "trackingParameterValue" )) .pageCustomVariables(new CustomVariables().add(pageCustomVariable, 2)) .visitCustomVariables(new CustomVariables().add(visitCustomVariable, 3)) @@ -41,7 +40,7 @@ void buildsRequest() { assertThat(matomoRequest.getResponseAsImage()).isFalse(); assertThat(matomoRequest.getRequired()).isTrue(); assertThat(matomoRequest.getReferrerUrl()).isEqualTo("https://referrer.com"); - assertThat(matomoRequest.getCustomTrackingParameter("trackingParameterName")).containsExactly( + assertThat(matomoRequest.getCustomTrackingParameter("trackingParameterName")).isEqualTo( "trackingParameterValue"); assertThat(matomoRequest.getPageCustomVariables()).hasToString( "{\"2\":[\"pageCustomVariableName\",\"pageCustomVariableValue\"]}"); @@ -61,20 +60,20 @@ void setCustomTrackingParameters() { .referrerUrl("https://referrer.com") .build(); - assertThat(matomoRequest.getCustomTrackingParameter("foo")).containsExactly("bar"); + assertThat(matomoRequest.getCustomTrackingParameter("foo")).isEqualTo("bar"); } @Test void setCustomTrackingParametersWithCollectopm() { MatomoRequest matomoRequest = new MatomoRequestBuilder() - .customTrackingParameters(singletonMap("foo", singleton("bar"))) + .customTrackingParameters(singletonMap("foo", "bar")) .siteId(42) .actionName("ACTION_NAME") .actionUrl("https://www.your-domain.tld/some/page?query=foo") .referrerUrl("https://referrer.com") .build(); - assertThat(matomoRequest.getCustomTrackingParameter("foo")).containsExactly("bar"); + assertThat(matomoRequest.getCustomTrackingParameter("foo")).isEqualTo("bar"); } @Test diff --git a/core/src/test/java/org/matomo/java/tracking/MatomoRequestTest.java b/core/src/test/java/org/matomo/java/tracking/MatomoRequestTest.java index 65e83def..b4ad0e04 100644 --- a/core/src/test/java/org/matomo/java/tracking/MatomoRequestTest.java +++ b/core/src/test/java/org/matomo/java/tracking/MatomoRequestTest.java @@ -14,9 +14,9 @@ void returnsEmptyListWhenCustomTrackingParametersDoesNotContainKey() { request.setCustomTrackingParameter("foo", "bar"); - assertThat(request.getCustomTrackingParameter("baz")).isEmpty(); + assertThat(request.getCustomTrackingParameter("baz")).isNull(); assertThat(request.getAdditionalParameters()).isNotEmpty(); - assertThat(request.getCustomTrackingParameter("foo")).isNotEmpty(); + assertThat(request.getCustomTrackingParameter("foo")).isEqualTo("bar"); } @Test @@ -95,7 +95,7 @@ void setVisitCustomVariableInitializesVisitCustomVariablesIfCustomVariableParame @Test void setsCustomParameter() { request.setParameter("foo", 1); - assertThat(request.getCustomTrackingParameter("foo")).contains(1); + assertThat(request.getCustomTrackingParameter("foo")).isEqualTo(1); } @Test diff --git a/core/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java b/core/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java index 49432619..c88c7570 100644 --- a/core/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java +++ b/core/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java @@ -10,7 +10,6 @@ import java.nio.charset.Charset; import java.time.Instant; import java.time.ZonedDateTime; -import java.util.List; import java.util.Locale; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -193,7 +192,7 @@ void testGetCustomTrackingParameter_T() { @Test void testGetCustomTrackingParameter_FT() { - assertThat(request.getCustomTrackingParameter("key")).isEmpty(); + assertThat(request.getCustomTrackingParameter("key")).isNull(); } @Test @@ -209,28 +208,26 @@ void testSetCustomTrackingParameter_T() { @Test void testSetCustomTrackingParameter1() { request.setCustomTrackingParameter("key", "value"); - List l = request.getCustomTrackingParameter("key"); - assertThat(l).hasSize(1); - assertThat(l.get(0)).isEqualTo("value"); + Object l = request.getCustomTrackingParameter("key"); + assertThat(l).isEqualTo("value"); request.setCustomTrackingParameter("key", "value2"); } @Test void testSetCustomTrackingParameter2() { request.setCustomTrackingParameter("key", "value2"); - List l = request.getCustomTrackingParameter("key"); - assertThat(l).hasSize(1); - assertThat(l.get(0)).isEqualTo("value2"); + Object l = request.getCustomTrackingParameter("key"); + assertThat(l).isEqualTo("value2"); request.setCustomTrackingParameter("key", null); l = request.getCustomTrackingParameter("key"); - assertThat(l).isEmpty(); + assertThat(l).isNull(); } @Test void testSetCustomTrackingParameter3() { request.setCustomTrackingParameter("key", null); - List l = request.getCustomTrackingParameter("key"); - assertThat(l).isEmpty(); + Object l = request.getCustomTrackingParameter("key"); + assertThat(l).isNull(); } @Test @@ -243,38 +240,27 @@ void testAddCustomTrackingParameter_T() { } } - @Test - void testAddCustomTrackingParameter_FT() { - try { - request.addCustomTrackingParameter("key", null); - fail("Exception should have been thrown."); - } catch (NullPointerException e) { - assertThat(e.getLocalizedMessage()).isEqualTo("value is marked non-null but is null"); - } - } - @Test void testAddCustomTrackingParameter1() { request.addCustomTrackingParameter("key", "value"); - List l = request.getCustomTrackingParameter("key"); - assertThat(l).hasSize(1); - assertThat(l.get(0)).isEqualTo("value"); + Object l = request.getCustomTrackingParameter("key"); + assertThat(l).isEqualTo("value"); } @Test void testAddCustomTrackingParameter2() { request.addCustomTrackingParameter("key", "value"); request.addCustomTrackingParameter("key", "value2"); - List l = request.getCustomTrackingParameter("key"); - assertThat(l).hasSize(2).contains(new String[] {"value"}).contains(new String[] {"value2"}); + Object l = request.getCustomTrackingParameter("key"); + assertThat(l).isEqualTo("value2"); } @Test void testClearCustomTrackingParameter() { request.setCustomTrackingParameter("key", "value"); request.clearCustomTrackingParameter(); - List l = request.getCustomTrackingParameter("key"); - assertThat(l).isEmpty(); + Object l = request.getCustomTrackingParameter("key"); + assertThat(l).isNull(); } /** diff --git a/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java b/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java index 668e4b46..1b1cecd4 100644 --- a/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java +++ b/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java @@ -9,8 +9,6 @@ import java.net.URI; import java.nio.charset.StandardCharsets; import java.time.Instant; -import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Locale.LanguageRange; @@ -247,19 +245,19 @@ void testGetQueryString() { matomoRequestBuilder.additionalParameters(singletonMap("key", singleton("test"))); whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key=test"); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key=%5Btest%5D"); matomoRequestBuilder.additionalParameters(singletonMap("key", asList("test", "test2"))); whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key=test&key=test2"); - Map> customTrackingParameters = new HashMap<>(); - customTrackingParameters.put("key", asList("test", "test2")); - customTrackingParameters.put("key2", Collections.singletonList("test3")); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key=%5Btest%2C+test2%5D"); + Map customTrackingParameters = new HashMap<>(); + customTrackingParameters.put("key", "test2"); + customTrackingParameters.put("key2", "test3"); matomoRequestBuilder.additionalParameters(customTrackingParameters); whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key2=test3&key=test&key=test2"); - customTrackingParameters.put("key", Collections.singletonList("test4")); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key2=test3&key=test2"); + customTrackingParameters.put("key", "test4"); whenCreatesQuery(); assertThat(query).isEqualTo( "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key2=test3&key=test4"); @@ -301,24 +299,24 @@ void testGetUrlEncodedQueryString() { whenCreatesQuery(); assertThat(query).isEqualTo( "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random"); - Map> customTrackingParameters = new HashMap<>(); - customTrackingParameters.put("ke/y", Collections.singletonList("te:st")); + Map customTrackingParameters = new HashMap<>(); + customTrackingParameters.put("ke/y", "te:st"); matomoRequestBuilder.additionalParameters(customTrackingParameters); whenCreatesQuery(); assertThat(query).isEqualTo( "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast"); - customTrackingParameters.put("ke/y", asList("te:st", "te:st2")); + customTrackingParameters.put("ke/y", "te:st2"); whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2"); - customTrackingParameters.put("ke/y2", Collections.singletonList("te:st3")); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast2"); + customTrackingParameters.put("ke/y2", "te:st3"); whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2&ke%2Fy2=te%3Ast3"); - customTrackingParameters.put("ke/y", asList("te:st3", "te:st4")); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast2&ke%2Fy2=te%3Ast3"); + customTrackingParameters.put("ke/y", "te:st3"); whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast3&ke%2Fy=te%3Ast4&ke%2Fy2=te%3Ast3"); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast3&ke%2Fy2=te%3Ast3"); matomoRequestBuilder .randomValue(null) .siteId(null) @@ -328,7 +326,7 @@ void testGetUrlEncodedQueryString() { .visitorId(null) .actionUrl(null); whenCreatesQuery(); - assertThat(query).isEqualTo("idsite=42&ke%2Fy=te%3Ast3&ke%2Fy=te%3Ast4&ke%2Fy2=te%3Ast3"); + assertThat(query).isEqualTo("idsite=42&ke%2Fy=te%3Ast3&ke%2Fy2=te%3Ast3"); } diff --git a/java11/pom.xml b/java11/pom.xml index 18bf2b64..3b66e26b 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.2-SNAPSHOT + 3.2.0-SNAPSHOT ../pom.xml matomo-java-tracker-java11 - 3.1.2-SNAPSHOT + 3.2.0-SNAPSHOT jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index e4c9a001..8626022d 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.2-SNAPSHOT + 3.2.0-SNAPSHOT ../pom.xml matomo-java-tracker - 3.1.2-SNAPSHOT + 3.2.0-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index ced31caa..bc835318 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.2-SNAPSHOT + 3.2.0-SNAPSHOT pom Matomo Java Tracker Parent diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 92ae57d2..b13a2d10 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.2-SNAPSHOT + 3.2.0-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-jakarta - 3.1.2-SNAPSHOT + 3.2.0-SNAPSHOT jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 794fb084..c169548a 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.2-SNAPSHOT + 3.2.0-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-javax - 3.1.2-SNAPSHOT + 3.2.0-SNAPSHOT jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index 0932c7cd..39f1f284 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.2-SNAPSHOT + 3.2.0-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 41c7fc6c..504a4296 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.2-SNAPSHOT + 3.2.0-SNAPSHOT ../pom.xml From 792aa3c0e49e53b89536353eeaae07bf8c760776 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 20 Nov 2023 23:29:46 +0000 Subject: [PATCH 255/467] [ci skip] prepare release matomo-java-tracker-parent-3.2.0 --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 2e1ae75e..6eab4d6e 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.0-SNAPSHOT + 3.2.0 ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index 3b66e26b..a99c1a6a 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.0-SNAPSHOT + 3.2.0 ../pom.xml matomo-java-tracker-java11 - 3.2.0-SNAPSHOT + 3.2.0 jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index 8626022d..a40cbfbb 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.0-SNAPSHOT + 3.2.0 ../pom.xml matomo-java-tracker - 3.2.0-SNAPSHOT + 3.2.0 jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index bc835318..9abf137e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.0-SNAPSHOT + 3.2.0 pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - HEAD + matomo-java-tracker-parent-3.2.0 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index b13a2d10..25a77886 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.0-SNAPSHOT + 3.2.0 ../pom.xml matomo-java-tracker-servlet-jakarta - 3.2.0-SNAPSHOT + 3.2.0 jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index c169548a..4ae2ccfe 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.0-SNAPSHOT + 3.2.0 ../pom.xml matomo-java-tracker-servlet-javax - 3.2.0-SNAPSHOT + 3.2.0 jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index 39f1f284..10e61619 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.0-SNAPSHOT + 3.2.0 ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 504a4296..b0ea22f2 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.0-SNAPSHOT + 3.2.0 ../pom.xml From b8bf8edc10ebd5cbd604607c978b2c664445e2e6 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Mon, 20 Nov 2023 23:29:48 +0000 Subject: [PATCH 256/467] [ci skip] prepare for next development iteration --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 6eab4d6e..e05e2923 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.0 + 3.2.1-SNAPSHOT ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index a99c1a6a..6aa9b2d0 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.0 + 3.2.1-SNAPSHOT ../pom.xml matomo-java-tracker-java11 - 3.2.0 + 3.2.1-SNAPSHOT jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index a40cbfbb..21c8ed91 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.0 + 3.2.1-SNAPSHOT ../pom.xml matomo-java-tracker - 3.2.0 + 3.2.1-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index 9abf137e..c19a6b46 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.0 + 3.2.1-SNAPSHOT pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - matomo-java-tracker-parent-3.2.0 + HEAD diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 25a77886..9a8e4257 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.0 + 3.2.1-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-jakarta - 3.2.0 + 3.2.1-SNAPSHOT jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 4ae2ccfe..83205e5a 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.0 + 3.2.1-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-javax - 3.2.0 + 3.2.1-SNAPSHOT jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index 10e61619..aa334936 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.0 + 3.2.1-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index b0ea22f2..f60465d4 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.0 + 3.2.1-SNAPSHOT ../pom.xml From 20ca4ca4d95350c9df75e39d6ddaf5ecfae4cac8 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 21 Nov 2023 01:00:42 +0100 Subject: [PATCH 257/467] Update README.md --- README.md | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index fa22bf5d..ba741f48 100644 --- a/README.md +++ b/README.md @@ -5,26 +5,23 @@ [![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/matomo-org/matomo-java-tracker.svg)](https://isitmaintained.com/project/matomo-org/matomo-java-tracker "Average time to resolve an issue") [![Percentage of issues still open](https://isitmaintained.com/badge/open/matomo-org/matomo-java-tracker.svg)](https://isitmaintained.com/project/matomo-org/matomo-java-tracker "Percentage of issues still open") -Matomo Java Tracker is the official Java implementation of -the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api). The tracker allows you to track -visits, goals and ecommerce transactions and items. It is designed to be used in server-side applications, such as -Java-based web applications or web services. - -Features include: - -* Track page views, goals, ecommerce transactions and items -* Supports custom dimensions and custom variables -* Includes tracking parameters for campaigns, events, downloads, outlinks, site search, devices, visitors -* Supports Java 8 and higher (if you use Java 11, please use artifact matomo-java-tracker-java11) -* Allows you to skip SSL certificate validation (not recommended for production) -* Contains nearly no runtime dependencies (only SLF4J) -* Allows asynchronous requests -* Supports Matomo 4 and 5 -* Single and multiple requests can be sent -* Well documented with Javadoc -* Ensures correct values are sent to Matomo Tracking API -* Includes debug and error logging -* Easy to integrate in frameworks, e.g. Spring: Just create the MatomoTracker Spring bean and use it in other beans +The Matomo Java Tracker functions as the official Java implementation for the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api). This versatile tracker empowers you to monitor visits, goals, and ecommerce transactions and items. Specifically designed for integration into server-side applications, it seamlessly integrates with Java-based web applications or web services. + +Key features: + +* Comprehensive tracking capabilities: Monitor page views, goals, ecommerce transactions, and items. +* Customization options: Support for custom dimensions and variables. +* Extensive tracking parameters: Capture data on campaigns, events, downloads, outlinks, site searches, devices, and visitors. +* Java compatibility: Supports Java 8 and higher, with a dedicated artifact (matomo-java-tracker-java11) for Java 11. +* SSL certificate flexibility: Option to skip SSL certificate validation (caution: not recommended for production). +* Minimal runtime dependencies: Relies solely on SLF4J. +* Asynchronous request support: Permits non-blocking requests. +* Compatibility with Matomo versions 4 and 5. +* Versatile request handling: Send both single and multiple requests. +* Robust documentation: Thoroughly documented with Javadoc for easy reference. +* Data accuracy assurance: Ensures correct values are transmitted to the Matomo Tracking API. +* Logging capabilities: Include debug and error logging for effective troubleshooting. +* Seamless integration: Easily integrates into frameworks such as Spring by creating the MatomoTracker Spring bean for use in other beans. Please prefer the Java 11 version as the Java 8 will become obsolete in the future. From 2501d49dcc84ee6232034c991971ff9bad5f5e56 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Fri, 24 Nov 2023 22:44:05 +0100 Subject: [PATCH 258/467] Extend documentation --- README.md | 152 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 88 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index ba741f48..bcd72ba4 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,17 @@ [![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/matomo-org/matomo-java-tracker.svg)](https://isitmaintained.com/project/matomo-org/matomo-java-tracker "Average time to resolve an issue") [![Percentage of issues still open](https://isitmaintained.com/badge/open/matomo-org/matomo-java-tracker.svg)](https://isitmaintained.com/project/matomo-org/matomo-java-tracker "Percentage of issues still open") -The Matomo Java Tracker functions as the official Java implementation for the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api). This versatile tracker empowers you to monitor visits, goals, and ecommerce transactions and items. Specifically designed for integration into server-side applications, it seamlessly integrates with Java-based web applications or web services. +The Matomo Java Tracker functions as the official Java implementation for +the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api). This versatile tracker empowers +you to monitor visits, goals, and ecommerce transactions and items. Specifically designed for integration into +server-side applications, it seamlessly integrates with Java-based web applications or web services. Key features: * Comprehensive tracking capabilities: Monitor page views, goals, ecommerce transactions, and items. * Customization options: Support for custom dimensions and variables. -* Extensive tracking parameters: Capture data on campaigns, events, downloads, outlinks, site searches, devices, and visitors. +* Extensive tracking parameters: Capture data on campaigns, events, downloads, outlinks, site searches, devices, and + visitors. * Java compatibility: Supports Java 8 and higher, with a dedicated artifact (matomo-java-tracker-java11) for Java 11. * SSL certificate flexibility: Option to skip SSL certificate validation (caution: not recommended for production). * Minimal runtime dependencies: Relies solely on SLF4J. @@ -21,7 +25,8 @@ Key features: * Robust documentation: Thoroughly documented with Javadoc for easy reference. * Data accuracy assurance: Ensures correct values are transmitted to the Matomo Tracking API. * Logging capabilities: Include debug and error logging for effective troubleshooting. -* Seamless integration: Easily integrates into frameworks such as Spring by creating the MatomoTracker Spring bean for use in other beans. +* Seamless integration: Easily integrates into frameworks such as Spring by creating the MatomoTracker Spring bean for + use in other beans. Please prefer the Java 11 version as the Java 8 will become obsolete in the future. @@ -48,7 +53,7 @@ Projects that use Matomo Java Tracker: * [Digitale Ehrenamtskarte](https://github.com/digitalfabrik/entitlementcard) * [skidfuscator-java-obfuscator](https://github.com/skidfuscatordev/skidfuscator-java-obfuscator) * [DnA](https://github.com/mercedes-benz/DnA) -* And many closed source projects that we are not aware of :smile: +* And many closed source projects that we are not aware of :smile: ## Table of Contents @@ -80,7 +85,7 @@ See also the [Developer Guide here](https://developer.matomo.org/api-reference/t ## Javadoc -The Javadoc for all versions can be found +The Javadoc for all versions can be found [at javadoc.io](https://javadoc.io/doc/org.piwik.java.tracking/matomo-java-tracker-core/latest/index.html). Thanks to [javadoc.io](https://javadoc.io) for hosting it. @@ -100,11 +105,38 @@ recommend to read the [Tracking API User Guide](https://matomo.org/guide/apis/tr The [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) is well documented and contains many examples. +This project contains the following Maven artifacts: + +1. **matomo-java-tracker-core**: This artifact is the core module of the Matomo Java Tracker project. It provides the + main functionality of the Matomo Java Tracker, which is a Java implementation for the Matomo Tracking HTTP API. This + module is designed to be used as a base for other modules in the project that provide additional functionality or + integrations. +2. **matomo-java-tracker**: This is a specific implementation of the core module designed for Java 8. It provides the + main functionality of the Matomo Java Tracker and is built upon the core. This artifact is + specifically designed for applications running on Java 8. +3. **matomo-java-tracker-java11**: This artifact is a Java 11 implementation of the Matomo Java Tracker. It uses the + HttpClient available since Java 11. It is recommended to use this version if you are using Java 11 or higher. +4. **matomo-java-tracker-spring-boot-starter**: This artifact is a Spring Boot Starter for the Matomo Java Tracker. It + provides auto-configuration for the Matomo Java Tracker in a Spring Boot application. By including this artifact in + your project, you can take advantage of Spring Boot's auto-configuration features to automatically set up and + configure the Matomo Java Tracker. +5. **matomo-java-tracker-servlet-jakarta**: This artifact is specifically designed for applications using the Jakarta + Servlet API (part of Jakarta EE). +6. **matomo-java-tracker-servlet-javax**: This artifact is specifically designed for applications using the older Java + Servlet API (part of Java EE). +7. **matomo-java-tracker-test**: This artifact contains tools for manual testing against a local Matomo instance created + with Docker. It contains a tester class that sends randomized requests to a local Matomo instance and a servlet that + can be used to test the servlet integration. + +Each of these artifacts serves a different purpose and can be used depending on the specific needs of your project and +the Java version you are using. + ### Add library to your build Add a dependency on Matomo Java Tracker using Maven. For Java 8: ```xml + org.piwik.java.tracking matomo-java-tracker @@ -115,6 +147,7 @@ Add a dependency on Matomo Java Tracker using Maven. For Java 8: For Java 11: ```xml + org.piwik.java.tracking matomo-java-tracker-java11 @@ -210,8 +243,8 @@ Or if you use YAML: ```yaml matomo: - tracker: - api-endpoint: https://your-matomo-domain.tld/matomo.php + tracker: + api-endpoint: https://your-matomo-domain.tld/matomo.php ``` You can automatically add the `MatomoTrackerFilter` to your Spring Boot application if you add the following property: @@ -224,9 +257,9 @@ Or if you use YAML: ```yaml matomo: - tracker: - filter: - enabled: true + tracker: + filter: + enabled: true ``` The filter uses `ServletMatomoRequest` to create a `MatomoRequest` from a `HttpServletRequest` on every filter call. @@ -345,12 +378,12 @@ public class BulkExample { VisitorId visitorId = VisitorId.fromString("customer@mail.com"); tracker.sendBulkRequestAsync( MatomoRequests.siteSearch("Running shoes", "Running", 120L) - .visitorId(visitorId).build(), + .visitorId(visitorId).build(), MatomoRequests.pageView("VelocityStride ProX Running Shoes") - .visitorId(visitorId).build(), + .visitorId(visitorId).build(), MatomoRequests.ecommerceOrder("QXZ-789LMP", 100.0, 124.0, 19.0, 10.0, 5.0) - .visitorId(visitorId) - .build() + .visitorId(visitorId) + .build() ); } @@ -389,53 +422,56 @@ import org.matomo.java.tracking.parameters.VisitorId; public class EcommerceExample { - public static void main(String[] args) { + public static void main(String[] args) { + + TrackerConfiguration configuration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://www.yourdomain.com/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("ee6e3dd9ed1b61f5328cf5978b5a8c71") + .logFailedTracking(true) + .build(); - TrackerConfiguration configuration = TrackerConfiguration - .builder() - .apiEndpoint(URI.create("https://www.yourdomain.com/matomo.php")) - .defaultSiteId(1) - .defaultAuthToken("ee6e3dd9ed1b61f5328cf5978b5a8c71") - .logFailedTracking(true) - .build(); + MatomoTracker tracker = new MatomoTracker(configuration); - MatomoTracker tracker = new MatomoTracker(configuration); + tracker.sendBulkRequestAsync(MatomoRequests + .ecommerceCartUpdate(50.0) + .ecommerceItems(EcommerceItems + .builder() + .item(EcommerceItem + .builder() + .sku("XYZ12345") + .name("Matomo - The big book about web analytics") + .category("Education & Teaching") + .price(23.1) + .quantity(2) + .build()) + .item(EcommerceItem + .builder() + .sku("B0C2WV3MRJ") + .name("Matomo for data visualization") + .category("Education & Teaching") + .price(15.0) + .quantity(1) + .build()) + .build()) + .visitorId(VisitorId.fromString("customer@mail.com")) + .build() + ); - tracker.sendBulkRequestAsync(MatomoRequests - .ecommerceCartUpdate(50.0) - .ecommerceItems(EcommerceItems - .builder() - .item(EcommerceItem - .builder() - .sku("XYZ12345") - .name("Matomo - The big book about web analytics") - .category("Education & Teaching") - .price(23.1) - .quantity(2) - .build()) - .item(EcommerceItem - .builder() - .sku("B0C2WV3MRJ") - .name("Matomo for data visualization") - .category("Education & Teaching") - .price(15.0) - .quantity(1) - .build()) - .build()) - .visitorId(VisitorId.fromString("customer@mail.com")) - .build() - ); - - } + } } ``` Note that if you want to be able to track campaigns using *Referrers > Campaigns*, you must add the correct -URL parameters to your actionUrl. See [Tracking Campaigns](https://matomo.org/docs/tracking-campaigns/) for more information. All HTTP query parameters -denoted on the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate getters and setters. See -[MatomoRequest](core/src/main/java/org/matomo/java/tracking/MatomoRequest.java) for the mappings of the parameters to their corresponding attributes. +URL parameters to your actionUrl. See [Tracking Campaigns](https://matomo.org/docs/tracking-campaigns/) for more +information. All HTTP query parameters +denoted on the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the +appropriate getters and setters. See +[MatomoRequest](core/src/main/java/org/matomo/java/tracking/MatomoRequest.java) for the mappings of the parameters to +their corresponding attributes. Requests are validated prior to sending. If a request is invalid, a `MatomoException` will be thrown. @@ -599,18 +635,6 @@ This project can be tested and built by calling mvn install ``` -This project contains the following modules: - -* `core` contains the core functionality of the Matomo Java Tracker -* `java8` contains the Java 8 implementation of the Matomo Java Tracker -* `java11` contains the Java 11 implementation of the Matomo Java Tracker using the HttpClient available since Java 11 - (recommended) -* `servlet` contains `SerlvetMatomoRequest` to create a `MatomoRequest` from a `HttpServletRequest` and a filter - `MatomoTrackingFilter` that can be used to track requests to a servlet -* `spring` contains the Spring Boot Starter -* `test` contains tools for manual test against a local Matomo instance created with Docker (see below) - - The built jars and javadoc can be found in `target`. By using the Maven goal `install, a snapshot version can be used in your local Maven repository for testing purposes, e.g. From f16e73b57c29ac9adafe9283172e0ac4dc01eab3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Nov 2023 22:54:34 +0100 Subject: [PATCH 259/467] build(deps): bump org.apache.maven.plugins:maven-project-info-reports-plugin from 3.4.5 to 3.5.0 (#171) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c19a6b46..c8ca5f10 100644 --- a/pom.xml +++ b/pom.xml @@ -122,7 +122,7 @@ org.apache.maven.plugins maven-project-info-reports-plugin - 3.4.5 + 3.5.0 org.apache.maven.plugins From 7ca008522db1bb4fea5a83d451cd923e31f709a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Nov 2023 22:54:44 +0100 Subject: [PATCH 260/467] build(deps): bump org.springframework.boot:spring-boot-dependencies from 3.1.5 to 3.2.0 (#170) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- spring/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring/pom.xml b/spring/pom.xml index aa334936..81207821 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -17,7 +17,7 @@ 17 17 - 3.1.5 + 3.2.0 From 719339300860374598e4743326c3bbbbc4ce7854 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Nov 2023 22:54:54 +0100 Subject: [PATCH 261/467] build(deps): bump org.owasp:dependency-check-maven from 8.4.3 to 9.0.1 (#169) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c8ca5f10..02dadb26 100644 --- a/pom.xml +++ b/pom.xml @@ -359,7 +359,7 @@ org.owasp dependency-check-maven - 8.4.3 + 9.0.1 true From 00add392bdad7848e5aa6e334ed33a65dc13c756 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 22:13:35 +0100 Subject: [PATCH 262/467] build(deps): bump org.owasp:dependency-check-maven from 9.0.1 to 9.0.2 (#175) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 02dadb26..7d070aa9 100644 --- a/pom.xml +++ b/pom.xml @@ -359,7 +359,7 @@ org.owasp dependency-check-maven - 9.0.1 + 9.0.2 true From 1373808eecb35cc72877a260e1b77f9087fcab02 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 22:13:47 +0100 Subject: [PATCH 263/467] build(deps): bump org.apache.maven.plugins:maven-javadoc-plugin from 3.6.2 to 3.6.3 (#174) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7d070aa9..f76b6806 100644 --- a/pom.xml +++ b/pom.xml @@ -253,7 +253,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.6.2 + 3.6.3 ${delombok.output} none From 11e3f515cebf090f60e1af29c41e3c6114ce787a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 22:13:59 +0100 Subject: [PATCH 264/467] build(deps): bump com.github.spotbugs:spotbugs-maven-plugin from 4.8.1.0 to 4.8.2.0 (#173) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f76b6806..1a32c309 100644 --- a/pom.xml +++ b/pom.xml @@ -354,7 +354,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.8.1.0 + 4.8.2.0 org.owasp From d1d9fcb066b43222dc6b862ad82e071a03b1c6fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 22:14:14 +0100 Subject: [PATCH 265/467] build(deps): bump com.github.spotbugs:spotbugs-annotations from 4.8.1 to 4.8.2 (#172) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1a32c309..ad2920c0 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ com.github.spotbugs spotbugs-annotations - 4.8.1 + 4.8.2 provided From 40094e4b3be38e1e9cbbe848d1e7f046a213f8ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 22:50:19 +0100 Subject: [PATCH 266/467] build(deps): bump actions/configure-pages from 3 to 4 (#178) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 4b99e6e7..fff584eb 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/configure-pages@v3 + - uses: actions/configure-pages@v4 - uses: actions/jekyll-build-pages@v1 with: source: ./ From 76d489935dcc1285952a7799809a69bac96b9570 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 22:50:28 +0100 Subject: [PATCH 267/467] build(deps): bump actions/setup-java from 3 to 4 (#177) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 20a6c625..b00cbb0b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ jobs: contents: read steps: - uses: actions/checkout@v4 - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 25627e1e..b695a65c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -24,7 +24,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a60b8676..5e565b69 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' From 24593c646694b497de16786bc9ef002f00a5281c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 22:50:37 +0100 Subject: [PATCH 268/467] build(deps): bump actions/deploy-pages from 2 to 3 (#176) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index fff584eb..32434c9a 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -29,4 +29,4 @@ jobs: needs: build steps: - id: deployment - uses: actions/deploy-pages@v2 + uses: actions/deploy-pages@v3 From 8426b9dd1aa888dc95d19839993e8516c11cfc58 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Dec 2023 02:26:22 +0100 Subject: [PATCH 269/467] build(deps): bump org.owasp:dependency-check-maven from 9.0.2 to 9.0.4 (#180) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ad2920c0..40935687 100644 --- a/pom.xml +++ b/pom.xml @@ -359,7 +359,7 @@ org.owasp dependency-check-maven - 9.0.2 + 9.0.4 true From 4e787d929fda647f58c28708b7de57f783c89f3a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Dec 2023 08:21:30 +0100 Subject: [PATCH 270/467] build(deps-dev): bump org.eclipse.jetty.ee10:jetty-ee10-servlet from 12.0.3 to 12.0.4 (#179) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- servlet-jakarta/pom.xml | 2 +- test/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 40935687..46a83401 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.3 + 12.0.4 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 9a8e4257..8c3fffff 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.3 + 12.0.4 test diff --git a/test/pom.xml b/test/pom.xml index f60465d4..9382c955 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -46,7 +46,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.3 + 12.0.4 From 1d7f3b7bdab16a1f9f42ee1cdea9d2a744b6dc5e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 23:24:39 +0100 Subject: [PATCH 271/467] build(deps): bump github/codeql-action from 2 to 3 (#185) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b695a65c..f6ad8c52 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -29,8 +29,8 @@ jobs: java-version: '17' distribution: 'temurin' cache: maven - - uses: github/codeql-action/init@v2 + - uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} - - uses: github/codeql-action/autobuild@v2 - - uses: github/codeql-action/analyze@v2 + - uses: github/codeql-action/autobuild@v3 + - uses: github/codeql-action/analyze@v3 From e5a440c16dee398808fc0e894f7b77a9403ec285 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 23:24:48 +0100 Subject: [PATCH 272/467] build(deps): bump com.github.spotbugs:spotbugs-annotations from 4.8.2 to 4.8.3 (#184) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 46a83401..93081226 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ com.github.spotbugs spotbugs-annotations - 4.8.2 + 4.8.3 provided From ce1e7560f3816c9426e05dd0caabd220706effce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 23:24:59 +0100 Subject: [PATCH 273/467] build(deps): bump org.apache.maven.plugins:maven-surefire-plugin from 3.2.2 to 3.2.3 (#183) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 93081226..975ef0de 100644 --- a/pom.xml +++ b/pom.xml @@ -157,7 +157,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.2.2 + 3.2.3 org.apache.maven.plugins From 9986337fd5f351943ce37d3c7e6ca7b6af515189 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 23:25:13 +0100 Subject: [PATCH 274/467] build(deps): bump org.owasp:dependency-check-maven from 9.0.4 to 9.0.7 (#182) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 975ef0de..edaeb2fb 100644 --- a/pom.xml +++ b/pom.xml @@ -359,7 +359,7 @@ org.owasp dependency-check-maven - 9.0.4 + 9.0.7 true From 077bc157676dd5abf61a50facfb8cafa9f29a5b9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 23:25:22 +0100 Subject: [PATCH 275/467] build(deps): bump org.apache.maven.plugins:maven-failsafe-plugin from 3.2.2 to 3.2.3 (#181) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index edaeb2fb..3ee8b9ed 100644 --- a/pom.xml +++ b/pom.xml @@ -202,7 +202,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.2.2 + 3.2.3 From 0ff1efb48808459317a423de57b654456322b111 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Dec 2023 02:53:43 +0100 Subject: [PATCH 276/467] build(deps): bump actions/upload-pages-artifact from 2 to 3 (#191) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 32434c9a..ce4e9653 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -20,7 +20,7 @@ jobs: with: source: ./ destination: ./_site - - uses: actions/upload-pages-artifact@v2 + - uses: actions/upload-pages-artifact@v3 deploy: environment: name: github-pages From 6af68fa6324e8d5fa1e7cf12e0e529827f7910da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Dec 2023 02:54:40 +0100 Subject: [PATCH 277/467] build(deps): bump actions/deploy-pages from 3 to 4 (#190) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index ce4e9653..44316aef 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -29,4 +29,4 @@ jobs: needs: build steps: - id: deployment - uses: actions/deploy-pages@v3 + uses: actions/deploy-pages@v4 From 3c3e7964f1c788e2cc0c985bc2b757307bd4e1f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Dec 2023 02:54:49 +0100 Subject: [PATCH 278/467] build(deps): bump org.apache.maven.plugins:maven-compiler-plugin from 3.11.0 to 3.12.1 (#189) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3ee8b9ed..7af8ea1e 100644 --- a/pom.xml +++ b/pom.xml @@ -117,7 +117,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.11.0 + 3.12.1 org.apache.maven.plugins From 76804e770a6b8f59853f42cd785bdc9ba908b399 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Jan 2024 04:50:10 +0100 Subject: [PATCH 279/467] build(deps): bump org.assertj:assertj-core from 3.24.2 to 3.25.0 (#193) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7af8ea1e..6ec8c2df 100644 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ org.assertj assertj-core - 3.24.2 + 3.25.0 test From 41971c9525d7b38ecae41facffd0bfe09f318f93 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Jan 2024 04:50:55 +0100 Subject: [PATCH 280/467] build(deps): bump slf4j.version from 2.0.9 to 2.0.10 (#192) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6ec8c2df..799b0c6e 100644 --- a/pom.xml +++ b/pom.xml @@ -63,7 +63,7 @@ 1.8 ${project.build.outputDirectory}/delombok 1.18.30 - 2.0.9 + 2.0.10 From 1bf372a2cf0a0b7f888cf71d97c47f8ef34dede1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 22:14:13 +0100 Subject: [PATCH 281/467] build(deps): bump org.assertj:assertj-core from 3.25.0 to 3.25.1 (#195) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 799b0c6e..5e83f4d6 100644 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ org.assertj assertj-core - 3.25.0 + 3.25.1 test From 9fbb2618ccbd61c26cb2345738c763ea59049ae7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 22:14:22 +0100 Subject: [PATCH 282/467] build(deps): bump slf4j.version from 2.0.10 to 2.0.11 (#194) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5e83f4d6..23bc84ed 100644 --- a/pom.xml +++ b/pom.xml @@ -63,7 +63,7 @@ 1.8 ${project.build.outputDirectory}/delombok 1.18.30 - 2.0.10 + 2.0.11 From a8ece376bac559919ffe73cd61588a9da45c56ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 22:25:11 +0100 Subject: [PATCH 283/467] build(deps): bump scacap/action-surefire-report from 1.7.2 to 1.7.3 (#197) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b00cbb0b..5fde8dd7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,4 +27,4 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} min-coverage-overall: 80 min-coverage-changed-files: 80 - - uses: scacap/action-surefire-report@v1.7.2 \ No newline at end of file + - uses: scacap/action-surefire-report@v1.7.3 \ No newline at end of file From e212e79a4ce884a1b82b0c681e8b5e09f27ed03d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 22:26:00 +0100 Subject: [PATCH 284/467] build(deps): bump com.github.spotbugs:spotbugs-maven-plugin from 4.8.2.0 to 4.8.3.0 (#199) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 23bc84ed..50fb616d 100644 --- a/pom.xml +++ b/pom.xml @@ -354,7 +354,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.8.2.0 + 4.8.3.0 org.owasp From 518f57e9a64ab65f2e662343e9674b9e07b57666 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 22:26:11 +0100 Subject: [PATCH 285/467] build(deps): bump org.apache.maven.plugins:maven-surefire-plugin from 3.2.3 to 3.2.5 (#198) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 50fb616d..08bf006d 100644 --- a/pom.xml +++ b/pom.xml @@ -157,7 +157,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.2.3 + 3.2.5 org.apache.maven.plugins From 38c2cd9d14804309b76b3279bacf2fafa70ec3b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 22:26:20 +0100 Subject: [PATCH 286/467] build(deps): bump org.apache.maven.plugins:maven-failsafe-plugin from 3.2.3 to 3.2.5 (#200) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 08bf006d..fff2acb3 100644 --- a/pom.xml +++ b/pom.xml @@ -202,7 +202,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.2.3 + 3.2.5 From 8fc594339577d0e427b30e65c43795cbc22494e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:59:12 +0100 Subject: [PATCH 287/467] build(deps-dev): bump org.eclipse.jetty:jetty-servlet from 10.0.18 to 10.0.19 (#188) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- servlet-javax/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 83205e5a..da8dc021 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jetty jetty-servlet - 10.0.18 + 10.0.19 test From fb5faefd9fae197a12deac60f9efac9afb8ff18c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:59:36 +0100 Subject: [PATCH 288/467] build(deps-dev): bump org.eclipse.jetty.ee10:jetty-ee10-servlet from 12.0.4 to 12.0.5 (#187) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- servlet-jakarta/pom.xml | 2 +- test/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index fff2acb3..0be5d5d9 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.4 + 12.0.5 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 8c3fffff..574b4225 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.4 + 12.0.5 test diff --git a/test/pom.xml b/test/pom.xml index 9382c955..1da4fff4 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -46,7 +46,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.4 + 12.0.5 From 4689d54799292462169aba734b9ec19964efe132 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:59:44 +0100 Subject: [PATCH 289/467] build(deps): bump org.springframework.boot:spring-boot-dependencies from 3.2.0 to 3.2.1 (#186) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- spring/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring/pom.xml b/spring/pom.xml index 81207821..bdd2d1ca 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -17,7 +17,7 @@ 17 17 - 3.2.0 + 3.2.1 From 975a0bb07a751fc4d276c1c6fe1b6e8636f49e04 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 22:33:18 +0100 Subject: [PATCH 290/467] build(deps): bump org.owasp:dependency-check-maven from 9.0.7 to 9.0.9 (#202) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0be5d5d9..f1350d4a 100644 --- a/pom.xml +++ b/pom.xml @@ -359,7 +359,7 @@ org.owasp dependency-check-maven - 9.0.7 + 9.0.9 true From a7929f5e98bd3850848b204f554361e13433b746 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 22:33:27 +0100 Subject: [PATCH 291/467] build(deps): bump org.springframework.boot:spring-boot-dependencies from 3.2.1 to 3.2.2 (#201) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- spring/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring/pom.xml b/spring/pom.xml index bdd2d1ca..de1807a3 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -17,7 +17,7 @@ 17 17 - 3.2.1 + 3.2.2 From 9e898b716572385ec3c546fc11bc754dd278ea73 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 22:20:23 +0100 Subject: [PATCH 292/467] build(deps): bump org.assertj:assertj-core from 3.25.1 to 3.25.2 (#203) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f1350d4a..1184e8be 100644 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ org.assertj assertj-core - 3.25.1 + 3.25.2 test From d378e035dc958760e53e9d6ccff9470d80e7b6d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 22:50:09 +0100 Subject: [PATCH 293/467] build(deps): bump release-drafter/release-drafter from 5 to 6 (#204) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5e565b69..69ce43c6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,7 +28,7 @@ jobs: OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - - uses: release-drafter/release-drafter@v5 + - uses: release-drafter/release-drafter@v6 with: version: ${{ steps.version.outputs.version }} publish: true From 2070b96957bf3e434bf7a141b9556a9970427840 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 22:57:38 +0100 Subject: [PATCH 294/467] build(deps-dev): bump org.eclipse.jetty:jetty-servlet from 10.0.19 to 10.0.20 (#209) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- servlet-javax/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index da8dc021..87ef6cde 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jetty jetty-servlet - 10.0.19 + 10.0.20 test From 14d8a10f621a5d9c47a3087ca0df17d968aaa902 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 22:58:42 +0100 Subject: [PATCH 295/467] build(deps): bump org.junit.jupiter:junit-jupiter from 5.10.1 to 5.10.2 (#208) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1184e8be..0ac30cb4 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ org.junit.jupiter junit-jupiter - 5.10.1 + 5.10.2 test From 6068652c85d80c3af1ea87f18ddc672c8def7d2d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 22:58:50 +0100 Subject: [PATCH 296/467] build(deps-dev): bump org.eclipse.jetty.ee10:jetty-ee10-servlet from 12.0.5 to 12.0.6 (#207) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- servlet-jakarta/pom.xml | 2 +- test/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 0ac30cb4..77bdcb50 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.5 + 12.0.6 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 574b4225..7e1d7f93 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.5 + 12.0.6 test diff --git a/test/pom.xml b/test/pom.xml index 1da4fff4..8162e42e 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -46,7 +46,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.5 + 12.0.6 From a546676fa987748f61199f3cde83ebcf7bcb0cda Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 22:58:58 +0100 Subject: [PATCH 297/467] build(deps): bump org.assertj:assertj-core from 3.25.2 to 3.25.3 (#206) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 77bdcb50..a6ce7789 100644 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ org.assertj assertj-core - 3.25.2 + 3.25.3 test From 769d6683b063906b4116e91dcf07bd53ad0c6446 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 22:59:06 +0100 Subject: [PATCH 298/467] build(deps): bump slf4j.version from 2.0.11 to 2.0.12 (#205) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a6ce7789..8db8a19d 100644 --- a/pom.xml +++ b/pom.xml @@ -63,7 +63,7 @@ 1.8 ${project.build.outputDirectory}/delombok 1.18.30 - 2.0.11 + 2.0.12 From 8a39faab72ebde89cba1cc5da3ae1d5c1305e204 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Tue, 6 Feb 2024 22:00:59 +0000 Subject: [PATCH 299/467] [ci skip] prepare release matomo-java-tracker-parent-3.2.1 --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index e05e2923..861de180 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.1-SNAPSHOT + 3.2.1 ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index 6aa9b2d0..8c90d203 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.1-SNAPSHOT + 3.2.1 ../pom.xml matomo-java-tracker-java11 - 3.2.1-SNAPSHOT + 3.2.1 jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index 21c8ed91..0cbf5cc3 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.1-SNAPSHOT + 3.2.1 ../pom.xml matomo-java-tracker - 3.2.1-SNAPSHOT + 3.2.1 jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index 8db8a19d..ee6d0a8c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.1-SNAPSHOT + 3.2.1 pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - HEAD + matomo-java-tracker-parent-3.2.1 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 7e1d7f93..2f9032f3 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.1-SNAPSHOT + 3.2.1 ../pom.xml matomo-java-tracker-servlet-jakarta - 3.2.1-SNAPSHOT + 3.2.1 jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 87ef6cde..dd52220a 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.1-SNAPSHOT + 3.2.1 ../pom.xml matomo-java-tracker-servlet-javax - 3.2.1-SNAPSHOT + 3.2.1 jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index de1807a3..c06ff425 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.1-SNAPSHOT + 3.2.1 ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 8162e42e..8ad39261 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.1-SNAPSHOT + 3.2.1 ../pom.xml From cf40af88c6104767bbf821561074f829c60f63ee Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Tue, 6 Feb 2024 22:01:02 +0000 Subject: [PATCH 300/467] [ci skip] prepare for next development iteration --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 861de180..b6d57050 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.1 + 3.2.2-SNAPSHOT ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index 8c90d203..f367934e 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.1 + 3.2.2-SNAPSHOT ../pom.xml matomo-java-tracker-java11 - 3.2.1 + 3.2.2-SNAPSHOT jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index 0cbf5cc3..9c47baae 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.1 + 3.2.2-SNAPSHOT ../pom.xml matomo-java-tracker - 3.2.1 + 3.2.2-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index ee6d0a8c..fdaf2f0a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.1 + 3.2.2-SNAPSHOT pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - matomo-java-tracker-parent-3.2.1 + HEAD diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 2f9032f3..59d85e5b 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.1 + 3.2.2-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-jakarta - 3.2.1 + 3.2.2-SNAPSHOT jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index dd52220a..4fd8bf9d 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.1 + 3.2.2-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-javax - 3.2.1 + 3.2.2-SNAPSHOT jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index c06ff425..8adfadc4 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.1 + 3.2.2-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 8ad39261..24aeb628 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.1 + 3.2.2-SNAPSHOT ../pom.xml From f604170cc4f8b6d20c78ab122aac8f032c3d308c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 22:57:25 +0100 Subject: [PATCH 301/467] build(deps): bump com.github.spotbugs:spotbugs-maven-plugin from 4.8.3.0 to 4.8.3.1 (#210) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fdaf2f0a..247ff62f 100644 --- a/pom.xml +++ b/pom.xml @@ -354,7 +354,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.8.3.0 + 4.8.3.1 org.owasp From 569cf81f04f723b8901044beb7bfcfe325d0bb48 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Feb 2024 22:59:18 +0100 Subject: [PATCH 302/467] build(deps-dev): bump org.wiremock:wiremock from 3.3.1 to 3.4.1 (#211) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- java11/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java11/pom.xml b/java11/pom.xml index f367934e..539cfdd0 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -48,7 +48,7 @@ org.wiremock wiremock - 3.3.1 + 3.4.1 test From de8db508597b0d60446ae0df28eb6b63c4678e2e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 22:15:19 +0100 Subject: [PATCH 303/467] build(deps): bump scacap/action-surefire-report from 1.7.3 to 1.8.0 (#212) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5fde8dd7..2c79ce42 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,4 +27,4 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} min-coverage-overall: 80 min-coverage-changed-files: 80 - - uses: scacap/action-surefire-report@v1.7.3 \ No newline at end of file + - uses: scacap/action-surefire-report@v1.8.0 \ No newline at end of file From 4722ebd689ed4265452e94c58d58db65d9dacbcf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 22:24:02 +0100 Subject: [PATCH 304/467] build(deps): bump org.springframework.boot:spring-boot-dependencies from 3.2.2 to 3.2.3 (#214) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- spring/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring/pom.xml b/spring/pom.xml index 8adfadc4..b1a3c1e6 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -17,7 +17,7 @@ 17 17 - 3.2.2 + 3.2.3 From 892f688b602bc3c983ea0002e2c8813806f1f0b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 22:24:16 +0100 Subject: [PATCH 305/467] build(deps-dev): bump org.wiremock:wiremock from 3.4.1 to 3.4.2 (#213) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- java11/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java11/pom.xml b/java11/pom.xml index 539cfdd0..7d181c7f 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -48,7 +48,7 @@ org.wiremock wiremock - 3.4.1 + 3.4.2 test From 88280fd5f5add0aed8219b19722d876b9b3a8b37 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Sat, 9 Mar 2024 13:30:18 +0100 Subject: [PATCH 306/467] Resolves #215 --- README.md | 190 +++++++++++------- core/pom.xml | 2 +- .../java/tracking/ExecutorServiceCloser.java | 42 ++++ .../matomo/java/tracking/MatomoTracker.java | 10 +- .../java/org/matomo/java/tracking/Sender.java | 2 +- .../tracking/ExecutorServiceCloserTest.java | 46 +++++ .../org/matomo/java/tracking/TestSender.java | 4 + java11/pom.xml | 4 +- .../matomo/java/tracking/Java11Sender.java | 71 +++++-- .../java/tracking/Java11SenderProvider.java | 25 ++- .../matomo/java/tracking/Java11SenderIT.java | 13 +- java8/pom.xml | 4 +- .../org/matomo/java/tracking/Java8Sender.java | 12 +- .../matomo/java/tracking/Java8SenderIT.java | 3 +- .../matomo/java/tracking/MatomoTrackerIT.java | 8 + pom.xml | 2 +- servlet-jakarta/pom.xml | 4 +- .../tracking/servlet/MatomoTrackerFilter.java | 11 + .../org/matomo/java/tracking/TestSender.java | 4 + servlet-javax/pom.xml | 4 +- .../java/tracking/MatomoTrackerFilterIT.java | 2 + .../org/matomo/java/tracking/TestSender.java | 4 + spring/pom.xml | 2 +- test/pom.xml | 2 +- .../java/tracking/test/BulkExample.java | 26 +-- .../java/tracking/test/ConsumerExample.java | 28 +-- .../java/tracking/test/EcommerceExample.java | 54 ++--- .../tracking/test/MatomoTrackerTester.java | 21 +- .../java/tracking/test/SendExample.java | 16 +- 29 files changed, 426 insertions(+), 190 deletions(-) create mode 100644 core/src/main/java/org/matomo/java/tracking/ExecutorServiceCloser.java create mode 100644 core/src/test/java/org/matomo/java/tracking/ExecutorServiceCloserTest.java diff --git a/README.md b/README.md index bcd72ba4..fe47114d 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ dependencies. Release notes can be found here: https://github.com/matomo-org/mat Here are the most important changes: -* Matomo Java Tracker 3.2.0 is compatible with Matomo 4 and 5 +* Matomo Java Tracker 3.3.0 is compatible with Matomo 4 and 5 * less dependencies * new dimension parameter * special types allow to provide valid parameters now @@ -140,7 +140,7 @@ Add a dependency on Matomo Java Tracker using Maven. For Java 8: org.piwik.java.tracking matomo-java-tracker - 3.2.0 + 3.3.0 ``` @@ -151,7 +151,7 @@ For Java 11: org.piwik.java.tracking matomo-java-tracker-java11 - 3.2.0 + 3.3.0 ``` @@ -159,7 +159,7 @@ or Gradle (Java 8): ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker:3.2.0") + implementation("org.piwik.java.tracking:matomo-java-tracker:3.3.0") } ``` @@ -167,20 +167,20 @@ or Gradle (Java 11): ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.2.0") + implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.3.0") } ``` or Gradle with Kotlin DSL (Java 8) ```kotlin -implementation("org.piwik.java.tracking:matomo-java-tracker:3.2.0") +implementation("org.piwik.java.tracking:matomo-java-tracker:3.3.0") ``` or Gradle with Kotlin DSL (Java 11) ```kotlin -implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.2.0") +implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.3.0") ``` ### Spring Boot Module @@ -193,7 +193,7 @@ and allows you to configure the tracker via application properties. Add the foll org.piwik.java.tracking matomo-java-tracker-spring-boot-starter - 3.2.0 + 3.3.0 ``` @@ -201,14 +201,14 @@ or Gradle: ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.2.0") + implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.3.0") } ``` or Gradle with Kotlin DSL ```kotlin -implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.2.0") +implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.3.0") ``` The following properties are supported: @@ -270,14 +270,21 @@ To let the Matomo Java Tracker send a request to the Matomo instance, you need t ```java import java.net.URI; -import org.matomo.java.tracking.MatomoRequest; import org.matomo.java.tracking.MatomoRequests; import org.matomo.java.tracking.MatomoTracker; import org.matomo.java.tracking.TrackerConfiguration; import org.matomo.java.tracking.parameters.VisitorId; +/** + * Example for sending a request. + */ public class SendExample { + /** + * Example for sending a request. + * + * @param args ignored + */ public static void main(String[] args) { TrackerConfiguration configuration = TrackerConfiguration @@ -288,17 +295,19 @@ public class SendExample { .logFailedTracking(true) .build(); - MatomoTracker tracker = new MatomoTracker(configuration); - - tracker.sendRequestAsync(MatomoRequests - .event("Training", "Workout completed", "Bench press", 60.0) - .visitorId(VisitorId.fromString("customer@mail.com")) - .build() - ); - + try (MatomoTracker tracker = new MatomoTracker(configuration)) { + tracker.sendRequestAsync(MatomoRequests + .event("Training", "Workout completed", "Bench press", 60.0) + .visitorId(VisitorId.fromString("customer@mail.com")) + .build() + ); + } catch (Exception e) { + throw new RuntimeException("Could not close tracker", e); + } } } + ``` This will send a request to the Matomo instance at https://www.yourdomain.com/matomo.php and track a page view for the @@ -311,15 +320,23 @@ If you want to perform an operation after a successful asynchronous call to Mato result like this: ```java - import java.net.URI; +import org.matomo.java.tracking.MatomoRequest; import org.matomo.java.tracking.MatomoRequests; import org.matomo.java.tracking.MatomoTracker; import org.matomo.java.tracking.TrackerConfiguration; import org.matomo.java.tracking.parameters.VisitorId; +/** + * Example for sending a request and performing an action when the request was sent successfully. + */ public class ConsumerExample { + /** + * Example for sending a request and performing an action when the request was sent successfully. + * + * @param args ignored + */ public static void main(String[] args) { TrackerConfiguration configuration = TrackerConfiguration @@ -330,24 +347,27 @@ public class ConsumerExample { .logFailedTracking(true) .build(); - MatomoTracker tracker = new MatomoTracker(configuration); - - MatomoRequest request = MatomoRequests - .event("Training", "Workout completed", "Bench press", 60.0) - .visitorId(VisitorId.fromString("customer@mail.com")) - .build(); - - tracker.sendRequestAsync(request) - .thenAccept(req -> System.out.printf("Sent request %s%n", req)) - .exceptionally(throwable -> { - System.err.printf("Failed to send request: %s%n", throwable.getMessage()); - return null; - }); + try (MatomoTracker tracker = new MatomoTracker(configuration)) { + MatomoRequest request = MatomoRequests + .event("Training", "Workout completed", "Bench press", 60.0) + .visitorId(VisitorId.fromString("customer@mail.com")) + .build(); + + tracker.sendRequestAsync(request) + .thenAccept(req -> System.out.printf("Sent request %s%n", req)) + .exceptionally(throwable -> { + System.err.printf("Failed to send request: %s%n", throwable.getMessage()); + return null; + }); + } catch (Exception e) { + throw new RuntimeException("Could not close tracker", e); + } } } + ``` If you have multiple requests to wish to track, it may be more efficient to send them in a single HTTP call. To do this, @@ -355,14 +375,21 @@ send a bulk request. Place your requests in an _Iterable_ data structure and cal ```java import java.net.URI; -import org.matomo.java.tracking.MatomoRequest; import org.matomo.java.tracking.MatomoRequests; import org.matomo.java.tracking.MatomoTracker; import org.matomo.java.tracking.TrackerConfiguration; import org.matomo.java.tracking.parameters.VisitorId; +/** + * Example for sending multiple requests in one bulk request. + */ public class BulkExample { + /** + * Example for sending multiple requests in one bulk request. + * + * @param args ignored + */ public static void main(String[] args) { TrackerConfiguration configuration = TrackerConfiguration @@ -373,23 +400,24 @@ public class BulkExample { .logFailedTracking(true) .build(); - MatomoTracker tracker = new MatomoTracker(configuration); - - VisitorId visitorId = VisitorId.fromString("customer@mail.com"); - tracker.sendBulkRequestAsync( - MatomoRequests.siteSearch("Running shoes", "Running", 120L) - .visitorId(visitorId).build(), - MatomoRequests.pageView("VelocityStride ProX Running Shoes") - .visitorId(visitorId).build(), - MatomoRequests.ecommerceOrder("QXZ-789LMP", 100.0, 124.0, 19.0, 10.0, 5.0) - .visitorId(visitorId) - .build() - ); + try (MatomoTracker tracker = new MatomoTracker(configuration)) { + VisitorId visitorId = VisitorId.fromString("customer@mail.com"); + tracker.sendBulkRequestAsync( + MatomoRequests.siteSearch("Running shoes", "Running", 120L) + .visitorId(visitorId).build(), + MatomoRequests.pageView("VelocityStride ProX Running Shoes") + .visitorId(visitorId).build(), + MatomoRequests.ecommerceOrder("QXZ-789LMP", 100.0, 124.0, 19.0, 10.0, 5.0) + .visitorId(visitorId) + .build() + ); + } catch (Exception e) { + throw new RuntimeException("Could not close tracker", e); + } } } - ``` This will send two requests in a single HTTP call. The requests will be sent asynchronously. @@ -410,9 +438,7 @@ a unique identifier, e.g. an email address. If you do not provide a visitor id, Ecommerce requests contain ecommerce items, that can be fluently build: ```java - import java.net.URI; -import org.matomo.java.tracking.MatomoRequest; import org.matomo.java.tracking.MatomoRequests; import org.matomo.java.tracking.MatomoTracker; import org.matomo.java.tracking.TrackerConfiguration; @@ -420,8 +446,16 @@ import org.matomo.java.tracking.parameters.EcommerceItem; import org.matomo.java.tracking.parameters.EcommerceItems; import org.matomo.java.tracking.parameters.VisitorId; +/** + * Example for sending an ecommerce request. + */ public class EcommerceExample { + /** + * Example for sending an ecommerce request. + * + * @param args ignored + */ public static void main(String[] args) { TrackerConfiguration configuration = TrackerConfiguration @@ -432,37 +466,38 @@ public class EcommerceExample { .logFailedTracking(true) .build(); - MatomoTracker tracker = new MatomoTracker(configuration); - - tracker.sendBulkRequestAsync(MatomoRequests - .ecommerceCartUpdate(50.0) - .ecommerceItems(EcommerceItems - .builder() - .item(EcommerceItem - .builder() - .sku("XYZ12345") - .name("Matomo - The big book about web analytics") - .category("Education & Teaching") - .price(23.1) - .quantity(2) - .build()) - .item(EcommerceItem - .builder() - .sku("B0C2WV3MRJ") - .name("Matomo for data visualization") - .category("Education & Teaching") - .price(15.0) - .quantity(1) - .build()) - .build()) - .visitorId(VisitorId.fromString("customer@mail.com")) - .build() - ); + try (MatomoTracker tracker = new MatomoTracker(configuration)) { + tracker.sendBulkRequestAsync(MatomoRequests + .ecommerceCartUpdate(50.0) + .ecommerceItems(EcommerceItems + .builder() + .item(EcommerceItem + .builder() + .sku("XYZ12345") + .name("Matomo - The big book about web analytics") + .category("Education & Teaching") + .price(23.1) + .quantity(2) + .build()) + .item(EcommerceItem + .builder() + .sku("B0C2WV3MRJ") + .name("Matomo for data visualization") + .category("Education & Teaching") + .price(15.0) + .quantity(1) + .build()) + .build()) + .visitorId(VisitorId.fromString("customer@mail.com")) + .build() + ); + } catch (Exception e) { + throw new RuntimeException("Could not close tracker", e); + } } } - ``` Note that if you want to be able to track campaigns using *Referrers > Campaigns*, you must add the correct @@ -640,11 +675,10 @@ the Maven goal `install, a snapshot version can be used in your local Maven repository for testing purposes, e.g. ```xml - org.piwik.java.tracking matomo-java-tracker - 3.2.1-SNAPSHOT + 3.3.1-SNAPSHOT ``` diff --git a/core/pom.xml b/core/pom.xml index b6d57050..e7453fc2 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.2-SNAPSHOT + 3.3.0-SNAPSHOT ../pom.xml diff --git a/core/src/main/java/org/matomo/java/tracking/ExecutorServiceCloser.java b/core/src/main/java/org/matomo/java/tracking/ExecutorServiceCloser.java new file mode 100644 index 00000000..1ef11c97 --- /dev/null +++ b/core/src/main/java/org/matomo/java/tracking/ExecutorServiceCloser.java @@ -0,0 +1,42 @@ +package org.matomo.java.tracking; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import lombok.NonNull; + +/** + * Helps to close an executor service. + */ +public class ExecutorServiceCloser { + + /** + * Closes the given executor service. + * + *

This will check whether the executor service is already terminated, and if not, it + * initiates a shutdown and waits a minute. If the minute expires, the executor service + * is shutdown immediately. + * + * @param executorService The executor service to close + */ + public static void close(@NonNull ExecutorService executorService) { + boolean terminated = executorService.isTerminated(); + if (!terminated) { + executorService.shutdown(); + boolean interrupted = false; + while (!terminated) { + try { + terminated = executorService.awaitTermination(1L, TimeUnit.MINUTES); + } catch (InterruptedException e) { + if (!interrupted) { + executorService.shutdownNow(); + interrupted = true; + } + } + } + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + +} diff --git a/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java index 51a585d5..84c48271 100644 --- a/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java +++ b/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -35,7 +35,7 @@ * @author brettcsorba */ @Slf4j -public class MatomoTracker { +public class MatomoTracker implements AutoCloseable { private final TrackerConfiguration trackerConfiguration; @@ -364,4 +364,12 @@ public CompletableFuture sendBulkRequestAsync( ) { return sendBulkRequestAsync(requests, authToken, null); } + + @Override + public void close() throws Exception { + if (sender != null) { + sender.close(); + } + } + } diff --git a/core/src/main/java/org/matomo/java/tracking/Sender.java b/core/src/main/java/org/matomo/java/tracking/Sender.java index acb82280..a256335e 100644 --- a/core/src/main/java/org/matomo/java/tracking/Sender.java +++ b/core/src/main/java/org/matomo/java/tracking/Sender.java @@ -4,7 +4,7 @@ import edu.umd.cs.findbugs.annotations.Nullable; import java.util.concurrent.CompletableFuture; -interface Sender { +interface Sender extends AutoCloseable { @NonNull CompletableFuture sendSingleAsync( @NonNull MatomoRequest request diff --git a/core/src/test/java/org/matomo/java/tracking/ExecutorServiceCloserTest.java b/core/src/test/java/org/matomo/java/tracking/ExecutorServiceCloserTest.java new file mode 100644 index 00000000..e0d167a9 --- /dev/null +++ b/core/src/test/java/org/matomo/java/tracking/ExecutorServiceCloserTest.java @@ -0,0 +1,46 @@ +package org.matomo.java.tracking; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import org.junit.jupiter.api.Test; + +class ExecutorServiceCloserTest { + + @Test + void shutsDownExecutorService() { + + ExecutorService executorService = Executors.newFixedThreadPool(2, new DaemonThreadFactory()); + + ExecutorServiceCloser.close(executorService); + + assertThat(executorService.isTerminated()).isTrue(); + assertThat(executorService.isShutdown()).isTrue(); + + } + + @Test + void shutsDownExecutorServiceImmediately() throws Exception { + + ExecutorService executorService = Executors.newSingleThreadExecutor(); + executorService.submit(() -> { + try { + Thread.sleep(10000L); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }); + + Thread thread = new Thread(() -> { + ExecutorServiceCloser.close(executorService); + }); + thread.start(); + Thread.sleep(1000L); + thread.interrupt(); + + assertThat(executorService.isShutdown()).isTrue(); + + } + +} \ No newline at end of file diff --git a/core/src/test/java/org/matomo/java/tracking/TestSender.java b/core/src/test/java/org/matomo/java/tracking/TestSender.java index 4d4c7465..0342cf98 100644 --- a/core/src/test/java/org/matomo/java/tracking/TestSender.java +++ b/core/src/test/java/org/matomo/java/tracking/TestSender.java @@ -69,4 +69,8 @@ private void createQueryAndAddRequest(@lombok.NonNull MatomoRequest request, @Nu requests.add(request); } + @Override + public void close() { + // do nothing + } } diff --git a/java11/pom.xml b/java11/pom.xml index 7d181c7f..28779b26 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.2-SNAPSHOT + 3.3.0-SNAPSHOT ../pom.xml matomo-java-tracker-java11 - 3.2.2-SNAPSHOT + 3.3.0-SNAPSHOT jar Matomo Java Tracker Java 11 diff --git a/java11/src/main/java/org/matomo/java/tracking/Java11Sender.java b/java11/src/main/java/org/matomo/java/tracking/Java11Sender.java index 3daf434f..2cff400d 100644 --- a/java11/src/main/java/org/matomo/java/tracking/Java11Sender.java +++ b/java11/src/main/java/org/matomo/java/tracking/Java11Sender.java @@ -17,6 +17,7 @@ import java.util.TreeMap; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -39,9 +40,14 @@ public class Java11Sender implements Sender { @lombok.NonNull private final CookieStore cookieStore; + @lombok.NonNull + private final ExecutorService executorService; + @NonNull @Override - public CompletableFuture sendSingleAsync(@NonNull @lombok.NonNull MatomoRequest request) { + public CompletableFuture sendSingleAsync( + @NonNull @lombok.NonNull MatomoRequest request + ) { return sendAsyncAndCheckResponse(buildHttpGetRequest(request), request); } @@ -51,14 +57,19 @@ public void sendSingle(@NonNull @lombok.NonNull MatomoRequest request) { } private void sendAndCheckResponse(@NonNull HttpRequest httpRequest) { - checkResponse(send(httpRequest, () -> httpClient.send(httpRequest, HttpResponse.BodyHandlers.discarding())), + checkResponse( + send( + httpRequest, + () -> httpClient.send(httpRequest, HttpResponse.BodyHandlers.discarding()) + ), httpRequest ); } @Override public void sendBulk( - @NonNull @lombok.NonNull Iterable requests, @Nullable String overrideAuthToken + @NonNull @lombok.NonNull Iterable requests, + @Nullable String overrideAuthToken ) { sendAndCheckResponse(buildHttpPostRequest(requests, overrideAuthToken)); } @@ -67,7 +78,8 @@ public void sendBulk( private HttpRequest buildHttpPostRequest( @NonNull Iterable requests, @Nullable String overrideAuthToken ) { - String authToken = AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); + String authToken = + AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); Collection queries = new ArrayList<>(); Map headers = new LinkedHashMap<>(10); String headerUserAgent = null; @@ -103,7 +115,8 @@ private HttpRequest buildHttpPostRequest( @NonNull @Override public CompletableFuture sendBulkAsync( - @NonNull @lombok.NonNull Iterable requests, @Nullable String overrideAuthToken + @NonNull @lombok.NonNull Iterable requests, + @Nullable String overrideAuthToken ) { return sendAsyncAndCheckResponse(buildHttpPostRequest(requests, overrideAuthToken), null); } @@ -112,11 +125,13 @@ public CompletableFuture sendBulkAsync( private CompletableFuture sendAsyncAndCheckResponse( @NonNull HttpRequest httpRequest, @Nullable T result ) { - return send(httpRequest, - () -> httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.discarding()).thenApply(response -> { - checkResponse(response, httpRequest); - return result; - }) + return send( + httpRequest, + () -> httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.discarding()) + .thenApply(response -> { + checkResponse(response, httpRequest); + return result; + }) ); } @@ -129,7 +144,8 @@ private HttpRequest buildHttpGetRequest(@NonNull MatomoRequest request) { URI apiEndpoint = trackerConfiguration.getApiEndpoint(); HttpRequest.Builder builder = HttpRequest .newBuilder() - .uri(apiEndpoint.resolve(String.format("%s?%s", + .uri(apiEndpoint.resolve(String.format( + "%s?%s", apiEndpoint.getPath(), queryCreator.createQuery(request, authToken) ))); @@ -155,12 +171,22 @@ private T send( } } - private void checkResponse(@NonNull HttpResponse response, @NonNull HttpRequest httpRequest) { + private void checkResponse( + @NonNull HttpResponse response, + @NonNull HttpRequest httpRequest + ) { if (response.statusCode() > 399) { if (trackerConfiguration.isLogFailedTracking()) { - log.error("Received HTTP error code {} for URL {}", response.statusCode(), httpRequest.uri()); + log.error( + "Received HTTP error code {} for URL {}", + response.statusCode(), + httpRequest.uri() + ); } - throw new MatomoException(String.format("Tracking endpoint responded with code %d", response.statusCode())); + throw new MatomoException(String.format( + "Tracking endpoint responded with code %d", + response.statusCode() + )); } } @@ -176,13 +202,16 @@ private void addCookies(MatomoRequest request) { } private void applyTrackerConfiguration(@NonNull HttpRequest.Builder builder) { - if (trackerConfiguration.getSocketTimeout() != null && trackerConfiguration.getSocketTimeout().toMillis() > 0L) { + if (trackerConfiguration.getSocketTimeout() != null + && trackerConfiguration.getSocketTimeout().toMillis() > 0L) { builder.timeout(trackerConfiguration.getSocketTimeout()); } } private void setUserAgentHeader( - HttpRequest.Builder builder, @Nullable String headerUserAgent, @Nullable Map headers + HttpRequest.Builder builder, + @Nullable String headerUserAgent, + @Nullable Map headers ) { String userAgentHeader = null; if ((headerUserAgent == null || headerUserAgent.trim().isEmpty()) && headers != null) { @@ -197,11 +226,19 @@ private void setUserAgentHeader( } } - private void addHeaders(@NonNull HttpRequest.Builder builder, @Nullable Map headers) { + private void addHeaders( + @NonNull HttpRequest.Builder builder, + @Nullable Map headers + ) { if (headers != null) { for (Map.Entry header : headers.entrySet()) { builder.header(header.getKey(), header.getValue()); } } } + + @Override + public void close() { + ExecutorServiceCloser.close(executorService); + } } diff --git a/java11/src/main/java/org/matomo/java/tracking/Java11SenderProvider.java b/java11/src/main/java/org/matomo/java/tracking/Java11SenderProvider.java index 273ca19f..d6145198 100644 --- a/java11/src/main/java/org/matomo/java/tracking/Java11SenderProvider.java +++ b/java11/src/main/java/org/matomo/java/tracking/Java11SenderProvider.java @@ -6,6 +6,7 @@ import java.net.ProxySelector; import java.net.http.HttpClient; import java.security.SecureRandom; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; @@ -22,19 +23,25 @@ public Sender provideSender( TrackerConfiguration trackerConfiguration, QueryCreator queryCreator ) { CookieManager cookieManager = new CookieManager(); + ExecutorService executorService = Executors.newFixedThreadPool( + trackerConfiguration.getThreadPoolSize(), + new DaemonThreadFactory() + ); HttpClient.Builder builder = HttpClient .newBuilder() .cookieHandler(cookieManager) - .executor(Executors.newFixedThreadPool(trackerConfiguration.getThreadPoolSize(), new DaemonThreadFactory())) - ; - if (trackerConfiguration.getConnectTimeout() != null && trackerConfiguration.getConnectTimeout().toMillis() > 0L) { + .executor(executorService); + if (trackerConfiguration.getConnectTimeout() != null + && trackerConfiguration.getConnectTimeout().toMillis() > 0L) { builder.connectTimeout(trackerConfiguration.getConnectTimeout()); } if (!isEmpty(trackerConfiguration.getProxyHost()) && trackerConfiguration.getProxyPort() > 0) { - builder.proxy(ProxySelector.of(new InetSocketAddress(trackerConfiguration.getProxyHost(), + builder.proxy(ProxySelector.of(new InetSocketAddress( + trackerConfiguration.getProxyHost(), trackerConfiguration.getProxyPort() ))); - if (!isEmpty(trackerConfiguration.getProxyUsername()) && !isEmpty(trackerConfiguration.getProxyPassword())) { + if (!isEmpty(trackerConfiguration.getProxyUsername()) + && !isEmpty(trackerConfiguration.getProxyPassword())) { builder.authenticator(new ProxyAuthenticator( trackerConfiguration.getProxyUsername(), trackerConfiguration.getProxyPassword() @@ -54,7 +61,13 @@ public Sender provideSender( throw new MatomoException("Please disable SSL hostname verification manually using the system parameter -Djdk.internal.httpclient.disableHostnameVerification=true"); } - return new Java11Sender(trackerConfiguration, queryCreator, builder.build(), cookieManager.getCookieStore()); + return new Java11Sender( + trackerConfiguration, + queryCreator, + builder.build(), + cookieManager.getCookieStore(), + executorService + ); } private static boolean isEmpty( diff --git a/java11/src/test/java/org/matomo/java/tracking/Java11SenderIT.java b/java11/src/test/java/org/matomo/java/tracking/Java11SenderIT.java index cdfef1df..6ce1a63b 100644 --- a/java11/src/test/java/org/matomo/java/tracking/Java11SenderIT.java +++ b/java11/src/test/java/org/matomo/java/tracking/Java11SenderIT.java @@ -21,6 +21,7 @@ import java.time.Duration; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -45,7 +46,8 @@ void failsIfTrackerConfigurationIsNotSet() { .apiEndpoint(URI.create("http://localhost")) .build()), HttpClient.newBuilder().cookieHandler(cookieManager).build(), - cookieManager.getCookieStore() + cookieManager.getCookieStore(), + Executors.newFixedThreadPool(2, new DaemonThreadFactory()) )).isInstanceOf(NullPointerException.class) .hasMessage("trackerConfiguration is marked non-null but is null"); } @@ -57,7 +59,8 @@ void failsIfQueryCreatorIsNotSet() { TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build(), null, HttpClient.newBuilder().cookieHandler(cookieManager).build(), - cookieManager.getCookieStore() + cookieManager.getCookieStore(), + Executors.newFixedThreadPool(2, new DaemonThreadFactory()) )).isInstanceOf(NullPointerException.class) .hasMessage("queryCreator is marked non-null but is null"); } @@ -71,7 +74,8 @@ void failsIfHttpClientIsNotSet() { .apiEndpoint(URI.create("http://localhost")) .build()), null, - cookieManager.getCookieStore() + cookieManager.getCookieStore(), + Executors.newFixedThreadPool(2, new DaemonThreadFactory()) )).isInstanceOf(NullPointerException.class) .hasMessage("httpClient is marked non-null but is null"); } @@ -85,7 +89,8 @@ void failsIfCookieStoreIsNotSet() { .apiEndpoint(URI.create("http://localhost")) .build()), HttpClient.newBuilder().cookieHandler(cookieManager).build(), - null + null, + Executors.newFixedThreadPool(2, new DaemonThreadFactory()) )).isInstanceOf(NullPointerException.class) .hasMessage("cookieStore is marked non-null but is null"); } diff --git a/java8/pom.xml b/java8/pom.xml index 9c47baae..d7bf1a49 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.2-SNAPSHOT + 3.3.0-SNAPSHOT ../pom.xml matomo-java-tracker - 3.2.2-SNAPSHOT + 3.3.0-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java b/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java index 9b386f89..849ccda1 100644 --- a/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java +++ b/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java @@ -30,7 +30,7 @@ import java.util.Map; import java.util.TreeMap; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; @@ -59,7 +59,7 @@ class Java8Sender implements Sender { private final QueryCreator queryCreator; - private final Executor executor; + private final ExecutorService executorService; private final Collection queries = new ArrayList<>(16); @Override @@ -70,7 +70,7 @@ public CompletableFuture sendSingleAsync( return CompletableFuture.supplyAsync(() -> { sendSingle(request); return request; - }, executor); + }, executorService); } @Override @@ -340,7 +340,7 @@ public CompletableFuture sendBulkAsync( } } return CompletableFuture.supplyAsync(() -> - sendBulkAsync(authToken, headers, headerUserAgent, sessionId, cookies), executor); + sendBulkAsync(authToken, headers, headerUserAgent, sessionId, cookies), executorService); } @Nullable @@ -385,4 +385,8 @@ private Map findCookies(Iterable reques return null; } + @Override + public void close() { + ExecutorServiceCloser.close(executorService); + } } diff --git a/java8/src/test/java/org/matomo/java/tracking/Java8SenderIT.java b/java8/src/test/java/org/matomo/java/tracking/Java8SenderIT.java index 4b92535f..dcaeafa4 100644 --- a/java8/src/test/java/org/matomo/java/tracking/Java8SenderIT.java +++ b/java8/src/test/java/org/matomo/java/tracking/Java8SenderIT.java @@ -16,6 +16,7 @@ import java.net.MalformedURLException; import java.net.URI; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -242,7 +243,7 @@ private void givenSender() { sender = new Java8Sender( trackerConfiguration, new QueryCreator(trackerConfiguration), - Runnable::run + Executors.newFixedThreadPool(2, new DaemonThreadFactory()) ); } diff --git a/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java index fe8eb7c1..04500061 100644 --- a/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java +++ b/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java @@ -23,6 +23,7 @@ import java.util.Locale.LanguageRange; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -69,6 +70,13 @@ void givenStub() { wireMockServer.stubFor(get(urlPathEqualTo("/matomo.php")).willReturn(status(204))); } + @AfterEach + void closeTracker() throws Exception { + if (matomoTracker != null) { + matomoTracker.close(); + } + } + @Test void requiresSiteId() { diff --git a/pom.xml b/pom.xml index 247ff62f..724fbfea 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.2-SNAPSHOT + 3.3.0-SNAPSHOT pom Matomo Java Tracker Parent diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 59d85e5b..0f96202e 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.2-SNAPSHOT + 3.3.0-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-jakarta - 3.2.2-SNAPSHOT + 3.3.0-SNAPSHOT jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/MatomoTrackerFilter.java b/servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/MatomoTrackerFilter.java index 664ce209..11ca4494 100644 --- a/servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/MatomoTrackerFilter.java +++ b/servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/MatomoTrackerFilter.java @@ -30,4 +30,15 @@ protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterC tracker.sendRequestAsync(matomoRequest); super.doFilter(req, res, chain); } + + @Override + public void destroy() { + if (tracker != null) { + try { + tracker.close(); + } catch (Exception e) { + throw new RuntimeException("Could not close tracker", e); + } + } + } } diff --git a/servlet-jakarta/src/test/java/org/matomo/java/tracking/TestSender.java b/servlet-jakarta/src/test/java/org/matomo/java/tracking/TestSender.java index d6c03833..d610ff08 100644 --- a/servlet-jakarta/src/test/java/org/matomo/java/tracking/TestSender.java +++ b/servlet-jakarta/src/test/java/org/matomo/java/tracking/TestSender.java @@ -51,4 +51,8 @@ public CompletableFuture sendBulkAsync( throw new UnsupportedOperationException(); } + @Override + public void close() { + // Do nothing + } } diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 4fd8bf9d..ee46c5c4 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.2-SNAPSHOT + 3.3.0-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-javax - 3.2.2-SNAPSHOT + 3.3.0-SNAPSHOT jar Matomo Java Tracker Servlet Javax diff --git a/servlet-javax/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java b/servlet-javax/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java index 62979d9b..676524c2 100644 --- a/servlet-javax/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java +++ b/servlet-javax/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java @@ -58,6 +58,8 @@ void sendsAnAsyncRequestOnFilter() throws Exception { ); }); + tracker.close(); + } } \ No newline at end of file diff --git a/servlet-javax/src/test/java/org/matomo/java/tracking/TestSender.java b/servlet-javax/src/test/java/org/matomo/java/tracking/TestSender.java index d6c03833..4d155494 100644 --- a/servlet-javax/src/test/java/org/matomo/java/tracking/TestSender.java +++ b/servlet-javax/src/test/java/org/matomo/java/tracking/TestSender.java @@ -51,4 +51,8 @@ public CompletableFuture sendBulkAsync( throw new UnsupportedOperationException(); } + @Override + public void close() { + // do nothing + } } diff --git a/spring/pom.xml b/spring/pom.xml index b1a3c1e6..eebbb0f3 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.2-SNAPSHOT + 3.3.0-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 24aeb628..a8fc3f93 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.2-SNAPSHOT + 3.3.0-SNAPSHOT ../pom.xml diff --git a/test/src/main/java/org/matomo/java/tracking/test/BulkExample.java b/test/src/main/java/org/matomo/java/tracking/test/BulkExample.java index d674acf2..54f35f92 100644 --- a/test/src/main/java/org/matomo/java/tracking/test/BulkExample.java +++ b/test/src/main/java/org/matomo/java/tracking/test/BulkExample.java @@ -26,18 +26,20 @@ public static void main(String[] args) { .logFailedTracking(true) .build(); - MatomoTracker tracker = new MatomoTracker(configuration); - - VisitorId visitorId = VisitorId.fromString("customer@mail.com"); - tracker.sendBulkRequestAsync( - MatomoRequests.siteSearch("Running shoes", "Running", 120L) - .visitorId(visitorId).build(), - MatomoRequests.pageView("VelocityStride ProX Running Shoes") - .visitorId(visitorId).build(), - MatomoRequests.ecommerceOrder("QXZ-789LMP", 100.0, 124.0, 19.0, 10.0, 5.0) - .visitorId(visitorId) - .build() - ); + try (MatomoTracker tracker = new MatomoTracker(configuration)) { + VisitorId visitorId = VisitorId.fromString("customer@mail.com"); + tracker.sendBulkRequestAsync( + MatomoRequests.siteSearch("Running shoes", "Running", 120L) + .visitorId(visitorId).build(), + MatomoRequests.pageView("VelocityStride ProX Running Shoes") + .visitorId(visitorId).build(), + MatomoRequests.ecommerceOrder("QXZ-789LMP", 100.0, 124.0, 19.0, 10.0, 5.0) + .visitorId(visitorId) + .build() + ); + } catch (Exception e) { + throw new RuntimeException("Could not close tracker", e); + } } diff --git a/test/src/main/java/org/matomo/java/tracking/test/ConsumerExample.java b/test/src/main/java/org/matomo/java/tracking/test/ConsumerExample.java index 3c49b87a..6c37626a 100644 --- a/test/src/main/java/org/matomo/java/tracking/test/ConsumerExample.java +++ b/test/src/main/java/org/matomo/java/tracking/test/ConsumerExample.java @@ -27,19 +27,21 @@ public static void main(String[] args) { .logFailedTracking(true) .build(); - MatomoTracker tracker = new MatomoTracker(configuration); - - MatomoRequest request = MatomoRequests - .event("Training", "Workout completed", "Bench press", 60.0) - .visitorId(VisitorId.fromString("customer@mail.com")) - .build(); - - tracker.sendRequestAsync(request) - .thenAccept(req -> System.out.printf("Sent request %s%n", req)) - .exceptionally(throwable -> { - System.err.printf("Failed to send request: %s%n", throwable.getMessage()); - return null; - }); + try (MatomoTracker tracker = new MatomoTracker(configuration)) { + MatomoRequest request = MatomoRequests + .event("Training", "Workout completed", "Bench press", 60.0) + .visitorId(VisitorId.fromString("customer@mail.com")) + .build(); + + tracker.sendRequestAsync(request) + .thenAccept(req -> System.out.printf("Sent request %s%n", req)) + .exceptionally(throwable -> { + System.err.printf("Failed to send request: %s%n", throwable.getMessage()); + return null; + }); + } catch (Exception e) { + throw new RuntimeException("Could not close tracker", e); + } } diff --git a/test/src/main/java/org/matomo/java/tracking/test/EcommerceExample.java b/test/src/main/java/org/matomo/java/tracking/test/EcommerceExample.java index 584aa32d..3f16258a 100644 --- a/test/src/main/java/org/matomo/java/tracking/test/EcommerceExample.java +++ b/test/src/main/java/org/matomo/java/tracking/test/EcommerceExample.java @@ -28,32 +28,34 @@ public static void main(String[] args) { .logFailedTracking(true) .build(); - MatomoTracker tracker = new MatomoTracker(configuration); - - tracker.sendBulkRequestAsync(MatomoRequests - .ecommerceCartUpdate(50.0) - .ecommerceItems(EcommerceItems - .builder() - .item(EcommerceItem - .builder() - .sku("XYZ12345") - .name("Matomo - The big book about web analytics") - .category("Education & Teaching") - .price(23.1) - .quantity(2) - .build()) - .item(EcommerceItem - .builder() - .sku("B0C2WV3MRJ") - .name("Matomo for data visualization") - .category("Education & Teaching") - .price(15.0) - .quantity(1) - .build()) - .build()) - .visitorId(VisitorId.fromString("customer@mail.com")) - .build() - ); + try (MatomoTracker tracker = new MatomoTracker(configuration)) { + tracker.sendBulkRequestAsync(MatomoRequests + .ecommerceCartUpdate(50.0) + .ecommerceItems(EcommerceItems + .builder() + .item(EcommerceItem + .builder() + .sku("XYZ12345") + .name("Matomo - The big book about web analytics") + .category("Education & Teaching") + .price(23.1) + .quantity(2) + .build()) + .item(EcommerceItem + .builder() + .sku("B0C2WV3MRJ") + .name("Matomo for data visualization") + .category("Education & Teaching") + .price(15.0) + .quantity(1) + .build()) + .build()) + .visitorId(VisitorId.fromString("customer@mail.com")) + .build() + ); + } catch (Exception e) { + throw new RuntimeException("Could not close tracker", e); + } } diff --git a/test/src/main/java/org/matomo/java/tracking/test/MatomoTrackerTester.java b/test/src/main/java/org/matomo/java/tracking/test/MatomoTrackerTester.java index 8255d0ed..8532c370 100644 --- a/test/src/main/java/org/matomo/java/tracking/test/MatomoTrackerTester.java +++ b/test/src/main/java/org/matomo/java/tracking/test/MatomoTrackerTester.java @@ -25,7 +25,7 @@ import org.matomo.java.tracking.parameters.VisitorId; @Slf4j -class MatomoTrackerTester { +class MatomoTrackerTester implements AutoCloseable { private final MatomoTracker tracker; @@ -39,7 +39,7 @@ class MatomoTrackerTester { } } - public static void main(String[] args) { + public static void main(String[] args) throws Exception { TrackerConfiguration configuration = TrackerConfiguration .builder() @@ -49,12 +49,12 @@ public static void main(String[] args) { .logFailedTracking(true) .build(); - MatomoTrackerTester matomoTrackerTester = new MatomoTrackerTester(configuration); - - matomoTrackerTester.sendRequestAsync(); - matomoTrackerTester.sendBulkRequestsAsync(); - matomoTrackerTester.sendRequest(); - matomoTrackerTester.sendBulkRequests(); + try (MatomoTrackerTester matomoTrackerTester = new MatomoTrackerTester(configuration)) { + matomoTrackerTester.sendRequestAsync(); + matomoTrackerTester.sendBulkRequestsAsync(); + matomoTrackerTester.sendRequest(); + matomoTrackerTester.sendBulkRequests(); + } } @@ -187,4 +187,9 @@ private MatomoRequest randomRequest() { .debug(true) .build(); } + + @Override + public void close() throws Exception { + tracker.close(); + } } diff --git a/test/src/main/java/org/matomo/java/tracking/test/SendExample.java b/test/src/main/java/org/matomo/java/tracking/test/SendExample.java index 1fe55b3e..d43bcfc3 100644 --- a/test/src/main/java/org/matomo/java/tracking/test/SendExample.java +++ b/test/src/main/java/org/matomo/java/tracking/test/SendExample.java @@ -26,13 +26,15 @@ public static void main(String[] args) { .logFailedTracking(true) .build(); - MatomoTracker tracker = new MatomoTracker(configuration); - - tracker.sendRequestAsync(MatomoRequests - .event("Training", "Workout completed", "Bench press", 60.0) - .visitorId(VisitorId.fromString("customer@mail.com")) - .build() - ); + try (MatomoTracker tracker = new MatomoTracker(configuration)) { + tracker.sendRequestAsync(MatomoRequests + .event("Training", "Workout completed", "Bench press", 60.0) + .visitorId(VisitorId.fromString("customer@mail.com")) + .build() + ); + } catch (Exception e) { + throw new RuntimeException("Could not close tracker", e); + } } } From 36324181fdeee88f503b09c5460920ae16d459e8 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Sat, 9 Mar 2024 12:36:20 +0000 Subject: [PATCH 307/467] [ci skip] prepare release matomo-java-tracker-parent-3.3.0 --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index e7453fc2..46059efe 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.0-SNAPSHOT + 3.3.0 ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index 28779b26..ee15ed98 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.0-SNAPSHOT + 3.3.0 ../pom.xml matomo-java-tracker-java11 - 3.3.0-SNAPSHOT + 3.3.0 jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index d7bf1a49..764363a9 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.0-SNAPSHOT + 3.3.0 ../pom.xml matomo-java-tracker - 3.3.0-SNAPSHOT + 3.3.0 jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index 724fbfea..4b7daa10 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.0-SNAPSHOT + 3.3.0 pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - HEAD + matomo-java-tracker-parent-3.3.0 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 0f96202e..1d56c33d 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.0-SNAPSHOT + 3.3.0 ../pom.xml matomo-java-tracker-servlet-jakarta - 3.3.0-SNAPSHOT + 3.3.0 jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index ee46c5c4..9b3e33ec 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.0-SNAPSHOT + 3.3.0 ../pom.xml matomo-java-tracker-servlet-javax - 3.3.0-SNAPSHOT + 3.3.0 jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index eebbb0f3..cb2ffbb6 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.0-SNAPSHOT + 3.3.0 ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index a8fc3f93..f99f3e6c 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.0-SNAPSHOT + 3.3.0 ../pom.xml From a10f2c421b0f855884e6a466b6556824c99b32b9 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Sat, 9 Mar 2024 12:36:21 +0000 Subject: [PATCH 308/467] [ci skip] prepare for next development iteration --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 46059efe..092b2f28 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.0 + 3.3.1-SNAPSHOT ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index ee15ed98..aceb475e 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.0 + 3.3.1-SNAPSHOT ../pom.xml matomo-java-tracker-java11 - 3.3.0 + 3.3.1-SNAPSHOT jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index 764363a9..ae6d8733 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.0 + 3.3.1-SNAPSHOT ../pom.xml matomo-java-tracker - 3.3.0 + 3.3.1-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index 4b7daa10..db1ede34 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.0 + 3.3.1-SNAPSHOT pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - matomo-java-tracker-parent-3.3.0 + HEAD diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 1d56c33d..59e0a18e 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.0 + 3.3.1-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-jakarta - 3.3.0 + 3.3.1-SNAPSHOT jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 9b3e33ec..9f0c5451 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.0 + 3.3.1-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-javax - 3.3.0 + 3.3.1-SNAPSHOT jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index cb2ffbb6..1f73d522 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.0 + 3.3.1-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index f99f3e6c..ef0874bf 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.0 + 3.3.1-SNAPSHOT ../pom.xml From 5d4e7201344af03c3796e6ac95e09670263e7bf9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 22:17:36 +0100 Subject: [PATCH 309/467] build(deps): bump org.apache.maven.plugins:maven-gpg-plugin from 3.1.0 to 3.2.0 (#217) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index db1ede34..219c9a25 100644 --- a/pom.xml +++ b/pom.xml @@ -174,7 +174,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.1.0 + 3.2.0 sign-artifacts From 87ecbe83a0bc785d277cc6311cc03733b19a4a92 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 22:17:46 +0100 Subject: [PATCH 310/467] build(deps-dev): bump org.eclipse.jetty.ee10:jetty-ee10-servlet from 12.0.6 to 12.0.7 (#216) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- servlet-jakarta/pom.xml | 2 +- test/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 219c9a25..50e14916 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.6 + 12.0.7 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 59e0a18e..0b36cfcd 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.6 + 12.0.7 test diff --git a/test/pom.xml b/test/pom.xml index ef0874bf..687ce85c 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -46,7 +46,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.6 + 12.0.7 From 1039bd3d465f2c76a2fd000c9733b35af5ff08ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 22:09:30 +0100 Subject: [PATCH 311/467] build(deps): bump org.apache.maven.plugins:maven-gpg-plugin from 3.2.0 to 3.2.1 (#220) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 50e14916..a67a4a8b 100644 --- a/pom.xml +++ b/pom.xml @@ -174,7 +174,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.0 + 3.2.1 sign-artifacts From 143520c0912b629763530b13dc825a95f175bb1c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 22:09:39 +0100 Subject: [PATCH 312/467] build(deps): bump org.apache.maven.plugins:maven-compiler-plugin from 3.12.1 to 3.13.0 (#219) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a67a4a8b..ae87df44 100644 --- a/pom.xml +++ b/pom.xml @@ -117,7 +117,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.12.1 + 3.13.0 org.apache.maven.plugins From 75a499331ea1b076e413cac2e1748b07967c72c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 22:09:48 +0100 Subject: [PATCH 313/467] build(deps): bump org.owasp:dependency-check-maven from 9.0.9 to 9.0.10 (#218) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ae87df44..194e346d 100644 --- a/pom.xml +++ b/pom.xml @@ -359,7 +359,7 @@ org.owasp dependency-check-maven - 9.0.9 + 9.0.10 true From 964278f6edc115468044f2173b7ec6810bc3d98b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Mar 2024 23:42:38 +0100 Subject: [PATCH 314/467] build(deps): bump org.projectlombok:lombok from 1.18.30 to 1.18.32 (#222) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 194e346d..2d868210 100644 --- a/pom.xml +++ b/pom.xml @@ -62,7 +62,7 @@ 1.8 1.8 ${project.build.outputDirectory}/delombok - 1.18.30 + 1.18.32 2.0.12 From 04b73e1f0c6fccab3c2721838c6ee393c636516d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Mar 2024 23:43:01 +0100 Subject: [PATCH 315/467] build(deps): bump org.springframework.boot:spring-boot-dependencies from 3.2.3 to 3.2.4 (#221) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- spring/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring/pom.xml b/spring/pom.xml index 1f73d522..e0d3da50 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -17,7 +17,7 @@ 17 17 - 3.2.3 + 3.2.4 From 5ace22cc53823ec2b704b2098733110805cceafc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 23:21:22 +0200 Subject: [PATCH 316/467] build(deps): bump org.owasp:dependency-check-maven from 9.0.10 to 9.1.0 (#225) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2d868210..279c8483 100644 --- a/pom.xml +++ b/pom.xml @@ -359,7 +359,7 @@ org.owasp dependency-check-maven - 9.0.10 + 9.1.0 true From 8c698401322a5dfbb89ef59a6a666a9d812e8088 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 23:21:38 +0200 Subject: [PATCH 317/467] build(deps): bump org.apache.maven.plugins:maven-gpg-plugin from 3.2.1 to 3.2.2 (#224) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 279c8483..957681dd 100644 --- a/pom.xml +++ b/pom.xml @@ -174,7 +174,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.1 + 3.2.2 sign-artifacts From 5583c7c3589084c256386c11cc5a0aee09103915 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 23:21:57 +0200 Subject: [PATCH 318/467] build(deps-dev): bump org.wiremock:wiremock from 3.4.2 to 3.5.2 (#223) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- java11/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java11/pom.xml b/java11/pom.xml index aceb475e..22e009be 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -48,7 +48,7 @@ org.wiremock wiremock - 3.4.2 + 3.5.2 test From 1f84e3334a613eed289f956ce4285ae39885ebc2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Apr 2024 07:06:51 +0200 Subject: [PATCH 319/467] build(deps): bump actions/configure-pages from 4 to 5 (#226) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 44316aef..dcad339d 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/configure-pages@v4 + - uses: actions/configure-pages@v5 - uses: actions/jekyll-build-pages@v1 with: source: ./ From 88ebabbfef4bbfae260a79cc61db0b780dca2230 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Apr 2024 07:07:22 +0200 Subject: [PATCH 320/467] build(deps): bump com.github.spotbugs:spotbugs-annotations from 4.8.3 to 4.8.4 (#230) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 957681dd..21a8a15d 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ com.github.spotbugs spotbugs-annotations - 4.8.3 + 4.8.4 provided From 9e74b386f1dddd518e59a88ae6fe4e92ea81cea5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Apr 2024 07:07:37 +0200 Subject: [PATCH 321/467] build(deps): bump org.apache.maven.plugins:maven-source-plugin from 3.3.0 to 3.3.1 (#229) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 21a8a15d..ad055e1d 100644 --- a/pom.xml +++ b/pom.xml @@ -215,7 +215,7 @@ org.apache.maven.plugins maven-source-plugin - 3.3.0 + 3.3.1 attach-sources From 528edb0903a313baf3e8f8c2b4970a56cc7762e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Apr 2024 07:07:55 +0200 Subject: [PATCH 322/467] build(deps): bump org.jacoco:jacoco-maven-plugin from 0.8.11 to 0.8.12 (#228) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ad055e1d..37b6a6a5 100644 --- a/pom.xml +++ b/pom.xml @@ -290,7 +290,7 @@ org.jacoco jacoco-maven-plugin - 0.8.11 + 0.8.12 prepare-agent From 1974fe00ccbc943761b993a2e2f1556b58c2889e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 07:10:26 +0200 Subject: [PATCH 323/467] build(deps): bump org.apache.maven.plugins:maven-jar-plugin from 3.3.0 to 3.4.0 (#231) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 37b6a6a5..6ce2327d 100644 --- a/pom.xml +++ b/pom.xml @@ -142,7 +142,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.3.0 + 3.4.0 org.apache.maven.plugins From c3b3146236a245c6f10864a921bf500e3b962523 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 07:10:47 +0200 Subject: [PATCH 324/467] build(deps): bump slf4j.version from 2.0.12 to 2.0.13 (#232) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6ce2327d..414655da 100644 --- a/pom.xml +++ b/pom.xml @@ -63,7 +63,7 @@ 1.8 ${project.build.outputDirectory}/delombok 1.18.32 - 2.0.12 + 2.0.13 From 20cf3ab29d4ff31f6244befc0f799f0da20f5c08 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 07:11:01 +0200 Subject: [PATCH 325/467] build(deps): bump org.apache.maven.plugins:maven-gpg-plugin from 3.2.2 to 3.2.3 (#233) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 414655da..1dc46c25 100644 --- a/pom.xml +++ b/pom.xml @@ -174,7 +174,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.2 + 3.2.3 sign-artifacts From c79d837b282d6f83827ef9c696fbd04a89093afd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 07:11:22 +0200 Subject: [PATCH 326/467] build(deps): bump com.github.spotbugs:spotbugs-maven-plugin from 4.8.3.1 to 4.8.4.0 (#234) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1dc46c25..516531e2 100644 --- a/pom.xml +++ b/pom.xml @@ -354,7 +354,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.8.3.1 + 4.8.4.0 org.owasp From 93d077dbe8c5f5b8b7dbb6ddb6d22479b9639078 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Apr 2024 10:27:23 +0200 Subject: [PATCH 327/467] build(deps-dev): bump org.eclipse.jetty.ee10:jetty-ee10-servlet from 12.0.7 to 12.0.8 (#227) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- servlet-jakarta/pom.xml | 2 +- test/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 516531e2..c7d3839e 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.7 + 12.0.8 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 0b36cfcd..de818e0a 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.7 + 12.0.8 test diff --git a/test/pom.xml b/test/pom.xml index 687ce85c..1ae6e78d 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -46,7 +46,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.7 + 12.0.8 From 6b72ca67c4dbfd23b30db1277cef6ac8b7573a8c Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Fri, 19 Apr 2024 19:57:28 +0200 Subject: [PATCH 328/467] Closes #235 Fix tracking parameter name for event value --- .../java/org/matomo/java/tracking/MatomoRequest.java | 2 +- .../org/matomo/java/tracking/QueryCreatorTest.java | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java index ea2ddbfc..d9349836 100644 --- a/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -433,7 +433,7 @@ public class MatomoRequest { /** * Some numeric value that represents the event value. */ - @TrackingParameter(name = "e_n") + @TrackingParameter(name = "e_v") private Double eventValue; /** diff --git a/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java b/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java index 1b1cecd4..8611d106 100644 --- a/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java +++ b/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java @@ -538,4 +538,16 @@ void failsIfLongitudeIsGreaterThan180() { .hasRootCauseMessage("Invalid value for long. Must be less or equal than 180"); } + @Test + void tracksEvent() { + matomoRequestBuilder.eventName("Event Name") + .eventValue(23.456) + .eventAction("Event Action") + .eventCategory("Event Category"); + + whenCreatesQuery(); + + assertThat(query).isEqualTo("idsite=42&token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&e_c=Event+Category&e_a=Event+Action&e_n=Event+Name&e_v=23.456&send_image=0&rand=random-value"); + } + } From 2d5258a1df488c86987b49e2980a261162c4bbed Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Fri, 19 Apr 2024 18:10:54 +0000 Subject: [PATCH 329/467] [ci skip] prepare release matomo-java-tracker-parent-3.3.1 --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 092b2f28..8a81f824 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.1-SNAPSHOT + 3.3.1 ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index 22e009be..c86981a8 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.1-SNAPSHOT + 3.3.1 ../pom.xml matomo-java-tracker-java11 - 3.3.1-SNAPSHOT + 3.3.1 jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index ae6d8733..f2a05a41 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.1-SNAPSHOT + 3.3.1 ../pom.xml matomo-java-tracker - 3.3.1-SNAPSHOT + 3.3.1 jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index c7d3839e..3d4e2611 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.1-SNAPSHOT + 3.3.1 pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - HEAD + matomo-java-tracker-parent-3.3.1 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index de818e0a..1093e401 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.1-SNAPSHOT + 3.3.1 ../pom.xml matomo-java-tracker-servlet-jakarta - 3.3.1-SNAPSHOT + 3.3.1 jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 9f0c5451..47b3d3dd 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.1-SNAPSHOT + 3.3.1 ../pom.xml matomo-java-tracker-servlet-javax - 3.3.1-SNAPSHOT + 3.3.1 jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index e0d3da50..71f97a9d 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.1-SNAPSHOT + 3.3.1 ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 1ae6e78d..14f31cbe 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.1-SNAPSHOT + 3.3.1 ../pom.xml From 67c45e926f59cbc0bc5a9602ad758fe142e7cb20 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Fri, 19 Apr 2024 18:10:55 +0000 Subject: [PATCH 330/467] [ci skip] prepare for next development iteration --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 8a81f824..07d3c099 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.1 + 3.3.2-SNAPSHOT ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index c86981a8..de8a04fb 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.1 + 3.3.2-SNAPSHOT ../pom.xml matomo-java-tracker-java11 - 3.3.1 + 3.3.2-SNAPSHOT jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index f2a05a41..96c05b2b 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.1 + 3.3.2-SNAPSHOT ../pom.xml matomo-java-tracker - 3.3.1 + 3.3.2-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index 3d4e2611..8a5b0cf6 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.1 + 3.3.2-SNAPSHOT pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - matomo-java-tracker-parent-3.3.1 + HEAD diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 1093e401..0679c2e9 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.1 + 3.3.2-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-jakarta - 3.3.1 + 3.3.2-SNAPSHOT jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 47b3d3dd..65511295 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.1 + 3.3.2-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-javax - 3.3.1 + 3.3.2-SNAPSHOT jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index 71f97a9d..89325afb 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.1 + 3.3.2-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 14f31cbe..72e713aa 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.1 + 3.3.2-SNAPSHOT ../pom.xml From c4f8b32ebe0c0d5653ab133e01214ccb498e342c Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Fri, 19 Apr 2024 20:17:18 +0200 Subject: [PATCH 331/467] #168 Remove synchronization on queries --- README.md | 31 ++++++++++------ core/pom.xml | 2 +- .../matomo/java/tracking/MatomoTracker.java | 25 ++++++------- .../java/org/matomo/java/tracking/Sender.java | 3 +- .../org/matomo/java/tracking/TestSender.java | 2 +- java11/pom.xml | 4 +-- .../matomo/java/tracking/Java11Sender.java | 2 +- java8/pom.xml | 4 +-- .../org/matomo/java/tracking/Java8Sender.java | 36 +++++++++---------- pom.xml | 2 +- servlet-jakarta/pom.xml | 4 +-- .../org/matomo/java/tracking/TestSender.java | 2 +- servlet-javax/pom.xml | 4 +-- .../org/matomo/java/tracking/TestSender.java | 2 +- spring/pom.xml | 2 +- test/pom.xml | 2 +- 16 files changed, 68 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index fe47114d..db941103 100644 --- a/README.md +++ b/README.md @@ -69,13 +69,22 @@ Projects that use Matomo Java Tracker: ## What Is New? +### Version 3.4.x + +We fixed a synchronization issue in the Java 8 sender (https://github.com/matomo-org/matomo-java-tracker/issues/168). +To consume the exact amount of space needed for the queries to send to Matomo, we need the collection size of the incoming +requests. So we changed `Iterable` to `Collection` in some `MatomoTracker`. This could affect users, that use parameters +of type `Iterable` in the tracker. Please use `Collection` instead. + +### Version 3.3.x + Do you still use Matomo Java Tracker 2.x? We created version 3, that is compatible with Matomo 4 and 5 and contains fewer dependencies. Release notes can be found here: https://github.com/matomo-org/matomo-java-tracker/releases Here are the most important changes: -* Matomo Java Tracker 3.3.0 is compatible with Matomo 4 and 5 +* Matomo Java Tracker 3.4.0 is compatible with Matomo 4 and 5 * less dependencies * new dimension parameter * special types allow to provide valid parameters now @@ -140,7 +149,7 @@ Add a dependency on Matomo Java Tracker using Maven. For Java 8: org.piwik.java.tracking matomo-java-tracker - 3.3.0 + 3.4.0 ``` @@ -151,7 +160,7 @@ For Java 11: org.piwik.java.tracking matomo-java-tracker-java11 - 3.3.0 + 3.4.0 ``` @@ -159,7 +168,7 @@ or Gradle (Java 8): ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker:3.3.0") + implementation("org.piwik.java.tracking:matomo-java-tracker:3.4.0") } ``` @@ -167,20 +176,20 @@ or Gradle (Java 11): ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.3.0") + implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.4.0") } ``` or Gradle with Kotlin DSL (Java 8) ```kotlin -implementation("org.piwik.java.tracking:matomo-java-tracker:3.3.0") +implementation("org.piwik.java.tracking:matomo-java-tracker:3.4.0") ``` or Gradle with Kotlin DSL (Java 11) ```kotlin -implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.3.0") +implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.4.0") ``` ### Spring Boot Module @@ -193,7 +202,7 @@ and allows you to configure the tracker via application properties. Add the foll org.piwik.java.tracking matomo-java-tracker-spring-boot-starter - 3.3.0 + 3.4.0 ``` @@ -201,14 +210,14 @@ or Gradle: ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.3.0") + implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.4.0") } ``` or Gradle with Kotlin DSL ```kotlin -implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.3.0") +implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.4.0") ``` The following properties are supported: @@ -678,7 +687,7 @@ version can be used in your local Maven repository for testing purposes, e.g. org.piwik.java.tracking matomo-java-tracker - 3.3.1-SNAPSHOT + 3.4.1-SNAPSHOT ``` diff --git a/core/pom.xml b/core/pom.xml index 07d3c099..9455a6c0 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.2-SNAPSHOT + 3.4.0-SNAPSHOT ../pom.xml diff --git a/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java index 84c48271..3c38d017 100644 --- a/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java +++ b/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -11,6 +11,7 @@ import java.net.URI; import java.time.Duration; import java.util.Arrays; +import java.util.Collection; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import java.util.function.Function; @@ -135,7 +136,7 @@ public MatomoTracker( * *

Use this method if you want to send a single request. If you want to send multiple requests at once, use * {@link #sendBulkRequest(Iterable)} instead. If you want to send multiple requests asynchronously, use - * {@link #sendRequestAsync(MatomoRequest)} or {@link #sendBulkRequestAsync(Iterable)} instead. + * {@link #sendRequestAsync(MatomoRequest)} or {@link #sendBulkRequestAsync(Collection)} instead. * * @param request request to send. must not be null */ @@ -160,7 +161,7 @@ private void initializeSender() { * Send a request asynchronously via HTTP GET. * *

Use this method if you want to send a single request. If you want to send multiple requests at once, use - * {@link #sendBulkRequestAsync(Iterable)} instead. If you want to send multiple requests synchronously, use + * {@link #sendBulkRequestAsync(Collection)} instead. If you want to send multiple requests synchronously, use * {@link #sendRequest(MatomoRequest)} or {@link #sendBulkRequest(Iterable)} instead. * * @param request request to send @@ -176,8 +177,8 @@ public CompletableFuture sendRequestAsync( * Send a request asynchronously via HTTP GET and specify a callback that gets executed when the response arrives. * *

Use this method if you want to send a single request. If you want to send multiple requests at once, use - * {@link #sendBulkRequestAsync(Iterable, Consumer)} instead. If you want to send multiple requests synchronously, use - * {@link #sendRequest(MatomoRequest)} or {@link #sendBulkRequest(Iterable)} instead. + * {@link #sendBulkRequestAsync(Collection, Consumer)} instead. If you want to send multiple requests synchronously, + * use {@link #sendRequest(MatomoRequest)} or {@link #sendBulkRequest(Iterable)} instead. * * @param request request to send * @param callback callback that gets executed when response arrives, must not be null @@ -223,7 +224,7 @@ private void applyGoalIdAndCheckSiteId( * *

More efficient than sending several individual requests. If you want to send a single request, use * {@link #sendRequest(MatomoRequest)} instead. If you want to send multiple requests asynchronously, use - * {@link #sendBulkRequestAsync(Iterable)} instead. + * {@link #sendBulkRequestAsync(Collection)} instead. * * @param requests the requests to send */ @@ -236,7 +237,7 @@ public void sendBulkRequest(MatomoRequest... requests) { * *

More efficient than sending several individual requests. If you want to send a single request, use * {@link #sendRequest(MatomoRequest)} instead. If you want to send multiple requests asynchronously, use - * {@link #sendBulkRequestAsync(Iterable)} instead. + * {@link #sendBulkRequestAsync(Collection)} instead. * * @param requests the requests to send */ @@ -250,7 +251,7 @@ public void sendBulkRequest(@NonNull Iterable requests) * *

Specify the AuthToken if parameters that require an auth token is used. If you want to send a single request, * use {@link #sendRequest(MatomoRequest)} instead. If you want to send multiple requests asynchronously, use - * {@link #sendBulkRequestAsync(Iterable)} instead. + * {@link #sendBulkRequestAsync(Collection)} instead. * * @param requests the requests to send * @param authToken specify if any of the parameters use require AuthToken, if null the default auth token from the @@ -293,7 +294,7 @@ public CompletableFuture sendBulkRequestAsync(MatomoRequest... requests) { * @return completable future to let you know when the request is done */ public CompletableFuture sendBulkRequestAsync( - @NonNull Iterable requests + @NonNull Collection requests ) { return sendBulkRequestAsync(requests, null, null); } @@ -313,7 +314,7 @@ public CompletableFuture sendBulkRequestAsync( */ @Deprecated public CompletableFuture sendBulkRequestAsync( - @NonNull Iterable requests, + @NonNull Collection requests, @Nullable String authToken, @Nullable Consumer callback ) { @@ -342,7 +343,7 @@ public CompletableFuture sendBulkRequestAsync( * @return completable future to let you know when the request is done */ public CompletableFuture sendBulkRequestAsync( - @NonNull Iterable requests, + @NonNull Collection requests, @Nullable Consumer callback ) { return sendBulkRequestAsync(requests, null, callback); @@ -357,10 +358,10 @@ public CompletableFuture sendBulkRequestAsync( * @param authToken specify if any of the parameters use require AuthToken, null allowed * @return completable future to let you know when the request is done * @deprecated Please set the auth token in the tracker configuration or the requests directly and use - * {@link #sendBulkRequestAsync(Iterable)} instead. + * {@link #sendBulkRequestAsync(Collection)} instead. */ public CompletableFuture sendBulkRequestAsync( - @NonNull Iterable requests, @Nullable String authToken + @NonNull Collection requests, @Nullable String authToken ) { return sendBulkRequestAsync(requests, authToken, null); } diff --git a/core/src/main/java/org/matomo/java/tracking/Sender.java b/core/src/main/java/org/matomo/java/tracking/Sender.java index a256335e..7b1df985 100644 --- a/core/src/main/java/org/matomo/java/tracking/Sender.java +++ b/core/src/main/java/org/matomo/java/tracking/Sender.java @@ -2,6 +2,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.Collection; import java.util.concurrent.CompletableFuture; interface Sender extends AutoCloseable { @@ -20,6 +21,6 @@ void sendBulk( @NonNull CompletableFuture sendBulkAsync( - @NonNull Iterable requests, @Nullable String overrideAuthToken + @NonNull Collection requests, @Nullable String overrideAuthToken ); } diff --git a/core/src/test/java/org/matomo/java/tracking/TestSender.java b/core/src/test/java/org/matomo/java/tracking/TestSender.java index 0342cf98..f000bc8d 100644 --- a/core/src/test/java/org/matomo/java/tracking/TestSender.java +++ b/core/src/test/java/org/matomo/java/tracking/TestSender.java @@ -53,7 +53,7 @@ public void sendBulk( @NonNull @Override public CompletableFuture sendBulkAsync( - @NonNull Iterable requests, @Nullable String overrideAuthToken + @NonNull Collection requests, @Nullable String overrideAuthToken ) { for (MatomoRequest request : requests) { createQueryAndAddRequest(request, overrideAuthToken); diff --git a/java11/pom.xml b/java11/pom.xml index de8a04fb..def72bd4 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.2-SNAPSHOT + 3.4.0-SNAPSHOT ../pom.xml matomo-java-tracker-java11 - 3.3.2-SNAPSHOT + 3.4.0-SNAPSHOT jar Matomo Java Tracker Java 11 diff --git a/java11/src/main/java/org/matomo/java/tracking/Java11Sender.java b/java11/src/main/java/org/matomo/java/tracking/Java11Sender.java index 2cff400d..abb6181f 100644 --- a/java11/src/main/java/org/matomo/java/tracking/Java11Sender.java +++ b/java11/src/main/java/org/matomo/java/tracking/Java11Sender.java @@ -115,7 +115,7 @@ private HttpRequest buildHttpPostRequest( @NonNull @Override public CompletableFuture sendBulkAsync( - @NonNull @lombok.NonNull Iterable requests, + @NonNull @lombok.NonNull Collection requests, @Nullable String overrideAuthToken ) { return sendAsyncAndCheckResponse(buildHttpPostRequest(requests, overrideAuthToken), null); diff --git a/java8/pom.xml b/java8/pom.xml index 96c05b2b..5c61634c 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.2-SNAPSHOT + 3.4.0-SNAPSHOT ../pom.xml matomo-java-tracker - 3.3.2-SNAPSHOT + 3.4.0-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java b/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java index 849ccda1..9aca209c 100644 --- a/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java +++ b/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java @@ -27,6 +27,7 @@ import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.CompletableFuture; @@ -60,7 +61,6 @@ class Java8Sender implements Sender { private final QueryCreator queryCreator; private final ExecutorService executorService; - private final Collection queries = new ArrayList<>(16); @Override @NonNull @@ -322,39 +322,37 @@ private static void preparePostConnection(HttpURLConnection connection) { @Override @NonNull public CompletableFuture sendBulkAsync( - @NonNull Iterable requests, @Nullable String overrideAuthToken + @NonNull Collection requests, @Nullable String overrideAuthToken ) { String authToken = AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); Map headers = new LinkedHashMap<>(); String headerUserAgent = findHeaderUserAgent(requests); String sessionId = findSessionId(requests); Map cookies = findCookies(requests); - synchronized (queries) { - for (MatomoRequest request : requests) { - RequestValidator.validate(request, authToken); - if (request.getHeaders() != null && !request.getHeaders().isEmpty()) { - headers.putAll(request.getHeaders()); - } - String query = queryCreator.createQuery(request, null); - queries.add(query); + List queries = new ArrayList<>(requests.size()); + for (MatomoRequest request : requests) { + RequestValidator.validate(request, authToken); + if (request.getHeaders() != null && !request.getHeaders().isEmpty()) { + headers.putAll(request.getHeaders()); } + queries.add(queryCreator.createQuery(request, null)); } return CompletableFuture.supplyAsync(() -> - sendBulkAsync(authToken, headers, headerUserAgent, sessionId, cookies), executorService); + sendBulkAsync(queries, authToken, headers, headerUserAgent, sessionId, cookies), + executorService); } @Nullable private Void sendBulkAsync( - @Nullable String authToken, Map headers, String headerUserAgent, String sessionId, + List queries, + @Nullable String authToken, + Map headers, + String headerUserAgent, + String sessionId, Map cookies ) { - synchronized (queries) { - if (!queries.isEmpty()) { - sendBulk(queries, authToken, headers, headerUserAgent, sessionId, cookies); - queries.clear(); - } - return null; - } + sendBulk(queries, authToken, headers, headerUserAgent, sessionId, cookies); + return null; } @Nullable diff --git a/pom.xml b/pom.xml index 8a5b0cf6..f868b1b8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.2-SNAPSHOT + 3.4.0-SNAPSHOT pom Matomo Java Tracker Parent diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 0679c2e9..0964d5a4 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.2-SNAPSHOT + 3.4.0-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-jakarta - 3.3.2-SNAPSHOT + 3.4.0-SNAPSHOT jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-jakarta/src/test/java/org/matomo/java/tracking/TestSender.java b/servlet-jakarta/src/test/java/org/matomo/java/tracking/TestSender.java index d610ff08..14f8cde6 100644 --- a/servlet-jakarta/src/test/java/org/matomo/java/tracking/TestSender.java +++ b/servlet-jakarta/src/test/java/org/matomo/java/tracking/TestSender.java @@ -46,7 +46,7 @@ public void sendBulk( @NonNull @Override public CompletableFuture sendBulkAsync( - @NonNull Iterable requests, @Nullable String overrideAuthToken + @NonNull Collection requests, @Nullable String overrideAuthToken ) { throw new UnsupportedOperationException(); } diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 65511295..0152f0d6 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.2-SNAPSHOT + 3.4.0-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-javax - 3.3.2-SNAPSHOT + 3.4.0-SNAPSHOT jar Matomo Java Tracker Servlet Javax diff --git a/servlet-javax/src/test/java/org/matomo/java/tracking/TestSender.java b/servlet-javax/src/test/java/org/matomo/java/tracking/TestSender.java index 4d155494..0e1704f1 100644 --- a/servlet-javax/src/test/java/org/matomo/java/tracking/TestSender.java +++ b/servlet-javax/src/test/java/org/matomo/java/tracking/TestSender.java @@ -46,7 +46,7 @@ public void sendBulk( @NonNull @Override public CompletableFuture sendBulkAsync( - @NonNull Iterable requests, @Nullable String overrideAuthToken + @NonNull Collection requests, @Nullable String overrideAuthToken ) { throw new UnsupportedOperationException(); } diff --git a/spring/pom.xml b/spring/pom.xml index 89325afb..9df6330a 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.2-SNAPSHOT + 3.4.0-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 72e713aa..a95ba3d8 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.3.2-SNAPSHOT + 3.4.0-SNAPSHOT ../pom.xml From 2e12058400723df2d94db9fed06a3aa8315ba21f Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Fri, 19 Apr 2024 18:22:55 +0000 Subject: [PATCH 332/467] [ci skip] prepare release matomo-java-tracker-parent-3.4.0 --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 9455a6c0..0ab47efa 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.4.0-SNAPSHOT + 3.4.0 ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index def72bd4..0dbf947a 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.4.0-SNAPSHOT + 3.4.0 ../pom.xml matomo-java-tracker-java11 - 3.4.0-SNAPSHOT + 3.4.0 jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index 5c61634c..9caeb73d 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.4.0-SNAPSHOT + 3.4.0 ../pom.xml matomo-java-tracker - 3.4.0-SNAPSHOT + 3.4.0 jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index f868b1b8..be87655a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.4.0-SNAPSHOT + 3.4.0 pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - HEAD + matomo-java-tracker-parent-3.4.0 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 0964d5a4..c30435e0 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.4.0-SNAPSHOT + 3.4.0 ../pom.xml matomo-java-tracker-servlet-jakarta - 3.4.0-SNAPSHOT + 3.4.0 jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 0152f0d6..bd0ca43c 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.4.0-SNAPSHOT + 3.4.0 ../pom.xml matomo-java-tracker-servlet-javax - 3.4.0-SNAPSHOT + 3.4.0 jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index 9df6330a..7b32cf60 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.4.0-SNAPSHOT + 3.4.0 ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index a95ba3d8..4d473a99 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.4.0-SNAPSHOT + 3.4.0 ../pom.xml From 5542c0fa4185fb6393aff3ecc16eb6a90bed71c1 Mon Sep 17 00:00:00 2001 From: Matomo Java Tracker Date: Fri, 19 Apr 2024 18:22:56 +0000 Subject: [PATCH 333/467] [ci skip] prepare for next development iteration --- core/pom.xml | 2 +- java11/pom.xml | 4 ++-- java8/pom.xml | 4 ++-- pom.xml | 4 ++-- servlet-jakarta/pom.xml | 4 ++-- servlet-javax/pom.xml | 4 ++-- spring/pom.xml | 2 +- test/pom.xml | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 0ab47efa..a716cbbb 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.4.0 + 3.4.1-SNAPSHOT ../pom.xml diff --git a/java11/pom.xml b/java11/pom.xml index 0dbf947a..60a579a9 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.4.0 + 3.4.1-SNAPSHOT ../pom.xml matomo-java-tracker-java11 - 3.4.0 + 3.4.1-SNAPSHOT jar Matomo Java Tracker Java 11 diff --git a/java8/pom.xml b/java8/pom.xml index 9caeb73d..4b4621ad 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.4.0 + 3.4.1-SNAPSHOT ../pom.xml matomo-java-tracker - 3.4.0 + 3.4.1-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/pom.xml b/pom.xml index be87655a..d36bb830 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.4.0 + 3.4.1-SNAPSHOT pom Matomo Java Tracker Parent @@ -41,7 +41,7 @@ scm:git:https://@github.com/matomo-org/matomo-java-tracker.git scm:git:https://github.com/matomo-org/matomo-java-tracker.git https://github.com/matomo-org/matomo-java-tracker - matomo-java-tracker-parent-3.4.0 + HEAD diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index c30435e0..ae494787 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.4.0 + 3.4.1-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-jakarta - 3.4.0 + 3.4.1-SNAPSHOT jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index bd0ca43c..498b8534 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.4.0 + 3.4.1-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-javax - 3.4.0 + 3.4.1-SNAPSHOT jar Matomo Java Tracker Servlet Javax diff --git a/spring/pom.xml b/spring/pom.xml index 7b32cf60..23f42edd 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.4.0 + 3.4.1-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 4d473a99..b90599d7 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.4.0 + 3.4.1-SNAPSHOT ../pom.xml From d3c741e0db1d5f09f75d6cc2918bf89ec771d8e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 07:12:10 +0200 Subject: [PATCH 334/467] build(deps): bump org.apache.maven.plugins:maven-jar-plugin from 3.4.0 to 3.4.1 (#236) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d36bb830..7e3c8af3 100644 --- a/pom.xml +++ b/pom.xml @@ -142,7 +142,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.4.0 + 3.4.1 org.apache.maven.plugins From e672646c6fce22dac8c792e82e0e6240062757ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 07:12:20 +0200 Subject: [PATCH 335/467] build(deps): bump org.apache.maven.plugins:maven-gpg-plugin from 3.2.3 to 3.2.4 (#237) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7e3c8af3..5bfe6013 100644 --- a/pom.xml +++ b/pom.xml @@ -174,7 +174,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.3 + 3.2.4 sign-artifacts From 7a7c5a7db4d48a3ca7776cf33e67202f8c518ff3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 07:12:37 +0200 Subject: [PATCH 336/467] build(deps): bump org.springframework.boot:spring-boot-dependencies from 3.2.4 to 3.2.5 (#238) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- spring/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring/pom.xml b/spring/pom.xml index 23f42edd..e43588d2 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -17,7 +17,7 @@ 17 17 - 3.2.4 + 3.2.5 From e81410c3025e4aa619a944f0db8f1b3f8e18bc90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 07:12:49 +0200 Subject: [PATCH 337/467] build(deps-dev): bump org.wiremock:wiremock from 3.5.2 to 3.5.3 (#239) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- java11/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java11/pom.xml b/java11/pom.xml index 60a579a9..2f133864 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -48,7 +48,7 @@ org.wiremock wiremock - 3.5.2 + 3.5.3 test From f1a2f0c2d29d92f92f965cda47ab7fd625cb824b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 23:25:22 +0200 Subject: [PATCH 338/467] build(deps-dev): bump org.wiremock:wiremock from 3.5.3 to 3.5.4 (#240) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- java11/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java11/pom.xml b/java11/pom.xml index 2f133864..829972f8 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -48,7 +48,7 @@ org.wiremock wiremock - 3.5.3 + 3.5.4 test From 80ce74627c069f013171908dfa58826da519f7ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 09:59:31 +0200 Subject: [PATCH 339/467] build(deps): bump org.apache.maven.plugins:maven-deploy-plugin from 3.1.1 to 3.1.2 (#242) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5bfe6013..e415b4dd 100644 --- a/pom.xml +++ b/pom.xml @@ -132,7 +132,7 @@ org.apache.maven.plugins maven-deploy-plugin - 3.1.1 + 3.1.2 org.apache.maven.plugins From 0708b5b049a3afd8ad59acc50122784a9e2070cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 09:59:46 +0200 Subject: [PATCH 340/467] build(deps): bump org.apache.maven.plugins:maven-install-plugin from 3.1.1 to 3.1.2 (#241) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e415b4dd..cba12891 100644 --- a/pom.xml +++ b/pom.xml @@ -137,7 +137,7 @@ org.apache.maven.plugins maven-install-plugin - 3.1.1 + 3.1.2 org.apache.maven.plugins From 0b4c690fab7f914fa2111113251f44fdf31470a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 23:45:14 +0200 Subject: [PATCH 341/467] build(deps): bump com.github.spotbugs:spotbugs-annotations from 4.8.4 to 4.8.5 (#244) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cba12891..4cc5a809 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ com.github.spotbugs spotbugs-annotations - 4.8.4 + 4.8.5 provided From 30b601d6c84d7941f99c34bff973fb50f2b40547 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 23:47:02 +0200 Subject: [PATCH 342/467] build(deps): bump com.github.spotbugs:spotbugs-maven-plugin from 4.8.4.0 to 4.8.5.0 (#243) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4cc5a809..d5ff3a7f 100644 --- a/pom.xml +++ b/pom.xml @@ -354,7 +354,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.8.4.0 + 4.8.5.0 org.owasp From 441f4bdfd4daca5264a894b9561733735d0c8e7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 23:02:41 +0200 Subject: [PATCH 343/467] build(deps-dev): bump org.eclipse.jetty.ee10:jetty-ee10-servlet from 12.0.8 to 12.0.9 (#245) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- servlet-jakarta/pom.xml | 2 +- test/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index d5ff3a7f..91c2cd95 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.8 + 12.0.9 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index ae494787..5d30951a 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.8 + 12.0.9 test diff --git a/test/pom.xml b/test/pom.xml index b90599d7..c99e6a1d 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -46,7 +46,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.8 + 12.0.9 From bb2d2e2de4b53d424242dcea1eeaa45151c4aa81 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 May 2024 00:27:35 +0200 Subject: [PATCH 344/467] build(deps): bump org.owasp:dependency-check-maven from 9.1.0 to 9.2.0 (#247) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 91c2cd95..77461595 100644 --- a/pom.xml +++ b/pom.xml @@ -359,7 +359,7 @@ org.owasp dependency-check-maven - 9.1.0 + 9.2.0 true From 23e70abab94bd97ea137fab67785519eb7978604 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 May 2024 00:28:27 +0200 Subject: [PATCH 345/467] build(deps-dev): bump org.eclipse.jetty:jetty-servlet from 10.0.20 to 10.0.21 (#246) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- servlet-javax/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 498b8534..e6e5ba7b 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jetty jetty-servlet - 10.0.20 + 10.0.21 test From c85017060b4adaf82e5a6157e60fa767e5845b5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 May 2024 06:32:17 +0200 Subject: [PATCH 346/467] build(deps): bump org.sonatype.plugins:nexus-staging-maven-plugin from 1.6.13 to 1.7.0 (#251) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 77461595..859cfe87 100644 --- a/pom.xml +++ b/pom.xml @@ -188,7 +188,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.13 + 1.7.0 true ossrh From 49671c2992aa3e77123723f15b705696c25df6f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 May 2024 06:32:31 +0200 Subject: [PATCH 347/467] build(deps): bump org.assertj:assertj-core from 3.25.3 to 3.26.0 (#249) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 859cfe87..841487f1 100644 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ org.assertj assertj-core - 3.25.3 + 3.26.0 test From ce0cb550b31a1d171b6dda39f2f7d806b33ac48f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 May 2024 06:32:42 +0200 Subject: [PATCH 348/467] build(deps-dev): bump org.wiremock:wiremock from 3.5.4 to 3.6.0 (#248) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- java11/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java11/pom.xml b/java11/pom.xml index 829972f8..9981a3dc 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -48,7 +48,7 @@ org.wiremock wiremock - 3.5.4 + 3.6.0 test From ffc4f46a1b5918f643abe591dc27d19e7ea26a23 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 May 2024 06:33:03 +0200 Subject: [PATCH 349/467] build(deps): bump org.springframework.boot:spring-boot-dependencies from 3.2.5 to 3.3.0 (#250) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- spring/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring/pom.xml b/spring/pom.xml index e43588d2..20002f18 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -17,7 +17,7 @@ 17 17 - 3.2.5 + 3.3.0 From 39b64013a2f6fc57107714b7361e49f26819b06b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 23:16:41 +0200 Subject: [PATCH 350/467] build(deps-dev): bump org.eclipse.jetty.ee10:jetty-ee10-servlet from 12.0.9 to 12.0.10 (#254) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- servlet-jakarta/pom.xml | 2 +- test/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 841487f1..60c47977 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.9 + 12.0.10 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 5d30951a..e3b6698d 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.9 + 12.0.10 test diff --git a/test/pom.xml b/test/pom.xml index c99e6a1d..0b20fdc6 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -46,7 +46,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.9 + 12.0.10 From cc19fe5c410e75efb0cbd7ee7779aff2ba546ca5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 23:17:16 +0200 Subject: [PATCH 351/467] build(deps): bump org.apache.maven.plugins:maven-javadoc-plugin from 3.6.3 to 3.7.0 (#252) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 60c47977..bd9894ad 100644 --- a/pom.xml +++ b/pom.xml @@ -253,7 +253,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.6.3 + 3.7.0 ${delombok.output} none From 2b5256fa9830679641886027a07a0edfb5c96b6e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 23:17:27 +0200 Subject: [PATCH 352/467] build(deps): bump org.apache.maven.plugins:maven-enforcer-plugin from 3.4.1 to 3.5.0 (#253) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bd9894ad..83c03eaf 100644 --- a/pom.xml +++ b/pom.xml @@ -270,7 +270,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.4.1 + 3.5.0 enforce-maven From 3003c5183a5e29414248387ca2bfc6c6a19b545a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 23:19:11 +0200 Subject: [PATCH 353/467] build(deps): bump org.apache.maven.plugins:maven-checkstyle-plugin from 3.3.1 to 3.4.0 (#255) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 83c03eaf..3b942b84 100644 --- a/pom.xml +++ b/pom.xml @@ -334,7 +334,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.3.1 + 3.4.0 warning checkstyle.xml From 20e38fd170acc6415c3e07854a29fc8ed9bfa3e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Jun 2024 00:11:38 +0200 Subject: [PATCH 354/467] build(deps): bump jakarta.servlet:jakarta.servlet-api from 6.0.0 to 6.1.0 (#259) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- servlet-jakarta/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index e3b6698d..bcb02355 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -29,7 +29,7 @@ jakarta.servlet jakarta.servlet-api - 6.0.0 + 6.1.0 provided From aab6b1e9eb856e3620a8cb5a8f138a6ef898592f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Jun 2024 00:11:47 +0200 Subject: [PATCH 355/467] build(deps): bump org.apache.maven.plugins:maven-surefire-plugin from 3.2.5 to 3.3.0 (#258) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3b942b84..88c1ff59 100644 --- a/pom.xml +++ b/pom.xml @@ -157,7 +157,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.2.5 + 3.3.0 org.apache.maven.plugins From 300d86a5eeb68a811a76205b749b95c58814a9bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Jun 2024 00:11:57 +0200 Subject: [PATCH 356/467] build(deps): bump org.apache.maven.plugins:maven-failsafe-plugin from 3.2.5 to 3.3.0 (#257) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 88c1ff59..1db0d4df 100644 --- a/pom.xml +++ b/pom.xml @@ -202,7 +202,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.2.5 + 3.3.0 From f59f11440cc8d940d14cbc8e7e0fcea38d151456 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Jun 2024 00:12:08 +0200 Subject: [PATCH 357/467] build(deps): bump org.apache.maven.plugins:maven-release-plugin from 3.0.1 to 3.1.0 (#256) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1db0d4df..7b99f164 100644 --- a/pom.xml +++ b/pom.xml @@ -162,7 +162,7 @@ org.apache.maven.plugins maven-release-plugin - 3.0.1 + 3.1.0 true false From 33a785389f429805dfdd8b142a8d93386ee8591a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 23:32:59 +0200 Subject: [PATCH 358/467] build(deps): bump com.github.spotbugs:spotbugs-annotations from 4.8.5 to 4.8.6 (#264) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7b99f164..98a478d9 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ com.github.spotbugs spotbugs-annotations - 4.8.5 + 4.8.6 provided From d74ca2dd25e64aa802c88f6deb71865c9017ad1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 23:33:09 +0200 Subject: [PATCH 359/467] build(deps): bump org.apache.maven.plugins:maven-jar-plugin from 3.4.1 to 3.4.2 (#263) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 98a478d9..174bc31a 100644 --- a/pom.xml +++ b/pom.xml @@ -142,7 +142,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.4.1 + 3.4.2 org.apache.maven.plugins From 0097aaa4e74388d1707217d4e2f3f62412ceb4aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 23:33:17 +0200 Subject: [PATCH 360/467] build(deps): bump org.apache.maven.plugins:maven-clean-plugin from 3.3.2 to 3.4.0 (#262) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 174bc31a..e9e9311b 100644 --- a/pom.xml +++ b/pom.xml @@ -127,7 +127,7 @@ org.apache.maven.plugins maven-clean-plugin - 3.3.2 + 3.4.0 org.apache.maven.plugins From c8d0af3a643f4229a96949c6f9354aaecb4c820b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 23:33:25 +0200 Subject: [PATCH 361/467] build(deps-dev): bump org.wiremock:wiremock from 3.6.0 to 3.7.0 (#261) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- java11/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java11/pom.xml b/java11/pom.xml index 9981a3dc..c53706e7 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -48,7 +48,7 @@ org.wiremock wiremock - 3.6.0 + 3.7.0 test From b28a6ffd21090b142edc0d5918755bcdf4a019f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 11:54:32 +0200 Subject: [PATCH 362/467] build(deps): bump org.apache.maven.plugins:maven-project-info-reports-plugin from 3.5.0 to 3.6.0 (#260) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e9e9311b..6c90df9b 100644 --- a/pom.xml +++ b/pom.xml @@ -122,7 +122,7 @@ org.apache.maven.plugins maven-project-info-reports-plugin - 3.5.0 + 3.6.0 org.apache.maven.plugins From bf7cf45415898d09ebb4678eba420c81adbeef1e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 23:37:22 +0200 Subject: [PATCH 363/467] build(deps-dev): bump org.wiremock:wiremock from 3.7.0 to 3.8.0 (#266) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- java11/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java11/pom.xml b/java11/pom.xml index c53706e7..70eff122 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -48,7 +48,7 @@ org.wiremock wiremock - 3.7.0 + 3.8.0 test From 02c9fc28bbefa705daee1b64d30bbe87564a139f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 23:37:33 +0200 Subject: [PATCH 364/467] build(deps): bump com.github.spotbugs:spotbugs-maven-plugin from 4.8.5.0 to 4.8.6.2 (#269) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6c90df9b..48a6cc4f 100644 --- a/pom.xml +++ b/pom.xml @@ -354,7 +354,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.8.5.0 + 4.8.6.2 org.owasp From 4763ecb76875be04fa49250fa233097fbe5b57fe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 23:37:41 +0200 Subject: [PATCH 365/467] build(deps): bump org.junit.jupiter:junit-jupiter from 5.10.2 to 5.10.3 (#268) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 48a6cc4f..41736394 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ org.junit.jupiter junit-jupiter - 5.10.2 + 5.10.3 test From 162f7e910584cfc166e193fa23020720f4092bf2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 23:37:50 +0200 Subject: [PATCH 366/467] build(deps): bump org.springframework.boot:spring-boot-dependencies from 3.3.0 to 3.3.1 (#267) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- spring/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring/pom.xml b/spring/pom.xml index 20002f18..69b99615 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -17,7 +17,7 @@ 17 17 - 3.3.0 + 3.3.1 From cfae546fc5e131734c8afa485787029537ca78b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 23:38:00 +0200 Subject: [PATCH 367/467] build(deps-dev): bump org.eclipse.jetty:jetty-servlet from 10.0.21 to 10.0.22 (#265) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- servlet-javax/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index e6e5ba7b..52e05711 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jetty jetty-servlet - 10.0.21 + 10.0.22 test From 8a185fa348aeb07d6a90675c5750b080a565b6c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 23:44:24 +0200 Subject: [PATCH 368/467] build(deps-dev): bump org.eclipse.jetty.ee10:jetty-ee10-servlet from 12.0.10 to 12.0.11 (#274) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- servlet-jakarta/pom.xml | 2 +- test/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 41736394..ee1e7b2b 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.10 + 12.0.11 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index bcb02355..e574ddf1 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.10 + 12.0.11 test diff --git a/test/pom.xml b/test/pom.xml index 0b20fdc6..df1068db 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -46,7 +46,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.10 + 12.0.11 From 8dff063a5ce280269eae081e1f6df20c0bcee606 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 23:55:24 +0200 Subject: [PATCH 369/467] build(deps): bump org.apache.maven.plugins:maven-project-info-reports-plugin from 3.6.0 to 3.6.2 (#273) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ee1e7b2b..2aefdc40 100644 --- a/pom.xml +++ b/pom.xml @@ -122,7 +122,7 @@ org.apache.maven.plugins maven-project-info-reports-plugin - 3.6.0 + 3.6.2 org.apache.maven.plugins From 7ac5a39acdcc4ffc15f016325e98af82bb99c22f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 23:55:35 +0200 Subject: [PATCH 370/467] build(deps): bump org.apache.maven.plugins:maven-release-plugin from 3.1.0 to 3.1.1 (#272) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2aefdc40..950ecd84 100644 --- a/pom.xml +++ b/pom.xml @@ -162,7 +162,7 @@ org.apache.maven.plugins maven-release-plugin - 3.1.0 + 3.1.1 true false From aeb1b4e5300686b73d7dde21ecdf7398f176bb27 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 23:55:43 +0200 Subject: [PATCH 371/467] build(deps): bump org.projectlombok:lombok from 1.18.32 to 1.18.34 (#271) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 950ecd84..c7a72ca1 100644 --- a/pom.xml +++ b/pom.xml @@ -62,7 +62,7 @@ 1.8 1.8 ${project.build.outputDirectory}/delombok - 1.18.32 + 1.18.34 2.0.13 From af7d90b39f04abdfbcc81ef67d6088ff9236db75 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 23:55:59 +0200 Subject: [PATCH 372/467] build(deps): bump org.apache.maven.plugins:maven-surefire-plugin from 3.3.0 to 3.3.1 (#270) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c7a72ca1..ab8e401e 100644 --- a/pom.xml +++ b/pom.xml @@ -157,7 +157,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.3.0 + 3.3.1 org.apache.maven.plugins From 8d239b6d8f10d8864f358634384b5c036a9f05d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 08:17:43 +0200 Subject: [PATCH 373/467] build(deps): bump org.assertj:assertj-core from 3.26.0 to 3.26.3 (#279) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ab8e401e..d3e59107 100644 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ org.assertj assertj-core - 3.26.0 + 3.26.3 test From 93ae170d62ea918414ebcf55bb40db0f0a3efe64 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 08:21:06 +0200 Subject: [PATCH 374/467] build(deps): bump org.owasp:dependency-check-maven from 9.2.0 to 10.0.3 (#278) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d3e59107..37ab1cde 100644 --- a/pom.xml +++ b/pom.xml @@ -359,7 +359,7 @@ org.owasp dependency-check-maven - 9.2.0 + 10.0.3 true From 558b2e0fcc9008b3b4dbaeee9750008ac7cab439 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 08:21:16 +0200 Subject: [PATCH 375/467] build(deps): bump org.apache.maven.plugins:maven-javadoc-plugin from 3.7.0 to 3.8.0 (#277) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 37ab1cde..75d84ca3 100644 --- a/pom.xml +++ b/pom.xml @@ -253,7 +253,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.7.0 + 3.8.0 ${delombok.output} none From a005f04b572500e000984d1ef49ef32015ac65c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 08:21:27 +0200 Subject: [PATCH 376/467] build(deps): bump org.springframework.boot:spring-boot-dependencies from 3.3.1 to 3.3.2 (#276) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- spring/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring/pom.xml b/spring/pom.xml index 69b99615..b0626dc8 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -17,7 +17,7 @@ 17 17 - 3.3.1 + 3.3.2 From 187d745f1e9267516d4c5955d33d7d54d93d22cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 08:21:36 +0200 Subject: [PATCH 377/467] build(deps-dev): bump org.wiremock:wiremock from 3.8.0 to 3.9.0 (#275) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- java11/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java11/pom.xml b/java11/pom.xml index 70eff122..3f60cf5f 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -48,7 +48,7 @@ org.wiremock wiremock - 3.8.0 + 3.9.0 test From 771e679e58d804778d9aba04efee25af63c3a868 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 23:55:16 +0200 Subject: [PATCH 378/467] build(deps-dev): bump org.wiremock:wiremock from 3.9.0 to 3.9.1 (#281) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- java11/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java11/pom.xml b/java11/pom.xml index 3f60cf5f..d05e8443 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -48,7 +48,7 @@ org.wiremock wiremock - 3.9.0 + 3.9.1 test From 3a1bf69844395ec483152fb1084aec96191bb90b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 23:55:27 +0200 Subject: [PATCH 379/467] build(deps): bump org.apache.maven.plugins:maven-failsafe-plugin from 3.3.0 to 3.3.1 (#280) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 75d84ca3..76983919 100644 --- a/pom.xml +++ b/pom.xml @@ -202,7 +202,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.3.0 + 3.3.1 From d02047fdf5047576b60d3fcee113b63bfc49a57e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 23:38:55 +0200 Subject: [PATCH 380/467] build(deps-dev): bump org.eclipse.jetty.ee10:jetty-ee10-servlet from 12.0.11 to 12.0.12 (#282) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- servlet-jakarta/pom.xml | 2 +- test/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 76983919..967941f0 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.11 + 12.0.12 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index e574ddf1..a3dd22ea 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.11 + 12.0.12 test diff --git a/test/pom.xml b/test/pom.xml index df1068db..5430caf2 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -46,7 +46,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.11 + 12.0.12 From eabce239df7225f24013e43a085116d7f255c25a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 08:02:35 +0200 Subject: [PATCH 381/467] build(deps): bump org.apache.maven.plugins:maven-gpg-plugin from 3.2.4 to 3.2.5 (#284) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 967941f0..c65b3b42 100644 --- a/pom.xml +++ b/pom.xml @@ -174,7 +174,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.4 + 3.2.5 sign-artifacts From 15e7bab95e65c3d820f55cf71220f3f4116559f8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 08:02:51 +0200 Subject: [PATCH 382/467] build(deps): bump slf4j.version from 2.0.13 to 2.0.16 (#283) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c65b3b42..c266a36c 100644 --- a/pom.xml +++ b/pom.xml @@ -63,7 +63,7 @@ 1.8 ${project.build.outputDirectory}/delombok 1.18.34 - 2.0.13 + 2.0.16 From c3fccea2487341eb74473630ea3c439f938cc54a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 00:04:36 +0200 Subject: [PATCH 383/467] build(deps): bump org.apache.maven.plugins:maven-deploy-plugin from 3.1.2 to 3.1.3 (#289) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c266a36c..9b3b46d4 100644 --- a/pom.xml +++ b/pom.xml @@ -132,7 +132,7 @@ org.apache.maven.plugins maven-deploy-plugin - 3.1.2 + 3.1.3 org.apache.maven.plugins From 06249e9cc841924298aa8c99e603dd77498ac497 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 00:04:45 +0200 Subject: [PATCH 384/467] build(deps): bump org.apache.maven.plugins:maven-install-plugin from 3.1.2 to 3.1.3 (#288) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9b3b46d4..d6d69fa3 100644 --- a/pom.xml +++ b/pom.xml @@ -137,7 +137,7 @@ org.apache.maven.plugins maven-install-plugin - 3.1.2 + 3.1.3 org.apache.maven.plugins From 1c4c10e90ca05feb06ee7fe65ea2d9056b8db09f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 00:04:54 +0200 Subject: [PATCH 385/467] build(deps): bump org.apache.maven.plugins:maven-surefire-plugin from 3.3.1 to 3.4.0 (#287) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d6d69fa3..f92cf9fa 100644 --- a/pom.xml +++ b/pom.xml @@ -157,7 +157,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.3.1 + 3.4.0 org.apache.maven.plugins From df06d271a68309bf9479a86b305a40ccccf5acb4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 00:05:02 +0200 Subject: [PATCH 386/467] build(deps): bump org.apache.maven.plugins:maven-site-plugin from 3.12.1 to 3.20.0 (#286) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f92cf9fa..c6e4de01 100644 --- a/pom.xml +++ b/pom.xml @@ -152,7 +152,7 @@ org.apache.maven.plugins maven-site-plugin - 3.12.1 + 3.20.0 org.apache.maven.plugins From 6e2d7c221e4c74d070053b8f2826089b33276477 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 00:05:10 +0200 Subject: [PATCH 387/467] build(deps): bump org.apache.maven.plugins:maven-failsafe-plugin from 3.3.1 to 3.4.0 (#285) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c6e4de01..4d7d04fc 100644 --- a/pom.xml +++ b/pom.xml @@ -202,7 +202,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.3.1 + 3.4.0 From b060dc265bc35e09eeb5fd4afdefeb2e84c24fad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 00:13:12 +0200 Subject: [PATCH 388/467] build(deps): bump org.springframework.boot:spring-boot-dependencies from 3.3.2 to 3.3.3 (#294) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- spring/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring/pom.xml b/spring/pom.xml index b0626dc8..6b3567e3 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -17,7 +17,7 @@ 17 17 - 3.3.2 + 3.3.3 From 66e7e1c04543c2520e44f01c9417f3bf990c7a7d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 00:13:22 +0200 Subject: [PATCH 389/467] build(deps): bump org.apache.maven.plugins:maven-checkstyle-plugin from 3.4.0 to 3.5.0 (#292) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4d7d04fc..f8967970 100644 --- a/pom.xml +++ b/pom.xml @@ -334,7 +334,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.4.0 + 3.5.0 warning checkstyle.xml From be2883d30a9def19b0c9cd8f4daa971cac7bef3b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 00:13:29 +0200 Subject: [PATCH 390/467] build(deps): bump org.junit.jupiter:junit-jupiter from 5.10.3 to 5.11.0 (#291) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f8967970..adc08d17 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ org.junit.jupiter junit-jupiter - 5.10.3 + 5.11.0 test From 61f255d2f05af2152ab7045e4f26c6d381a14646 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 00:13:33 +0200 Subject: [PATCH 391/467] build(deps): bump org.apache.maven.plugins:maven-project-info-reports-plugin from 3.6.2 to 3.7.0 (#290) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index adc08d17..bf9b9d67 100644 --- a/pom.xml +++ b/pom.xml @@ -122,7 +122,7 @@ org.apache.maven.plugins maven-project-info-reports-plugin - 3.6.2 + 3.7.0 org.apache.maven.plugins From afe60198c6d03c290d7e87221f3b9d3e51dc4448 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 00:13:45 +0200 Subject: [PATCH 392/467] build(deps-dev): bump org.eclipse.jetty:jetty-servlet from 10.0.22 to 10.0.23 (#293) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- servlet-javax/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 52e05711..ab084815 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jetty jetty-servlet - 10.0.22 + 10.0.23 test From 2ae527eaf204737732aaee05f486f04c594961de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 23:36:39 +0200 Subject: [PATCH 393/467] build(deps): bump org.owasp:dependency-check-maven from 10.0.3 to 10.0.4 (#296) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bf9b9d67..0d18efdb 100644 --- a/pom.xml +++ b/pom.xml @@ -359,7 +359,7 @@ org.owasp dependency-check-maven - 10.0.3 + 10.0.4 true From 2852de78a1746c43a5e9c65257f774fa0a937fa6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 23:36:49 +0200 Subject: [PATCH 394/467] build(deps): bump org.apache.maven.plugins:maven-javadoc-plugin from 3.8.0 to 3.10.0 (#298) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0d18efdb..579d4aef 100644 --- a/pom.xml +++ b/pom.xml @@ -253,7 +253,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.8.0 + 3.10.0 ${delombok.output} none From 4639459a84df1fcef5beffb155451fd71d2bf8c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 23:36:55 +0200 Subject: [PATCH 395/467] build(deps): bump org.apache.maven.plugins:maven-surefire-plugin from 3.4.0 to 3.5.0 (#297) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 579d4aef..0945c228 100644 --- a/pom.xml +++ b/pom.xml @@ -157,7 +157,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.4.0 + 3.5.0 org.apache.maven.plugins From 065ebfd8a42708784f0e715a9cdd0cc2980e09d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 23:37:03 +0200 Subject: [PATCH 396/467] build(deps): bump org.apache.maven.plugins:maven-failsafe-plugin from 3.4.0 to 3.5.0 (#295) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0945c228..c18d3836 100644 --- a/pom.xml +++ b/pom.xml @@ -202,7 +202,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.4.0 + 3.5.0 From 95559149836bc53e6074494eba74060c804b792c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 00:41:28 +0200 Subject: [PATCH 397/467] build(deps): bump madrapps/jacoco-report from 1.6.1 to 1.7.0 (#299) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2c79ce42..886981a6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,7 +21,7 @@ jobs: distribution: 'temurin' cache: maven - run: mvn -B verify - - uses: madrapps/jacoco-report@v1.6.1 + - uses: madrapps/jacoco-report@v1.7.0 with: paths: ${{ github.workspace }}/target/site/jacoco/jacoco.xml token: ${{ secrets.GITHUB_TOKEN }} From 830fb665c2b30e81a6b4929b939d178939f1fed7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 06:24:18 +0200 Subject: [PATCH 398/467] build(deps-dev): bump org.eclipse.jetty:jetty-servlet from 10.0.23 to 10.0.24 (#302) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- servlet-javax/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index ab084815..7e76f0a5 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jetty jetty-servlet - 10.0.23 + 10.0.24 test From d169f29015dde31a4e938a77c80f926d5a62cb66 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 06:34:49 +0200 Subject: [PATCH 399/467] build(deps): bump com.github.spotbugs:spotbugs-maven-plugin from 4.8.6.2 to 4.8.6.3 (#301) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c18d3836..46de61ec 100644 --- a/pom.xml +++ b/pom.xml @@ -354,7 +354,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.8.6.2 + 4.8.6.3 org.owasp From f9584f74b1b8ebe88735869b049718ae0c814f27 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 06:34:57 +0200 Subject: [PATCH 400/467] build(deps-dev): bump org.eclipse.jetty.ee10:jetty-ee10-servlet from 12.0.12 to 12.0.13 (#300) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- servlet-jakarta/pom.xml | 2 +- test/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 46de61ec..ea52fe94 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.12 + 12.0.13 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index a3dd22ea..95ad6f16 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.12 + 12.0.13 test diff --git a/test/pom.xml b/test/pom.xml index 5430caf2..41239feb 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -46,7 +46,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.12 + 12.0.13 From eaf21e77bb0f19f2adb08b57300644528977595c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 23:44:15 +0200 Subject: [PATCH 401/467] build(deps): bump org.apache.maven.plugins:maven-gpg-plugin from 3.2.5 to 3.2.6 (#304) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ea52fe94..d77f79fb 100644 --- a/pom.xml +++ b/pom.xml @@ -174,7 +174,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.5 + 3.2.6 sign-artifacts From fe522f147d635cc0876562fa488b60461e99bbbf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 23:44:26 +0200 Subject: [PATCH 402/467] build(deps): bump org.springframework.boot:spring-boot-dependencies from 3.3.3 to 3.3.4 (#306) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- spring/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring/pom.xml b/spring/pom.xml index 6b3567e3..d90e1b1e 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -17,7 +17,7 @@ 17 17 - 3.3.3 + 3.3.4 From a1b912f4d6787fe187f505f56730a5d486b2a947 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 23:44:39 +0200 Subject: [PATCH 403/467] build(deps): bump com.github.spotbugs:spotbugs-maven-plugin from 4.8.6.3 to 4.8.6.4 (#305) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d77f79fb..59559d15 100644 --- a/pom.xml +++ b/pom.xml @@ -354,7 +354,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.8.6.3 + 4.8.6.4 org.owasp From 845ec3545530f2987ec647dd00cd2bc3a23cefab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 23:44:50 +0200 Subject: [PATCH 404/467] build(deps): bump madrapps/jacoco-report from 1.7.0 to 1.7.1 (#303) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 886981a6..75e2ff95 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,7 +21,7 @@ jobs: distribution: 'temurin' cache: maven - run: mvn -B verify - - uses: madrapps/jacoco-report@v1.7.0 + - uses: madrapps/jacoco-report@v1.7.1 with: paths: ${{ github.workspace }}/target/site/jacoco/jacoco.xml token: ${{ secrets.GITHUB_TOKEN }} From 31ab1266cad2c40afab9a0b3ee4932c23708418a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 23:32:09 +0200 Subject: [PATCH 405/467] build(deps): bump org.apache.maven.plugins:maven-gpg-plugin from 3.2.6 to 3.2.7 (#308) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 59559d15..a65589e6 100644 --- a/pom.xml +++ b/pom.xml @@ -174,7 +174,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.6 + 3.2.7 sign-artifacts From 21bfd23f1b160463579d6601c9822578dc643536 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 23:32:19 +0200 Subject: [PATCH 406/467] build(deps): bump org.junit.jupiter:junit-jupiter from 5.11.0 to 5.11.1 (#307) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a65589e6..0c4f49f2 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ org.junit.jupiter junit-jupiter - 5.11.0 + 5.11.1 test From ee2529a502ddb66d75aeaae3bbbaf0811ab9172d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 23:07:53 +0200 Subject: [PATCH 407/467] build(deps): bump org.junit.jupiter:junit-jupiter from 5.11.1 to 5.11.2 (#313) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0c4f49f2..d35b5233 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ org.junit.jupiter junit-jupiter - 5.11.1 + 5.11.2 test From abc38e8d915bd1e8dd27b6319bdd5ccf44f7c773 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 23:08:00 +0200 Subject: [PATCH 408/467] build(deps-dev): bump org.eclipse.jetty.ee10:jetty-ee10-servlet from 12.0.13 to 12.0.14 (#311) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- servlet-jakarta/pom.xml | 2 +- test/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index d35b5233..1640bcd4 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.13 + 12.0.14 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 95ad6f16..b3062ba0 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.13 + 12.0.14 test diff --git a/test/pom.xml b/test/pom.xml index 41239feb..235f918f 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -46,7 +46,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.13 + 12.0.14 From dc10652a14482e1b0377408d226367d77e08d434 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 23:08:06 +0200 Subject: [PATCH 409/467] build(deps): bump org.apache.maven.plugins:maven-javadoc-plugin from 3.10.0 to 3.10.1 (#310) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1640bcd4..273d9147 100644 --- a/pom.xml +++ b/pom.xml @@ -253,7 +253,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.10.0 + 3.10.1 ${delombok.output} none From 9d6b0e0b387311558f8ef35ea7c3168ad3c8ac0a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 23:08:14 +0200 Subject: [PATCH 410/467] build(deps): bump org.apache.maven.plugins:maven-failsafe-plugin from 3.5.0 to 3.5.1 (#309) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 273d9147..e2b8fc46 100644 --- a/pom.xml +++ b/pom.xml @@ -202,7 +202,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.5.0 + 3.5.1 From f2e2da3d843569f4e21fee1190b7e374051b0feb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 23:09:28 +0200 Subject: [PATCH 411/467] build(deps-dev): bump org.eclipse.jetty:jetty-servlet from 10.0.24 to 11.0.0 (#312) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- servlet-javax/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 7e76f0a5..26c169b5 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jetty jetty-servlet - 10.0.24 + 11.0.0 test From 91c3ffd937a3086e5b6caa9c87458984fa8d51fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 23:25:27 +0200 Subject: [PATCH 412/467] build(deps): bump org.apache.maven.plugins:maven-surefire-plugin from 3.5.0 to 3.5.1 (#314) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e2b8fc46..ae932085 100644 --- a/pom.xml +++ b/pom.xml @@ -157,7 +157,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.0 + 3.5.1 org.apache.maven.plugins From b9a9ee84245b2c4d33222f84daae2b07949f8f73 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 23:23:48 +0200 Subject: [PATCH 413/467] build(deps): bump org.apache.maven.plugins:maven-project-info-reports-plugin from 3.7.0 to 3.8.0 (#317) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ae932085..f7b3bedd 100644 --- a/pom.xml +++ b/pom.xml @@ -122,7 +122,7 @@ org.apache.maven.plugins maven-project-info-reports-plugin - 3.7.0 + 3.8.0 org.apache.maven.plugins From cf988b3dce976d55810a8e98624cf7e657a15d2b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 23:23:57 +0200 Subject: [PATCH 414/467] build(deps): bump org.owasp:dependency-check-maven from 10.0.4 to 11.0.0 (#316) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f7b3bedd..9207b725 100644 --- a/pom.xml +++ b/pom.xml @@ -359,7 +359,7 @@ org.owasp dependency-check-maven - 10.0.4 + 11.0.0 true From 7546123f01c5b2fca3472f804dd61343b69acd44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 23:24:05 +0200 Subject: [PATCH 415/467] build(deps): bump org.junit.jupiter:junit-jupiter from 5.11.2 to 5.11.3 (#315) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9207b725..22b2b8cc 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ org.junit.jupiter junit-jupiter - 5.11.2 + 5.11.3 test From 531c7406bd0e3813983185cd128fa66ae6a75f61 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 07:36:00 +0100 Subject: [PATCH 416/467] build(deps): bump org.apache.maven.plugins:maven-checkstyle-plugin from 3.5.0 to 3.6.0 (#322) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 22b2b8cc..16ac252b 100644 --- a/pom.xml +++ b/pom.xml @@ -334,7 +334,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.5.0 + 3.6.0 warning checkstyle.xml From 2f3e42a92a85d8b185fd88e6df4f635c3c9cd138 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 07:36:10 +0100 Subject: [PATCH 417/467] build(deps): bump org.apache.maven.plugins:maven-site-plugin from 3.20.0 to 3.21.0 (#321) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 16ac252b..3acbdd9f 100644 --- a/pom.xml +++ b/pom.xml @@ -152,7 +152,7 @@ org.apache.maven.plugins maven-site-plugin - 3.20.0 + 3.21.0 org.apache.maven.plugins From a47e6b2b44f23a657c6682003e36f4ec68a1e86d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 07:36:20 +0100 Subject: [PATCH 418/467] build(deps): bump org.springframework.boot:spring-boot-dependencies from 3.3.4 to 3.3.5 (#320) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- spring/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring/pom.xml b/spring/pom.xml index d90e1b1e..c8ea6e21 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -17,7 +17,7 @@ 17 17 - 3.3.4 + 3.3.5 From 96cb800ddcfe4e239b3f0f23aab4af9e7d7e0dcd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 07:36:27 +0100 Subject: [PATCH 419/467] build(deps-dev): bump org.wiremock:wiremock from 3.9.1 to 3.9.2 (#319) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- java11/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java11/pom.xml b/java11/pom.xml index d05e8443..cf9708fc 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -48,7 +48,7 @@ org.wiremock wiremock - 3.9.1 + 3.9.2 test From 2d37ea4661ca6759a02f53b9a4856dd7d10a5bf0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 07:36:35 +0100 Subject: [PATCH 420/467] build(deps): bump com.github.spotbugs:spotbugs-maven-plugin from 4.8.6.4 to 4.8.6.5 (#318) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3acbdd9f..836746e1 100644 --- a/pom.xml +++ b/pom.xml @@ -354,7 +354,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.8.6.4 + 4.8.6.5 org.owasp From 41ceb027f299d45fa62f9ad0399c63a3036efa8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 22:22:59 +0100 Subject: [PATCH 421/467] build(deps): bump org.apache.maven.plugins:maven-failsafe-plugin from 3.5.1 to 3.5.2 (#326) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 836746e1..94c1a150 100644 --- a/pom.xml +++ b/pom.xml @@ -202,7 +202,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.5.1 + 3.5.2 From 7686dc31053244aff6cae14f630dc06cc17c0095 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 22:23:05 +0100 Subject: [PATCH 422/467] build(deps): bump org.apache.maven.plugins:maven-javadoc-plugin from 3.10.1 to 3.11.1 (#325) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 94c1a150..defaf7d4 100644 --- a/pom.xml +++ b/pom.xml @@ -253,7 +253,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.10.1 + 3.11.1 ${delombok.output} none From 17922a22266f3538d6495f81d9d33d3a3a34e444 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 22:23:13 +0100 Subject: [PATCH 423/467] build(deps): bump org.owasp:dependency-check-maven from 11.0.0 to 11.1.0 (#324) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index defaf7d4..6ab40282 100644 --- a/pom.xml +++ b/pom.xml @@ -359,7 +359,7 @@ org.owasp dependency-check-maven - 11.0.0 + 11.1.0 true From 5d715e53658c3088e55de63710649008d4281e56 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 22:23:19 +0100 Subject: [PATCH 424/467] build(deps): bump org.apache.maven.plugins:maven-surefire-plugin from 3.5.1 to 3.5.2 (#323) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6ab40282..5d808e45 100644 --- a/pom.xml +++ b/pom.xml @@ -157,7 +157,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.1 + 3.5.2 org.apache.maven.plugins From 5e4b0de0f7de4f945f393913b627e376d6b5b343 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 23:31:20 +0100 Subject: [PATCH 425/467] build(deps-dev): bump org.eclipse.jetty.ee10:jetty-ee10-servlet from 12.0.14 to 12.0.15 (#327) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- servlet-jakarta/pom.xml | 2 +- test/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 5d808e45..75fa8bc2 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.14 + 12.0.15 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index b3062ba0..1e641606 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.14 + 12.0.15 test diff --git a/test/pom.xml b/test/pom.xml index 235f918f..84d56aa2 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -46,7 +46,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.14 + 12.0.15 From b13f9ae8a62137e0fbe2c86e94be24335ea28fa3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 23:08:57 +0100 Subject: [PATCH 426/467] build(deps): bump org.projectlombok:lombok from 1.18.34 to 1.18.36 (#329) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 75fa8bc2..03b4c6ac 100644 --- a/pom.xml +++ b/pom.xml @@ -62,7 +62,7 @@ 1.8 1.8 ${project.build.outputDirectory}/delombok - 1.18.34 + 1.18.36 2.0.16 From 8baba81240c829ab096faf370064d8719202753a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 23:09:11 +0100 Subject: [PATCH 427/467] build(deps): bump com.github.spotbugs:spotbugs-maven-plugin from 4.8.6.5 to 4.8.6.6 (#328) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 03b4c6ac..792cc063 100644 --- a/pom.xml +++ b/pom.xml @@ -354,7 +354,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.8.6.5 + 4.8.6.6 org.owasp From b681f17aba9ff16e752e6e4507c962e7fcf58e8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 22:44:59 +0100 Subject: [PATCH 428/467] build(deps): bump org.springframework.boot:spring-boot-dependencies from 3.3.5 to 3.4.0 (#330) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- spring/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring/pom.xml b/spring/pom.xml index c8ea6e21..733ff5b3 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -17,7 +17,7 @@ 17 17 - 3.3.5 + 3.4.0 From a9792131b15eb8b3cf6937e8a4aa5d909e7062d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 23:07:33 +0100 Subject: [PATCH 429/467] build(deps-dev): bump org.wiremock:wiremock from 3.9.2 to 3.10.0 (#331) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- java11/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java11/pom.xml b/java11/pom.xml index cf9708fc..35245095 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -48,7 +48,7 @@ org.wiremock wiremock - 3.9.2 + 3.10.0 test From 56a1c0d522cc86ac62b6ace3990e2e9ce8aaf4a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 22:21:49 +0100 Subject: [PATCH 430/467] build(deps): bump org.apache.maven.plugins:maven-javadoc-plugin from 3.11.1 to 3.11.2 (#333) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 792cc063..ef9e164f 100644 --- a/pom.xml +++ b/pom.xml @@ -253,7 +253,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.11.1 + 3.11.2 ${delombok.output} none From ee9454a4e0cd9e1935b62765dd39c61e0a067e15 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 22:22:34 +0100 Subject: [PATCH 431/467] build(deps): bump org.owasp:dependency-check-maven from 11.1.0 to 11.1.1 (#332) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ef9e164f..b06bc7ba 100644 --- a/pom.xml +++ b/pom.xml @@ -359,7 +359,7 @@ org.owasp dependency-check-maven - 11.1.0 + 11.1.1 true From 98f87436b4ab1b9092ca52b6e5cd18af21d2da07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 22:13:45 +0100 Subject: [PATCH 432/467] build(deps): bump org.junit.jupiter:junit-jupiter from 5.11.3 to 5.11.4 (#335) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b06bc7ba..9f238bca 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ org.junit.jupiter junit-jupiter - 5.11.3 + 5.11.4 test From 8779d8c0c54729db06534f4c525a175e29e77715 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 22:17:56 +0100 Subject: [PATCH 433/467] build(deps-dev): bump org.eclipse.jetty.ee10:jetty-ee10-servlet from 12.0.15 to 12.0.16 (#334) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- servlet-jakarta/pom.xml | 2 +- test/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 9f238bca..62f632b2 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.15 + 12.0.16 diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 1e641606..0c31328f 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.15 + 12.0.16 test diff --git a/test/pom.xml b/test/pom.xml index 84d56aa2..7a2eb183 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -46,7 +46,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.15 + 12.0.16 From 34e42cd6f93a1d6b333b8c7fe15976c32840aa76 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 23:24:45 +0100 Subject: [PATCH 434/467] build(deps): bump org.assertj:assertj-core from 3.26.3 to 3.27.0 (#336) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 62f632b2..fee95c2f 100644 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ org.assertj assertj-core - 3.26.3 + 3.27.0 test From b2ba92630668b64cd8ac48310feea86ad3bd8a01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 23:25:19 +0100 Subject: [PATCH 435/467] build(deps): bump org.springframework.boot:spring-boot-dependencies from 3.4.0 to 3.4.1 (#337) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- spring/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring/pom.xml b/spring/pom.xml index 733ff5b3..f94a448e 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -17,7 +17,7 @@ 17 17 - 3.4.0 + 3.4.1 From 70e3c6fe2365479b48d9c2b0e47c75ed30c1687e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 23:29:24 +0100 Subject: [PATCH 436/467] build(deps): bump org.assertj:assertj-core from 3.27.0 to 3.27.2 (#338) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fee95c2f..378c5981 100644 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ org.assertj assertj-core - 3.27.0 + 3.27.2 test From 6b2e9564d86a4c57e79e6f96e4d54bee173d8b19 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 22:56:05 +0100 Subject: [PATCH 437/467] build(deps): bump scacap/action-surefire-report from 1.8.0 to 1.9.0 (#339) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 75e2ff95..607838f4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,4 +27,4 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} min-coverage-overall: 80 min-coverage-changed-files: 80 - - uses: scacap/action-surefire-report@v1.8.0 \ No newline at end of file + - uses: scacap/action-surefire-report@v1.9.0 \ No newline at end of file From 42bf987fc404994335c156543b52d8596e7729ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 23:00:43 +0100 Subject: [PATCH 438/467] build(deps): bump org.owasp:dependency-check-maven from 11.1.1 to 12.0.0 (#340) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 378c5981..c531dd29 100644 --- a/pom.xml +++ b/pom.xml @@ -359,7 +359,7 @@ org.owasp dependency-check-maven - 11.1.1 + 12.0.0 true From 2fc21c4c6f64ab78430aa486a4264a1d749cf746 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 22:14:43 +0100 Subject: [PATCH 439/467] build(deps): bump org.assertj:assertj-core from 3.27.2 to 3.27.3 (#343) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c531dd29..3b967745 100644 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ org.assertj assertj-core - 3.27.2 + 3.27.3 test From 9052afd0efb1b7cdc538c17040adc3024b3b14ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 22:15:01 +0100 Subject: [PATCH 440/467] build(deps): bump org.owasp:dependency-check-maven from 12.0.0 to 12.0.1 (#342) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3b967745..8d0d6c85 100644 --- a/pom.xml +++ b/pom.xml @@ -359,7 +359,7 @@ org.owasp dependency-check-maven - 12.0.0 + 12.0.1 true From 4e472459623ef126e43924848501686c32e6d493 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 22:15:11 +0100 Subject: [PATCH 441/467] build(deps): bump com.github.spotbugs:spotbugs-annotations from 4.8.6 to 4.9.0 (#341) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8d0d6c85..52897e7e 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ com.github.spotbugs spotbugs-annotations - 4.8.6 + 4.9.0 provided From e5808ec0a71c1180c2aa720286fff319e2fdba52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jan 2025 22:05:05 +0100 Subject: [PATCH 442/467] build(deps): bump org.springframework.boot:spring-boot-dependencies from 3.4.1 to 3.4.2 (#344) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- spring/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring/pom.xml b/spring/pom.xml index f94a448e..c55f8c43 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -17,7 +17,7 @@ 17 17 - 3.4.1 + 3.4.2 From c276d202459a0438533e106e22397fbedee1bcb2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 22:04:21 +0100 Subject: [PATCH 443/467] build(deps-dev): bump org.wiremock:wiremock from 3.10.0 to 3.11.0 (#346) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- java11/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java11/pom.xml b/java11/pom.xml index 35245095..677f0c18 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -48,7 +48,7 @@ org.wiremock wiremock - 3.10.0 + 3.11.0 test From 8dee99e6eddec1f89d0414924c58db8efc43a4e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 22:04:37 +0100 Subject: [PATCH 444/467] build(deps): bump org.owasp:dependency-check-maven from 12.0.1 to 12.0.2 (#345) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 52897e7e..d28931b9 100644 --- a/pom.xml +++ b/pom.xml @@ -359,7 +359,7 @@ org.owasp dependency-check-maven - 12.0.1 + 12.0.2 true From 57e40d5b0c4114e4a4531da2b53e490e2eaa19db Mon Sep 17 00:00:00 2001 From: Alex Imenes <132364326+aleximenes@users.noreply.github.com> Date: Tue, 3 Mar 2026 09:36:35 +0100 Subject: [PATCH 445/467] Allow zero as value to certain tracking parameters (#362) --- .../matomo/java/tracking/MatomoRequest.java | 12 ++--- .../java/tracking/QueryCreatorTest.java | 53 ++++++++++++++++++- servlet-javax/pom.xml | 2 +- 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java index d9349836..42ef0c29 100644 --- a/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -355,7 +355,7 @@ public class MatomoRequest { /** * The grand total for the ecommerce order (required when tracking an ecommerce order). */ - @TrackingParameter(name = "revenue") + @TrackingParameter(name = "revenue", min = 0) private Double ecommerceRevenue; /** @@ -433,7 +433,7 @@ public class MatomoRequest { /** * Some numeric value that represents the event value. */ - @TrackingParameter(name = "e_v") + @TrackingParameter(name = "e_v", min = 0) private Double eventValue; /** @@ -476,25 +476,25 @@ public class MatomoRequest { /** * The subtotal of the order; excludes shipping. */ - @TrackingParameter(name = "ec_st") + @TrackingParameter(name = "ec_st", min = 0) private Double ecommerceSubtotal; /** * Tax amount of the order. */ - @TrackingParameter(name = "ec_tx") + @TrackingParameter(name = "ec_tx", min = 0) private Double ecommerceTax; /** * Shipping cost of the order. */ - @TrackingParameter(name = "ec_sh") + @TrackingParameter(name = "ec_sh", min = 0) private Double ecommerceShippingCost; /** * Discount offered. */ - @TrackingParameter(name = "ec_dt") + @TrackingParameter(name = "ec_dt", min = 0) private Double ecommerceDiscount; /** diff --git a/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java b/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java index 8611d106..a4e87e5b 100644 --- a/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java +++ b/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java @@ -550,4 +550,55 @@ void tracksEvent() { assertThat(query).isEqualTo("idsite=42&token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&e_c=Event+Category&e_a=Event+Action&e_n=Event+Name&e_v=23.456&send_image=0&rand=random-value"); } -} + @Test + void allowsZeroForEventValue() { + matomoRequestBuilder.eventName("Event Name") + .eventValue(0.0) + .eventAction("Event Action") + .eventCategory("Event Category"); + + whenCreatesQuery(); + + assertThat(query) + .isEqualTo("idsite=42&" + + "token_auth=876de1876fb2cda2816c362a61bfc712&" + + "rec=1&" + + "apiv=1&" + + "_id=112210f47de98115&" + + "e_c=Event+Category&" + + "e_a=Event+Action&" + + "e_n=Event+Name&" + + "e_v=0.0&" + + "send_image=0&" + + "rand=random-value" + ); + } + + @Test + void allowsZeroForEcommerceValues() { + matomoRequestBuilder + .ecommerceRevenue(0.0) + .ecommerceSubtotal(0.0) + .ecommerceTax(0.0) + .ecommerceShippingCost(0.0) + .ecommerceDiscount(0.0); + + whenCreatesQuery(); + + assertThat(query) + .isEqualTo("idsite=42&" + + "token_auth=876de1876fb2cda2816c362a61bfc712&" + + "rec=1&" + + "apiv=1&" + + "_id=112210f47de98115&" + + "revenue=0.0&" + + "ec_st=0.0&" + + "ec_tx=0.0&" + + "ec_sh=0.0&" + + "ec_dt=0.0&" + + "send_image=0&" + + "rand=random-value" + ); + } + +} \ No newline at end of file diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 26c169b5..7e76f0a5 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jetty jetty-servlet - 11.0.0 + 10.0.24 test From 970e2f043e49c544dc796bb0d532f55fa45f8d36 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 09:51:18 +0100 Subject: [PATCH 446/467] build(deps): bump org.apache.maven.plugins:maven-clean-plugin from 3.4.0 to 3.4.1 (#351) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d28931b9..ee204290 100644 --- a/pom.xml +++ b/pom.xml @@ -127,7 +127,7 @@ org.apache.maven.plugins maven-clean-plugin - 3.4.0 + 3.5.0 org.apache.maven.plugins From 40fe3f0e34aba0c7dae9440fb67e0d8182aebe9d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 09:51:30 +0100 Subject: [PATCH 447/467] build(deps): bump com.github.spotbugs:spotbugs-annotations from 4.9.0 to 4.9.3 (#355) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ee204290..e481ba3d 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ com.github.spotbugs spotbugs-annotations - 4.9.0 + 4.9.8 provided From fec2e5fbe9f8a880abf7fd57a61881c4a1dfaebf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 09:51:45 +0100 Subject: [PATCH 448/467] build(deps): bump com.github.spotbugs:spotbugs-maven-plugin from 4.8.6.6 to 4.9.3.0 (#356) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e481ba3d..3aac5f84 100644 --- a/pom.xml +++ b/pom.xml @@ -354,7 +354,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.8.6.6 + 4.9.8.2 org.owasp From bea60d5289fe5a1047c5c6c6f4a2644ebb1af851 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 09:51:59 +0100 Subject: [PATCH 449/467] build(deps): bump org.owasp:dependency-check-maven from 12.0.2 to 12.1.1 (#357) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3aac5f84..28e7248d 100644 --- a/pom.xml +++ b/pom.xml @@ -359,7 +359,7 @@ org.owasp dependency-check-maven - 12.0.2 + 12.2.0 true From 2143624c829f046ed32edabc3ed5d93a198c1b2f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 09:52:13 +0100 Subject: [PATCH 450/467] build(deps-dev): bump org.wiremock:wiremock from 3.11.0 to 3.13.0 (#359) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- java11/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java11/pom.xml b/java11/pom.xml index 677f0c18..fb03eedf 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -48,7 +48,7 @@ org.wiremock wiremock - 3.11.0 + 3.13.2 test From b66ef234580703a36aa7002ee92cdd35b1b18b45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 09:59:34 +0100 Subject: [PATCH 451/467] build(deps): bump org.assertj:assertj-core from 3.27.3 to 3.27.7 (#363) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 28e7248d..6d23254f 100644 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ org.assertj assertj-core - 3.27.3 + 3.27.7 test From 5700bf42ee8688f8ee26a2ca2840c0064e15e1cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 10:00:53 +0100 Subject: [PATCH 452/467] build(deps): bump madrapps/jacoco-report from 1.7.1 to 1.7.2 (#360) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 607838f4..d8f4898a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,7 +21,7 @@ jobs: distribution: 'temurin' cache: maven - run: mvn -B verify - - uses: madrapps/jacoco-report@v1.7.1 + - uses: madrapps/jacoco-report@v1.7.2 with: paths: ${{ github.workspace }}/target/site/jacoco/jacoco.xml token: ${{ secrets.GITHUB_TOKEN }} From 60cee9f757c4c2046fb8913920c7fd11e03d0fa8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 09:37:25 +0100 Subject: [PATCH 453/467] build(deps): bump actions/setup-java from 4 to 5 (#373) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d8f4898a..aaad7c01 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ jobs: contents: read steps: - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index f6ad8c52..d1cf4457 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -24,7 +24,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 69ce43c6..ea458515 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' From c0ad294136ffd45cfb850d0a98ed03e3a4bdcec5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 09:38:12 +0100 Subject: [PATCH 454/467] build(deps): bump org.apache.maven.plugins:maven-failsafe-plugin from 3.5.2 to 3.5.5 (#368) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6d23254f..cafc547d 100644 --- a/pom.xml +++ b/pom.xml @@ -202,7 +202,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.5.2 + 3.5.5 From e67ebdf681e15d7fd593300aaeb4c771663cfd7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 09:38:28 +0100 Subject: [PATCH 455/467] build(deps-dev): bump org.apache.maven.plugins:maven-deploy-plugin from 3.1.3 to 3.1.4 (#367) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cafc547d..75c7f967 100644 --- a/pom.xml +++ b/pom.xml @@ -132,7 +132,7 @@ org.apache.maven.plugins maven-deploy-plugin - 3.1.3 + 3.1.4 org.apache.maven.plugins From d25bcd97a22cb05aa924f07179bc54a84c43ee29 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 09:38:47 +0100 Subject: [PATCH 456/467] build(deps-dev): bump org.apache.maven.plugins:maven-release-plugin from 3.1.1 to 3.3.1 (#364) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 75c7f967..f1ba8c43 100644 --- a/pom.xml +++ b/pom.xml @@ -162,7 +162,7 @@ org.apache.maven.plugins maven-release-plugin - 3.1.1 + 3.3.1 true false From e42028d26211b5a5159b9d2a46f44efcbb508c01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 09:40:49 +0100 Subject: [PATCH 457/467] build(deps-dev): bump org.apache.maven.plugins:maven-compiler-plugin from 3.13.0 to 3.15.0 (#365) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f1ba8c43..b9bea5ff 100644 --- a/pom.xml +++ b/pom.xml @@ -117,7 +117,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.13.0 + 3.15.0 org.apache.maven.plugins From 160228ef4c3801b084f10902062836358f516a25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 09:41:09 +0100 Subject: [PATCH 458/467] build(deps-dev): bump org.jacoco:jacoco-maven-plugin from 0.8.12 to 0.8.14 (#366) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b9bea5ff..14a14d62 100644 --- a/pom.xml +++ b/pom.xml @@ -290,7 +290,7 @@ org.jacoco jacoco-maven-plugin - 0.8.12 + 0.8.14 prepare-agent From 0a4521f38b45ca181f83122131c597b3cb23ea37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 09:41:39 +0100 Subject: [PATCH 459/467] build(deps): bump scacap/action-surefire-report from 1.9.0 to 1.9.1 (#369) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index aaad7c01..fc160921 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,4 +27,4 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} min-coverage-overall: 80 min-coverage-changed-files: 80 - - uses: scacap/action-surefire-report@v1.9.0 \ No newline at end of file + - uses: scacap/action-surefire-report@v1.9.1 \ No newline at end of file From daeaf00605d34a11856daa3d1bd95802e00c2a35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 09:41:50 +0100 Subject: [PATCH 460/467] build(deps): bump actions/upload-pages-artifact from 3 to 4 (#370) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index dcad339d..c9d5cbf8 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -20,7 +20,7 @@ jobs: with: source: ./ destination: ./_site - - uses: actions/upload-pages-artifact@v3 + - uses: actions/upload-pages-artifact@v4 deploy: environment: name: github-pages From 5362c0a0e350fb8c6add571b28d7d2a239668074 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 09:43:33 +0100 Subject: [PATCH 461/467] build(deps): bump github/codeql-action from 3 to 4 (#371) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index d1cf4457..cc940fba 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -29,8 +29,8 @@ jobs: java-version: '17' distribution: 'temurin' cache: maven - - uses: github/codeql-action/init@v3 + - uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} - - uses: github/codeql-action/autobuild@v3 - - uses: github/codeql-action/analyze@v3 + - uses: github/codeql-action/autobuild@v4 + - uses: github/codeql-action/analyze@v4 From 3064425ab9115264a18768bc00616ec838227174 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 09:43:44 +0100 Subject: [PATCH 462/467] build(deps): bump actions/checkout from 4 to 6 (#372) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/gh-pages.yml | 2 +- .github/workflows/release.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fc160921..1711ae4b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,7 +14,7 @@ jobs: checks: write contents: read steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: actions/setup-java@v5 with: java-version: '17' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index cc940fba..c00ed329 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -23,7 +23,7 @@ jobs: language: [ 'java' ] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: actions/setup-java@v5 with: java-version: '17' diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index c9d5cbf8..ace69696 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -14,7 +14,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: actions/configure-pages@v5 - uses: actions/jekyll-build-pages@v1 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ea458515..1697b03b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,7 +5,7 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: actions/setup-java@v5 with: java-version: '17' From 2640b7464560fbebe0567aa263e08d5864aae9b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 10:17:43 +0100 Subject: [PATCH 463/467] build(deps): bump release-drafter/release-drafter from 6 to 7 (#379) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1697b03b..3ab20fb0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,7 +28,7 @@ jobs: OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - - uses: release-drafter/release-drafter@v6 + - uses: release-drafter/release-drafter@v7 with: version: ${{ steps.version.outputs.version }} publish: true From 16b153779ffe3ead1ce8d8a3b8bd80a055d4429a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 10:17:58 +0100 Subject: [PATCH 464/467] build(deps): bump org.apache.maven.plugins:maven-gpg-plugin from 3.2.7 to 3.2.8 (#378) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 14a14d62..570fdffc 100644 --- a/pom.xml +++ b/pom.xml @@ -174,7 +174,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.7 + 3.2.8 sign-artifacts From 12dbcb9fcc28134acfdcd42e08a085a68f8087e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 10:18:08 +0100 Subject: [PATCH 465/467] build(deps-dev): bump org.apache.maven.plugins:maven-surefire-plugin from 3.5.2 to 3.5.5 (#376) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 570fdffc..bb102d9b 100644 --- a/pom.xml +++ b/pom.xml @@ -157,7 +157,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.2 + 3.5.5 org.apache.maven.plugins From 433ed938fd8c5bb286f4035d3e8db9ce4fd6aeaf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 10:18:26 +0100 Subject: [PATCH 466/467] build(deps): bump slf4j.version from 2.0.16 to 2.0.17 (#377) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bb102d9b..79fd170e 100644 --- a/pom.xml +++ b/pom.xml @@ -63,7 +63,7 @@ 1.8 ${project.build.outputDirectory}/delombok 1.18.36 - 2.0.16 + 2.0.17 From 267b9cffbb0e62e79306bbd4e5af462f7a5f4608 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 10:18:40 +0100 Subject: [PATCH 467/467] build(deps-dev): bump org.apache.maven.plugins:maven-resources-plugin from 3.3.1 to 3.5.0 (#375) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 79fd170e..f4c62914 100644 --- a/pom.xml +++ b/pom.xml @@ -147,7 +147,7 @@ org.apache.maven.plugins maven-resources-plugin - 3.3.1 + 3.5.0 org.apache.maven.plugins