forked from datahub-project/datahub
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ingestion) Add frontend connection test for Snowflake (datahub-p…
- Loading branch information
1 parent
160f8e8
commit d1f0c7a
Showing
12 changed files
with
514 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
74 changes: 74 additions & 0 deletions
74
...eact/src/app/ingest/source/builder/RecipeForm/TestConnection/ConnectionCapabilityView.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { CheckOutlined, CloseOutlined, QuestionCircleOutlined } from '@ant-design/icons'; | ||
import { Tooltip } from 'antd'; | ||
import React from 'react'; | ||
import { green, red } from '@ant-design/colors'; | ||
import styled from 'styled-components/macro'; | ||
import { ANTD_GRAY } from '../../../../../entity/shared/constants'; | ||
|
||
const CapabilityWrapper = styled.div` | ||
align-items: center; | ||
display: flex; | ||
margin: 10px 0; | ||
`; | ||
|
||
const CapabilityName = styled.span` | ||
color: ${ANTD_GRAY[8]}; | ||
font-size: 18px; | ||
margin-right: 12px; | ||
`; | ||
|
||
const CapabilityMessage = styled.span<{ success: boolean }>` | ||
color: ${(props) => (props.success ? `${green[6]}` : `${red[5]}`)}; | ||
font-size: 12px; | ||
flex: 1; | ||
padding-left: 4px; | ||
`; | ||
|
||
const StyledQuestion = styled(QuestionCircleOutlined)` | ||
color: rgba(0, 0, 0, 0.45); | ||
margin-left: 4px; | ||
`; | ||
|
||
export const StyledCheck = styled(CheckOutlined)` | ||
color: ${green[6]}; | ||
margin-right: 15px; | ||
`; | ||
|
||
export const StyledClose = styled(CloseOutlined)` | ||
color: ${red[5]}; | ||
margin-right: 15px; | ||
`; | ||
|
||
const NumberWrapper = styled.span` | ||
margin-right: 8px; | ||
`; | ||
|
||
interface Props { | ||
success: boolean; | ||
capability: string; | ||
displayMessage: string | null; | ||
tooltipMessage: string | null; | ||
number?: number; | ||
} | ||
|
||
function ConnectionCapabilityView({ success, capability, displayMessage, tooltipMessage, number }: Props) { | ||
return ( | ||
<CapabilityWrapper> | ||
<CapabilityName> | ||
{success ? <StyledCheck /> : <StyledClose />} | ||
{number ? <NumberWrapper>{number}.</NumberWrapper> : ''} | ||
{capability} | ||
</CapabilityName> | ||
<CapabilityMessage success={success}> | ||
{displayMessage} | ||
{tooltipMessage && ( | ||
<Tooltip overlay={tooltipMessage}> | ||
<StyledQuestion /> | ||
</Tooltip> | ||
)} | ||
</CapabilityMessage> | ||
</CapabilityWrapper> | ||
); | ||
} | ||
|
||
export default ConnectionCapabilityView; |
128 changes: 128 additions & 0 deletions
128
...eb-react/src/app/ingest/source/builder/RecipeForm/TestConnection/TestConnectionButton.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import { CheckCircleOutlined } from '@ant-design/icons'; | ||
import { Button, message } from 'antd'; | ||
import React, { useEffect, useState } from 'react'; | ||
import { green } from '@ant-design/colors'; | ||
import { | ||
useCreateTestConnectionRequestMutation, | ||
useGetIngestionExecutionRequestLazyQuery, | ||
} from '../../../../../../graphql/ingestion.generated'; | ||
import { FAILURE, getSourceConfigs, RUNNING, yamlToJson } from '../../../utils'; | ||
import { TestConnectionResult } from './types'; | ||
import TestConnectionModal from './TestConnectionModal'; | ||
|
||
export function getRecipeJson(recipeYaml: string) { | ||
// Convert the recipe into it's json representation, and catch + report exceptions while we do it. | ||
let recipeJson; | ||
try { | ||
recipeJson = yamlToJson(recipeYaml); | ||
} catch (e) { | ||
const messageText = (e as any).parsedLine | ||
? `Please fix line ${(e as any).parsedLine} in your recipe.` | ||
: 'Please check your recipe configuration.'; | ||
message.warn(`Found invalid YAML. ${messageText}`); | ||
return null; | ||
} | ||
return recipeJson; | ||
} | ||
|
||
interface Props { | ||
type: string; | ||
recipe: string; | ||
} | ||
|
||
function TestConnectionButton(props: Props) { | ||
const { type, recipe } = props; | ||
const [isLoading, setIsLoading] = useState(false); | ||
const [isModalVisible, setIsModalVisible] = useState(false); | ||
const [pollingInterval, setPollingInterval] = useState<null | NodeJS.Timeout>(null); | ||
const [testConnectionResult, setTestConnectionResult] = useState<null | TestConnectionResult>(null); | ||
const [createTestConnectionRequest, { data: requestData }] = useCreateTestConnectionRequestMutation(); | ||
const [getIngestionExecutionRequest, { data: resultData, loading }] = useGetIngestionExecutionRequestLazyQuery(); | ||
|
||
const sourceConfigs = getSourceConfigs(type); | ||
|
||
useEffect(() => { | ||
if (requestData && requestData.createTestConnectionRequest) { | ||
const interval = setInterval( | ||
() => | ||
getIngestionExecutionRequest({ | ||
variables: { urn: requestData.createTestConnectionRequest as string }, | ||
}), | ||
2000, | ||
); | ||
setIsLoading(true); | ||
setIsModalVisible(true); | ||
setPollingInterval(interval); | ||
} | ||
}, [requestData, getIngestionExecutionRequest]); | ||
|
||
useEffect(() => { | ||
if (!loading && resultData) { | ||
const result = resultData.executionRequest?.result; | ||
if (result && result.status !== RUNNING) { | ||
if (result.status === FAILURE) { | ||
message.error( | ||
'Something went wrong with your connection test. Please check your recipe and try again.', | ||
); | ||
setIsModalVisible(false); | ||
} | ||
if (result.structuredReport) { | ||
const testConnectionReport = JSON.parse(result.structuredReport.serializedValue); | ||
setTestConnectionResult(testConnectionReport); | ||
} | ||
if (pollingInterval) clearInterval(pollingInterval); | ||
setIsLoading(false); | ||
} | ||
} | ||
}, [resultData, pollingInterval, loading]); | ||
|
||
useEffect(() => { | ||
if (!isModalVisible && pollingInterval) { | ||
clearInterval(pollingInterval); | ||
} | ||
}, [isModalVisible, pollingInterval]); | ||
|
||
function testConnection() { | ||
const recipeJson = getRecipeJson(recipe); | ||
if (recipeJson) { | ||
createTestConnectionRequest({ variables: { input: { recipe: recipeJson } } }) | ||
.then((res) => | ||
getIngestionExecutionRequest({ | ||
variables: { urn: res.data?.createTestConnectionRequest as string }, | ||
}), | ||
) | ||
.catch(() => { | ||
message.error( | ||
'There was an unexpected error when trying to test your connection. Please try again.', | ||
); | ||
}); | ||
|
||
setIsLoading(true); | ||
setIsModalVisible(true); | ||
} | ||
} | ||
|
||
const internalFailure = !!testConnectionResult?.internal_failure; | ||
const basicConnectivityFailure = testConnectionResult?.basic_connectivity?.capable === false; | ||
const testConnectionFailed = internalFailure || basicConnectivityFailure; | ||
|
||
return ( | ||
<> | ||
<Button onClick={testConnection}> | ||
<CheckCircleOutlined style={{ color: green[5] }} /> | ||
Test Connection | ||
</Button> | ||
{isModalVisible && ( | ||
<TestConnectionModal | ||
isLoading={isLoading} | ||
testConnectionFailed={testConnectionFailed} | ||
sourceConfig={sourceConfigs} | ||
testConnectionResult={testConnectionResult} | ||
hideModal={() => setIsModalVisible(false)} | ||
/> | ||
)} | ||
</> | ||
); | ||
} | ||
|
||
export default TestConnectionButton; |
Oops, something went wrong.