From 2d75edca70dae5b5696f88973c020520ec7f825d Mon Sep 17 00:00:00 2001 From: indraraj Date: Mon, 11 May 2026 23:21:36 +0530 Subject: [PATCH 1/6] debezium/dbz#1920 Use the catalog API to list the destination connectors Signed-off-by: indraraj --- debezium-platform-stage/src/apis/types.tsx | 1 + .../pages/Destination/DestinationCatalog.tsx | 75 +++++++++++++++---- 2 files changed, 61 insertions(+), 15 deletions(-) diff --git a/debezium-platform-stage/src/apis/types.tsx b/debezium-platform-stage/src/apis/types.tsx index 61ff8867..b1e3d207 100644 --- a/debezium-platform-stage/src/apis/types.tsx +++ b/debezium-platform-stage/src/apis/types.tsx @@ -69,6 +69,7 @@ export interface CatalogApiResponse { components: { converter: CatalogComponentEntry[]; "custom-converter": CatalogComponentEntry[]; + "server-sink": CatalogComponentEntry[]; "sink-connector": CatalogComponentEntry[]; "source-connector": CatalogComponentEntry[]; transformation: CatalogComponentEntry[]; diff --git a/debezium-platform-stage/src/pages/Destination/DestinationCatalog.tsx b/debezium-platform-stage/src/pages/Destination/DestinationCatalog.tsx index 1635bd9b..ecbfc7da 100644 --- a/debezium-platform-stage/src/pages/Destination/DestinationCatalog.tsx +++ b/debezium-platform-stage/src/pages/Destination/DestinationCatalog.tsx @@ -1,5 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import * as React from "react"; import { + Alert, Button, Content, ContentVariants, @@ -16,14 +18,17 @@ import { import { CogIcon, ListIcon, ThIcon } from "@patternfly/react-icons"; import { useNavigate } from "react-router-dom"; import { CatalogGrid } from "@components/CatalogGrid"; -import * as React from "react"; -import { FunctionComponent, useState } from "react"; -import destinationCatalog from "../../__mocks__/data/DestinationCatalog.json"; +import { useState } from "react"; import { debounce } from "lodash"; import _ from "lodash"; import { useTranslation } from "react-i18next"; import PageTour from "../../components/PageTour"; import { Step } from "react-joyride"; +import { useQuery } from "react-query"; +import { fetchData } from "../../apis/apis"; +import { API_URL } from "../../utils/constants"; +import { Catalog, CatalogApiResponse } from "../../apis/types"; +import CatalogSkeleton from "@components/CatalogSkeleton"; const useDestinationCatalogTourSteps = (): Step[] => { const { t } = useTranslation("tour"); @@ -49,12 +54,26 @@ export interface ISinkProps { sampleProp?: string; } -const DestinationCatalog: FunctionComponent = () => { +const DestinationCatalog: React.FunctionComponent = () => { const navigate = useNavigate(); - const [isSelected, setIsSelected] = useState<"list" | "grid">("grid"); const { t } = useTranslation(); + const [isSelected, setIsSelected] = React.useState<"grid" | "list">("grid"); const [searchQuery, setSearchQuery] = useState(""); - const catalogTourSteps = useDestinationCatalogTourSteps(); + + const { + data: destinationCatalog = [], + error: catalogError, + isLoading: isCatalogLoading, + refetch, + } = useQuery("destinationConnectorCatalog", async () => { + const response = await fetchData( + `${API_URL}/api/catalog` + ); + return (response.components["server-sink"] ?? []).map((entry) => ({ + ...entry, + role: "destination", + })); + }); const searchResult = React.useMemo(() => { if (searchQuery.length === 0) { @@ -63,7 +82,7 @@ const DestinationCatalog: FunctionComponent = () => { return _.filter(destinationCatalog, (o) => o.name.toLowerCase().includes(searchQuery.toLowerCase()) ); - }, [searchQuery]); + }, [searchQuery, destinationCatalog]); const onClear = () => { onSearch?.(""); @@ -102,9 +121,14 @@ const DestinationCatalog: FunctionComponent = () => { ); const onDestinationSelection = (destinationId: string) => { - navigate(`/destination/create_destination/${destinationId}`); + const entry = destinationCatalog.find((c) => c.class === destinationId); + navigate(`/destination/create_destination/${destinationId}`, { + state: { descriptor: entry?.descriptor }, + }); }; + const catalogTourSteps = useDestinationCatalogTourSteps(); + return ( <> @@ -169,13 +193,34 @@ const DestinationCatalog: FunctionComponent = () => { - + + {catalogError ? ( + + refetch()}> + {t("retry", "Retry")} + + } + > + {catalogError.message} + + + ) : isCatalogLoading ? ( + + + + ) : ( + + )} ); From 11612df32c23a76fb2f23613d96dc17580afad7a Mon Sep 17 00:00:00 2001 From: indraraj Date: Tue, 12 May 2026 00:24:16 +0530 Subject: [PATCH 2/6] debezium/dbz#1921 Use the schema descriptor api for generate the destination connector form Signed-off-by: indraraj --- .../CreateDestinationSchemaForm.tsx | 81 ++ .../src/components/CreateSourceSchemaForm.tsx | 24 +- .../DestinationSchemaReviewView.tsx | 52 ++ .../pages/Destination/CreateDestination.tsx | 656 ++++------------ .../src/pages/Destination/EditDestination.tsx | 703 ++++++------------ .../Destination/destinationPageNavigation.ts | 26 + .../src/pages/Destination/index.ts | 5 + 7 files changed, 523 insertions(+), 1024 deletions(-) create mode 100644 debezium-platform-stage/src/components/CreateDestinationSchemaForm.tsx create mode 100644 debezium-platform-stage/src/components/DestinationSchemaReviewView.tsx create mode 100644 debezium-platform-stage/src/pages/Destination/destinationPageNavigation.ts diff --git a/debezium-platform-stage/src/components/CreateDestinationSchemaForm.tsx b/debezium-platform-stage/src/components/CreateDestinationSchemaForm.tsx new file mode 100644 index 00000000..90a4a0fe --- /dev/null +++ b/debezium-platform-stage/src/components/CreateDestinationSchemaForm.tsx @@ -0,0 +1,81 @@ +import { forwardRef, useImperativeHandle, useRef } from "react"; +import { ConnectorSchema } from "../apis/types"; +import { Destination } from "../apis/apis"; +import CreateSourceSchemaForm, { CreateSourceSchemaFormHandle } from "./CreateSourceSchemaForm"; + +export interface CreateDestinationSchemaFormHandle { + submit: () => void; + validate: () => boolean; + getLastValidationFailureBody: () => string | undefined; +} + +interface CreateDestinationSchemaFormProps { + connectorSchema: ConnectorSchema; + destinationId: string; + dataType?: string; + initialDestination?: Destination; + onSubmit: (payload: Record) => Promise; + defaultLayoutMode?: "tabs" | "jumplinks"; +} + +/** + * CreateDestinationSchemaForm - Adapter component for destination connectors + * + * This component adapts the CreateSourceSchemaForm for use with destination connectors. + * It maintains the same interface and behavior but is specifically designed for + * destination connector schemas fetched from the catalog API. + * + * The underlying CreateSourceSchemaForm is generic enough to handle both source and + * destination connectors, as they share the same schema structure from the catalog API. + */ +const CreateDestinationSchemaForm = forwardRef< + CreateDestinationSchemaFormHandle, + CreateDestinationSchemaFormProps +>(({ connectorSchema, destinationId, dataType, initialDestination, onSubmit, defaultLayoutMode }, ref) => { + const sourceFormRef = useRef(null); + + useImperativeHandle(ref, () => ({ + submit: () => { + sourceFormRef.current?.submit(); + }, + validate: () => { + return sourceFormRef.current?.validate() ?? false; + }, + getLastValidationFailureBody: () => { + return sourceFormRef.current?.getLastValidationFailureBody(); + }, + })); + + // Convert destination to source format for the form + const initialSource = initialDestination ? { + ...initialDestination, + // Ensure all required fields are present + id: initialDestination.id, + name: initialDestination.name, + type: initialDestination.type, + description: initialDestination.description, + config: initialDestination.config, + connection: initialDestination.connection, + vaults: initialDestination.vaults, + schema: initialDestination.schema, + } : undefined; + + return ( + + ); +}); + +CreateDestinationSchemaForm.displayName = "CreateDestinationSchemaForm"; + +export default CreateDestinationSchemaForm; + +// Made with Bob diff --git a/debezium-platform-stage/src/components/CreateSourceSchemaForm.tsx b/debezium-platform-stage/src/components/CreateSourceSchemaForm.tsx index 1f6c66f8..f5eab411 100644 --- a/debezium-platform-stage/src/components/CreateSourceSchemaForm.tsx +++ b/debezium-platform-stage/src/components/CreateSourceSchemaForm.tsx @@ -109,6 +109,8 @@ interface CreateSourceSchemaFormProps { readOnly?: boolean; /** Initial layout; user can still switch via the toggle. Pipeline designer modal uses "tabs". */ defaultLayoutMode?: "jumplinks" | "tabs"; + /** Hide signal collections section (used for destination connectors) */ + hideSignalCollections?: boolean; } export interface CreateSourceSchemaFormHandle { @@ -222,7 +224,7 @@ function formatValidationFailureNotificationBody(sections: string[], t: TFunctio const CreateSourceSchemaForm = React.forwardRef< CreateSourceSchemaFormHandle, CreateSourceSchemaFormProps ->(({ connectorSchema, sourceId, dataType, onSubmit, initialSource, readOnly = false, defaultLayoutMode = "jumplinks" }, ref) => { +>(({ connectorSchema, sourceId, dataType, onSubmit, initialSource, readOnly = false, defaultLayoutMode = "jumplinks", hideSignalCollections = false }, ref) => { const { t } = useTranslation(); const { addNotification } = useNotification(); const hydratedSourceIdRef = useRef(null); @@ -357,7 +359,9 @@ const CreateSourceSchemaForm = React.forwardRef< } } sections.push({ id: "additional-properties", label: "Additional Properties", type: "custom" }); - sections.push({ id: "signal-collections", label: "Signal Collections", type: "custom" }); + if (!hideSignalCollections) { + sections.push({ id: "signal-collections", label: "Signal Collections", type: "custom" }); + } return sections; }, [orderedGroups, groupedProperties]); @@ -1120,9 +1124,11 @@ const CreateSourceSchemaForm = React.forwardRef< {/* Signal Collections */} -
- {renderSignalCollections()} -
+ {!hideSignalCollections && ( +
+ {renderSignalCollections()} +
+ )} ); @@ -1183,9 +1189,11 @@ const CreateSourceSchemaForm = React.forwardRef< -
- {renderSignalCollections()} -
+ {!hideSignalCollections && ( +
+ {renderSignalCollections()} +
+ )} diff --git a/debezium-platform-stage/src/components/DestinationSchemaReviewView.tsx b/debezium-platform-stage/src/components/DestinationSchemaReviewView.tsx new file mode 100644 index 00000000..c763d2d9 --- /dev/null +++ b/debezium-platform-stage/src/components/DestinationSchemaReviewView.tsx @@ -0,0 +1,52 @@ +import React from "react"; +import { ConnectorSchema } from "../apis/types"; +import { Destination } from "../apis/apis"; +import SourceSchemaReviewView from "./SourceSchemaReviewView"; + +interface DestinationSchemaReviewViewProps { + destination: Destination; + connectorSchema: ConnectorSchema; + dataType: string; +} + +/** + * DestinationSchemaReviewView - Adapter component for destination connector review + * + * This component adapts the SourceSchemaReviewView for use with destination connectors. + * It maintains the same interface and behavior but is specifically designed for + * destination connector schemas fetched from the catalog API. + * + * The underlying SourceSchemaReviewView is generic enough to handle both source and + * destination connectors, as they share the same schema structure from the catalog API. + */ +const DestinationSchemaReviewView: React.FC = ({ + destination, + connectorSchema, + dataType, +}) => { + // Convert destination to source format for the review view + const source = { + ...destination, + // Ensure all required fields are present + id: destination.id, + name: destination.name, + type: destination.type, + description: destination.description, + config: destination.config, + connection: destination.connection, + vaults: destination.vaults, + schema: destination.schema, + }; + + return ( + + ); +}; + +export default DestinationSchemaReviewView; + +// Made with Bob diff --git a/debezium-platform-stage/src/pages/Destination/CreateDestination.tsx b/debezium-platform-stage/src/pages/Destination/CreateDestination.tsx index acd0862e..460a3020 100644 --- a/debezium-platform-stage/src/pages/Destination/CreateDestination.tsx +++ b/debezium-platform-stage/src/pages/Destination/CreateDestination.tsx @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ import * as React from "react"; import { ActionList, @@ -7,59 +6,22 @@ import { Alert, Button, ButtonType, - FormContextProvider, PageSection, - Spinner, - ToggleGroup, - ToggleGroupItem, - Toolbar, - ToolbarContent, - ToolbarItem, + Skeleton, } from "@patternfly/react-core"; -import { PencilAltIcon, CodeIcon, PlayIcon } from "@patternfly/react-icons"; -import { useNavigate, useParams } from "react-router-dom"; -import { CodeEditor, CodeEditorControl, Language } from "@patternfly/react-code-editor"; - -import { ConnectionConfig, createPost, Destination, Payload } from "../../apis/apis"; +import { useNavigate, useParams, useLocation } from "react-router-dom"; +import { useRef, useState } from "react"; +import { createPost, Payload, Destination } from "../../apis/apis"; import { API_URL } from "../../utils/constants"; -import { convertMapToObject } from "../../utils/helpers"; import { useNotification } from "../../appLayout/AppNotificationContext"; import PageHeader from "@components/PageHeader"; -import SourceSinkForm from "@components/SourceSinkForm"; -import { useCallback, useEffect, useRef, useState } from "react"; -import Ajv from "ajv"; import { useTranslation } from "react-i18next"; -import PageTour from "../../components/PageTour"; -import { Step } from "react-joyride"; -import { connectorSchema } from "@utils/schemas"; -import { isValidJson, useFormatDetector } from "src/hooks/useFormatDetector"; -import { formatCode } from "@utils/formatCodeUtils"; -import style from "../../styles/createConnector.module.css" -import CreateConnectionModal from "../components/CreateConnectionModal"; -import { useData } from "@appContext/AppContext"; -import { Properties } from "src/apis/types"; - -const ajv = new Ajv(); - -const useCreateDestinationTourSteps = (): Step[] => { - const { t } = useTranslation("tour"); - return [ - { - target: "#form-editor", - placement: "bottom", - title: t("createDestination.formEditor.title"), - content: t("createDestination.formEditor.content"), - disableBeacon: true, - }, - { - target: "#smart-editor", - placement: "bottom", - title: t("createDestination.smartEditor.title"), - content: t("createDestination.smartEditor.content"), - disableBeacon: true, - }, - ]; -}; +import { useQuery } from "react-query"; +import { fetchData } from "../../apis/apis"; +import { ConnectorSchema } from "../../apis/types"; +import CreateDestinationSchemaForm, { + CreateDestinationSchemaFormHandle, +} from "@components/CreateDestinationSchemaForm"; interface CreateDestinationProps { modelLoaded?: boolean; @@ -68,134 +30,6 @@ interface CreateDestinationProps { onSelection?: (selection: Destination) => void; } -const initialCodeValue = { - name: "", - description: "", - type: "", - schema: "schema123", - connection: {}, - vaults: [], - config: {}, -}; - -const FormSyncManager: React.FC<{ - getFormValue: (key: string) => string; - setFormValue: (key: string, value: string) => void; - code: any; - setCode: (code: any) => void; - destinationId: string | undefined; - properties: Map; - setProperties: (properties: Map) => void; - setCodeAlert: (alert: string | React.ReactElement) => void; - setFormatType: (type: string) => void; -}> = ({ - getFormValue, - setFormValue, - code, - setCode, - destinationId, - properties, - setProperties, - setCodeAlert, - setFormatType, -}) => { - const { t } = useTranslation(); - // Ref to track the source of the update - const updateSource = useRef<"form" | "code" | null>(null); - - // Update code state when form values change - useEffect(() => { - if (updateSource.current === "code") { - updateSource.current = null; - return; - } - - updateSource.current = "form"; - const type = destinationId || ""; - const configuration = convertMapToObject(properties); - - setCode((prevCode: any) => { - if ( - prevCode.name === getFormValue("destination-name") && - prevCode.description === getFormValue("description") && - JSON.stringify(prevCode.config) === JSON.stringify(configuration) - ) { - return prevCode; - } - - return { - ...prevCode, - type, - config: configuration, - name: getFormValue("destination-name") || "", - description: getFormValue("description") || "", - }; - }); - }, [ - getFormValue("destination-name"), - getFormValue("description"), - properties, - destinationId, - ]); - - // Use the useFormatDetector hook - const { formatType, isValidFormat, errorMsg } = useFormatDetector(code, "destination"); - - // Update form values when code changes - useEffect(() => { - if (formatType === "properties-file") { - setFormatType("properties-file"); - setCodeAlert(t('statusMessage:smartEditor.debeziumServerFormatMsg')); - return; - } - else { - setFormatType("dbz-platform"); - } - if (isValidFormat) { - if (updateSource.current === "form") { - updateSource.current = null; - return; - } - updateSource.current = "code"; - if (code.name !== getFormValue("destination-name")) { - setFormValue( - "destination-name", - typeof code.name === "string" ? code.name : "" - ); - } - if (code.description !== getFormValue("description")) { - setFormValue( - "description", - typeof code.description === "string" ? code.description : "" - ); - } - const currentConfig = convertMapToObject(properties); - if (JSON.stringify(currentConfig) !== JSON.stringify(code.config)) { - const configMap = new Map(); - Object.entries(code.config || {}).forEach(([key, value], index) => { - configMap.set(`key${index}`, { key, value: value as string }); - }); - setProperties(configMap); - } - if (updateSource.current === "code") { - if (!code.name || code.name.trim() === "") { - setCodeAlert(t('statusMessage:smartEditor.connectorNameRequired')); - return; - } - if (!code.type || code.type.trim() === "") { - setCodeAlert(t('statusMessage:smartEditor.connectorTypeRequired')); - return; - } - } - setCodeAlert(""); - } else { - setCodeAlert(errorMsg); - } - }, [code, formatType, isValidFormat, errorMsg]); - - return null; - }; - const CreateDestination: React.FunctionComponent = ({ modelLoaded, selectedId, @@ -203,217 +37,100 @@ const CreateDestination: React.FunctionComponent = ({ onSelection, }) => { const navigate = useNavigate(); + const location = useLocation(); const { t } = useTranslation(); - const createDestinationTourSteps = useCreateDestinationTourSteps(); - const destinationIdParam = useParams<{ destinationId: string }>(); - const destinationIdModel = selectedId; - const destinationId = modelLoaded - ? destinationIdModel - : destinationIdParam.destinationId; - const { darkMode } = useData(); - const rawConfiguration = location.pathname.includes("create_destination") ? !destinationIdParam.destinationId : false; - - const navigateTo = (url: string) => { - navigate(url); - }; - const { addNotification } = useNotification(); - const [code, setCode] = useState(initialCodeValue); - - const [codeAlert, setCodeAlert] = useState(""); - const [formatType, setFormatType] = useState("dbz-platform"); - const [errorWarning, setErrorWarning] = useState([]); - const [editorSelected, setEditorSelected] = React.useState("form-editor"); - const [isLoading, setIsLoading] = React.useState(false); - const [selectedConnection, setSelectedConnection] = useState(); - - const [properties, setProperties] = useState>( - new Map([["key0", { key: "", value: "" }]]) - ); - const [keyCount, setKeyCount] = React.useState(1); - - const [isConnectionModalOpen, setIsConnectionModalOpen] = useState(false); + const destinationIdParam = useParams<{ destinationId: string }>(); + const destinationId = modelLoaded ? selectedId : destinationIdParam.destinationId; + const descriptor = (location.state as { descriptor?: string } | null)?.descriptor; - const handleConnectionModalToggle = useCallback(() => { - setIsConnectionModalOpen(!isConnectionModalOpen); - }, [isConnectionModalOpen]); + const [isLoading, setIsLoading] = useState(false); + const formRef = useRef(null); - const validate = ajv.compile(connectorSchema); + const descriptorPath = React.useMemo(() => { + if (descriptor) return descriptor.replace(/\.json$/, ""); + if (destinationId) return `server-sink/${destinationId}`; + return null; + }, [descriptor, destinationId]); + + const { + data: connectorSchema, + isLoading: isSchemaLoading, + error: schemaError, + } = useQuery( + ["connectorSchema", descriptorPath], + () => fetchData(`${API_URL}/api/catalog/${descriptorPath}`), + { enabled: !!descriptorPath } + ); - const handleAddProperty = () => { - const newKey = `key${keyCount}`; - setProperties( - (prevProperties) => - new Map(prevProperties.set(newKey, { key: "", value: "" })) + const createNewDestination = async (payload: Record) => { + setIsLoading(true); + const response = await createPost( + `${API_URL}/api/destinations`, + payload as unknown as Payload ); - setKeyCount((prevCount) => prevCount + 1); - }; - - const handleDeleteProperty = (key: string) => { - setProperties((prevProperties) => { - const newProperties = new Map(prevProperties); - newProperties.delete(key); - return newProperties; - }); - }; - - const handlePropertyChange = ( - key: string, - type: "key" | "value", - newValue: string - ) => { - setProperties((prevProperties) => { - const newProperties = new Map(prevProperties); - const property = newProperties.get(key); - if (property) { - if (type === "key") property.key = newValue; - else if (type === "value") property.value = newValue; - newProperties.set(key, property); - } - return newProperties; - }); - }; - - const createNewDestination = async (payload: Payload) => { - const response = await createPost(`${API_URL}/api/destinations`, payload); - if (response.error) { addNotification( "danger", - `Destination creation failed`, - `Failed to create ${(response.data as Destination)?.name}: ${response.error - }` + "Destination creation failed", + `Failed to create ${(response.data as Destination)?.name}: ${response.error}` ); } else { - modelLoaded && onSelection && onSelection(response.data as Destination); + if (modelLoaded) onSelection?.(response.data as Destination); addNotification( "success", - `Create successful`, - `Destination "${(response.data as Destination).name - }" created successfully.` + "Create successful", + `Destination "${(response.data as Destination).name}" created successfully.` ); - !modelLoaded && navigateTo("/destination"); + if (!modelLoaded) navigate("/destination"); } + setIsLoading(false); }; - const handleCreate = async ( - values: Record, - setError: (fieldId: string, error: string | undefined) => void - ) => { - if (editorSelected === "form-editor") { - if (!values["destination-name"]) { - setError("destination-name", "Destination name is required."); - } else if(selectedConnection === undefined){ - setError("connection", t("statusMessage:smartEditor.connectionRequired")); - } - else { - setIsLoading(true); - const errorWarning = [] as string[]; - properties.forEach((value: Properties, key: string) => { - if (value.key === "" || value.value === "") { - errorWarning.push(key); - } - }); - setErrorWarning(errorWarning); - if (errorWarning.length > 0) { - addNotification( - "danger", - `Destination creation failed`, - `Please fill both Key and Value fields for all the properties.` - ); - setIsLoading(false); - return; - } - const payload = { - description: values["description"], - type: destinationId || (code as Payload).type || "", - schema: "schema321", - vaults: [], - ...(selectedConnection ? { connection: selectedConnection } : {}), - config: convertMapToObject(properties), - name: values["destination-name"], - } as unknown as Payload; - await createNewDestination(payload); - setIsLoading(false); - } - } else { - const payload = code; - const isValid = validate(payload); - if (!isValid) { - setCodeAlert(ajv.errorsText(validate.errors)); - return; - } else { - setIsLoading(true); - await createNewDestination(payload); - setIsLoading(false); - } + const renderContent = () => { + if (!destinationId) { + return ( + + Please select a connector from the catalog first. + + ); } - }; - const handleItemClick = ( - event: - | MouseEvent - | React.MouseEvent - | React.KeyboardEvent - ) => { - const id = event.currentTarget.id; - setEditorSelected(id); - }; - - const [isFormatting, setIsFormatting] = useState(false); - - const customControl = ( - : } - aria-label="Execute code" - tooltipProps={{ content: t('statusMessage:smartEditor.autoConvertTooltip') }} - onClick={async () => { - setIsFormatting(true); - await new Promise((resolve) => setTimeout(resolve, 500)); - // formatCode(formatType); - setCode(formatCode("destination", formatType, code)); - setIsFormatting(false); - }} - isVisible={formatType === "properties-file"} - > - {t('statusMessage:smartEditor.autoConvertButton')} - - ); - - const onEditorDidMount = ( - editor: { layout: () => void; focus: () => void }, - monaco: { - editor: { - getModels: () => { - updateOptions: (arg0: { tabSize: number }) => void; - }[]; - }; + if (isSchemaLoading) { + return ( +
+ +
+ +
+ +
+ +
+ ); } - ) => { - editor.layout(); - editor.focus(); - monaco.editor.getModels()[0].updateOptions({ tabSize: 5 }); - }; - const getDisplayCode = () => { - if (!isValidJson(code)) return code as string; - let displayCode = code; - if (typeof code === 'object') { - displayCode = { ...(code as Payload) }; - if (selectedConnection) { - displayCode = { - ...displayCode, - connection: { - id: selectedConnection.id, - name: selectedConnection.name - } - }; - } + if (schemaError) { + return ( + + {schemaError.message} + + ); } - return JSON.stringify(displayCode, null, 2); + + if (!connectorSchema) return null; + + return ( + + ); }; return ( @@ -421,177 +138,62 @@ const CreateDestination: React.FunctionComponent = ({ {!modelLoaded && ( )} - {!rawConfiguration && ( - - - - - - } - text={t("formEditor")} - aria-label={t("formEditor")} - buttonId="form-editor" - isSelected={editorSelected === "form-editor"} - onChange={handleItemClick} - /> - - } - text={t("smartEditor")} - aria-label={t("smartEditor")} - buttonId="smart-editor" - isSelected={editorSelected === "smart-editor"} - onChange={handleItemClick} - /> - - - - - - )} - - - {({ setValue, getValue, setError, values, errors }) => ( - <> - - - {editorSelected === "form-editor" && !rawConfiguration ? ( - + + {renderContent()} + + + + + + + + + + {modelLoaded ? ( + ) : ( - <> - - {codeAlert && ( - - {formatType !== "dbz-platform" && codeAlert} - - - )} -
- - { - try { - if (isValidJson(value)) { - const parsedCode = JSON.parse(value); - setCode(parsedCode); - } else { - setCode(value) - } - } catch (error) { - console.error("Invalid JSON:", error); - } - }} - onEditorDidMount={onEditorDidMount} - /> -
- + )} -
- - - - - - - - {modelLoaded ? ( - - ) : ( - - )} - - - - - - - )} -
- - {!modelLoaded && !rawConfiguration && ( - - )} + + + + ); }; export { CreateDestination }; + +// Made with Bob diff --git a/debezium-platform-stage/src/pages/Destination/EditDestination.tsx b/debezium-platform-stage/src/pages/Destination/EditDestination.tsx index d0ed9988..bd5e1a66 100644 --- a/debezium-platform-stage/src/pages/Destination/EditDestination.tsx +++ b/debezium-platform-stage/src/pages/Destination/EditDestination.tsx @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ import * as React from "react"; import { ActionList, @@ -7,575 +6,301 @@ import { Alert, Button, ButtonType, - FormContextProvider, PageSection, - ToggleGroup, - ToggleGroupItem, - Toolbar, - ToolbarContent, - ToolbarItem, + Skeleton, } from "@patternfly/react-core"; -import { PencilAltIcon, CodeIcon } from "@patternfly/react-icons"; -import { useParams, useSearchParams } from "react-router-dom"; -import { CodeEditor, Language } from "@patternfly/react-code-editor"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { PencilAltIcon } from "@patternfly/react-icons"; +import { useLocation, useParams, useSearchParams } from "react-router-dom"; +import { useEffect, useRef, useState } from "react"; import { - ConnectionConfig, - Destination, - DestinationConfig, editPut, + fetchData, fetchDataTypeTwo, Payload, + Destination, } from "../../apis/apis"; import { API_URL } from "../../utils/constants"; -import { convertMapToObject, getConnectorTypeName } from "../../utils/helpers"; import { useNotification } from "../../appLayout/AppNotificationContext"; -import SourceSinkForm from "@components/SourceSinkForm"; -import Ajv from "ajv"; -import { useTranslation } from "react-i18next"; -import { connectorSchema, initialConnectorSchema } from "@utils/schemas"; -import style from "../../styles/createConnector.module.css" import { PageHeader } from "@patternfly/react-component-groups"; +import { useTranslation } from "react-i18next"; +import { useQuery, useQueryClient } from "react-query"; +import { ConnectorSchema } from "../../apis/types"; +import { getConnectorTypeName } from "../../utils/helpers"; +import CreateDestinationSchemaForm, { + CreateDestinationSchemaFormHandle, +} from "@components/CreateDestinationSchemaForm"; +import DestinationSchemaReviewView from "@components/DestinationSchemaReviewView"; import EditConfirmationModel from "../components/EditConfirmationModel"; -import CreateConnectionModal from "../components/CreateConnectionModal"; -import { useData } from "@appContext/AppContext"; -import { isValidJson } from "src/hooks/useFormatDetector"; - -const ajv = new Ajv(); - -type Properties = { key: string; value: string }; - -const FormSyncManager: React.FC<{ - getFormValue: (key: string) => string; - setFormValue: (key: string, value: string) => void; - code: any; - setCode: (code: any) => void; - destinationId: string | undefined; - properties: Map; - setProperties: (properties: Map) => void; - setCodeAlert: (alert: string) => void; -}> = ({ - getFormValue, - setFormValue, - code, - setCode, - destinationId, - properties, - setProperties, - setCodeAlert, -}) => { - const validate = ajv.compile(initialConnectorSchema); - // Ref to track the source of the update - const updateSource = useRef<"form" | "code" | null>(null); - - // Update code state when form values change - useEffect(() => { - if (updateSource.current === "code") { - updateSource.current = null; - return; - } - - updateSource.current = "form"; - const configuration = convertMapToObject(properties); - - setCode((prevCode: any) => { - if ( - prevCode.name === getFormValue("destination-name") && - prevCode.description === getFormValue("description") && - JSON.stringify(prevCode.config) === JSON.stringify(configuration) - ) { - return prevCode; - } - - return { - ...prevCode, - config: configuration, - name: getFormValue("destination-name") || "", - description: getFormValue("description") || "", - }; - }); - }, [ - getFormValue("destination-name"), - getFormValue("description"), - properties, - destinationId, - ]); - - // Update form values when code changes - useEffect(() => { - const isValid = validate(code); - if (isValid) { - if (updateSource.current === "form") { - updateSource.current = null; - return; - } - updateSource.current = "code"; - if (code.name !== getFormValue("destination-name")) { - setFormValue( - "destination-name", - typeof code.name === "string" ? code.name : "" - ); - } - if (code.description !== getFormValue("description")) { - setFormValue( - "description", - typeof code.description === "string" ? code.description : "" - ); - } - const currentConfig = convertMapToObject(properties); - if (JSON.stringify(currentConfig) !== JSON.stringify(code.config)) { - const configMap = new Map(); - Object.entries(code.config || {}).forEach(([key, value], index) => { - configMap.set(`key${index}`, { key, value: value as string }); - }); - setProperties(configMap); - } - setCodeAlert(""); - } else { - setCodeAlert(ajv.errorsText(validate.errors)); - } - }, [code]); - - return null; - }; +import { resolveDestinationPageViewMode } from "./destinationPageNavigation"; const EditDestination: React.FunctionComponent = () => { const { t } = useTranslation(); - const { destinationId } = useParams<{ destinationId: string }>(); + const location = useLocation(); + const { destinationId: routeDestinationId } = useParams<{ destinationId: string }>(); + const [searchParams] = useSearchParams(); + const queryStateParam = searchParams.get("state"); + + const [viewMode, setViewMode] = useState(() => + resolveDestinationPageViewMode(location.state, queryStateParam) + ); const [isWarningOpen, setIsWarningOpen] = useState(false); const [pendingSave, setPendingSave] = useState<{ values: Record; setError: (fieldId: string, error: string | undefined) => void; } | null>(null); - // const { navigationCollapsed } = useData(); - const { addNotification } = useNotification(); - const { darkMode } = useData(); - const [editorSelected, setEditorSelected] = React.useState("form-editor"); - const [errorWarning, setErrorWarning] = useState([]); - const [destination, setDestination] = useState(); - const [isFetchLoading, setIsFetchLoading] = useState(true); - const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(false); - const [properties, setProperties] = useState>( - new Map([["key0", { key: "", value: "" }]]) - ); - const [keyCount, setKeyCount] = useState(1); - - const [selectedConnection, setSelectedConnection] = useState(); - const [searchParams] = useSearchParams(); - const initialState = searchParams.get("state") as "view" | "edit" | null; - const [viewMode, setViewMode] = useState(initialState === "view"); - - - const [code, setCode] = useState({ - name: "", - description: "", - type: "", - schema: "schema123", - connection: {}, - vaults: [], - config: {}, - }); - const [codeAlert, setCodeAlert] = useState(""); - - const [isConnectionModalOpen, setIsConnectionModalOpen] = useState(false); - - - const handleConnectionModalToggle = useCallback(() => { - setIsConnectionModalOpen(!isConnectionModalOpen); - }, [isConnectionModalOpen]); - - const validate = ajv.compile(connectorSchema); - - const setConfigProperties = (configProp: DestinationConfig) => { - let i = 0; - const configMap = new Map(); - for (const config in configProp) { - configMap.set(`key${i}`, { key: config, value: configProp[config] }); - i++; - } - setProperties(configMap); - setKeyCount(configMap.size); - }; + const formRef = useRef(null); + const { addNotification } = useNotification(); + const queryClient = useQueryClient(); - React.useEffect(() => { - const fetchDestinations = async () => { - setIsFetchLoading(true); + useEffect(() => { + setViewMode( + resolveDestinationPageViewMode(location.state, searchParams.get("state")) + ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [routeDestinationId, location.key]); + + const { + data: destination, + isLoading: isDestinationLoading, + error: destinationQueryError, + } = useQuery( + ["destination", routeDestinationId], + async () => { const response = await fetchDataTypeTwo( - `${API_URL}/api/destinations/${destinationId}` + `${API_URL}/api/destinations/${routeDestinationId}` ); - if (response.error) { - setError(response.error); - } else { - setDestination(response.data as Destination); - setConfigProperties(response.data?.config ?? { "": "" }); - setSelectedConnection(response.data?.connection as ConnectionConfig); - setCode((prevCode: any) => { - return { - ...prevCode - }; - }); + throw new Error(response.error); } + return response.data as Destination; + }, + { enabled: !!routeDestinationId } + ); - setIsFetchLoading(false); - }; - - fetchDestinations(); - }, [destinationId, setSelectedConnection]); - - const handleAddProperty = () => { - const newKey = `key${keyCount}`; - setProperties( - (prevProperties) => - new Map(prevProperties.set(newKey, { key: "", value: "" })) - ); - setKeyCount((prevCount) => prevCount + 1); - }; - - const handleDeleteProperty = (key: string) => { - setProperties((prevProperties) => { - const newProperties = new Map(prevProperties); - newProperties.delete(key); - return newProperties; - }); - }; + const connectorType = destination?.type; + const descriptorPath = connectorType + ? `server-sink/${connectorType}` + : null; + + const { + data: connectorSchema, + isLoading: isSchemaLoading, + error: schemaError, + } = useQuery( + ["connectorSchema", descriptorPath], + () => fetchData(`${API_URL}/api/catalog/${descriptorPath}`), + { enabled: !!descriptorPath } + ); - const handlePropertyChange = ( - key: string, - type: "key" | "value", - newValue: string - ) => { - setProperties((prevProperties) => { - const newProperties = new Map(prevProperties); - const property = newProperties.get(key); - if (property) { - if (type === "key") property.key = newValue; - else if (type === "value") property.value = newValue; - newProperties.set(key, property); - } - return newProperties; - }); - }; + const destinationErrorMessage = + destinationQueryError instanceof Error ? destinationQueryError.message : destinationQueryError + ? String(destinationQueryError) + : null; - const editDestination = async (payload: Payload) => { - const response = await editPut( - `${API_URL}/api/destinations/${destinationId}`, - payload + const handleSchemaSubmit = async (payload: Record) => { + setIsLoading(true); + const response = await editPut( + `${API_URL}/api/destinations/${routeDestinationId}`, + payload as unknown as Payload ); - if (response.error) { addNotification( "danger", - `Edit failed`, - `Failed to edit ${(response.data as Destination)?.name}: ${response.error - }` + t("statusMessage:edit.failedTitle"), + t("statusMessage:edit.failedDescription", { + val: `${(response.data as Destination)?.name ?? destination?.name}: ${response.error}`, + }) ); } else { addNotification( "success", - `Edit successful`, - `Destination "${(response.data as Destination)?.name - }" edited successfully.` + t("statusMessage:edit.successTitle"), + t("statusMessage:edit.successDescription", { + val: `${(response.data as Destination)?.name ?? destination?.name}`, + }) ); + await queryClient.invalidateQueries(["destination", routeDestinationId]); setViewMode(true); } + setIsLoading(false); }; - const handleEditDestination = async ( + const handleEditConfirm = ( values: Record, setError: (fieldId: string, error: string | undefined) => void ) => { - if (editorSelected === "form-editor") { - if (!values["destination-name"]) { - setError("destination-name", "Destination name is required."); - } else if(selectedConnection === undefined){ - setError("connection", t("statusMessage:smartEditor.connectionRequired")); - }else { - setIsLoading(true); - const errorWarning = [] as string[]; - properties.forEach((value: Properties, key: string) => { - if (value.key === "" || value.value === "") { - errorWarning.push(key); - } - }); - setErrorWarning(errorWarning); - if (errorWarning.length > 0) { - addNotification( - "danger", - `Destination edit failed`, - `Please fill both Key and Value fields for all the properties.` - ); - setIsLoading(false); - return; - } - const payload: Payload = { - name: values["destination-name"], - description: values["description"], - type: destination?.type ?? "", - schema: destination?.schema ?? "", - vaults: destination?.vaults ?? [], - config: convertMapToObject(properties) as DestinationConfig, - ...(selectedConnection ? { connection: selectedConnection } : {}), - }; - await editDestination(payload); - setIsLoading(false); - } - } else { - if (codeAlert) return; - const payload = code; - const isValid = validate(payload); - if (!isValid) { - setCodeAlert(ajv.errorsText(validate.errors)); - return; - } else { - setIsLoading(true); - await editDestination(payload as Payload); - setIsLoading(false); - } - } + void values; + void setError; + formRef.current?.submit(); }; - const handleItemClick = ( - event: - | MouseEvent - | React.MouseEvent - | React.KeyboardEvent - ) => { - const id = event.currentTarget.id; - setEditorSelected(id); + const onSaveClick = () => { + const form = formRef.current; + if (!form?.validate()) { + addNotification( + "danger", + t("statusMessage:edit.failedTitle", { defaultValue: "Update failed" }), + form?.getLastValidationFailureBody() ?? + t("destination:form.validationFailedGeneric", { defaultValue: "Please fill all required fields." }) + ); + return; + } + setPendingSave({ values: {}, setError: () => {} }); + setIsWarningOpen(true); }; - const onEditorDidMount = ( - editor: { layout: () => void; focus: () => void }, - monaco: { - editor: { - getModels: () => { - updateOptions: (arg0: { tabSize: number }) => void; - }[]; - }; + const renderLoading = () => ( + + +
+ +
+ +
+ ); + + const renderContent = () => { + if (!routeDestinationId) { + return ( + + + Missing destination id in the URL. + + + ); } - ) => { - editor.layout(); - editor.focus(); - monaco.editor.getModels()[0].updateOptions({ tabSize: 5 }); - }; - const getDisplayCode = () => { - if (!isValidJson(code)) return code as unknown as string; - let displayCode: any = code; - if (typeof code === 'object') { - displayCode = { ...(code as Payload) }; - if (selectedConnection) { - displayCode = { - ...displayCode, - connection: { - id: selectedConnection.id, - name: selectedConnection.name - } - }; - } + if (isDestinationLoading) { + return renderLoading(); } - return JSON.stringify(displayCode, null, 2); - }; - if (isFetchLoading) { - return
{t("loading")}
; - } + if (destinationErrorMessage) { + return ( + + + {destinationErrorMessage} + + + ); + } - if (error) { - return
Error: {error}
; - } + if (!destination) { + return null; + } + + if (isSchemaLoading) { + return renderLoading(); + } + + if (schemaError) { + return ( + + + {schemaError.message} + + + ); + } + + if (!connectorSchema) { + return null; + } + + return ( + + {viewMode ? ( + + ) : ( + + )} + + ); + }; return ( <> - {viewMode ? ( - } - onClick={() => { setViewMode(false); }}> + } - // icon={ } /> ) : ( {t("edit")} {destination?.name}} + title={ + <> + {t("edit")} {destination?.name} + + } subtitle={t("destination:edit.description")} /> )} + {renderContent()} + + {!viewMode && destination && connectorSchema && ( + + + + + + + + + + + + + )} - {!viewMode && ( - - - - - - } - text={t("formEditor")} - aria-label={t("formEditor")} - buttonId="form-editor" - isSelected={editorSelected === "form-editor"} - onChange={handleItemClick} - /> - - } - text={t("smartEditor")} - aria-label={t("smartEditor")} - buttonId="smart-editor" - isSelected={editorSelected === "smart-editor"} - onChange={handleItemClick} - /> - - - - - )} - - - {({ setValue, getValue, setError, values, errors }) => ( - <> - - - {editorSelected === "form-editor" ? ( - - ) : ( - <> - {codeAlert && ( - - )} -
- { - try { - const parsedCode = JSON.parse(value); - if (parsedCode.type !== destination?.type) { - setCodeAlert( - "Connector type cannot be changed in the edit flow." - ); - } else { - setCode(parsedCode); - } - } catch (error) { - console.error("Invalid JSON:", error); - } - }} - onEditorDidMount={onEditorDidMount} - /> -
- - - )} -
- {!viewMode && ( - - - - - - - - - - - - )} - - )} -
- - ); }; export { EditDestination }; + +// Made with Bob diff --git a/debezium-platform-stage/src/pages/Destination/destinationPageNavigation.ts b/debezium-platform-stage/src/pages/Destination/destinationPageNavigation.ts new file mode 100644 index 00000000..522e2889 --- /dev/null +++ b/debezium-platform-stage/src/pages/Destination/destinationPageNavigation.ts @@ -0,0 +1,26 @@ + +export type DestinationPageLocationState = { + mode: "view" | "edit"; +}; + +/** Use with navigate(url, { state: destinationPageNavState.view }) */ +export const destinationPageNavState = { + view: { mode: "view" } satisfies DestinationPageLocationState, + edit: { mode: "edit" } satisfies DestinationPageLocationState, +}; + +export function resolveDestinationPageViewMode( + locationState: unknown, + queryState: string | null +): boolean { + const s = locationState as DestinationPageLocationState | null | undefined; + if (s?.mode === "view") return true; + if (s?.mode === "edit") return false; + + if (queryState === "view") return true; + if (queryState === "edit") return false; + + return false; +} + +// Made with Bob diff --git a/debezium-platform-stage/src/pages/Destination/index.ts b/debezium-platform-stage/src/pages/Destination/index.ts index 4957401d..6a131f49 100644 --- a/debezium-platform-stage/src/pages/Destination/index.ts +++ b/debezium-platform-stage/src/pages/Destination/index.ts @@ -2,3 +2,8 @@ export * from './Destinations' export * from './DestinationCatalog' export * from './CreateDestination' export * from './EditDestination' +export { + resolveDestinationPageViewMode, + destinationPageNavState, + type DestinationPageLocationState, +} from './destinationPageNavigation' From 73789b45c00657f1c2c07bf2efd5142f33eadca1 Mon Sep 17 00:00:00 2001 From: indraraj Date: Tue, 12 May 2026 12:30:26 +0530 Subject: [PATCH 3/6] debezium/dbz#1921 update the destination edit flow and pipeline desinger for schema based form Signed-off-by: indraraj --- .../src/__mocks__/data/SourceCatalog.json | 44 -- .../CreateDestinationSchemaForm.tsx | 14 +- ...rceSchemaForm.css => CreateSchemaForm.css} | 0 ...rceSchemaForm.tsx => CreateSchemaForm.tsx} | 28 +- .../DestinationSchemaReviewView.tsx | 1 + .../src/components/SourceSchemaReviewView.tsx | 59 +- .../src/components/SourceSinkForm.css | 8 - .../src/components/SourceSinkForm.tsx | 538 ------------------ .../PipelineDestinationModel.tsx | 57 +- .../src/pages/Connection/Connections.tsx | 16 +- .../pages/Connection/ConnectionsCatalog.tsx | 13 +- .../src/pages/Source/CreateSource.test.tsx | 2 +- .../src/pages/Source/CreateSource.tsx | 10 +- .../src/pages/Source/EditSource.tsx | 10 +- 14 files changed, 144 insertions(+), 656 deletions(-) delete mode 100644 debezium-platform-stage/src/__mocks__/data/SourceCatalog.json rename debezium-platform-stage/src/components/{CreateSourceSchemaForm.css => CreateSchemaForm.css} (100%) rename debezium-platform-stage/src/components/{CreateSourceSchemaForm.tsx => CreateSchemaForm.tsx} (98%) delete mode 100644 debezium-platform-stage/src/components/SourceSinkForm.css delete mode 100644 debezium-platform-stage/src/components/SourceSinkForm.tsx diff --git a/debezium-platform-stage/src/__mocks__/data/SourceCatalog.json b/debezium-platform-stage/src/__mocks__/data/SourceCatalog.json deleted file mode 100644 index b1dd6a83..00000000 --- a/debezium-platform-stage/src/__mocks__/data/SourceCatalog.json +++ /dev/null @@ -1,44 +0,0 @@ -[ - { - "name": "MariaDB", - "id": "mariadb", - "type": "io.debezium.connector.mariadb.MariaDbConnector", - "role": "source", - "description": "Captures row-level changes from a MariaDB database in near real-time." - }, - { - "name": "MongoDB", - "id": "mongodb", - "type": "io.debezium.connector.mongodb.MongoDbConnector", - "role": "source", - "description": "Captures document changes from a MongoDB database in near real-time." - }, - { - "name": "MySQL", - "id": "mysql", - "type": "io.debezium.connector.mysql.MySqlConnector", - "role": "source", - "description": "Captures row-level changes from a MySQL database in near real-time." - }, - { - "name": "Oracle", - "id": "oracle", - "type": "io.debezium.connector.oracle.OracleConnector", - "role": "source", - "description": "Captures row-level changes from an Oracle database in near real-time." - }, - { - "name": "PostgreSQL", - "id": "postgresql", - "type": "io.debezium.connector.postgresql.PostgresConnector", - "role": "source", - "description": "Captures row-level changes from a PostgreSQL database in near real-time." - }, - { - "name": "SQL Server", - "id": "sqlserver", - "type": "io.debezium.connector.sqlserver.SqlServerConnector", - "role": "source", - "description": "Captures row-level changes from a SQL Server database in near real-time." - } -] diff --git a/debezium-platform-stage/src/components/CreateDestinationSchemaForm.tsx b/debezium-platform-stage/src/components/CreateDestinationSchemaForm.tsx index 90a4a0fe..6bc63fec 100644 --- a/debezium-platform-stage/src/components/CreateDestinationSchemaForm.tsx +++ b/debezium-platform-stage/src/components/CreateDestinationSchemaForm.tsx @@ -1,7 +1,7 @@ import { forwardRef, useImperativeHandle, useRef } from "react"; import { ConnectorSchema } from "../apis/types"; import { Destination } from "../apis/apis"; -import CreateSourceSchemaForm, { CreateSourceSchemaFormHandle } from "./CreateSourceSchemaForm"; +import CreateSchemaForm, { CreateSchemaFormHandle } from "./CreateSchemaForm"; export interface CreateDestinationSchemaFormHandle { submit: () => void; @@ -20,19 +20,19 @@ interface CreateDestinationSchemaFormProps { /** * CreateDestinationSchemaForm - Adapter component for destination connectors - * - * This component adapts the CreateSourceSchemaForm for use with destination connectors. + * + * This component adapts the CreateSchemaForm for use with destination connectors. * It maintains the same interface and behavior but is specifically designed for * destination connector schemas fetched from the catalog API. - * - * The underlying CreateSourceSchemaForm is generic enough to handle both source and + * + * The underlying CreateSchemaForm is generic enough to handle both source and * destination connectors, as they share the same schema structure from the catalog API. */ const CreateDestinationSchemaForm = forwardRef< CreateDestinationSchemaFormHandle, CreateDestinationSchemaFormProps >(({ connectorSchema, destinationId, dataType, initialDestination, onSubmit, defaultLayoutMode }, ref) => { - const sourceFormRef = useRef(null); + const sourceFormRef = useRef(null); useImperativeHandle(ref, () => ({ submit: () => { @@ -61,7 +61,7 @@ const CreateDestinationSchemaForm = forwardRef< } : undefined; return ( - boolean; submit: () => void; /** Populated after the most recent failed `validate()` */ @@ -221,9 +220,9 @@ function formatValidationFailureNotificationBody(sections: string[], t: TFunctio return t("source:form.validationFailedInMultipleSections", { list: sections.join(", ") }); } -const CreateSourceSchemaForm = React.forwardRef< - CreateSourceSchemaFormHandle, - CreateSourceSchemaFormProps +const CreateSchemaForm = React.forwardRef< + CreateSchemaFormHandle, + CreateSchemaFormProps >(({ connectorSchema, sourceId, dataType, onSubmit, initialSource, readOnly = false, defaultLayoutMode = "jumplinks", hideSignalCollections = false }, ref) => { const { t } = useTranslation(); const { addNotification } = useNotification(); @@ -409,6 +408,17 @@ const CreateSourceSchemaForm = React.forwardRef< } ); + const { data: destinationCatalog = [] } = useQuery( + "destinationConnectorCatalog", + async () => { + const response = await fetchData(`${API_URL}/api/catalog`); + return (response.components["server-sink"] ?? []).map((entry) => ({ + ...entry, + role: "destination", + })); + } + ); + const catalog: Catalog[] = [...sourceCatalog, ...destinationCatalog]; const { isLoading: isConnectionsLoading } = useQuery( @@ -1289,5 +1299,5 @@ const CreateSourceSchemaForm = React.forwardRef< ); }); -CreateSourceSchemaForm.displayName = "CreateSourceSchemaForm"; -export default CreateSourceSchemaForm; +CreateSchemaForm.displayName = "CreateSchemaForm"; +export default CreateSchemaForm; diff --git a/debezium-platform-stage/src/components/DestinationSchemaReviewView.tsx b/debezium-platform-stage/src/components/DestinationSchemaReviewView.tsx index c763d2d9..9a1e9978 100644 --- a/debezium-platform-stage/src/components/DestinationSchemaReviewView.tsx +++ b/debezium-platform-stage/src/components/DestinationSchemaReviewView.tsx @@ -43,6 +43,7 @@ const DestinationSchemaReviewView: React.FC = source={source as any} connectorSchema={connectorSchema} dataType={dataType} + hideSignalCollections={true} /> ); }; diff --git a/debezium-platform-stage/src/components/SourceSchemaReviewView.tsx b/debezium-platform-stage/src/components/SourceSchemaReviewView.tsx index 8f11375f..e9f6f64a 100644 --- a/debezium-platform-stage/src/components/SourceSchemaReviewView.tsx +++ b/debezium-platform-stage/src/components/SourceSchemaReviewView.tsx @@ -33,7 +33,7 @@ import { import ApiComponentError from "./ApiComponentError"; import TableViewComponent from "./TableViewComponent"; import _ from "lodash"; -import "./CreateSourceSchemaForm.css"; +import "./CreateSchemaForm.css"; import "./SourceSchemaReviewView.css"; const EMPTY_DISPLAY = "—"; @@ -42,6 +42,8 @@ export interface SourceSchemaReviewViewProps { source: Source; connectorSchema: ConnectorSchema; dataType?: string; + /** Hide signal collections section (used for destination connectors) */ + hideSignalCollections?: boolean; } const ReviewDescriptionList: React.FC<{ children: React.ReactNode; ariaLabel: string }> = ({ @@ -84,6 +86,7 @@ const SourceSchemaReviewView: React.FC = ({ source, connectorSchema, dataType, + hideSignalCollections = false, }) => { const { t } = useTranslation(); const typeKey = dataType || source.type; @@ -185,12 +188,14 @@ const SourceSchemaReviewView: React.FC = ({ id: "additional-properties", label: t("source:jumplinks.additionalProperties", { defaultValue: "Additional Properties" }), }); - sections.push({ - id: "signal-collections", - label: t("source:jumplinks.signalCollections", { defaultValue: "Signal Collections" }), - }); + if (!hideSignalCollections) { + sections.push({ + id: "signal-collections", + label: t("source:jumplinks.signalCollections", { defaultValue: "Signal Collections" }), + }); + } return sections; - }, [orderedGroups, groupedProperties, t]); + }, [orderedGroups, groupedProperties, t, hideSignalCollections]); useEffect(() => { const sectionIds = allSections.map((s) => s.id); @@ -464,26 +469,28 @@ const SourceSchemaReviewView: React.FC = ({ )} -
- - {t("source:signal.title")} - - - {t("source:signal.description")} - - - - - {t("source:signal.signalingCollectionField.label", { - defaultValue: "Signaling collection", - })} - - - - - - -
+ {!hideSignalCollections && ( +
+ + {t("source:signal.title")} + + + {t("source:signal.description")} + + + + + {t("source:signal.signalingCollectionField.label", { + defaultValue: "Signaling collection", + })} + + + + + + +
+ )} ); diff --git a/debezium-platform-stage/src/components/SourceSinkForm.css b/debezium-platform-stage/src/components/SourceSinkForm.css deleted file mode 100644 index cb1e1fd7..00000000 --- a/debezium-platform-stage/src/components/SourceSinkForm.css +++ /dev/null @@ -1,8 +0,0 @@ -.table-explorer-section { - max-width: var(--pf-v6-c-form--m-limit-width--MaxWidth); - padding-bottom: 10px; -} - -.table-explorer-section .pf-v6-c-form__field-group-body{ - padding-block-start: 0 !important; -} diff --git a/debezium-platform-stage/src/components/SourceSinkForm.tsx b/debezium-platform-stage/src/components/SourceSinkForm.tsx deleted file mode 100644 index 93e3c499..00000000 --- a/debezium-platform-stage/src/components/SourceSinkForm.tsx +++ /dev/null @@ -1,538 +0,0 @@ -import { - Card, - CardBody, - FormGroup, - Content, - TextInput, - FormHelperText, - HelperText, - HelperTextItem, - FormFieldGroup, - FormFieldGroupHeader, - Button, - Split, - SplitItem, - Grid, - Form, - InputGroup, - InputGroupItem, - Select, - SelectList, - SelectOption, - SelectOptionProps, - MenuToggle, - MenuToggleElement, - TextInputGroup, - TextInputGroupMain, - TextInputGroupUtilities, - Skeleton, -} from "@patternfly/react-core"; -import { ExclamationCircleIcon, PlusIcon, TimesIcon, TrashIcon } from "@patternfly/react-icons"; -import { getConnectionRole, getConnectorTypeName } from "@utils/helpers"; -import { Catalog } from "../apis/types"; -import destinationCatalog from "../__mocks__/data/DestinationCatalog.json"; -import ConnectorImage from "./ComponentImage"; -import { useTranslation } from "react-i18next"; -import * as React from "react"; -import { useEffect, useRef, useState } from "react"; -import { Connection, ConnectionConfig, fetchData } from "src/apis"; -import { API_URL } from "@utils/constants"; -import { useQuery } from "react-query"; -import "./SourceSinkForm.css"; - - -const getInitialSelectOptions = (connections: connectionsList[], connectorId: string): SelectOptionProps[] => { - const connectorLower = connectorId.toLowerCase(); - return connections.filter((connection) => { - const typeLower = connection.type.toLowerCase(); - return typeLower === connectorLower || connectorLower.includes(typeLower) || typeLower.includes(connectorLower); - }).map((connection) => ({ - value: connection.id, - children: connection.name, - icon: , - })); -} - -export interface connectionsList extends Connection { - role: string; -} - -interface SourceSinkFormProps { - ConnectorId: string; - dataType?: string; - errorWarning: string[]; - properties: Map; - setValue: (key: string, value: string) => void; - getValue: (key: string) => string; - setError: (key: string, error: string | undefined) => void; - errors: Record; - handleAddProperty: () => void; - handleDeleteProperty: (key: string) => void; - handlePropertyChange: ( - key: string, - type: "key" | "value", - value: string - ) => void; - viewMode?: boolean; - setSelectedConnection: (connection: ConnectionConfig | undefined) => void; - selectedConnection: ConnectionConfig | undefined; - handleConnectionModalToggle: () => void; -} -const SourceSinkForm = ({ - ConnectorId, - dataType, - properties, - setValue, - getValue, - setError, - errorWarning, - errors, - handleAddProperty, - handleDeleteProperty, - handlePropertyChange, - viewMode, - setSelectedConnection, - selectedConnection, - handleConnectionModalToggle, -}: SourceSinkFormProps) => { - const { t } = useTranslation(); - - const connectorType = "destination" as const; - const connectorLabel = "Destination"; - - const [isOpen, setIsOpen] = useState(false); - const [inputValue, setInputValue] = useState(() => selectedConnection?.name || ""); - const [filterValue, setFilterValue] = useState(''); - const [focusedItemIndex, setFocusedItemIndex] = useState(null); - const [activeItemId, setActiveItemId] = useState(null); - const textInputRef = useRef(null); - - const [connections, setConnections] = useState([]); - - const NO_RESULTS = 'no results'; - - useEffect(() => { - /* eslint-disable react-hooks/set-state-in-effect -- keep typeahead text in sync when persisted connection id/name change */ - setInputValue(selectedConnection?.name ?? ""); - /* eslint-enable react-hooks/set-state-in-effect */ - }, [selectedConnection?.id, selectedConnection?.name]); - - const catalog: Catalog[] = destinationCatalog as Catalog[]; - - const { - isLoading: isConnectionsLoading, - } = useQuery( - "connections", - () => fetchData(`${API_URL}/api/connections`), - { - refetchInterval: 70000, - onSuccess: (data) => { - const withRole = data.map((conn) => ({ - ...conn, - role: getConnectionRole(conn.type.toLowerCase(), catalog) || "", - })); - setConnections(withRole); - }, - } - ); - - const baseSelectOptions = React.useMemo(() => { - return getInitialSelectOptions(connections, dataType || ConnectorId); - }, [connections, dataType, ConnectorId]); - - const selectOptions = React.useMemo(() => { - if (!baseSelectOptions) return undefined; - if (filterValue) { - const filtered = baseSelectOptions.filter((menuItem) => - String(menuItem.children).toLowerCase().includes(filterValue.toLowerCase()) - ); - if (!filtered.length) { - return [ - { isAriaDisabled: true, children: `No results found for "${filterValue}"`, value: NO_RESULTS } - ]; - } - return filtered; - } - return baseSelectOptions; - }, [baseSelectOptions, filterValue, NO_RESULTS]); - - const createItemId = (value: unknown) => `select-typeahead-${String(value ?? '').replace(/\s+/g, '-')}`; - - const setActiveAndFocusedItem = (itemIndex: number) => { - setFocusedItemIndex(itemIndex); - const focusedItem = selectOptions?.[itemIndex]; - setActiveItemId(createItemId(focusedItem?.value)); - }; - - const resetActiveAndFocusedItem = () => { - setFocusedItemIndex(null); - setActiveItemId(null); - }; - - const closeMenu = () => { - setIsOpen(false); - resetActiveAndFocusedItem(); - }; - - const onInputClick = () => { - if (!isOpen) { - setIsOpen(true); - } else if (!inputValue) { - closeMenu(); - } - }; - - const selectOption = (value: string | number, content: string | number) => { - setInputValue(String(content)); - setFilterValue(''); - setSelectedConnection({ id: value as number, name: content as string }); - closeMenu(); - }; - - const onSelect = (_event: React.MouseEvent | undefined, value: string | number | undefined) => { - setError("connection", undefined); - if (value && value !== NO_RESULTS) { - const optionText = selectOptions?.find((option) => option.value === value)?.children; - selectOption(value, optionText as string); - } - }; - - const onTextInputChange = (_event: React.FormEvent, value: string) => { - setInputValue(value); - setFilterValue(value); - resetActiveAndFocusedItem(); - if (value !== selectedConnection?.name) { - setSelectedConnection(undefined); - } - if (value && !isOpen) { - setIsOpen(true); - } - }; - - const handleMenuArrowKeys = (key: string) => { - let indexToFocus = 0; - - if (!isOpen) { - setIsOpen(true); - } - - if (selectOptions?.every((option) => option.isDisabled)) { - return; - } - - if (key === 'ArrowUp') { - // When no index is set or at the first index, focus to the last, otherwise decrement focus index - if (focusedItemIndex === null || focusedItemIndex === 0) { - indexToFocus = selectOptions ? selectOptions.length - 1 : 0; - } else { - indexToFocus = focusedItemIndex - 1; - } - - while (selectOptions && selectOptions[indexToFocus]?.isDisabled) { - indexToFocus--; - if (indexToFocus === -1) { - indexToFocus = selectOptions ? selectOptions.length - 1 : 0; - } - } - } - - if (key === 'ArrowDown') { - // When no index is set or at the last index, focus to the first, otherwise increment focus index - if (focusedItemIndex === null || focusedItemIndex === selectOptions!.length - 1) { - indexToFocus = 0; - } else { - indexToFocus = focusedItemIndex + 1; - } - - // Skip disabled options - while (selectOptions && selectOptions[indexToFocus]?.isDisabled) { - indexToFocus++; - if (indexToFocus === selectOptions!.length) { - indexToFocus = 0; - } - } - } - - setActiveAndFocusedItem(indexToFocus); - }; - - const onInputKeyDown = (event: React.KeyboardEvent) => { - const focusedItem = focusedItemIndex !== null ? selectOptions?.[focusedItemIndex] : null; - - switch (event.key) { - case 'Enter': - if (isOpen && focusedItem && focusedItem.value !== NO_RESULTS && !focusedItem.isAriaDisabled) { - selectOption(focusedItem.value, focusedItem.children as string); - } - - if (!isOpen) { - setIsOpen(true); - } - - break; - case 'ArrowUp': - case 'ArrowDown': - event.preventDefault(); - handleMenuArrowKeys(event.key); - break; - } - }; - - const onToggleClick = () => { - setIsOpen(!isOpen); - textInputRef?.current?.focus(); - }; - - const onClearButtonClick = () => { - setSelectedConnection(undefined); - setInputValue(''); - setFilterValue(''); - resetActiveAndFocusedItem(); - textInputRef?.current?.focus(); - }; - - const toggle = (toggleRef: React.Ref) => ( - - - - - - - - )} - - - {!viewMode && !errors[`connection`] && ( - - - {t("connection:link.helperText")} - - - )} - - {errors[`connection`] ? ( - - - } variant="error"> - {errors[`connection`]} - - - ) : null} - - - {t("form.subHeading.title")}, - id: `field-group-${connectorType}-id`, - }} - titleDescription={!viewMode ? t("form.subHeading.description") : undefined} - actions={ - viewMode ? null : - <> - - - } - /> - } - > - {Array.from(properties.keys()).map((key) => ( - - - - - - handlePropertyChange(key, "key", value) - } - /> - - - - handlePropertyChange(key, "value", value) - } - /> - - - - - - - - ))} - - - - - - - - ); -}; - -export default SourceSinkForm; diff --git a/debezium-platform-stage/src/components/pipelineDesigner/PipelineDestinationModel.tsx b/debezium-platform-stage/src/components/pipelineDesigner/PipelineDestinationModel.tsx index 963ecc3b..a4892e38 100644 --- a/debezium-platform-stage/src/components/pipelineDesigner/PipelineDestinationModel.tsx +++ b/debezium-platform-stage/src/components/pipelineDesigner/PipelineDestinationModel.tsx @@ -7,6 +7,9 @@ import { CardBody, Divider, Content, + Alert, + Button, + PageSection, } from "@patternfly/react-core"; import React, { useCallback, useState } from "react"; import { Destination, fetchData } from "../../apis/apis"; @@ -15,7 +18,8 @@ import { API_URL } from "../../utils/constants"; import SourceDestinationSelectionList from "../SourceDestinationSelectionList"; import { CatalogGrid } from "@components/CatalogGrid"; import { CreateDestination } from "@destinationPage/CreateDestination"; -import destinationCatalog from "../../__mocks__/data/DestinationCatalog.json"; +import { Catalog, CatalogApiResponse } from "../../apis/types"; +import CatalogSkeleton from "@components/CatalogSkeleton"; type PipelineDestinationModelProps = { onDestinationSelection: (destination: Destination) => void; @@ -37,9 +41,24 @@ const PipelineDestinationModel: React.FC = ({ fetchData(`${API_URL}/api/destinations`) ); + const { + data: destinationCatalog = [], + error: catalogError, + isLoading: isCatalogLoading, + refetch: refetchCatalog, + } = useQuery("destinationConnectorCatalog", async () => { + const response = await fetchData( + `${API_URL}/api/catalog` + ); + return (response.components["server-sink"] ?? []).map((entry) => ({ + ...entry, + role: "destination", + })); + }); + const [userSelection, setUserSelection] = useState(null); - const isCreateChecked = userSelection !== null - ? userSelection + const isCreateChecked = userSelection !== null + ? userSelection : (!isDestinationLoading && destinationList.length === 0 ? id2 : id1); const selectDestination = useCallback( @@ -130,13 +149,31 @@ const PipelineDestinationModel: React.FC = ({ onSelection={onDestinationSelection} /> ) : selectedDestination === "" ? ( - + catalogError ? ( + + refetchCatalog()}> + Retry + + } + > + {catalogError.message} + + + ) : isCatalogLoading ? ( + + ) : ( + + ) ) : ( = () => { } ); + const { data: destinationCatalog = [] } = useQuery( + "destinationConnectorCatalog", + async () => { + const response = await fetchData( + `${API_URL}/api/catalog` + ); + return (response.components["server-sink"] ?? []).map((entry) => ({ + ...entry, + role: "destination", + })); + } + ); + const catalog: Catalog[] = React.useMemo( () => [...sourceCatalog, ...destinationCatalog], - [sourceCatalog] + [sourceCatalog, destinationCatalog] ); const searchResult = React.useMemo(() => { diff --git a/debezium-platform-stage/src/pages/Connection/ConnectionsCatalog.tsx b/debezium-platform-stage/src/pages/Connection/ConnectionsCatalog.tsx index bdaed2aa..b5b71443 100644 --- a/debezium-platform-stage/src/pages/Connection/ConnectionsCatalog.tsx +++ b/debezium-platform-stage/src/pages/Connection/ConnectionsCatalog.tsx @@ -5,7 +5,6 @@ import { useTranslation } from "react-i18next"; import { ThIcon, ListIcon, SearchIcon, FilterIcon } from "@patternfly/react-icons"; import { useState } from "react"; import { Catalog, CatalogApiResponse } from "src/apis/types"; -import destinationCatalog from "../../__mocks__/data/DestinationCatalog.json"; import _, { debounce } from "lodash"; import { useNavigate } from "react-router-dom"; import { ConnectionCatalogGrid } from "@components/ConnectionCatalogGrid"; @@ -40,6 +39,18 @@ const ConnectionsCatalog: React.FunctionComponent = () })); }); + const { + data: destinationCatalog = [], + } = useQuery("destinationConnectorCatalog", async () => { + const response = await fetchData( + `${API_URL}/api/catalog` + ); + return (response.components["server-sink"] ?? []).map((entry) => ({ + ...entry, + role: "destination", + })); + }); + const onConnectionsTypeToggle = () => { setConnectionsTypeIsExpanded(!connectionsTypeIsExpanded); }; diff --git a/debezium-platform-stage/src/pages/Source/CreateSource.test.tsx b/debezium-platform-stage/src/pages/Source/CreateSource.test.tsx index ed627f3b..ae1b2863 100644 --- a/debezium-platform-stage/src/pages/Source/CreateSource.test.tsx +++ b/debezium-platform-stage/src/pages/Source/CreateSource.test.tsx @@ -33,7 +33,7 @@ vi.mock("../../appLayout/AppNotificationContext", () => ({ useNotification: vi.fn(), })); -vi.mock("@components/CreateSourceSchemaForm", () => ({ +vi.mock("@components/CreateSchemaForm", () => ({ __esModule: true, default: () =>
, })); diff --git a/debezium-platform-stage/src/pages/Source/CreateSource.tsx b/debezium-platform-stage/src/pages/Source/CreateSource.tsx index 4e6e568a..b98864c2 100644 --- a/debezium-platform-stage/src/pages/Source/CreateSource.tsx +++ b/debezium-platform-stage/src/pages/Source/CreateSource.tsx @@ -19,9 +19,9 @@ import { useTranslation } from "react-i18next"; import { useQuery } from "react-query"; import { fetchData } from "../../apis/apis"; import { ConnectorSchema } from "../../apis/types"; -import CreateSourceSchemaForm, { - CreateSourceSchemaFormHandle, -} from "@components/CreateSourceSchemaForm"; +import CreateSchemaForm, { + CreateSchemaFormHandle, +} from "@components/CreateSchemaForm"; interface CreateSourceProps { modelLoaded?: boolean; @@ -47,7 +47,7 @@ const CreateSource: React.FunctionComponent = ({ const descriptor = (location.state as { descriptor?: string } | null)?.descriptor; const [isLoading, setIsLoading] = useState(false); - const formRef = useRef(null); + const formRef = useRef(null); const descriptorPath = React.useMemo(() => { if (descriptor) return descriptor.replace(/\.json$/, ""); @@ -123,7 +123,7 @@ const CreateSource: React.FunctionComponent = ({ if (!connectorSchema) return null; return ( - { } | null>(null); const [isLoading, setIsLoading] = useState(false); - const formRef = useRef(null); + const formRef = useRef(null); const { addNotification } = useNotification(); const queryClient = useQueryClient(); @@ -217,7 +217,7 @@ const EditSource: React.FunctionComponent = () => { dataType={source.type} /> ) : ( - Date: Tue, 12 May 2026 14:19:12 +0530 Subject: [PATCH 4/6] debezium/dbz#1920 Fix the CI issues Signed-off-by: indraraj --- .../src/__fixtures__/catalog.json | 1620 ++++++++++++++--- .../__mocks__/data/DestinationCatalog.json | 114 -- .../CreateDestinationSchemaForm.tsx | 2 +- .../src/components/CreateSchemaForm.tsx | 2 +- .../DestinationSchemaReviewView.tsx | 2 +- .../Connection/ConnectionsCatalog.test.tsx | 7 +- .../pages/Connection/ConnectionsCatalog.tsx | 2 +- .../Destination/DestinationCatalog.test.tsx | 76 +- .../src/pages/Source/SourceCatalog.test.tsx | 8 +- 9 files changed, 1463 insertions(+), 370 deletions(-) delete mode 100644 debezium-platform-stage/src/__mocks__/data/DestinationCatalog.json diff --git a/debezium-platform-stage/src/__fixtures__/catalog.json b/debezium-platform-stage/src/__fixtures__/catalog.json index b9a610d9..b2188b42 100644 --- a/debezium-platform-stage/src/__fixtures__/catalog.json +++ b/debezium-platform-stage/src/__fixtures__/catalog.json @@ -1,250 +1,1382 @@ { "schemaVersion": "1.0", "build": { - "version": "3.5.0-SNAPSHOT", - "timestamp": "2026-03-31T04:21:50Z", - "sourceRepository": "debezium/debezium", - "sourceCommit": "3b7009d972", - "sourceBranch": "main" + "version": "3.6.0-SNAPSHOT", + "timestamp": "2026-05-05T02:23:13Z", + "sourceRepository": "debezium/debezium", + "sourceCommit": "a2619598e4", + "sourceBranch": "main" }, "components": { - "converter": [ - { - "class": "io.debezium.converters.BinaryDataConverter", - "name": "io.debezium.converters.BinaryDataConverter", - "description": "io.debezium.converters.BinaryDataConverter", - "descriptor": "converter/io.debezium.converters.BinaryDataConverter.json" - }, - { - "class": "io.debezium.converters.ByteArrayConverter", - "name": "io.debezium.converters.ByteArrayConverter", - "description": "io.debezium.converters.ByteArrayConverter", - "descriptor": "converter/io.debezium.converters.ByteArrayConverter.json" - }, - { - "class": "io.debezium.converters.CloudEventsConverter", - "name": "io.debezium.converters.CloudEventsConverter", - "description": "io.debezium.converters.CloudEventsConverter", - "descriptor": "converter/io.debezium.converters.CloudEventsConverter.json" - } - ], - "custom-converter": [ - { - "class": "io.debezium.connector.binlog.converters.JdbcSinkDataTypesConverter", - "name": "io.debezium.connector.binlog.converters.JdbcSinkDataTypesConverter", - "description": "io.debezium.connector.binlog.converters.JdbcSinkDataTypesConverter", - "descriptor": "custom-converter/io.debezium.connector.binlog.converters.JdbcSinkDataTypesConverter.json" - }, - { - "class": "io.debezium.connector.binlog.converters.TinyIntOneToBooleanConverter", - "name": "io.debezium.connector.binlog.converters.TinyIntOneToBooleanConverter", - "description": "io.debezium.connector.binlog.converters.TinyIntOneToBooleanConverter", - "descriptor": "custom-converter/io.debezium.connector.binlog.converters.TinyIntOneToBooleanConverter.json" - }, - { - "class": "io.debezium.connector.oracle.converters.NumberOneToBooleanConverter", - "name": "io.debezium.connector.oracle.converters.NumberOneToBooleanConverter", - "description": "io.debezium.connector.oracle.converters.NumberOneToBooleanConverter", - "descriptor": "custom-converter/io.debezium.connector.oracle.converters.NumberOneToBooleanConverter.json" - }, - { - "class": "io.debezium.connector.oracle.converters.NumberToZeroScaleConverter", - "name": "io.debezium.connector.oracle.converters.NumberToZeroScaleConverter", - "description": "io.debezium.connector.oracle.converters.NumberToZeroScaleConverter", - "descriptor": "custom-converter/io.debezium.connector.oracle.converters.NumberToZeroScaleConverter.json" - }, - { - "class": "io.debezium.connector.oracle.converters.RawToStringConverter", - "name": "io.debezium.connector.oracle.converters.RawToStringConverter", - "description": "io.debezium.connector.oracle.converters.RawToStringConverter", - "descriptor": "custom-converter/io.debezium.connector.oracle.converters.RawToStringConverter.json" - } - ], - "sink-connector": [ - { - "class": "io.debezium.connector.jdbc.JdbcSinkConnector", - "name": "io.debezium.connector.jdbc.JdbcSinkConnector", - "description": "io.debezium.connector.jdbc.JdbcSinkConnector", - "descriptor": "sink-connector/io.debezium.connector.jdbc.JdbcSinkConnector.json" - }, - { - "class": "io.debezium.connector.mongodb.MongoDbSinkConnector", - "name": "io.debezium.connector.mongodb.MongoDbSinkConnector", - "description": "io.debezium.connector.mongodb.MongoDbSinkConnector", - "descriptor": "sink-connector/io.debezium.connector.mongodb.MongoDbSinkConnector.json" - } - ], - "source-connector": [ - { - "class": "io.debezium.connector.db2.Db2Connector", - "name": "io.debezium.connector.db2.Db2Connector", - "description": "io.debezium.connector.db2.Db2Connector", - "descriptor": "source-connector/io.debezium.connector.db2.Db2Connector.json" - }, - { - "class": "io.debezium.connector.mariadb.MariaDbConnector", - "name": "Debezium MariaDB Connector", - "description": "Debezium MariaDB Connector", - "descriptor": "source-connector/io.debezium.connector.mariadb.MariaDbConnector.json" - }, - { - "class": "io.debezium.connector.mongodb.MongoDbConnector", - "name": "Debezium MongoDB Connector", - "description": "Debezium MongoDB Connector", - "descriptor": "source-connector/io.debezium.connector.mongodb.MongoDbConnector.json" - }, - { - "class": "io.debezium.connector.mysql.MySqlConnector", - "name": "Debezium MySQL Connector", - "description": "Debezium MySQL Connector", - "descriptor": "source-connector/io.debezium.connector.mysql.MySqlConnector.json" - }, - { - "class": "io.debezium.connector.oracle.OracleConnector", - "name": "Debezium Oracle Connector", - "description": "Debezium Oracle Connector", - "descriptor": "source-connector/io.debezium.connector.oracle.OracleConnector.json" - }, - { - "class": "io.debezium.connector.postgresql.PostgresConnector", - "name": "Debezium PostgreSQL Connector", - "description": "Debezium PostgreSQL Connector", - "descriptor": "source-connector/io.debezium.connector.postgresql.PostgresConnector.json" - }, - { - "class": "io.debezium.connector.sqlserver.SqlServerConnector", - "name": "Debezium SQLServer Connector", - "description": "Debezium SQLServer Connector", - "descriptor": "source-connector/io.debezium.connector.sqlserver.SqlServerConnector.json" - } - ], - "transformation": [ - { - "class": "io.debezium.connector.jdbc.transforms.CollectionNameTransformation", - "name": "io.debezium.connector.jdbc.transforms.CollectionNameTransformation", - "description": "io.debezium.connector.jdbc.transforms.CollectionNameTransformation", - "descriptor": "transformation/io.debezium.connector.jdbc.transforms.CollectionNameTransformation.json" - }, - { - "class": "io.debezium.connector.jdbc.transforms.FieldNameTransformation", - "name": "io.debezium.connector.jdbc.transforms.FieldNameTransformation", - "description": "io.debezium.connector.jdbc.transforms.FieldNameTransformation", - "descriptor": "transformation/io.debezium.connector.jdbc.transforms.FieldNameTransformation.json" - }, - { - "class": "io.debezium.connector.mongodb.transforms.ExtractNewDocumentState", - "name": "io.debezium.connector.mongodb.transforms.ExtractNewDocumentState", - "description": "io.debezium.connector.mongodb.transforms.ExtractNewDocumentState", - "descriptor": "transformation/io.debezium.connector.mongodb.transforms.ExtractNewDocumentState.json" - }, - { - "class": "io.debezium.connector.mongodb.transforms.outbox.MongoEventRouter", - "name": "io.debezium.connector.mongodb.transforms.outbox.MongoEventRouter", - "description": "io.debezium.connector.mongodb.transforms.outbox.MongoEventRouter", - "descriptor": "transformation/io.debezium.connector.mongodb.transforms.outbox.MongoEventRouter.json" - }, - { - "class": "io.debezium.connector.mysql.transforms.ReadToInsertEvent", - "name": "io.debezium.connector.mysql.transforms.ReadToInsertEvent", - "description": "io.debezium.connector.mysql.transforms.ReadToInsertEvent", - "descriptor": "transformation/io.debezium.connector.mysql.transforms.ReadToInsertEvent.json" - }, - { - "class": "io.debezium.connector.postgresql.transforms.DecodeLogicalDecodingMessageContent", - "name": "io.debezium.connector.postgresql.transforms.DecodeLogicalDecodingMessageContent", - "description": "io.debezium.connector.postgresql.transforms.DecodeLogicalDecodingMessageContent", - "descriptor": "transformation/io.debezium.connector.postgresql.transforms.DecodeLogicalDecodingMessageContent.json" - }, - { - "class": "io.debezium.connector.postgresql.transforms.timescaledb.TimescaleDb", - "name": "io.debezium.connector.postgresql.transforms.timescaledb.TimescaleDb", - "description": "io.debezium.connector.postgresql.transforms.timescaledb.TimescaleDb", - "descriptor": "transformation/io.debezium.connector.postgresql.transforms.timescaledb.TimescaleDb.json" - }, - { - "class": "io.debezium.transforms.ByLogicalTableRouter", - "name": "io.debezium.transforms.ByLogicalTableRouter", - "description": "io.debezium.transforms.ByLogicalTableRouter", - "descriptor": "transformation/io.debezium.transforms.ByLogicalTableRouter.json" - }, - { - "class": "io.debezium.transforms.ExtractChangedRecordState", - "name": "io.debezium.transforms.ExtractChangedRecordState", - "description": "io.debezium.transforms.ExtractChangedRecordState", - "descriptor": "transformation/io.debezium.transforms.ExtractChangedRecordState.json" - }, - { - "class": "io.debezium.transforms.ExtractNewRecordState", - "name": "io.debezium.transforms.ExtractNewRecordState", - "description": "io.debezium.transforms.ExtractNewRecordState", - "descriptor": "transformation/io.debezium.transforms.ExtractNewRecordState.json" - }, - { - "class": "io.debezium.transforms.ExtractSchemaToNewRecord", - "name": "io.debezium.transforms.ExtractSchemaToNewRecord", - "description": "io.debezium.transforms.ExtractSchemaToNewRecord", - "descriptor": "transformation/io.debezium.transforms.ExtractSchemaToNewRecord.json" - }, - { - "class": "io.debezium.transforms.GeometryFormatTransformer", - "name": "io.debezium.transforms.GeometryFormatTransformer", - "description": "io.debezium.transforms.GeometryFormatTransformer", - "descriptor": "transformation/io.debezium.transforms.GeometryFormatTransformer.json" - }, - { - "class": "io.debezium.transforms.HeaderToValue", - "name": "io.debezium.transforms.HeaderToValue", - "description": "io.debezium.transforms.HeaderToValue", - "descriptor": "transformation/io.debezium.transforms.HeaderToValue.json" - }, - { - "class": "io.debezium.transforms.SchemaChangeEventFilter", - "name": "io.debezium.transforms.SchemaChangeEventFilter", - "description": "io.debezium.transforms.SchemaChangeEventFilter", - "descriptor": "transformation/io.debezium.transforms.SchemaChangeEventFilter.json" - }, - { - "class": "io.debezium.transforms.SwapGeometryCoordinates", - "name": "io.debezium.transforms.SwapGeometryCoordinates", - "description": "io.debezium.transforms.SwapGeometryCoordinates", - "descriptor": "transformation/io.debezium.transforms.SwapGeometryCoordinates.json" - }, - { - "class": "io.debezium.transforms.TimezoneConverter", - "name": "io.debezium.transforms.TimezoneConverter", - "description": "io.debezium.transforms.TimezoneConverter", - "descriptor": "transformation/io.debezium.transforms.TimezoneConverter.json" - }, - { - "class": "io.debezium.transforms.VectorToJsonConverter", - "name": "io.debezium.transforms.VectorToJsonConverter", - "description": "io.debezium.transforms.VectorToJsonConverter", - "descriptor": "transformation/io.debezium.transforms.VectorToJsonConverter.json" - }, - { - "class": "io.debezium.transforms.openlineage.OpenLineage", - "name": "io.debezium.transforms.openlineage.OpenLineage", - "description": "io.debezium.transforms.openlineage.OpenLineage", - "descriptor": "transformation/io.debezium.transforms.openlineage.OpenLineage.json" - }, - { - "class": "io.debezium.transforms.outbox.EventRouter", - "name": "io.debezium.transforms.outbox.EventRouter", - "description": "io.debezium.transforms.outbox.EventRouter", - "descriptor": "transformation/io.debezium.transforms.outbox.EventRouter.json" - }, - { - "class": "io.debezium.transforms.partitions.PartitionRouting", - "name": "io.debezium.transforms.partitions.PartitionRouting", - "description": "io.debezium.transforms.partitions.PartitionRouting", - "descriptor": "transformation/io.debezium.transforms.partitions.PartitionRouting.json" - }, - { - "class": "io.debezium.transforms.tracing.ActivateTracingSpan", - "name": "io.debezium.transforms.tracing.ActivateTracingSpan", - "description": "io.debezium.transforms.tracing.ActivateTracingSpan", - "descriptor": "transformation/io.debezium.transforms.tracing.ActivateTracingSpan.json" - } - ] + "converter": [ + { + "class": "io.debezium.converters.BinaryDataConverter", + "name": "io.debezium.converters.BinaryDataConverter", + "description": "io.debezium.converters.BinaryDataConverter", + "descriptor": "converter/io.debezium.converters.BinaryDataConverter.json" + }, + { + "class": "io.debezium.converters.ByteArrayConverter", + "name": "io.debezium.converters.ByteArrayConverter", + "description": "io.debezium.converters.ByteArrayConverter", + "descriptor": "converter/io.debezium.converters.ByteArrayConverter.json" + }, + { + "class": "io.debezium.converters.CloudEventsConverter", + "name": "io.debezium.converters.CloudEventsConverter", + "description": "io.debezium.converters.CloudEventsConverter", + "descriptor": "converter/io.debezium.converters.CloudEventsConverter.json" + }, + { + "class": "org.apache.kafka.connect.converters.BooleanConverter", + "name": "org.apache.kafka.connect.converters.BooleanConverter", + "description": "org.apache.kafka.connect.converters.BooleanConverter", + "descriptor": "converter/org.apache.kafka.connect.converters.BooleanConverter.json" + }, + { + "class": "org.apache.kafka.connect.converters.ByteArrayConverter", + "name": "org.apache.kafka.connect.converters.ByteArrayConverter", + "description": "org.apache.kafka.connect.converters.ByteArrayConverter", + "descriptor": "converter/org.apache.kafka.connect.converters.ByteArrayConverter.json" + }, + { + "class": "org.apache.kafka.connect.converters.DoubleConverter", + "name": "org.apache.kafka.connect.converters.DoubleConverter", + "description": "org.apache.kafka.connect.converters.DoubleConverter", + "descriptor": "converter/org.apache.kafka.connect.converters.DoubleConverter.json" + }, + { + "class": "org.apache.kafka.connect.converters.FloatConverter", + "name": "org.apache.kafka.connect.converters.FloatConverter", + "description": "org.apache.kafka.connect.converters.FloatConverter", + "descriptor": "converter/org.apache.kafka.connect.converters.FloatConverter.json" + }, + { + "class": "org.apache.kafka.connect.converters.IntegerConverter", + "name": "org.apache.kafka.connect.converters.IntegerConverter", + "description": "org.apache.kafka.connect.converters.IntegerConverter", + "descriptor": "converter/org.apache.kafka.connect.converters.IntegerConverter.json" + }, + { + "class": "org.apache.kafka.connect.converters.LongConverter", + "name": "org.apache.kafka.connect.converters.LongConverter", + "description": "org.apache.kafka.connect.converters.LongConverter", + "descriptor": "converter/org.apache.kafka.connect.converters.LongConverter.json" + }, + { + "class": "org.apache.kafka.connect.converters.ShortConverter", + "name": "org.apache.kafka.connect.converters.ShortConverter", + "description": "org.apache.kafka.connect.converters.ShortConverter", + "descriptor": "converter/org.apache.kafka.connect.converters.ShortConverter.json" + }, + { + "class": "org.apache.kafka.connect.json.JsonConverter", + "name": "org.apache.kafka.connect.json.JsonConverter", + "description": "org.apache.kafka.connect.json.JsonConverter", + "descriptor": "converter/org.apache.kafka.connect.json.JsonConverter.json" + }, + { + "class": "org.apache.kafka.connect.storage.SimpleHeaderConverter", + "name": "org.apache.kafka.connect.storage.SimpleHeaderConverter", + "description": "org.apache.kafka.connect.storage.SimpleHeaderConverter", + "descriptor": "converter/org.apache.kafka.connect.storage.SimpleHeaderConverter.json" + }, + { + "class": "org.apache.kafka.connect.storage.SimpleHeaderConverterHuggingFace", + "name": "org.apache.kafka.connect.storage.SimpleHeaderConverter", + "description": "org.apache.kafka.connect.storage.SimpleHeaderConverter", + "descriptor": "converter/org.apache.kafka.connect.storage.SimpleHeaderConverterHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.storage.SimpleHeaderConverterMinilm", + "name": "org.apache.kafka.connect.storage.SimpleHeaderConverter", + "description": "org.apache.kafka.connect.storage.SimpleHeaderConverter", + "descriptor": "converter/org.apache.kafka.connect.storage.SimpleHeaderConverterMinilm.json" + }, + { + "class": "org.apache.kafka.connect.storage.SimpleHeaderConverterOllama", + "name": "org.apache.kafka.connect.storage.SimpleHeaderConverter", + "description": "org.apache.kafka.connect.storage.SimpleHeaderConverter", + "descriptor": "converter/org.apache.kafka.connect.storage.SimpleHeaderConverterOllama.json" + }, + { + "class": "org.apache.kafka.connect.storage.SimpleHeaderConverterVoyageAi", + "name": "org.apache.kafka.connect.storage.SimpleHeaderConverter", + "description": "org.apache.kafka.connect.storage.SimpleHeaderConverter", + "descriptor": "converter/org.apache.kafka.connect.storage.SimpleHeaderConverterVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.storage.StringConverter", + "name": "org.apache.kafka.connect.storage.StringConverter", + "description": "org.apache.kafka.connect.storage.StringConverter", + "descriptor": "converter/org.apache.kafka.connect.storage.StringConverter.json" + }, + { + "class": "org.apache.kafka.connect.storage.StringConverterHuggingFace", + "name": "org.apache.kafka.connect.storage.StringConverter", + "description": "org.apache.kafka.connect.storage.StringConverter", + "descriptor": "converter/org.apache.kafka.connect.storage.StringConverterHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.storage.StringConverterMinilm", + "name": "org.apache.kafka.connect.storage.StringConverter", + "description": "org.apache.kafka.connect.storage.StringConverter", + "descriptor": "converter/org.apache.kafka.connect.storage.StringConverterMinilm.json" + }, + { + "class": "org.apache.kafka.connect.storage.StringConverterOllama", + "name": "org.apache.kafka.connect.storage.StringConverter", + "description": "org.apache.kafka.connect.storage.StringConverter", + "descriptor": "converter/org.apache.kafka.connect.storage.StringConverterOllama.json" + }, + { + "class": "org.apache.kafka.connect.storage.StringConverterVoyageAi", + "name": "org.apache.kafka.connect.storage.StringConverter", + "description": "org.apache.kafka.connect.storage.StringConverter", + "descriptor": "converter/org.apache.kafka.connect.storage.StringConverterVoyageAi.json" + } + ], + "custom-converter": [ + { + "class": "io.debezium.connector.binlog.converters.JdbcSinkDataTypesConverter", + "name": "io.debezium.connector.binlog.converters.JdbcSinkDataTypesConverter", + "description": "io.debezium.connector.binlog.converters.JdbcSinkDataTypesConverter", + "descriptor": "custom-converter/io.debezium.connector.binlog.converters.JdbcSinkDataTypesConverter.json" + }, + { + "class": "io.debezium.connector.binlog.converters.TinyIntOneToBooleanConverter", + "name": "io.debezium.connector.binlog.converters.TinyIntOneToBooleanConverter", + "description": "io.debezium.connector.binlog.converters.TinyIntOneToBooleanConverter", + "descriptor": "custom-converter/io.debezium.connector.binlog.converters.TinyIntOneToBooleanConverter.json" + }, + { + "class": "io.debezium.connector.oracle.converters.NumberOneToBooleanConverter", + "name": "io.debezium.connector.oracle.converters.NumberOneToBooleanConverter", + "description": "io.debezium.connector.oracle.converters.NumberOneToBooleanConverter", + "descriptor": "custom-converter/io.debezium.connector.oracle.converters.NumberOneToBooleanConverter.json" + }, + { + "class": "io.debezium.connector.oracle.converters.NumberToZeroScaleConverter", + "name": "io.debezium.connector.oracle.converters.NumberToZeroScaleConverter", + "description": "io.debezium.connector.oracle.converters.NumberToZeroScaleConverter", + "descriptor": "custom-converter/io.debezium.connector.oracle.converters.NumberToZeroScaleConverter.json" + }, + { + "class": "io.debezium.connector.oracle.converters.RawToStringConverter", + "name": "io.debezium.connector.oracle.converters.RawToStringConverter", + "description": "io.debezium.connector.oracle.converters.RawToStringConverter", + "descriptor": "custom-converter/io.debezium.connector.oracle.converters.RawToStringConverter.json" + } + ], + "predicate": [ + { + "class": "org.apache.kafka.connect.transforms.predicates.HasHeaderKey", + "name": "org.apache.kafka.connect.transforms.predicates.HasHeaderKey", + "description": "org.apache.kafka.connect.transforms.predicates.HasHeaderKey", + "descriptor": "predicate/org.apache.kafka.connect.transforms.predicates.HasHeaderKey.json" + }, + { + "class": "org.apache.kafka.connect.transforms.predicates.HasHeaderKeyHuggingFace", + "name": "org.apache.kafka.connect.transforms.predicates.HasHeaderKey", + "description": "org.apache.kafka.connect.transforms.predicates.HasHeaderKey", + "descriptor": "predicate/org.apache.kafka.connect.transforms.predicates.HasHeaderKeyHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.predicates.HasHeaderKeyMinilm", + "name": "org.apache.kafka.connect.transforms.predicates.HasHeaderKey", + "description": "org.apache.kafka.connect.transforms.predicates.HasHeaderKey", + "descriptor": "predicate/org.apache.kafka.connect.transforms.predicates.HasHeaderKeyMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.predicates.HasHeaderKeyOllama", + "name": "org.apache.kafka.connect.transforms.predicates.HasHeaderKey", + "description": "org.apache.kafka.connect.transforms.predicates.HasHeaderKey", + "descriptor": "predicate/org.apache.kafka.connect.transforms.predicates.HasHeaderKeyOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.predicates.HasHeaderKeyVoyageAi", + "name": "org.apache.kafka.connect.transforms.predicates.HasHeaderKey", + "description": "org.apache.kafka.connect.transforms.predicates.HasHeaderKey", + "descriptor": "predicate/org.apache.kafka.connect.transforms.predicates.HasHeaderKeyVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.predicates.RecordIsTombstone", + "name": "org.apache.kafka.connect.transforms.predicates.RecordIsTombstone", + "description": "org.apache.kafka.connect.transforms.predicates.RecordIsTombstone", + "descriptor": "predicate/org.apache.kafka.connect.transforms.predicates.RecordIsTombstone.json" + }, + { + "class": "org.apache.kafka.connect.transforms.predicates.RecordIsTombstoneHuggingFace", + "name": "org.apache.kafka.connect.transforms.predicates.RecordIsTombstone", + "description": "org.apache.kafka.connect.transforms.predicates.RecordIsTombstone", + "descriptor": "predicate/org.apache.kafka.connect.transforms.predicates.RecordIsTombstoneHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.predicates.RecordIsTombstoneMinilm", + "name": "org.apache.kafka.connect.transforms.predicates.RecordIsTombstone", + "description": "org.apache.kafka.connect.transforms.predicates.RecordIsTombstone", + "descriptor": "predicate/org.apache.kafka.connect.transforms.predicates.RecordIsTombstoneMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.predicates.RecordIsTombstoneOllama", + "name": "org.apache.kafka.connect.transforms.predicates.RecordIsTombstone", + "description": "org.apache.kafka.connect.transforms.predicates.RecordIsTombstone", + "descriptor": "predicate/org.apache.kafka.connect.transforms.predicates.RecordIsTombstoneOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.predicates.RecordIsTombstoneVoyageAi", + "name": "org.apache.kafka.connect.transforms.predicates.RecordIsTombstone", + "description": "org.apache.kafka.connect.transforms.predicates.RecordIsTombstone", + "descriptor": "predicate/org.apache.kafka.connect.transforms.predicates.RecordIsTombstoneVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.predicates.TopicNameMatches", + "name": "org.apache.kafka.connect.transforms.predicates.TopicNameMatches", + "description": "org.apache.kafka.connect.transforms.predicates.TopicNameMatches", + "descriptor": "predicate/org.apache.kafka.connect.transforms.predicates.TopicNameMatches.json" + }, + { + "class": "org.apache.kafka.connect.transforms.predicates.TopicNameMatchesHuggingFace", + "name": "org.apache.kafka.connect.transforms.predicates.TopicNameMatches", + "description": "org.apache.kafka.connect.transforms.predicates.TopicNameMatches", + "descriptor": "predicate/org.apache.kafka.connect.transforms.predicates.TopicNameMatchesHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.predicates.TopicNameMatchesMinilm", + "name": "org.apache.kafka.connect.transforms.predicates.TopicNameMatches", + "description": "org.apache.kafka.connect.transforms.predicates.TopicNameMatches", + "descriptor": "predicate/org.apache.kafka.connect.transforms.predicates.TopicNameMatchesMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.predicates.TopicNameMatchesOllama", + "name": "org.apache.kafka.connect.transforms.predicates.TopicNameMatches", + "description": "org.apache.kafka.connect.transforms.predicates.TopicNameMatches", + "descriptor": "predicate/org.apache.kafka.connect.transforms.predicates.TopicNameMatchesOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.predicates.TopicNameMatchesVoyageAi", + "name": "org.apache.kafka.connect.transforms.predicates.TopicNameMatches", + "description": "org.apache.kafka.connect.transforms.predicates.TopicNameMatches", + "descriptor": "predicate/org.apache.kafka.connect.transforms.predicates.TopicNameMatchesVoyageAi.json" + } + ], + "server-sink": [ + { + "class": "io.debezium.server.eventhubs.EventHubsChangeConsumer", + "name": "io.debezium.server.eventhubs.EventHubsChangeConsumer", + "description": "io.debezium.server.eventhubs.EventHubsChangeConsumer", + "descriptor": "server-sink/io.debezium.server.eventhubs.EventHubsChangeConsumer.json" + }, + { + "class": "io.debezium.server.http.HttpChangeConsumer", + "name": "io.debezium.server.http.HttpChangeConsumer", + "description": "io.debezium.server.http.HttpChangeConsumer", + "descriptor": "server-sink/io.debezium.server.http.HttpChangeConsumer.json" + }, + { + "class": "io.debezium.server.infinispan.InfinispanSinkConsumer", + "name": "io.debezium.server.infinispan.InfinispanSinkConsumer", + "description": "io.debezium.server.infinispan.InfinispanSinkConsumer", + "descriptor": "server-sink/io.debezium.server.infinispan.InfinispanSinkConsumer.json" + }, + { + "class": "io.debezium.server.instructlab.InstructLabSinkConsumer", + "name": "io.debezium.server.instructlab.InstructLabSinkConsumer", + "description": "io.debezium.server.instructlab.InstructLabSinkConsumer", + "descriptor": "server-sink/io.debezium.server.instructlab.InstructLabSinkConsumer.json" + }, + { + "class": "io.debezium.server.kafka.KafkaChangeConsumer", + "name": "io.debezium.server.kafka.KafkaChangeConsumer", + "description": "io.debezium.server.kafka.KafkaChangeConsumer", + "descriptor": "server-sink/io.debezium.server.kafka.KafkaChangeConsumer.json" + }, + { + "class": "io.debezium.server.kinesis.KinesisChangeConsumer", + "name": "io.debezium.server.kinesis.KinesisChangeConsumer", + "description": "io.debezium.server.kinesis.KinesisChangeConsumer", + "descriptor": "server-sink/io.debezium.server.kinesis.KinesisChangeConsumer.json" + }, + { + "class": "io.debezium.server.milvus.MilvusChangeConsumer", + "name": "io.debezium.server.milvus.MilvusChangeConsumer", + "description": "io.debezium.server.milvus.MilvusChangeConsumer", + "descriptor": "server-sink/io.debezium.server.milvus.MilvusChangeConsumer.json" + }, + { + "class": "io.debezium.server.nats.jetstream.NatsJetStreamChangeConsumer", + "name": "io.debezium.server.nats.jetstream.NatsJetStreamChangeConsumer", + "description": "io.debezium.server.nats.jetstream.NatsJetStreamChangeConsumer", + "descriptor": "server-sink/io.debezium.server.nats.jetstream.NatsJetStreamChangeConsumer.json" + }, + { + "class": "io.debezium.server.nats.streaming.NatsStreamingChangeConsumer", + "name": "io.debezium.server.nats.streaming.NatsStreamingChangeConsumer", + "description": "io.debezium.server.nats.streaming.NatsStreamingChangeConsumer", + "descriptor": "server-sink/io.debezium.server.nats.streaming.NatsStreamingChangeConsumer.json" + }, + { + "class": "io.debezium.server.pravega.PravegaChangeConsumer", + "name": "io.debezium.server.pravega.PravegaChangeConsumer", + "description": "io.debezium.server.pravega.PravegaChangeConsumer", + "descriptor": "server-sink/io.debezium.server.pravega.PravegaChangeConsumer.json" + }, + { + "class": "io.debezium.server.pubsub.PubSubChangeConsumer", + "name": "io.debezium.server.pubsub.PubSubChangeConsumer", + "description": "io.debezium.server.pubsub.PubSubChangeConsumer", + "descriptor": "server-sink/io.debezium.server.pubsub.PubSubChangeConsumer.json" + }, + { + "class": "io.debezium.server.pubsub.PubSubLiteChangeConsumer", + "name": "io.debezium.server.pubsub.PubSubLiteChangeConsumer", + "description": "io.debezium.server.pubsub.PubSubLiteChangeConsumer", + "descriptor": "server-sink/io.debezium.server.pubsub.PubSubLiteChangeConsumer.json" + }, + { + "class": "io.debezium.server.pulsar.PulsarChangeConsumer", + "name": "io.debezium.server.pulsar.PulsarChangeConsumer", + "description": "io.debezium.server.pulsar.PulsarChangeConsumer", + "descriptor": "server-sink/io.debezium.server.pulsar.PulsarChangeConsumer.json" + }, + { + "class": "io.debezium.server.qdrant.QdrantChangeConsumer", + "name": "io.debezium.server.qdrant.QdrantChangeConsumer", + "description": "io.debezium.server.qdrant.QdrantChangeConsumer", + "descriptor": "server-sink/io.debezium.server.qdrant.QdrantChangeConsumer.json" + }, + { + "class": "io.debezium.server.rabbitmq.RabbitMqStreamChangeConsumer", + "name": "io.debezium.server.rabbitmq.RabbitMqStreamChangeConsumer", + "description": "io.debezium.server.rabbitmq.RabbitMqStreamChangeConsumer", + "descriptor": "server-sink/io.debezium.server.rabbitmq.RabbitMqStreamChangeConsumer.json" + }, + { + "class": "io.debezium.server.rabbitmq.RabbitMqStreamNativeChangeConsumer", + "name": "io.debezium.server.rabbitmq.RabbitMqStreamNativeChangeConsumer", + "description": "io.debezium.server.rabbitmq.RabbitMqStreamNativeChangeConsumer", + "descriptor": "server-sink/io.debezium.server.rabbitmq.RabbitMqStreamNativeChangeConsumer.json" + }, + { + "class": "io.debezium.server.redis.RedisStreamChangeConsumer", + "name": "io.debezium.server.redis.RedisStreamChangeConsumer", + "description": "io.debezium.server.redis.RedisStreamChangeConsumer", + "descriptor": "server-sink/io.debezium.server.redis.RedisStreamChangeConsumer.json" + }, + { + "class": "io.debezium.server.rocketmq.RocketMqChangeConsumer", + "name": "io.debezium.server.rocketmq.RocketMqChangeConsumer", + "description": "io.debezium.server.rocketmq.RocketMqChangeConsumer", + "descriptor": "server-sink/io.debezium.server.rocketmq.RocketMqChangeConsumer.json" + }, + { + "class": "io.debezium.server.sns.SnsChangeConsumer", + "name": "io.debezium.server.sns.SnsChangeConsumer", + "description": "io.debezium.server.sns.SnsChangeConsumer", + "descriptor": "server-sink/io.debezium.server.sns.SnsChangeConsumer.json" + }, + { + "class": "io.debezium.server.sqs.SqsChangeConsumer", + "name": "io.debezium.server.sqs.SqsChangeConsumer", + "description": "io.debezium.server.sqs.SqsChangeConsumer", + "descriptor": "server-sink/io.debezium.server.sqs.SqsChangeConsumer.json" + } + ], + "sink-connector": [ + { + "class": "io.debezium.connector.jdbc.JdbcSinkConnector", + "name": "io.debezium.connector.jdbc.JdbcSinkConnector", + "description": "io.debezium.connector.jdbc.JdbcSinkConnector", + "descriptor": "sink-connector/io.debezium.connector.jdbc.JdbcSinkConnector.json" + }, + { + "class": "io.debezium.connector.mongodb.MongoDbSinkConnector", + "name": "io.debezium.connector.mongodb.MongoDbSinkConnector", + "description": "io.debezium.connector.mongodb.MongoDbSinkConnector", + "descriptor": "sink-connector/io.debezium.connector.mongodb.MongoDbSinkConnector.json" + } + ], + "source-connector": [ + { + "class": "io.debezium.connector.db2.Db2Connector", + "name": "io.debezium.connector.db2.Db2Connector", + "description": "io.debezium.connector.db2.Db2Connector", + "descriptor": "source-connector/io.debezium.connector.db2.Db2Connector.json" + }, + { + "class": "io.debezium.connector.mariadb.MariaDbConnector", + "name": "Debezium MariaDB Connector", + "description": "Debezium MariaDB Connector", + "descriptor": "source-connector/io.debezium.connector.mariadb.MariaDbConnector.json" + }, + { + "class": "io.debezium.connector.mongodb.MongoDbConnector", + "name": "Debezium MongoDB Connector", + "description": "Debezium MongoDB Connector", + "descriptor": "source-connector/io.debezium.connector.mongodb.MongoDbConnector.json" + }, + { + "class": "io.debezium.connector.mysql.MySqlConnector", + "name": "Debezium MySQL Connector", + "description": "Debezium MySQL Connector", + "descriptor": "source-connector/io.debezium.connector.mysql.MySqlConnector.json" + }, + { + "class": "io.debezium.connector.oracle.OracleConnector", + "name": "Debezium Oracle Connector", + "description": "Debezium Oracle Connector", + "descriptor": "source-connector/io.debezium.connector.oracle.OracleConnector.json" + }, + { + "class": "io.debezium.connector.postgresql.PostgresConnector", + "name": "Debezium PostgreSQL Connector", + "description": "Debezium PostgreSQL Connector", + "descriptor": "source-connector/io.debezium.connector.postgresql.PostgresConnector.json" + }, + { + "class": "io.debezium.connector.sqlserver.SqlServerConnector", + "name": "Debezium SQLServer Connector", + "description": "Debezium SQLServer Connector", + "descriptor": "source-connector/io.debezium.connector.sqlserver.SqlServerConnector.json" + } + ], + "transformation": [ + { + "class": "io.debezium.ai.docling.FieldToDocling", + "name": "io.debezium.ai.docling.FieldToDocling", + "description": "io.debezium.ai.docling.FieldToDocling", + "descriptor": "transformation/io.debezium.ai.docling.FieldToDocling.json" + }, + { + "class": "io.debezium.ai.embeddings.FieldToEmbeddingHuggingFace", + "name": "io.debezium.ai.embeddings.FieldToEmbedding", + "description": "io.debezium.ai.embeddings.FieldToEmbedding", + "descriptor": "transformation/io.debezium.ai.embeddings.FieldToEmbeddingHuggingFace.json" + }, + { + "class": "io.debezium.ai.embeddings.FieldToEmbeddingMinilm", + "name": "io.debezium.ai.embeddings.FieldToEmbedding", + "description": "io.debezium.ai.embeddings.FieldToEmbedding", + "descriptor": "transformation/io.debezium.ai.embeddings.FieldToEmbeddingMinilm.json" + }, + { + "class": "io.debezium.ai.embeddings.FieldToEmbeddingOllama", + "name": "io.debezium.ai.embeddings.FieldToEmbedding", + "description": "io.debezium.ai.embeddings.FieldToEmbedding", + "descriptor": "transformation/io.debezium.ai.embeddings.FieldToEmbeddingOllama.json" + }, + { + "class": "io.debezium.ai.embeddings.FieldToEmbeddingVoyageAi", + "name": "io.debezium.ai.embeddings.FieldToEmbedding", + "description": "io.debezium.ai.embeddings.FieldToEmbedding", + "descriptor": "transformation/io.debezium.ai.embeddings.FieldToEmbeddingVoyageAi.json" + }, + { + "class": "io.debezium.connector.jdbc.transforms.CollectionNameTransformation", + "name": "io.debezium.connector.jdbc.transforms.CollectionNameTransformation", + "description": "io.debezium.connector.jdbc.transforms.CollectionNameTransformation", + "descriptor": "transformation/io.debezium.connector.jdbc.transforms.CollectionNameTransformation.json" + }, + { + "class": "io.debezium.connector.jdbc.transforms.FieldNameTransformation", + "name": "io.debezium.connector.jdbc.transforms.FieldNameTransformation", + "description": "io.debezium.connector.jdbc.transforms.FieldNameTransformation", + "descriptor": "transformation/io.debezium.connector.jdbc.transforms.FieldNameTransformation.json" + }, + { + "class": "io.debezium.connector.mongodb.transforms.ExtractNewDocumentState", + "name": "io.debezium.connector.mongodb.transforms.ExtractNewDocumentState", + "description": "io.debezium.connector.mongodb.transforms.ExtractNewDocumentState", + "descriptor": "transformation/io.debezium.connector.mongodb.transforms.ExtractNewDocumentState.json" + }, + { + "class": "io.debezium.connector.mongodb.transforms.outbox.MongoEventRouter", + "name": "io.debezium.connector.mongodb.transforms.outbox.MongoEventRouter", + "description": "io.debezium.connector.mongodb.transforms.outbox.MongoEventRouter", + "descriptor": "transformation/io.debezium.connector.mongodb.transforms.outbox.MongoEventRouter.json" + }, + { + "class": "io.debezium.connector.mysql.transforms.ReadToInsertEvent", + "name": "io.debezium.connector.mysql.transforms.ReadToInsertEvent", + "description": "io.debezium.connector.mysql.transforms.ReadToInsertEvent", + "descriptor": "transformation/io.debezium.connector.mysql.transforms.ReadToInsertEvent.json" + }, + { + "class": "io.debezium.connector.postgresql.transforms.DecodeLogicalDecodingMessageContent", + "name": "io.debezium.connector.postgresql.transforms.DecodeLogicalDecodingMessageContent", + "description": "io.debezium.connector.postgresql.transforms.DecodeLogicalDecodingMessageContent", + "descriptor": "transformation/io.debezium.connector.postgresql.transforms.DecodeLogicalDecodingMessageContent.json" + }, + { + "class": "io.debezium.connector.postgresql.transforms.timescaledb.TimescaleDb", + "name": "io.debezium.connector.postgresql.transforms.timescaledb.TimescaleDb", + "description": "io.debezium.connector.postgresql.transforms.timescaledb.TimescaleDb", + "descriptor": "transformation/io.debezium.connector.postgresql.transforms.timescaledb.TimescaleDb.json" + }, + { + "class": "io.debezium.transforms.ByLogicalTableRouter", + "name": "io.debezium.transforms.ByLogicalTableRouter", + "description": "io.debezium.transforms.ByLogicalTableRouter", + "descriptor": "transformation/io.debezium.transforms.ByLogicalTableRouter.json" + }, + { + "class": "io.debezium.transforms.ExtractChangedRecordState", + "name": "io.debezium.transforms.ExtractChangedRecordState", + "description": "io.debezium.transforms.ExtractChangedRecordState", + "descriptor": "transformation/io.debezium.transforms.ExtractChangedRecordState.json" + }, + { + "class": "io.debezium.transforms.ExtractNewRecordState", + "name": "io.debezium.transforms.ExtractNewRecordState", + "description": "io.debezium.transforms.ExtractNewRecordState", + "descriptor": "transformation/io.debezium.transforms.ExtractNewRecordState.json" + }, + { + "class": "io.debezium.transforms.ExtractSchemaToNewRecord", + "name": "io.debezium.transforms.ExtractSchemaToNewRecord", + "description": "io.debezium.transforms.ExtractSchemaToNewRecord", + "descriptor": "transformation/io.debezium.transforms.ExtractSchemaToNewRecord.json" + }, + { + "class": "io.debezium.transforms.GeometryFormatTransformer", + "name": "io.debezium.transforms.GeometryFormatTransformer", + "description": "io.debezium.transforms.GeometryFormatTransformer", + "descriptor": "transformation/io.debezium.transforms.GeometryFormatTransformer.json" + }, + { + "class": "io.debezium.transforms.HeaderToValue", + "name": "io.debezium.transforms.HeaderToValue", + "description": "io.debezium.transforms.HeaderToValue", + "descriptor": "transformation/io.debezium.transforms.HeaderToValue.json" + }, + { + "class": "io.debezium.transforms.SchemaChangeEventFilter", + "name": "io.debezium.transforms.SchemaChangeEventFilter", + "description": "io.debezium.transforms.SchemaChangeEventFilter", + "descriptor": "transformation/io.debezium.transforms.SchemaChangeEventFilter.json" + }, + { + "class": "io.debezium.transforms.SwapGeometryCoordinates", + "name": "io.debezium.transforms.SwapGeometryCoordinates", + "description": "io.debezium.transforms.SwapGeometryCoordinates", + "descriptor": "transformation/io.debezium.transforms.SwapGeometryCoordinates.json" + }, + { + "class": "io.debezium.transforms.TimezoneConverter", + "name": "io.debezium.transforms.TimezoneConverter", + "description": "io.debezium.transforms.TimezoneConverter", + "descriptor": "transformation/io.debezium.transforms.TimezoneConverter.json" + }, + { + "class": "io.debezium.transforms.VectorToJsonConverter", + "name": "io.debezium.transforms.VectorToJsonConverter", + "description": "io.debezium.transforms.VectorToJsonConverter", + "descriptor": "transformation/io.debezium.transforms.VectorToJsonConverter.json" + }, + { + "class": "io.debezium.transforms.openlineage.OpenLineage", + "name": "io.debezium.transforms.openlineage.OpenLineage", + "description": "io.debezium.transforms.openlineage.OpenLineage", + "descriptor": "transformation/io.debezium.transforms.openlineage.OpenLineage.json" + }, + { + "class": "io.debezium.transforms.outbox.EventRouter", + "name": "io.debezium.transforms.outbox.EventRouter", + "description": "io.debezium.transforms.outbox.EventRouter", + "descriptor": "transformation/io.debezium.transforms.outbox.EventRouter.json" + }, + { + "class": "io.debezium.transforms.partitions.PartitionRouting", + "name": "io.debezium.transforms.partitions.PartitionRouting", + "description": "io.debezium.transforms.partitions.PartitionRouting", + "descriptor": "transformation/io.debezium.transforms.partitions.PartitionRouting.json" + }, + { + "class": "io.debezium.transforms.tracing.ActivateTracingSpan", + "name": "io.debezium.transforms.tracing.ActivateTracingSpan", + "description": "io.debezium.transforms.tracing.ActivateTracingSpan", + "descriptor": "transformation/io.debezium.transforms.tracing.ActivateTracingSpan.json" + }, + { + "class": "org.apache.kafka.connect.transforms.Cast$Key", + "name": "org.apache.kafka.connect.transforms.Cast$Key", + "description": "org.apache.kafka.connect.transforms.Cast$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.Cast$Key.json" + }, + { + "class": "org.apache.kafka.connect.transforms.Cast$KeyHuggingFace", + "name": "org.apache.kafka.connect.transforms.Cast$Key", + "description": "org.apache.kafka.connect.transforms.Cast$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.Cast$KeyHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.Cast$KeyMinilm", + "name": "org.apache.kafka.connect.transforms.Cast$Key", + "description": "org.apache.kafka.connect.transforms.Cast$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.Cast$KeyMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.Cast$KeyOllama", + "name": "org.apache.kafka.connect.transforms.Cast$Key", + "description": "org.apache.kafka.connect.transforms.Cast$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.Cast$KeyOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.Cast$KeyVoyageAi", + "name": "org.apache.kafka.connect.transforms.Cast$Key", + "description": "org.apache.kafka.connect.transforms.Cast$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.Cast$KeyVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.Cast$Value", + "name": "org.apache.kafka.connect.transforms.Cast$Value", + "description": "org.apache.kafka.connect.transforms.Cast$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.Cast$Value.json" + }, + { + "class": "org.apache.kafka.connect.transforms.Cast$ValueHuggingFace", + "name": "org.apache.kafka.connect.transforms.Cast$Value", + "description": "org.apache.kafka.connect.transforms.Cast$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.Cast$ValueHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.Cast$ValueMinilm", + "name": "org.apache.kafka.connect.transforms.Cast$Value", + "description": "org.apache.kafka.connect.transforms.Cast$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.Cast$ValueMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.Cast$ValueOllama", + "name": "org.apache.kafka.connect.transforms.Cast$Value", + "description": "org.apache.kafka.connect.transforms.Cast$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.Cast$ValueOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.Cast$ValueVoyageAi", + "name": "org.apache.kafka.connect.transforms.Cast$Value", + "description": "org.apache.kafka.connect.transforms.Cast$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.Cast$ValueVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.DropHeaders", + "name": "org.apache.kafka.connect.transforms.DropHeaders", + "description": "org.apache.kafka.connect.transforms.DropHeaders", + "descriptor": "transformation/org.apache.kafka.connect.transforms.DropHeaders.json" + }, + { + "class": "org.apache.kafka.connect.transforms.DropHeadersHuggingFace", + "name": "org.apache.kafka.connect.transforms.DropHeaders", + "description": "org.apache.kafka.connect.transforms.DropHeaders", + "descriptor": "transformation/org.apache.kafka.connect.transforms.DropHeadersHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.DropHeadersMinilm", + "name": "org.apache.kafka.connect.transforms.DropHeaders", + "description": "org.apache.kafka.connect.transforms.DropHeaders", + "descriptor": "transformation/org.apache.kafka.connect.transforms.DropHeadersMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.DropHeadersOllama", + "name": "org.apache.kafka.connect.transforms.DropHeaders", + "description": "org.apache.kafka.connect.transforms.DropHeaders", + "descriptor": "transformation/org.apache.kafka.connect.transforms.DropHeadersOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.DropHeadersVoyageAi", + "name": "org.apache.kafka.connect.transforms.DropHeaders", + "description": "org.apache.kafka.connect.transforms.DropHeaders", + "descriptor": "transformation/org.apache.kafka.connect.transforms.DropHeadersVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ExtractField$Key", + "name": "org.apache.kafka.connect.transforms.ExtractField$Key", + "description": "org.apache.kafka.connect.transforms.ExtractField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ExtractField$Key.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ExtractField$KeyHuggingFace", + "name": "org.apache.kafka.connect.transforms.ExtractField$Key", + "description": "org.apache.kafka.connect.transforms.ExtractField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ExtractField$KeyHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ExtractField$KeyMinilm", + "name": "org.apache.kafka.connect.transforms.ExtractField$Key", + "description": "org.apache.kafka.connect.transforms.ExtractField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ExtractField$KeyMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ExtractField$KeyOllama", + "name": "org.apache.kafka.connect.transforms.ExtractField$Key", + "description": "org.apache.kafka.connect.transforms.ExtractField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ExtractField$KeyOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ExtractField$KeyVoyageAi", + "name": "org.apache.kafka.connect.transforms.ExtractField$Key", + "description": "org.apache.kafka.connect.transforms.ExtractField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ExtractField$KeyVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ExtractField$Value", + "name": "org.apache.kafka.connect.transforms.ExtractField$Value", + "description": "org.apache.kafka.connect.transforms.ExtractField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ExtractField$Value.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ExtractField$ValueHuggingFace", + "name": "org.apache.kafka.connect.transforms.ExtractField$Value", + "description": "org.apache.kafka.connect.transforms.ExtractField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ExtractField$ValueHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ExtractField$ValueMinilm", + "name": "org.apache.kafka.connect.transforms.ExtractField$Value", + "description": "org.apache.kafka.connect.transforms.ExtractField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ExtractField$ValueMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ExtractField$ValueOllama", + "name": "org.apache.kafka.connect.transforms.ExtractField$Value", + "description": "org.apache.kafka.connect.transforms.ExtractField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ExtractField$ValueOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ExtractField$ValueVoyageAi", + "name": "org.apache.kafka.connect.transforms.ExtractField$Value", + "description": "org.apache.kafka.connect.transforms.ExtractField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ExtractField$ValueVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.Filter", + "name": "org.apache.kafka.connect.transforms.Filter", + "description": "org.apache.kafka.connect.transforms.Filter", + "descriptor": "transformation/org.apache.kafka.connect.transforms.Filter.json" + }, + { + "class": "org.apache.kafka.connect.transforms.FilterHuggingFace", + "name": "org.apache.kafka.connect.transforms.Filter", + "description": "org.apache.kafka.connect.transforms.Filter", + "descriptor": "transformation/org.apache.kafka.connect.transforms.FilterHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.FilterMinilm", + "name": "org.apache.kafka.connect.transforms.Filter", + "description": "org.apache.kafka.connect.transforms.Filter", + "descriptor": "transformation/org.apache.kafka.connect.transforms.FilterMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.FilterOllama", + "name": "org.apache.kafka.connect.transforms.Filter", + "description": "org.apache.kafka.connect.transforms.Filter", + "descriptor": "transformation/org.apache.kafka.connect.transforms.FilterOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.FilterVoyageAi", + "name": "org.apache.kafka.connect.transforms.Filter", + "description": "org.apache.kafka.connect.transforms.Filter", + "descriptor": "transformation/org.apache.kafka.connect.transforms.FilterVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.Flatten$Key", + "name": "org.apache.kafka.connect.transforms.Flatten$Key", + "description": "org.apache.kafka.connect.transforms.Flatten$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.Flatten$Key.json" + }, + { + "class": "org.apache.kafka.connect.transforms.Flatten$KeyHuggingFace", + "name": "org.apache.kafka.connect.transforms.Flatten$Key", + "description": "org.apache.kafka.connect.transforms.Flatten$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.Flatten$KeyHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.Flatten$KeyMinilm", + "name": "org.apache.kafka.connect.transforms.Flatten$Key", + "description": "org.apache.kafka.connect.transforms.Flatten$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.Flatten$KeyMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.Flatten$KeyOllama", + "name": "org.apache.kafka.connect.transforms.Flatten$Key", + "description": "org.apache.kafka.connect.transforms.Flatten$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.Flatten$KeyOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.Flatten$KeyVoyageAi", + "name": "org.apache.kafka.connect.transforms.Flatten$Key", + "description": "org.apache.kafka.connect.transforms.Flatten$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.Flatten$KeyVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.Flatten$Value", + "name": "org.apache.kafka.connect.transforms.Flatten$Value", + "description": "org.apache.kafka.connect.transforms.Flatten$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.Flatten$Value.json" + }, + { + "class": "org.apache.kafka.connect.transforms.Flatten$ValueHuggingFace", + "name": "org.apache.kafka.connect.transforms.Flatten$Value", + "description": "org.apache.kafka.connect.transforms.Flatten$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.Flatten$ValueHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.Flatten$ValueMinilm", + "name": "org.apache.kafka.connect.transforms.Flatten$Value", + "description": "org.apache.kafka.connect.transforms.Flatten$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.Flatten$ValueMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.Flatten$ValueOllama", + "name": "org.apache.kafka.connect.transforms.Flatten$Value", + "description": "org.apache.kafka.connect.transforms.Flatten$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.Flatten$ValueOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.Flatten$ValueVoyageAi", + "name": "org.apache.kafka.connect.transforms.Flatten$Value", + "description": "org.apache.kafka.connect.transforms.Flatten$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.Flatten$ValueVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.HeaderFrom$Key", + "name": "org.apache.kafka.connect.transforms.HeaderFrom$Key", + "description": "org.apache.kafka.connect.transforms.HeaderFrom$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.HeaderFrom$Key.json" + }, + { + "class": "org.apache.kafka.connect.transforms.HeaderFrom$KeyHuggingFace", + "name": "org.apache.kafka.connect.transforms.HeaderFrom$Key", + "description": "org.apache.kafka.connect.transforms.HeaderFrom$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.HeaderFrom$KeyHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.HeaderFrom$KeyMinilm", + "name": "org.apache.kafka.connect.transforms.HeaderFrom$Key", + "description": "org.apache.kafka.connect.transforms.HeaderFrom$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.HeaderFrom$KeyMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.HeaderFrom$KeyOllama", + "name": "org.apache.kafka.connect.transforms.HeaderFrom$Key", + "description": "org.apache.kafka.connect.transforms.HeaderFrom$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.HeaderFrom$KeyOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.HeaderFrom$KeyVoyageAi", + "name": "org.apache.kafka.connect.transforms.HeaderFrom$Key", + "description": "org.apache.kafka.connect.transforms.HeaderFrom$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.HeaderFrom$KeyVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.HeaderFrom$Value", + "name": "org.apache.kafka.connect.transforms.HeaderFrom$Value", + "description": "org.apache.kafka.connect.transforms.HeaderFrom$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.HeaderFrom$Value.json" + }, + { + "class": "org.apache.kafka.connect.transforms.HeaderFrom$ValueHuggingFace", + "name": "org.apache.kafka.connect.transforms.HeaderFrom$Value", + "description": "org.apache.kafka.connect.transforms.HeaderFrom$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.HeaderFrom$ValueHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.HeaderFrom$ValueMinilm", + "name": "org.apache.kafka.connect.transforms.HeaderFrom$Value", + "description": "org.apache.kafka.connect.transforms.HeaderFrom$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.HeaderFrom$ValueMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.HeaderFrom$ValueOllama", + "name": "org.apache.kafka.connect.transforms.HeaderFrom$Value", + "description": "org.apache.kafka.connect.transforms.HeaderFrom$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.HeaderFrom$ValueOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.HeaderFrom$ValueVoyageAi", + "name": "org.apache.kafka.connect.transforms.HeaderFrom$Value", + "description": "org.apache.kafka.connect.transforms.HeaderFrom$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.HeaderFrom$ValueVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.HoistField$Key", + "name": "org.apache.kafka.connect.transforms.HoistField$Key", + "description": "org.apache.kafka.connect.transforms.HoistField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.HoistField$Key.json" + }, + { + "class": "org.apache.kafka.connect.transforms.HoistField$KeyHuggingFace", + "name": "org.apache.kafka.connect.transforms.HoistField$Key", + "description": "org.apache.kafka.connect.transforms.HoistField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.HoistField$KeyHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.HoistField$KeyMinilm", + "name": "org.apache.kafka.connect.transforms.HoistField$Key", + "description": "org.apache.kafka.connect.transforms.HoistField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.HoistField$KeyMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.HoistField$KeyOllama", + "name": "org.apache.kafka.connect.transforms.HoistField$Key", + "description": "org.apache.kafka.connect.transforms.HoistField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.HoistField$KeyOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.HoistField$KeyVoyageAi", + "name": "org.apache.kafka.connect.transforms.HoistField$Key", + "description": "org.apache.kafka.connect.transforms.HoistField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.HoistField$KeyVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.HoistField$Value", + "name": "org.apache.kafka.connect.transforms.HoistField$Value", + "description": "org.apache.kafka.connect.transforms.HoistField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.HoistField$Value.json" + }, + { + "class": "org.apache.kafka.connect.transforms.HoistField$ValueHuggingFace", + "name": "org.apache.kafka.connect.transforms.HoistField$Value", + "description": "org.apache.kafka.connect.transforms.HoistField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.HoistField$ValueHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.HoistField$ValueMinilm", + "name": "org.apache.kafka.connect.transforms.HoistField$Value", + "description": "org.apache.kafka.connect.transforms.HoistField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.HoistField$ValueMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.HoistField$ValueOllama", + "name": "org.apache.kafka.connect.transforms.HoistField$Value", + "description": "org.apache.kafka.connect.transforms.HoistField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.HoistField$ValueOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.HoistField$ValueVoyageAi", + "name": "org.apache.kafka.connect.transforms.HoistField$Value", + "description": "org.apache.kafka.connect.transforms.HoistField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.HoistField$ValueVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.InsertField$Key", + "name": "org.apache.kafka.connect.transforms.InsertField$Key", + "description": "org.apache.kafka.connect.transforms.InsertField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.InsertField$Key.json" + }, + { + "class": "org.apache.kafka.connect.transforms.InsertField$KeyHuggingFace", + "name": "org.apache.kafka.connect.transforms.InsertField$Key", + "description": "org.apache.kafka.connect.transforms.InsertField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.InsertField$KeyHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.InsertField$KeyMinilm", + "name": "org.apache.kafka.connect.transforms.InsertField$Key", + "description": "org.apache.kafka.connect.transforms.InsertField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.InsertField$KeyMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.InsertField$KeyOllama", + "name": "org.apache.kafka.connect.transforms.InsertField$Key", + "description": "org.apache.kafka.connect.transforms.InsertField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.InsertField$KeyOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.InsertField$KeyVoyageAi", + "name": "org.apache.kafka.connect.transforms.InsertField$Key", + "description": "org.apache.kafka.connect.transforms.InsertField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.InsertField$KeyVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.InsertField$Value", + "name": "org.apache.kafka.connect.transforms.InsertField$Value", + "description": "org.apache.kafka.connect.transforms.InsertField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.InsertField$Value.json" + }, + { + "class": "org.apache.kafka.connect.transforms.InsertField$ValueHuggingFace", + "name": "org.apache.kafka.connect.transforms.InsertField$Value", + "description": "org.apache.kafka.connect.transforms.InsertField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.InsertField$ValueHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.InsertField$ValueMinilm", + "name": "org.apache.kafka.connect.transforms.InsertField$Value", + "description": "org.apache.kafka.connect.transforms.InsertField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.InsertField$ValueMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.InsertField$ValueOllama", + "name": "org.apache.kafka.connect.transforms.InsertField$Value", + "description": "org.apache.kafka.connect.transforms.InsertField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.InsertField$ValueOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.InsertField$ValueVoyageAi", + "name": "org.apache.kafka.connect.transforms.InsertField$Value", + "description": "org.apache.kafka.connect.transforms.InsertField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.InsertField$ValueVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.InsertHeader", + "name": "org.apache.kafka.connect.transforms.InsertHeader", + "description": "org.apache.kafka.connect.transforms.InsertHeader", + "descriptor": "transformation/org.apache.kafka.connect.transforms.InsertHeader.json" + }, + { + "class": "org.apache.kafka.connect.transforms.InsertHeaderHuggingFace", + "name": "org.apache.kafka.connect.transforms.InsertHeader", + "description": "org.apache.kafka.connect.transforms.InsertHeader", + "descriptor": "transformation/org.apache.kafka.connect.transforms.InsertHeaderHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.InsertHeaderMinilm", + "name": "org.apache.kafka.connect.transforms.InsertHeader", + "description": "org.apache.kafka.connect.transforms.InsertHeader", + "descriptor": "transformation/org.apache.kafka.connect.transforms.InsertHeaderMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.InsertHeaderOllama", + "name": "org.apache.kafka.connect.transforms.InsertHeader", + "description": "org.apache.kafka.connect.transforms.InsertHeader", + "descriptor": "transformation/org.apache.kafka.connect.transforms.InsertHeaderOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.InsertHeaderVoyageAi", + "name": "org.apache.kafka.connect.transforms.InsertHeader", + "description": "org.apache.kafka.connect.transforms.InsertHeader", + "descriptor": "transformation/org.apache.kafka.connect.transforms.InsertHeaderVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.MaskField$Key", + "name": "org.apache.kafka.connect.transforms.MaskField$Key", + "description": "org.apache.kafka.connect.transforms.MaskField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.MaskField$Key.json" + }, + { + "class": "org.apache.kafka.connect.transforms.MaskField$KeyHuggingFace", + "name": "org.apache.kafka.connect.transforms.MaskField$Key", + "description": "org.apache.kafka.connect.transforms.MaskField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.MaskField$KeyHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.MaskField$KeyMinilm", + "name": "org.apache.kafka.connect.transforms.MaskField$Key", + "description": "org.apache.kafka.connect.transforms.MaskField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.MaskField$KeyMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.MaskField$KeyOllama", + "name": "org.apache.kafka.connect.transforms.MaskField$Key", + "description": "org.apache.kafka.connect.transforms.MaskField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.MaskField$KeyOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.MaskField$KeyVoyageAi", + "name": "org.apache.kafka.connect.transforms.MaskField$Key", + "description": "org.apache.kafka.connect.transforms.MaskField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.MaskField$KeyVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.MaskField$Value", + "name": "org.apache.kafka.connect.transforms.MaskField$Value", + "description": "org.apache.kafka.connect.transforms.MaskField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.MaskField$Value.json" + }, + { + "class": "org.apache.kafka.connect.transforms.MaskField$ValueHuggingFace", + "name": "org.apache.kafka.connect.transforms.MaskField$Value", + "description": "org.apache.kafka.connect.transforms.MaskField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.MaskField$ValueHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.MaskField$ValueMinilm", + "name": "org.apache.kafka.connect.transforms.MaskField$Value", + "description": "org.apache.kafka.connect.transforms.MaskField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.MaskField$ValueMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.MaskField$ValueOllama", + "name": "org.apache.kafka.connect.transforms.MaskField$Value", + "description": "org.apache.kafka.connect.transforms.MaskField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.MaskField$ValueOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.MaskField$ValueVoyageAi", + "name": "org.apache.kafka.connect.transforms.MaskField$Value", + "description": "org.apache.kafka.connect.transforms.MaskField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.MaskField$ValueVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.RegexRouter", + "name": "org.apache.kafka.connect.transforms.RegexRouter", + "description": "org.apache.kafka.connect.transforms.RegexRouter", + "descriptor": "transformation/org.apache.kafka.connect.transforms.RegexRouter.json" + }, + { + "class": "org.apache.kafka.connect.transforms.RegexRouterHuggingFace", + "name": "org.apache.kafka.connect.transforms.RegexRouter", + "description": "org.apache.kafka.connect.transforms.RegexRouter", + "descriptor": "transformation/org.apache.kafka.connect.transforms.RegexRouterHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.RegexRouterMinilm", + "name": "org.apache.kafka.connect.transforms.RegexRouter", + "description": "org.apache.kafka.connect.transforms.RegexRouter", + "descriptor": "transformation/org.apache.kafka.connect.transforms.RegexRouterMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.RegexRouterOllama", + "name": "org.apache.kafka.connect.transforms.RegexRouter", + "description": "org.apache.kafka.connect.transforms.RegexRouter", + "descriptor": "transformation/org.apache.kafka.connect.transforms.RegexRouterOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.RegexRouterVoyageAi", + "name": "org.apache.kafka.connect.transforms.RegexRouter", + "description": "org.apache.kafka.connect.transforms.RegexRouter", + "descriptor": "transformation/org.apache.kafka.connect.transforms.RegexRouterVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ReplaceField$Key", + "name": "org.apache.kafka.connect.transforms.ReplaceField$Key", + "description": "org.apache.kafka.connect.transforms.ReplaceField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ReplaceField$Key.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ReplaceField$KeyHuggingFace", + "name": "org.apache.kafka.connect.transforms.ReplaceField$Key", + "description": "org.apache.kafka.connect.transforms.ReplaceField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ReplaceField$KeyHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ReplaceField$KeyMinilm", + "name": "org.apache.kafka.connect.transforms.ReplaceField$Key", + "description": "org.apache.kafka.connect.transforms.ReplaceField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ReplaceField$KeyMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ReplaceField$KeyOllama", + "name": "org.apache.kafka.connect.transforms.ReplaceField$Key", + "description": "org.apache.kafka.connect.transforms.ReplaceField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ReplaceField$KeyOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ReplaceField$KeyVoyageAi", + "name": "org.apache.kafka.connect.transforms.ReplaceField$Key", + "description": "org.apache.kafka.connect.transforms.ReplaceField$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ReplaceField$KeyVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ReplaceField$Value", + "name": "org.apache.kafka.connect.transforms.ReplaceField$Value", + "description": "org.apache.kafka.connect.transforms.ReplaceField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ReplaceField$Value.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ReplaceField$ValueHuggingFace", + "name": "org.apache.kafka.connect.transforms.ReplaceField$Value", + "description": "org.apache.kafka.connect.transforms.ReplaceField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ReplaceField$ValueHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ReplaceField$ValueMinilm", + "name": "org.apache.kafka.connect.transforms.ReplaceField$Value", + "description": "org.apache.kafka.connect.transforms.ReplaceField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ReplaceField$ValueMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ReplaceField$ValueOllama", + "name": "org.apache.kafka.connect.transforms.ReplaceField$Value", + "description": "org.apache.kafka.connect.transforms.ReplaceField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ReplaceField$ValueOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ReplaceField$ValueVoyageAi", + "name": "org.apache.kafka.connect.transforms.ReplaceField$Value", + "description": "org.apache.kafka.connect.transforms.ReplaceField$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ReplaceField$ValueVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Key", + "name": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Key", + "description": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.SetSchemaMetadata$Key.json" + }, + { + "class": "org.apache.kafka.connect.transforms.SetSchemaMetadata$KeyHuggingFace", + "name": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Key", + "description": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.SetSchemaMetadata$KeyHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.SetSchemaMetadata$KeyMinilm", + "name": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Key", + "description": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.SetSchemaMetadata$KeyMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.SetSchemaMetadata$KeyOllama", + "name": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Key", + "description": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.SetSchemaMetadata$KeyOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.SetSchemaMetadata$KeyVoyageAi", + "name": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Key", + "description": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.SetSchemaMetadata$KeyVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Value", + "name": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Value", + "description": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.SetSchemaMetadata$Value.json" + }, + { + "class": "org.apache.kafka.connect.transforms.SetSchemaMetadata$ValueHuggingFace", + "name": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Value", + "description": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.SetSchemaMetadata$ValueHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.SetSchemaMetadata$ValueMinilm", + "name": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Value", + "description": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.SetSchemaMetadata$ValueMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.SetSchemaMetadata$ValueOllama", + "name": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Value", + "description": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.SetSchemaMetadata$ValueOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.SetSchemaMetadata$ValueVoyageAi", + "name": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Value", + "description": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.SetSchemaMetadata$ValueVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.TimestampConverter$Key", + "name": "org.apache.kafka.connect.transforms.TimestampConverter$Key", + "description": "org.apache.kafka.connect.transforms.TimestampConverter$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.TimestampConverter$Key.json" + }, + { + "class": "org.apache.kafka.connect.transforms.TimestampConverter$KeyHuggingFace", + "name": "org.apache.kafka.connect.transforms.TimestampConverter$Key", + "description": "org.apache.kafka.connect.transforms.TimestampConverter$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.TimestampConverter$KeyHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.TimestampConverter$KeyMinilm", + "name": "org.apache.kafka.connect.transforms.TimestampConverter$Key", + "description": "org.apache.kafka.connect.transforms.TimestampConverter$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.TimestampConverter$KeyMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.TimestampConverter$KeyOllama", + "name": "org.apache.kafka.connect.transforms.TimestampConverter$Key", + "description": "org.apache.kafka.connect.transforms.TimestampConverter$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.TimestampConverter$KeyOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.TimestampConverter$KeyVoyageAi", + "name": "org.apache.kafka.connect.transforms.TimestampConverter$Key", + "description": "org.apache.kafka.connect.transforms.TimestampConverter$Key", + "descriptor": "transformation/org.apache.kafka.connect.transforms.TimestampConverter$KeyVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.TimestampConverter$Value", + "name": "org.apache.kafka.connect.transforms.TimestampConverter$Value", + "description": "org.apache.kafka.connect.transforms.TimestampConverter$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.TimestampConverter$Value.json" + }, + { + "class": "org.apache.kafka.connect.transforms.TimestampConverter$ValueHuggingFace", + "name": "org.apache.kafka.connect.transforms.TimestampConverter$Value", + "description": "org.apache.kafka.connect.transforms.TimestampConverter$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.TimestampConverter$ValueHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.TimestampConverter$ValueMinilm", + "name": "org.apache.kafka.connect.transforms.TimestampConverter$Value", + "description": "org.apache.kafka.connect.transforms.TimestampConverter$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.TimestampConverter$ValueMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.TimestampConverter$ValueOllama", + "name": "org.apache.kafka.connect.transforms.TimestampConverter$Value", + "description": "org.apache.kafka.connect.transforms.TimestampConverter$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.TimestampConverter$ValueOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.TimestampConverter$ValueVoyageAi", + "name": "org.apache.kafka.connect.transforms.TimestampConverter$Value", + "description": "org.apache.kafka.connect.transforms.TimestampConverter$Value", + "descriptor": "transformation/org.apache.kafka.connect.transforms.TimestampConverter$ValueVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.TimestampRouter", + "name": "org.apache.kafka.connect.transforms.TimestampRouter", + "description": "org.apache.kafka.connect.transforms.TimestampRouter", + "descriptor": "transformation/org.apache.kafka.connect.transforms.TimestampRouter.json" + }, + { + "class": "org.apache.kafka.connect.transforms.TimestampRouterHuggingFace", + "name": "org.apache.kafka.connect.transforms.TimestampRouter", + "description": "org.apache.kafka.connect.transforms.TimestampRouter", + "descriptor": "transformation/org.apache.kafka.connect.transforms.TimestampRouterHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.TimestampRouterMinilm", + "name": "org.apache.kafka.connect.transforms.TimestampRouter", + "description": "org.apache.kafka.connect.transforms.TimestampRouter", + "descriptor": "transformation/org.apache.kafka.connect.transforms.TimestampRouterMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.TimestampRouterOllama", + "name": "org.apache.kafka.connect.transforms.TimestampRouter", + "description": "org.apache.kafka.connect.transforms.TimestampRouter", + "descriptor": "transformation/org.apache.kafka.connect.transforms.TimestampRouterOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.TimestampRouterVoyageAi", + "name": "org.apache.kafka.connect.transforms.TimestampRouter", + "description": "org.apache.kafka.connect.transforms.TimestampRouter", + "descriptor": "transformation/org.apache.kafka.connect.transforms.TimestampRouterVoyageAi.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ValueToKey", + "name": "org.apache.kafka.connect.transforms.ValueToKey", + "description": "org.apache.kafka.connect.transforms.ValueToKey", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ValueToKey.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ValueToKeyHuggingFace", + "name": "org.apache.kafka.connect.transforms.ValueToKey", + "description": "org.apache.kafka.connect.transforms.ValueToKey", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ValueToKeyHuggingFace.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ValueToKeyMinilm", + "name": "org.apache.kafka.connect.transforms.ValueToKey", + "description": "org.apache.kafka.connect.transforms.ValueToKey", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ValueToKeyMinilm.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ValueToKeyOllama", + "name": "org.apache.kafka.connect.transforms.ValueToKey", + "description": "org.apache.kafka.connect.transforms.ValueToKey", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ValueToKeyOllama.json" + }, + { + "class": "org.apache.kafka.connect.transforms.ValueToKeyVoyageAi", + "name": "org.apache.kafka.connect.transforms.ValueToKey", + "description": "org.apache.kafka.connect.transforms.ValueToKey", + "descriptor": "transformation/org.apache.kafka.connect.transforms.ValueToKeyVoyageAi.json" + } + ] } - } \ No newline at end of file +} \ No newline at end of file diff --git a/debezium-platform-stage/src/__mocks__/data/DestinationCatalog.json b/debezium-platform-stage/src/__mocks__/data/DestinationCatalog.json deleted file mode 100644 index 5b4f2248..00000000 --- a/debezium-platform-stage/src/__mocks__/data/DestinationCatalog.json +++ /dev/null @@ -1,114 +0,0 @@ -[ - { - "class": "kinesis", - "name": "Amazon Kinesis", - "description": "Streams change events to Amazon Kinesis, a fully managed, distributed streaming platform.", - "descriptor": "", - "role": "destination" - }, - { - "class": "pulsar", - "name": "Apache Pulsar", - "description": "Streams CDC events into Apache Pulsar, a distributed messaging and event-streaming platform offering low-latency and high-throughput.", - "descriptor": "", - "role": "destination" - }, - { - "class": "eventhubs", - "name": "Azure Event Hub", - "description": "Sends change events to Azure Event Hub, a real-time data ingestion service optimized for streaming millions of events per second.", - "descriptor": "", - "role": "destination" - }, - { - "class": "http", - "name": "HTTPClint", - "description": "Streams change events to any HTTP Server for additional processing with the original design goal to have Debezium act as a Knative Event Source.", - "descriptor": "", - "role": "destination" - }, - { - "class": "infinispan", - "name": "Infinispan", - "description": "Propagates events to Infinispan, a distributed in-memory data grid providing high availability and scalability for data replication.", - "descriptor": "", - "role": "destination" - }, - { - "class": "kafka", - "name": "Kafka", - "description": "Streams real-time change events into Apache Kafka, a distributed platform for building streaming data pipelines and real-time applications.", - "descriptor": "", - "role": "destination" - }, - { - "class": "milvus", - "name": "Milvus", - "description": "Streams change events to Milvus, a vector database for scalable similarity search and vector embedding.", - "descriptor": "", - "role": "destination" - }, - { - "class": "nats-streaming", - "name": "NATS Streaming", - "description": "Sends change events to NATS Streaming, a messaging system designed for distributed systems and microservices architectures.", - "descriptor": "", - "role": "destination" - }, - { - "class": "nats-jetstream", - "name": "NATS JetStream", - "description": "Sends change events to NATS JetStream, a messaging system designed for distributed systems and microservices architectures.", - "descriptor": "", - "role": "destination" - }, - { - "class": "pravega", - "name": "Pravega", - "description": "Streams change events to Pravega, a scalable storage system for distributed data streams, offering a tiered storage model.", - "descriptor": "", - "role": "destination" - }, - { - "class": "pubsub", - "name": "Pub/Sub", - "description": "Sends real-time change events to Google Pub/Sub, a fully managed messaging service for building event-driven systems and real-time analytics.", - "descriptor": "", - "role": "destination" - }, - { - "class": "pubsublite", - "name": "Pub/Sub Lite", - "description": "Streams events to Google Pub/Sub Lite, a cost-effective, low-latency message delivery service for simpler use cases.", - "descriptor": "", - "role": "destination" - }, - { - "class": "qdrant", - "name": "Qdrant", - "description": "Streams change events to Qdrant, a vector database for scalable similarity search and vector embedding.", - "descriptor": "", - "role": "destination" - }, - { - "class": "rabbitmq", - "name": "RabbitMQ", - "description": "Publishes change events to RabbitMQ, an open-source message broker supporting various messaging protocols for inter-service communication.", - "descriptor": "", - "role": "destination" - }, - { - "class": "redis", - "name": "Redis(Stream)", - "description": "Streams change events to Redis Streams, a high-performance, open-source, in-memory data structure store.", - "descriptor": "", - "role": "destination" - }, - { - "class": "rocketmq", - "name": "RocketMQ", - "description": "Streams change events to Apache RocketMQ, a high-throughput, low-latency messaging platform.", - "descriptor": "", - "role": "destination" - } -] diff --git a/debezium-platform-stage/src/components/CreateDestinationSchemaForm.tsx b/debezium-platform-stage/src/components/CreateDestinationSchemaForm.tsx index 6bc63fec..7fddfd27 100644 --- a/debezium-platform-stage/src/components/CreateDestinationSchemaForm.tsx +++ b/debezium-platform-stage/src/components/CreateDestinationSchemaForm.tsx @@ -66,7 +66,7 @@ const CreateDestinationSchemaForm = forwardRef< connectorSchema={connectorSchema} sourceId={destinationId} dataType={dataType} - initialSource={initialSource as any} + initialSource={initialSource} onSubmit={onSubmit} defaultLayoutMode={defaultLayoutMode} hideSignalCollections={true} diff --git a/debezium-platform-stage/src/components/CreateSchemaForm.tsx b/debezium-platform-stage/src/components/CreateSchemaForm.tsx index 970612a1..e498b8f2 100644 --- a/debezium-platform-stage/src/components/CreateSchemaForm.tsx +++ b/debezium-platform-stage/src/components/CreateSchemaForm.tsx @@ -362,7 +362,7 @@ const CreateSchemaForm = React.forwardRef< sections.push({ id: "signal-collections", label: "Signal Collections", type: "custom" }); } return sections; - }, [orderedGroups, groupedProperties]); + }, [orderedGroups, groupedProperties, hideSignalCollections]); useEffect(() => { if (layoutMode !== "jumplinks") return; diff --git a/debezium-platform-stage/src/components/DestinationSchemaReviewView.tsx b/debezium-platform-stage/src/components/DestinationSchemaReviewView.tsx index 9a1e9978..54945034 100644 --- a/debezium-platform-stage/src/components/DestinationSchemaReviewView.tsx +++ b/debezium-platform-stage/src/components/DestinationSchemaReviewView.tsx @@ -40,7 +40,7 @@ const DestinationSchemaReviewView: React.FC = return ( ({ useNavigate: () => vi.fn(), })); diff --git a/debezium-platform-stage/src/pages/Connection/ConnectionsCatalog.tsx b/debezium-platform-stage/src/pages/Connection/ConnectionsCatalog.tsx index b5b71443..a4562120 100644 --- a/debezium-platform-stage/src/pages/Connection/ConnectionsCatalog.tsx +++ b/debezium-platform-stage/src/pages/Connection/ConnectionsCatalog.tsx @@ -82,7 +82,7 @@ const ConnectionsCatalog: React.FunctionComponent = () } return _.sortBy(filtered, (o) => o.name.toLowerCase()); - }, [connectionsTypeSelected, searchQuery, sourceCatalog]); + }, [connectionsTypeSelected, searchQuery, sourceCatalog, destinationCatalog]); const onClear = () => { onSearch?.(""); diff --git a/debezium-platform-stage/src/pages/Destination/DestinationCatalog.test.tsx b/debezium-platform-stage/src/pages/Destination/DestinationCatalog.test.tsx index f08b7b73..0276dcdd 100644 --- a/debezium-platform-stage/src/pages/Destination/DestinationCatalog.test.tsx +++ b/debezium-platform-stage/src/pages/Destination/DestinationCatalog.test.tsx @@ -1,8 +1,10 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { screen, fireEvent, waitFor } from "@testing-library/react"; -import { describe, it, expect, vi } from "vitest"; +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { useQuery } from "react-query"; import { DestinationCatalog } from "./DestinationCatalog"; -import destinationCatalogFixture from "../../__mocks__/data/DestinationCatalog.json"; import { render } from "../../__test__/unit/test-utils"; +import catalogFixture from "../../__fixtures__/catalog.json"; const mockNavigate = vi.fn(); @@ -10,6 +12,14 @@ vi.mock("react-router-dom", () => ({ useNavigate: () => mockNavigate, })); +vi.mock("react-query", async (importOriginal) => { + const mod = await importOriginal(); + return { + ...mod, + useQuery: vi.fn(), + }; +}); + vi.mock("@components/CatalogGrid", () => ({ CatalogGrid: ({ searchResult }: { searchResult: { name: string }[] }) => (
@@ -20,18 +30,65 @@ vi.mock("@components/CatalogGrid", () => ({ ), })); +vi.mock("@components/CatalogSkeleton", () => ({ + __esModule: true, + default: () =>
, +})); + vi.mock("@components/PageTour", () => ({ __esModule: true, default: () => null, })); +// Extract destination catalog from fixture +const destinationCatalogFixture = (catalogFixture.components["server-sink"] ?? []).map((entry) => ({ + ...entry, + role: "destination", +})); + describe("DestinationCatalog", () => { - it("renders heading and static catalog entries", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("shows skeleton while catalog is loading", () => { + vi.mocked(useQuery).mockReturnValue({ + data: undefined, + error: null, + isLoading: true, + refetch: vi.fn(), + } as any); + + render(); + expect(screen.getByTestId("catalog-skeleton")).toBeInTheDocument(); + }); + + it("shows error alert when catalog query fails", () => { + vi.mocked(useQuery).mockReturnValue({ + data: undefined, + error: new Error("network failed"), + isLoading: false, + refetch: vi.fn(), + } as any); + + render(); + expect(screen.getByText("Failed to load destination catalog")).toBeInTheDocument(); + expect(screen.getByText("network failed")).toBeInTheDocument(); + }); + + it("renders catalog grid with connector names when loaded", () => { + vi.mocked(useQuery).mockReturnValue({ + data: destinationCatalogFixture as any, + error: null, + isLoading: false, + refetch: vi.fn(), + } as any); + render(); expect(screen.getByText("Destination catalog")).toBeInTheDocument(); expect(screen.getByTestId("destination-catalog-grid")).toHaveTextContent( - "Amazon Kinesis", + "io.debezium.server.kafka.KafkaChangeConsumer", ); expect( screen.getByText(`${destinationCatalogFixture.length} Items`), @@ -39,6 +96,13 @@ describe("DestinationCatalog", () => { }); it("debounces search to filter visible connectors", async () => { + vi.mocked(useQuery).mockReturnValue({ + data: destinationCatalogFixture as any, + error: null, + isLoading: false, + refetch: vi.fn(), + } as any); + render(); const searchInput = screen.getByPlaceholderText("Search by name"); @@ -47,9 +111,9 @@ describe("DestinationCatalog", () => { await waitFor( () => { expect(screen.getByTestId("destination-catalog-grid")).toHaveTextContent( - "Apache Pulsar", + "io.debezium.server.pulsar.PulsarChangeConsumer", ); - expect(screen.queryByText("Amazon Kinesis")).not.toBeInTheDocument(); + expect(screen.queryByText("io.debezium.server.kafka.KafkaChangeConsumer")).not.toBeInTheDocument(); }, { timeout: 3000 }, ); diff --git a/debezium-platform-stage/src/pages/Source/SourceCatalog.test.tsx b/debezium-platform-stage/src/pages/Source/SourceCatalog.test.tsx index a3d19863..cf5b0350 100644 --- a/debezium-platform-stage/src/pages/Source/SourceCatalog.test.tsx +++ b/debezium-platform-stage/src/pages/Source/SourceCatalog.test.tsx @@ -3,8 +3,14 @@ import { screen, fireEvent, waitFor } from "@testing-library/react"; import { describe, it, expect, vi, beforeEach } from "vitest"; import { useQuery } from "react-query"; import { SourceCatalog } from "./SourceCatalog"; -import sourceCatalogFixture from "../../__mocks__/data/SourceCatalog.json"; import { render } from "../../__test__/unit/test-utils"; +import catalogFixture from "../../__fixtures__/catalog.json"; + +// Extract source catalog from fixture +const sourceCatalogFixture = (catalogFixture.components["source-connector"] ?? []).map((entry) => ({ + ...entry, + role: "source", +})); const mockNavigate = vi.fn(); From 165bcff36f2616f813315d30ea6caf84679e1e98 Mon Sep 17 00:00:00 2001 From: indraraj Date: Tue, 12 May 2026 16:18:48 +0530 Subject: [PATCH 5/6] debezium/dbz#1921 Update the schema review to be common component Signed-off-by: indraraj --- .../CreateDestinationSchemaForm.tsx | 81 ------------------- .../src/components/CreateSchemaForm.tsx | 1 - .../DestinationSchemaReviewView.tsx | 53 ------------ ...emaReviewView.css => SchemaReviewView.css} | 0 ...iew.test.tsx => SchemaReviewView.test.tsx} | 10 +-- ...emaReviewView.tsx => SchemaReviewView.tsx} | 8 +- .../pages/Destination/CreateDestination.tsx | 13 +-- .../src/pages/Destination/EditDestination.tsx | 22 ++--- .../src/pages/Source/EditSource.tsx | 4 +- 9 files changed, 30 insertions(+), 162 deletions(-) delete mode 100644 debezium-platform-stage/src/components/CreateDestinationSchemaForm.tsx delete mode 100644 debezium-platform-stage/src/components/DestinationSchemaReviewView.tsx rename debezium-platform-stage/src/components/{SourceSchemaReviewView.css => SchemaReviewView.css} (100%) rename debezium-platform-stage/src/components/{SourceSchemaReviewView.test.tsx => SchemaReviewView.test.tsx} (91%) rename debezium-platform-stage/src/components/{SourceSchemaReviewView.tsx => SchemaReviewView.tsx} (98%) diff --git a/debezium-platform-stage/src/components/CreateDestinationSchemaForm.tsx b/debezium-platform-stage/src/components/CreateDestinationSchemaForm.tsx deleted file mode 100644 index 7fddfd27..00000000 --- a/debezium-platform-stage/src/components/CreateDestinationSchemaForm.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { forwardRef, useImperativeHandle, useRef } from "react"; -import { ConnectorSchema } from "../apis/types"; -import { Destination } from "../apis/apis"; -import CreateSchemaForm, { CreateSchemaFormHandle } from "./CreateSchemaForm"; - -export interface CreateDestinationSchemaFormHandle { - submit: () => void; - validate: () => boolean; - getLastValidationFailureBody: () => string | undefined; -} - -interface CreateDestinationSchemaFormProps { - connectorSchema: ConnectorSchema; - destinationId: string; - dataType?: string; - initialDestination?: Destination; - onSubmit: (payload: Record) => Promise; - defaultLayoutMode?: "tabs" | "jumplinks"; -} - -/** - * CreateDestinationSchemaForm - Adapter component for destination connectors - * - * This component adapts the CreateSchemaForm for use with destination connectors. - * It maintains the same interface and behavior but is specifically designed for - * destination connector schemas fetched from the catalog API. - * - * The underlying CreateSchemaForm is generic enough to handle both source and - * destination connectors, as they share the same schema structure from the catalog API. - */ -const CreateDestinationSchemaForm = forwardRef< - CreateDestinationSchemaFormHandle, - CreateDestinationSchemaFormProps ->(({ connectorSchema, destinationId, dataType, initialDestination, onSubmit, defaultLayoutMode }, ref) => { - const sourceFormRef = useRef(null); - - useImperativeHandle(ref, () => ({ - submit: () => { - sourceFormRef.current?.submit(); - }, - validate: () => { - return sourceFormRef.current?.validate() ?? false; - }, - getLastValidationFailureBody: () => { - return sourceFormRef.current?.getLastValidationFailureBody(); - }, - })); - - // Convert destination to source format for the form - const initialSource = initialDestination ? { - ...initialDestination, - // Ensure all required fields are present - id: initialDestination.id, - name: initialDestination.name, - type: initialDestination.type, - description: initialDestination.description, - config: initialDestination.config, - connection: initialDestination.connection, - vaults: initialDestination.vaults, - schema: initialDestination.schema, - } : undefined; - - return ( - - ); -}); - -CreateDestinationSchemaForm.displayName = "CreateDestinationSchemaForm"; - -export default CreateDestinationSchemaForm; - -// Made with Bob diff --git a/debezium-platform-stage/src/components/CreateSchemaForm.tsx b/debezium-platform-stage/src/components/CreateSchemaForm.tsx index e498b8f2..63298c38 100644 --- a/debezium-platform-stage/src/components/CreateSchemaForm.tsx +++ b/debezium-platform-stage/src/components/CreateSchemaForm.tsx @@ -108,7 +108,6 @@ interface CreateSchemaFormProps { readOnly?: boolean; /** Initial layout; user can still switch via the toggle. Pipeline designer modal uses "tabs". */ defaultLayoutMode?: "jumplinks" | "tabs"; - /** Hide signal collections section (used for destination connectors) */ hideSignalCollections?: boolean; } diff --git a/debezium-platform-stage/src/components/DestinationSchemaReviewView.tsx b/debezium-platform-stage/src/components/DestinationSchemaReviewView.tsx deleted file mode 100644 index 54945034..00000000 --- a/debezium-platform-stage/src/components/DestinationSchemaReviewView.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from "react"; -import { ConnectorSchema } from "../apis/types"; -import { Destination } from "../apis/apis"; -import SourceSchemaReviewView from "./SourceSchemaReviewView"; - -interface DestinationSchemaReviewViewProps { - destination: Destination; - connectorSchema: ConnectorSchema; - dataType: string; -} - -/** - * DestinationSchemaReviewView - Adapter component for destination connector review - * - * This component adapts the SourceSchemaReviewView for use with destination connectors. - * It maintains the same interface and behavior but is specifically designed for - * destination connector schemas fetched from the catalog API. - * - * The underlying SourceSchemaReviewView is generic enough to handle both source and - * destination connectors, as they share the same schema structure from the catalog API. - */ -const DestinationSchemaReviewView: React.FC = ({ - destination, - connectorSchema, - dataType, -}) => { - // Convert destination to source format for the review view - const source = { - ...destination, - // Ensure all required fields are present - id: destination.id, - name: destination.name, - type: destination.type, - description: destination.description, - config: destination.config, - connection: destination.connection, - vaults: destination.vaults, - schema: destination.schema, - }; - - return ( - - ); -}; - -export default DestinationSchemaReviewView; - -// Made with Bob diff --git a/debezium-platform-stage/src/components/SourceSchemaReviewView.css b/debezium-platform-stage/src/components/SchemaReviewView.css similarity index 100% rename from debezium-platform-stage/src/components/SourceSchemaReviewView.css rename to debezium-platform-stage/src/components/SchemaReviewView.css diff --git a/debezium-platform-stage/src/components/SourceSchemaReviewView.test.tsx b/debezium-platform-stage/src/components/SchemaReviewView.test.tsx similarity index 91% rename from debezium-platform-stage/src/components/SourceSchemaReviewView.test.tsx rename to debezium-platform-stage/src/components/SchemaReviewView.test.tsx index 35e07a28..7361f8d3 100644 --- a/debezium-platform-stage/src/components/SourceSchemaReviewView.test.tsx +++ b/debezium-platform-stage/src/components/SchemaReviewView.test.tsx @@ -2,7 +2,7 @@ import { screen } from "@testing-library/react"; import { describe, it, expect, vi, beforeAll, beforeEach } from "vitest"; import { useQuery } from "react-query"; -import SourceSchemaReviewView from "./SourceSchemaReviewView"; +import SchemaReviewView from "./SchemaReviewView"; import type { Source } from "../apis/apis"; import type { ConnectorSchema } from "../apis/types"; import { render } from "../__test__/unit/test-utils"; @@ -55,7 +55,7 @@ beforeAll(() => { } as unknown as typeof IntersectionObserver; }); -describe("SourceSchemaReviewView", () => { +describe("SchemaReviewView", () => { beforeEach(() => { vi.clearAllMocks(); }); @@ -69,7 +69,7 @@ describe("SourceSchemaReviewView", () => { } as any); render( - , + , ); expect(await screen.findByText("Review Source")).toBeInTheDocument(); @@ -87,7 +87,7 @@ describe("SourceSchemaReviewView", () => { } as any); render( - , + , ); const unsetValues = await screen.findAllByText("—"); @@ -143,7 +143,7 @@ describe("SourceSchemaReviewView", () => { }; render( - , + , ); expect( diff --git a/debezium-platform-stage/src/components/SourceSchemaReviewView.tsx b/debezium-platform-stage/src/components/SchemaReviewView.tsx similarity index 98% rename from debezium-platform-stage/src/components/SourceSchemaReviewView.tsx rename to debezium-platform-stage/src/components/SchemaReviewView.tsx index e9f6f64a..c32325ea 100644 --- a/debezium-platform-stage/src/components/SourceSchemaReviewView.tsx +++ b/debezium-platform-stage/src/components/SchemaReviewView.tsx @@ -34,11 +34,11 @@ import ApiComponentError from "./ApiComponentError"; import TableViewComponent from "./TableViewComponent"; import _ from "lodash"; import "./CreateSchemaForm.css"; -import "./SourceSchemaReviewView.css"; +import "./SchemaReviewView.css"; const EMPTY_DISPLAY = "—"; -export interface SourceSchemaReviewViewProps { +export interface SchemaReviewViewProps { source: Source; connectorSchema: ConnectorSchema; dataType?: string; @@ -82,7 +82,7 @@ const ReviewValueSpan: React.FC<{ raw: string | undefined }> = ({ raw }) => { ); }; -const SourceSchemaReviewView: React.FC = ({ +const SchemaReviewView: React.FC = ({ source, connectorSchema, dataType, @@ -496,4 +496,4 @@ const SourceSchemaReviewView: React.FC = ({ ); }; -export default SourceSchemaReviewView; +export default SchemaReviewView; diff --git a/debezium-platform-stage/src/pages/Destination/CreateDestination.tsx b/debezium-platform-stage/src/pages/Destination/CreateDestination.tsx index 460a3020..6ba2c095 100644 --- a/debezium-platform-stage/src/pages/Destination/CreateDestination.tsx +++ b/debezium-platform-stage/src/pages/Destination/CreateDestination.tsx @@ -19,9 +19,9 @@ import { useTranslation } from "react-i18next"; import { useQuery } from "react-query"; import { fetchData } from "../../apis/apis"; import { ConnectorSchema } from "../../apis/types"; -import CreateDestinationSchemaForm, { - CreateDestinationSchemaFormHandle, -} from "@components/CreateDestinationSchemaForm"; +import CreateSchemaForm, { + CreateSchemaFormHandle, +} from "@components/CreateSchemaForm"; interface CreateDestinationProps { modelLoaded?: boolean; @@ -47,7 +47,7 @@ const CreateDestination: React.FunctionComponent = ({ const descriptor = (location.state as { descriptor?: string } | null)?.descriptor; const [isLoading, setIsLoading] = useState(false); - const formRef = useRef(null); + const formRef = useRef(null); const descriptorPath = React.useMemo(() => { if (descriptor) return descriptor.replace(/\.json$/, ""); @@ -123,11 +123,12 @@ const CreateDestination: React.FunctionComponent = ({ if (!connectorSchema) return null; return ( - ); diff --git a/debezium-platform-stage/src/pages/Destination/EditDestination.tsx b/debezium-platform-stage/src/pages/Destination/EditDestination.tsx index bd5e1a66..b784dd12 100644 --- a/debezium-platform-stage/src/pages/Destination/EditDestination.tsx +++ b/debezium-platform-stage/src/pages/Destination/EditDestination.tsx @@ -26,10 +26,10 @@ import { useTranslation } from "react-i18next"; import { useQuery, useQueryClient } from "react-query"; import { ConnectorSchema } from "../../apis/types"; import { getConnectorTypeName } from "../../utils/helpers"; -import CreateDestinationSchemaForm, { - CreateDestinationSchemaFormHandle, -} from "@components/CreateDestinationSchemaForm"; -import DestinationSchemaReviewView from "@components/DestinationSchemaReviewView"; +import CreateSchemaForm, { + CreateSchemaFormHandle, +} from "@components/CreateSchemaForm"; +import SchemaReviewView from "@components/SchemaReviewView"; import EditConfirmationModel from "../components/EditConfirmationModel"; import { resolveDestinationPageViewMode } from "./destinationPageNavigation"; @@ -50,7 +50,7 @@ const EditDestination: React.FunctionComponent = () => { } | null>(null); const [isLoading, setIsLoading] = useState(false); - const formRef = useRef(null); + const formRef = useRef(null); const { addNotification } = useNotification(); const queryClient = useQueryClient(); @@ -211,20 +211,22 @@ const EditDestination: React.FunctionComponent = () => { return ( {viewMode ? ( - ) : ( - )} diff --git a/debezium-platform-stage/src/pages/Source/EditSource.tsx b/debezium-platform-stage/src/pages/Source/EditSource.tsx index 7413568e..370d8d5a 100644 --- a/debezium-platform-stage/src/pages/Source/EditSource.tsx +++ b/debezium-platform-stage/src/pages/Source/EditSource.tsx @@ -29,7 +29,7 @@ import { getConnectorTypeName } from "../../utils/helpers"; import CreateSchemaForm, { CreateSchemaFormHandle, } from "@components/CreateSchemaForm"; -import SourceSchemaReviewView from "@components/SourceSchemaReviewView"; +import SchemaReviewView from "@components/SchemaReviewView"; import EditConfirmationModel from "../components/EditConfirmationModel"; import { resolveSourcePageViewMode } from "./sourcePageNavigation"; @@ -211,7 +211,7 @@ const EditSource: React.FunctionComponent = () => { return ( {viewMode ? ( - Date: Tue, 12 May 2026 17:13:11 +0530 Subject: [PATCH 6/6] debezium/dbz#1920 Fix CI issues Signed-off-by: indraraj --- .../src/components/SchemaReviewView.tsx | 1 - .../Connection/ConnectionsCatalog.test.tsx | 73 ++++++++++--------- .../pages/Destination/CreateDestination.tsx | 2 - .../src/pages/Destination/EditDestination.tsx | 2 - .../Destination/destinationPageNavigation.ts | 2 - 5 files changed, 37 insertions(+), 43 deletions(-) diff --git a/debezium-platform-stage/src/components/SchemaReviewView.tsx b/debezium-platform-stage/src/components/SchemaReviewView.tsx index c32325ea..c1663047 100644 --- a/debezium-platform-stage/src/components/SchemaReviewView.tsx +++ b/debezium-platform-stage/src/components/SchemaReviewView.tsx @@ -42,7 +42,6 @@ export interface SchemaReviewViewProps { source: Source; connectorSchema: ConnectorSchema; dataType?: string; - /** Hide signal collections section (used for destination connectors) */ hideSignalCollections?: boolean; } diff --git a/debezium-platform-stage/src/pages/Connection/ConnectionsCatalog.test.tsx b/debezium-platform-stage/src/pages/Connection/ConnectionsCatalog.test.tsx index 18aa04d6..c19fed51 100644 --- a/debezium-platform-stage/src/pages/Connection/ConnectionsCatalog.test.tsx +++ b/debezium-platform-stage/src/pages/Connection/ConnectionsCatalog.test.tsx @@ -1,15 +1,20 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { screen, fireEvent, waitFor } from "@testing-library/react"; import { describe, it, expect, vi, beforeEach } from "vitest"; -import { useQuery } from "react-query"; import { ConnectionsCatalog } from "./ConnectionsCatalog"; import type { Catalog } from "../../apis/types"; import { render } from "../../__test__/unit/test-utils"; +import catalogFixture from "../../__fixtures__/catalog.json"; -// Mock destination catalog data -const destinationCatalogFixture = [ - { id: "kafka", name: "Apache Kafka", description: "Kafka destination" }, - { id: "pulsar", name: "Apache Pulsar", description: "Pulsar destination" }, +// Mock data +const sourceCatalogRows: Catalog[] = [ + { + class: "mariadb", + name: "MariaDB", + description: "d", + descriptor: "desc", + role: "source", + }, ]; vi.mock("react-router-dom", () => ({ @@ -20,7 +25,26 @@ vi.mock("react-query", async (importOriginal) => { const mod = await importOriginal(); return { ...mod, - useQuery: vi.fn(), + useQuery: vi.fn((queryKey) => { + // Return different data based on query key + if (queryKey === "sourceConnectorCatalog") { + return { + data: sourceCatalogRows, + error: null, + isLoading: false, + }; + } else if (queryKey === "destinationConnectorCatalog") { + return { + data: catalogFixture.components["server-sink"].map((entry: any) => ({ + ...entry, + role: "destination", + })), + error: null, + isLoading: false, + }; + } + return { data: [], error: null, isLoading: false }; + }), }; }); @@ -38,46 +62,28 @@ vi.mock("@components/ConnectionCatalogGrid", () => ({ ), })); -const catalogRows: Catalog[] = [ - { - class: "mariadb", - name: "MariaDB", - description: "d", - descriptor: "desc", - role: "source", - }, - { - class: "kinesis", - name: "Amazon Kinesis", - description: "d", - descriptor: "desc2", - role: "destination", - }, -]; - describe("ConnectionsCatalog", () => { + const destinationCatalogFixture = catalogFixture.components["server-sink"].map((entry: any) => ({ + ...entry, + role: "destination", + })); const defaultMergedCount = - catalogRows.length + destinationCatalogFixture.length; + sourceCatalogRows.length + destinationCatalogFixture.length; beforeEach(() => { vi.clearAllMocks(); }); it("renders title and merged catalog entries", () => { - vi.mocked(useQuery).mockReturnValue({ - data: catalogRows, - error: null, - isLoading: false, - } as any); - render(); expect(screen.getByText("Connection catalog")).toBeInTheDocument(); expect(screen.getByTestId("connection-catalog-grid")).toHaveTextContent( "MariaDB", ); + // Check for one of the destinations from the fixture expect(screen.getByTestId("connection-catalog-grid")).toHaveTextContent( - "Amazon Kinesis", + "io.debezium.server.kafka.KafkaChangeConsumer", ); expect( screen.getByText(new RegExp(`${defaultMergedCount}\\s+Items`)), @@ -85,11 +91,6 @@ describe("ConnectionsCatalog", () => { }); it("shows empty search state when no connectors match", async () => { - vi.mocked(useQuery).mockReturnValue({ - data: catalogRows, - error: null, - isLoading: false, - } as any); render(); diff --git a/debezium-platform-stage/src/pages/Destination/CreateDestination.tsx b/debezium-platform-stage/src/pages/Destination/CreateDestination.tsx index 6ba2c095..b0063dda 100644 --- a/debezium-platform-stage/src/pages/Destination/CreateDestination.tsx +++ b/debezium-platform-stage/src/pages/Destination/CreateDestination.tsx @@ -196,5 +196,3 @@ const CreateDestination: React.FunctionComponent = ({ }; export { CreateDestination }; - -// Made with Bob diff --git a/debezium-platform-stage/src/pages/Destination/EditDestination.tsx b/debezium-platform-stage/src/pages/Destination/EditDestination.tsx index b784dd12..edbb1c1c 100644 --- a/debezium-platform-stage/src/pages/Destination/EditDestination.tsx +++ b/debezium-platform-stage/src/pages/Destination/EditDestination.tsx @@ -304,5 +304,3 @@ const EditDestination: React.FunctionComponent = () => { }; export { EditDestination }; - -// Made with Bob diff --git a/debezium-platform-stage/src/pages/Destination/destinationPageNavigation.ts b/debezium-platform-stage/src/pages/Destination/destinationPageNavigation.ts index 522e2889..03745450 100644 --- a/debezium-platform-stage/src/pages/Destination/destinationPageNavigation.ts +++ b/debezium-platform-stage/src/pages/Destination/destinationPageNavigation.ts @@ -22,5 +22,3 @@ export function resolveDestinationPageViewMode( return false; } - -// Made with Bob