Fix #3443: Add GitHub star button to sandbox (#3597)

This commit is contained in:
darth-coder00 2022-03-26 21:40:23 +05:30 committed by GitHub
parent 0c12a55ebf
commit 6dbb1e578d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 262 additions and 27 deletions

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1792 1792" fill="#37352f"><path d="M1408 928v320q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h704q14 0 23 9t9 23v64q0 14-9 23t-23 9h-704q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-320q0-14 9-23t23-9h64q14 0 23 9t9 23zm384-864v512q0 26-19 45t-45 19-45-19l-176-176-652 652q-10 10-23 10t-23-10l-114-114q-10-10-10-23t10-23l652-652-176-176q-19-19-19-45t19-45 45-19h512q26 0 45 19t19 45z"/></svg>

After

Width:  |  Height:  |  Size: 520 B

View File

@ -0,0 +1 @@
<svg data-v-49ba913f="" width="80" height="75" viewBox="0 0 80 75" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path fill="#F6C247" d="M63.196 48.5l2.126 26.188-24.585-11.316-25.364 10.696 1.72-25.563L0 29.013l27.775-7.464L40.135 0l14.262 23.331L80 28.688 63.196 48.5"></path><path d="M60.097 48.955l1.657 20.42-15.109-6.954a1.755 1.755 0 01-1.022-1.61 995.1 995.1 0 00.036-6.576c0-1.89-.65-3.128-1.379-3.753 4.523-.503 9.268-2.216 9.268-10.004 0-2.212-.786-4.021-2.087-5.438.21-.513.906-2.574-.202-5.365 0 0-1.7-.545-5.575 2.078a19.514 19.514 0 00-5.08-.683 19.49 19.49 0 00-5.082.683c-3.877-2.623-5.58-2.078-5.58-2.078-1.106 2.79-.409 4.852-.2 5.365-1.298 1.417-2.09 3.226-2.09 5.438 0 7.77 4.738 9.507 9.246 10.02-.58.506-1.104 1.399-1.289 2.709-1.156.52-4.096 1.414-5.907-1.685 0 0-.717-1.643-2.754-1.787 0 0-1.982-.026-.14 1.232 0 0 1.314.754 1.9 2.126 0 0 1.19 3.942 6.837 2.718.006.981.014 3.32.02 5.113a1.756 1.756 0 01-1.075 1.624l-15.336 6.468 1.452-21.584-.973-1.11-13.54-15.443 22.64-6.085 1.43-.384L40.399 6.562l12.103 19.805 1.51.316 20.051 4.195-14.085 16.61.12 1.467" fill="#DE852E"></path></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -59,6 +59,10 @@ export const fetchAuthorizerConfig: Function = (): Promise<AxiosResponse> => {
return APIClient.get('/config/authorizer');
};
export const fetchSandboxConfig = (): Promise<AxiosResponse> => {
return APIClient.get('/config/sandbox');
};
export const getSuggestions: Function = (
queryString: string,
searchIndex?: string

View File

@ -0,0 +1,82 @@
/*
* Copyright 2021 Collate
* Licensed 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 { isNil } from 'lodash';
import React, { CSSProperties, FunctionComponent, useState } from 'react';
import SVGIcons, { Icons } from '../../utils/SvgUtils';
import PopOver from '../common/popover/PopOver';
const GithubStarButton: FunctionComponent = () => {
const [open, setOpen] = useState<boolean>(true);
const handleClick = (isOpen?: boolean) => {
setOpen((pre) => (!isNil(isOpen) ? isOpen : !pre));
};
return (
<div className="tw-fixed tw-bottom-8 tw-right-8">
<PopOver
delay={100}
html={
<>
<a
className="link-text-grey tw-text-sm tw-font-medium"
href="https://github.com/open-metadata/OpenMetadata"
rel="noopener noreferrer"
target="_blank">
<span className="tw-mr-1">Star us on Github</span>
<SVGIcons
alt="external-link"
className="tw-align-middle"
icon={Icons.EXTERNAL_LINK_GREY}
width="12px"
/>
</a>
</>
}
open={open}
popperOptions={{
modifiers: {
addZIndex: {
enabled: true,
order: 810,
// react-tippy has this dataObject that can be of any type
fn: (data: { styles: CSSProperties }) => ({
...data,
styles: {
...data.styles,
zIndex: 9990,
},
}),
},
},
}}
position="left"
theme="light"
trigger="click">
<button
className="tw-h-12 tw-w-12 tw-rounded-full tw-shadow-lg tw-cursor-pointer tw-bg-white"
onClick={() => handleClick()}>
<SVGIcons
alt="gh-star"
data-testid="gh-star"
icon={Icons.GITHUB_STAR}
width="30"
/>
</button>
</PopOver>
</div>
);
};
export default GithubStarButton;

View File

@ -30,7 +30,8 @@ const PopOver: React.FC<PopOverProp> = ({
trigger,
theme = 'dark',
sticky = false,
}): JSX.Element => {
...props
}: PopOverProp): JSX.Element => {
return (
<Tooltip
arrow={arrow}
@ -43,7 +44,8 @@ const PopOver: React.FC<PopOverProp> = ({
sticky={sticky}
theme={theme}
title={title || ''}
trigger={trigger}>
trigger={trigger}
{...props}>
{children}
</Tooltip>
);

View File

@ -12,13 +12,14 @@
*/
import React, { ReactNode } from 'react';
import { TooltipProps } from 'react-tippy';
export type Position = 'top' | 'left' | 'bottom' | 'right';
export type Trigger = 'mouseenter' | 'focus' | 'click' | 'manual';
export type Theme = 'dark' | 'light' | 'transparent';
export type Size = 'small' | 'regular' | 'big';
export type PopOverProp = {
export interface PopOverProp extends TooltipProps {
html?: React.ReactElement;
title?: string;
arrow?: boolean;
@ -31,4 +32,4 @@ export type PopOverProp = {
delay?: number;
hideDelay?: number;
sticky?: boolean;
};
}

View File

@ -59,6 +59,8 @@ const jsonData = {
'fetch-ingestion-error': 'Error while fetching ingestion workflow!',
'fetch-service-error': 'Error while fetching service details!',
'unexpected-server-response': 'Unexpected response from server!',
'update-chart-error': 'Error while updating charts!',
'update-owner-error': 'Error while updating owner',
'update-glossary-term-error': 'Error while updating glossary term!',
@ -70,7 +72,6 @@ const jsonData = {
'update-entity-unfollow-error': 'Error while unfollowing entity!',
'update-ingestion-error': 'Error while updating ingestion workflow',
'update-service-config-error': 'Error while updating ingestion workflow',
'unexpected-server-response': 'Unexpected response from server!',
},
'api-success-messages': {
'create-conversation': 'Conversation created successfully!',

View File

@ -20,13 +20,14 @@ import {
FormatedTableData,
SearchResponse,
} from 'Models';
import React, { useEffect, useState } from 'react';
import React, { Fragment, useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom';
import AppState from '../../AppState';
import { getAirflowPipelines } from '../../axiosAPIs/airflowPipelineAPI';
import { getFeedsWithFilter, postFeedById } from '../../axiosAPIs/feedsAPI';
import { searchData } from '../../axiosAPIs/miscAPI';
import { fetchSandboxConfig, searchData } from '../../axiosAPIs/miscAPI';
import PageContainerV1 from '../../components/containers/PageContainerV1';
import GithubStarButton from '../../components/GithubStarButton/GithubStarButton';
import Loader from '../../components/Loader/Loader';
import MyData from '../../components/MyData/MyData.component';
import {
@ -41,6 +42,7 @@ import {
import { FeedFilter, Ownership } from '../../enums/mydata.enum';
import { useAuth } from '../../hooks/authHooks';
import useToastContext from '../../hooks/useToastContext';
import jsonData from '../../jsons/en';
import { formatDataResponse } from '../../utils/APIUtils';
import { getEntityCountByType } from '../../utils/EntityUtils';
import { deletePost, getUpdatedThread } from '../../utils/FeedUtils';
@ -64,6 +66,7 @@ const MyDataPage = () => {
const [feedFilter, setFeedFilter] = useState<FeedFilter>(FeedFilter.ALL);
const [entityThread, setEntityThread] = useState<EntityThread[]>([]);
const [isFeedLoading, setIsFeedLoading] = useState<boolean>(false);
const [isSandbox, setIsSandbox] = useState<boolean>(false);
const feedFilterHandler = (filter: FeedFilter) => {
setFeedFilter(filter);
};
@ -219,7 +222,29 @@ const MyDataPage = () => {
showToast({ variant: 'error', body: message ?? onErrorText });
});
};
const fetchOMDMode = () => {
fetchSandboxConfig()
.then((res) => {
if (res.data) {
setIsSandbox(Boolean(res.data.sandboxModeEnabled));
} else {
throw '';
}
})
.catch((err: AxiosError) => {
showToast({
variant: 'error',
body:
err.response?.data?.message ||
jsonData['api-error-messages']['unexpected-server-response'],
});
setIsSandbox(false);
});
};
useEffect(() => {
fetchOMDMode();
fetchData(true);
}, []);
@ -243,21 +268,24 @@ const MyDataPage = () => {
!isUndefined(entityCounts) &&
!isUndefined(ingestionCount) &&
!isLoading ? (
<MyData
countServices={countServices}
deletePostHandler={deletePostHandler}
entityCounts={entityCounts}
error={error}
feedData={entityThread || []}
feedFilter={feedFilter}
feedFilterHandler={feedFilterHandler}
followedData={followedData || []}
ingestionCount={ingestionCount}
isFeedLoading={isFeedLoading}
ownedData={ownedData || []}
postFeedHandler={postFeedHandler}
searchResult={searchResult}
/>
<Fragment>
<MyData
countServices={countServices}
deletePostHandler={deletePostHandler}
entityCounts={entityCounts}
error={error}
feedData={entityThread || []}
feedFilter={feedFilter}
feedFilterHandler={feedFilterHandler}
followedData={followedData || []}
ingestionCount={ingestionCount}
isFeedLoading={isFeedLoading}
ownedData={ownedData || []}
postFeedHandler={postFeedHandler}
searchResult={searchResult}
/>
{isSandbox ? <GithubStarButton /> : null}
</Fragment>
) : (
<Loader />
)}

View File

@ -11,10 +11,19 @@
* limitations under the License.
*/
import { findByText, render } from '@testing-library/react';
import { findByText, queryByText, render } from '@testing-library/react';
import React, { ReactNode } from 'react';
import { fetchSandboxConfig } from '../../axiosAPIs/miscAPI';
import MyDataPageComponent from './MyDataPage.component';
const mockAuth = {
isAuthDisabled: true,
};
const mockErrors = {
sandboxMode: 'SandboxModeError',
};
jest.mock('../../components/MyData/MyData.component', () => {
return jest
.fn()
@ -34,6 +43,13 @@ jest.mock('../../axiosAPIs/miscAPI', () => ({
},
})
),
fetchSandboxConfig: jest.fn().mockImplementation(() =>
Promise.resolve({
data: {
sandboxModeEnabled: false,
},
})
),
}));
jest.mock('../../axiosAPIs/airflowPipelineAPI', () => ({
@ -46,6 +62,16 @@ jest.mock('../../axiosAPIs/airflowPipelineAPI', () => ({
),
}));
jest.mock('../../axiosAPIs/feedsAPI', () => ({
getFeedsWithFilter: jest.fn().mockImplementation(() =>
Promise.resolve({
data: {
data: [],
},
})
),
}));
jest.mock('../../utils/ServiceUtils', () => ({
getAllServices: jest.fn().mockImplementation(() => Promise.resolve(['test'])),
getEntityCountByService: jest.fn().mockReturnValue({
@ -56,9 +82,9 @@ jest.mock('../../utils/ServiceUtils', () => ({
}),
}));
const mockAuth = {
isAuthDisabled: true,
};
jest.mock('../../utils/CommonUtils', () => ({
isSandboxOMD: jest.fn().mockReturnValue(true),
}));
jest.mock('../../hooks/authHooks', () => ({
useAuth: jest.fn(() => mockAuth),
@ -86,12 +112,84 @@ jest.mock('../../components/MyData/MyData.component', () => {
return jest.fn().mockImplementation(() => <p>MyData.component</p>);
});
jest.mock('../../components/GithubStarButton/GithubStarButton', () => {
return jest.fn().mockImplementation(() => <p>GithubStarButton.component</p>);
});
jest.mock('../../components/common/Toast/Toast', () => {
return jest.fn().mockImplementation(() => <p>GithubStarButton.component</p>);
});
describe('Test MyData page component', () => {
it('Component should render', async () => {
const { container } = render(<MyDataPageComponent />);
const myData = await findByText(container, /MyData.component/i);
const githubStarButton = await queryByText(
container,
/GithubStarButton.component/i
);
expect(myData).toBeInTheDocument();
expect(githubStarButton).not.toBeInTheDocument();
});
it('Component should render in sandbox mode', async () => {
(fetchSandboxConfig as jest.Mock).mockImplementationOnce(() =>
Promise.resolve({
data: {
sandboxModeEnabled: true,
},
})
);
const { container } = render(<MyDataPageComponent />);
const myData = await findByText(container, /MyData.component/i);
const githubStarButton = await findByText(
container,
/GithubStarButton.component/i
);
expect(myData).toBeInTheDocument();
expect(githubStarButton).toBeInTheDocument();
});
describe('render Sad Paths', () => {
it('show error message on failing of config/sandbox api', async () => {
(fetchSandboxConfig as jest.Mock).mockImplementationOnce(() =>
Promise.reject({
response: { data: { message: mockErrors.sandboxMode } },
})
);
const { container } = render(<MyDataPageComponent />);
const myData = await findByText(container, /MyData.component/i);
const githubStarButton = await queryByText(
container,
/GithubStarButton.component/i
);
expect(myData).toBeInTheDocument();
expect(githubStarButton).not.toBeInTheDocument();
});
it('show error message on no data from config/sandbox api', async () => {
(fetchSandboxConfig as jest.Mock).mockImplementationOnce(() =>
Promise.resolve({})
);
const { container } = render(<MyDataPageComponent />);
const myData = await findByText(container, /MyData.component/i);
const githubStarButton = await queryByText(
container,
/GithubStarButton.component/i
);
expect(myData).toBeInTheDocument();
expect(githubStarButton).not.toBeInTheDocument();
});
});
});

View File

@ -140,6 +140,11 @@
@apply tw-text-primary-hover hover:tw-text-primary-hover focus:tw-text-primary-hover hover:tw-underline focus:tw-underline tw-cursor-pointer;
}
a[href].link-text-grey,
.link-text-grey {
@apply tw-text-grey-body hover:tw-text-grey-body focus:tw-text-grey-body hover:tw-underline focus:tw-underline tw-cursor-pointer;
}
.page-container {
@apply tw-bg-body-main;
}

View File

@ -45,9 +45,11 @@ import IconDoc from '../assets/svg/doc.svg';
import IconEditBlack from '../assets/svg/edit-black.svg';
import IconEditPrimary from '../assets/svg/edit-primary.svg';
import IconError from '../assets/svg/error.svg';
import IconExternalLinkGrey from '../assets/svg/external-link-grey.svg';
import IconExternalLinkWhite from '../assets/svg/external-link-white.svg';
import IconExternalLink from '../assets/svg/external-link.svg';
import IconFitView from '../assets/svg/fitview.svg';
import IconGithubStar from '../assets/svg/github-star.svg';
import IconCheckCircle from '../assets/svg/ic-check-circle.svg';
import IconDelete from '../assets/svg/ic-delete.svg';
import IconDownArrow from '../assets/svg/ic-down-arrow.svg';
@ -214,6 +216,7 @@ export const Icons = {
SLACK_GREY: 'slack-grey',
EXTERNAL_LINK: 'external-link',
EXTERNAL_LINK_WHITE: 'external-link-white',
EXTERNAL_LINK_GREY: 'external-link-grey',
PROFILER: 'icon-profiler',
PIPELINE: 'pipeline',
PIPELINE_GREY: 'pipeline-grey',
@ -261,6 +264,7 @@ export const Icons = {
WEBHOOK: 'icon-webhook',
WEBHOOK_GREY: 'icon-webhook-grey',
WEBHOOK_PRIMARY: 'icon-webhook-primary',
GITHUB_STAR: 'icon-github-star',
};
const SVGIcons: FunctionComponent<Props> = ({
@ -562,6 +566,10 @@ const SVGIcons: FunctionComponent<Props> = ({
case Icons.EXTERNAL_LINK_WHITE:
IconComponent = IconExternalLinkWhite;
break;
case Icons.EXTERNAL_LINK_GREY:
IconComponent = IconExternalLinkGrey;
break;
case Icons.PROFILER:
IconComponent = IconProfiler;
@ -756,6 +764,10 @@ const SVGIcons: FunctionComponent<Props> = ({
case Icons.WEBHOOK_PRIMARY:
IconComponent = IconWebhookPrimary;
break;
case Icons.GITHUB_STAR:
IconComponent = IconGithubStar;
break;
default: