diff --git a/airbyte-webapp/src/components/hooks/services/useConnectionHook.tsx b/airbyte-webapp/src/components/hooks/services/useConnectionHook.tsx index a6ae16b653efb..3dff6088ca6f5 100644 --- a/airbyte-webapp/src/components/hooks/services/useConnectionHook.tsx +++ b/airbyte-webapp/src/components/hooks/services/useConnectionHook.tsx @@ -1,15 +1,17 @@ -import { useCallback, useEffect, useState } from "react"; -import { useFetcher, useResource } from "rest-hooks"; +import { useCallback } from "react"; +import { useResource, useFetcher } from "rest-hooks"; import config from "config"; +import FrequencyConfig from "config/FrequencyConfig.json"; + import { AnalyticsService } from "core/analytics/AnalyticsService"; +import { connectionService, Connection } from "core/domain/connection"; + import ConnectionResource, { - Connection, ScheduleProperties, } from "core/resources/Connection"; import { SyncSchema } from "core/domain/catalog"; import { SourceDefinition } from "core/resources/SourceDefinition"; -import FrequencyConfig from "config/FrequencyConfig.json"; import { Source } from "core/resources/Source"; import { Routes } from "pages/routes"; import useRouter from "../useRouterHook"; @@ -58,44 +60,25 @@ type UpdateStateConnection = { sourceName: string; prefix: string; connectionConfiguration: ConnectionConfiguration; - schedule: { - units: number; - timeUnit: string; - } | null; + schedule: ScheduleProperties | null; }; export const useConnectionLoad = ( - connectionId: string, - withRefresh?: boolean -): { connection: Connection | null; isLoadingConnection: boolean } => { - const [connection, setConnection] = useState(null); - const [isLoadingConnection, setIsLoadingConnection] = useState(false); - - // TODO: change to useStatefulResource - const fetchConnection = useFetcher(ConnectionResource.detailShape(), false); - const baseConnection = useResource(ConnectionResource.detailShape(), { + connectionId: string +): { + connection: Connection; + refreshConnectionCatalog: () => Promise; +} => { + const connection = useResource(ConnectionResource.detailShape(), { connectionId, }); - useEffect(() => { - (async () => { - if (withRefresh) { - setIsLoadingConnection(true); - setConnection( - await fetchConnection({ - connectionId, - withRefreshedCatalog: withRefresh, - }) - ); - - setIsLoadingConnection(false); - } - })(); - }, [connectionId, fetchConnection, withRefresh]); + const refreshConnectionCatalog = async () => + await connectionService.getConnection(connectionId, true); return { - connection: withRefresh ? connection : baseConnection, - isLoadingConnection, + connection, + refreshConnectionCatalog, }; }; @@ -155,7 +138,6 @@ const useConnection = (): { ); AnalyticsService.track("New Connection - Action", { - user_id: config.ui.workspaceId, action: "Set up connection", frequency: frequencyData?.text, connector_source_definition: source?.sourceName, diff --git a/airbyte-webapp/src/core/domain/connection/ConnectionService.ts b/airbyte-webapp/src/core/domain/connection/ConnectionService.ts new file mode 100644 index 0000000000000..a8f467df31b8d --- /dev/null +++ b/airbyte-webapp/src/core/domain/connection/ConnectionService.ts @@ -0,0 +1,22 @@ +import { AirbyteRequestService } from "core/request/AirbyteRequestService"; +import { Connection } from "./types"; + +class ConnectionService extends AirbyteRequestService { + get url() { + return "web_backend/connections"; + } + + public async getConnection( + connectionId: string, + withRefreshedCatalog?: boolean + ): Promise { + const rs = ((await this.fetch(`${this.url}/get`, { + connectionId, + withRefreshedCatalog, + })) as any) as Connection; + + return rs; + } +} + +export const connectionService = new ConnectionService(); diff --git a/airbyte-webapp/src/core/domain/connection/index.ts b/airbyte-webapp/src/core/domain/connection/index.ts index eea524d655708..5ac0dd6fe5787 100644 --- a/airbyte-webapp/src/core/domain/connection/index.ts +++ b/airbyte-webapp/src/core/domain/connection/index.ts @@ -1 +1,4 @@ export * from "./types"; +export * from "./operation"; +export * from "./ConnectionService"; +export * from "./OperationService"; diff --git a/airbyte-webapp/src/core/domain/connection/types.ts b/airbyte-webapp/src/core/domain/connection/types.ts index dd5decf1552ad..565206c62c9f8 100644 --- a/airbyte-webapp/src/core/domain/connection/types.ts +++ b/airbyte-webapp/src/core/domain/connection/types.ts @@ -1,3 +1,8 @@ +import { SyncSchema } from "core/domain/catalog"; +import { Source } from "core/resources/Source"; +import { Destination } from "core/resources/Destination"; +import { Operation } from "./operation"; + // eslint-disable-next-line @typescript-eslint/no-explicit-any type ConnectionConfiguration = any; @@ -14,3 +19,30 @@ export enum ConnectionNamespaceDefinition { Destination = "destination", CustomFormat = "customformat", } + +export type ScheduleProperties = { + units: number; + timeUnit: string; +}; + +export interface Connection { + connectionId: string; + name: string; + prefix: string; + sourceId: string; + destinationId: string; + status: string; + schedule: ScheduleProperties | null; + syncCatalog: SyncSchema; + latestSyncJobCreatedAt?: number | null; + namespaceDefinition: ConnectionNamespaceDefinition; + namespaceFormat: string; + isSyncing?: boolean; + latestSyncJobStatus: string | null; + operationIds: string[]; + + // WebBackend connection specific fields + source: Source; + destination: Destination; + operations: Operation[]; +} diff --git a/airbyte-webapp/src/core/resources/Connection.ts b/airbyte-webapp/src/core/resources/Connection.ts index 2eec13d0b57d8..1248a7a81c341 100644 --- a/airbyte-webapp/src/core/resources/Connection.ts +++ b/airbyte-webapp/src/core/resources/Connection.ts @@ -9,39 +9,18 @@ import { import { SyncSchema } from "core/domain/catalog"; import { CommonRequestError } from "core/request/CommonRequestError"; -import { Operation } from "core/domain/connection/operation"; import { Source } from "./Source"; import { Destination } from "./Destination"; import BaseResource from "./BaseResource"; -import { ConnectionNamespaceDefinition } from "../domain/connection"; - -export type ScheduleProperties = { - units: number; - timeUnit: string; -}; - -export interface Connection { - connectionId: string; - name: string; - prefix: string; - sourceId: string; - destinationId: string; - status: string; - schedule: ScheduleProperties | null; - syncCatalog: SyncSchema; - latestSyncJobCreatedAt?: number | null; - namespaceDefinition: ConnectionNamespaceDefinition; - namespaceFormat: string; - isSyncing?: boolean; - latestSyncJobStatus: string | null; - operationIds: string[]; - - // WebBackend connection specific fields - source: Source; - destination: Destination; - operations: Operation[]; -} +import { + ConnectionNamespaceDefinition, + Connection, + ScheduleProperties, + Operation, +} from "core/domain/connection"; + +export type { Connection, ScheduleProperties }; export default class ConnectionResource extends BaseResource @@ -83,10 +62,8 @@ export default class ConnectionResource ): ReadShape> { return { ...super.detailShape(), - getFetchKey: (params: { - connectionId: string; - withRefreshedCatalog?: boolean; - }) => "POST /web_backend/get" + JSON.stringify(params), + getFetchKey: (params: { connectionId: string }) => + "POST /web_backend/get" + JSON.stringify(params), fetch: async ( params: Readonly> ): Promise => diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/EnabledControl.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/EnabledControl.tsx index a30272a0f820e..c5334b278b636 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/EnabledControl.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/EnabledControl.tsx @@ -27,10 +27,15 @@ const Content = styled.div` type IProps = { connection: Connection; + disabled?: boolean; frequencyText?: string; }; -const EnabledControl: React.FC = ({ connection, frequencyText }) => { +const EnabledControl: React.FC = ({ + connection, + disabled, + frequencyText, +}) => { const { updateConnection } = useConnection(); const onChangeStatus = async () => { @@ -48,7 +53,7 @@ const EnabledControl: React.FC = ({ connection, frequencyText }) => { AnalyticsService.track("Source - Action", { action: - connection.status === "active" + connection.status === Status.ACTIVE ? "Disable connection" : "Reenable connection", connector_source: connection.source?.sourceName, @@ -65,15 +70,16 @@ const EnabledControl: React.FC = ({ connection, frequencyText }) => { diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/SettingsView.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/SettingsView.tsx index 36d812401f7f0..d28fcf8208c01 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/SettingsView.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/SettingsView.tsx @@ -21,6 +21,7 @@ import { SourceDefinition } from "core/resources/SourceDefinition"; import { equal } from "utils/objects"; import EnabledControl from "./EnabledControl"; import { ConnectionNamespaceDefinition } from "core/domain/connection"; +import { useAsyncFn } from "react-use"; type IProps = { onAfterSaveSchema: () => void; @@ -86,17 +87,13 @@ const SettingsView: React.FC = ({ prefix: "", syncCatalog: { streams: [] }, }); + const { updateConnection, deleteConnection, resetConnection, } = useConnection(); - const { connection, isLoadingConnection } = useConnectionLoad( - connectionId, - activeUpdatingSchemaMode - ); - const onDelete = useCallback(() => deleteConnection({ connectionId }), [ deleteConnection, connectionId, @@ -107,13 +104,27 @@ const SettingsView: React.FC = ({ connectionId, ]); + const { + connection: initialConnection, + refreshConnectionCatalog, + } = useConnectionLoad(connectionId); + + const [ + { value: connectionWithRefreshCatalog, loading: isRefreshingCatalog }, + refreshCatalog, + ] = useAsyncFn(refreshConnectionCatalog, [connectionId]); + + const connection = activeUpdatingSchemaMode + ? connectionWithRefreshCatalog + : initialConnection; + const onSubmit = async (values: ValuesProps) => { const initialSyncSchema = connection?.syncCatalog; await updateConnection({ ...values, - connectionId: connectionId, - status: connection?.status || "", + connectionId, + status: initialConnection.status || "", withRefreshedCatalog: activeUpdatingSchemaMode, }); @@ -141,10 +152,19 @@ const SettingsView: React.FC = ({ } }; - const UpdateSchemaButton = () => { + const onEnterRefreshCatalogMode = async () => { + setActiveUpdatingSchemaMode(true); + await refreshCatalog(); + }; + + const onExitRefreshCatalogMode = () => { + setActiveUpdatingSchemaMode(false); + }; + + const renderUpdateSchemaButton = () => { if (!activeUpdatingSchemaMode) { return ( - @@ -168,16 +188,15 @@ const SettingsView: React.FC = ({ {" "} - {connection && ( - - )} + } > - {!isLoadingConnection && connection ? ( + {!isRefreshingCatalog && connection ? ( = ({ successMessage={ saved && } - onCancel={() => setActiveUpdatingSchemaMode(false)} + onCancel={onExitRefreshCatalogMode} editSchemeMode={activeUpdatingSchemaMode} - additionalSchemaControl={UpdateSchemaButton()} + additionalSchemaControl={renderUpdateSchemaButton()} destinationIcon={destinationDefinition?.icon} sourceIcon={sourceDefinition?.icon} />