Fix(ui): Force Test Connection (#21734)

* added force test connection

* fixed test connection appearns on input field click

* fixed test connection

* removed blocker when ingestion is off

* made test connection consistent

* fixed test cases

* removed condition on test

* removed delete initiated condition

* fixed custom service condition
This commit is contained in:
Dhruv Parmar 2025-08-19 14:08:00 +05:30 committed by GitHub
parent f39f57ddcd
commit b1a1cd89a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 50 additions and 19 deletions

View File

@ -19,6 +19,7 @@ import {
toastNotification, toastNotification,
uuid, uuid,
} from '../../utils/common'; } from '../../utils/common';
import { testConnection } from '../../utils/serviceIngestion';
import { settingClick } from '../../utils/sidebar'; import { settingClick } from '../../utils/sidebar';
const apiServiceConfig = { const apiServiceConfig = {
@ -55,6 +56,7 @@ test.describe('API service', () => {
.fill(apiServiceConfig.openAPISchemaURL); .fill(apiServiceConfig.openAPISchemaURL);
await page.locator('#root\\/token').fill(apiServiceConfig.token); await page.locator('#root\\/token').fill(apiServiceConfig.token);
await testConnection(page);
await page.getByTestId('submit-btn').click(); await page.getByTestId('submit-btn').click();
const autoPilotApplicationRequest = page.waitForRequest( const autoPilotApplicationRequest = page.waitForRequest(

View File

@ -26,7 +26,7 @@ class MlFlowIngestionClass extends ServiceBaseClass {
shouldAddDefaultFilters?: boolean; shouldAddDefaultFilters?: boolean;
}) { }) {
const { const {
shouldTestConnection = false, shouldTestConnection = true,
shouldAddIngestion = false, shouldAddIngestion = false,
shouldAddDefaultFilters = false, shouldAddDefaultFilters = false,
} = extraParams ?? {}; } = extraParams ?? {};

View File

@ -104,6 +104,8 @@ class ServiceBaseClass {
await this.fillConnectionDetails(page); await this.fillConnectionDetails(page);
if (this.shouldTestConnection) { if (this.shouldTestConnection) {
expect(page.getByTestId('next-button')).not.toBeVisible();
await testConnection(page); await testConnection(page);
} }

View File

@ -159,12 +159,19 @@ export const testConnection = async (page: Page) => {
const warningBadge = page.locator('[data-testid="warning-badge"]'); const warningBadge = page.locator('[data-testid="warning-badge"]');
await expect(successBadge.or(warningBadge)).toBeVisible({ const failBadge = page.locator('[data-testid="fail-badge"]');
await expect(successBadge.or(warningBadge).or(failBadge)).toBeVisible({
timeout: 2.5 * 60 * 1000, timeout: 2.5 * 60 * 1000,
}); });
await expect(page.getByTestId('messag-text')).toContainText( await expect(page.getByTestId('messag-text')).toContainText(
/Connection test was successful.|Test connection partially successful: Some steps had failures, we will only ingest partial metadata. Click here to view details./g new RegExp(
'Connection test was successful.|' +
'Test connection partially successful: Some steps had failures, we will only ingest partial metadata. Click here to view details.|' +
'Test connection failed, please validate your connection and permissions for the failed steps.',
'g'
)
); );
}; };

View File

@ -37,6 +37,7 @@ import {
getFilteredSchema, getFilteredSchema,
getUISchemaWithNestedDefaultFilterFieldsHidden, getUISchemaWithNestedDefaultFilterFieldsHidden,
} from '../../../../utils/ServiceConnectionUtils'; } from '../../../../utils/ServiceConnectionUtils';
import { shouldTestConnection } from '../../../../utils/ServiceUtils';
import AirflowMessageBanner from '../../../common/AirflowMessageBanner/AirflowMessageBanner'; import AirflowMessageBanner from '../../../common/AirflowMessageBanner/AirflowMessageBanner';
import BooleanFieldTemplate from '../../../common/Form/JSONSchema/JSONSchemaTemplate/BooleanFieldTemplate'; import BooleanFieldTemplate from '../../../common/Form/JSONSchema/JSONSchemaTemplate/BooleanFieldTemplate';
import WorkflowArrayFieldTemplate from '../../../common/Form/JSONSchema/JSONSchemaTemplate/WorkflowArrayFieldTemplate'; import WorkflowArrayFieldTemplate from '../../../common/Form/JSONSchema/JSONSchemaTemplate/WorkflowArrayFieldTemplate';
@ -60,10 +61,17 @@ const ConnectionConfigForm = ({
const { inlineAlertDetails } = useApplicationStore(); const { inlineAlertDetails } = useApplicationStore();
const { t } = useTranslation(); const { t } = useTranslation();
const [ingestionRunner, setIngestionRunner] = useState<string | undefined>(); const [ingestionRunner, setIngestionRunner] = useState<string | undefined>();
const { isAirflowAvailable, platform } = useAirflowStatus();
const allowTestConn = useMemo(() => {
return shouldTestConnection(serviceType);
}, [serviceType]);
const [hasTestedConnection, setHasTestedConnection] = useState(
!isAirflowAvailable || !allowTestConn || disableTestConnection
);
const formRef = useRef<Form<ConfigData>>(null); const formRef = useRef<Form<ConfigData>>(null);
const { isAirflowAvailable, platform } = useAirflowStatus();
const [hostIp, setHostIp] = useState<string>(); const [hostIp, setHostIp] = useState<string>();
const fetchHostIp = async () => { const fetchHostIp = async () => {
@ -153,6 +161,10 @@ const ConnectionConfigForm = ({
} }
}, [formRef.current?.state?.formData]); }, [formRef.current?.state?.formData]);
const handleTestConnection = () => {
setHasTestedConnection(true);
};
return ( return (
<Fragment> <Fragment>
<AirflowMessageBanner /> <AirflowMessageBanner />
@ -160,6 +172,7 @@ const ConnectionConfigForm = ({
cancelText={cancelText ?? ''} cancelText={cancelText ?? ''}
fields={customFields} fields={customFields}
formData={validConfig} formData={validConfig}
hasTestedConnection={hasTestedConnection}
okText={okText ?? ''} okText={okText ?? ''}
ref={formRef} ref={formRef}
schema={schemaWithoutDefaultFilterPatternFields} schema={schemaWithoutDefaultFilterPatternFields}
@ -190,19 +203,18 @@ const ConnectionConfigForm = ({
type="info" type="info"
/> />
)} )}
{!isEmpty(connSch.schema) && {!isEmpty(connSch.schema) && (
isAirflowAvailable && <TestConnection
formRef.current?.state?.formData && ( connectionType={serviceType}
<TestConnection getData={() => formRef.current?.state?.formData}
connectionType={serviceType} hostIp={hostIp}
getData={() => formRef.current?.state?.formData} isTestingDisabled={disableTestConnection}
hostIp={hostIp} serviceCategory={serviceCategory}
isTestingDisabled={disableTestConnection} serviceName={data?.name}
serviceCategory={serviceCategory} onTestConnection={handleTestConnection}
serviceName={data?.name} onValidateFormRequiredFields={handleRequiredFieldsValidation}
onValidateFormRequiredFields={handleRequiredFieldsValidation} />
/> )}
)}
{!isUndefined(inlineAlertDetails) && ( {!isUndefined(inlineAlertDetails) && (
<InlineAlert alertClassName="m-t-xs" {...inlineAlertDetails} /> <InlineAlert alertClassName="m-t-xs" {...inlineAlertDetails} />
)} )}

View File

@ -42,6 +42,7 @@ export interface Props extends FormProps {
status?: LoadingState; status?: LoadingState;
onCancel?: () => void; onCancel?: () => void;
useSelectWidget?: boolean; useSelectWidget?: boolean;
hasTestedConnection?: boolean;
} }
const FormBuilder = forwardRef<Form, Props>( const FormBuilder = forwardRef<Form, Props>(
@ -61,6 +62,7 @@ const FormBuilder = forwardRef<Form, Props>(
onFocus, onFocus,
useSelectWidget = false, useSelectWidget = false,
children, children,
hasTestedConnection,
...props ...props
}, },
ref ref
@ -94,6 +96,9 @@ const FormBuilder = forwardRef<Form, Props>(
}; };
const submitButton = useMemo(() => { const submitButton = useMemo(() => {
if (hasTestedConnection === false) {
return null;
}
if (status === 'waiting') { if (status === 'waiting') {
return ( return (
<Button <Button
@ -124,7 +129,7 @@ const FormBuilder = forwardRef<Form, Props>(
</Button> </Button>
); );
} }
}, [status, isLoading, okText]); }, [status, isLoading, okText, hasTestedConnection]);
return ( return (
<Form <Form

View File

@ -23,6 +23,7 @@ export interface TestConnectionProps {
serviceName?: string; serviceName?: string;
shouldValidateForm?: boolean; shouldValidateForm?: boolean;
onValidateFormRequiredFields?: () => boolean; onValidateFormRequiredFields?: () => boolean;
onTestConnection?: () => void;
hostIp?: string; hostIp?: string;
} }

View File

@ -70,6 +70,7 @@ const TestConnection: FC<TestConnectionProps> = ({
onValidateFormRequiredFields, onValidateFormRequiredFields,
shouldValidateForm = true, shouldValidateForm = true,
showDetails = true, showDetails = true,
onTestConnection,
hostIp, hostIp,
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
@ -372,6 +373,8 @@ const TestConnection: FC<TestConnectionProps> = ({
if (workflowId) { if (workflowId) {
await handleDeleteWorkflow(workflowId); await handleDeleteWorkflow(workflowId);
} }
} finally {
onTestConnection?.();
} }
}; };

View File

@ -80,7 +80,6 @@ const AddServicePage = () => {
const [saveServiceState, setSaveServiceState] = const [saveServiceState, setSaveServiceState] =
useState<LoadingState>('initial'); useState<LoadingState>('initial');
const [activeField, setActiveField] = useState<string>(''); const [activeField, setActiveField] = useState<string>('');
const slashedBreadcrumb = getAddServiceEntityBreadcrumb(serviceCategory); const slashedBreadcrumb = getAddServiceEntityBreadcrumb(serviceCategory);
const handleServiceTypeClick = (type: string) => { const handleServiceTypeClick = (type: string) => {