mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-20 06:58:18 +00:00
This commit is contained in:
parent
67fb846dd9
commit
6c00b80f1d
@ -14,6 +14,7 @@
|
||||
import { AxiosResponse } from 'axios';
|
||||
import { isNil } from 'lodash';
|
||||
import { ServiceOption } from 'Models';
|
||||
import { ConfigData } from '../interface/service.interface';
|
||||
import { getURLWithQueryFields } from '../utils/APIUtils';
|
||||
import APIClient from './index';
|
||||
|
||||
@ -79,3 +80,15 @@ export const deleteService: Function = (
|
||||
): Promise<AxiosResponse> => {
|
||||
return APIClient.delete(`/services/${serviceName}/${id}`);
|
||||
};
|
||||
|
||||
export const TestConnection = (
|
||||
data: ConfigData,
|
||||
type: string
|
||||
): Promise<AxiosResponse> => {
|
||||
const payload = {
|
||||
connection: { config: data },
|
||||
connectionType: type,
|
||||
};
|
||||
|
||||
return APIClient.post(`/services/ingestionPipelines/testConnection`, payload);
|
||||
};
|
||||
|
@ -101,7 +101,7 @@ const SelectServiceType = ({
|
||||
theme="primary"
|
||||
variant="text"
|
||||
onClick={onCancel}>
|
||||
<span>Back</span>
|
||||
<span>Discard</span>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
|
@ -15,6 +15,7 @@ import { ISubmitEvent } from '@rjsf/core';
|
||||
import { cloneDeep, isNil } from 'lodash';
|
||||
import { LoadingState } from 'Models';
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import { TestConnection } from '../../axiosAPIs/serviceAPI';
|
||||
import { ServiceCategory } from '../../enums/service.enum';
|
||||
import {
|
||||
DashboardConnection,
|
||||
@ -30,10 +31,12 @@ import {
|
||||
} from '../../generated/entity/services/messagingService';
|
||||
import { PipelineService } from '../../generated/entity/services/pipelineService';
|
||||
import { ConfigData } from '../../interface/service.interface';
|
||||
import jsonData from '../../jsons/en';
|
||||
import { getDashboardConfig } from '../../utils/DashboardServiceUtils';
|
||||
import { getDatabaseConfig } from '../../utils/DatabaseServiceUtils';
|
||||
import { getMessagingConfig } from '../../utils/MessagingServiceUtils';
|
||||
import { getPipelineConfig } from '../../utils/PipelineServiceUtils';
|
||||
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
|
||||
import FormBuilder from '../common/FormBuilder/FormBuilder';
|
||||
|
||||
interface Props {
|
||||
@ -62,6 +65,32 @@ const ConnectionConfigForm: FunctionComponent<Props> = ({
|
||||
.connection.config as ConfigData)
|
||||
: ({ pipelineUrl: (data as PipelineService).pipelineUrl } as ConfigData)
|
||||
: ({} as ConfigData);
|
||||
|
||||
const handleTestConnection = (formData: ConfigData) => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
TestConnection(formData, 'Database')
|
||||
.then((res) => {
|
||||
// This api only responds with status 200 on success
|
||||
// No data sent on api success
|
||||
if (res.status === 200) {
|
||||
showSuccessToast(
|
||||
jsonData['api-success-messages']['test-connection-success']
|
||||
);
|
||||
resolve();
|
||||
} else {
|
||||
throw jsonData['api-error-messages']['unexpected-server-response'];
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
showErrorToast(
|
||||
err,
|
||||
jsonData['api-error-messages']['test-connection-error']
|
||||
);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const getDatabaseFields = () => {
|
||||
let connSch = {
|
||||
schema: {},
|
||||
@ -115,6 +144,7 @@ const ConnectionConfigForm: FunctionComponent<Props> = ({
|
||||
uiSchema={connSch.uiSchema}
|
||||
onCancel={onCancel}
|
||||
onSubmit={onSave}
|
||||
onTestConnection={handleTestConnection}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -18,8 +18,14 @@ import Form, {
|
||||
ObjectFieldTemplateProps,
|
||||
} from '@rjsf/core';
|
||||
import classNames from 'classnames';
|
||||
import { debounce, isEmpty, isEqual } from 'lodash';
|
||||
import { LoadingState } from 'Models';
|
||||
import React, { Fragment, FunctionComponent } from 'react';
|
||||
import React, {
|
||||
Fragment,
|
||||
FunctionComponent,
|
||||
useCallback,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { ConfigData } from '../../../interface/service.interface';
|
||||
import SVGIcons, { Icons } from '../../../utils/SvgUtils';
|
||||
import { Button } from '../../buttons/Button/Button';
|
||||
@ -31,6 +37,7 @@ interface Props extends FormProps<ConfigData> {
|
||||
showFormHeader?: boolean;
|
||||
status?: LoadingState;
|
||||
onCancel?: () => void;
|
||||
onTestConnection?: (formData: ConfigData) => Promise<void>;
|
||||
}
|
||||
|
||||
function ArrayFieldTemplate(props: ArrayFieldTemplateProps) {
|
||||
@ -124,17 +131,26 @@ function ObjectFieldTemplate(props: ObjectFieldTemplateProps) {
|
||||
}
|
||||
|
||||
const FormBuilder: FunctionComponent<Props> = ({
|
||||
formData,
|
||||
schema,
|
||||
okText,
|
||||
cancelText,
|
||||
showFormHeader = false,
|
||||
status = 'initial',
|
||||
onCancel,
|
||||
onSubmit,
|
||||
onTestConnection,
|
||||
...props
|
||||
}: Props) => {
|
||||
let oForm: Form<ConfigData> | null;
|
||||
const [localFormData, setLocalFormData] = useState<ConfigData | undefined>(
|
||||
formData
|
||||
);
|
||||
const [connectionTesting, setConnectionTesting] = useState<boolean>(false);
|
||||
const [connectionTested, setConnectionTested] = useState<boolean>(false);
|
||||
|
||||
const handleCancel = () => {
|
||||
setLocalFormData(formData);
|
||||
if (onCancel) {
|
||||
onCancel();
|
||||
}
|
||||
@ -146,6 +162,40 @@ const FormBuilder: FunctionComponent<Props> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const handleTestConnection = () => {
|
||||
if (localFormData && onTestConnection) {
|
||||
setConnectionTesting(true);
|
||||
onTestConnection(localFormData)
|
||||
.then(() => {
|
||||
setConnectionTested(true);
|
||||
})
|
||||
.catch(() => {
|
||||
setConnectionTested(false);
|
||||
})
|
||||
.finally(() => {
|
||||
setConnectionTesting(false);
|
||||
});
|
||||
}
|
||||
};
|
||||
const debouncedOnChange = useCallback(
|
||||
(updatedData: ConfigData): void => {
|
||||
setLocalFormData(updatedData);
|
||||
},
|
||||
[setLocalFormData]
|
||||
);
|
||||
|
||||
const debounceOnSearch = useCallback(debounce(debouncedOnChange, 1500), [
|
||||
debouncedOnChange,
|
||||
]);
|
||||
const handleChange = (updatedData: ConfigData) => {
|
||||
debounceOnSearch(updatedData);
|
||||
setConnectionTested(false);
|
||||
};
|
||||
|
||||
const disableTestConnection = () => {
|
||||
return connectionTesting || isEqual(localFormData, formData);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form
|
||||
ArrayFieldTemplate={ArrayFieldTemplate}
|
||||
@ -153,48 +203,79 @@ const FormBuilder: FunctionComponent<Props> = ({
|
||||
className={classNames('rjsf', props.className, {
|
||||
'no-header': !showFormHeader,
|
||||
})}
|
||||
formData={localFormData}
|
||||
ref={(form) => {
|
||||
oForm = form;
|
||||
}}
|
||||
schema={schema}
|
||||
onChange={(e) => {
|
||||
handleChange(e.formData);
|
||||
props.onChange && props.onChange(e);
|
||||
}}
|
||||
onSubmit={onSubmit}
|
||||
{...props}>
|
||||
<div className="tw-mt-6 tw-text-right" data-testid="buttons">
|
||||
<Button
|
||||
size="regular"
|
||||
theme="primary"
|
||||
variant="text"
|
||||
onClick={handleCancel}>
|
||||
{cancelText}
|
||||
</Button>
|
||||
{status === 'waiting' ? (
|
||||
{isEmpty(schema) && (
|
||||
<div className="tw-text-grey-muted tw-text-center">
|
||||
No Connection Configs available.
|
||||
</div>
|
||||
)}
|
||||
<div className="tw-mt-6 tw-flex tw-justify-between">
|
||||
<div>
|
||||
{!isEmpty(schema) && onTestConnection && (
|
||||
<Button
|
||||
className={classNames({
|
||||
'tw-opacity-40': disableTestConnection(),
|
||||
})}
|
||||
disabled={disableTestConnection()}
|
||||
size="regular"
|
||||
theme="primary"
|
||||
variant="outlined"
|
||||
onClick={handleTestConnection}>
|
||||
Test Connection
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="tw-text-right" data-testid="buttons">
|
||||
<Button
|
||||
disabled
|
||||
className="tw-w-16 tw-h-10 disabled:tw-opacity-100"
|
||||
size="regular"
|
||||
theme="primary"
|
||||
variant="contained">
|
||||
<Loader size="small" type="white" />
|
||||
variant="text"
|
||||
onClick={handleCancel}>
|
||||
{cancelText}
|
||||
</Button>
|
||||
) : status === 'success' ? (
|
||||
<Button
|
||||
disabled
|
||||
className="tw-w-16 tw-h-10 disabled:tw-opacity-100"
|
||||
size="regular"
|
||||
theme="primary"
|
||||
variant="contained">
|
||||
<FontAwesomeIcon icon="check" />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
className="tw-w-16 tw-h-10"
|
||||
data-testid="saveManageTab"
|
||||
size="regular"
|
||||
theme="primary"
|
||||
variant="contained"
|
||||
onClick={handleSubmit}>
|
||||
{okText}
|
||||
</Button>
|
||||
)}
|
||||
{status === 'waiting' ? (
|
||||
<Button
|
||||
disabled
|
||||
className="tw-w-16 tw-h-10 disabled:tw-opacity-100"
|
||||
size="regular"
|
||||
theme="primary"
|
||||
variant="contained">
|
||||
<Loader size="small" type="white" />
|
||||
</Button>
|
||||
) : status === 'success' ? (
|
||||
<Button
|
||||
disabled
|
||||
className="tw-w-16 tw-h-10 disabled:tw-opacity-100"
|
||||
size="regular"
|
||||
theme="primary"
|
||||
variant="contained">
|
||||
<FontAwesomeIcon icon="check" />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
className={classNames('tw-w-16 tw-h-10', {
|
||||
'tw-opacity-40': !connectionTested && !isEmpty(schema),
|
||||
})}
|
||||
data-testid="saveManageTab"
|
||||
disabled={!connectionTested && !isEmpty(schema)}
|
||||
size="regular"
|
||||
theme="primary"
|
||||
variant="contained"
|
||||
onClick={handleSubmit}>
|
||||
{okText}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
|
@ -42,7 +42,7 @@ const SuccessScreen = ({
|
||||
</div>
|
||||
<p className="tw-mb-7" data-testid="success-line">
|
||||
<span className="tw-mr-1 tw-font-semibold">"{name}"</span>
|
||||
<span>has been successfuly created</span>
|
||||
<span>has been created successfully</span>
|
||||
</p>
|
||||
|
||||
<div>
|
||||
|
@ -102,6 +102,8 @@ const jsonData = {
|
||||
'fetch-webhook-error': 'Error while fetching webhooks!',
|
||||
'fetch-user-count-error': 'Error while getting users count!',
|
||||
|
||||
'test-connection-error': 'Error while testing connection!',
|
||||
|
||||
'unexpected-server-response': 'Unexpected response from server!',
|
||||
|
||||
'update-chart-error': 'Error while updating charts!',
|
||||
@ -138,6 +140,7 @@ const jsonData = {
|
||||
'delete-test': 'Test deleted successfully!',
|
||||
'delete-message': 'Message deleted successfully!',
|
||||
'delete-entity-success': 'Entity deleted successfully!',
|
||||
'test-connection-success': 'Connection tested successfully!',
|
||||
},
|
||||
'form-error-messages': {
|
||||
'empty-email': 'Email is required.',
|
||||
|
@ -449,6 +449,10 @@
|
||||
width: 450px;
|
||||
}
|
||||
|
||||
.Toastify__toast {
|
||||
@apply tw-overflow-y-auto tw-max-h-80vh;
|
||||
}
|
||||
|
||||
.Toastify__toast-body {
|
||||
@apply tw-items-start;
|
||||
}
|
||||
|
@ -170,6 +170,7 @@ module.exports = {
|
||||
},
|
||||
maxHeight: {
|
||||
32: '8rem',
|
||||
'80vh': '80vh',
|
||||
'90vh': '90vh',
|
||||
},
|
||||
minHeight: {
|
||||
|
Loading…
x
Reference in New Issue
Block a user