From e60986e70a1c57f646bd3378eeac049f8e1ae30e Mon Sep 17 00:00:00 2001 From: Shailesh Parmar Date: Tue, 26 Oct 2021 17:27:06 +0530 Subject: [PATCH] UI: Refector Landing page (#934) * UI: refactor explore page * rename index to exlore.component for mock file and test file * UI: refactore landing page * Merge Main * re-name test files and my-data folder and miner fix * re-name LandingPage folder to MyData * miner fix --- .../components/Explore/Explore.component.tsx | 5 +- ...re.component.test.tsx => Explore.test.tsx} | 0 .../components/Explore/explore.interface.ts | 31 +- .../MyData/MyData.component.tsx} | 122 ++++---- .../src/components/MyData/MyData.interface.ts | 10 + .../MyData/MyData.mock.js} | 0 .../ui/src/components/MyData/MyData.test.tsx | 282 ++++++++++++++++++ .../components/my-data/Description.test.js | 2 +- .../components/my-data/MiscDetails.test.js | 2 +- .../ui/src/components/my-data/MyData.jsx | 2 +- .../ui/src/components/my-data/MyData.test.js | 2 +- .../components/my-data/QueryDetails.test.js | 2 +- .../ui/src/components/my-data/Stats.test.js | 2 +- .../my-data => constants}/Mydata.constants.ts | 4 +- .../explore.constants.ts | 10 +- .../resources/ui/src/interface/types.d.ts | 10 + .../pages/MyDataPage/MyDataPage.component.tsx | 83 ++++++ .../src/pages/MyDataPage/MyDataPage.test.tsx | 37 +++ .../pages/explore/ExplorePage.component.tsx | 9 +- ...omponent.test.tsx => ExplorePage.test.tsx} | 0 .../ui/src/pages/my-data/index.test.tsx | 239 --------------- .../ui/src/pages/tour-page/index.tsx | 4 +- .../ui/src/router/AuthenticatedAppRouter.tsx | 2 +- 23 files changed, 504 insertions(+), 356 deletions(-) rename catalog-rest-service/src/main/resources/ui/src/components/Explore/{Explore.component.test.tsx => Explore.test.tsx} (100%) rename catalog-rest-service/src/main/resources/ui/src/{pages/my-data/index.tsx => components/MyData/MyData.component.tsx} (67%) create mode 100644 catalog-rest-service/src/main/resources/ui/src/components/MyData/MyData.interface.ts rename catalog-rest-service/src/main/resources/ui/src/{pages/my-data/index.mock.js => components/MyData/MyData.mock.js} (100%) create mode 100644 catalog-rest-service/src/main/resources/ui/src/components/MyData/MyData.test.tsx rename catalog-rest-service/src/main/resources/ui/src/{pages/my-data => constants}/Mydata.constants.ts (92%) rename catalog-rest-service/src/main/resources/ui/src/{components/Explore => constants}/explore.constants.ts (93%) create mode 100644 catalog-rest-service/src/main/resources/ui/src/pages/MyDataPage/MyDataPage.component.tsx create mode 100644 catalog-rest-service/src/main/resources/ui/src/pages/MyDataPage/MyDataPage.test.tsx rename catalog-rest-service/src/main/resources/ui/src/pages/explore/{ExplorePage.component.test.tsx => ExplorePage.test.tsx} (100%) delete mode 100644 catalog-rest-service/src/main/resources/ui/src/pages/my-data/index.test.tsx diff --git a/catalog-rest-service/src/main/resources/ui/src/components/Explore/Explore.component.tsx b/catalog-rest-service/src/main/resources/ui/src/components/Explore/Explore.component.tsx index 1addd8321b3..5ff63aead5a 100644 --- a/catalog-rest-service/src/main/resources/ui/src/components/Explore/Explore.component.tsx +++ b/catalog-rest-service/src/main/resources/ui/src/components/Explore/Explore.component.tsx @@ -38,6 +38,10 @@ import { tableSortingFields, visibleFilters, } from '../../constants/constants'; +import { + getAggrWithDefaultValue, + tabsInfo, +} from '../../constants/explore.constants'; import { SearchIndex } from '../../enums/search.enum'; import { usePrevious } from '../../hooks/usePrevious'; import { getAggregationList } from '../../utils/AggregationUtils'; @@ -46,7 +50,6 @@ import { getCountBadge } from '../../utils/CommonUtils'; import { getFilterString } from '../../utils/FilterUtils'; import { dropdownIcon as DropDownIcon } from '../../utils/svgconstant'; import SVGIcons from '../../utils/SvgUtils'; -import { getAggrWithDefaultValue, tabsInfo } from './explore.constants'; import { ExploreProps } from './explore.interface'; const getQueryParam = (urlSearchQuery = ''): FilterObject => { diff --git a/catalog-rest-service/src/main/resources/ui/src/components/Explore/Explore.component.test.tsx b/catalog-rest-service/src/main/resources/ui/src/components/Explore/Explore.test.tsx similarity index 100% rename from catalog-rest-service/src/main/resources/ui/src/components/Explore/Explore.component.test.tsx rename to catalog-rest-service/src/main/resources/ui/src/components/Explore/Explore.test.tsx diff --git a/catalog-rest-service/src/main/resources/ui/src/components/Explore/explore.interface.ts b/catalog-rest-service/src/main/resources/ui/src/components/Explore/explore.interface.ts index 478684c8b1b..2a0014cf2d7 100644 --- a/catalog-rest-service/src/main/resources/ui/src/components/Explore/explore.interface.ts +++ b/catalog-rest-service/src/main/resources/ui/src/components/Explore/explore.interface.ts @@ -15,28 +15,13 @@ * limitations under the License. */ -import { SearchResponse } from 'Models'; +import { SearchDataFunctionType, SearchResponse } from 'Models'; -export type Params = { +export type UrlParams = { searchQuery: string; tab: string; }; -export type Service = { - collection: { - name: string; - documentation: string; - href: string; - }; -}; -export type Team = { - id: string; - name: string; - displayName: string; - description: string; - href: string; -}; - export type ExploreSearchData = { resSearchResults: SearchResponse; resAggServiceType: SearchResponse; @@ -44,16 +29,6 @@ export type ExploreSearchData = { resAggTag: SearchResponse; }; -export interface FetchData { - queryString: string; - from: number; - size: number; - filters: string; - sortField: string; - sortOrder: string; - searchIndex: string; -} - export interface ExploreProps { tabCounts: { table: number; @@ -71,6 +46,6 @@ export interface ExploreProps { updateTopicCount: (count: number) => void; updateDashboardCount: (count: number) => void; updatePipelineCount: (count: number) => void; - fetchData: (value: FetchData[]) => void; + fetchData: (value: SearchDataFunctionType[]) => void; searchResult: ExploreSearchData | undefined; } diff --git a/catalog-rest-service/src/main/resources/ui/src/pages/my-data/index.tsx b/catalog-rest-service/src/main/resources/ui/src/components/MyData/MyData.component.tsx similarity index 67% rename from catalog-rest-service/src/main/resources/ui/src/pages/my-data/index.tsx rename to catalog-rest-service/src/main/resources/ui/src/components/MyData/MyData.component.tsx index 527d2ed81cf..c0ea036aa20 100644 --- a/catalog-rest-service/src/main/resources/ui/src/pages/my-data/index.tsx +++ b/catalog-rest-service/src/main/resources/ui/src/components/MyData/MyData.component.tsx @@ -15,54 +15,50 @@ * limitations under the License. */ -import { AxiosError } from 'axios'; import { isEmpty } from 'lodash'; -import { observer } from 'mobx-react'; -import { Bucket, FormatedTableData, SearchResponse, Sterm } from 'Models'; +import { Bucket, FormatedTableData, Sterm } from 'Models'; import React, { useEffect, useRef, useState } from 'react'; -import AppState from '../../AppState'; -import { searchData } from '../../axiosAPIs/miscAPI'; -import ErrorPlaceHolderES from '../../components/common/error-with-placeholder/ErrorPlaceHolderES'; -import PageContainer from '../../components/containers/PageContainer'; -import Loader from '../../components/Loader/Loader'; -import MyDataHeader from '../../components/my-data/MyDataHeader'; -import RecentlyViewed from '../../components/recently-viewed/RecentlyViewed'; -import SearchedData from '../../components/searched-data/SearchedData'; -import { ERROR500, PAGE_SIZE } from '../../constants/constants'; import { Ownership } from '../../enums/mydata.enum'; -import useToastContext from '../../hooks/useToastContext'; import { formatDataResponse } from '../../utils/APIUtils'; import { getCurrentUserId } from '../../utils/CommonUtils'; -import { - getAllServices, - getEntityCountByService, -} from '../../utils/ServiceUtils'; +import { getEntityCountByService } from '../../utils/ServiceUtils'; +import ErrorPlaceHolderES from '../common/error-with-placeholder/ErrorPlaceHolderES'; +import PageContainer from '../containers/PageContainer'; +import Loader from '../Loader/Loader'; +import MyDataHeader from '../my-data/MyDataHeader'; +import RecentlyViewed from '../recently-viewed/RecentlyViewed'; +import SearchedData from '../searched-data/SearchedData'; +import { MyDataProps } from './MyData.interface'; -const MyDataPage: React.FC = (): React.ReactElement => { - const showToast = useToastContext(); +const MyData: React.FC = ({ + countServices, + userDetails, + searchResult, + fetchData, + error, +}: MyDataProps): React.ReactElement => { const [data, setData] = useState>([]); const [currentPage, setCurrentPage] = useState(1); const [totalNumberOfValue, setTotalNumberOfValues] = useState(0); const [isLoading, setIsLoading] = useState(true); const [isEntityLoading, setIsEntityLoading] = useState(true); const [currentTab, setCurrentTab] = useState(1); - const [error, setError] = useState(''); const [filter, setFilter] = useState(''); const [aggregations, setAggregations] = useState>(); const [searchIndex] = useState( 'dashboard_search_index,topic_search_index,table_search_index,pipeline_search_index' ); - const [countServices, setCountServices] = useState(0); const isMounted = useRef(false); + const setAssetCount = useRef(true); const getActiveTabClass = (tab: number) => { return tab === currentTab ? 'active' : ''; }; const getFilters = (): string => { - if (filter === 'owner' && AppState.userDetails.teams) { - const userTeams = !isEmpty(AppState.userDetails) - ? AppState.userDetails.teams.map((team) => `${filter}:${team.id}`) + if (filter === 'owner' && userDetails.teams) { + const userTeams = !isEmpty(userDetails) + ? userDetails.teams.map((team) => `${filter}:${team.id}`) : []; const ownerIds = [...userTeams, `${filter}:${getCurrentUserId()}`]; @@ -72,45 +68,19 @@ const MyDataPage: React.FC = (): React.ReactElement => { return `${filter}:${getCurrentUserId()}`; }; - const fetchTableData = (setAssetCount = false) => { + const fetchTableData = () => { if (!isEntityLoading) { setIsLoading(true); } - searchData( - '', - currentPage, - PAGE_SIZE, - filter ? getFilters() : '', - '', - '', - searchIndex - ) - .then((res: SearchResponse) => { - const hits = res.data.hits.hits; - if (hits.length > 0) { - setTotalNumberOfValues(res.data.hits.total.value); - setData(formatDataResponse(hits)); - if (setAssetCount) { - setAggregations(res.data.aggregations); - } - setIsLoading(false); - setIsEntityLoading(false); - } else { - setData([]); - setTotalNumberOfValues(0); - setIsLoading(false); - setIsEntityLoading(false); - } - }) - .catch((err: AxiosError) => { - setError(err.response?.data?.responseMessage); - showToast({ - variant: 'error', - body: err.response?.data?.responseMessage ?? ERROR500, - }); - setIsLoading(false); - setIsEntityLoading(false); - }); + + fetchData({ + queryString: '', + from: currentPage, + filters: filter ? getFilters() : '', + sortField: '', + sortOrder: '', + searchIndex: searchIndex, + }); }; const getTabs = () => { @@ -163,14 +133,36 @@ const MyDataPage: React.FC = (): React.ReactElement => { }; useEffect(() => { - fetchTableData(!isMounted.current); + setAssetCount.current = !isMounted.current; + fetchTableData(); }, [currentPage, filter]); + useEffect(() => { + if (searchResult) { + const hits = searchResult.data.hits.hits; + if (hits.length > 0) { + setTotalNumberOfValues(searchResult.data.hits.total.value); + setData(formatDataResponse(hits)); + if (setAssetCount.current) { + setAggregations(searchResult.data.aggregations); + } + setIsLoading(false); + setIsEntityLoading(false); + } else { + setData([]); + setTotalNumberOfValues(0); + setIsLoading(false); + setIsEntityLoading(false); + } + } else { + setIsLoading(false); + setIsEntityLoading(false); + } + }, [searchResult]); + useEffect(() => { isMounted.current = true; - getAllServices() - .then((res) => setCountServices(res.length)) - .catch(() => setCountServices(0)); + setAssetCount.current = true; }, []); return ( @@ -212,4 +204,4 @@ const MyDataPage: React.FC = (): React.ReactElement => { ); }; -export default observer(MyDataPage); +export default MyData; diff --git a/catalog-rest-service/src/main/resources/ui/src/components/MyData/MyData.interface.ts b/catalog-rest-service/src/main/resources/ui/src/components/MyData/MyData.interface.ts new file mode 100644 index 00000000000..f08efd15ee0 --- /dev/null +++ b/catalog-rest-service/src/main/resources/ui/src/components/MyData/MyData.interface.ts @@ -0,0 +1,10 @@ +import { SearchDataFunctionType, SearchResponse } from 'Models'; +import { User } from '../../generated/entity/teams/user'; + +export interface MyDataProps { + countServices: number; + userDetails: User; + error: string; + searchResult: SearchResponse | undefined; + fetchData: (value: SearchDataFunctionType) => void; +} diff --git a/catalog-rest-service/src/main/resources/ui/src/pages/my-data/index.mock.js b/catalog-rest-service/src/main/resources/ui/src/components/MyData/MyData.mock.js similarity index 100% rename from catalog-rest-service/src/main/resources/ui/src/pages/my-data/index.mock.js rename to catalog-rest-service/src/main/resources/ui/src/components/MyData/MyData.mock.js diff --git a/catalog-rest-service/src/main/resources/ui/src/components/MyData/MyData.test.tsx b/catalog-rest-service/src/main/resources/ui/src/components/MyData/MyData.test.tsx new file mode 100644 index 00000000000..0c2a6b91a8d --- /dev/null +++ b/catalog-rest-service/src/main/resources/ui/src/components/MyData/MyData.test.tsx @@ -0,0 +1,282 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +/* eslint-disable @typescript-eslint/camelcase */ + +import { + findAllByTestId, + findByTestId, + findByText, + render, +} from '@testing-library/react'; +import { SearchResponse } from 'Models'; +import React from 'react'; +import { MemoryRouter } from 'react-router-dom'; +import { User } from '../../generated/entity/teams/user'; +import MyDataPage from './MyData.component'; + +const mockData = { + data: { + took: 50, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 12, + relation: 'eq', + }, + max_score: 5, + hits: [ + { + _index: 'table_search_indexcb2cac4c-735e-474e-a968-da8d8e0b218c', + _type: '_doc', + _id: '53761de6-6c41-431f-9c4e-62d39cf4aba9', + _score: 5, + _source: { + table_id: '53761de6-6c41-431f-9c4e-62d39cf4aba9', + database: 'shopify', + service: 'bigquery', + service_type: 'BigQuery', + table_name: 'dim_address', + suggest: [ + { + input: ['bigquery.shopify.dim_address'], + weight: 5, + }, + { + input: ['dim_address'], + weight: 10, + }, + ], + description: 'test description', + table_type: null, + last_updated_timestamp: 1628498201, + column_names: ['address_id', 'shop_id'], + column_descriptions: [ + 'Unique identifier for the address.', + 'The ID of the store. This column is a foreign key reference to the shop_id column in the shops table.', + ], + monthly_stats: 0, + weekly_stats: 0, + daily_stats: 0, + tags: ['User.Phone'], + fqdn: 'bigquery.shopify.dim_address', + tier: null, + schema_description: null, + owner: '', + followers: [], + table_entity: { + id: '53761de6-6c41-431f-9c4e-62d39cf4aba9', + name: 'dim_address', + description: 'test table description', + href: 'http://test', + tableType: null, + fullyQualifiedName: 'bigquery.shopify.dim_address', + columns: [ + { + name: 'address_id', + columnDataType: 'NUMERIC', + description: 'Unique identifier for the address.', + fullyQualifiedName: 'bigquery.shopify.dim_address.address_id', + tags: [ + { + tagFQN: 'PersonalData.Personal', + labelType: 'Derived', + state: 'Suggested', + href: null, + }, + { + tagFQN: 'PII.Sensitive', + labelType: 'Derived', + state: 'Suggested', + href: null, + }, + { + tagFQN: 'User.Address', + labelType: 'Automated', + state: 'Suggested', + href: null, + }, + ], + columnConstraint: 'PRIMARY_KEY', + ordinalPosition: 1, + }, + ], + tableConstraints: null, + usageSummary: { + dailyStats: { + count: 0, + percentileRank: 0, + }, + weeklyStats: { + count: 0, + percentileRank: 0, + }, + monthlyStats: { + count: 0, + percentileRank: 0, + }, + date: '2021-08-09', + }, + owner: null, + followers: [], + database: { + id: '2d04ddc1-4c9b-43a3-85fb-3de204d67f32', + type: 'database', + name: 'shopify', + description: + 'This database contains tables related to shopify order and related dimensions', + href: 'http://test/test', + }, + tags: [], + joins: null, + sampleData: null, + }, + }, + highlight: { + description: ['data test'], + table_name: ['dim_address'], + }, + }, + ], + }, + aggregations: { + 'sterms#Tier': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [], + }, + 'sterms#Tags': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'PII.Sensitive', + doc_count: 7, + }, + ], + }, + 'sterms#Service Type': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'BigQuery', + doc_count: 12, + }, + ], + }, + }, + }, +}; + +const mockUserDetails = { + data: [ + { + id: 'e8672538-d479-4390-b81d-a85eb19c8221', + name: 'Cloud_Infra', + displayName: 'Cloud Infra', + description: '', + version: 0.1, + updatedAt: 1634886222355, + updatedBy: 'anonymous', + href: 'http://localhost:8585/api/v1/teams/e8672538-d479-4390-b81d-a85eb19c8221', + }, + { + id: '1fffc9a9-33fd-42ca-a605-ab87055bed96', + name: 'Customer_Support', + displayName: 'Customer Support', + description: '', + version: 0.1, + updatedAt: 1634886221511, + updatedBy: 'anonymous', + href: 'http://localhost:8585/api/v1/teams/1fffc9a9-33fd-42ca-a605-ab87055bed96', + }, + ], + paging: { + total: 2, + }, +}; + +jest.mock('../../axiosAPIs/miscAPI', () => ({ + searchData: jest + .fn() + .mockImplementation(() => Promise.resolve({ data: mockData })), +})); + +jest.mock('../../components/searched-data/SearchedData', () => { + return jest + .fn() + .mockImplementation(({ children }: { children: React.ReactNode }) => ( +
+
{children}
+
+ )); +}); + +jest.mock('../../components/recently-viewed/RecentlyViewed', () => { + return jest.fn().mockReturnValue(

RecentlyViewed

); +}); + +jest.mock('../../components/my-data/MyDataHeader', () => { + return jest.fn().mockReturnValue(

MyDataHeader

); +}); + +jest.mock('../../utils/ServiceUtils', () => ({ + getAllServices: jest + .fn() + .mockImplementation(() => Promise.resolve(['test', 'test2', 'test3'])), + getEntityCountByService: jest + .fn() + .mockReturnValue({ tableCount: 4, topicCount: 5, dashboardCount: 6 }), + getTotalEntityCountByService: jest.fn().mockReturnValue(2), +})); + +const fetchData = jest.fn(); + +describe('Test MyData page', () => { + it('Check if there is an element in the page', async () => { + const { container } = render( + , + { + wrapper: MemoryRouter, + } + ); + const pageContainer = await findByTestId(container, 'fluid-container'); + const searchData = await findByTestId(container, 'search-data'); + const wrappedContent = await findByTestId(container, 'wrapped-content'); + const tabs = await findAllByTestId(container, 'tab'); + const myDataHeader = await findByText(container, /MyDataHeader/i); + + expect(pageContainer).toBeInTheDocument(); + expect(searchData).toBeInTheDocument(); + expect(wrappedContent).toBeInTheDocument(); + expect(myDataHeader).toBeInTheDocument(); + expect(tabs.length).toBe(3); + }); +}); diff --git a/catalog-rest-service/src/main/resources/ui/src/components/my-data/Description.test.js b/catalog-rest-service/src/main/resources/ui/src/components/my-data/Description.test.js index c57ec306149..098d4db4694 100644 --- a/catalog-rest-service/src/main/resources/ui/src/components/my-data/Description.test.js +++ b/catalog-rest-service/src/main/resources/ui/src/components/my-data/Description.test.js @@ -17,7 +17,7 @@ import { getAllByTestId, getByTestId, render } from '@testing-library/react'; import React from 'react'; -import { descriptionData } from '../../pages/my-data/index.mock'; +import { descriptionData } from '../MyData/MyData.mock'; import Description from './Description'; describe('Test Description Component', () => { diff --git a/catalog-rest-service/src/main/resources/ui/src/components/my-data/MiscDetails.test.js b/catalog-rest-service/src/main/resources/ui/src/components/my-data/MiscDetails.test.js index c25dbc82da4..c532f89e734 100644 --- a/catalog-rest-service/src/main/resources/ui/src/components/my-data/MiscDetails.test.js +++ b/catalog-rest-service/src/main/resources/ui/src/components/my-data/MiscDetails.test.js @@ -17,7 +17,7 @@ import { getByTestId, render } from '@testing-library/react'; import React from 'react'; -import { miscDetailsData } from '../../pages/my-data/index.mock'; +import { miscDetailsData } from '../MyData/MyData.mock'; import MiscDetails from './MiscDetails'; describe('Test MiscDetails Component', () => { diff --git a/catalog-rest-service/src/main/resources/ui/src/components/my-data/MyData.jsx b/catalog-rest-service/src/main/resources/ui/src/components/my-data/MyData.jsx index 79786bf588b..614d2b2aad5 100644 --- a/catalog-rest-service/src/main/resources/ui/src/components/my-data/MyData.jsx +++ b/catalog-rest-service/src/main/resources/ui/src/components/my-data/MyData.jsx @@ -19,7 +19,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { Link } from 'react-router-dom'; import { getDatasetDetailsPath } from '../../constants/constants'; -import { dummyData } from '../../pages/my-data/index.mock'; +import { dummyData } from '../MyData/MyData.mock'; import Description from './Description'; import QueryDetails from './QueryDetails'; diff --git a/catalog-rest-service/src/main/resources/ui/src/components/my-data/MyData.test.js b/catalog-rest-service/src/main/resources/ui/src/components/my-data/MyData.test.js index e58aa98a9a9..0e0917a43a1 100644 --- a/catalog-rest-service/src/main/resources/ui/src/components/my-data/MyData.test.js +++ b/catalog-rest-service/src/main/resources/ui/src/components/my-data/MyData.test.js @@ -18,7 +18,7 @@ import { getByTestId, render } from '@testing-library/react'; import React from 'react'; import { MemoryRouter } from 'react-router-dom'; -import { dataDetails } from '../../pages/my-data/index.mock'; +import { dataDetails } from '../MyData/MyData.mock'; import MyData from './MyData'; describe('Test MyData Component', () => { diff --git a/catalog-rest-service/src/main/resources/ui/src/components/my-data/QueryDetails.test.js b/catalog-rest-service/src/main/resources/ui/src/components/my-data/QueryDetails.test.js index a5780e238c6..5ee1ad5f750 100644 --- a/catalog-rest-service/src/main/resources/ui/src/components/my-data/QueryDetails.test.js +++ b/catalog-rest-service/src/main/resources/ui/src/components/my-data/QueryDetails.test.js @@ -17,7 +17,7 @@ import { getByTestId, render } from '@testing-library/react'; import React from 'react'; -import { queryDetailsData } from '../../pages/my-data/index.mock'; +import { queryDetailsData } from '../MyData/MyData.mock'; import QueryDetails from './QueryDetails'; describe('Test QueryDetails Component', () => { diff --git a/catalog-rest-service/src/main/resources/ui/src/components/my-data/Stats.test.js b/catalog-rest-service/src/main/resources/ui/src/components/my-data/Stats.test.js index a2b34d6f590..fab11ff72c1 100644 --- a/catalog-rest-service/src/main/resources/ui/src/components/my-data/Stats.test.js +++ b/catalog-rest-service/src/main/resources/ui/src/components/my-data/Stats.test.js @@ -18,7 +18,7 @@ import { findByTestId, render } from '@testing-library/react'; import React from 'react'; import { MemoryRouter } from 'react-router-dom'; -import { statData } from '../../pages/my-data/index.mock'; +import { statData } from '../MyData/MyData.mock'; import Stats from './Stats'; describe('Test Stats Component', () => { diff --git a/catalog-rest-service/src/main/resources/ui/src/pages/my-data/Mydata.constants.ts b/catalog-rest-service/src/main/resources/ui/src/constants/Mydata.constants.ts similarity index 92% rename from catalog-rest-service/src/main/resources/ui/src/pages/my-data/Mydata.constants.ts rename to catalog-rest-service/src/main/resources/ui/src/constants/Mydata.constants.ts index 2625ebdd007..4fdb406b5d8 100644 --- a/catalog-rest-service/src/main/resources/ui/src/pages/my-data/Mydata.constants.ts +++ b/catalog-rest-service/src/main/resources/ui/src/constants/Mydata.constants.ts @@ -16,8 +16,8 @@ */ import { FilterObject } from 'Models'; -import { getCurrentUserId } from '../../utils/CommonUtils'; -import { getFilterString } from '../../utils/FilterUtils'; +import { getCurrentUserId } from '../utils/CommonUtils'; +import { getFilterString } from '../utils/FilterUtils'; export const myDataFilterType = [ { key: 'Owned', value: 'owner' }, diff --git a/catalog-rest-service/src/main/resources/ui/src/components/Explore/explore.constants.ts b/catalog-rest-service/src/main/resources/ui/src/constants/explore.constants.ts similarity index 93% rename from catalog-rest-service/src/main/resources/ui/src/components/Explore/explore.constants.ts rename to catalog-rest-service/src/main/resources/ui/src/constants/explore.constants.ts index a70d8c8b653..c22cd4f028b 100644 --- a/catalog-rest-service/src/main/resources/ui/src/components/Explore/explore.constants.ts +++ b/catalog-rest-service/src/main/resources/ui/src/constants/explore.constants.ts @@ -17,13 +17,9 @@ import { lowerCase } from 'lodash'; import { AggregationType, Bucket } from 'Models'; -import { - tableSortingFields, - tiers, - topicSortingFields, -} from '../../constants/constants'; -import { SearchIndex } from '../../enums/search.enum'; -import { Icons } from '../../utils/SvgUtils'; +import { SearchIndex } from '../enums/search.enum'; +import { Icons } from '../utils/SvgUtils'; +import { tableSortingFields, tiers, topicSortingFields } from './constants'; export const getBucketList = (buckets: Array) => { let bucketList: Array = [...tiers]; diff --git a/catalog-rest-service/src/main/resources/ui/src/interface/types.d.ts b/catalog-rest-service/src/main/resources/ui/src/interface/types.d.ts index 2b24362a076..483fbda636a 100644 --- a/catalog-rest-service/src/main/resources/ui/src/interface/types.d.ts +++ b/catalog-rest-service/src/main/resources/ui/src/interface/types.d.ts @@ -324,6 +324,16 @@ declare module 'Models' { rows: Array>; }; + export type SearchDataFunctionType = { + queryString: string; + from: number; + size?: number; + filters: string; + sortField: string; + sortOrder: string; + searchIndex: string; + }; + // topic interface start export interface Topic { cleanupPolicies: string[]; diff --git a/catalog-rest-service/src/main/resources/ui/src/pages/MyDataPage/MyDataPage.component.tsx b/catalog-rest-service/src/main/resources/ui/src/pages/MyDataPage/MyDataPage.component.tsx new file mode 100644 index 00000000000..92b20a0223b --- /dev/null +++ b/catalog-rest-service/src/main/resources/ui/src/pages/MyDataPage/MyDataPage.component.tsx @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +import { AxiosError } from 'axios'; +import { observer } from 'mobx-react'; +import { SearchDataFunctionType, SearchResponse } from 'Models'; +import React, { useEffect, useState } from 'react'; +import AppState from '../../AppState'; +import { searchData } from '../../axiosAPIs/miscAPI'; +import Loader from '../../components/Loader/Loader'; +import MyData from '../../components/MyData/MyData.component'; +import { ERROR500, PAGE_SIZE } from '../../constants/constants'; +import useToastContext from '../../hooks/useToastContext'; +import { getAllServices } from '../../utils/ServiceUtils'; + +const MyDataPage = () => { + const showToast = useToastContext(); + const [error, setError] = useState(''); + const [countServices, setCountServices] = useState(0); + const [isLoading, setIsLoading] = useState(true); + const [searchResult, setSearchResult] = useState(); + + const fetchData = (value: SearchDataFunctionType) => { + searchData( + value.queryString, + value.from, + PAGE_SIZE, + value.filters, + value.sortField, + value.sortOrder, + value.searchIndex + ) + .then((res: SearchResponse) => { + setSearchResult(res); + }) + .catch((err: AxiosError) => { + setError(err.response?.data?.responseMessage); + showToast({ + variant: 'error', + body: err.response?.data?.responseMessage ?? ERROR500, + }); + }); + }; + + useEffect(() => { + getAllServices() + .then((res) => setCountServices(res.length)) + .catch(() => setCountServices(0)); + setIsLoading(false); + }, []); + + return ( +
+ {isLoading ? ( + + ) : ( + + )} +
+ ); +}; + +export default observer(MyDataPage); diff --git a/catalog-rest-service/src/main/resources/ui/src/pages/MyDataPage/MyDataPage.test.tsx b/catalog-rest-service/src/main/resources/ui/src/pages/MyDataPage/MyDataPage.test.tsx new file mode 100644 index 00000000000..eb9812b6724 --- /dev/null +++ b/catalog-rest-service/src/main/resources/ui/src/pages/MyDataPage/MyDataPage.test.tsx @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +import { findByTestId, render } from '@testing-library/react'; +import React from 'react'; +import MyDataPageComponent from './MyDataPage.component'; + +jest.mock('../../components/LandingPage/MyData.component', () => { + return jest.fn().mockReturnValue(

Mydata component

); +}); + +describe('Test MyData page component', () => { + it('Component should render', async () => { + const { container } = render(); + + const myDataPageContainer = await findByTestId( + container, + 'my-data-page-conatiner' + ); + + expect(myDataPageContainer).toBeInTheDocument(); + }); +}); diff --git a/catalog-rest-service/src/main/resources/ui/src/pages/explore/ExplorePage.component.tsx b/catalog-rest-service/src/main/resources/ui/src/pages/explore/ExplorePage.component.tsx index 723a5ec72b8..ef960a79faa 100644 --- a/catalog-rest-service/src/main/resources/ui/src/pages/explore/ExplorePage.component.tsx +++ b/catalog-rest-service/src/main/resources/ui/src/pages/explore/ExplorePage.component.tsx @@ -16,15 +16,14 @@ */ import { AxiosError } from 'axios'; -import { Bucket, SearchResponse } from 'Models'; +import { Bucket, SearchDataFunctionType, SearchResponse } from 'Models'; import React, { FunctionComponent, useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; import { searchData } from '../../axiosAPIs/miscAPI'; import Explore from '../../components/Explore/Explore.component'; import { ExploreSearchData, - FetchData, - Params, + UrlParams, } from '../../components/Explore/explore.interface'; import Loader from '../../components/Loader/Loader'; import { ERROR500 } from '../../constants/constants'; @@ -36,7 +35,7 @@ const ExplorePage: FunctionComponent = () => { const showToast = useToastContext(); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(''); - const { searchQuery, tab } = useParams(); + const { searchQuery, tab } = useParams(); const [searchText, setSearchText] = useState(searchQuery || ''); const [tableCount, setTableCount] = useState(0); const [topicCount, setTopicCount] = useState(0); @@ -151,7 +150,7 @@ const ExplorePage: FunctionComponent = () => { ); }; - const fetchData = (value: FetchData[]) => { + const fetchData = (value: SearchDataFunctionType[]) => { setIsLoading(true); const promiseValue = value.map((d) => { return searchData( diff --git a/catalog-rest-service/src/main/resources/ui/src/pages/explore/ExplorePage.component.test.tsx b/catalog-rest-service/src/main/resources/ui/src/pages/explore/ExplorePage.test.tsx similarity index 100% rename from catalog-rest-service/src/main/resources/ui/src/pages/explore/ExplorePage.component.test.tsx rename to catalog-rest-service/src/main/resources/ui/src/pages/explore/ExplorePage.test.tsx diff --git a/catalog-rest-service/src/main/resources/ui/src/pages/my-data/index.test.tsx b/catalog-rest-service/src/main/resources/ui/src/pages/my-data/index.test.tsx deleted file mode 100644 index 66f60039658..00000000000 --- a/catalog-rest-service/src/main/resources/ui/src/pages/my-data/index.test.tsx +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - - * http://www.apache.org/licenses/LICENSE-2.0 - - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -/* eslint-disable @typescript-eslint/camelcase */ - -import { - findAllByTestId, - findByTestId, - findByText, - render, -} from '@testing-library/react'; -import React from 'react'; -import { MemoryRouter } from 'react-router-dom'; -import MyDataPage from './index'; - -const mockData = { - took: 50, - timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, - }, - hits: { - total: { - value: 12, - relation: 'eq', - }, - max_score: 5, - hits: [ - { - _index: 'table_search_indexcb2cac4c-735e-474e-a968-da8d8e0b218c', - _type: '_doc', - _id: '53761de6-6c41-431f-9c4e-62d39cf4aba9', - _score: 5, - _source: { - table_id: '53761de6-6c41-431f-9c4e-62d39cf4aba9', - database: 'shopify', - service: 'bigquery', - service_type: 'BigQuery', - table_name: 'dim_address', - suggest: [ - { - input: ['bigquery.shopify.dim_address'], - weight: 5, - }, - { - input: ['dim_address'], - weight: 10, - }, - ], - description: 'test description', - table_type: null, - last_updated_timestamp: 1628498201, - column_names: ['address_id', 'shop_id'], - column_descriptions: [ - 'Unique identifier for the address.', - 'The ID of the store. This column is a foreign key reference to the shop_id column in the shops table.', - ], - monthly_stats: 0, - weekly_stats: 0, - daily_stats: 0, - tags: ['User.Phone'], - fqdn: 'bigquery.shopify.dim_address', - tier: null, - schema_description: null, - owner: '', - followers: [], - table_entity: { - id: '53761de6-6c41-431f-9c4e-62d39cf4aba9', - name: 'dim_address', - description: 'test table description', - href: 'http://test', - tableType: null, - fullyQualifiedName: 'bigquery.shopify.dim_address', - columns: [ - { - name: 'address_id', - columnDataType: 'NUMERIC', - description: 'Unique identifier for the address.', - fullyQualifiedName: 'bigquery.shopify.dim_address.address_id', - tags: [ - { - tagFQN: 'PersonalData.Personal', - labelType: 'Derived', - state: 'Suggested', - href: null, - }, - { - tagFQN: 'PII.Sensitive', - labelType: 'Derived', - state: 'Suggested', - href: null, - }, - { - tagFQN: 'User.Address', - labelType: 'Automated', - state: 'Suggested', - href: null, - }, - ], - columnConstraint: 'PRIMARY_KEY', - ordinalPosition: 1, - }, - ], - tableConstraints: null, - usageSummary: { - dailyStats: { - count: 0, - percentileRank: 0, - }, - weeklyStats: { - count: 0, - percentileRank: 0, - }, - monthlyStats: { - count: 0, - percentileRank: 0, - }, - date: '2021-08-09', - }, - owner: null, - followers: [], - database: { - id: '2d04ddc1-4c9b-43a3-85fb-3de204d67f32', - type: 'database', - name: 'shopify', - description: - 'This database contains tables related to shopify order and related dimensions', - href: 'http://test/test', - }, - tags: [], - joins: null, - sampleData: null, - }, - }, - highlight: { - description: ['data test'], - table_name: ['dim_address'], - }, - }, - ], - }, - aggregations: { - 'sterms#Tier': { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [], - }, - 'sterms#Tags': { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'PII.Sensitive', - doc_count: 7, - }, - ], - }, - 'sterms#Service Type': { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'BigQuery', - doc_count: 12, - }, - ], - }, - }, -}; - -jest.mock('../../axiosAPIs/miscAPI', () => ({ - searchData: jest - .fn() - .mockImplementation(() => Promise.resolve({ data: mockData })), -})); - -jest.mock('../../components/searched-data/SearchedData', () => { - return jest - .fn() - .mockImplementation(({ children }: { children: React.ReactNode }) => ( -
-
{children}
-
- )); -}); - -jest.mock('../../components/recently-viewed/RecentlyViewed', () => { - return jest.fn().mockReturnValue(

RecentlyViewed

); -}); - -jest.mock('../../components/my-data/MyDataHeader', () => { - return jest.fn().mockReturnValue(

MyDataHeader

); -}); - -jest.mock('../../utils/ServiceUtils', () => ({ - getAllServices: jest - .fn() - .mockImplementation(() => Promise.resolve(['test', 'test2', 'test3'])), - getEntityCountByService: jest - .fn() - .mockReturnValue({ tableCount: 4, topicCount: 5, dashboardCount: 6 }), - getTotalEntityCountByService: jest.fn().mockReturnValue(2), -})); - -describe('Test MyData page', () => { - it('Check if there is an element in the page', async () => { - const { container } = render(, { - wrapper: MemoryRouter, - }); - const pageContainer = await findByTestId(container, 'fluid-container'); - const searchData = await findByTestId(container, 'search-data'); - const wrappedContent = await findByTestId(container, 'wrapped-content'); - const tabs = await findAllByTestId(container, 'tab'); - const myDataHeader = await findByText(container, /MyDataHeader/i); - - expect(pageContainer).toBeInTheDocument(); - expect(searchData).toBeInTheDocument(); - expect(wrappedContent).toBeInTheDocument(); - expect(myDataHeader).toBeInTheDocument(); - expect(tabs.length).toBe(3); - }); -}); diff --git a/catalog-rest-service/src/main/resources/ui/src/pages/tour-page/index.tsx b/catalog-rest-service/src/main/resources/ui/src/pages/tour-page/index.tsx index f9dc6bced8a..4d8eccef152 100644 --- a/catalog-rest-service/src/main/resources/ui/src/pages/tour-page/index.tsx +++ b/catalog-rest-service/src/main/resources/ui/src/pages/tour-page/index.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from 'react'; import { FirstTimeUserModal } from '../../components/Modals/FirstTimeUserModal/FirstTimeUserModal'; import { useTour } from '../../hooks/useTour'; -import MyDataPage from '../my-data'; +// import MyDataPage from '../../components/LandingPage/MyData.component'; const TourPage = () => { const [showFirstTimeUserModal, setShowFirstTimeUserModal] = useState(true); @@ -17,7 +17,7 @@ const TourPage = () => { return (
- + {/* */} {showFirstTimeUserModal && ( setShowFirstTimeUserModal(true)} diff --git a/catalog-rest-service/src/main/resources/ui/src/router/AuthenticatedAppRouter.tsx b/catalog-rest-service/src/main/resources/ui/src/router/AuthenticatedAppRouter.tsx index dab0b264afa..dd1a06e859d 100644 --- a/catalog-rest-service/src/main/resources/ui/src/router/AuthenticatedAppRouter.tsx +++ b/catalog-rest-service/src/main/resources/ui/src/router/AuthenticatedAppRouter.tsx @@ -25,7 +25,7 @@ import MyDashBoardPage from '../pages/dashboard-details'; import DatabaseDetails from '../pages/database-details/index'; import DatasetDetailsPage from '../pages/DatasetDetailsPage/DatasetDetailsPage.component'; import ExplorePage from '../pages/explore/ExplorePage.component'; -import MyDataPage from '../pages/my-data'; +import MyDataPage from '../pages/MyDataPage/MyDataPage.component'; import MyPipelinePage from '../pages/Pipeline-details'; import ReportsPage from '../pages/reports'; import Scorecard from '../pages/scorecard';