mirror of
https://github.com/ansible/awx.git
synced 2026-01-13 19:10:07 -03:30
Adds support for pendo initialization across the app
This commit is contained in:
parent
550a66553e
commit
3a56d2447c
@ -19,6 +19,7 @@ import { MeAPI, RootAPI } from '../../api';
|
||||
import { useConfig, useAuthorizedPath } from '../../contexts/Config';
|
||||
import { SESSION_TIMEOUT_KEY } from '../../constants';
|
||||
import { isAuthenticated } from '../../util/auth';
|
||||
import issuePendoIdentity from '../../util/issuePendoIdentity';
|
||||
import About from '../About';
|
||||
import AlertModal from '../AlertModal';
|
||||
import BrandLogo from './BrandLogo';
|
||||
@ -138,6 +139,13 @@ function AppContainer({ navRouteConfig = [], children }) {
|
||||
}
|
||||
}, [handleLogout, timeRemaining]);
|
||||
|
||||
useEffect(() => {
|
||||
if ('analytics_status' in config) {
|
||||
issuePendoIdentity(config, 'foobar');
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [config.analytics_status]);
|
||||
|
||||
const brandName = config?.license_info?.product_name;
|
||||
const alt = brandName ? t`${brandName} logo` : t`brand logo`;
|
||||
|
||||
|
||||
@ -4,19 +4,25 @@ import {
|
||||
mountWithContexts,
|
||||
waitForElement,
|
||||
} from '../../../testUtils/enzymeHelpers';
|
||||
import { ConfigAPI, MeAPI, RootAPI } from '../../api';
|
||||
import { MeAPI, RootAPI } from '../../api';
|
||||
import { useAuthorizedPath } from '../../contexts/Config';
|
||||
import AppContainer from './AppContainer';
|
||||
|
||||
jest.mock('../../api');
|
||||
jest.mock('../../util/bootstrapPendo');
|
||||
|
||||
global.pendo = {
|
||||
initialize: jest.fn(),
|
||||
};
|
||||
|
||||
describe('<AppContainer />', () => {
|
||||
const version = '222';
|
||||
|
||||
beforeEach(() => {
|
||||
ConfigAPI.read.mockResolvedValue({
|
||||
RootAPI.readAssetVariables.mockResolvedValue({
|
||||
data: {
|
||||
version,
|
||||
BRAND_NAME: 'AWX',
|
||||
PENDO_API_KEY: '',
|
||||
},
|
||||
});
|
||||
MeAPI.read.mockResolvedValue({ data: { results: [{}] } });
|
||||
@ -52,7 +58,22 @@ describe('<AppContainer />', () => {
|
||||
{routeConfig.map(({ groupId }) => (
|
||||
<div key={groupId} id={groupId} />
|
||||
))}
|
||||
</AppContainer>
|
||||
</AppContainer>,
|
||||
{
|
||||
context: {
|
||||
config: {
|
||||
analytics_status: 'detailed',
|
||||
ansible_version: null,
|
||||
custom_virtualenvs: [],
|
||||
version: '9000',
|
||||
me: { is_superuser: true },
|
||||
toJSON: () => '/config/',
|
||||
license_info: {
|
||||
valid_key: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
wrapper.update();
|
||||
@ -70,6 +91,31 @@ describe('<AppContainer />', () => {
|
||||
|
||||
expect(wrapper.find('#group_one').length).toBe(1);
|
||||
expect(wrapper.find('#group_two').length).toBe(1);
|
||||
|
||||
expect(global.pendo.initialize).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('expected content is rendered', async () => {
|
||||
let wrapper;
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<AppContainer />, {
|
||||
context: {
|
||||
config: {
|
||||
analytics_status: 'off',
|
||||
ansible_version: null,
|
||||
custom_virtualenvs: [],
|
||||
version: '9000',
|
||||
me: { is_superuser: true },
|
||||
toJSON: () => '/config/',
|
||||
license_info: {
|
||||
valid_key: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
wrapper.update();
|
||||
expect(global.pendo.initialize).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test('opening the about modal renders prefetched config data', async () => {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useContext, useEffect, useMemo } from 'react';
|
||||
import { useLocation, useRouteMatch } from 'react-router-dom';
|
||||
import { useRouteMatch } from 'react-router-dom';
|
||||
|
||||
import { t } from '@lingui/macro';
|
||||
|
||||
@ -22,8 +22,6 @@ export const useConfig = () => {
|
||||
};
|
||||
|
||||
export const ConfigProvider = ({ children }) => {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const {
|
||||
error: configError,
|
||||
isLoading,
|
||||
@ -48,10 +46,8 @@ export const ConfigProvider = ({ children }) => {
|
||||
const { error, dismissError } = useDismissableError(configError);
|
||||
|
||||
useEffect(() => {
|
||||
if (pathname !== '/login') {
|
||||
request();
|
||||
}
|
||||
}, [request, pathname]);
|
||||
request();
|
||||
}, [request]);
|
||||
|
||||
useEffect(() => {
|
||||
if (error?.response?.status === 401) {
|
||||
|
||||
@ -18,7 +18,6 @@ import ContentLoading from '../../../../components/ContentLoading';
|
||||
import ContentError from '../../../../components/ContentError';
|
||||
import { FormSubmitError } from '../../../../components/FormField';
|
||||
import { useConfig } from '../../../../contexts/Config';
|
||||
import issuePendoIdentity from './pendoUtils';
|
||||
import SubscriptionStep from './SubscriptionStep';
|
||||
import AnalyticsStep from './AnalyticsStep';
|
||||
import EulaStep from './EulaStep';
|
||||
@ -102,20 +101,18 @@ function SubscriptionEdit() {
|
||||
isLoading: isContentLoading,
|
||||
error: contentError,
|
||||
request: fetchContent,
|
||||
result: { brandName, pendoApiKey },
|
||||
result: { brandName },
|
||||
} = useRequest(
|
||||
useCallback(async () => {
|
||||
const {
|
||||
data: { BRAND_NAME, PENDO_API_KEY },
|
||||
data: { BRAND_NAME },
|
||||
} = await RootAPI.readAssetVariables();
|
||||
return {
|
||||
brandName: BRAND_NAME,
|
||||
pendoApiKey: PENDO_API_KEY,
|
||||
};
|
||||
}, []),
|
||||
{
|
||||
brandName: null,
|
||||
pendoApiKey: null,
|
||||
}
|
||||
);
|
||||
|
||||
@ -145,23 +142,11 @@ function SubscriptionEdit() {
|
||||
});
|
||||
}
|
||||
|
||||
const [
|
||||
{ data },
|
||||
{
|
||||
data: {
|
||||
results: [me],
|
||||
},
|
||||
},
|
||||
] = await Promise.all([ConfigAPI.read(), MeAPI.read()]);
|
||||
const newConfig = { ...data, me };
|
||||
setConfig(newConfig);
|
||||
|
||||
if (!hasValidKey) {
|
||||
if (form.pendo) {
|
||||
await SettingsAPI.updateCategory('ui', {
|
||||
PENDO_TRACKING_STATE: 'detailed',
|
||||
});
|
||||
await issuePendoIdentity(newConfig, pendoApiKey);
|
||||
} else {
|
||||
await SettingsAPI.updateCategory('ui', {
|
||||
PENDO_TRACKING_STATE: 'off',
|
||||
@ -178,6 +163,18 @@ function SubscriptionEdit() {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const [
|
||||
{ data },
|
||||
{
|
||||
data: {
|
||||
results: [me],
|
||||
},
|
||||
},
|
||||
] = await Promise.all([ConfigAPI.read(), MeAPI.read()]);
|
||||
const newConfig = { ...data, me };
|
||||
setConfig(newConfig);
|
||||
|
||||
return true;
|
||||
}, []) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
);
|
||||
|
||||
@ -14,7 +14,6 @@ import {
|
||||
} from '../../../../api';
|
||||
import SubscriptionEdit from './SubscriptionEdit';
|
||||
|
||||
jest.mock('./bootstrapPendo');
|
||||
jest.mock('../../../../api');
|
||||
|
||||
const mockConfig = {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Link, useHistory, useLocation } from 'react-router-dom';
|
||||
|
||||
import { t } from '@lingui/macro';
|
||||
import { Button } from '@patternfly/react-core';
|
||||
@ -19,6 +19,12 @@ import { SettingDetail } from '../../shared';
|
||||
function UIDetail() {
|
||||
const { me } = useConfig();
|
||||
const { GET: options } = useSettings();
|
||||
const history = useHistory();
|
||||
const { hardReload } = useLocation();
|
||||
|
||||
if (hardReload) {
|
||||
history.go();
|
||||
}
|
||||
|
||||
const { isLoading, error, request, result: ui } = useRequest(
|
||||
useCallback(async () => {
|
||||
|
||||
@ -49,9 +49,18 @@ function UIEdit() {
|
||||
useCallback(
|
||||
async values => {
|
||||
await SettingsAPI.updateAll(values);
|
||||
history.push('/settings/ui/details');
|
||||
if (
|
||||
values?.PENDO_TRACKING_STATE !== uiData?.PENDO_TRACKING_STATE?.value
|
||||
) {
|
||||
history.push({
|
||||
pathname: '/settings/ui/details',
|
||||
hardReload: true,
|
||||
});
|
||||
} else {
|
||||
history.push('/settings/ui/details');
|
||||
}
|
||||
},
|
||||
[history]
|
||||
[history, uiData]
|
||||
),
|
||||
null
|
||||
);
|
||||
|
||||
@ -111,6 +111,21 @@ describe('<UIEdit />', () => {
|
||||
wrapper.find('Form').invoke('onSubmit')();
|
||||
});
|
||||
expect(history.location.pathname).toEqual('/settings/ui/details');
|
||||
expect(history.location.hardReload).toEqual(undefined);
|
||||
});
|
||||
|
||||
test('should navigate to ui detail with reload param on successful submission where PENDO_TRACKING_STATE changes', async () => {
|
||||
act(() => {
|
||||
wrapper.find('select#PENDO_TRACKING_STATE').simulate('change', {
|
||||
target: { value: 'off', name: 'CUSTOM_LOGIN_INFO' },
|
||||
});
|
||||
});
|
||||
wrapper.update();
|
||||
await act(async () => {
|
||||
wrapper.find('Form').invoke('onSubmit')();
|
||||
});
|
||||
expect(history.location.pathname).toEqual('/settings/ui/details');
|
||||
expect(history.location.hardReload).toEqual(true);
|
||||
});
|
||||
|
||||
test('should navigate to ui detail when cancel is clicked', async () => {
|
||||
|
||||
@ -1,30 +1,26 @@
|
||||
import { UsersAPI } from '../../../../api';
|
||||
import { RootAPI, UsersAPI } from '../api';
|
||||
import bootstrapPendo from './bootstrapPendo';
|
||||
|
||||
function buildPendoOptions(config, pendoApiKey) {
|
||||
const tower_version = config.version.split('-')[0];
|
||||
const towerVersion = config.version.split('-')[0];
|
||||
const trial = config.trial ? config.trial : false;
|
||||
const options = {
|
||||
|
||||
return {
|
||||
apiKey: pendoApiKey,
|
||||
visitor: {
|
||||
id: null,
|
||||
id: 0,
|
||||
role: null,
|
||||
},
|
||||
account: {
|
||||
id: null,
|
||||
id: 'tower.ansible.com',
|
||||
planLevel: config.license_type,
|
||||
planPrice: config.instance_count,
|
||||
creationDate: config.license_date,
|
||||
trial,
|
||||
tower_version,
|
||||
tower_version: towerVersion,
|
||||
ansible_version: config.ansible_version,
|
||||
},
|
||||
};
|
||||
|
||||
options.visitor.id = 0;
|
||||
options.account.id = 'tower.ansible.com';
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
async function buildPendoOptionsRole(options, config) {
|
||||
@ -45,14 +41,20 @@ async function buildPendoOptionsRole(options, config) {
|
||||
}
|
||||
}
|
||||
|
||||
async function issuePendoIdentity(config, pendoApiKey) {
|
||||
async function issuePendoIdentity(config) {
|
||||
if (!('license_info' in config)) {
|
||||
config.license_info = {};
|
||||
}
|
||||
config.license_info.analytics_status = config.analytics_status;
|
||||
config.license_info.version = config.version;
|
||||
config.license_info.ansible_version = config.ansible_version;
|
||||
|
||||
if (config.analytics_status !== 'off') {
|
||||
bootstrapPendo(pendoApiKey);
|
||||
const pendoOptions = buildPendoOptions(config, pendoApiKey);
|
||||
const {
|
||||
data: { PENDO_API_KEY },
|
||||
} = await RootAPI.readAssetVariables();
|
||||
bootstrapPendo(PENDO_API_KEY);
|
||||
const pendoOptions = buildPendoOptions(config, PENDO_API_KEY);
|
||||
const pendoOptionsWithRole = await buildPendoOptionsRole(
|
||||
pendoOptions,
|
||||
config
|
||||
Loading…
x
Reference in New Issue
Block a user