From 3b1b29036f42a59e1d7fd742e0a5deef2baf0c7e Mon Sep 17 00:00:00 2001 From: Neelkanth Kaushik Date: Mon, 12 May 2025 22:26:35 +0530 Subject: [PATCH 01/11] This commit will update the okhttp version to 4.12.0 in pom.xml. This update is done to fix the Github issue #503. (#505) * This commit will update the okhttp version to 4.12.0 in pom.xml. okhttp-4.12.0 uses okio-3.6.0. This update is done to fix the Github issue #503. * This commit upgrades Kotlin from 1.7.20 to 1.9.0 in pom.xml. This is done to address the CI workflow errors because okhttp-4.12.0 is pulling okio-3.6.0 which is compiled using Kotlin 1.9.0 but the our SDK's pom.xml is expecting Kotlin metadata compiled with Kotlin 1.7.20. --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9a716e1f..39ce738d 100644 --- a/pom.xml +++ b/pom.xml @@ -28,14 +28,14 @@ UTF-8 8 - 1.7.20 + 1.9.0 2.27.2 2.9.0 1.10.1 3.0.1 2.9.1 - 4.10.0 + 4.12.0 4.10.0 33.3.1-jre 1.0.0 From fca4a60a23a71d33cd9f17e59012b919007b1f75 Mon Sep 17 00:00:00 2001 From: Artem Shubovych Date: Tue, 13 May 2025 03:05:53 +1000 Subject: [PATCH 02/11] Bump retrofit to fix CVEe (#504) Co-authored-by: Michael Grosse Huelsewiesche --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 39ce738d..a324471e 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ 8 1.9.0 2.27.2 - 2.9.0 + 2.11.0 1.10.1 3.0.1 From c57a583b235c9a4dcdb5d0d298dc4b4533ce3190 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 May 2025 15:20:06 -0400 Subject: [PATCH 03/11] Bump com.google.guava:guava from 33.3.1-jre to 33.4.0-jre (#500) Bumps [com.google.guava:guava](https://github.com/google/guava) from 33.3.1-jre to 33.4.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 update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Michael Grosse Huelsewiesche --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a324471e..8d0424e9 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ 2.9.1 4.12.0 4.10.0 - 33.3.1-jre + 33.4.0-jre 1.0.0 2.7.5 0.6.0.20150202 From 82dfde29ef78fd8c1aa4175e04d1a4a6af6dda2a Mon Sep 17 00:00:00 2001 From: Neelkanth Kaushik Date: Wed, 3 Dec 2025 05:27:57 +0530 Subject: [PATCH 04/11] Patch for Github issue #524 (#526) * Patch for Github issue #524 --- .../analytics/internal/AnalyticsClient.java | 105 ++++++++++++++++-- 1 file changed, 95 insertions(+), 10 deletions(-) diff --git a/analytics/src/main/java/com/segment/analytics/internal/AnalyticsClient.java b/analytics/src/main/java/com/segment/analytics/internal/AnalyticsClient.java index f7560004..2430cd1e 100644 --- a/analytics/src/main/java/com/segment/analytics/internal/AnalyticsClient.java +++ b/analytics/src/main/java/com/segment/analytics/internal/AnalyticsClient.java @@ -25,7 +25,9 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; @@ -42,6 +44,8 @@ public class AnalyticsClient { private static final Charset ENCODING = StandardCharsets.UTF_8; private Gson gsonInstance; private static final String instanceId = UUID.randomUUID().toString(); + private static final int WAIT_FOR_THREAD_COMPLETE_S = 5; + private static final int TERMINATION_TIMEOUT_S = 1; static { Map library = new LinkedHashMap<>(); @@ -67,6 +71,7 @@ public class AnalyticsClient { private final ScheduledExecutorService flushScheduler; private final AtomicBoolean isShutDown; private final String writeKey; + private volatile Future looperFuture; public static AnalyticsClient create( HttpUrl uploadUrl, @@ -130,7 +135,9 @@ public AnalyticsClient( this.currentQueueSizeInBytes = 0; - if (!isShutDown.get()) looperExecutor.submit(new Looper()); + if (!isShutDown.get()) { + this.looperFuture = looperExecutor.submit(new Looper()); + } flushScheduler = Executors.newScheduledThreadPool(1, threadFactory); flushScheduler.scheduleAtFixedRate( @@ -218,6 +225,8 @@ public void shutdown() { // we can shutdown the flush scheduler without worrying flushScheduler.shutdownNow(); + // Wait for the looper to complete processing before shutting down executors + waitForLooperCompletion(); shutdownAndWait(looperExecutor, "looper"); shutdownAndWait(networkExecutor, "network"); @@ -226,19 +235,81 @@ public void shutdown() { } } + /** + * Wait for the looper to complete processing all messages before proceeding with shutdown. This + * prevents the race condition where the network executor is shut down before the looper finishes + * submitting all batches. + */ + private void waitForLooperCompletion() { + if (looperFuture != null) { + try { + // Wait for the looper to complete processing the STOP message and finish + // Use a reasonable timeout to avoid hanging indefinitely + looperFuture.get(WAIT_FOR_THREAD_COMPLETE_S, TimeUnit.SECONDS); + log.print(VERBOSE, "Looper completed successfully."); + } catch (Exception e) { + log.print(ERROR, e, "Error waiting for looper to complete."); + // Cancel the looper if it's taking too long or if there's an error + if (!looperFuture.isDone()) { + looperFuture.cancel(true); + log.print(VERBOSE, "Looper was cancelled due to timeout or error."); + } + } + } + } + public void shutdownAndWait(ExecutorService executor, String name) { + boolean isLooperExecutor = name != null && name.equalsIgnoreCase("looper"); try { executor.shutdown(); - final boolean executorTerminated = executor.awaitTermination(1, TimeUnit.SECONDS); - - log.print( - VERBOSE, - "%s executor %s.", - name, - executorTerminated ? "terminated normally" : "timed out"); + boolean terminated = executor.awaitTermination(TERMINATION_TIMEOUT_S, TimeUnit.SECONDS); + if (terminated) { + log.print(VERBOSE, "%s executor terminated normally.", name); + return; + } + if (isLooperExecutor) { // Handle looper - network should finish on its own + // not terminated within timeout -> force shutdown + log.print( + VERBOSE, + "%s did not terminate in %d seconds; requesting shutdownNow().", + name, + TERMINATION_TIMEOUT_S); + List dropped = executor.shutdownNow(); // interrupts running tasks + log.print( + VERBOSE, + "%s shutdownNow returned %d queued tasks that never started.", + name, + dropped.size()); + + // optional short wait to give interrupted tasks a chance to exit + boolean terminatedAfterForce = + executor.awaitTermination(TERMINATION_TIMEOUT_S, TimeUnit.SECONDS); + log.print( + VERBOSE, + "%s executor %s after shutdownNow().", + name, + terminatedAfterForce ? "terminated" : "still running (did not terminate)"); + + if (!terminatedAfterForce) { + // final warning — investigate tasks that ignore interrupts + log.print( + ERROR, + "%s executor still did not terminate; tasks may be ignoring interrupts.", + name); + } + } } catch (InterruptedException e) { + // Preserve interrupt status and attempt forceful shutdown log.print(ERROR, e, "Interrupted while stopping %s executor.", name); Thread.currentThread().interrupt(); + if (isLooperExecutor) { + List dropped = executor.shutdownNow(); + log.print( + VERBOSE, + "%s shutdownNow invoked after interrupt; %d tasks returned.", + name, + dropped.size()); + } } } @@ -299,8 +370,22 @@ public void run() { "Batching %s message(s) into batch %s.", batch.batch().size(), batch.sequence()); - networkExecutor.submit( - BatchUploadTask.create(AnalyticsClient.this, batch, maximumRetries)); + try { + networkExecutor.submit( + BatchUploadTask.create(AnalyticsClient.this, batch, maximumRetries)); + } catch (RejectedExecutionException e) { + log.print( + ERROR, + e, + "Failed to submit batch %s to network executor during shutdown. Batch will be lost.", + batch.sequence()); + // Notify callbacks about the failure + for (Message msg : batch.batch()) { + for (Callback callback : callbacks) { + callback.failure(msg, e); + } + } + } currentBatchSize.set(0); messages.clear(); From 4326150f0b973eea2a8bf14ca5cbc06e455bf61c Mon Sep 17 00:00:00 2001 From: Neelkanth Kaushik Date: Wed, 3 Dec 2025 05:36:09 +0530 Subject: [PATCH 05/11] Applying patch for LIBRARIES-2720 (#517) * Created ISO8601InstantAdapter.java file * Fixed CI errors * Spotless Fix * Changed version in pom.xml * Revert - Changed version in pom.xml because of failing test cases --------- Co-authored-by: Michael Grosse Huelsewiesche --- .../analytics/gson/ISO8601InstantAdapter.java | 37 +++++++++++++++++++ .../segment/analytics/gson/Iso8601Utils.java | 7 ++++ .../java/com/segment/analytics/Analytics.java | 3 ++ 3 files changed, 47 insertions(+) create mode 100644 analytics-core/src/main/java/com/segment/analytics/gson/ISO8601InstantAdapter.java diff --git a/analytics-core/src/main/java/com/segment/analytics/gson/ISO8601InstantAdapter.java b/analytics-core/src/main/java/com/segment/analytics/gson/ISO8601InstantAdapter.java new file mode 100644 index 00000000..c21638ad --- /dev/null +++ b/analytics-core/src/main/java/com/segment/analytics/gson/ISO8601InstantAdapter.java @@ -0,0 +1,37 @@ +package com.segment.analytics.gson; + +import com.google.gson.*; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.lang.reflect.Type; +import java.time.Instant; + +/** + * A {@link JsonSerializer} that formats {@link Instant} objects into iso8601 formatted strings, and + * {@link JsonDeserializer} that parses iso8601 formatted strings into {@link Instant} objects. + */ +public class ISO8601InstantAdapter extends TypeAdapter + implements JsonSerializer, JsonDeserializer { + @Override + public JsonElement serialize(Instant src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(Iso8601Utils.format(src)); // ISO 8601 format + } + + @Override + public Instant deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + return Instant.parse(json.getAsString()); + } + + @Override + public void write(JsonWriter out, Instant value) throws IOException { + out.value(value == null ? null : value.toString()); + } + + @Override + public Instant read(JsonReader in) throws IOException { + String str = in.nextString(); + return str == null ? null : Instant.parse(str); + } +} diff --git a/analytics-core/src/main/java/com/segment/analytics/gson/Iso8601Utils.java b/analytics-core/src/main/java/com/segment/analytics/gson/Iso8601Utils.java index 9504a641..95ca5286 100644 --- a/analytics-core/src/main/java/com/segment/analytics/gson/Iso8601Utils.java +++ b/analytics-core/src/main/java/com/segment/analytics/gson/Iso8601Utils.java @@ -16,6 +16,7 @@ package com.segment.analytics.gson; import com.google.gson.JsonParseException; +import java.time.Instant; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; @@ -277,4 +278,10 @@ private static int indexOfNonDigit(String string, int offset) { } return string.length(); } + + /** Returns {@code date} formatted as yyyy-MM-ddThh:mm:ss.sssZ */ + static String format(Instant instant) { + // Format the instant + return String.valueOf(instant.getEpochSecond()); + } } diff --git a/analytics/src/main/java/com/segment/analytics/Analytics.java b/analytics/src/main/java/com/segment/analytics/Analytics.java index 81af36c7..9b88de8c 100644 --- a/analytics/src/main/java/com/segment/analytics/Analytics.java +++ b/analytics/src/main/java/com/segment/analytics/Analytics.java @@ -4,11 +4,13 @@ import com.google.gson.GsonBuilder; import com.segment.analytics.gson.AutoValueAdapterFactory; import com.segment.analytics.gson.ISO8601DateAdapter; +import com.segment.analytics.gson.ISO8601InstantAdapter; import com.segment.analytics.http.SegmentService; import com.segment.analytics.internal.AnalyticsClient; import com.segment.analytics.internal.AnalyticsVersion; import com.segment.analytics.messages.Message; import com.segment.analytics.messages.MessageBuilder; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -362,6 +364,7 @@ public Analytics build() { gsonBuilder .registerTypeAdapterFactory(new AutoValueAdapterFactory()) + .registerTypeAdapter(Instant.class, new ISO8601InstantAdapter()) .registerTypeAdapter(Date.class, new ISO8601DateAdapter()); Gson gson = gsonBuilder.create(); From b00f95156f5013f68ad29ce8a29e6c4af2519a46 Mon Sep 17 00:00:00 2001 From: Michael Grosse Huelsewiesche Date: Tue, 2 Dec 2025 19:16:46 -0500 Subject: [PATCH 06/11] Updating changelog for 3.5.2 release (#506) * Updating changelog for 3.5.2 release * [maven-release-plugin] prepare release analytics-parent-3.5.2 * [maven-release-plugin] prepare for next development iteration --- CHANGELOG.md | 5 ++++- analytics-cli/pom.xml | 2 +- analytics-core/pom.xml | 2 +- analytics-sample/pom.xml | 2 +- analytics-spring-boot-starter/pom.xml | 2 +- analytics/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 10 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fa7e873..3bfb6679 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ -# Verion 3.5.1 (Feb 21, 2024) +# Version 3.5.2 (May 12, 2025) +- [Chore] Depenency upgrades + +# Version 3.5.1 (Feb 21, 2024) - [Chore] Dependency upgrades - [New](https://github.com/segmentio/analytics-java/pull/471) BlockingFlush, deregister parties upon message completion - [New](https://github.com/segmentio/analytics-java/pull/476) Added wk size to message size calc along with a buffer diff --git a/analytics-cli/pom.xml b/analytics-cli/pom.xml index 0f474c60..d4341f18 100644 --- a/analytics-cli/pom.xml +++ b/analytics-cli/pom.xml @@ -6,7 +6,7 @@ analytics-parent com.segment.analytics.java - 3.5.2-SNAPSHOT + 3.5.3-SNAPSHOT analytics-cli diff --git a/analytics-core/pom.xml b/analytics-core/pom.xml index b3ff7932..f3dc3d23 100644 --- a/analytics-core/pom.xml +++ b/analytics-core/pom.xml @@ -6,7 +6,7 @@ com.segment.analytics.java analytics-parent - 3.5.2-SNAPSHOT + 3.5.3-SNAPSHOT analytics-core diff --git a/analytics-sample/pom.xml b/analytics-sample/pom.xml index 273519df..8151af27 100644 --- a/analytics-sample/pom.xml +++ b/analytics-sample/pom.xml @@ -6,7 +6,7 @@ analytics-parent com.segment.analytics.java - 3.5.2-SNAPSHOT + 3.5.3-SNAPSHOT analytics-sample diff --git a/analytics-spring-boot-starter/pom.xml b/analytics-spring-boot-starter/pom.xml index 6e01b6ce..d5d70478 100644 --- a/analytics-spring-boot-starter/pom.xml +++ b/analytics-spring-boot-starter/pom.xml @@ -6,7 +6,7 @@ com.segment.analytics.java analytics-parent - 3.5.2-SNAPSHOT + 3.5.3-SNAPSHOT analytics-spring-boot-starter diff --git a/analytics/pom.xml b/analytics/pom.xml index 127fd658..0ef7606b 100644 --- a/analytics/pom.xml +++ b/analytics/pom.xml @@ -6,7 +6,7 @@ com.segment.analytics.java analytics-parent - 3.5.2-SNAPSHOT + 3.5.3-SNAPSHOT analytics diff --git a/pom.xml b/pom.xml index 8d0424e9..22316d4b 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ com.segment.analytics.java analytics-parent - 3.5.2-SNAPSHOT + 3.5.3-SNAPSHOT pom Analytics for Java (Parent) The hassle-free way to add analytics to your Android app. From abb47619e36be7b28bbc979df64ea140701c418b Mon Sep 17 00:00:00 2001 From: Michael Grosse Huelsewiesche Date: Wed, 3 Dec 2025 12:52:00 -0500 Subject: [PATCH 07/11] Updating changelog for 3.5.3 and fixing verify errors (#528) --- CHANGELOG.md | 4 ++++ .../main/java/com/segment/analytics/http/UploadResponse.java | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bfb6679..32676a9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# Version 3.5.3 (Dec 3, 2025) +- [New](https://github.com/segmentio/analytics-java/pull/526) Improved shutdown process +- [New](https://github.com/segmentio/analytics-java/pull/517) Support (de)serializing Instant objects to support newer java versions + # Version 3.5.2 (May 12, 2025) - [Chore] Depenency upgrades diff --git a/analytics-core/src/main/java/com/segment/analytics/http/UploadResponse.java b/analytics-core/src/main/java/com/segment/analytics/http/UploadResponse.java index e91be413..ed41a2ca 100644 --- a/analytics-core/src/main/java/com/segment/analytics/http/UploadResponse.java +++ b/analytics-core/src/main/java/com/segment/analytics/http/UploadResponse.java @@ -7,4 +7,8 @@ @AutoGson public abstract class UploadResponse { public abstract boolean success(); + + public static UploadResponse create(boolean success) { + return new AutoValue_UploadResponse(success); + } } From 1eb13a35b0ebef8278a804fff50e8ff7d117eb22 Mon Sep 17 00:00:00 2001 From: Michael Grosse Huelsewiesche Date: Mon, 12 Jan 2026 12:09:40 -0500 Subject: [PATCH 08/11] Release/3.5.4 (#530) * Updating release for new sonatype repository * Updating changelog * Update release plugin (#529) * Changing release plugin * [maven-release-plugin] prepare release analytics-parent-3.5.4 * [maven-release-plugin] prepare for next development iteration * Moving gpg signing to release deploy --- .buildscript/deploy_snapshot.sh | 2 +- .buildscript/settings.xml | 7 +- .gitignore | 4 +- CHANGELOG.md | 3 + RELEASING.md | 2 +- analytics-cli/pom.xml | 32 +- .../src/main/kotlin/cli/JavadocStub.java | 8 + analytics-core/effective-analytics-core.xml | 633 ++++++++++++++++++ analytics-core/pom.xml | 52 +- analytics-sample/pom.xml | 30 +- analytics-spring-boot-starter/pom.xml | 30 +- analytics/pom.xml | 41 +- pom.xml | 81 ++- 13 files changed, 896 insertions(+), 29 deletions(-) create mode 100644 analytics-cli/src/main/kotlin/cli/JavadocStub.java create mode 100644 analytics-core/effective-analytics-core.xml diff --git a/.buildscript/deploy_snapshot.sh b/.buildscript/deploy_snapshot.sh index e02d3c73..2d98a91b 100755 --- a/.buildscript/deploy_snapshot.sh +++ b/.buildscript/deploy_snapshot.sh @@ -23,6 +23,6 @@ elif [ "$CIRCLE_BRANCH" != "$BRANCH" ]; then echo "Skipping snapshot deployment: wrong branch. Expected '$BRANCH' but was '$CIRCLE_BRANCH'." else echo "Deploying snapshot..." - mvn clean source:jar javadoc:jar deploy --settings=".buildscript/settings.xml" -Dmaven.test.skip=true + mvn clean source:jar javadoc:jar deploy --settings=".buildscript/settings.xml" -Dmaven.test.skip=true -Dgpg.skip=true echo "Snapshot deployed!" fi diff --git a/.buildscript/settings.xml b/.buildscript/settings.xml index 97ec83d7..81d298ff 100644 --- a/.buildscript/settings.xml +++ b/.buildscript/settings.xml @@ -1,12 +1,7 @@ - sonatype-nexus-snapshots - ${env.CI_DEPLOY_USERNAME} - ${env.CI_DEPLOY_PASSWORD} - - - sonatype-nexus-staging + central ${env.CI_DEPLOY_USERNAME} ${env.CI_DEPLOY_PASSWORD} diff --git a/.gitignore b/.gitignore index ea323992..4041df6b 100644 --- a/.gitignore +++ b/.gitignore @@ -128,4 +128,6 @@ atlassian-ide-plugin.xml .classpath .project .settings/ -.factorypath \ No newline at end of file +.factorypath + +.vscode diff --git a/CHANGELOG.md b/CHANGELOG.md index 32676a9e..74b4fe12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# Version 3.5.4 (Dec 5, 2025) +- Updating release for new sonatype repository + # Version 3.5.3 (Dec 3, 2025) - [New](https://github.com/segmentio/analytics-java/pull/526) Improved shutdown process - [New](https://github.com/segmentio/analytics-java/pull/517) Support (de)serializing Instant objects to support newer java versions diff --git a/RELEASING.md b/RELEASING.md index 744ce63c..5d59be70 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -5,4 +5,4 @@ Releasing 2. Update the `CHANGELOG.md` for the impending release. 3. `mvn clean release:clean` 4. `mvn release:prepare release:perform` - 5. Visit [Sonatype Nexus](https://oss.sonatype.org/) and promote the artifact. + 4. Visit the [Maven Central Portal](https://central.sonatype.com/publishing/deployments) to review and publish the release. diff --git a/analytics-cli/pom.xml b/analytics-cli/pom.xml index d4341f18..67e1d4b9 100644 --- a/analytics-cli/pom.xml +++ b/analytics-cli/pom.xml @@ -6,16 +6,45 @@ analytics-parent com.segment.analytics.java - 3.5.3-SNAPSHOT + 3.5.5-SNAPSHOT + com.segment.analytics.java analytics-cli + 3.5.5-SNAPSHOT Analytics Java CLI + Command-line interface for Segment Analytics for Java. + https://github.com/segmentio/analytics-java + + + + The MIT License (MIT) + http://opensource.org/licenses/MIT + + + + + https://github.com/segmentio/analytics-java/ + scm:git:https://github.com/segmentio/analytics-java.git + scm:git:git@github.com:segmentio/analytics-java.git + analytics-parent-3.5.4 + + + + + segment + Segment + Segment + https://segment.com + + + org.jetbrains.kotlin kotlin-stdlib + ${kotlin.version} com.segment.analytics.java @@ -25,6 +54,7 @@ com.offbytwo docopt + ${docopt.version} diff --git a/analytics-cli/src/main/kotlin/cli/JavadocStub.java b/analytics-cli/src/main/kotlin/cli/JavadocStub.java new file mode 100644 index 00000000..403beaa7 --- /dev/null +++ b/analytics-cli/src/main/kotlin/cli/JavadocStub.java @@ -0,0 +1,8 @@ +package cli; + +/** + * Placeholder class to generate a Javadoc JAR for the CLI artifact. + */ +public final class JavadocStub { + private JavadocStub() {} +} diff --git a/analytics-core/effective-analytics-core.xml b/analytics-core/effective-analytics-core.xml new file mode 100644 index 00000000..3cb09524 --- /dev/null +++ b/analytics-core/effective-analytics-core.xml @@ -0,0 +1,633 @@ + + + + + + + + + + + + + + + 4.0.0 + + com.segment.analytics.java + analytics-parent + 3.5.4 + + com.segment.analytics.java + analytics-core + 3.5.5-SNAPSHOT + Analytics for Java (Core) + Core runtime for Segment Analytics for Java. + https://github.com/segmentio/analytics-java + + + The MIT License (MIT) + http://opensource.org/licenses/MIT + + + + + segment + Segment + Segment + https://segment.com + + + + scm:git:https://github.com/segmentio/analytics-java.git + scm:git:git@github.com:segmentio/analytics-java.git + analytics-parent-3.5.4 + https://github.com/segmentio/analytics-java/ + + + GitHub Issues + https://github.com/segmentio/analytics-java/issues + + + 3.24.2 + 1.10.1 + 1.0.0 + 1.2.0 + 0.6.0.20150202 + 3.0.1 + 2.9.1 + 33.4.0-jre + 8 + 4.13.2 + 1.9.0 + 4.10.0 + true + 4.11.0 + 4.12.0 + UTF-8 + 2.11.0 + 2.27.2 + 2.7.5 + + + + + com.squareup.retrofit2 + retrofit + 2.11.0 + + + com.squareup.retrofit2 + converter-gson + 2.9.0 + + + com.squareup.retrofit2 + retrofit-mock + 2.11.0 + + + com.google.auto.value + auto-value + 1.10.1 + provided + + + com.google.auto.value + auto-value-annotations + 1.10.1 + + + com.google.code.findbugs + findbugs + 3.0.1 + + + com.google.code.gson + gson + 2.9.1 + + + com.squareup.okhttp3 + okhttp + 4.12.0 + + + com.squareup.okhttp3 + logging-interceptor + 4.10.0 + + + com.google.guava + guava + 33.4.0-jre + + + com.segment.backo + backo + 1.0.0 + + + junit + junit + 4.13.2 + + + com.squareup.burst + burst-junit4 + 1.2.0 + + + com.squareup.burst + burst + 1.2.0 + + + org.assertj + assertj-core + 3.24.2 + + + org.mockito + mockito-core + 4.11.0 + + + com.offbytwo + docopt + 0.6.0.20150202 + + + org.jetbrains.kotlin + kotlin-stdlib + 1.9.0 + + + + + + com.squareup.retrofit2 + retrofit + 2.11.0 + compile + + + com.google.code.gson + gson + 2.9.1 + compile + + + com.google.code.findbugs + findbugs + 3.0.1 + provided + + + com.google.auto.value + auto-value + 1.10.1 + provided + + + com.google.auto.value + auto-value-annotations + 1.10.1 + compile + + + com.google.guava + guava + 33.4.0-jre + provided + + + jakarta.annotation + jakarta.annotation-api + 2.1.1 + compile + + + javax.annotation + javax.annotation-api + 1.3.2 + compile + + + junit + junit + 4.13.2 + test + + + com.squareup.burst + burst-junit4 + 1.2.0 + test + + + com.squareup.burst + burst + 1.2.0 + test + + + org.assertj + assertj-core + 3.24.2 + test + + + org.mockito + mockito-core + 4.11.0 + test + + + + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + + + never + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + /Users/michaelgh/dev/analytics-java/analytics-core/src/main/java + /Users/michaelgh/dev/analytics-java/analytics-core/src/main/scripts + /Users/michaelgh/dev/analytics-java/analytics-core/src/test/java + /Users/michaelgh/dev/analytics-java/analytics-core/target/classes + /Users/michaelgh/dev/analytics-java/analytics-core/target/test-classes + + + /Users/michaelgh/dev/analytics-java/analytics-core/src/main/resources + + + + + /Users/michaelgh/dev/analytics-java/analytics-core/src/test/resources + + + /Users/michaelgh/dev/analytics-java/analytics-core/target + analytics-core-3.5.5-SNAPSHOT + + + + maven-antrun-plugin + 1.3 + + + maven-assembly-plugin + 2.2-beta-5 + + + maven-dependency-plugin + 2.8 + + + maven-release-plugin + 2.5.3 + + + maven-compiler-plugin + 3.11.0 + + true + 8 + 8 + true + + + + com.diffplug.spotless + spotless-maven-plugin + 2.27.2 + + + + 1.5 + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.codehaus.mojo + templating-maven-plugin + [1.0-alpha-3,) + + filter-sources + filter-test-sources + + + + + true + true + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + [1.0.0,) + + enforce + + + + + + + + + + + + + + + org.sonatype.central + central-publishing-maven-plugin + 0.9.0 + true + + + injected-central-publishing + deploy + + publish + + + central + + + + + central + + + + maven-deploy-plugin + 3.0.0 + + true + + + + maven-assembly-plugin + 2.2-beta-5 + + + + com.segment.analytics.Analytics + + + + jar-with-dependencies + + + + + org.codehaus.mojo + cobertura-maven-plugin + 2.7 + + + html + xml + + + + + + maven-surefire-plugin + 3.2.5 + + + default-test + test + + test + + + false + + + + + false + + + + maven-source-plugin + 3.3.1 + + + attach-sources + + jar-no-fork + + + + + + maven-javadoc-plugin + 3.6.3 + + + attach-javadocs + + jar + + + + + + maven-gpg-plugin + 3.2.7 + + + sign-artifacts + verify + + sign + + + + + + maven-clean-plugin + 3.2.0 + + + default-clean + clean + + clean + + + + + + maven-resources-plugin + 3.3.0 + + + default-testResources + process-test-resources + + testResources + + + + default-resources + process-resources + + resources + + + + + + maven-jar-plugin + 3.3.0 + + + default-jar + package + + jar + + + + + + maven-compiler-plugin + 3.11.0 + + + default-compile + compile + + compile + + + true + 8 + 8 + true + + + + default-testCompile + test-compile + + testCompile + + + true + 8 + 8 + true + + + + + true + 8 + 8 + true + + + + maven-install-plugin + 3.1.0 + + + default-install + install + + install + + + + + + maven-site-plugin + 3.12.1 + + + default-site + site + + site + + + /Users/michaelgh/dev/analytics-java/analytics-core/target/site + + + org.apache.maven.plugins + maven-project-info-reports-plugin + + + + + + default-deploy + site-deploy + + deploy + + + /Users/michaelgh/dev/analytics-java/analytics-core/target/site + + + org.apache.maven.plugins + maven-project-info-reports-plugin + + + + + + + /Users/michaelgh/dev/analytics-java/analytics-core/target/site + + + org.apache.maven.plugins + maven-project-info-reports-plugin + + + + + + + + /Users/michaelgh/dev/analytics-java/analytics-core/target/site + + diff --git a/analytics-core/pom.xml b/analytics-core/pom.xml index f3dc3d23..e2db32c3 100644 --- a/analytics-core/pom.xml +++ b/analytics-core/pom.xml @@ -6,38 +6,72 @@ com.segment.analytics.java analytics-parent - 3.5.3-SNAPSHOT + 3.5.5-SNAPSHOT + com.segment.analytics.java analytics-core + 3.5.5-SNAPSHOT Analytics for Java (Core) + Core runtime for Segment Analytics for Java. + https://github.com/segmentio/analytics-java + + + + The MIT License (MIT) + http://opensource.org/licenses/MIT + + + + + https://github.com/segmentio/analytics-java/ + scm:git:https://github.com/segmentio/analytics-java.git + scm:git:git@github.com:segmentio/analytics-java.git + analytics-parent-3.5.4 + + + + + segment + Segment + Segment + https://segment.com + + + com.squareup.retrofit2 retrofit + ${retrofit.version} com.google.code.gson gson + ${gson.version} com.google.code.findbugs findbugs + ${findbugs.version} provided com.google.auto.value auto-value + ${auto.version} provided com.google.auto.value auto-value-annotations + ${auto.version} com.google.guava guava + ${guava.version} provided @@ -45,30 +79,46 @@ jakarta.annotation-api 2.1.1 + + javax.annotation + javax.annotation-api + 1.3.2 + + + + javax.annotation + javax.annotation-api + 1.3.2 + junit junit + ${junit.version} test com.squareup.burst burst-junit4 + ${burst.version} test com.squareup.burst burst + ${burst.version} test org.assertj assertj-core + ${assertj.version} test org.mockito mockito-core + ${mockito.version} test diff --git a/analytics-sample/pom.xml b/analytics-sample/pom.xml index 8151af27..4794d406 100644 --- a/analytics-sample/pom.xml +++ b/analytics-sample/pom.xml @@ -6,12 +6,40 @@ analytics-parent com.segment.analytics.java - 3.5.3-SNAPSHOT + 3.5.5-SNAPSHOT + com.segment.analytics.java analytics-sample + 3.5.5-SNAPSHOT Analytics Sample + Sample application demonstrating Segment Analytics for Java. + https://github.com/segmentio/analytics-java + + + + The MIT License (MIT) + http://opensource.org/licenses/MIT + + + + + https://github.com/segmentio/analytics-java/ + scm:git:https://github.com/segmentio/analytics-java.git + scm:git:git@github.com:segmentio/analytics-java.git + analytics-parent-3.5.4 + + + + + segment + Segment + Segment + https://segment.com + + + com.segment.analytics.java diff --git a/analytics-spring-boot-starter/pom.xml b/analytics-spring-boot-starter/pom.xml index d5d70478..4ebc8959 100644 --- a/analytics-spring-boot-starter/pom.xml +++ b/analytics-spring-boot-starter/pom.xml @@ -6,12 +6,40 @@ com.segment.analytics.java analytics-parent - 3.5.3-SNAPSHOT + 3.5.5-SNAPSHOT + com.segment.analytics.java analytics-spring-boot-starter + 3.5.5-SNAPSHOT Spring Boot starter for Segment Analytics for Java + Spring Boot auto-configuration for Segment Analytics for Java. + https://github.com/segmentio/analytics-java + + + + The MIT License (MIT) + http://opensource.org/licenses/MIT + + + + + https://github.com/segmentio/analytics-java/ + scm:git:https://github.com/segmentio/analytics-java.git + scm:git:git@github.com:segmentio/analytics-java.git + analytics-parent-3.5.4 + + + + + segment + Segment + Segment + https://segment.com + + + diff --git a/analytics/pom.xml b/analytics/pom.xml index 0ef7606b..05e36fbb 100644 --- a/analytics/pom.xml +++ b/analytics/pom.xml @@ -6,12 +6,40 @@ com.segment.analytics.java analytics-parent - 3.5.3-SNAPSHOT + 3.5.5-SNAPSHOT + com.segment.analytics.java analytics + 3.5.5-SNAPSHOT Analytics for Java + The hassle-free way to add analytics to your Java app. + https://github.com/segmentio/analytics-java + + + + The MIT License (MIT) + http://opensource.org/licenses/MIT + + + + + https://github.com/segmentio/analytics-java/ + scm:git:https://github.com/segmentio/analytics-java.git + scm:git:git@github.com:segmentio/analytics-java.git + analytics-parent-3.5.4 + + + + + segment + Segment + Segment + https://segment.com + + + com.segment.analytics.java @@ -21,51 +49,62 @@ com.squareup.okhttp3 okhttp + ${okhttp.version} com.squareup.okhttp3 logging-interceptor + ${logging.version} com.squareup.retrofit2 converter-gson + 2.9.0 com.squareup.retrofit2 retrofit-mock + ${retrofit.version} com.google.code.findbugs findbugs + ${findbugs.version} provided com.segment.backo backo + ${backo.version} junit junit + ${junit.version} test com.squareup.burst burst-junit4 + ${burst.version} test com.squareup.burst burst + ${burst.version} test org.assertj assertj-core + ${assertj.version} test org.mockito mockito-core + ${mockito.version} test diff --git a/pom.xml b/pom.xml index 22316d4b..70cfe8b4 100644 --- a/pom.xml +++ b/pom.xml @@ -3,20 +3,23 @@ 4.0.0 - - org.sonatype.oss - oss-parent - 7 - - com.segment.analytics.java analytics-parent - 3.5.3-SNAPSHOT + 3.5.5-SNAPSHOT pom Analytics for Java (Parent) The hassle-free way to add analytics to your Android app. https://github.com/segmentio/analytics-java + + + segment + Segment + Segment + https://segment.com + + + analytics-core analytics @@ -42,6 +45,9 @@ 2.7.5 0.6.0.20150202 + + true + 4.13.2 1.2.0 @@ -53,7 +59,7 @@ https://github.com/segmentio/analytics-java/ scm:git:https://github.com/segmentio/analytics-java.git scm:git:git@github.com:segmentio/analytics-java.git - HEAD + analytics-parent-3.5.4 @@ -240,16 +246,21 @@ + + org.sonatype.central + central-publishing-maven-plugin + 0.9.0 + true + + central + + org.apache.maven.plugins - maven-release-plugin - 2.5.3 + maven-deploy-plugin + 3.0.0 - true - - pom.xml - .buildscript/settings.xml - + true @@ -287,6 +298,46 @@ false + + org.apache.maven.plugins + maven-source-plugin + 3.3.1 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.6.3 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 3.2.7 + + + sign-artifacts + deploy + + sign + + + + From 3d4dd86e6ed760c61503d3808aa2cb55f21ef904 Mon Sep 17 00:00:00 2001 From: Michael Grosse Huelsewiesche Date: Wed, 18 Feb 2026 16:14:56 -0500 Subject: [PATCH 09/11] E2e cli for cross-SDK tests (#532) * Add e2e-cli module for analytics-java - Kotlin CLI using Gson for JSON parsing - Separate from existing analytics-cli to avoid disruption - Added to parent pom.xml modules * Add E2E test workflow Runs sdk-e2e-tests suite against the e2e-cli to verify SDK behavior. * Add E2E_TEST_SUITES to e2e workflow for selective test execution * Adding README.md for e2e-cli * Add E2E_TESTS_TOKEN for private sdk-e2e-tests repo checkout Per-SDK config and convenience script for the generic test runner in sdk-e2e-tests. Run ./e2e-cli/run-e2e.sh to build and test locally. Replace hardcoded env vars and direct npm test call with ./scripts/run-tests.sh which reads e2e-config.json for test configuration. This ensures CI uses the same config as local runs. Co-authored-by: Claude Opus 4.6 --- .github/workflows/e2e-tests.yml | 70 ++++++++++++ e2e-cli/README.md | 54 ++++++++++ e2e-cli/e2e-config.json | 7 ++ e2e-cli/pom.xml | 91 ++++++++++++++++ e2e-cli/run-e2e.sh | 41 +++++++ e2e-cli/src/main/kotlin/cli/Main.kt | 159 ++++++++++++++++++++++++++++ pom.xml | 1 + 7 files changed, 423 insertions(+) create mode 100644 .github/workflows/e2e-tests.yml create mode 100644 e2e-cli/README.md create mode 100644 e2e-cli/e2e-config.json create mode 100644 e2e-cli/pom.xml create mode 100755 e2e-cli/run-e2e.sh create mode 100644 e2e-cli/src/main/kotlin/cli/Main.kt diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml new file mode 100644 index 00000000..2e83d68e --- /dev/null +++ b/.github/workflows/e2e-tests.yml @@ -0,0 +1,70 @@ +# E2E Tests for analytics-java +# Copy this file to: analytics-java/.github/workflows/e2e-tests.yml +# +# This workflow: +# 1. Checks out the SDK and sdk-e2e-tests repos +# 2. Builds the SDK and e2e-cli +# 3. Runs the e2e test suite + +name: E2E Tests + +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] + workflow_dispatch: # Allow manual trigger + +jobs: + e2e-tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout SDK + uses: actions/checkout@v4 + with: + path: sdk + + - name: Checkout sdk-e2e-tests + uses: actions/checkout@v4 + with: + repository: segmentio/sdk-e2e-tests + token: ${{ secrets.E2E_TESTS_TOKEN }} + path: sdk-e2e-tests + + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '11' + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Build Java SDK and e2e-cli + working-directory: sdk + run: mvn package -pl e2e-cli -am -DskipTests + + - name: Find e2e-cli jar + id: find-jar + working-directory: sdk + run: | + JAR_PATH=$(find e2e-cli/target -name "e2e-cli-*-jar-with-dependencies.jar" | head -1) + echo "jar_path=$JAR_PATH" >> $GITHUB_OUTPUT + + - name: Run E2E tests + working-directory: sdk-e2e-tests + run: | + ./scripts/run-tests.sh \ + --sdk-dir "${{ github.workspace }}/sdk/e2e-cli" \ + --cli "java -jar ${{ github.workspace }}/sdk/${{ steps.find-jar.outputs.jar_path }}" + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: e2e-test-results + path: sdk-e2e-tests/test-results/ + if-no-files-found: ignore diff --git a/e2e-cli/README.md b/e2e-cli/README.md new file mode 100644 index 00000000..b319749e --- /dev/null +++ b/e2e-cli/README.md @@ -0,0 +1,54 @@ +# analytics-java e2e-cli + +E2E test CLI for the [analytics-java](https://github.com/segmentio/analytics-java) SDK. Accepts a JSON input describing events and SDK configuration, sends them through the real SDK, and outputs results as JSON. + +Built with Kotlin (JVM) and packaged as a fat jar via Maven. + +## Setup + +```bash +mvn package -pl e2e-cli -am +``` + +## Usage + +```bash +java -jar e2e-cli/target/e2e-cli-*-jar-with-dependencies.jar --input '{"writeKey":"...", ...}' +``` + +## Input Format + +```jsonc +{ + "writeKey": "your-write-key", // required + "apiHost": "https://...", // optional — SDK default if omitted + "sequences": [ // required — event sequences to send + { + "delayMs": 0, + "events": [ + { "type": "track", "event": "Test", "userId": "user-1" } + ] + } + ], + "config": { // optional + "flushAt": 250, + "flushInterval": 10000, + "maxRetries": 3, + "timeout": 15 + } +} +``` + +Note: Java is a server-side SDK — there is no CDN settings fetch, so `cdnHost` does not apply. + +## Output Format + +```json +{ "success": true, "sentBatches": 1 } +``` + +On failure: + +```json +{ "success": false, "error": "description", "sentBatches": 0 } +``` diff --git a/e2e-cli/e2e-config.json b/e2e-cli/e2e-config.json new file mode 100644 index 00000000..db3d0167 --- /dev/null +++ b/e2e-cli/e2e-config.json @@ -0,0 +1,7 @@ +{ + "sdk": "java", + "test_suites": "basic", + "auto_settings": false, + "patch": null, + "env": {} +} diff --git a/e2e-cli/pom.xml b/e2e-cli/pom.xml new file mode 100644 index 00000000..b5782b8f --- /dev/null +++ b/e2e-cli/pom.xml @@ -0,0 +1,91 @@ + + + + 4.0.0 + + + analytics-parent + com.segment.analytics.java + 3.5.5-SNAPSHOT + + + com.segment.analytics.java + e2e-cli + 3.5.5-SNAPSHOT + Analytics Java E2E CLI + + E2E testing CLI for Segment Analytics for Java. + + + + org.jetbrains.kotlin + kotlin-stdlib + ${kotlin.version} + + + com.segment.analytics.java + analytics + ${project.version} + + + org.jetbrains.kotlinx + kotlinx-serialization-json + 1.4.1 + + + + + src/main/kotlin + + + + kotlin-maven-plugin + org.jetbrains.kotlin + ${kotlin.version} + + + kotlinx-serialization + + + + + org.jetbrains.kotlin + kotlin-maven-serialization + ${kotlin.version} + + + + + compile + compile + + compile + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + jar-with-dependencies + + + + cli.MainKt + + + + + + package + + single + + + + + + + diff --git a/e2e-cli/run-e2e.sh b/e2e-cli/run-e2e.sh new file mode 100755 index 00000000..782df0e8 --- /dev/null +++ b/e2e-cli/run-e2e.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# +# Run E2E tests for analytics-java +# +# Prerequisites: Java 11+, Maven, Node.js 18+ +# +# Usage: +# ./run-e2e.sh [extra args passed to run-tests.sh] +# +# Override sdk-e2e-tests location: +# E2E_TESTS_DIR=../my-e2e-tests ./run-e2e.sh +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SDK_ROOT="$SCRIPT_DIR/.." +E2E_DIR="${E2E_TESTS_DIR:-$SDK_ROOT/../sdk-e2e-tests}" + +echo "=== Building analytics-java e2e-cli ===" + +# Build SDK and e2e-cli +cd "$SDK_ROOT" +mvn package -pl e2e-cli -am -DskipTests + +# Find the built jar +CLI_JAR=$(find "$SDK_ROOT/e2e-cli/target" -name "e2e-cli-*-jar-with-dependencies.jar" | head -1) +if [[ -z "$CLI_JAR" ]]; then + echo "Error: Could not find e2e-cli jar" + exit 1 +fi +echo "Found jar: $CLI_JAR" + +echo "" + +# Run tests +cd "$E2E_DIR" +./scripts/run-tests.sh \ + --sdk-dir "$SCRIPT_DIR" \ + --cli "java -jar $CLI_JAR" \ + "$@" diff --git a/e2e-cli/src/main/kotlin/cli/Main.kt b/e2e-cli/src/main/kotlin/cli/Main.kt new file mode 100644 index 00000000..6db10cbd --- /dev/null +++ b/e2e-cli/src/main/kotlin/cli/Main.kt @@ -0,0 +1,159 @@ +package cli + +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.segment.analytics.Analytics +import com.segment.analytics.Callback +import com.segment.analytics.messages.* +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicBoolean + +data class CLIOutput( + val success: Boolean, + val error: String? = null, + val sentBatches: Int = 0 +) + +data class CLIConfig( + val flushAt: Int? = null, + val flushInterval: Long? = null, + val maxRetries: Int? = null, + val timeout: Int? = null +) + +data class EventSequence( + val delayMs: Long = 0, + val events: List> +) + +data class CLIInput( + val writeKey: String, + val apiHost: String, + val sequences: List, + val config: CLIConfig? = null +) + +private val gson = Gson() + +fun main(args: Array) { + var output = CLIOutput(success = false, error = "Unknown error") + + try { + // Parse --input argument + val inputIndex = args.indexOf("--input") + if (inputIndex == -1 || inputIndex + 1 >= args.size) { + throw IllegalArgumentException("Missing required --input argument") + } + + val inputJson = args[inputIndex + 1] + val input = gson.fromJson(inputJson, CLIInput::class.java) + + val flushAt = input.config?.flushAt ?: 20 + val flushIntervalMs = input.config?.flushInterval ?: 10000L + + val flushLatch = CountDownLatch(1) + val hasError = AtomicBoolean(false) + var errorMessage: String? = null + + val analytics = Analytics.builder(input.writeKey) + .endpoint(input.apiHost) + .flushQueueSize(flushAt) + .flushInterval(maxOf(flushIntervalMs, 1000L), TimeUnit.MILLISECONDS) + .callback(object : Callback { + override fun success(message: Message?) { + // Event sent successfully + } + + override fun failure(message: Message?, throwable: Throwable?) { + hasError.set(true) + errorMessage = throwable?.message + } + }) + .build() + + // Process event sequences + for (seq in input.sequences) { + if (seq.delayMs > 0) { + Thread.sleep(seq.delayMs) + } + + for (event in seq.events) { + sendEvent(analytics, event) + } + } + + // Flush and shutdown + analytics.flush() + analytics.shutdown() + + output = if (hasError.get()) { + CLIOutput(success = false, error = errorMessage, sentBatches = 0) + } else { + CLIOutput(success = true, sentBatches = 1) + } + + } catch (e: Exception) { + output = CLIOutput(success = false, error = e.message ?: e.toString()) + } + + println(gson.toJson(output)) +} + +fun sendEvent(analytics: Analytics, event: Map) { + val type = event["type"] as? String + ?: throw IllegalArgumentException("Event missing 'type' field") + + val userId = event["userId"] as? String ?: "" + val anonymousId = event["anonymousId"] as? String + val messageId = event["messageId"] as? String + @Suppress("UNCHECKED_CAST") + val traits = event["traits"] as? Map ?: emptyMap() + @Suppress("UNCHECKED_CAST") + val properties = event["properties"] as? Map ?: emptyMap() + val eventName = event["event"] as? String + val name = event["name"] as? String + val groupId = event["groupId"] as? String + val previousId = event["previousId"] as? String + + val messageBuilder: MessageBuilder<*, *> = when (type) { + "identify" -> { + IdentifyMessage.builder().apply { + traits(traits) + } + } + "track" -> { + TrackMessage.builder(eventName ?: "Unknown Event").apply { + properties(properties) + } + } + "page" -> { + PageMessage.builder(name ?: "Unknown Page").apply { + properties(properties) + } + } + "screen" -> { + ScreenMessage.builder(name ?: "Unknown Screen").apply { + properties(properties) + } + } + "alias" -> { + AliasMessage.builder(previousId ?: "") + } + "group" -> { + GroupMessage.builder(groupId ?: "").apply { + traits(traits) + } + } + else -> throw IllegalArgumentException("Unknown event type: $type") + } + + if (userId.isNotEmpty()) { + messageBuilder.userId(userId) + } + if (anonymousId != null) { + messageBuilder.anonymousId(anonymousId) + } + + analytics.enqueue(messageBuilder) +} diff --git a/pom.xml b/pom.xml index 70cfe8b4..d873d8ba 100644 --- a/pom.xml +++ b/pom.xml @@ -26,6 +26,7 @@ analytics-sample analytics-cli analytics-spring-boot-starter + e2e-cli From 8585e31a7a3e27ea9e7c3ec0ab64ffcee8d8f778 Mon Sep 17 00:00:00 2001 From: Michael Grosse Huelsewiesche Date: Thu, 26 Feb 2026 14:41:52 -0500 Subject: [PATCH 10/11] Align e2e-cli types with sdk-e2e-tests (#533) * Align e2e-cli types with sdk-e2e-tests definitions Add missing field extraction: timestamp, category, context, integrations. Wire timestamp, messageId, context, and integrations through to the Java SDK MessageBuilder for full fidelity. Co-Authored-By: Claude Opus 4.6 * Replace SimpleDateFormat with java.time.Instant for timestamp parsing Instant.parse() handles all ISO 8601 formats natively, avoids deprecated SimpleDateFormat, and throws DateTimeParseException with clear context on invalid input instead of returning null. Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 --- e2e-cli/src/main/kotlin/cli/Main.kt | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/e2e-cli/src/main/kotlin/cli/Main.kt b/e2e-cli/src/main/kotlin/cli/Main.kt index 6db10cbd..b4577615 100644 --- a/e2e-cli/src/main/kotlin/cli/Main.kt +++ b/e2e-cli/src/main/kotlin/cli/Main.kt @@ -5,6 +5,8 @@ import com.google.gson.reflect.TypeToken import com.segment.analytics.Analytics import com.segment.analytics.Callback import com.segment.analytics.messages.* +import java.time.Instant +import java.util.Date import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean @@ -107,14 +109,20 @@ fun sendEvent(analytics: Analytics, event: Map) { val userId = event["userId"] as? String ?: "" val anonymousId = event["anonymousId"] as? String val messageId = event["messageId"] as? String + val timestamp = event["timestamp"] as? String @Suppress("UNCHECKED_CAST") val traits = event["traits"] as? Map ?: emptyMap() @Suppress("UNCHECKED_CAST") val properties = event["properties"] as? Map ?: emptyMap() val eventName = event["event"] as? String val name = event["name"] as? String + val category = event["category"] as? String val groupId = event["groupId"] as? String val previousId = event["previousId"] as? String + @Suppress("UNCHECKED_CAST") + val context = event["context"] as? Map + @Suppress("UNCHECKED_CAST") + val integrations = event["integrations"] as? Map val messageBuilder: MessageBuilder<*, *> = when (type) { "identify" -> { @@ -154,6 +162,28 @@ fun sendEvent(analytics: Analytics, event: Map) { if (anonymousId != null) { messageBuilder.anonymousId(anonymousId) } + if (messageId != null) { + messageBuilder.messageId(messageId) + } + if (timestamp != null) { + messageBuilder.timestamp(parseTimestamp(timestamp)) + } + if (context != null) { + messageBuilder.context(context) + } + if (integrations != null) { + for ((key, value) in integrations) { + when (value) { + is Boolean -> messageBuilder.enableIntegration(key, value) + is Map<*, *> -> @Suppress("UNCHECKED_CAST") + messageBuilder.integrationOptions(key, value as Map) + } + } + } analytics.enqueue(messageBuilder) } + +private fun parseTimestamp(iso: String): Date { + return Date.from(Instant.parse(iso)) +} From 2fc9f1c40b3665835951f9567545dc595efee8a3 Mon Sep 17 00:00:00 2001 From: Michael Grosse Huelsewiesche Date: Fri, 13 Mar 2026 16:49:54 -0400 Subject: [PATCH 11/11] Update e2e-tests CI: skip fork PRs, accept test ref input (#534) - Skip e2e tests on fork PRs (no access to repo secrets) - Accept e2e_tests_ref input for cross-repo test validation Co-authored-by: Claude Opus 4.6 --- .github/workflows/e2e-tests.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 2e83d68e..c59ec2cb 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -13,10 +13,17 @@ on: branches: [main, master] pull_request: branches: [main, master] - workflow_dispatch: # Allow manual trigger + workflow_dispatch: + inputs: + e2e_tests_ref: + description: 'Branch or ref of sdk-e2e-tests to use' + required: false + default: 'main' jobs: e2e-tests: + # Skip on fork PRs where repo secrets aren't available + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} runs-on: ubuntu-latest steps: @@ -29,6 +36,7 @@ jobs: uses: actions/checkout@v4 with: repository: segmentio/sdk-e2e-tests + ref: ${{ inputs.e2e_tests_ref || 'main' }} token: ${{ secrets.E2E_TESTS_TOKEN }} path: sdk-e2e-tests