mirror of
https://github.com/datahub-project/datahub.git
synced 2025-06-27 05:03:31 +00:00
feat(telemetry): add telemetry events to the settings page (#6198)
This commit is contained in:
parent
09616ee2b3
commit
a58502c5dc
@ -27,6 +27,20 @@ export enum EventType {
|
||||
DownloadAsCsvEvent,
|
||||
SignUpEvent,
|
||||
ResetCredentialsEvent,
|
||||
CreateAccessTokenEvent,
|
||||
RevokeAccessTokenEvent,
|
||||
CreateGroupEvent,
|
||||
CreateInviteLinkEvent,
|
||||
CreateResetCredentialsLinkEvent,
|
||||
DeleteEntityEvent,
|
||||
SelectUserRoleEvent,
|
||||
BatchSelectUserRoleEvent,
|
||||
CreatePolicyEvent,
|
||||
UpdatePolicyEvent,
|
||||
DeactivatePolicyEvent,
|
||||
ActivatePolicyEvent,
|
||||
ShowSimplifiedHomepageEvent,
|
||||
ShowStandardHomepageEvent,
|
||||
}
|
||||
|
||||
/**
|
||||
@ -247,6 +261,74 @@ export interface DownloadAsCsvEvent extends BaseEvent {
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface CreateAccessTokenEvent extends BaseEvent {
|
||||
type: EventType.CreateAccessTokenEvent;
|
||||
accessTokenType: string;
|
||||
duration: string;
|
||||
}
|
||||
|
||||
export interface RevokeAccessTokenEvent extends BaseEvent {
|
||||
type: EventType.RevokeAccessTokenEvent;
|
||||
}
|
||||
|
||||
export interface CreateGroupEvent extends BaseEvent {
|
||||
type: EventType.CreateGroupEvent;
|
||||
}
|
||||
export interface CreateInviteLinkEvent extends BaseEvent {
|
||||
type: EventType.CreateInviteLinkEvent;
|
||||
roleUrn?: string;
|
||||
}
|
||||
|
||||
export interface CreateResetCredentialsLinkEvent extends BaseEvent {
|
||||
type: EventType.CreateResetCredentialsLinkEvent;
|
||||
userUrn: string;
|
||||
}
|
||||
|
||||
export interface DeleteEntityEvent extends BaseEvent {
|
||||
type: EventType.DeleteEntityEvent;
|
||||
entityUrn: string;
|
||||
entityType: EntityType;
|
||||
}
|
||||
|
||||
export interface SelectUserRoleEvent extends BaseEvent {
|
||||
type: EventType.SelectUserRoleEvent;
|
||||
roleUrn: string;
|
||||
userUrn: string;
|
||||
}
|
||||
|
||||
export interface BatchSelectUserRoleEvent extends BaseEvent {
|
||||
type: EventType.BatchSelectUserRoleEvent;
|
||||
roleUrn: string;
|
||||
userUrns: string[];
|
||||
}
|
||||
|
||||
export interface CreatePolicyEvent extends BaseEvent {
|
||||
type: EventType.CreatePolicyEvent;
|
||||
}
|
||||
|
||||
export interface UpdatePolicyEvent extends BaseEvent {
|
||||
type: EventType.UpdatePolicyEvent;
|
||||
policyUrn: string;
|
||||
}
|
||||
|
||||
export interface DeactivatePolicyEvent extends BaseEvent {
|
||||
type: EventType.DeactivatePolicyEvent;
|
||||
policyUrn: string;
|
||||
}
|
||||
|
||||
export interface ActivatePolicyEvent extends BaseEvent {
|
||||
type: EventType.ActivatePolicyEvent;
|
||||
policyUrn: string;
|
||||
}
|
||||
|
||||
export interface ShowSimplifiedHomepageEvent extends BaseEvent {
|
||||
type: EventType.ShowSimplifiedHomepageEvent;
|
||||
}
|
||||
|
||||
export interface ShowStandardHomepageEvent extends BaseEvent {
|
||||
type: EventType.ShowStandardHomepageEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event consisting of a union of specific event types.
|
||||
*/
|
||||
@ -272,4 +354,18 @@ export type Event =
|
||||
| DownloadAsCsvEvent
|
||||
| RecommendationClickEvent
|
||||
| HomePageRecommendationClickEvent
|
||||
| BatchEntityActionEvent;
|
||||
| BatchEntityActionEvent
|
||||
| CreateAccessTokenEvent
|
||||
| RevokeAccessTokenEvent
|
||||
| CreateGroupEvent
|
||||
| CreateInviteLinkEvent
|
||||
| CreateResetCredentialsLinkEvent
|
||||
| DeleteEntityEvent
|
||||
| SelectUserRoleEvent
|
||||
| BatchSelectUserRoleEvent
|
||||
| CreatePolicyEvent
|
||||
| UpdatePolicyEvent
|
||||
| DeactivatePolicyEvent
|
||||
| ActivatePolicyEvent
|
||||
| ShowSimplifiedHomepageEvent
|
||||
| ShowStandardHomepageEvent;
|
||||
|
@ -3,6 +3,7 @@ import { message, Modal } from 'antd';
|
||||
import { EntityType } from '../../../../types.generated';
|
||||
import { useEntityRegistry } from '../../../useEntityRegistry';
|
||||
import { getDeleteEntityMutation } from '../../../shared/deleteUtils';
|
||||
import analytics, { EventType } from '../../../analytics';
|
||||
|
||||
/**
|
||||
* Performs the flow for deleting an entity of a given type.
|
||||
@ -25,6 +26,11 @@ function useDeleteEntity(urn: string, type: EntityType, entityData: any, onDelet
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
analytics.event({
|
||||
type: EventType.DeleteEntityEvent,
|
||||
entityUrn: urn,
|
||||
entityType: type,
|
||||
});
|
||||
message.loading({
|
||||
content: 'Deleting...',
|
||||
duration: 2,
|
||||
|
@ -3,6 +3,7 @@ import { message, Button, Input, Modal, Typography, Form, Collapse } from 'antd'
|
||||
import { useCreateGroupMutation } from '../../../graphql/group.generated';
|
||||
import { useEnterKeyListener } from '../../shared/useEnterKeyListener';
|
||||
import { groupIdTextValidation } from '../../shared/textUtil';
|
||||
import analytics, { EventType } from '../../analytics';
|
||||
|
||||
type Props = {
|
||||
onClose: () => void;
|
||||
@ -27,6 +28,13 @@ export default function CreateGroupModal({ onClose, onCreate }: Props) {
|
||||
},
|
||||
},
|
||||
})
|
||||
.then(({ errors }) => {
|
||||
if (!errors) {
|
||||
analytics.event({
|
||||
type: EventType.CreateGroupEvent,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
message.destroy();
|
||||
message.error({ content: `Failed to create group!: \n ${e.message || ''}`, duration: 3 });
|
||||
|
@ -2,6 +2,7 @@ import React from 'react';
|
||||
import { message, Popconfirm } from 'antd';
|
||||
import { useBatchAssignRoleMutation } from '../../../graphql/mutations.generated';
|
||||
import { DataHubRole } from '../../../types.generated';
|
||||
import analytics, { EventType } from '../../analytics';
|
||||
|
||||
type Props = {
|
||||
visible: boolean;
|
||||
@ -34,6 +35,11 @@ export default function AssignRoleConfirmation({
|
||||
})
|
||||
.then(({ errors }) => {
|
||||
if (!errors) {
|
||||
analytics.event({
|
||||
type: EventType.SelectUserRoleEvent,
|
||||
roleUrn: roleToAssign.urn,
|
||||
userUrn,
|
||||
});
|
||||
message.success({
|
||||
content: `Assigned role ${roleToAssign?.name} to user ${username}!`,
|
||||
duration: 2,
|
||||
|
@ -10,6 +10,7 @@ import { DataHubRole } from '../../../types.generated';
|
||||
import { mapRoleIcon } from './UserUtils';
|
||||
import { useCreateInviteTokenMutation } from '../../../graphql/mutations.generated';
|
||||
import { ANTD_GRAY } from '../../entity/shared/constants';
|
||||
import analytics, { EventType } from '../../analytics';
|
||||
|
||||
const ModalSection = styled.div`
|
||||
display: flex;
|
||||
@ -138,6 +139,10 @@ export default function ViewInviteTokenModal({ visible, onClose }: Props) {
|
||||
})
|
||||
.then(({ data, errors }) => {
|
||||
if (!errors) {
|
||||
analytics.event({
|
||||
type: EventType.CreateInviteLinkEvent,
|
||||
roleUrn,
|
||||
});
|
||||
setInviteToken(data?.createInviteToken?.inviteToken || '');
|
||||
message.success('Generated new invite link');
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { RedoOutlined } from '@ant-design/icons';
|
||||
import { Button, Modal, Typography } from 'antd';
|
||||
import { Button, message, Modal, Typography } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { PageRoutes } from '../../../conf/Global';
|
||||
import { useCreateNativeUserResetTokenMutation } from '../../../graphql/user.generated';
|
||||
import analytics, { EventType } from '../../analytics';
|
||||
|
||||
const ModalSection = styled.div`
|
||||
display: flex;
|
||||
@ -43,9 +44,36 @@ export default function ViewResetTokenModal({ visible, userUrn, username, onClos
|
||||
const baseUrl = window.location.origin;
|
||||
const [hasGeneratedResetToken, setHasGeneratedResetToken] = useState(false);
|
||||
|
||||
const [createNativeUserResetToken, { data: createNativeUserResetTokenData }] =
|
||||
const [createNativeUserResetTokenMutation, { data: createNativeUserResetTokenData }] =
|
||||
useCreateNativeUserResetTokenMutation({});
|
||||
|
||||
const createNativeUserResetToken = () => {
|
||||
createNativeUserResetTokenMutation({
|
||||
variables: {
|
||||
input: {
|
||||
userUrn,
|
||||
},
|
||||
},
|
||||
})
|
||||
.then(({ errors }) => {
|
||||
if (!errors) {
|
||||
analytics.event({
|
||||
type: EventType.CreateResetCredentialsLinkEvent,
|
||||
userUrn,
|
||||
});
|
||||
setHasGeneratedResetToken(true);
|
||||
message.success('Generated new link to reset credentials');
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
message.destroy();
|
||||
message.error({
|
||||
content: `Failed to create new link to reset credentials : \n ${e.message || ''}`,
|
||||
duration: 3,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const resetToken = createNativeUserResetTokenData?.createNativeUserResetToken?.resetToken || '';
|
||||
|
||||
const inviteLink = `${baseUrl}${PageRoutes.RESET_CREDENTIALS}?reset_token=${resetToken}`;
|
||||
@ -86,20 +114,7 @@ export default function ViewResetTokenModal({ visible, userUrn, username, onClos
|
||||
<ModalSectionParagraph>
|
||||
Generate a new reset link! Note, any old links will <b>cease to be active</b>.
|
||||
</ModalSectionParagraph>
|
||||
<CreateResetTokenButton
|
||||
onClick={() => {
|
||||
createNativeUserResetToken({
|
||||
variables: {
|
||||
input: {
|
||||
userUrn,
|
||||
},
|
||||
},
|
||||
});
|
||||
setHasGeneratedResetToken(true);
|
||||
}}
|
||||
size="small"
|
||||
type="text"
|
||||
>
|
||||
<CreateResetTokenButton onClick={createNativeUserResetToken} size="small" type="text">
|
||||
<RedoOutlined style={{}} />
|
||||
</CreateResetTokenButton>
|
||||
</ModalSection>
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
PolicyMatchFilter,
|
||||
PolicyMatchFilterInput,
|
||||
PolicyMatchCriterionInput,
|
||||
EntityType,
|
||||
} from '../../../types.generated';
|
||||
import { useAppConfig } from '../../useAppConfig';
|
||||
import PolicyDetailsModal from './PolicyDetailsModal';
|
||||
@ -33,6 +34,7 @@ import { useEntityRegistry } from '../../useEntityRegistry';
|
||||
import { ANTD_GRAY } from '../../entity/shared/constants';
|
||||
import { SearchBar } from '../../search/SearchBar';
|
||||
import { scrollToTop } from '../../shared/searchUtils';
|
||||
import analytics, { EventType } from '../../analytics';
|
||||
|
||||
const SourceContainer = styled.div``;
|
||||
|
||||
@ -247,6 +249,11 @@ export const ManagePolicies = () => {
|
||||
content: `Are you sure you want to remove policy?`,
|
||||
onOk() {
|
||||
deletePolicy({ variables: { urn: policy?.urn as string } }); // There must be a focus policy urn.
|
||||
analytics.event({
|
||||
type: EventType.DeleteEntityEvent,
|
||||
entityUrn: policy?.urn,
|
||||
entityType: EntityType.DatahubPolicy,
|
||||
});
|
||||
message.success('Successfully removed policy.');
|
||||
setTimeout(function () {
|
||||
policiesRefetch();
|
||||
@ -285,9 +292,16 @@ export const ManagePolicies = () => {
|
||||
if (focusPolicyUrn) {
|
||||
// If there's an URN associated with the focused policy, then we are editing an existing policy.
|
||||
updatePolicy({ variables: { urn: focusPolicyUrn, input: toPolicyInput(savePolicy) } });
|
||||
analytics.event({
|
||||
type: EventType.UpdatePolicyEvent,
|
||||
policyUrn: focusPolicyUrn,
|
||||
});
|
||||
} else {
|
||||
// If there's no URN associated with the focused policy, then we are creating.
|
||||
createPolicy({ variables: { input: toPolicyInput(savePolicy) } });
|
||||
analytics.event({
|
||||
type: EventType.CreatePolicyEvent,
|
||||
});
|
||||
}
|
||||
message.success('Successfully saved policy.');
|
||||
setTimeout(function () {
|
||||
@ -369,7 +383,13 @@ export const ManagePolicies = () => {
|
||||
{record?.state === PolicyState.Active ? (
|
||||
<Button
|
||||
disabled={!record?.editable}
|
||||
onClick={() => onToggleActiveDuplicate(record?.policy)}
|
||||
onClick={() => {
|
||||
onToggleActiveDuplicate(record?.policy);
|
||||
analytics.event({
|
||||
type: EventType.DeactivatePolicyEvent,
|
||||
policyUrn: record?.policy?.urn,
|
||||
});
|
||||
}}
|
||||
style={{ color: record?.editable ? 'red' : ANTD_GRAY[6], width: 100 }}
|
||||
>
|
||||
DEACTIVATE
|
||||
@ -377,7 +397,13 @@ export const ManagePolicies = () => {
|
||||
) : (
|
||||
<Button
|
||||
disabled={!record?.editable}
|
||||
onClick={() => onToggleActiveDuplicate(record?.policy)}
|
||||
onClick={() => {
|
||||
onToggleActiveDuplicate(record?.policy);
|
||||
analytics.event({
|
||||
type: EventType.ActivatePolicyEvent,
|
||||
policyUrn: record?.policy?.urn,
|
||||
});
|
||||
}}
|
||||
style={{ color: record?.editable ? 'green' : ANTD_GRAY[6], width: 100 }}
|
||||
>
|
||||
ACTIVATE
|
||||
|
@ -15,6 +15,7 @@ import { EntityCapabilityType } from '../../entity/Entity';
|
||||
import { useBatchAssignRoleMutation } from '../../../graphql/mutations.generated';
|
||||
import { CorpUser, DataHubRole, DataHubPolicy } from '../../../types.generated';
|
||||
import RoleDetailsModal from './RoleDetailsModal';
|
||||
import analytics, { EventType } from '../../analytics';
|
||||
|
||||
const SourceContainer = styled.div``;
|
||||
|
||||
@ -99,6 +100,11 @@ export const ManageRoles = () => {
|
||||
})
|
||||
.then(({ errors }) => {
|
||||
if (!errors) {
|
||||
analytics.event({
|
||||
type: EventType.BatchSelectUserRoleEvent,
|
||||
roleUrn: focusRole?.urn,
|
||||
userUrns: actorUrns,
|
||||
});
|
||||
message.success({
|
||||
content: `Assigned Role to users!`,
|
||||
duration: 2,
|
||||
|
@ -14,6 +14,7 @@ import CreateTokenModal from './CreateTokenModal';
|
||||
import { useAppConfigQuery } from '../../graphql/app.generated';
|
||||
import { getLocaleTimezone } from '../shared/time/timeUtils';
|
||||
import { scrollToTop } from '../shared/searchUtils';
|
||||
import analytics, { EventType } from '../analytics';
|
||||
|
||||
const SourceContainer = styled.div`
|
||||
width: 100%;
|
||||
@ -130,7 +131,13 @@ export const AccessTokens = () => {
|
||||
// Hack to deal with eventual consistency.
|
||||
const newTokenIds = [...removedTokens, token.id];
|
||||
setRemovedTokens(newTokenIds);
|
||||
|
||||
revokeAccessToken({ variables: { tokenId: token.id } })
|
||||
.then(({ errors }) => {
|
||||
if (!errors) {
|
||||
analytics.event({ type: EventType.RevokeAccessTokenEvent });
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
message.destroy();
|
||||
message.error({ content: `Failed to revoke Token!: \n ${e.message || ''}`, duration: 3 });
|
||||
|
@ -8,6 +8,7 @@ import { ACCESS_TOKEN_DURATIONS, getTokenExpireDate } from './utils';
|
||||
import { useCreateAccessTokenMutation } from '../../graphql/auth.generated';
|
||||
import { AccessTokenDuration, AccessTokenType, CreateAccessTokenInput } from '../../types.generated';
|
||||
import { AccessTokenModal } from './AccessTokenModal';
|
||||
import analytics, { EventType } from '../analytics';
|
||||
|
||||
type Props = {
|
||||
currentUserUrn: string;
|
||||
@ -75,6 +76,15 @@ export default function CreateTokenModal({ currentUserUrn, visible, onClose, onC
|
||||
description: tokenDescription,
|
||||
};
|
||||
createAccessToken({ variables: { input } })
|
||||
.then(({ errors }) => {
|
||||
if (!errors) {
|
||||
analytics.event({
|
||||
type: EventType.CreateAccessTokenEvent,
|
||||
accessTokenType: AccessTokenType.Personal,
|
||||
duration: selectedTokenDuration,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
message.destroy();
|
||||
message.error({ content: `Failed to create Token!: \n ${e.message || ''}`, duration: 3 });
|
||||
|
@ -5,6 +5,7 @@ import { Divider, Typography, Switch, Card, message } from 'antd';
|
||||
import { useGetMeQuery, useUpdateUserSettingMutation } from '../../graphql/me.generated';
|
||||
import { UserSetting } from '../../types.generated';
|
||||
import { ANTD_GRAY } from '../entity/shared/constants';
|
||||
import analytics, { EventType } from '../analytics';
|
||||
|
||||
const Page = styled.div`
|
||||
width: 100%;
|
||||
@ -69,7 +70,7 @@ export const Preferences = () => {
|
||||
<Card>
|
||||
<UserSettingRow>
|
||||
<span>
|
||||
<SettingText>Show simplified hompeage </SettingText>
|
||||
<SettingText>Show simplified homepage </SettingText>
|
||||
<div>
|
||||
<DescriptionText>
|
||||
Limits entity browse cards on homepage to Domains, Charts, Datasets, Dashboards and
|
||||
@ -88,6 +89,11 @@ export const Preferences = () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
analytics.event({
|
||||
type: showSimplifiedHomepage
|
||||
? EventType.ShowStandardHomepageEvent
|
||||
: EventType.ShowSimplifiedHomepageEvent,
|
||||
});
|
||||
message.success({ content: 'Setting updated!', duration: 2 });
|
||||
refetch?.();
|
||||
}}
|
||||
|
Binary file not shown.
@ -49,11 +49,16 @@ public class TrackingService {
|
||||
private static final String RENDER_TYPE_FIELD = "renderType";
|
||||
private static final String SCENARIO_TYPE_FIELD = "scenarioType";
|
||||
private static final String SECTION_FIELD = "section";
|
||||
private static final String ACCESS_TOKEN_TYPE_FIELD = "accessTokenType";
|
||||
private static final String DURATION_FIELD = "duration";
|
||||
private static final String ROLE_URN_FIELD = "roleUrn";
|
||||
private static final String POLICY_URN_FIELD = "policyUrn";
|
||||
|
||||
private static final Set<String> ALLOWED_EVENT_FIELDS = new HashSet<>(
|
||||
ImmutableList.of(EVENT_TYPE_FIELD, SIGN_UP_TITLE_FIELD, ENTITY_TYPE_FIELD, ENTITY_TYPE_FILTER_FIELD,
|
||||
PAGE_NUMBER_FIELD, PAGE_FIELD, TOTAL_FIELD, INDEX_FIELD, RESULT_TYPE_FIELD, RENDER_ID_FIELD, MODULE_ID_FIELD,
|
||||
RENDER_TYPE_FIELD, SCENARIO_TYPE_FIELD, SECTION_FIELD));
|
||||
RENDER_TYPE_FIELD, SCENARIO_TYPE_FIELD, SECTION_FIELD, ACCESS_TOKEN_TYPE_FIELD, DURATION_FIELD,
|
||||
ROLE_URN_FIELD, POLICY_URN_FIELD));
|
||||
|
||||
private static final String ACTOR_URN_FIELD = "actorUrn";
|
||||
private static final String ORIGIN_FIELD = "origin";
|
||||
@ -62,9 +67,11 @@ public class TrackingService {
|
||||
private static final String GROUP_NAME_FIELD = "groupName";
|
||||
private static final String ENTITY_PAGE_FILTER_FIELD = "entityPageFilter";
|
||||
private static final String PATH_FIELD = "path";
|
||||
private static final String USER_URN_FIELD = "userUrn";
|
||||
private static final String USER_URNS_FIELD = "userUrns";
|
||||
private static final Set<String> ALLOWED_OBFUSCATED_EVENT_FIELDS = new HashSet<>(
|
||||
ImmutableList.of(ACTOR_URN_FIELD, ORIGIN_FIELD, ENTITY_URN_FIELD, ENTITY_URNS_FIELD, GROUP_NAME_FIELD,
|
||||
SECTION_FIELD, ENTITY_PAGE_FILTER_FIELD, PATH_FIELD));
|
||||
SECTION_FIELD, ENTITY_PAGE_FILTER_FIELD, PATH_FIELD, USER_URN_FIELD, USER_URNS_FIELD));
|
||||
|
||||
private final MixpanelAPI _mixpanelAPI;
|
||||
private final MessageBuilder _mixpanelMessageBuilder;
|
||||
|
Loading…
x
Reference in New Issue
Block a user