code: /*
   * Copyright (c) 2022 Airbyte, Inc., all rights reserved.
   */
  
  package io.airbyte.analytics;
  
  import com.google.common.annotations.VisibleForTesting;
  import com.google.common.base.Strings;
  import com.segment.analytics.Analytics;
  import com.segment.analytics.messages.AliasMessage;
  import com.segment.analytics.messages.IdentifyMessage;
  import com.segment.analytics.messages.TrackMessage;
  import io.airbyte.config.StandardWorkspace;
  import java.time.Instant;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.UUID;
  import java.util.function.Function;
  
  /**
   * This class is a wrapper around the Segment backend Java SDK.
   * <p>
   * In general, the Segment SDK events have two pieces to them, a top-level userId field and a map of
   * properties.
   * <p>
   * As of 2021/11/03, the top level userId field is standardised on the
   * {@link StandardWorkspace#getCustomerId()} field. This field is a random UUID generated when a
   * workspace model is created. This standardisation is through OSS Airbyte and Cloud Airbyte. This
   * join key now underpins Airbyte OSS Segment tracking. Although the id is meaningless and the name
   * confusing, it is not worth performing a migration at this time. Interested parties can look at
   * https://github.com/airbytehq/airbyte/issues/7456 for more context.
   * <p>
   * Consumers utilising this class must understand that the top-level userId field is subject to this
   * constraint.
   * <p>
   * See the following document for details on tracked events. Please update this document if tracked
   * events change.
   * https://docs.google.com/spreadsheets/d/1lGLmLIhiSPt_-oaEf3CpK-IxXnCO0NRHurvmWldoA2w/edit#gid=1567609168
   */
  public class SegmentTrackingClient implements TrackingClient {
  
    public static final String CUSTOMER_ID_KEY = "user_id";
    private static final String SEGMENT_WRITE_KEY = "7UDdp5K55CyiGgsauOr2pNNujGvmhaeu";
    private static final String AIRBYTE_VERSION_KEY = "airbyte_version";
    private static final String AIRBYTE_ROLE = "airbyte_role";
    private static final String AIRBYTE_TRACKED_AT = "tracked_at";
  
    // Analytics is threadsafe.
    private final Analytics analytics;
    private final Function<UUID, TrackingIdentity> identityFetcher;
    private final Deployment deployment;
    private final String airbyteRole;
  
    @VisibleForTesting
    SegmentTrackingClient(final Function<UUID, TrackingIdentity> identityFetcher,
                          final Deployment deployment,
                          final String airbyteRole,
                          final Analytics analytics) {
      this.identityFetcher = identityFetcher;
      this.deployment = deployment;
      this.analytics = analytics;
      this.airbyteRole = airbyteRole;
    }
  
    public SegmentTrackingClient(final Function<UUID, TrackingIdentity> identityFetcher,
                                 final Deployment deployment,
  
                                 final String airbyteRole) {
      this(identityFetcher, deployment, airbyteRole, Analytics.builder(SEGMENT_WRITE_KEY).build());
    }
  
    @Override
    public void identify(final UUID workspaceId) {
      final TrackingIdentity trackingIdentity = identityFetcher.apply(workspaceId);
      final Map<String, Object> identityMetadata = new HashMap<>();
  
      // deployment
      identityMetadata.put(AIRBYTE_VERSION_KEY, trackingIdentity.getAirbyteVersion().serialize());
      identityMetadata.put("deployment_mode", deployment.getDeploymentMode());
      identityMetadata.put("deployment_env", deployment.getDeploymentEnv());
      identityMetadata.put("deployment_id", deployment.getDeploymentId());
  
      // workspace (includes info that in the future we would store in an organization)
      identityMetadata.put("anonymized", trackingIdentity.isAnonymousDataCollection());
      identityMetadata.put("subscribed_newsletter", trackingIdentity.isNews());
      identityMetadata.put("subscribed_security", trackingIdentity.isSecurityUpdates());
      trackingIdentity.getEmail().ifPresent(email -> identityMetadata.put("email", email));
  
      // other
      if (!Strings.isNullOrEmpty(airbyteRole)) {
        identityMetadata.put(AIRBYTE_ROLE, airbyteRole);
      }
  
      final String joinKey = trackingIdentity.getCustomerId().toString();
      analytics.enqueue(IdentifyMessage.builder()
          // user id is scoped by workspace. there is no cross-workspace tracking.
          .userId(joinKey)
          .traits(identityMetadata));
    }
  
    @Override
    public void alias(final UUID workspaceId, final String previousCustomerId) {
      final var joinKey = identityFetcher.apply(workspaceId).getCustomerId().toString();
      analytics.enqueue(AliasMessage.builder(previousCustomerId).userId(joinKey));
    }
  
    @Override
    public void track(final UUID workspaceId, final String action) {
      track(workspaceId, action, Collections.emptyMap());
    }
  
    @Override
    public void track(final UUID workspaceId, final String action, final Map<String, Object> metadata) {
      final Map<String, Object> mapCopy = new HashMap<>(metadata);
      final TrackingIdentity trackingIdentity = identityFetcher.apply(workspaceId);
  
      // Always add these traits.
      mapCopy.put(AIRBYTE_VERSION_KEY, trackingIdentity.getAirbyteVersion().serialize());
      mapCopy.put(CUSTOMER_ID_KEY, trackingIdentity.getCustomerId());
      mapCopy.put(AIRBYTE_TRACKED_AT, Instant.now().toString());
      if (!metadata.isEmpty()) {
        trackingIdentity.getEmail().ifPresent(email -> mapCopy.put("email", email));
      }
  
      final var joinKey = trackingIdentity.getCustomerId().toString();
      analytics.enqueue(TrackMessage.builder(action)
          .userId(joinKey)
          .properties(mapCopy));
    }
  
  }
  