diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/GmsGraphQLEngine.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/GmsGraphQLEngine.java index 1cd91e38a32e5..f7870071d6f09 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/GmsGraphQLEngine.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/GmsGraphQLEngine.java @@ -51,6 +51,7 @@ import com.linkedin.datahub.graphql.generated.RecommendationContent; import com.linkedin.datahub.graphql.generated.SearchAcrossLineageResult; import com.linkedin.datahub.graphql.generated.SearchResult; +import com.linkedin.datahub.graphql.generated.TrackingConfiguration; import com.linkedin.datahub.graphql.generated.UsageQueryResult; import com.linkedin.datahub.graphql.generated.UserUsageCounts; import com.linkedin.datahub.graphql.generated.VisualConfiguration; @@ -225,6 +226,7 @@ public class GmsGraphQLEngine { private final AuthenticationConfiguration authenticationConfiguration; private final AuthorizationConfiguration authorizationConfiguration; private final VisualConfiguration visualConfiguration; + private final TrackingConfiguration trackingConfiguration; private final DatasetType datasetType; private final CorpUserType corpUserType; @@ -292,6 +294,7 @@ public GmsGraphQLEngine() { null, null, false, + null, null); } @@ -311,7 +314,8 @@ public GmsGraphQLEngine( final AuthorizationConfiguration authorizationConfiguration, final GitVersion gitVersion, final boolean supportsImpactAnalysis, - final VisualConfiguration visualConfiguration + final VisualConfiguration visualConfiguration, + final TrackingConfiguration trackingConfiguration ) { this.entityClient = entityClient; @@ -332,6 +336,7 @@ public GmsGraphQLEngine( this.authenticationConfiguration = Objects.requireNonNull(authenticationConfiguration); this.authorizationConfiguration = Objects.requireNonNull(authorizationConfiguration); this.visualConfiguration = visualConfiguration; + this.trackingConfiguration = trackingConfiguration; this.datasetType = new DatasetType(entityClient); this.corpUserType = new CorpUserType(entityClient); @@ -569,7 +574,7 @@ private void configureQueryResolvers(final RuntimeWiring.Builder builder) { this.ingestionConfiguration, this.authenticationConfiguration, this.authorizationConfiguration, - supportsImpactAnalysis, this.visualConfiguration)) + supportsImpactAnalysis, this.visualConfiguration, this.trackingConfiguration)) .dataFetcher("me", new AuthenticatedResolver<>( new MeResolver(this.entityClient))) .dataFetcher("search", new AuthenticatedResolver<>( diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/config/AppConfigResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/config/AppConfigResolver.java index 8fda3c198c482..e6118747e1f7a 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/config/AppConfigResolver.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/config/AppConfigResolver.java @@ -13,6 +13,7 @@ import com.linkedin.datahub.graphql.generated.PoliciesConfig; import com.linkedin.datahub.graphql.generated.Privilege; import com.linkedin.datahub.graphql.generated.ResourcePrivileges; +import com.linkedin.datahub.graphql.generated.TrackingConfiguration; import com.linkedin.datahub.graphql.generated.VisualConfiguration; import com.linkedin.metadata.config.IngestionConfiguration; import com.linkedin.metadata.version.GitVersion; @@ -34,6 +35,7 @@ public class AppConfigResolver implements DataFetcher get(final DataFetchingEnvironment environmen appConfig.setManagedIngestionConfig(ingestionConfig); appConfig.setAuthConfig(authConfig); appConfig.setVisualConfig(_visualConfiguration); + appConfig.setTrackingConfig(_trackingConfiguration); return CompletableFuture.completedFuture(appConfig); } diff --git a/datahub-graphql-core/src/main/resources/app.graphql b/datahub-graphql-core/src/main/resources/app.graphql index a1ce09a6d71a1..ce142170961df 100644 --- a/datahub-graphql-core/src/main/resources/app.graphql +++ b/datahub-graphql-core/src/main/resources/app.graphql @@ -112,6 +112,11 @@ type AppConfig { Configurations related to visual appearance, allows styling the UI without rebuilding the bundle """ visualConfig: VisualConfiguration! + + """ + Configurations related to tracking users in the app + """ + trackingConfig: TrackingConfiguration! } """ @@ -124,6 +129,16 @@ type VisualConfiguration { logoUrl: String } +""" +Configurations related to tracking users in the app +""" +type TrackingConfiguration { + """ + Env variable for whether or not third party logging should be enabled for this instance + """ + isThirdPartyLoggingEnabled: Boolean +} + """ Configurations related to Lineage """ diff --git a/datahub-web-react/src/AppConfigProvider.tsx b/datahub-web-react/src/AppConfigProvider.tsx index 2213f2afad2d7..f46f9e334ef07 100644 --- a/datahub-web-react/src/AppConfigProvider.tsx +++ b/datahub-web-react/src/AppConfigProvider.tsx @@ -1,5 +1,7 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import './App.less'; +import { THIRD_PARTY_LOGGING_KEY } from './app/analytics/analytics'; +import { checkAuthStatus } from './app/auth/checkAuthStatus'; import { AppConfigContext, DEFAULT_APP_CONFIG } from './appConfigContext'; import { useAppConfigQuery } from './graphql/app.generated'; @@ -10,6 +12,17 @@ const AppConfigProvider = ({ children }: { children: React.ReactNode }) => { refetch(); }; + useEffect(() => { + if (appConfigData && appConfigData.appConfig) { + if (appConfigData.appConfig.trackingConfig.isThirdPartyLoggingEnabled) { + localStorage.setItem(THIRD_PARTY_LOGGING_KEY, 'true'); + checkAuthStatus(); // identify in analyitcs once we receive config response + } else { + localStorage.setItem(THIRD_PARTY_LOGGING_KEY, 'false'); + } + } + }, [appConfigData]); + return ( { + it('should update or create an options object with mixpanel set to the value of what is in localStorage', () => { + // before anything is set in localStorage + let trackingOptions = getMergedTrackingOptions(); + expect(trackingOptions).toMatchObject({ + plugins: { + mixpanel: false, + }, + }); + + localStorage.setItem(THIRD_PARTY_LOGGING_KEY, 'false'); + trackingOptions = getMergedTrackingOptions(); + expect(trackingOptions).toMatchObject({ + plugins: { + mixpanel: false, + }, + }); + + localStorage.setItem(THIRD_PARTY_LOGGING_KEY, 'true'); + trackingOptions = getMergedTrackingOptions(); + expect(trackingOptions).toMatchObject({ + plugins: { + mixpanel: true, + }, + }); + }); +}); diff --git a/datahub-web-react/src/app/analytics/analytics.ts b/datahub-web-react/src/app/analytics/analytics.ts index 11c1e7c6d5229..69daccac94dce 100644 --- a/datahub-web-react/src/app/analytics/analytics.ts +++ b/datahub-web-react/src/app/analytics/analytics.ts @@ -7,6 +7,8 @@ import { getBrowserId } from '../browserId'; const appName = 'datahub-react'; +export const THIRD_PARTY_LOGGING_KEY = 'isThirdPartyLoggingEnabled'; + const analytics = Analytics({ app: appName, plugins: plugins.filter((plugin) => plugin.isEnabled).map((plugin) => plugin.plugin), @@ -14,8 +16,16 @@ const analytics = Analytics({ const { NODE_ENV } = process.env; +export function getMergedTrackingOptions(options?: any) { + return { + ...options, + plugins: { + mixpanel: JSON.parse(localStorage.getItem(THIRD_PARTY_LOGGING_KEY) || 'false'), + }, + }; +} + export default { - ...analytics, page: (data?: PageData, options?: any, callback?: (...params: any[]) => any) => { const modifiedData = { ...data, @@ -29,7 +39,8 @@ export default { if (NODE_ENV === 'test') { return null; } - return analytics.page(modifiedData, options, callback); + const trackingOptions = getMergedTrackingOptions(options); + return analytics.page(modifiedData, trackingOptions, callback); }, event: (event: Event, options?: any, callback?: (...params: any[]) => any): Promise => { const eventTypeName = EventType[event.type]; @@ -45,6 +56,11 @@ export default { if (NODE_ENV === 'test') { return Promise.resolve(); } - return analytics.track(eventTypeName, modifiedEvent, options, callback); + const trackingOptions = getMergedTrackingOptions(options); + return analytics.track(eventTypeName, modifiedEvent, trackingOptions, callback); + }, + identify: (userId: string, traits?: any, options?: any, callback?: ((...params: any[]) => any) | undefined) => { + const trackingOptions = getMergedTrackingOptions(options); + return analytics.identify(userId, traits, trackingOptions, callback); }, }; diff --git a/datahub-web-react/src/appConfigContext.tsx b/datahub-web-react/src/appConfigContext.tsx index 0c0b3bde628b6..5f7f833fe4beb 100644 --- a/datahub-web-react/src/appConfigContext.tsx +++ b/datahub-web-react/src/appConfigContext.tsx @@ -25,6 +25,9 @@ export const DEFAULT_APP_CONFIG = { authConfig: { tokenAuthEnabled: false, }, + trackingConfig: { + isThirdPartyLoggingEnabled: false, + }, }; export const AppConfigContext = React.createContext<{ diff --git a/datahub-web-react/src/graphql/app.graphql b/datahub-web-react/src/graphql/app.graphql index 40f7cba9d69d5..1fc223192863c 100644 --- a/datahub-web-react/src/graphql/app.graphql +++ b/datahub-web-react/src/graphql/app.graphql @@ -37,6 +37,9 @@ query appConfig { visualConfig { logoUrl } + trackingConfig { + isThirdPartyLoggingEnabled + } } } diff --git a/metadata-service/factories/src/main/java/com/linkedin/gms/factory/common/TrackingConfigFactory.java b/metadata-service/factories/src/main/java/com/linkedin/gms/factory/common/TrackingConfigFactory.java new file mode 100644 index 0000000000000..1cf45bde268d4 --- /dev/null +++ b/metadata-service/factories/src/main/java/com/linkedin/gms/factory/common/TrackingConfigFactory.java @@ -0,0 +1,24 @@ +package com.linkedin.gms.factory.common; + +import com.linkedin.datahub.graphql.generated.TrackingConfiguration; +import javax.annotation.Nonnull; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +@Configuration +public class TrackingConfigFactory { + @Value("${trackingConfig.isThirdPartyLoggingEnabled}") + private Boolean isThirdPartyLoggingEnabled; + + @Nonnull + @Bean(name = "trackingConfig") + protected TrackingConfiguration getInstance() { + TrackingConfiguration config = new TrackingConfiguration(); + config.setIsThirdPartyLoggingEnabled(isThirdPartyLoggingEnabled); + + return config; + } +} diff --git a/metadata-service/factories/src/main/java/com/linkedin/gms/factory/graphql/GraphQLEngineFactory.java b/metadata-service/factories/src/main/java/com/linkedin/gms/factory/graphql/GraphQLEngineFactory.java index 6538f4af21fc7..0ac0e46585791 100644 --- a/metadata-service/factories/src/main/java/com/linkedin/gms/factory/graphql/GraphQLEngineFactory.java +++ b/metadata-service/factories/src/main/java/com/linkedin/gms/factory/graphql/GraphQLEngineFactory.java @@ -5,6 +5,7 @@ import com.linkedin.datahub.graphql.GraphQLEngine; import com.linkedin.datahub.graphql.analytics.service.AnalyticsService; import com.linkedin.datahub.graphql.generated.VisualConfiguration; +import com.linkedin.datahub.graphql.generated.TrackingConfiguration; import com.linkedin.entity.client.JavaEntityClient; import com.linkedin.gms.factory.auth.DataHubTokenServiceFactory; import com.linkedin.gms.factory.common.GitVersionFactory; @@ -97,6 +98,10 @@ public class GraphQLEngineFactory { @Qualifier("visualConfig") private VisualConfiguration _visualConfiguration; + @Autowired + @Qualifier("trackingConfig") + private TrackingConfiguration _trackingConfiguration; + @Value("${platformAnalytics.enabled}") // TODO: Migrate to DATAHUB_ANALYTICS_ENABLED private Boolean isAnalyticsEnabled; @@ -120,7 +125,8 @@ protected GraphQLEngine getInstance() { _configProvider.getAuthorization(), _gitVersion, _graphService.supportsMultiHop(), - _visualConfiguration + _visualConfiguration, + _trackingConfiguration ).builder().build(); } return new GmsGraphQLEngine( @@ -139,7 +145,8 @@ protected GraphQLEngine getInstance() { _configProvider.getAuthorization(), _gitVersion, _graphService.supportsMultiHop(), - _visualConfiguration + _visualConfiguration, + _trackingConfiguration ).builder().build(); } } diff --git a/metadata-service/factories/src/main/resources/application.yml b/metadata-service/factories/src/main/resources/application.yml index e3eb202dddac5..a7d138de9f76a 100644 --- a/metadata-service/factories/src/main/resources/application.yml +++ b/metadata-service/factories/src/main/resources/application.yml @@ -86,6 +86,9 @@ visualConfig: assets: logoUrl: ${REACT_APP_LOGO_URL:#{null}} +trackingConfig: + isThirdPartyLoggingEnabled: ${IS_THIRD_PARTY_LOGGING_ENABLED:false} + # Storage Layer ebean: username: ${EBEAN_DATASOURCE_USERNAME:datahub}