refactor(ui): Make Navigating DataHub UI easier, fix duplicate tracking, duplicate networks calls, + misc optimizations (#7592)

Co-authored-by: david-leifker <114954101+david-leifker@users.noreply.github.com>
This commit is contained in:
John Joyce 2023-03-21 18:36:21 -07:00 committed by GitHub
parent bd76183b00
commit 4da49fb1b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 296 additions and 244 deletions

View File

@ -61,7 +61,19 @@ const errorLink = onError((error) => {
const client = new ApolloClient({ const client = new ApolloClient({
connectToDevTools: true, connectToDevTools: true,
link: errorLink.concat(httpLink), link: errorLink.concat(httpLink),
cache: new InMemoryCache(), cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
dataset: {
merge: (oldObj, newObj) => {
return { ...oldObj, ...newObj };
},
},
},
},
},
}),
credentials: 'include', credentials: 'include',
defaultOptions: { defaultOptions: {
watchQuery: { watchQuery: {

View File

@ -233,7 +233,6 @@ export interface BatchEntityActionEvent extends BaseEvent {
export interface RecommendationImpressionEvent extends BaseEvent { export interface RecommendationImpressionEvent extends BaseEvent {
type: EventType.RecommendationImpressionEvent; type: EventType.RecommendationImpressionEvent;
renderId: string; // TODO : Determine whether we need a render id to join with click event.
moduleId: string; moduleId: string;
renderType: RecommendationRenderType; renderType: RecommendationRenderType;
scenarioType: ScenarioType; scenarioType: ScenarioType;

View File

@ -10,7 +10,7 @@ import { Message } from '../../shared/Message';
import { useListDomainsQuery } from '../../../graphql/domain.generated'; import { useListDomainsQuery } from '../../../graphql/domain.generated';
import filterSearchQuery from '../../search/utils/filterSearchQuery'; import filterSearchQuery from '../../search/utils/filterSearchQuery';
import { ANTD_GRAY } from '../../entity/shared/constants'; import { ANTD_GRAY } from '../../entity/shared/constants';
import { useGetAuthenticatedUser } from '../../useGetAuthenticatedUser'; import { useUserContext } from '../../context/useUserContext';
const HighlightGroup = styled.div` const HighlightGroup = styled.div`
display: flex; display: flex;
@ -47,7 +47,7 @@ const StyledSearchBar = styled(Input)`
`; `;
export const AnalyticsPage = () => { export const AnalyticsPage = () => {
const me = useGetAuthenticatedUser(); const me = useUserContext();
const canManageDomains = me?.platformPrivileges?.createDomains; const canManageDomains = me?.platformPrivileges?.createDomains;
const { data: chartData, loading: chartLoading, error: chartError } = useGetAnalyticsChartsQuery(); const { data: chartData, loading: chartLoading, error: chartError } = useGetAnalyticsChartsQuery();
const { data: highlightData, loading: highlightLoading, error: highlightError } = useGetHighlightsQuery(); const { data: highlightData, loading: highlightLoading, error: highlightError } = useGetHighlightsQuery();

View File

@ -19,8 +19,8 @@ export function useInitialRedirect(state, localState, setState, setLocalState) {
...state, ...state,
loadedInitialPath: true, loadedInitialPath: true,
}); });
if (localState.selectedPath) { if (localState.selectedPath && !localState.selectedPath.includes(PageRoutes.EMBED)) {
history.push({ history.replace({
pathname: localState.selectedPath, pathname: localState.selectedPath,
search: localState.selectedSearch || '', search: localState.selectedSearch || '',
}); });
@ -40,7 +40,10 @@ export function useInitialRedirect(state, localState, setState, setLocalState) {
* When the location of the browse changes, save the latest to local state. * When the location of the browse changes, save the latest to local state.
*/ */
useEffect(() => { useEffect(() => {
if (localState.selectedPath !== location.pathname || localState.selectedSearch !== location.search) { if (
(localState.selectedPath !== location.pathname || localState.selectedSearch !== location.search) &&
!location.pathname.includes(PageRoutes.EMBED)
) {
setLocalState({ setLocalState({
...localState, ...localState,
selectedPath: location.pathname, selectedPath: location.pathname,

View File

@ -7,12 +7,12 @@ import useIsLineageMode from '../lineage/utils/useIsLineageMode';
import { useEntityRegistry } from '../useEntityRegistry'; import { useEntityRegistry } from '../useEntityRegistry';
import analytics, { EventType } from '../analytics'; import analytics, { EventType } from '../analytics';
import { decodeUrn } from './shared/utils'; import { decodeUrn } from './shared/utils';
import { useGetAuthenticatedUserUrn } from '../useGetAuthenticatedUser';
import { useGetGrantedPrivilegesQuery } from '../../graphql/policy.generated'; import { useGetGrantedPrivilegesQuery } from '../../graphql/policy.generated';
import { Message } from '../shared/Message'; import { Message } from '../shared/Message';
import { UnauthorizedPage } from '../authorization/UnauthorizedPage'; import { UnauthorizedPage } from '../authorization/UnauthorizedPage';
import { ErrorSection } from '../shared/error/ErrorSection'; import { ErrorSection } from '../shared/error/ErrorSection';
import { VIEW_ENTITY_PAGE } from './shared/constants'; import { VIEW_ENTITY_PAGE } from './shared/constants';
import { useUserContext } from '../context/useUserContext';
interface RouteParams { interface RouteParams {
urn: string; urn: string;
@ -33,14 +33,15 @@ export const EntityPage = ({ entityType }: Props) => {
const isBrowsable = entity.isBrowseEnabled(); const isBrowsable = entity.isBrowseEnabled();
const isLineageSupported = entity.isLineageEnabled(); const isLineageSupported = entity.isLineageEnabled();
const isLineageMode = useIsLineageMode(); const isLineageMode = useIsLineageMode();
const authenticatedUserUrn = useGetAuthenticatedUserUrn(); const authenticatedUserUrn = useUserContext()?.user?.urn;
const { loading, error, data } = useGetGrantedPrivilegesQuery({ const { loading, error, data } = useGetGrantedPrivilegesQuery({
variables: { variables: {
input: { input: {
actorUrn: authenticatedUserUrn, actorUrn: authenticatedUserUrn as string,
resourceSpec: { resourceType: entityType, resourceUrn: urn }, resourceSpec: { resourceType: entityType, resourceUrn: urn },
}, },
}, },
skip: !authenticatedUserUrn,
fetchPolicy: 'cache-first', fetchPolicy: 'cache-first',
}); });
const privileges = data?.getGrantedPrivileges?.privileges || []; const privileges = data?.getGrantedPrivileges?.privileges || [];

View File

@ -10,7 +10,6 @@ import { SidebarOwnerSection } from '../shared/containers/profile/sidebar/Owners
import { getDataForEntityType } from '../shared/containers/profile/utils'; import { getDataForEntityType } from '../shared/containers/profile/utils';
import { useGetContainerQuery } from '../../../graphql/container.generated'; import { useGetContainerQuery } from '../../../graphql/container.generated';
import { ContainerEntitiesTab } from './ContainerEntitiesTab'; import { ContainerEntitiesTab } from './ContainerEntitiesTab';
import { SidebarRecommendationsSection } from '../shared/containers/profile/sidebar/Recommendations/SidebarRecommendationsSection';
import { SidebarTagsSection } from '../shared/containers/profile/sidebar/SidebarTagsSection'; import { SidebarTagsSection } from '../shared/containers/profile/sidebar/SidebarTagsSection';
import { PropertiesTab } from '../shared/tabs/Properties/PropertiesTab'; import { PropertiesTab } from '../shared/tabs/Properties/PropertiesTab';
import { SidebarDomainSection } from '../shared/containers/profile/sidebar/Domain/SidebarDomainSection'; import { SidebarDomainSection } from '../shared/containers/profile/sidebar/Domain/SidebarDomainSection';
@ -99,9 +98,10 @@ export class ContainerEntity implements Entity<Container> {
{ {
component: SidebarDomainSection, component: SidebarDomainSection,
}, },
{ // TODO: Add back once entity-level recommendations are complete.
component: SidebarRecommendationsSection, // {
}, // component: SidebarRecommendationsSection,
// },
]} ]}
/> />
); );

View File

@ -18,7 +18,6 @@ import { LineageTab } from '../shared/tabs/Lineage/LineageTab';
import { capitalizeFirstLetterOnly } from '../../shared/textUtil'; import { capitalizeFirstLetterOnly } from '../../shared/textUtil';
import ViewDefinitionTab from '../shared/tabs/Dataset/View/ViewDefinitionTab'; import ViewDefinitionTab from '../shared/tabs/Dataset/View/ViewDefinitionTab';
import { SidebarViewDefinitionSection } from '../shared/containers/profile/sidebar/Dataset/View/SidebarViewDefinitionSection'; import { SidebarViewDefinitionSection } from '../shared/containers/profile/sidebar/Dataset/View/SidebarViewDefinitionSection';
import { SidebarRecommendationsSection } from '../shared/containers/profile/sidebar/Recommendations/SidebarRecommendationsSection';
import { getDataForEntityType } from '../shared/containers/profile/utils'; import { getDataForEntityType } from '../shared/containers/profile/utils';
import { SidebarDomainSection } from '../shared/containers/profile/sidebar/Domain/SidebarDomainSection'; import { SidebarDomainSection } from '../shared/containers/profile/sidebar/Domain/SidebarDomainSection';
import { ValidationsTab } from '../shared/tabs/Dataset/Validations/ValidationsTab'; import { ValidationsTab } from '../shared/tabs/Dataset/Validations/ValidationsTab';
@ -209,9 +208,10 @@ export class DatasetEntity implements Entity<Dataset> {
{ {
component: SidebarDomainSection, component: SidebarDomainSection,
}, },
{ // TODO: Add back once entity-level recommendations are complete.
component: SidebarRecommendationsSection, // {
}, // component: SidebarRecommendationsSection,
// },
]} ]}
/> />
); );

View File

@ -1,15 +1,19 @@
import React from 'react'; import React from 'react';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import { MockedProvider } from '@apollo/client/testing';
import { Properties } from '../../../shared/components/legacy/Properties'; import { Properties } from '../../../shared/components/legacy/Properties';
import { sampleProperties } from '../stories/properties'; import { sampleProperties } from '../stories/properties';
import TestPageContainer from '../../../../../utils/test-utils/TestPageContainer'; import TestPageContainer from '../../../../../utils/test-utils/TestPageContainer';
import { mocks } from '../../../../../Mocks';
describe('Properties', () => { describe('Properties', () => {
it('renders', () => { it('renders', () => {
const { getByText } = render( const { getByText } = render(
<TestPageContainer> <MockedProvider mocks={mocks} addTypename={false}>
<Properties properties={sampleProperties} />, <TestPageContainer>
</TestPageContainer>, <Properties properties={sampleProperties} />,
</TestPageContainer>
</MockedProvider>,
); );
expect(getByText('Properties')).toBeInTheDocument(); expect(getByText('Properties')).toBeInTheDocument();
expect(getByText('Number of Partitions')).toBeInTheDocument(); expect(getByText('Number of Partitions')).toBeInTheDocument();

View File

@ -1,14 +1,18 @@
import React from 'react'; import React from 'react';
import { fireEvent, render, waitFor } from '@testing-library/react'; import { fireEvent, render, waitFor } from '@testing-library/react';
import { MockedProvider } from '@apollo/client/testing';
import SchemaDescriptionField from '../schema/components/SchemaDescriptionField'; import SchemaDescriptionField from '../schema/components/SchemaDescriptionField';
import TestPageContainer from '../../../../../utils/test-utils/TestPageContainer'; import TestPageContainer from '../../../../../utils/test-utils/TestPageContainer';
import { mocks } from '../../../../../Mocks';
describe('SchemaDescriptionField', () => { describe('SchemaDescriptionField', () => {
it('renders editable description', async () => { it('renders editable description', async () => {
const { getByText, getByRole, queryByText } = render( const { getByText, getByRole, queryByText } = render(
<TestPageContainer> <MockedProvider mocks={mocks} addTypename={false}>
<SchemaDescriptionField description="test description updated" isEdited onUpdate={async () => {}} /> <TestPageContainer>
</TestPageContainer>, <SchemaDescriptionField description="test description updated" isEdited onUpdate={async () => {}} />
</TestPageContainer>
</MockedProvider>,
); );
expect(getByRole('img')).toBeInTheDocument(); expect(getByRole('img')).toBeInTheDocument();
expect(getByText('test description updated')).toBeInTheDocument(); expect(getByText('test description updated')).toBeInTheDocument();
@ -17,14 +21,16 @@ describe('SchemaDescriptionField', () => {
it('renders update description modal', async () => { it('renders update description modal', async () => {
const { getByText, getByRole, queryByText } = render( const { getByText, getByRole, queryByText } = render(
<TestPageContainer> <MockedProvider mocks={mocks} addTypename={false}>
<SchemaDescriptionField <TestPageContainer>
description="test description" <SchemaDescriptionField
original="test description" description="test description"
isEdited original="test description"
onUpdate={async () => {}} isEdited
/> onUpdate={async () => {}}
</TestPageContainer>, />
</TestPageContainer>
</MockedProvider>,
); );
expect(queryByText('Update description')).not.toBeInTheDocument(); expect(queryByText('Update description')).not.toBeInTheDocument();
fireEvent.click(getByRole('img')); fireEvent.click(getByRole('img'));

View File

@ -1,15 +1,19 @@
import React from 'react'; import React from 'react';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import { MockedProvider } from '@apollo/client/testing';
import SnapshotStatsView from '../stats/snapshot/SnapshotStatsView'; import SnapshotStatsView from '../stats/snapshot/SnapshotStatsView';
import TestPageContainer from '../../../../../utils/test-utils/TestPageContainer'; import TestPageContainer from '../../../../../utils/test-utils/TestPageContainer';
import { completeSampleProfile, missingFieldStatsProfile, missingTableStatsProfile } from '../stories/stats'; import { completeSampleProfile, missingFieldStatsProfile, missingTableStatsProfile } from '../stories/stats';
import { mocks } from '../../../../../Mocks';
describe('SnapshotStatsView', () => { describe('SnapshotStatsView', () => {
it('renders complete profile', () => { it('renders complete profile', () => {
const { getByText } = render( const { getByText } = render(
<TestPageContainer> <MockedProvider mocks={mocks}>
<SnapshotStatsView profile={completeSampleProfile} /> <TestPageContainer>
</TestPageContainer>, <SnapshotStatsView profile={completeSampleProfile} />
</TestPageContainer>
</MockedProvider>,
); );
// Row Count // Row Count
@ -53,9 +57,11 @@ describe('SnapshotStatsView', () => {
it('renders profile without field stats', () => { it('renders profile without field stats', () => {
const { getByText, queryByText } = render( const { getByText, queryByText } = render(
<TestPageContainer> <MockedProvider mocks={mocks}>
<SnapshotStatsView profile={missingFieldStatsProfile} /> <TestPageContainer>
</TestPageContainer>, <SnapshotStatsView profile={missingFieldStatsProfile} />
</TestPageContainer>
</MockedProvider>,
); );
// Row Count // Row Count
@ -99,9 +105,11 @@ describe('SnapshotStatsView', () => {
it('renders profile without table stats', () => { it('renders profile without table stats', () => {
const { getByText, queryByText } = render( const { getByText, queryByText } = render(
<TestPageContainer> <MockedProvider mocks={mocks}>
<SnapshotStatsView profile={missingTableStatsProfile} /> <TestPageContainer>
</TestPageContainer>, <SnapshotStatsView profile={missingTableStatsProfile} />
</TestPageContainer>
</MockedProvider>,
); );
// Row Count // Row Count

View File

@ -1,5 +1,7 @@
import { MockedProvider } from '@apollo/client/testing';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import React from 'react'; import React from 'react';
import { mocks } from '../../../../../Mocks';
import TestPageContainer from '../../../../../utils/test-utils/TestPageContainer'; import TestPageContainer from '../../../../../utils/test-utils/TestPageContainer';
import GlossaryTermHeader from '../GlossaryTermHeader'; import GlossaryTermHeader from '../GlossaryTermHeader';
@ -13,14 +15,16 @@ const glossaryTermHeaderData = {
describe('Glossary Term Header', () => { describe('Glossary Term Header', () => {
it('renders', () => { it('renders', () => {
const { getByText } = render( const { getByText } = render(
<TestPageContainer> <MockedProvider mocks={mocks}>
<GlossaryTermHeader <TestPageContainer>
definition={glossaryTermHeaderData.definition} <GlossaryTermHeader
termSource={glossaryTermHeaderData.termSource} definition={glossaryTermHeaderData.definition}
sourceRef={glossaryTermHeaderData.sourceRef} termSource={glossaryTermHeaderData.termSource}
fqdn={glossaryTermHeaderData.fqdn} sourceRef={glossaryTermHeaderData.sourceRef}
/> fqdn={glossaryTermHeaderData.fqdn}
</TestPageContainer>, />
</TestPageContainer>
</MockedProvider>,
); );
expect(getByText(glossaryTermHeaderData.definition)).toBeInTheDocument(); expect(getByText(glossaryTermHeaderData.definition)).toBeInTheDocument();
}); });

View File

@ -44,11 +44,7 @@ export default function GroupEditModal({ visible, onClose, onSave, editModalData
}, },
}, },
}) })
.catch((e) => { .then(() => {
message.destroy();
message.error({ content: `Failed to Save changes!: \n ${e.message || ''}`, duration: 3 });
})
.finally(() => {
message.success({ message.success({
content: `Changes saved.`, content: `Changes saved.`,
duration: 3, duration: 3,
@ -60,6 +56,10 @@ export default function GroupEditModal({ visible, onClose, onSave, editModalData
slack: '', slack: '',
urn: '', urn: '',
}); });
})
.catch((e) => {
message.destroy();
message.error({ content: `Failed to Save changes!: \n ${e.message || ''}`, duration: 3 });
}); });
onClose(); onClose();
}; };

View File

@ -21,7 +21,7 @@ import {
GroupsSection, GroupsSection,
} from '../shared/SidebarStyledComponents'; } from '../shared/SidebarStyledComponents';
import GroupMembersSideBarSection from './GroupMembersSideBarSection'; import GroupMembersSideBarSection from './GroupMembersSideBarSection';
import { useGetAuthenticatedUser } from '../../useGetAuthenticatedUser'; import { useUserContext } from '../../context/useUserContext';
const { Paragraph } = Typography; const { Paragraph } = Typography;
@ -103,8 +103,8 @@ export default function GroupInfoSidebar({ sideBarData, refetch }: Props) {
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
const [editGroupModal, showEditGroupModal] = useState(false); const [editGroupModal, showEditGroupModal] = useState(false);
const me = useGetAuthenticatedUser(); const me = useUserContext();
const canEditGroup = me?.platformPrivileges.manageIdentities; const canEditGroup = me?.platformPrivileges?.manageIdentities;
const [groupTitle, setGroupTitle] = useState(name); const [groupTitle, setGroupTitle] = useState(name);
const [updateName] = useUpdateNameMutation(); const [updateName] = useUpdateNameMutation();
@ -145,16 +145,16 @@ export default function GroupInfoSidebar({ sideBarData, refetch }: Props) {
}, },
}, },
}) })
.catch((e) => { .then(() => {
message.destroy();
message.error({ content: `Failed to Save changes!: \n ${e.message || ''}`, duration: 3 });
})
.finally(() => {
message.success({ message.success({
content: `Changes saved.`, content: `Changes saved.`,
duration: 3, duration: 3,
}); });
refetch(); refetch();
})
.catch((e) => {
message.destroy();
message.error({ content: `Failed to Save changes!: \n ${e.message || ''}`, duration: 3 });
}); });
}; };
return ( return (
@ -220,7 +220,7 @@ export default function GroupInfoSidebar({ sideBarData, refetch }: Props) {
<GroupMembersSideBarSection <GroupMembersSideBarSection
total={groupMemberRelationships?.total || 0} total={groupMemberRelationships?.total || 0}
relationships={groupMemberRelationships?.relationships || []} relationships={groupMemberRelationships?.relationships || []}
onSeeMore={() => history.push(`${url}/members`)} onSeeMore={() => history.replace(`${url}/members`)}
/> />
</GroupsSection> </GroupsSection>
</SideBarSubSection> </SideBarSubSection>

View File

@ -1,10 +1,10 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { message, Modal, Button, Form, Input } from 'antd'; import { message, Modal, Button, Form, Input } from 'antd';
import { PlusOutlined } from '@ant-design/icons'; import { PlusOutlined } from '@ant-design/icons';
import { useGetAuthenticatedUser } from '../../../../useGetAuthenticatedUser';
import { useEntityData, useMutationUrn } from '../../EntityContext'; import { useEntityData, useMutationUrn } from '../../EntityContext';
import { useAddLinkMutation } from '../../../../../graphql/mutations.generated'; import { useAddLinkMutation } from '../../../../../graphql/mutations.generated';
import analytics, { EventType, EntityActionType } from '../../../../analytics'; import analytics, { EventType, EntityActionType } from '../../../../analytics';
import { useUserContext } from '../../../../context/useUserContext';
type AddLinkProps = { type AddLinkProps = {
buttonProps?: Record<string, unknown>; buttonProps?: Record<string, unknown>;
@ -14,7 +14,7 @@ type AddLinkProps = {
export const AddLinkModal = ({ buttonProps, refetch }: AddLinkProps) => { export const AddLinkModal = ({ buttonProps, refetch }: AddLinkProps) => {
const [isModalVisible, setIsModalVisible] = useState(false); const [isModalVisible, setIsModalVisible] = useState(false);
const mutationUrn = useMutationUrn(); const mutationUrn = useMutationUrn();
const user = useGetAuthenticatedUser(); const user = useUserContext();
const { entityType } = useEntityData(); const { entityType } = useEntityData();
const [addLinkMutation] = useAddLinkMutation(); const [addLinkMutation] = useAddLinkMutation();
@ -30,7 +30,7 @@ export const AddLinkModal = ({ buttonProps, refetch }: AddLinkProps) => {
}; };
const handleAdd = async (formData: any) => { const handleAdd = async (formData: any) => {
if (user?.corpUser.urn) { if (user?.urn) {
try { try {
await addLinkMutation({ await addLinkMutation({
variables: { input: { linkUrl: formData.url, label: formData.label, resourceUrn: mutationUrn } }, variables: { input: { linkUrl: formData.url, label: formData.label, resourceUrn: mutationUrn } },

View File

@ -39,7 +39,7 @@ export const navigateToEntitySearchUrl = ({
{ arrayFormat: 'comma' }, { arrayFormat: 'comma' },
); );
history.push({ history.replace({
pathname: `${baseUrl}`, pathname: `${baseUrl}`,
search, search,
}); });

View File

@ -182,7 +182,7 @@ export const EntityProfile = <T, U>({
({ ({
tabName, tabName,
tabParams, tabParams,
method = 'push', method = 'replace',
}: { }: {
tabName: string; tabName: string;
tabParams?: Record<string, any>; tabParams?: Record<string, any>;

View File

@ -5,7 +5,6 @@ import { EntityHealthStatus } from './EntityHealthStatus';
import EntityDropdown, { EntityMenuItems } from '../../../EntityDropdown/EntityDropdown'; import EntityDropdown, { EntityMenuItems } from '../../../EntityDropdown/EntityDropdown';
import PlatformContent from './PlatformContent'; import PlatformContent from './PlatformContent';
import { getPlatformName } from '../../../utils'; import { getPlatformName } from '../../../utils';
import { useGetAuthenticatedUser } from '../../../../../useGetAuthenticatedUser';
import { EntityType, PlatformPrivileges } from '../../../../../../types.generated'; import { EntityType, PlatformPrivileges } from '../../../../../../types.generated';
import EntityCount from './EntityCount'; import EntityCount from './EntityCount';
import EntityName from './EntityName'; import EntityName from './EntityName';
@ -16,6 +15,7 @@ import EntityActions, { EntityActionItem } from '../../../entity/EntityActions';
import ExternalUrlButton from '../../../ExternalUrlButton'; import ExternalUrlButton from '../../../ExternalUrlButton';
import ShareButton from '../../../../../shared/share/ShareButton'; import ShareButton from '../../../../../shared/share/ShareButton';
import { capitalizeFirstLetterOnly } from '../../../../../shared/textUtil'; import { capitalizeFirstLetterOnly } from '../../../../../shared/textUtil';
import { useUserContext } from '../../../../../context/useUserContext';
const TitleWrapper = styled.div` const TitleWrapper = styled.div`
display: flex; display: flex;
@ -81,7 +81,7 @@ type Props = {
export const EntityHeader = ({ headerDropdownItems, headerActionItems, isNameEditable, subHeader }: Props) => { export const EntityHeader = ({ headerDropdownItems, headerActionItems, isNameEditable, subHeader }: Props) => {
const { urn, entityType, entityData } = useEntityData(); const { urn, entityType, entityData } = useEntityData();
const refetch = useRefetch(); const refetch = useRefetch();
const me = useGetAuthenticatedUser(); const me = useUserContext();
const platformName = getPlatformName(entityData); const platformName = getPlatformName(entityData);
const externalUrl = entityData?.externalUrl || undefined; const externalUrl = entityData?.externalUrl || undefined;
const entityCount = entityData?.entityCount; const entityCount = entityData?.entityCount;

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { useGetAuthenticatedUser } from '../../../../../../useGetAuthenticatedUser'; import { useUserContext } from '../../../../../../context/useUserContext';
import { useEntityData } from '../../../../EntityContext'; import { useEntityData } from '../../../../EntityContext';
import { SidebarEntityRecommendations } from './SidebarEntityRecommendations'; import { SidebarEntityRecommendations } from './SidebarEntityRecommendations';
@ -8,7 +8,7 @@ const RecommendationsContainer = styled.div``;
export const SidebarRecommendationsSection = () => { export const SidebarRecommendationsSection = () => {
const { urn, entityType } = useEntityData(); const { urn, entityType } = useEntityData();
const authenticatedUserUrn = useGetAuthenticatedUser()?.corpUser?.urn; const authenticatedUserUrn = useUserContext()?.user?.urn;
return ( return (
<RecommendationsContainer> <RecommendationsContainer>
{authenticatedUserUrn && ( {authenticatedUserUrn && (

View File

@ -109,7 +109,7 @@ export const SchemaTab = ({ properties }: { properties?: any }) => {
} }
const { data: getSchemaBlameData } = useGetSchemaBlameQuery({ const { data: getSchemaBlameData } = useGetSchemaBlameQuery({
skip: !datasetUrn, skip: !datasetUrn || !selectedVersion,
variables: { variables: {
input: { input: {
datasetUrn, datasetUrn,

View File

@ -13,7 +13,7 @@ export default function useUpdateSchemaFilterQueryString(filterText: string) {
const stringifiedParams = QueryString.stringify(newParams, { arrayFormat: 'comma' }); const stringifiedParams = QueryString.stringify(newParams, { arrayFormat: 'comma' });
useEffect(() => { useEffect(() => {
history.push({ history.replace({
pathname: location.pathname, pathname: location.pathname,
search: stringifiedParams, search: stringifiedParams,
}); });

View File

@ -3,6 +3,10 @@ import { useSearchAcrossLineageQuery } from '../../../../../graphql/search.gener
import { LineageDirection } from '../../../../../types.generated'; import { LineageDirection } from '../../../../../types.generated';
import { GetSearchResultsParams } from '../../components/styled/search/types'; import { GetSearchResultsParams } from '../../components/styled/search/types';
const filtersExist = (filters, orFilters) => {
return filters?.length || orFilters?.length;
};
export default function generateUseSearchResultsViaRelationshipHook({ export default function generateUseSearchResultsViaRelationshipHook({
urn, urn,
direction, direction,
@ -41,6 +45,7 @@ export default function generateUseSearchResultsViaRelationshipHook({
variables: { variables: {
input: inputFields, input: inputFields,
}, },
skip: !filtersExist(filters, orFilters), // If you don't include any filters, we shound't return anything :). Might as well skip!
}); });
useEffect(() => { useEffect(() => {

View File

@ -59,11 +59,7 @@ export default function UserEditProfileModal({ visible, onClose, onSave, editMod
}, },
}, },
}) })
.catch((e) => { .then(() => {
message.destroy();
message.error({ content: `Failed to Save changes!: \n ${e.message || ''}`, duration: 3 });
})
.finally(() => {
message.success({ message.success({
content: `Changes saved.`, content: `Changes saved.`,
duration: 3, duration: 3,
@ -80,6 +76,10 @@ export default function UserEditProfileModal({ visible, onClose, onSave, editMod
phone: '', phone: '',
urn: '', urn: '',
}); });
})
.catch((e) => {
message.destroy();
message.error({ content: `Failed to Save changes!: \n ${e.message || ''}`, duration: 3 });
}); });
onClose(); onClose();
}; };

View File

@ -5,7 +5,6 @@ import { useUpdateCorpUserPropertiesMutation } from '../../../graphql/user.gener
import { EntityRelationship, DataHubRole } from '../../../types.generated'; import { EntityRelationship, DataHubRole } from '../../../types.generated';
import UserEditProfileModal from './UserEditProfileModal'; import UserEditProfileModal from './UserEditProfileModal';
import CustomAvatar from '../../shared/avatar/CustomAvatar'; import CustomAvatar from '../../shared/avatar/CustomAvatar';
import { useGetAuthenticatedUser } from '../../useGetAuthenticatedUser';
import { import {
SideBar, SideBar,
SideBarSubSection, SideBarSubSection,
@ -21,6 +20,7 @@ import {
} from '../shared/SidebarStyledComponents'; } from '../shared/SidebarStyledComponents';
import EntityGroups from '../shared/EntityGroups'; import EntityGroups from '../shared/EntityGroups';
import { mapRoleIcon } from '../../identity/user/UserUtils'; import { mapRoleIcon } from '../../identity/user/UserUtils';
import { useUserContext } from '../../context/useUserContext';
const { Paragraph } = Typography; const { Paragraph } = Typography;
@ -58,8 +58,8 @@ export default function UserInfoSideBar({ sideBarData, refetch }: Props) {
const [groupSectionExpanded, setGroupSectionExpanded] = useState(false); const [groupSectionExpanded, setGroupSectionExpanded] = useState(false);
const [editProfileModal, showEditProfileModal] = useState(false); const [editProfileModal, showEditProfileModal] = useState(false);
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
const me = useGetAuthenticatedUser(); const me = useUserContext();
const isProfileOwner = me?.corpUser?.urn === urn; const isProfileOwner = me?.user?.urn === urn;
const getEditModalData = { const getEditModalData = {
urn, urn,
@ -82,16 +82,16 @@ export default function UserInfoSideBar({ sideBarData, refetch }: Props) {
}, },
}, },
}) })
.catch((e) => { .then(() => {
message.destroy();
message.error({ content: `Failed to Save changes!: \n ${e.message || ''}`, duration: 3 });
})
.finally(() => {
message.success({ message.success({
content: `Changes saved.`, content: `Changes saved.`,
duration: 3, duration: 3,
}); });
refetch(); refetch();
})
.catch((e) => {
message.destroy();
message.error({ content: `Failed to Save changes!: \n ${e.message || ''}`, duration: 3 });
}); });
}; };
const dataHubRoleName = dataHubRoles && dataHubRoles.length > 0 && (dataHubRoles[0]?.entity as DataHubRole).name; const dataHubRoleName = dataHubRoles && dataHubRoles.length > 0 && (dataHubRoles[0]?.entity as DataHubRole).name;

View File

@ -12,7 +12,6 @@ import { Message } from '../shared/Message';
import { sortGlossaryTerms } from '../entity/glossaryTerm/utils'; import { sortGlossaryTerms } from '../entity/glossaryTerm/utils';
import { useEntityRegistry } from '../useEntityRegistry'; import { useEntityRegistry } from '../useEntityRegistry';
import { sortGlossaryNodes } from '../entity/glossaryNode/utils'; import { sortGlossaryNodes } from '../entity/glossaryNode/utils';
import { useGetAuthenticatedUser } from '../useGetAuthenticatedUser';
import { import {
BUSINESS_GLOSSARY_INTRO_ID, BUSINESS_GLOSSARY_INTRO_ID,
BUSINESS_GLOSSARY_CREATE_TERM_ID, BUSINESS_GLOSSARY_CREATE_TERM_ID,
@ -20,6 +19,7 @@ import {
} from '../onboarding/config/BusinessGlossaryOnboardingConfig'; } from '../onboarding/config/BusinessGlossaryOnboardingConfig';
import { OnboardingTour } from '../onboarding/OnboardingTour'; import { OnboardingTour } from '../onboarding/OnboardingTour';
import { useGlossaryEntityData } from '../entity/shared/GlossaryEntityContext'; import { useGlossaryEntityData } from '../entity/shared/GlossaryEntityContext';
import { useUserContext } from '../context/useUserContext';
export const HeaderWrapper = styled(TabToolbar)` export const HeaderWrapper = styled(TabToolbar)`
padding: 15px 45px 10px 24px; padding: 15px 45px 10px 24px;
@ -79,7 +79,7 @@ function BusinessGlossaryPage() {
const [isCreateTermModalVisible, setIsCreateTermModalVisible] = useState(false); const [isCreateTermModalVisible, setIsCreateTermModalVisible] = useState(false);
const [isCreateNodeModalVisible, setIsCreateNodeModalVisible] = useState(false); const [isCreateNodeModalVisible, setIsCreateNodeModalVisible] = useState(false);
const user = useGetAuthenticatedUser(); const user = useUserContext();
const canManageGlossaries = user?.platformPrivileges?.manageGlossaries; const canManageGlossaries = user?.platformPrivileges?.manageGlossaries;
return ( return (

View File

@ -4,8 +4,8 @@ import React, { useState } from 'react';
import styled from 'styled-components/macro'; import styled from 'styled-components/macro';
import { EntityType } from '../../types.generated'; import { EntityType } from '../../types.generated';
import { useEntityData } from '../entity/shared/EntityContext'; import { useEntityData } from '../entity/shared/EntityContext';
import { useGetAuthenticatedUser } from '../useGetAuthenticatedUser';
import CreateGlossaryEntityModal from '../entity/shared/EntityDropdown/CreateGlossaryEntityModal'; import CreateGlossaryEntityModal from '../entity/shared/EntityDropdown/CreateGlossaryEntityModal';
import { useUserContext } from '../context/useUserContext';
const StyledEmpty = styled(Empty)` const StyledEmpty = styled(Empty)`
padding: 80px 40px; padding: 80px 40px;
@ -33,7 +33,7 @@ function EmptyGlossarySection(props: Props) {
const [isCreateTermModalVisible, setIsCreateTermModalVisible] = useState(false); const [isCreateTermModalVisible, setIsCreateTermModalVisible] = useState(false);
const [isCreateNodeModalVisible, setIsCreateNodeModalVisible] = useState(false); const [isCreateNodeModalVisible, setIsCreateNodeModalVisible] = useState(false);
const user = useGetAuthenticatedUser(); const user = useUserContext();
const canManageGlossaries = user?.platformPrivileges?.manageGlossaries; const canManageGlossaries = user?.platformPrivileges?.manageGlossaries;
const { entityData } = useEntityData(); const { entityData } = useEntityData();
const canCreateGlossaryEntity = !!entityData?.privileges?.canManageChildren || canManageGlossaries; const canCreateGlossaryEntity = !!entityData?.privileges?.canManageChildren || canManageGlossaries;

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { useEffect } from 'react';
import { HomePageHeader } from './HomePageHeader'; import { HomePageHeader } from './HomePageHeader';
import { HomePageBody } from './HomePageBody'; import { HomePageBody } from './HomePageBody';
import analytics, { EventType } from '../analytics'; import analytics, { EventType } from '../analytics';
@ -13,7 +13,9 @@ import {
} from '../onboarding/config/HomePageOnboardingConfig'; } from '../onboarding/config/HomePageOnboardingConfig';
export const HomePage = () => { export const HomePage = () => {
analytics.event({ type: EventType.HomePageViewEvent }); useEffect(() => {
analytics.event({ type: EventType.HomePageViewEvent });
}, []);
return ( return (
<> <>
<OnboardingTour <OnboardingTour

View File

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { CorpUser } from '../../types.generated'; import { useUserContext } from '../context/useUserContext';
import { useGetAuthenticatedUser } from '../useGetAuthenticatedUser';
import { HomePageRecommendations } from './HomePageRecommendations'; import { HomePageRecommendations } from './HomePageRecommendations';
const BodyContainer = styled.div` const BodyContainer = styled.div`
@ -18,6 +17,6 @@ const BodyContainer = styled.div`
`; `;
export const HomePageBody = () => { export const HomePageBody = () => {
const user: CorpUser = useGetAuthenticatedUser()?.corpUser as CorpUser; const user = useUserContext()?.user;
return <BodyContainer>{user && <HomePageRecommendations user={user} />}</BodyContainer>; return <BodyContainer>{user && <HomePageRecommendations user={user} />}</BodyContainer>;
}; };

View File

@ -4,7 +4,6 @@ import { Typography, Image, Row, Button, Tag } from 'antd';
import styled, { useTheme } from 'styled-components/macro'; import styled, { useTheme } from 'styled-components/macro';
import { RightOutlined } from '@ant-design/icons'; import { RightOutlined } from '@ant-design/icons';
import { ManageAccount } from '../shared/ManageAccount'; import { ManageAccount } from '../shared/ManageAccount';
import { useGetAuthenticatedUser } from '../useGetAuthenticatedUser';
import { useEntityRegistry } from '../useEntityRegistry'; import { useEntityRegistry } from '../useEntityRegistry';
import { navigateToSearchUrl } from '../search/utils/navigateToSearchUrl'; import { navigateToSearchUrl } from '../search/utils/navigateToSearchUrl';
import { SearchBar } from '../search/SearchBar'; import { SearchBar } from '../search/SearchBar';
@ -20,6 +19,7 @@ import { ANTD_GRAY } from '../entity/shared/constants';
import { useAppConfig } from '../useAppConfig'; import { useAppConfig } from '../useAppConfig';
import { DEFAULT_APP_CONFIG } from '../../appConfigContext'; import { DEFAULT_APP_CONFIG } from '../../appConfigContext';
import { HOME_PAGE_SEARCH_BAR_ID } from '../onboarding/config/HomePageOnboardingConfig'; import { HOME_PAGE_SEARCH_BAR_ID } from '../onboarding/config/HomePageOnboardingConfig';
import { useUserContext } from '../context/useUserContext';
const Background = styled.div` const Background = styled.div`
width: 100%; width: 100%;
@ -140,7 +140,7 @@ export const HomePageHeader = () => {
const history = useHistory(); const history = useHistory();
const entityRegistry = useEntityRegistry(); const entityRegistry = useEntityRegistry();
const [getAutoCompleteResultsForMultiple, { data: suggestionsData }] = useGetAutoCompleteMultipleResultsLazyQuery(); const [getAutoCompleteResultsForMultiple, { data: suggestionsData }] = useGetAutoCompleteMultipleResultsLazyQuery();
const user = useGetAuthenticatedUser()?.corpUser; const user = useUserContext()?.user;
const themeConfig = useTheme(); const themeConfig = useTheme();
const appConfig = useAppConfig(); const appConfig = useAppConfig();
const [newSuggestionData, setNewSuggestionData] = useState<GetAutoCompleteMultipleResultsQuery | undefined>(); const [newSuggestionData, setNewSuggestionData] = useState<GetAutoCompleteMultipleResultsQuery | undefined>();

View File

@ -12,7 +12,6 @@ import TabToolbar from '../../entity/shared/components/styled/TabToolbar';
import { SearchBar } from '../../search/SearchBar'; import { SearchBar } from '../../search/SearchBar';
import { useEntityRegistry } from '../../useEntityRegistry'; import { useEntityRegistry } from '../../useEntityRegistry';
import ViewInviteTokenModal from './ViewInviteTokenModal'; import ViewInviteTokenModal from './ViewInviteTokenModal';
import { useGetAuthenticatedUser } from '../../useGetAuthenticatedUser';
import { useListRolesQuery } from '../../../graphql/role.generated'; import { useListRolesQuery } from '../../../graphql/role.generated';
import { scrollToTop } from '../../shared/searchUtils'; import { scrollToTop } from '../../shared/searchUtils';
import { OnboardingTour } from '../../onboarding/OnboardingTour'; import { OnboardingTour } from '../../onboarding/OnboardingTour';
@ -24,6 +23,7 @@ import {
} from '../../onboarding/config/UsersOnboardingConfig'; } from '../../onboarding/config/UsersOnboardingConfig';
import { useUpdateEducationStepIdsAllowlist } from '../../onboarding/useUpdateEducationStepIdsAllowlist'; import { useUpdateEducationStepIdsAllowlist } from '../../onboarding/useUpdateEducationStepIdsAllowlist';
import { DEFAULT_USER_LIST_PAGE_SIZE, removeUserFromListUsersCache } from './cacheUtils'; import { DEFAULT_USER_LIST_PAGE_SIZE, removeUserFromListUsersCache } from './cacheUtils';
import { useUserContext } from '../../context/useUserContext';
const UserContainer = styled.div``; const UserContainer = styled.div``;
@ -50,8 +50,8 @@ export const UserList = () => {
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [isViewingInviteToken, setIsViewingInviteToken] = useState(false); const [isViewingInviteToken, setIsViewingInviteToken] = useState(false);
const authenticatedUser = useGetAuthenticatedUser(); const authenticatedUser = useUserContext();
const canManagePolicies = authenticatedUser?.platformPrivileges.managePolicies || false; const canManagePolicies = authenticatedUser?.platformPrivileges?.managePolicies || false;
const pageSize = DEFAULT_USER_LIST_PAGE_SIZE; const pageSize = DEFAULT_USER_LIST_PAGE_SIZE;
const start = (page - 1) * pageSize; const start = (page - 1) * pageSize;

View File

@ -1,8 +1,14 @@
import React from 'react'; import React from 'react';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import { Zoom } from '@vx/zoom'; import { Zoom } from '@vx/zoom';
import { MockedProvider } from '@apollo/client/testing';
import { dataset3WithLineage, dataset4WithLineage, dataset5WithLineage, dataset6WithLineage } from '../../../Mocks'; import {
dataset3WithLineage,
dataset4WithLineage,
dataset5WithLineage,
dataset6WithLineage,
mocks,
} from '../../../Mocks';
import { Direction, FetchedEntities } from '../types'; import { Direction, FetchedEntities } from '../types';
import constructTree from '../utils/constructTree'; import constructTree from '../utils/constructTree';
import LineageTree from '../LineageTree'; import LineageTree from '../LineageTree';
@ -62,38 +68,40 @@ describe('LineageTree', () => {
); );
const { getByTestId } = render( const { getByTestId } = render(
<TestPageContainer> <MockedProvider mocks={mocks}>
<Zoom <TestPageContainer>
width={width} <Zoom
height={height} width={width}
scaleXMin={1 / 8} height={height}
scaleXMax={2} scaleXMin={1 / 8}
scaleYMin={1 / 8} scaleXMax={2}
scaleYMax={2} scaleYMin={1 / 8}
transformMatrix={initialTransform} scaleYMax={2}
> transformMatrix={initialTransform}
{(zoom) => ( >
<svg> {(zoom) => (
<LineageTree <svg>
upstreamData={upstreamData} <LineageTree
downstreamData={downstreamData} upstreamData={upstreamData}
zoom={zoom} downstreamData={downstreamData}
onEntityClick={jest.fn()} zoom={zoom}
onLineageExpand={jest.fn()} onEntityClick={jest.fn()}
canvasHeight={yMax} onLineageExpand={jest.fn()}
margin={margin} canvasHeight={yMax}
direction={Direction.Upstream} margin={margin}
setIsDraggingNode={jest.fn()} direction={Direction.Upstream}
draggedNodes={{}} setIsDraggingNode={jest.fn()}
setDraggedNodes={jest.fn()} draggedNodes={{}}
onEntityCenter={jest.fn()} setDraggedNodes={jest.fn()}
setHoveredEntity={jest.fn()} onEntityCenter={jest.fn()}
fetchedEntities={mockFetchedEntities} setHoveredEntity={jest.fn()}
/> fetchedEntities={mockFetchedEntities}
</svg> />
)} </svg>
</Zoom> )}
</TestPageContainer>, </Zoom>
</TestPageContainer>
</MockedProvider>,
); );
expect(getByTestId('edge-urn:li:dataset:6-urn:li:dataset:5-Upstream')).toBeInTheDocument(); expect(getByTestId('edge-urn:li:dataset:6-urn:li:dataset:5-Upstream')).toBeInTheDocument();

View File

@ -44,7 +44,7 @@ export const navigateToLineageUrl = ({
} }
const newSearchStringified = QueryString.stringify(newSearch, { arrayFormat: 'comma' }); const newSearchStringified = QueryString.stringify(newSearch, { arrayFormat: 'comma' });
history.push({ history.replace({
pathname: location.pathname, pathname: location.pathname,
search: newSearchStringified, search: newSearchStringified,
}); });

View File

@ -4,7 +4,7 @@ import Tour from 'reactour';
import { useBatchUpdateStepStatesMutation } from '../../graphql/step.generated'; import { useBatchUpdateStepStatesMutation } from '../../graphql/step.generated';
import { EducationStepsContext } from '../../providers/EducationStepsContext'; import { EducationStepsContext } from '../../providers/EducationStepsContext';
import { StepStateResult } from '../../types.generated'; import { StepStateResult } from '../../types.generated';
import { useGetAuthenticatedUser } from '../useGetAuthenticatedUser'; import { useUserContext } from '../context/useUserContext';
import { convertStepId, getStepsToRender } from './utils'; import { convertStepId, getStepsToRender } from './utils';
type Props = { type Props = {
@ -13,7 +13,7 @@ type Props = {
export const OnboardingTour = ({ stepIds }: Props) => { export const OnboardingTour = ({ stepIds }: Props) => {
const { educationSteps, setEducationSteps, educationStepIdsAllowlist } = useContext(EducationStepsContext); const { educationSteps, setEducationSteps, educationStepIdsAllowlist } = useContext(EducationStepsContext);
const userUrn = useGetAuthenticatedUser()?.corpUser.urn; const userUrn = useUserContext()?.user?.urn;
const [isOpen, setIsOpen] = useState(true); const [isOpen, setIsOpen] = useState(true);
const [reshow, setReshow] = useState(false); const [reshow, setReshow] = useState(false);
const accentColor = '#5cb7b7'; const accentColor = '#5cb7b7';

View File

@ -1,5 +1,5 @@
import { Typography } from 'antd'; import { Typography } from 'antd';
import React, { useMemo } from 'react'; import React, { useEffect, useMemo } from 'react';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { RecommendationModule as RecommendationModuleType, ScenarioType } from '../../types.generated'; import { RecommendationModule as RecommendationModuleType, ScenarioType } from '../../types.generated';
import analytics, { EventType } from '../analytics'; import analytics, { EventType } from '../analytics';
@ -16,18 +16,19 @@ type Props = {
export const RecommendationModule = ({ module, scenarioType, displayType, showTitle }: Props) => { export const RecommendationModule = ({ module, scenarioType, displayType, showTitle }: Props) => {
const finalDisplayType = displayType || RecommendationDisplayType.DEFAULT; const finalDisplayType = displayType || RecommendationDisplayType.DEFAULT;
const renderId = useMemo(() => uuidv4(), []); const renderId = useMemo(() => uuidv4(), []);
useEffect(() => {
analytics.event({
type: EventType.RecommendationImpressionEvent,
moduleId: module.moduleId,
renderType: module.renderType,
scenarioType,
});
}, [module, scenarioType]);
const RecommendationRenderer = renderTypeToRenderer.get(module.renderType); const RecommendationRenderer = renderTypeToRenderer.get(module.renderType);
if (!RecommendationRenderer) { if (!RecommendationRenderer) {
console.error(`Failed to find renderer corresponding to renderType ${module.renderType}`); console.error(`Failed to find renderer corresponding to renderType ${module.renderType}`);
return null; return null;
} }
analytics.event({
type: EventType.RecommendationImpressionEvent,
renderId,
moduleId: module.moduleId,
renderType: module.renderType,
scenarioType,
});
return ( return (
<> <>
{showTitle && <Typography.Title level={4}>{module.title}</Typography.Title>} {showTitle && <Typography.Title level={4}>{module.title}</Typography.Title>}

View File

@ -7,11 +7,6 @@ import { mocks } from '../../../Mocks';
import TestPageContainer from '../../../utils/test-utils/TestPageContainer'; import TestPageContainer from '../../../utils/test-utils/TestPageContainer';
import { PageRoutes } from '../../../conf/Global'; import { PageRoutes } from '../../../conf/Global';
import { SearchPage } from '../../search/SearchPage'; import { SearchPage } from '../../search/SearchPage';
import { EntityType } from '../../../types.generated';
import { useGetDatasetQuery, useUpdateDatasetMutation } from '../../../graphql/dataset.generated';
import { EntityProfile } from '../../entity/shared/containers/profile/EntityProfile';
import { SchemaTab } from '../../entity/shared/tabs/Dataset/Schema/SchemaTab';
import { SidebarRecommendationsSection } from '../../entity/shared/containers/profile/sidebar/Recommendations/SidebarRecommendationsSection';
describe('Recommendations', () => { describe('Recommendations', () => {
it('home renders recommendations', async () => { it('home renders recommendations', async () => {
@ -63,45 +58,46 @@ describe('Recommendations', () => {
await waitFor(() => expect(getByText('Some Other Dataset')).toBeInTheDocument()); await waitFor(() => expect(getByText('Some Other Dataset')).toBeInTheDocument());
}); });
it('renders entity page sidebar recommendations', async () => { // TODO: Uncomment once entity sidebar recs are fully supported.
const { getByText } = render( // it('renders entity page sidebar recommendations', async () => {
<MockedProvider // const { getByText } = render(
mocks={mocks} // <MockedProvider
addTypename={false} // mocks={mocks}
defaultOptions={{ // addTypename={false}
watchQuery: { fetchPolicy: 'no-cache' }, // defaultOptions={{
query: { fetchPolicy: 'no-cache' }, // watchQuery: { fetchPolicy: 'no-cache' },
}} // query: { fetchPolicy: 'no-cache' },
> // }}
<TestPageContainer initialEntries={['/dataset/urn:li:dataset:3']}> // >
<EntityProfile // <TestPageContainer initialEntries={['/dataset/urn:li:dataset:3']}>
urn="urn:li:dataset:3" // <EntityProfile
entityType={EntityType.Dataset} // urn="urn:li:dataset:3"
useEntityQuery={useGetDatasetQuery} // entityType={EntityType.Dataset}
useUpdateQuery={useUpdateDatasetMutation} // useEntityQuery={useGetDatasetQuery}
getOverrideProperties={() => ({})} // useUpdateQuery={useUpdateDatasetMutation}
tabs={[ // getOverrideProperties={() => ({})}
{ // tabs={[
name: 'Schema', // {
component: SchemaTab, // name: 'Schema',
}, // component: SchemaTab,
]} // },
sidebarSections={[ // ]}
{ // sidebarSections={[
component: SidebarRecommendationsSection, // {
}, // component: SidebarRecommendationsSection,
]} // },
/> // ]}
</TestPageContainer> // />
</MockedProvider>, // </TestPageContainer>
); // </MockedProvider>,
// );
// find recommendation modules // // find recommendation modules
await waitFor(() => expect(getByText('Top Platforms')).toBeInTheDocument()); // await waitFor(() => expect(getByText('Top Platforms')).toBeInTheDocument());
await waitFor(() => expect(getByText('Snowflake')).toBeInTheDocument()); // await waitFor(() => expect(getByText('Snowflake')).toBeInTheDocument());
await waitFor(() => expect(getByText('Popular Tags')).toBeInTheDocument()); // await waitFor(() => expect(getByText('Popular Tags')).toBeInTheDocument());
await waitFor(() => expect(getByText('TestTag')).toBeInTheDocument()); // await waitFor(() => expect(getByText('TestTag')).toBeInTheDocument());
await waitFor(() => expect(getByText('Most Popular')).toBeInTheDocument()); // await waitFor(() => expect(getByText('Most Popular')).toBeInTheDocument());
await waitFor(() => expect(getByText('Some Other Dataset')).toBeInTheDocument()); // await waitFor(() => expect(getByText('Some Other Dataset')).toBeInTheDocument());
}); // });
}); });

View File

@ -13,8 +13,8 @@ import { EXACT_SEARCH_PREFIX } from './utils/constants';
import { CustomAvatar } from '../shared/avatar'; import { CustomAvatar } from '../shared/avatar';
import { StyledTag } from '../entity/shared/components/styled/StyledTag'; import { StyledTag } from '../entity/shared/components/styled/StyledTag';
import { useListRecommendationsQuery } from '../../graphql/recommendations.generated'; import { useListRecommendationsQuery } from '../../graphql/recommendations.generated';
import { useGetAuthenticatedUserUrn } from '../useGetAuthenticatedUser';
import { getPlatformName } from '../entity/shared/utils'; import { getPlatformName } from '../entity/shared/utils';
import { useUserContext } from '../context/useUserContext';
const SuggestionContainer = styled.div` const SuggestionContainer = styled.div`
display: flex; display: flex;
@ -205,19 +205,19 @@ export const SearchBar = ({
useEffect(() => setSelected(initialQuery), [initialQuery]); useEffect(() => setSelected(initialQuery), [initialQuery]);
const searchEntityTypes = entityRegistry.getSearchEntityTypes(); const searchEntityTypes = entityRegistry.getSearchEntityTypes();
const userUrn = useGetAuthenticatedUserUrn(); const userUrn = useUserContext().user?.urn;
const { data } = useListRecommendationsQuery({ const { data } = useListRecommendationsQuery({
variables: { variables: {
input: { input: {
userUrn, userUrn: userUrn as string,
requestContext: { requestContext: {
scenario: ScenarioType.SearchBar, scenario: ScenarioType.SearchBar,
}, },
limit: 1, limit: 1,
}, },
}, },
skip: hideRecommendations, skip: hideRecommendations || !userUrn,
}); });
const effectiveQuery = searchQuery !== undefined ? searchQuery : initialQuery || ''; const effectiveQuery = searchQuery !== undefined ? searchQuery : initialQuery || '';

View File

@ -13,7 +13,6 @@ import {
} from '../../types.generated'; } from '../../types.generated';
import { SearchCfg } from '../../conf'; import { SearchCfg } from '../../conf';
import { SearchResultsRecommendations } from './SearchResultsRecommendations'; import { SearchResultsRecommendations } from './SearchResultsRecommendations';
import { useGetAuthenticatedUser } from '../useGetAuthenticatedUser';
import SearchExtendedMenu from '../entity/shared/components/styled/search/SearchExtendedMenu'; import SearchExtendedMenu from '../entity/shared/components/styled/search/SearchExtendedMenu';
import { combineSiblingsInSearchResults } from '../entity/shared/siblingUtils'; import { combineSiblingsInSearchResults } from '../entity/shared/siblingUtils';
import { SearchSelectBar } from '../entity/shared/components/styled/search/SearchSelectBar'; import { SearchSelectBar } from '../entity/shared/components/styled/search/SearchSelectBar';
@ -26,6 +25,7 @@ import { UnionType } from './utils/constants';
import { SearchFiltersSection } from './SearchFiltersSection'; import { SearchFiltersSection } from './SearchFiltersSection';
import { generateOrFilters } from './utils/generateOrFilters'; import { generateOrFilters } from './utils/generateOrFilters';
import { SEARCH_RESULTS_FILTERS_ID } from '../onboarding/config/SearchOnboardingConfig'; import { SEARCH_RESULTS_FILTERS_ID } from '../onboarding/config/SearchOnboardingConfig';
import { useUserContext } from '../context/useUserContext';
const SearchBody = styled.div` const SearchBody = styled.div`
display: flex; display: flex;
@ -132,7 +132,7 @@ export const SearchResults = ({
const pageSize = searchResponse?.count || 0; const pageSize = searchResponse?.count || 0;
const totalResults = searchResponse?.total || 0; const totalResults = searchResponse?.total || 0;
const lastResultIndex = pageStart + pageSize > totalResults ? totalResults : pageStart + pageSize; const lastResultIndex = pageStart + pageSize > totalResults ? totalResults : pageStart + pageSize;
const authenticatedUserUrn = useGetAuthenticatedUser()?.corpUser?.urn; const authenticatedUserUrn = useUserContext().user?.urn;
const combinedSiblingSearchResults = combineSiblingsInSearchResults(searchResponse?.searchResults); const combinedSiblingSearchResults = combineSiblingsInSearchResults(searchResponse?.searchResults);
const searchResultUrns = combinedSiblingSearchResults.map((result) => result.entity.urn) || []; const searchResultUrns = combinedSiblingSearchResults.map((result) => result.entity.urn) || [];

View File

@ -10,10 +10,10 @@ import {
useGetAutoCompleteMultipleResultsLazyQuery, useGetAutoCompleteMultipleResultsLazyQuery,
} from '../../graphql/search.generated'; } from '../../graphql/search.generated';
import { navigateToSearchUrl } from './utils/navigateToSearchUrl'; import { navigateToSearchUrl } from './utils/navigateToSearchUrl';
import { useGetAuthenticatedUser } from '../useGetAuthenticatedUser';
import analytics, { EventType } from '../analytics'; import analytics, { EventType } from '../analytics';
import useFilters from './utils/useFilters'; import useFilters from './utils/useFilters';
import { PageRoutes } from '../../conf/Global'; import { PageRoutes } from '../../conf/Global';
import { useUserContext } from '../context/useUserContext';
const styles = { const styles = {
children: { children: {
@ -56,7 +56,7 @@ export const SearchablePage = ({ onSearch, onAutoComplete, children }: Props) =>
const themeConfig = useTheme(); const themeConfig = useTheme();
const [getAutoCompleteResults, { data: suggestionsData }] = useGetAutoCompleteMultipleResultsLazyQuery(); const [getAutoCompleteResults, { data: suggestionsData }] = useGetAutoCompleteMultipleResultsLazyQuery();
const user = useGetAuthenticatedUser()?.corpUser; const user = useUserContext()?.user;
const [newSuggestionData, setNewSuggestionData] = useState<GetAutoCompleteMultipleResultsQuery | undefined>(); const [newSuggestionData, setNewSuggestionData] = useState<GetAutoCompleteMultipleResultsQuery | undefined>();
useEffect(() => { useEffect(() => {

View File

@ -9,12 +9,12 @@ import { useListAccessTokensQuery, useRevokeAccessTokenMutation } from '../../gr
import { Message } from '../shared/Message'; import { Message } from '../shared/Message';
import TabToolbar from '../entity/shared/components/styled/TabToolbar'; import TabToolbar from '../entity/shared/components/styled/TabToolbar';
import { StyledTable } from '../entity/shared/components/styled/StyledTable'; import { StyledTable } from '../entity/shared/components/styled/StyledTable';
import { useGetAuthenticatedUser } from '../useGetAuthenticatedUser';
import CreateTokenModal from './CreateTokenModal'; import CreateTokenModal from './CreateTokenModal';
import { useAppConfigQuery } from '../../graphql/app.generated';
import { getLocaleTimezone } from '../shared/time/timeUtils'; import { getLocaleTimezone } from '../shared/time/timeUtils';
import { scrollToTop } from '../shared/searchUtils'; import { scrollToTop } from '../shared/searchUtils';
import analytics, { EventType } from '../analytics'; import analytics, { EventType } from '../analytics';
import { useUserContext } from '../context/useUserContext';
import { useAppConfig } from '../useAppConfig';
const SourceContainer = styled.div` const SourceContainer = styled.div`
width: 100%; width: 100%;
@ -78,12 +78,12 @@ export const AccessTokens = () => {
const [removedTokens, setRemovedTokens] = useState<string[]>([]); const [removedTokens, setRemovedTokens] = useState<string[]>([]);
// Current User Urn // Current User Urn
const authenticatedUser = useGetAuthenticatedUser(); const authenticatedUser = useUserContext();
const currentUserUrn = authenticatedUser?.corpUser.urn || ''; const currentUserUrn = authenticatedUser?.user?.urn || '';
const isTokenAuthEnabled = useAppConfigQuery().data?.appConfig?.authConfig?.tokenAuthEnabled; const isTokenAuthEnabled = useAppConfig().config?.authConfig?.tokenAuthEnabled;
const canGeneratePersonalAccessTokens = const canGeneratePersonalAccessTokens =
isTokenAuthEnabled && authenticatedUser?.platformPrivileges.generatePersonalAccessTokens; isTokenAuthEnabled && authenticatedUser?.platformPrivileges?.generatePersonalAccessTokens;
// Access Tokens list paging. // Access Tokens list paging.
const [page, setPage] = useState(1); const [page, setPage] = useState(1);

View File

@ -2,10 +2,11 @@ import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { Divider, Typography, Switch, Card, message } from 'antd'; import { Divider, Typography, Switch, Card, message } from 'antd';
import { useGetMeQuery, useUpdateUserSettingMutation } from '../../graphql/me.generated'; import { useUpdateUserSettingMutation } from '../../graphql/me.generated';
import { UserSetting } from '../../types.generated'; import { UserSetting } from '../../types.generated';
import { ANTD_GRAY } from '../entity/shared/constants'; import { ANTD_GRAY } from '../entity/shared/constants';
import analytics, { EventType } from '../analytics'; import analytics, { EventType } from '../analytics';
import { useUserContext } from '../context/useUserContext';
const Page = styled.div` const Page = styled.div`
width: 100%; width: 100%;
@ -52,9 +53,9 @@ const SettingText = styled(Typography.Text)`
export const Preferences = () => { export const Preferences = () => {
// Current User Urn // Current User Urn
const { data, refetch } = useGetMeQuery({ fetchPolicy: 'no-cache' }); const { user, refetchUser } = useUserContext();
const showSimplifiedHomepage = !!data?.me?.corpUser?.settings?.appearance?.showSimplifiedHomepage; const showSimplifiedHomepage = !!user?.settings?.appearance?.showSimplifiedHomepage;
const [updateUserSettingMutation] = useUpdateUserSettingMutation(); const [updateUserSettingMutation] = useUpdateUserSettingMutation();
return ( return (
@ -95,7 +96,7 @@ export const Preferences = () => {
: EventType.ShowSimplifiedHomepageEvent, : EventType.ShowSimplifiedHomepageEvent,
}); });
message.success({ content: 'Setting updated!', duration: 2 }); message.success({ content: 'Setting updated!', duration: 2 });
refetch?.(); refetchUser?.();
}} }}
/> />
</UserSettingRow> </UserSettingRow>

View File

@ -13,10 +13,10 @@ import { ANTD_GRAY } from '../entity/shared/constants';
import { ManageIdentities } from '../identity/ManageIdentities'; import { ManageIdentities } from '../identity/ManageIdentities';
import { ManagePermissions } from '../permissions/ManagePermissions'; import { ManagePermissions } from '../permissions/ManagePermissions';
import { useAppConfig } from '../useAppConfig'; import { useAppConfig } from '../useAppConfig';
import { useGetAuthenticatedUser } from '../useGetAuthenticatedUser';
import { AccessTokens } from './AccessTokens'; import { AccessTokens } from './AccessTokens';
import { Preferences } from './Preferences'; import { Preferences } from './Preferences';
import { ManageViews } from '../entity/view/ManageViews'; import { ManageViews } from '../entity/view/ManageViews';
import { useUserContext } from '../context/useUserContext';
const PageContainer = styled.div` const PageContainer = styled.div`
display: flex; display: flex;
@ -77,15 +77,15 @@ export const SettingsPage = () => {
const providedPath = splitPathName[1]; const providedPath = splitPathName[1];
const activePath = subRoutes.includes(providedPath) ? providedPath : DEFAULT_PATH.path.replace('/', ''); const activePath = subRoutes.includes(providedPath) ? providedPath : DEFAULT_PATH.path.replace('/', '');
const me = useGetAuthenticatedUser(); const me = useUserContext();
const { config } = useAppConfig(); const { config } = useAppConfig();
const isPoliciesEnabled = config?.policiesConfig.enabled; const isPoliciesEnabled = config?.policiesConfig.enabled;
const isIdentityManagementEnabled = config?.identityManagementConfig.enabled; const isIdentityManagementEnabled = config?.identityManagementConfig.enabled;
const isViewsEnabled = config?.viewsConfig.enabled; const isViewsEnabled = config?.viewsConfig.enabled;
const showPolicies = (isPoliciesEnabled && me && me.platformPrivileges.managePolicies) || false; const showPolicies = (isPoliciesEnabled && me && me?.platformPrivileges?.managePolicies) || false;
const showUsersGroups = (isIdentityManagementEnabled && me && me.platformPrivileges.manageIdentities) || false; const showUsersGroups = (isIdentityManagementEnabled && me && me?.platformPrivileges?.manageIdentities) || false;
const showViews = isViewsEnabled || false; const showViews = isViewsEnabled || false;
return ( return (
@ -102,7 +102,7 @@ export const SettingsPage = () => {
style={{ width: 256, marginTop: 8 }} style={{ width: 256, marginTop: 8 }}
selectedKeys={[activePath]} selectedKeys={[activePath]}
onClick={(newPath) => { onClick={(newPath) => {
history.push(`${url}/${newPath.key}`); history.replace(`${url}/${newPath.key}`);
}} }}
> >
<Menu.ItemGroup title="Developer"> <Menu.ItemGroup title="Developer">

View File

@ -53,7 +53,7 @@ export const NoPageFound = () => {
const history = useHistory(); const history = useHistory();
const goToHomepage = () => { const goToHomepage = () => {
history.push('/'); history.replace('/');
}; };
return ( return (

View File

@ -39,7 +39,7 @@ export const RoutedTabs = ({ defaultPath, tabs, onTabChange, ...props }: Props)
activeKey={activePath} activeKey={activePath}
size="large" size="large"
onTabClick={(tab: string) => onTabChange && onTabChange(tab)} onTabClick={(tab: string) => onTabChange && onTabChange(tab)}
onChange={(newPath) => history.push(`${url}/${newPath}`)} onChange={(newPath) => history.replace(`${url}/${newPath}`)}
{...props} {...props}
> >
{tabs.map((tab) => { {tabs.map((tab) => {

View File

@ -12,10 +12,10 @@ import {
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Button, Dropdown, Menu, Tooltip } from 'antd'; import { Button, Dropdown, Menu, Tooltip } from 'antd';
import { useAppConfig } from '../../useAppConfig'; import { useAppConfig } from '../../useAppConfig';
import { useGetAuthenticatedUser } from '../../useGetAuthenticatedUser';
import { ANTD_GRAY } from '../../entity/shared/constants'; import { ANTD_GRAY } from '../../entity/shared/constants';
import { HOME_PAGE_INGESTION_ID } from '../../onboarding/config/HomePageOnboardingConfig'; import { HOME_PAGE_INGESTION_ID } from '../../onboarding/config/HomePageOnboardingConfig';
import { useUpdateEducationStepIdsAllowlist } from '../../onboarding/useUpdateEducationStepIdsAllowlist'; import { useUpdateEducationStepIdsAllowlist } from '../../onboarding/useUpdateEducationStepIdsAllowlist';
import { useUserContext } from '../../context/useUserContext';
const LinkWrapper = styled.span` const LinkWrapper = styled.span`
margin-right: 0px; margin-right: 0px;
@ -63,17 +63,17 @@ interface Props {
export function HeaderLinks(props: Props) { export function HeaderLinks(props: Props) {
const { areLinksHidden } = props; const { areLinksHidden } = props;
const me = useGetAuthenticatedUser(); const me = useUserContext();
const { config } = useAppConfig(); const { config } = useAppConfig();
const isAnalyticsEnabled = config?.analyticsConfig.enabled; const isAnalyticsEnabled = config?.analyticsConfig.enabled;
const isIngestionEnabled = config?.managedIngestionConfig.enabled; const isIngestionEnabled = config?.managedIngestionConfig.enabled;
const showAnalytics = (isAnalyticsEnabled && me && me.platformPrivileges.viewAnalytics) || false; const showAnalytics = (isAnalyticsEnabled && me && me?.platformPrivileges?.viewAnalytics) || false;
const showSettings = true; const showSettings = true;
const showIngestion = const showIngestion =
isIngestionEnabled && me && me.platformPrivileges.manageIngestion && me.platformPrivileges.manageSecrets; isIngestionEnabled && me && me.platformPrivileges?.manageIngestion && me.platformPrivileges?.manageSecrets;
const showDomains = me?.platformPrivileges.createDomains || me?.platformPrivileges.manageDomains; const showDomains = me?.platformPrivileges?.createDomains || me?.platformPrivileges?.manageDomains;
useUpdateEducationStepIdsAllowlist(!!showIngestion, HOME_PAGE_INGESTION_ID); useUpdateEducationStepIdsAllowlist(!!showIngestion, HOME_PAGE_INGESTION_ID);

View File

@ -13,7 +13,7 @@ export default function updateQueryParams(newParams: QueryParam, location: Locat
}; };
const stringifiedParams = QueryString.stringify(updatedParams, { arrayFormat: 'comma' }); const stringifiedParams = QueryString.stringify(updatedParams, { arrayFormat: 'comma' });
history.push({ history.replace({
pathname: location.pathname, pathname: location.pathname,
search: stringifiedParams, search: stringifiedParams,
}); });

View File

@ -1,13 +1,13 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useGetAuthenticatedUser } from '../app/useGetAuthenticatedUser';
import { getStepIds } from '../app/onboarding/utils'; import { getStepIds } from '../app/onboarding/utils';
import { useBatchGetStepStatesQuery } from '../graphql/step.generated'; import { useBatchGetStepStatesQuery } from '../graphql/step.generated';
import { EducationStepsContext } from './EducationStepsContext'; import { EducationStepsContext } from './EducationStepsContext';
import { StepStateResult } from '../types.generated'; import { StepStateResult } from '../types.generated';
import { CURRENT_ONBOARDING_IDS } from '../app/onboarding/OnboardingConfig'; import { CURRENT_ONBOARDING_IDS } from '../app/onboarding/OnboardingConfig';
import { useUserContext } from '../app/context/useUserContext';
export function EducationStepsProvider({ children }: { children: React.ReactNode }) { export function EducationStepsProvider({ children }: { children: React.ReactNode }) {
const userUrn = useGetAuthenticatedUser()?.corpUser.urn; const userUrn = useUserContext()?.user?.urn;
const stepIds = getStepIds(userUrn || ''); const stepIds = getStepIds(userUrn || '');
const { data } = useBatchGetStepStatesQuery({ skip: !userUrn, variables: { input: { ids: stepIds } } }); const { data } = useBatchGetStepStatesQuery({ skip: !userUrn, variables: { input: { ids: stepIds } } });
const results = data?.batchGetStepStates.results; const results = data?.batchGetStepStates.results;

View File

@ -20,6 +20,7 @@ import { MLModelGroupEntity } from '../../app/entity/mlModelGroup/MLModelGroupEn
import { ChartEntity } from '../../app/entity/chart/ChartEntity'; import { ChartEntity } from '../../app/entity/chart/ChartEntity';
import { DashboardEntity } from '../../app/entity/dashboard/DashboardEntity'; import { DashboardEntity } from '../../app/entity/dashboard/DashboardEntity';
import { LineageExplorerContext } from '../../app/lineage/utils/LineageExplorerContext'; import { LineageExplorerContext } from '../../app/lineage/utils/LineageExplorerContext';
import UserContextProvider from '../../app/context/UserContextProvider';
type Props = { type Props = {
children: React.ReactNode; children: React.ReactNode;
@ -55,26 +56,28 @@ export default ({ children, initialEntries }: Props) => {
<ThemeProvider theme={defaultThemeConfig}> <ThemeProvider theme={defaultThemeConfig}>
<MemoryRouter initialEntries={initialEntries}> <MemoryRouter initialEntries={initialEntries}>
<EntityRegistryContext.Provider value={entityRegistry}> <EntityRegistryContext.Provider value={entityRegistry}>
<LineageExplorerContext.Provider <UserContextProvider>
value={{ <LineageExplorerContext.Provider
expandTitles: false, value={{
showColumns: false, expandTitles: false,
collapsedColumnsNodes: {}, showColumns: false,
setCollapsedColumnsNodes: null, collapsedColumnsNodes: {},
fineGrainedMap: {}, setCollapsedColumnsNodes: null,
selectedField: null, fineGrainedMap: {},
setSelectedField: () => {}, selectedField: null,
highlightedEdges: [], setSelectedField: () => {},
setHighlightedEdges: () => {}, highlightedEdges: [],
visibleColumnsByUrn: {}, setHighlightedEdges: () => {},
setVisibleColumnsByUrn: () => {}, visibleColumnsByUrn: {},
columnsByUrn: {}, setVisibleColumnsByUrn: () => {},
setColumnsByUrn: () => {}, columnsByUrn: {},
refetchCenterNode: () => {}, setColumnsByUrn: () => {},
}} refetchCenterNode: () => {},
> }}
{children} >
</LineageExplorerContext.Provider> {children}
</LineageExplorerContext.Provider>
</UserContextProvider>
</EntityRegistryContext.Provider> </EntityRegistryContext.Provider>
</MemoryRouter> </MemoryRouter>
</ThemeProvider> </ThemeProvider>