├── .gitignore ├── src ├── main │ ├── resources │ │ └── mixpanel-version.properties │ └── java │ │ └── com │ │ └── mixpanel │ │ └── mixpanelapi │ │ ├── Config.java │ │ ├── featureflags │ │ ├── EventSender.java │ │ ├── model │ │ │ ├── VariantOverride.java │ │ │ ├── Variant.java │ │ │ ├── RuleSet.java │ │ │ ├── Rollout.java │ │ │ ├── SelectedVariant.java │ │ │ └── ExperimentationFlag.java │ │ ├── util │ │ │ ├── TraceparentUtil.java │ │ │ ├── VersionUtil.java │ │ │ └── HashUtils.java │ │ ├── config │ │ │ ├── RemoteFlagsConfig.java │ │ │ ├── LocalFlagsConfig.java │ │ │ └── BaseFlagsConfig.java │ │ └── provider │ │ │ ├── RemoteFlagsProvider.java │ │ │ ├── BaseFlagsProvider.java │ │ │ └── LocalFlagsProvider.java │ │ ├── internal │ │ ├── OrgJsonSerializer.java │ │ └── JsonSerializer.java │ │ ├── MixpanelServerException.java │ │ ├── MixpanelMessageException.java │ │ ├── ClientDelivery.java │ │ ├── DeliveryOptions.java │ │ └── Base64Coder.java ├── test │ └── java │ │ └── com │ │ └── mixpanel │ │ └── mixpanelapi │ │ ├── featureflags │ │ └── provider │ │ │ ├── BaseExposureTrackerMock.java │ │ │ ├── BaseFlagsProviderTest.java │ │ │ ├── MockHttpProvider.java │ │ │ └── RemoteFlagsProviderTest.java │ │ └── internal │ │ └── JsonSerializerTest.java └── demo │ └── java │ └── com │ └── mixpanel │ └── mixpanelapi │ ├── featureflags │ └── demo │ │ ├── RemoteEvaluationExample.java │ │ └── LocalEvaluationExample.java │ └── demo │ └── MixpanelAPIDemo.java ├── mixpanel-java-extension-jackson ├── README.md ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── mixpanel │ │ │ └── mixpanelapi │ │ │ └── internal │ │ │ └── JacksonSerializer.java │ └── test │ │ └── java │ │ └── com │ │ └── mixpanel │ │ └── mixpanelapi │ │ └── internal │ │ └── JacksonSerializerTest.java └── pom.xml ├── .github ├── workflows │ ├── copilot-setup-steps.yml │ ├── ci.yml │ └── release.yml └── copilot-instructions.md ├── pom.xml ├── CLAUDE.md ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | .metadata 4 | target/ 5 | .vscode/ -------------------------------------------------------------------------------- /src/main/resources/mixpanel-version.properties: -------------------------------------------------------------------------------- 1 | version=${project.version} 2 | -------------------------------------------------------------------------------- /src/main/java/com/mixpanel/mixpanelapi/Config.java: -------------------------------------------------------------------------------- 1 | package com.mixpanel.mixpanelapi; 2 | 3 | /* package */ class Config { 4 | public static final String BASE_ENDPOINT = "https://api.mixpanel.com"; 5 | public static final int MAX_MESSAGE_SIZE = 50; 6 | public static final int IMPORT_MAX_MESSAGE_SIZE = 2000; 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/mixpanel/mixpanelapi/featureflags/EventSender.java: -------------------------------------------------------------------------------- 1 | package com.mixpanel.mixpanelapi.featureflags; 2 | 3 | import org.json.JSONObject; 4 | 5 | /** 6 | * Interface for sending events to an analytics backend. 7 | *
8 | * Implementations are responsible for constructing the event payload 9 | * and delivering it to the appropriate destination. 10 | *
11 | */ 12 | @FunctionalInterface 13 | public interface EventSender { 14 | /** 15 | * Sends an event with the specified properties. 16 | * 17 | * @param distinctId the user's distinct ID 18 | * @param eventName the name of the event (e.g., "$experiment_started") 19 | * @param properties the event properties as a JSONObject 20 | */ 21 | void sendEvent(String distinctId, String eventName, JSONObject properties); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/mixpanel/mixpanelapi/internal/OrgJsonSerializer.java: -------------------------------------------------------------------------------- 1 | package com.mixpanel.mixpanelapi.internal; 2 | 3 | import org.json.JSONArray; 4 | import org.json.JSONObject; 5 | import java.util.List; 6 | 7 | /** 8 | * JSON serialization implementation using org.json library. 9 | * This is the default implementation that maintains backward compatibility. 10 | * 11 | * @since 1.6.1 12 | */ 13 | public class OrgJsonSerializer implements JsonSerializer { 14 | 15 | @Override 16 | public String serializeArray(List6 | * A variant override forces selection of a specific variant when a rollout matches. 7 | *
8 | *9 | * This class is immutable and thread-safe. 10 | *
11 | */ 12 | public final class VariantOverride { 13 | private final String key; 14 | 15 | /** 16 | * Creates a new VariantOverride. 17 | * 18 | * @param key the variant key to force selection of 19 | */ 20 | public VariantOverride(String key) { 21 | this.key = key; 22 | } 23 | 24 | /** 25 | * @return the variant key 26 | */ 27 | public String getKey() { 28 | return key; 29 | } 30 | 31 | @Override 32 | public String toString() { 33 | return "VariantOverride{" + 34 | "key='" + key + '\'' + 35 | '}'; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/mixpanel/mixpanelapi/MixpanelMessageException.java: -------------------------------------------------------------------------------- 1 | package com.mixpanel.mixpanelapi; 2 | 3 | import org.json.JSONObject; 4 | 5 | /** 6 | * Thrown when the library detects malformed or invalid Mixpanel messages. 7 | * 8 | * Mixpanel messages are represented as JSONObjects, but not all JSONObjects represent valid Mixpanel messages. 9 | * MixpanelMessageExceptions are thrown when a JSONObject is passed to the Mixpanel library that can't be 10 | * passed on to the Mixpanel service. 11 | * 12 | * This is a runtime exception, since in most cases it is thrown due to errors in your application architecture. 13 | */ 14 | public class MixpanelMessageException extends RuntimeException { 15 | 16 | private static final long serialVersionUID = -6256936727567434262L; 17 | 18 | private JSONObject mBadMessage = null; 19 | 20 | /* package */ MixpanelMessageException(String message, JSONObject cause) { 21 | super(message); 22 | mBadMessage = cause; 23 | } 24 | 25 | /** 26 | * @return the (possibly null) JSONObject message associated with the failure 27 | */ 28 | public JSONObject getBadMessage() { 29 | return mBadMessage; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/com/mixpanel/mixpanelapi/featureflags/provider/BaseExposureTrackerMock.java: -------------------------------------------------------------------------------- 1 | package com.mixpanel.mixpanelapi.featureflags.provider; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Base class for exposure tracker mocks. 8 | * Provides common event storage and retrieval functionality. 9 | *10 | * Subclasses should extend this class and implement the specific ExposureTracker interface 11 | * for their provider type (LocalFlagsProvider.ExposureTracker or RemoteFlagsProvider.ExposureTracker). 12 | *
13 | * 14 | * @param8 | * Generates traceparent headers in the format: 00-{trace_id}-{span_id}-01 9 | * where trace_id is a 32-character hex string and span_id is a 16-character hex string. 10 | *
11 | *12 | * This class is thread-safe. 13 | *
14 | * 15 | * @see W3C Trace Context 16 | */ 17 | public final class TraceparentUtil { 18 | 19 | /** 20 | * Private constructor to prevent instantiation. 21 | */ 22 | private TraceparentUtil() { 23 | throw new AssertionError("TraceparentUtil should not be instantiated"); 24 | } 25 | 26 | /** 27 | * Generates a W3C traceparent header value. 28 | *29 | * Format: 00-{trace_id}-{span_id}-01 30 | * Uses two separate UUIDs with dashes removed - one for trace_id (32 chars) 31 | * and one for span_id (16 chars). 32 | *
33 | * 34 | * @return a traceparent header value 35 | */ 36 | public static String generateTraceparent() { 37 | String traceId = UUID.randomUUID().toString().replace("-", ""); 38 | String spanId = UUID.randomUUID().toString().replace("-", "").substring(0, 16); 39 | return "00-" + traceId + "-" + spanId + "-01"; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /mixpanel-java-extension-jackson/README.md: -------------------------------------------------------------------------------- 1 | # Mixpanel Java SDK - Jackson Extension 2 | 3 | High-performance Jackson serializer extension for the Mixpanel Java SDK. This extension provides improved JSON serialization performance for large batch operations. 4 | 5 | ## Installation 6 | 7 | Add this dependency to your project: 8 | 9 | ### Maven 10 | ```xml 11 |6 | * Extends {@link BaseFlagsConfig} with settings specific to remote evaluation mode. 7 | * Currently contains no additional configuration beyond the base settings. 8 | *
9 | */ 10 | public final class RemoteFlagsConfig extends BaseFlagsConfig { 11 | 12 | /** 13 | * Creates a new RemoteFlagsConfig with specified settings. 14 | * 15 | * @param projectToken the Mixpanel project token 16 | * @param apiHost the API endpoint host 17 | * @param requestTimeoutSeconds HTTP request timeout in seconds 18 | */ 19 | private RemoteFlagsConfig(String projectToken, String apiHost, int requestTimeoutSeconds) { 20 | super(projectToken, apiHost, requestTimeoutSeconds); 21 | } 22 | 23 | /** 24 | * Builder for RemoteFlagsConfig. 25 | */ 26 | public static final class Builder extends BaseFlagsConfig.Builder6 | * A variant defines a specific variation of a feature flag with its key, value, 7 | * control status, and percentage split allocation. 8 | *
9 | *10 | * This class is immutable and thread-safe. 11 | *
12 | */ 13 | public final class Variant { 14 | private final String key; 15 | private final Object value; 16 | private final boolean isControl; 17 | private final float split; 18 | 19 | /** 20 | * Creates a new Variant. 21 | * 22 | * @param key the unique identifier for this variant 23 | * @param value the value associated with this variant (can be boolean, string, number, or JSON object) 24 | * @param isControl whether this variant is the control variant 25 | * @param split the percentage split allocation for this variant (0.0-1.0) 26 | */ 27 | public Variant(String key, Object value, boolean isControl, float split) { 28 | this.key = key; 29 | this.value = value; 30 | this.isControl = isControl; 31 | this.split = split; 32 | } 33 | 34 | /** 35 | * @return the unique identifier for this variant 36 | */ 37 | public String getKey() { 38 | return key; 39 | } 40 | 41 | /** 42 | * @return the value associated with this variant 43 | */ 44 | public Object getValue() { 45 | return value; 46 | } 47 | 48 | /** 49 | * @return true if this is the control variant 50 | */ 51 | public boolean isControl() { 52 | return isControl; 53 | } 54 | 55 | /** 56 | * @return the percentage split allocation (0.0-1.0) 57 | */ 58 | public float getSplit() { 59 | return split; 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return "Variant{" + 65 | "key='" + key + '\'' + 66 | ", value=" + value + 67 | ", isControl=" + isControl + 68 | ", split=" + split + 69 | '}'; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/test/java/com/mixpanel/mixpanelapi/featureflags/provider/BaseFlagsProviderTest.java: -------------------------------------------------------------------------------- 1 | package com.mixpanel.mixpanelapi.featureflags.provider; 2 | 3 | import org.junit.After; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * Base class for feature flags provider tests. 10 | * Provides shared test infrastructure, lifecycle management, and helper methods. 11 | */ 12 | public abstract class BaseFlagsProviderTest { 13 | 14 | // Shared constants 15 | protected static final String TEST_TOKEN = "test-token"; 16 | protected static final String SDK_VERSION = "1.0.0"; 17 | protected static final String TEST_USER = "user-123"; 18 | 19 | /** 20 | * Shared test lifecycle - closes the provider after each test if it's closeable. 21 | */ 22 | @After 23 | public void tearDown() { 24 | Object provider = getProvider(); 25 | if (provider instanceof AutoCloseable) { 26 | try { 27 | ((AutoCloseable) provider).close(); 28 | } catch (Exception e) { 29 | // Ignore cleanup errors 30 | } 31 | } 32 | } 33 | 34 | /** 35 | * Abstract method for subclasses to provide their provider instance. 36 | * This allows the base class to manage the lifecycle. 37 | * 38 | * @return the provider instance to be closed after each test (if closeable) 39 | */ 40 | protected abstract Object getProvider(); 41 | 42 | /** 43 | * Helper to build a simple context with distinct_id. 44 | * 45 | * @param distinctId the distinct ID to include in the context 46 | * @return a context map with distinct_id 47 | */ 48 | protected Map12 | * The version is loaded from the mixpanel-version.properties file, 13 | * which is populated by Maven during the build process. 14 | *
15 | */ 16 | public class VersionUtil { 17 | private static final Logger logger = Logger.getLogger(VersionUtil.class.getName()); 18 | private static final String VERSION_FILE = "mixpanel-version.properties"; 19 | private static final String VERSION_KEY = "version"; 20 | private static final String UNKNOWN_VERSION = "unknown"; 21 | 22 | private static String cachedVersion = null; 23 | 24 | private VersionUtil() { 25 | // Utility class - prevent instantiation 26 | } 27 | 28 | /** 29 | * Gets the SDK version. 30 | *31 | * The version is loaded from the properties file on first access and cached. 32 | * Returns "unknown" if the version cannot be determined (e.g., running in IDE without build). 33 | *
34 | * 35 | * @return the SDK version string 36 | */ 37 | public static String getVersion() { 38 | if (cachedVersion == null) { 39 | cachedVersion = loadVersion(); 40 | } 41 | return cachedVersion; 42 | } 43 | 44 | /** 45 | * Loads the version from the properties file. 46 | */ 47 | private static String loadVersion() { 48 | try (InputStream input = VersionUtil.class.getClassLoader().getResourceAsStream(VERSION_FILE)) { 49 | if (input == null) { 50 | logger.log(Level.WARNING, "Version file not found: " + VERSION_FILE + " (using fallback version)"); 51 | return UNKNOWN_VERSION; 52 | } 53 | 54 | Properties props = new Properties(); 55 | props.load(input); 56 | 57 | String version = props.getProperty(VERSION_KEY); 58 | if (version == null || version.isEmpty()) { 59 | logger.log(Level.WARNING, "Version property not found in " + VERSION_FILE); 60 | return UNKNOWN_VERSION; 61 | } 62 | 63 | return version; 64 | } catch (IOException e) { 65 | logger.log(Level.WARNING, "Failed to load version from " + VERSION_FILE, e); 66 | return UNKNOWN_VERSION; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/test/java/com/mixpanel/mixpanelapi/featureflags/provider/MockHttpProvider.java: -------------------------------------------------------------------------------- 1 | package com.mixpanel.mixpanelapi.featureflags.provider; 2 | 3 | import java.io.IOException; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | /** 8 | * Utility class providing HTTP mocking infrastructure for testing providers. 9 | * This class provides URL-pattern-based HTTP response mocking. 10 | *11 | * Used by test subclasses to override httpGet() behavior without making real network calls. 12 | *
13 | */ 14 | public class MockHttpProvider { 15 | private final Map44 | * This method: 45 | *
10 | * A ruleset contains all variants available for the flag, rollout rules 11 | * (evaluated in order), and optional test user overrides. 12 | *
13 | *14 | * This class is immutable and thread-safe. 15 | *
16 | */ 17 | public final class RuleSet { 18 | private final List6 | * Extends {@link BaseFlagsConfig} with settings specific to local evaluation mode, 7 | * including polling configuration for periodic flag definition synchronization. 8 | *
9 | */ 10 | public final class LocalFlagsConfig extends BaseFlagsConfig { 11 | private final boolean enablePolling; 12 | private final int pollingIntervalSeconds; 13 | 14 | /** 15 | * Creates a new LocalFlagsConfig with all settings. 16 | * 17 | * @param projectToken the Mixpanel project token 18 | * @param apiHost the API endpoint host 19 | * @param requestTimeoutSeconds HTTP request timeout in seconds 20 | * @param enablePolling whether to periodically refresh flag definitions 21 | * @param pollingIntervalSeconds time between refresh cycles in seconds 22 | */ 23 | private LocalFlagsConfig(String projectToken, String apiHost, int requestTimeoutSeconds, boolean enablePolling, int pollingIntervalSeconds) { 24 | super(projectToken, apiHost, requestTimeoutSeconds); 25 | this.enablePolling = enablePolling; 26 | this.pollingIntervalSeconds = pollingIntervalSeconds; 27 | } 28 | 29 | /** 30 | * @return true if polling is enabled 31 | */ 32 | public boolean isEnablePolling() { 33 | return enablePolling; 34 | } 35 | 36 | /** 37 | * @return the polling interval in seconds 38 | */ 39 | public int getPollingIntervalSeconds() { 40 | return pollingIntervalSeconds; 41 | } 42 | 43 | /** 44 | * Builder for LocalFlagsConfig. 45 | */ 46 | public static final class Builder extends BaseFlagsConfig.Builder8 | * Implements the FNV-1a (Fowler-Noll-Vo hash, variant 1a) algorithm to generate 9 | * deterministic, uniformly distributed hash values in the range [0.0, 1.0). 10 | *
11 | *12 | * This class is thread-safe and all methods are static. 13 | *
14 | */ 15 | public final class HashUtils { 16 | 17 | /** 18 | * FNV-1a 64-bit offset basis constant. 19 | */ 20 | private static final long FNV_OFFSET_BASIS_64 = 0xcbf29ce484222325L; 21 | 22 | /** 23 | * FNV-1a 64-bit prime constant. 24 | */ 25 | private static final long FNV_PRIME_64 = 0x100000001b3L; 26 | 27 | // Private constructor to prevent instantiation 28 | private HashUtils() { 29 | throw new AssertionError("HashUtils should not be instantiated"); 30 | } 31 | 32 | /** 33 | * Generates a normalized hash value in the range [0.0, 1.0) using the FNV-1a algorithm. 34 | * 35 | * @param key the input string to hash (typically user identifier + flag key) 36 | * @param salt the salt to append to the input (e.g., "rollout" or "variant") 37 | * @return a float value in the range [0.0, 1.0) 38 | * @throws IllegalArgumentException if key or salt is null 39 | */ 40 | public static float normalizedHash(String key, String salt) { 41 | if (key == null) { 42 | throw new IllegalArgumentException("Key cannot be null"); 43 | } 44 | if (salt == null) { 45 | throw new IllegalArgumentException("Salt cannot be null"); 46 | } 47 | 48 | // Combine key and salt 49 | String combined = key + salt; 50 | byte[] bytes = combined.getBytes(StandardCharsets.UTF_8); 51 | 52 | // FNV-1a 64-bit hash 53 | long hash = FNV_OFFSET_BASIS_64; 54 | for (byte b : bytes) { 55 | // XOR with byte (converting to unsigned) 56 | hash ^= (b & 0xff); 57 | // Multiply by FNV prime 58 | hash *= FNV_PRIME_64; 59 | } 60 | 61 | // Normalize to [0.0, 1.0) matching Python's approach 62 | // Use Long.remainderUnsigned to handle negative values correctly 63 | return (float) (Long.remainderUnsigned(hash, 100) / 100.0); 64 | } 65 | 66 | /** 67 | * Generates a normalized hash value for rollout selection. 68 | *69 | * Convenience method that uses "rollout" as the salt. 70 | *
71 | * 72 | * @param input the input string to hash (typically user identifier + flag key) 73 | * @return a float value in the range [0.0, 1.0) 74 | */ 75 | public static float rolloutHash(String input) { 76 | return normalizedHash(input, "rollout"); 77 | } 78 | 79 | /** 80 | * Generates a normalized hash value for variant selection. 81 | *82 | * Convenience method that uses "variant" as the salt. 83 | *
84 | * 85 | * @param input the input string to hash (typically user identifier + flag key) 86 | * @return a float value in the range [0.0, 1.0) 87 | */ 88 | public static float variantHash(String input) { 89 | return normalizedHash(input, "variant"); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/mixpanel/mixpanelapi/featureflags/config/BaseFlagsConfig.java: -------------------------------------------------------------------------------- 1 | package com.mixpanel.mixpanelapi.featureflags.config; 2 | 3 | /** 4 | * Base configuration for feature flags providers. 5 | *6 | * Contains common configuration settings shared by both local and remote evaluation modes. 7 | *
8 | */ 9 | public class BaseFlagsConfig { 10 | private final String projectToken; 11 | private final String apiHost; 12 | private final int requestTimeoutSeconds; 13 | 14 | /** 15 | * Creates a new BaseFlagsConfig with specified settings. 16 | * 17 | * @param projectToken the Mixpanel project token 18 | * @param apiHost the API endpoint host 19 | * @param requestTimeoutSeconds HTTP request timeout in seconds 20 | */ 21 | protected BaseFlagsConfig(String projectToken, String apiHost, int requestTimeoutSeconds) { 22 | this.projectToken = projectToken; 23 | this.apiHost = apiHost; 24 | this.requestTimeoutSeconds = requestTimeoutSeconds; 25 | } 26 | 27 | /** 28 | * @return the Mixpanel project token 29 | */ 30 | public String getProjectToken() { 31 | return projectToken; 32 | } 33 | 34 | /** 35 | * @return the API endpoint host 36 | */ 37 | public String getApiHost() { 38 | return apiHost; 39 | } 40 | 41 | /** 42 | * @return the HTTP request timeout in seconds 43 | */ 44 | public int getRequestTimeoutSeconds() { 45 | return requestTimeoutSeconds; 46 | } 47 | 48 | /** 49 | * Builder for BaseFlagsConfig. 50 | * 51 | * @param9 | * A rollout defines the percentage of users that should receive this experiment, 10 | * optional runtime evaluation criteria, and an optional variant override. 11 | *
12 | *13 | * This class is immutable and thread-safe. 14 | *
15 | */ 16 | public final class Rollout { 17 | private final float rolloutPercentage; 18 | private final Map8 | * Contains the selected variant key and its value. Both may be null if the 9 | * fallback was returned (e.g., flag not found, evaluation error). 10 | *
11 | *12 | * This class is immutable and thread-safe. 13 | *
14 | * 15 | * @param8 | * An experimentation flag contains metadata (id, name, key, status, project) 9 | * and the ruleset that defines how variants are assigned to users. 10 | *
11 | *12 | * This class is immutable and thread-safe. 13 | *
14 | */ 15 | public final class ExperimentationFlag { 16 | private final String id; 17 | private final String name; 18 | private final String key; 19 | private final String status; 20 | private final int projectId; 21 | private final RuleSet ruleset; 22 | private final String context; 23 | private final UUID experimentId; 24 | private final Boolean isExperimentActive; 25 | private final String hashSalt; 26 | 27 | /** 28 | * Creates a new ExperimentationFlag. 29 | * 30 | * @param id the unique identifier for this flag 31 | * @param name the human-readable name of this flag 32 | * @param key the key used to reference this flag in code 33 | * @param status the current status of this flag 34 | * @param projectId the Mixpanel project ID this flag belongs to 35 | * @param ruleset the ruleset defining variant assignment logic 36 | * @param context the property name used for rollout hashing (e.g., "distinct_id") 37 | * @param experimentId the experiment ID (may be null) 38 | * @param isExperimentActive whether the experiment is active (may be null) 39 | * @param hashSalt the hash salt for this flag (may be null for legacy flags) 40 | */ 41 | public ExperimentationFlag(String id, String name, String key, String status, int projectId, RuleSet ruleset, String context, UUID experimentId, Boolean isExperimentActive, String hashSalt) { 42 | this.id = id; 43 | this.name = name; 44 | this.key = key; 45 | this.status = status; 46 | this.projectId = projectId; 47 | this.ruleset = ruleset; 48 | this.context = context; 49 | this.experimentId = experimentId; 50 | this.isExperimentActive = isExperimentActive; 51 | this.hashSalt = hashSalt; 52 | } 53 | 54 | /** 55 | * @return the unique identifier for this flag 56 | */ 57 | public String getId() { 58 | return id; 59 | } 60 | 61 | /** 62 | * @return the human-readable name 63 | */ 64 | public String getName() { 65 | return name; 66 | } 67 | 68 | /** 69 | * @return the key used to reference this flag 70 | */ 71 | public String getKey() { 72 | return key; 73 | } 74 | 75 | /** 76 | * @return the current status 77 | */ 78 | public String getStatus() { 79 | return status; 80 | } 81 | 82 | /** 83 | * @return the project ID 84 | */ 85 | public int getProjectId() { 86 | return projectId; 87 | } 88 | 89 | /** 90 | * @return the ruleset defining variant assignment 91 | */ 92 | public RuleSet getRuleset() { 93 | return ruleset; 94 | } 95 | 96 | /** 97 | * @return the property name used for rollout hashing (e.g., "distinct_id") 98 | */ 99 | public String getContext() { 100 | return context; 101 | } 102 | 103 | /** 104 | * @return the experiment ID, or null if not set 105 | */ 106 | public UUID getExperimentId() { 107 | return experimentId; 108 | } 109 | 110 | /** 111 | * @return whether the experiment is active, or null if not set 112 | */ 113 | public Boolean getIsExperimentActive() { 114 | return isExperimentActive; 115 | } 116 | 117 | /** 118 | * @return the hash salt for this flag, or null for legacy flags 119 | */ 120 | public String getHashSalt() { 121 | return hashSalt; 122 | } 123 | 124 | @Override 125 | public String toString() { 126 | return "ExperimentationFlag{" + 127 | "id=" + id + 128 | ", name='" + name + '\'' + 129 | ", key='" + key + '\'' + 130 | ", status=" + status + 131 | ", projectId=" + projectId + 132 | ", ruleset=" + ruleset + 133 | ", context='" + context + '\'' + 134 | ", experimentId=" + experimentId + 135 | ", isExperimentActive=" + isExperimentActive + 136 | ", hashSalt='" + hashSalt + '\'' + 137 | '}'; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/com/mixpanel/mixpanelapi/DeliveryOptions.java: -------------------------------------------------------------------------------- 1 | package com.mixpanel.mixpanelapi; 2 | 3 | /** 4 | * Options for configuring how messages are delivered to Mixpanel. 5 | * Use the {@link Builder} to create instances. 6 | * 7 | *Different options apply to different message types: 8 | *
Example usage: 14 | *
{@code
15 | * DeliveryOptions options = new DeliveryOptions.Builder()
16 | * .importStrictMode(false) // Disable strict validation for imports
17 | * .useIpAddress(true) // Use IP address for geolocation (events/people/groups only)
18 | * .build();
19 | *
20 | * mixpanelApi.deliver(delivery, options);
21 | * }
22 | */
23 | public class DeliveryOptions {
24 |
25 | private final boolean mImportStrictMode;
26 | private final boolean mUseIpAddress;
27 |
28 | private DeliveryOptions(Builder builder) {
29 | mImportStrictMode = builder.importStrictMode;
30 | mUseIpAddress = builder.useIpAddress;
31 | }
32 |
33 | /**
34 | * Returns whether strict mode is enabled for import messages.
35 | *
36 | * Note: This option only applies to import messages (historical events). 37 | * It has no effect on regular events, people, or groups messages. 38 | * 39 | *
When strict mode is enabled (default), the /import endpoint validates each event 40 | * and returns a 400 error if any event has issues. Correctly formed events are still 41 | * ingested, and problematic events are returned in the response with error messages. 42 | * 43 | *
When strict mode is disabled, validation is bypassed and all events are imported 44 | * regardless of their validity. 45 | * 46 | * @return true if strict mode is enabled for imports, false otherwise 47 | */ 48 | public boolean isImportStrictMode() { 49 | return mImportStrictMode; 50 | } 51 | 52 | /** 53 | * Returns whether the IP address should be used for geolocation. 54 | * 55 | *
Note: This option only applies to events, people, and groups messages. 56 | * It does NOT apply to import messages, which use Basic Auth and don't support the ip parameter. 57 | * 58 | * @return true if IP address should be used for geolocation, false otherwise 59 | */ 60 | public boolean useIpAddress() { 61 | return mUseIpAddress; 62 | } 63 | 64 | /** 65 | * Builder for creating {@link DeliveryOptions} instances. 66 | */ 67 | public static class Builder { 68 | private boolean importStrictMode = true; 69 | private boolean useIpAddress = false; 70 | 71 | /** 72 | * Sets whether to use strict mode for import messages. 73 | * 74 | * will validate the supplied events and return a 400 status code if any of the events fail validation with details of the error 75 | * 76 | *
Setting this value to true (default) will validate the supplied events and return 77 | * a 400 status code if any of the events fail validation with details of the error. 78 | * Setting this value to false disables validation. 79 | * 80 | * @param importStrictMode true to enable strict validation (default), false to disable 81 | * @return this Builder instance for method chaining 82 | */ 83 | public Builder importStrictMode(boolean importStrictMode) { 84 | this.importStrictMode = importStrictMode; 85 | return this; 86 | } 87 | 88 | /** 89 | * Sets whether to use the IP address for geolocation. 90 | * 91 | *
Note: This option only applies to events, people, and groups messages. 92 | * It does NOT apply to import messages. 93 | * 94 | *
When enabled, Mixpanel will use the IP address of the request to set
95 | * geolocation properties on events and profiles.
96 | *
97 | * @param useIpAddress true to use IP address for geolocation, false otherwise (default)
98 | * @return this Builder instance for method chaining
99 | */
100 | public Builder useIpAddress(boolean useIpAddress) {
101 | this.useIpAddress = useIpAddress;
102 | return this;
103 | }
104 |
105 | /**
106 | * Builds and returns a new {@link DeliveryOptions} instance.
107 | *
108 | * @return a new DeliveryOptions with the configured settings
109 | */
110 | public DeliveryOptions build() {
111 | return new DeliveryOptions(this);
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |