From 5ccc9609b8d291b5511a68ba05797fa03bfaf60b Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Tue, 29 Mar 2022 16:42:55 +0300 Subject: [PATCH 01/20] Refactor sync and rest connections calls --- .../src/components/EntityTable/hooks.tsx | 24 +--- .../domain/connection/ConnectionService.ts | 21 +-- .../connection/WebBackendConnectionService.ts | 26 ++++ .../src/core/domain/connection/index.ts | 2 +- .../src/core/resources/Connection.ts | 56 -------- .../src/hooks/services/useConnectionHook.tsx | 132 +++++++----------- .../AllConnectionsPage/AllConnectionsPage.tsx | 11 +- .../components/ReplicationView.tsx | 4 +- .../components/StatusView.tsx | 8 +- .../DestinationItemPage.tsx | 6 +- .../pages/OnboardingPage/OnboardingPage.tsx | 7 +- .../pages/SourceItemPage/SourceItemPage.tsx | 7 +- 12 files changed, 117 insertions(+), 187 deletions(-) create mode 100644 airbyte-webapp/src/core/domain/connection/WebBackendConnectionService.ts diff --git a/airbyte-webapp/src/components/EntityTable/hooks.tsx b/airbyte-webapp/src/components/EntityTable/hooks.tsx index 9f17e5a13a97a..affb350f05354 100644 --- a/airbyte-webapp/src/components/EntityTable/hooks.tsx +++ b/airbyte-webapp/src/components/EntityTable/hooks.tsx @@ -1,8 +1,8 @@ -import { useFetcher } from "rest-hooks"; - import FrequencyConfig from "config/FrequencyConfig.json"; -import ConnectionResource, { Connection } from "core/resources/Connection"; -import useConnection from "hooks/services/useConnectionHook"; +import { Connection } from "core/resources/Connection"; +import useConnection, { + useSyncConnection, +} from "hooks/services/useConnectionHook"; import { Status } from "./types"; import { useAnalyticsService } from "hooks/services/Analytics/useAnalyticsService"; @@ -11,7 +11,7 @@ const useSyncActions = (): { syncManualConnection: (connection: Connection) => Promise; } => { const { updateConnection } = useConnection(); - const SyncConnection = useFetcher(ConnectionResource.syncShape()); + const { mutateAsync: syncConnection } = useSyncConnection(); const analyticsService = useAnalyticsService(); const changeStatus = async (connection: Connection) => { @@ -47,19 +47,7 @@ const useSyncActions = (): { }; const syncManualConnection = async (connection: Connection) => { - analyticsService.track("Source - Action", { - action: "Full refresh sync", - connector_source: connection.source?.sourceName, - connector_source_id: connection.source?.sourceDefinitionId, - connector_destination: connection.destination?.destinationName, - connector_destination_definition_id: - connection.destination?.destinationDefinitionId, - frequency: "manual", // Only manual connections have this button - }); - - await SyncConnection({ - connectionId: connection.connectionId, - }); + await syncConnection(connection); }; return { changeStatus, syncManualConnection }; diff --git a/airbyte-webapp/src/core/domain/connection/ConnectionService.ts b/airbyte-webapp/src/core/domain/connection/ConnectionService.ts index 8fd6204b6bb2d..12d9774a500e8 100644 --- a/airbyte-webapp/src/core/domain/connection/ConnectionService.ts +++ b/airbyte-webapp/src/core/domain/connection/ConnectionService.ts @@ -3,18 +3,21 @@ import { Connection } from "./types"; class ConnectionService extends AirbyteRequestService { get url() { - return "web_backend/connections"; + return "connections"; } - public async getConnection( - connectionId: string, - withRefreshedCatalog?: boolean - ): Promise { - const rs = ((await this.fetch(`${this.url}/get`, { + public async sync(connectionId: string): Promise { + const rs = await this.fetch(`${this.url}/sync`, { connectionId, - withRefreshedCatalog, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - })) as any) as Connection; + }); + + return rs; + } + + public async reset(connectionId: string): Promise { + const rs = await this.fetch(`${this.url}/reset`, { + connectionId, + }); return rs; } diff --git a/airbyte-webapp/src/core/domain/connection/WebBackendConnectionService.ts b/airbyte-webapp/src/core/domain/connection/WebBackendConnectionService.ts new file mode 100644 index 0000000000000..4cb7df32edfde --- /dev/null +++ b/airbyte-webapp/src/core/domain/connection/WebBackendConnectionService.ts @@ -0,0 +1,26 @@ +import { AirbyteRequestService } from "core/request/AirbyteRequestService"; +import { Connection } from "./types"; + +class WebBackendConnectionService extends AirbyteRequestService { + get url() { + return "web_backend/connections"; + } + + public async getConnection( + connectionId: string, + withRefreshedCatalog?: boolean + ): Promise { + return await this.fetch(`${this.url}/get`, { + connectionId, + withRefreshedCatalog, + }); + } + + public async list(workspaceId: string): Promise { + return await this.fetch(`${this.url}/list`, { + workspaceId, + }); + } +} + +export { WebBackendConnectionService }; diff --git a/airbyte-webapp/src/core/domain/connection/index.ts b/airbyte-webapp/src/core/domain/connection/index.ts index 5ac0dd6fe5787..2bf8b387b5622 100644 --- a/airbyte-webapp/src/core/domain/connection/index.ts +++ b/airbyte-webapp/src/core/domain/connection/index.ts @@ -1,4 +1,4 @@ export * from "./types"; export * from "./operation"; -export * from "./ConnectionService"; +export * from "./WebBackendConnectionService"; export * from "./OperationService"; diff --git a/airbyte-webapp/src/core/resources/Connection.ts b/airbyte-webapp/src/core/resources/Connection.ts index bebc4926d6065..353d04c42d81c 100644 --- a/airbyte-webapp/src/core/resources/Connection.ts +++ b/airbyte-webapp/src/core/resources/Connection.ts @@ -61,8 +61,6 @@ export default class ConnectionResource ): ReadShape> { return { ...super.detailShape(), - getFetchKey: (params: { connectionId: string }) => - "POST /web_backend/get" + JSON.stringify(params), fetch: async ( params: Readonly> ): Promise => @@ -165,61 +163,7 @@ export default class ConnectionResource ): FetchShape> { return { ...super.deleteShape(), - getFetchKey: (params: { connectionId: string }) => - "POST /app/delete" + JSON.stringify(params), fetch: async (): Promise => null, }; } - - static updateStateShape( - this: T - ): MutateShape> { - return { - ...super.partialUpdateShape(), - getFetchKey: (params: { connectionId: string }) => - "POST /web_backend/update" + JSON.stringify(params), - fetch: async ( - params: Readonly>, - body: Partial - ): Promise> => { - return { ...params, ...body }; - }, - }; - } - - static reset( - this: T - ): ReadShape> { - return { - ...super.detailShape(), - getFetchKey: (params: Readonly>) => - "POST " + this.url(params) + "/reset" + JSON.stringify(params), - fetch: async ( - params: Readonly<{ connectionId: string }> - ): Promise<{ connectionId: string }> => { - await this.fetch("post", `${this.url(params)}/reset`, params); - return { - connectionId: params.connectionId, - }; - }, - }; - } - - static syncShape( - this: T - ): ReadShape> { - return { - ...super.detailShape(), - getFetchKey: (params: Readonly>) => - "POST " + this.url(params) + "/sync" + JSON.stringify(params), - fetch: async ( - params: Readonly> - ): Promise<{ connectionId: string }> => { - await this.fetch("post", `${this.url(params)}/sync`, params); - return { - connectionId: params.connectionId, - }; - }, - }; - } } diff --git a/airbyte-webapp/src/hooks/services/useConnectionHook.tsx b/airbyte-webapp/src/hooks/services/useConnectionHook.tsx index edb3c9a79165d..06c007cb765f8 100644 --- a/airbyte-webapp/src/hooks/services/useConnectionHook.tsx +++ b/airbyte-webapp/src/hooks/services/useConnectionHook.tsx @@ -1,13 +1,12 @@ -import { useCallback, useMemo } from "react"; import { useFetcher, useResource } from "rest-hooks"; +import { useMutation } from "react-query"; import FrequencyConfig from "config/FrequencyConfig.json"; import { useConfig } from "config"; import { Connection, - ConnectionConfiguration, ConnectionNamespaceDefinition, - ConnectionService, + WebBackendConnectionService, } from "core/domain/connection"; import ConnectionResource, { @@ -18,12 +17,13 @@ import useWorkspace from "./useWorkspace"; import { Operation } from "core/domain/connection/operation"; import { useAnalyticsService } from "hooks/services/Analytics/useAnalyticsService"; import useRouter from "hooks/useRouter"; -import { useGetService } from "core/servicesProvider"; -import { RequestMiddleware } from "core/request/RequestMiddleware"; import { equal } from "utils/objects"; import { Destination, Source, SourceDefinition } from "core/domain/connector"; -import { RoutePaths } from "../../pages/routePaths"; +import { RoutePaths } from "pages/routePaths"; +import { useDefaultRequestMiddlewares } from "services/useDefaultRequestMiddlewares"; +import { useInitService } from "services/useInitService"; +import { ConnectionService } from "core/domain/connection/ConnectionService"; export type ValuesProps = { schedule: ScheduleProperties | null; @@ -56,24 +56,26 @@ type UpdateConnection = { withRefreshedCatalog?: boolean; }; -type UpdateStateConnection = { - connection: Connection; - sourceName: string; - prefix: string; - connectionConfiguration: ConnectionConfiguration; - schedule: ScheduleProperties | null; -}; +function useWebConnectionService(): WebBackendConnectionService { + const config = useConfig(); + const middlewares = useDefaultRequestMiddlewares(); + + return useInitService( + () => new WebBackendConnectionService(config.apiUrl, middlewares), + // eslint-disable-next-line react-hooks/exhaustive-deps + [config] + ); +} function useConnectionService(): ConnectionService { const config = useConfig(); - const middlewares = useGetService( - "DefaultRequestMiddlewares" - ); + const middlewares = useDefaultRequestMiddlewares(); - // eslint-disable-next-line react-hooks/exhaustive-deps - return useMemo(() => new ConnectionService(config.apiUrl, middlewares), [ - config, - ]); + return useInitService( + () => new ConnectionService(config.apiUrl, middlewares), + // eslint-disable-next-line react-hooks/exhaustive-deps + [config] + ); } export const useConnectionLoad = ( @@ -86,7 +88,7 @@ export const useConnectionLoad = ( connectionId, }); - const connectionService = useConnectionService(); + const connectionService = useWebConnectionService(); const refreshConnectionCatalog = async () => await connectionService.getConnection(connectionId, true); @@ -97,12 +99,38 @@ export const useConnectionLoad = ( }; }; +export const useSyncConnection = () => { + const service = useConnectionService(); + const analyticsService = useAnalyticsService(); + + return useMutation((connection: Connection) => { + const frequency = FrequencyConfig.find((item) => + equal(item.config, connection.schedule) + ); + + analyticsService.track("Source - Action", { + action: "Full refresh sync", + connector_source: connection.source?.sourceName, + connector_source_id: connection.source?.sourceDefinitionId, + connector_destination: connection.destination?.name, + connector_destination_definition_id: + connection.destination?.destinationDefinitionId, + frequency: frequency?.text, + }); + + return service.sync(connection.connectionId); + }); +}; + +export const useResetConnection = () => { + const service = useConnectionService(); + + return useMutation((connectionId: string) => service.reset(connectionId)); +}; + const useConnection = (): { createConnection: (conn: CreateConnectionProps) => Promise; updateConnection: (conn: UpdateConnection) => Promise; - updateStateConnection: (conn: UpdateStateConnection) => Promise; - resetConnection: (connId: string) => Promise; - syncConnection: (conn: Connection) => Promise; deleteConnection: (payload: { connectionId: string }) => Promise; } => { const { push } = useRouter(); @@ -111,14 +139,9 @@ const useConnection = (): { const createConnectionResource = useFetcher(ConnectionResource.createShape()); const updateConnectionResource = useFetcher(ConnectionResource.updateShape()); - const updateStateConnectionResource = useFetcher( - ConnectionResource.updateStateShape() - ); const deleteConnectionResource = useFetcher( ConnectionResource.deleteShapeItem() ); - const resetConnectionResource = useFetcher(ConnectionResource.reset()); - const syncConnectionResource = useFetcher(ConnectionResource.syncShape()); const createConnection = async ({ values, @@ -217,61 +240,10 @@ const useConnection = (): { ); }; - const updateStateConnection = async ({ - connection, - sourceName, - connectionConfiguration, - schedule, - prefix, - }: UpdateStateConnection) => { - await updateStateConnectionResource( - {}, - { - ...connection, - schedule, - prefix, - source: { - ...connection.source, - name: sourceName, - connectionConfiguration: connectionConfiguration, - }, - } - ); - }; - - const resetConnection = useCallback( - async (connectionId: string) => { - await resetConnectionResource({ connectionId }); - }, - [resetConnectionResource] - ); - - const syncConnection = async (connection: Connection) => { - const frequency = FrequencyConfig.find((item) => - equal(item.config, connection.schedule) - ); - - analyticsService.track("Source - Action", { - action: "Full refresh sync", - connector_source: connection.source?.sourceName, - connector_source_id: connection.source?.sourceDefinitionId, - connector_destination: connection.destination?.name, - connector_destination_definition_id: - connection.destination?.destinationDefinitionId, - frequency: frequency?.text, - }); - await syncConnectionResource({ - connectionId: connection.connectionId, - }); - }; - return { createConnection, updateConnection, - updateStateConnection, - resetConnection, deleteConnection, - syncConnection, }; }; diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/AllConnectionsPage/AllConnectionsPage.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/AllConnectionsPage/AllConnectionsPage.tsx index 278e1cdc4f9ae..30d77a75723ab 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/AllConnectionsPage/AllConnectionsPage.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/AllConnectionsPage/AllConnectionsPage.tsx @@ -1,23 +1,18 @@ import React, { Suspense } from "react"; import { FormattedMessage } from "react-intl"; -import { useResource } from "rest-hooks"; -import { Button, MainPageWithScroll, PageTitle, LoadingPage } from "components"; -import ConnectionResource from "core/resources/Connection"; +import { Button, LoadingPage, MainPageWithScroll, PageTitle } from "components"; import ConnectionsTable from "./components/ConnectionsTable"; import useRouter from "hooks/useRouter"; import HeadTitle from "components/HeadTitle"; import Placeholder, { ResourceTypes } from "components/Placeholder"; -import useWorkspace from "hooks/services/useWorkspace"; import { RoutePaths } from "../../../routePaths"; +import { useConnectionList } from "hooks/services/useConnectionHook"; const AllConnectionsPage: React.FC = () => { const { push } = useRouter(); - const { workspace } = useWorkspace(); - const { connections } = useResource(ConnectionResource.listShape(), { - workspaceId: workspace.workspaceId, - }); + const { connections } = useConnectionList(); const onClick = () => push(`${RoutePaths.ConnectionNew}`); diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/ReplicationView.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/ReplicationView.tsx index e8679a7eab3a6..df5e799c21e38 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/ReplicationView.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/ReplicationView.tsx @@ -8,6 +8,7 @@ import { useAsyncFn } from "react-use"; import { Button, Card } from "components"; import useConnection, { useConnectionLoad, + useResetConnection, ValuesProps, } from "hooks/services/useConnectionHook"; import ConnectionForm from "views/Connection/ConnectionForm"; @@ -62,7 +63,8 @@ const ReplicationView: React.FC = ({ syncCatalog: { streams: [] }, }); - const { updateConnection, resetConnection } = useConnection(); + const { updateConnection } = useConnection(); + const { mutateAsync: resetConnection } = useResetConnection(); const onReset = () => resetConnection(connectionId); diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/StatusView.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/StatusView.tsx index 428b8e60da238..9ded960c2a98c 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/StatusView.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/StatusView.tsx @@ -13,7 +13,10 @@ import { Connection } from "core/resources/Connection"; import JobsList from "./JobsList"; import EmptyResource from "components/EmptyResourceBlock"; import ResetDataModal from "components/ResetDataModal"; -import useConnection from "hooks/services/useConnectionHook"; +import { + useResetConnection, + useSyncConnection, +} from "hooks/services/useConnectionHook"; import useLoadingState from "hooks/useLoadingState"; import SourceDefinitionResource from "core/resources/SourceDefinition"; import DestinationDefinitionResource from "core/resources/DestinationDefinition"; @@ -78,7 +81,8 @@ const StatusView: React.FC = ({ connection, frequencyText }) => { configTypes: ["sync", "reset_connection"], }); - const { resetConnection, syncConnection } = useConnection(); + const { mutateAsync: resetConnection } = useResetConnection(); + const { mutateAsync: syncConnection } = useSyncConnection(); const onSync = () => syncConnection(connection); const onReset = () => resetConnection(connection.connectionId); diff --git a/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/DestinationItemPage.tsx b/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/DestinationItemPage.tsx index 79d4b0421f182..92b003b267972 100644 --- a/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/DestinationItemPage.tsx +++ b/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/DestinationItemPage.tsx @@ -5,7 +5,6 @@ import { useResource } from "rest-hooks"; import PageTitle from "components/PageTitle"; import useRouter from "hooks/useRouter"; import Placeholder, { ResourceTypes } from "components/Placeholder"; -import ConnectionResource from "core/resources/Connection"; import Breadcrumbs from "components/Breadcrumbs"; import DestinationConnectionTable from "./components/DestinationConnectionTable"; import DestinationResource from "core/resources/Destination"; @@ -26,6 +25,7 @@ import HeadTitle from "components/HeadTitle"; import useWorkspace from "hooks/services/useWorkspace"; import { DropDownRow } from "components"; import { RoutePaths } from "../../../routePaths"; +import { useConnectionList } from "hooks/services/useConnectionHook"; const DestinationItemPage: React.FC = () => { const { params, push } = useRouter(); @@ -55,9 +55,7 @@ const DestinationItemPage: React.FC = () => { } ); - const { connections } = useResource(ConnectionResource.listShape(), { - workspaceId: workspace.workspaceId, - }); + const { connections } = useConnectionList(); const onClickBack = () => push(".."); diff --git a/airbyte-webapp/src/pages/OnboardingPage/OnboardingPage.tsx b/airbyte-webapp/src/pages/OnboardingPage/OnboardingPage.tsx index 8a25e0769fc32..6f7fb372f4976 100644 --- a/airbyte-webapp/src/pages/OnboardingPage/OnboardingPage.tsx +++ b/airbyte-webapp/src/pages/OnboardingPage/OnboardingPage.tsx @@ -9,8 +9,9 @@ import useSource, { useSourceList } from "hooks/services/useSourceHook"; import useDestination, { useDestinationList, } from "hooks/services/useDestinationHook"; -import useConnection, { +import { useConnectionList, + useSyncConnection, } from "hooks/services/useConnectionHook"; import { ConnectionConfiguration } from "core/domain/connection"; import SourceDefinitionResource from "core/resources/SourceDefinition"; @@ -28,7 +29,7 @@ import StepsCounter from "./components/StepsCounter"; import LoadingPage from "components/LoadingPage"; import useWorkspace from "hooks/services/useWorkspace"; import useRouterHook from "hooks/useRouter"; -import { JobInfo } from "../../core/domain/job/Job"; +import { JobInfo } from "core/domain/job/Job"; import { RoutePaths } from "../routePaths"; const Content = styled.div<{ big?: boolean; medium?: boolean }>` @@ -71,7 +72,7 @@ const OnboardingPage: React.FC = () => { const { sources } = useSourceList(); const { destinations } = useDestinationList(); const { connections } = useConnectionList(); - const { syncConnection } = useConnection(); + const { mutateAsync: syncConnection } = useSyncConnection(); const { sourceDefinitions } = useResource( SourceDefinitionResource.listShape(), {} diff --git a/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/SourceItemPage.tsx b/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/SourceItemPage.tsx index 09b6a18382a71..5d2cb7b982411 100644 --- a/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/SourceItemPage.tsx +++ b/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/SourceItemPage.tsx @@ -16,8 +16,6 @@ import MainPageWithScroll from "components/MainPageWithScroll"; import SourceConnectionTable from "./components/SourceConnectionTable"; import SourceSettings from "./components/SourceSettings"; - -import ConnectionResource from "core/resources/Connection"; import SourceResource from "core/resources/Source"; import DestinationResource from "core/resources/Destination"; @@ -28,6 +26,7 @@ import HeadTitle from "components/HeadTitle"; import Placeholder, { ResourceTypes } from "components/Placeholder"; import useWorkspace from "hooks/services/useWorkspace"; import { RoutePaths } from "../../../routePaths"; +import { useConnectionList } from "hooks/services/useConnectionHook"; const SourceItemPage: React.FC = () => { const { query, push } = useRouter<{ id: string }>(); @@ -54,9 +53,7 @@ const SourceItemPage: React.FC = () => { sourceDefinitionId: source.sourceDefinitionId, }); - const { connections } = useResource(ConnectionResource.listShape(), { - workspaceId: workspace.workspaceId, - }); + const { connections } = useConnectionList(); const breadcrumbsData = [ { From e4afe50726a97008e5bee4b5528049a2f9878ea3 Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Tue, 29 Mar 2022 19:54:03 +0300 Subject: [PATCH 02/20] Refactor connections store to react-query --- .../CreateConnectionContent.tsx | 7 +- .../components/DeleteBlock/DeleteBlock.tsx | 2 +- .../DeleteBlock/components/DeleteModal.tsx | 2 +- .../src/components/EntityTable/hooks.tsx | 7 +- .../src/components/EntityTable/utils.tsx | 2 +- .../domain/connection/ConnectionService.ts | 13 + .../connection/WebBackendConnectionService.ts | 27 +- .../src/core/resources/Connection.ts | 122 +--------- .../src/hooks/services/useConnectionHook.tsx | 230 +++++++++--------- .../src/hooks/services/useDestinationHook.tsx | 24 +- .../src/hooks/services/useSourceHook.tsx | 22 +- .../components/ConnectionsTable.tsx | 2 +- .../ConnectionItemPage/ConnectionItemPage.tsx | 8 +- .../components/EnabledControl.tsx | 6 +- .../components/ReplicationView.tsx | 5 +- .../components/SettingsView.tsx | 6 +- .../components/StatusMainInfo.tsx | 2 +- .../components/StatusView.tsx | 2 +- .../components/TransformationView.tsx | 4 +- .../components/DestinationConnectionTable.tsx | 2 +- .../components/DestinationSettings.tsx | 2 +- .../OnboardingPage/components/FinalStep.tsx | 15 +- .../components/SourceConnectionTable.tsx | 2 +- .../components/SourceSettings.tsx | 2 +- 24 files changed, 229 insertions(+), 287 deletions(-) diff --git a/airbyte-webapp/src/components/CreateConnectionContent/CreateConnectionContent.tsx b/airbyte-webapp/src/components/CreateConnectionContent/CreateConnectionContent.tsx index 02d8922defda5..88020049185ff 100644 --- a/airbyte-webapp/src/components/CreateConnectionContent/CreateConnectionContent.tsx +++ b/airbyte-webapp/src/components/CreateConnectionContent/CreateConnectionContent.tsx @@ -10,7 +10,10 @@ import JobItem from "components/JobItem"; import ConnectionForm from "views/Connection/ConnectionForm"; import TryAfterErrorBlock from "./components/TryAfterErrorBlock"; -import useConnection, { ValuesProps } from "hooks/services/useConnectionHook"; +import { + useCreateConnection, + ValuesProps, +} from "hooks/services/useConnectionHook"; import { useDiscoverSchema } from "hooks/services/useSchemaHook"; import { IDataItem } from "components/base/DropDown/components/Option"; import { useAnalyticsService } from "hooks/services/Analytics/useAnalyticsService"; @@ -47,7 +50,7 @@ const CreateConnectionContent: React.FC = ({ additionBottomControls, noTitles, }) => { - const { createConnection } = useConnection(); + const { mutateAsync: createConnection } = useCreateConnection(); const analyticsService = useAnalyticsService(); const { diff --git a/airbyte-webapp/src/components/DeleteBlock/DeleteBlock.tsx b/airbyte-webapp/src/components/DeleteBlock/DeleteBlock.tsx index 5aee42794814b..73dcf47ffe386 100644 --- a/airbyte-webapp/src/components/DeleteBlock/DeleteBlock.tsx +++ b/airbyte-webapp/src/components/DeleteBlock/DeleteBlock.tsx @@ -8,7 +8,7 @@ import DeleteModal from "./components/DeleteModal"; type IProps = { type: "source" | "destination" | "connection"; - onDelete: () => Promise; + onDelete: () => Promise; }; const DeleteBlockComponent = styled(ContentCard)` diff --git a/airbyte-webapp/src/components/DeleteBlock/components/DeleteModal.tsx b/airbyte-webapp/src/components/DeleteBlock/components/DeleteModal.tsx index a709a2ed4b4c6..860e747be35fb 100644 --- a/airbyte-webapp/src/components/DeleteBlock/components/DeleteModal.tsx +++ b/airbyte-webapp/src/components/DeleteBlock/components/DeleteModal.tsx @@ -7,7 +7,7 @@ import { Button, LoadingButton } from "components"; import { useMutation } from "react-query"; export type IProps = { onClose: () => void; - onSubmit: () => Promise; + onSubmit: () => Promise; type: "source" | "destination" | "connection"; }; diff --git a/airbyte-webapp/src/components/EntityTable/hooks.tsx b/airbyte-webapp/src/components/EntityTable/hooks.tsx index affb350f05354..8274ef7ffea45 100644 --- a/airbyte-webapp/src/components/EntityTable/hooks.tsx +++ b/airbyte-webapp/src/components/EntityTable/hooks.tsx @@ -1,7 +1,8 @@ import FrequencyConfig from "config/FrequencyConfig.json"; -import { Connection } from "core/resources/Connection"; -import useConnection, { +import { Connection } from "core/domain/connection"; +import { useSyncConnection, + useUpdateConnection, } from "hooks/services/useConnectionHook"; import { Status } from "./types"; import { useAnalyticsService } from "hooks/services/Analytics/useAnalyticsService"; @@ -10,7 +11,7 @@ const useSyncActions = (): { changeStatus: (connection: Connection) => Promise; syncManualConnection: (connection: Connection) => Promise; } => { - const { updateConnection } = useConnection(); + const { mutateAsync: updateConnection } = useUpdateConnection(); const { mutateAsync: syncConnection } = useSyncConnection(); const analyticsService = useAnalyticsService(); diff --git a/airbyte-webapp/src/components/EntityTable/utils.tsx b/airbyte-webapp/src/components/EntityTable/utils.tsx index 9ef1707a1f8c6..31cfa9e3951af 100644 --- a/airbyte-webapp/src/components/EntityTable/utils.tsx +++ b/airbyte-webapp/src/components/EntityTable/utils.tsx @@ -1,4 +1,4 @@ -import { Connection } from "core/resources/Connection"; +import { Connection } from "core/domain/connection"; import Status from "core/statuses"; import { ITableDataItem, diff --git a/airbyte-webapp/src/core/domain/connection/ConnectionService.ts b/airbyte-webapp/src/core/domain/connection/ConnectionService.ts index 12d9774a500e8..6066e686819e1 100644 --- a/airbyte-webapp/src/core/domain/connection/ConnectionService.ts +++ b/airbyte-webapp/src/core/domain/connection/ConnectionService.ts @@ -1,5 +1,6 @@ import { AirbyteRequestService } from "core/request/AirbyteRequestService"; import { Connection } from "./types"; +import { CommonRequestError } from "../../request/CommonRequestError"; class ConnectionService extends AirbyteRequestService { get url() { @@ -21,6 +22,18 @@ class ConnectionService extends AirbyteRequestService { return rs; } + + public async delete(connectionId: string): Promise { + const result = await this.fetch(`${this.url}/delete`, { + connectionId, + }); + + if (result.status === "failure") { + throw new CommonRequestError(result, result.message); + } + + return result; + } } export { ConnectionService }; diff --git a/airbyte-webapp/src/core/domain/connection/WebBackendConnectionService.ts b/airbyte-webapp/src/core/domain/connection/WebBackendConnectionService.ts index 4cb7df32edfde..041e1d9ddb1cd 100644 --- a/airbyte-webapp/src/core/domain/connection/WebBackendConnectionService.ts +++ b/airbyte-webapp/src/core/domain/connection/WebBackendConnectionService.ts @@ -1,5 +1,6 @@ import { AirbyteRequestService } from "core/request/AirbyteRequestService"; import { Connection } from "./types"; +import { CommonRequestError } from "../../request/CommonRequestError"; class WebBackendConnectionService extends AirbyteRequestService { get url() { @@ -16,11 +17,33 @@ class WebBackendConnectionService extends AirbyteRequestService { }); } - public async list(workspaceId: string): Promise { - return await this.fetch(`${this.url}/list`, { + public async list( + workspaceId: string + ): Promise<{ connections: Connection[] }> { + return await this.fetch<{ connections: Connection[] }>(`${this.url}/list`, { workspaceId, }); } + + public async update(payload: Record): Promise { + const result = await this.fetch(`${this.url}/update`, payload); + + if (result.status === "failure") { + throw new CommonRequestError(result, result.message); + } + + return result; + } + + public async create(payload: Record): Promise { + const result = await this.fetch(`${this.url}/create`, payload); + + if (result.status === "failure") { + throw new CommonRequestError(result, result.message); + } + + return result; + } } export { WebBackendConnectionService }; diff --git a/airbyte-webapp/src/core/resources/Connection.ts b/airbyte-webapp/src/core/resources/Connection.ts index 353d04c42d81c..c6c8f6a39bb5a 100644 --- a/airbyte-webapp/src/core/resources/Connection.ts +++ b/airbyte-webapp/src/core/resources/Connection.ts @@ -1,21 +1,13 @@ -import { - FetchOptions, - FetchShape, - MutateShape, - ReadShape, - Resource, - SchemaDetail, -} from "rest-hooks"; +import { FetchShape, Resource, SchemaDetail } from "rest-hooks"; import { SyncSchema } from "core/domain/catalog"; -import { CommonRequestError } from "core/request/CommonRequestError"; import BaseResource from "./BaseResource"; import { - ConnectionNamespaceDefinition, Connection, - ScheduleProperties, + ConnectionNamespaceDefinition, Operation, + ScheduleProperties, } from "core/domain/connection"; import { Destination, Source } from "core/domain/connector"; @@ -50,114 +42,6 @@ export default class ConnectionResource static urlRoot = "connections"; - static getFetchOptions(): FetchOptions { - return { - pollFrequency: 2500, // every 2,5 seconds - }; - } - - static detailShape( - this: T - ): ReadShape> { - return { - ...super.detailShape(), - fetch: async ( - params: Readonly> - ): Promise => - await this.fetch( - "post", - `${super.rootUrl()}web_backend/connections/get`, - params - ), - schema: this, - }; - } - - static updateShape( - this: T - ): MutateShape> { - return { - ...super.partialUpdateShape(), - fetch: async ( - _: Readonly>, - body: Record - ): Promise => { - const result = await this.fetch( - "post", - `${super.rootUrl()}web_backend/connections/update`, - body - ); - - if (result.status === "failure") { - throw new CommonRequestError(result, result.message); - } - - return result; - }, - schema: this, - }; - } - - static deleteShapeItem( - this: T - ): MutateShape> { - return { - ...super.deleteShape(), - fetch: async ( - _: Readonly>, - body: Readonly> - ): Promise => { - const result = await this.fetch( - "post", - `${super.rootUrl()}connections/delete`, - body - ); - - if (result.status === "failure") { - throw new CommonRequestError(result, result.message); - } - - return result; - }, - schema: this, - }; - } - - static createShape( - this: T - ): MutateShape> { - return { - ...super.createShape(), - schema: this, - fetch: async ( - _: Readonly>, - body: Readonly> - ): Promise => - await this.fetch( - "post", - `${super.rootUrl()}web_backend/connections/create`, - body - ), - }; - } - - static listShape( - this: T - ): ReadShape> { - return { - ...super.listShape(), - fetch: async ( - params: Readonly> - ): Promise<{ connections: Connection[] }> => - await this.fetch( - "post", - `${super.rootUrl()}web_backend/connections/list`, - params - ), - schema: { connections: [this] }, - }; - } - static updateStoreAfterDeleteShape( this: T ): FetchShape> { diff --git a/airbyte-webapp/src/hooks/services/useConnectionHook.tsx b/airbyte-webapp/src/hooks/services/useConnectionHook.tsx index 06c007cb765f8..4a5c746a3a7b4 100644 --- a/airbyte-webapp/src/hooks/services/useConnectionHook.tsx +++ b/airbyte-webapp/src/hooks/services/useConnectionHook.tsx @@ -1,5 +1,9 @@ -import { useFetcher, useResource } from "rest-hooks"; -import { useMutation } from "react-query"; +import { + QueryObserverSuccessResult, + useMutation, + useQuery, + useQueryClient, +} from "react-query"; import FrequencyConfig from "config/FrequencyConfig.json"; import { useConfig } from "config"; @@ -9,22 +13,26 @@ import { WebBackendConnectionService, } from "core/domain/connection"; -import ConnectionResource, { - ScheduleProperties, -} from "core/resources/Connection"; +import { ScheduleProperties } from "core/resources/Connection"; import { SyncSchema } from "core/domain/catalog"; import useWorkspace from "./useWorkspace"; import { Operation } from "core/domain/connection/operation"; import { useAnalyticsService } from "hooks/services/Analytics/useAnalyticsService"; -import useRouter from "hooks/useRouter"; import { equal } from "utils/objects"; import { Destination, Source, SourceDefinition } from "core/domain/connector"; -import { RoutePaths } from "pages/routePaths"; import { useDefaultRequestMiddlewares } from "services/useDefaultRequestMiddlewares"; import { useInitService } from "services/useInitService"; import { ConnectionService } from "core/domain/connection/ConnectionService"; +export const connectionsKeys = { + all: ["connections"] as const, + lists: () => [...connectionsKeys.all, "list"] as const, + list: (filters: string) => [...connectionsKeys.lists(), { filters }] as const, + detail: (connectionId: string) => + [...connectionsKeys.all, "details", connectionId] as const, +}; + export type ValuesProps = { schedule: ScheduleProperties | null; prefix: string; @@ -56,6 +64,8 @@ type UpdateConnection = { withRefreshedCatalog?: boolean; }; +export type ListConnection = { connections: Connection[] }; + function useWebConnectionService(): WebBackendConnectionService { const config = useConfig(); const middlewares = useDefaultRequestMiddlewares(); @@ -84,9 +94,7 @@ export const useConnectionLoad = ( connection: Connection; refreshConnectionCatalog: () => Promise; } => { - const connection = useResource(ConnectionResource.detailShape(), { - connectionId, - }); + const connection = useGetConnection(connectionId); const connectionService = useWebConnectionService(); @@ -128,53 +136,40 @@ export const useResetConnection = () => { return useMutation((connectionId: string) => service.reset(connectionId)); }; -const useConnection = (): { - createConnection: (conn: CreateConnectionProps) => Promise; - updateConnection: (conn: UpdateConnection) => Promise; - deleteConnection: (payload: { connectionId: string }) => Promise; -} => { - const { push } = useRouter(); - const { workspace } = useWorkspace(); - const analyticsService = useAnalyticsService(); +const useGetConnection = ( + connectionId: string, + options?: { refetchInterval: number } +): Connection => { + const service = useWebConnectionService(); + + return (useQuery( + connectionsKeys.detail(connectionId), + () => service.getConnection(connectionId), + options + ) as QueryObserverSuccessResult).data; +}; - const createConnectionResource = useFetcher(ConnectionResource.createShape()); - const updateConnectionResource = useFetcher(ConnectionResource.updateShape()); - const deleteConnectionResource = useFetcher( - ConnectionResource.deleteShapeItem() - ); +const useCreateConnection = () => { + const service = useWebConnectionService(); + const queryClient = useQueryClient(); - const createConnection = async ({ - values, - source, - destination, - sourceDefinition, - destinationDefinition, - }: CreateConnectionProps) => { - try { - const result = await createConnectionResource( - {}, - { - sourceId: source?.sourceId, - destinationId: destination?.destinationId, - ...values, - status: "active", - }, - [ - [ - ConnectionResource.listShape(), - { workspaceId: workspace.workspaceId }, - ( - newConnectionId: string, - connectionsIds: { connections: string[] } - ) => ({ - connections: [ - ...(connectionsIds?.connections || []), - newConnectionId, - ], - }), - ], - ] - ); + const analyticsService = useAnalyticsService(); + + return useMutation( + async (conn: CreateConnectionProps) => { + const { + values, + source, + destination, + sourceDefinition, + destinationDefinition, + } = conn; + const response = await service.create({ + sourceId: source?.sourceId, + destinationId: destination?.destinationId, + ...values, + status: "active", + }); const frequencyData = FrequencyConfig.find((item) => equal(item.config, values.schedule) @@ -190,69 +185,78 @@ const useConnection = (): { destinationDefinition?.destinationDefinitionId, }); - return result; - } catch (e) { - throw e; + return response; + }, + { + onSuccess: (data) => { + queryClient.setQueryData( + connectionsKeys.lists(), + (lst: ListConnection | undefined) => ({ + connections: [data, ...(lst?.connections ?? [])], + }) + ); + }, } - }; - - const updateConnectionsStore = useFetcher(ConnectionResource.listShape()); - - const deleteConnection = async ({ - connectionId, - }: { - connectionId: string; - }) => { - await deleteConnectionResource({}, { connectionId }, [ - [ - ConnectionResource.listShape(), - { workspaceId: workspace.workspaceId }, - (cId: string, connectionsIds: { connections: string[] }) => { - const res = connectionsIds?.connections || []; - const index = res.findIndex((c) => c === cId); - - return { - connections: [...res.slice(0, index), ...res.slice(index + 1)], - }; - }, - ], - ]); - - await updateConnectionsStore({ workspaceId: workspace.workspaceId }); - - push(RoutePaths.Connections); - }; + ); +}; - const updateConnection = async ({ - withRefreshedCatalog, - ...formValues - }: UpdateConnection) => { - const withRefreshedCatalogCleaned = withRefreshedCatalog - ? { withRefreshedCatalog } - : null; - - return await updateConnectionResource( - {}, - { - ...formValues, - ...withRefreshedCatalogCleaned, - } - ); - }; +const useDeleteConnection = () => { + const service = useConnectionService(); + const queryClient = useQueryClient(); + + return useMutation((connectionId: string) => service.delete(connectionId), { + onSuccess: (_data, connectionId) => { + queryClient.removeQueries(connectionsKeys.detail(connectionId)); + queryClient.setQueryData( + connectionsKeys.lists(), + (lst: ListConnection | undefined) => + ({ + connections: + lst?.connections.filter( + (conn) => conn.connectionId !== connectionId + ) ?? [], + } as ListConnection) + ); + }, + }); +}; - return { - createConnection, - updateConnection, - deleteConnection, - }; +const useUpdateConnection = () => { + const service = useWebConnectionService(); + const queryClient = useQueryClient(); + + return useMutation( + (conn: UpdateConnection) => { + const withRefreshedCatalogCleaned = conn.withRefreshedCatalog + ? { withRefreshedCatalog: conn.withRefreshedCatalog } + : null; + + return service.update({ ...conn, ...withRefreshedCatalogCleaned }); + }, + { + onSuccess: (data) => { + queryClient.setQueryData( + connectionsKeys.detail(data.connectionId), + data + ); + }, + } + ); }; -const useConnectionList = (): { connections: Connection[] } => { +const useConnectionList = (): ListConnection => { const { workspace } = useWorkspace(); - return useResource(ConnectionResource.listShape(), { - workspaceId: workspace.workspaceId, - }); + const service = useWebConnectionService(); + + return (useQuery(connectionsKeys.lists(), () => + service.list(workspace.workspaceId) + ) as QueryObserverSuccessResult<{ connections: Connection[] }>).data; }; -export { useConnectionList }; -export default useConnection; +export { + useConnectionList, + useGetConnection, + useUpdateConnection, + useCreateConnection, + useDeleteConnection, +}; diff --git a/airbyte-webapp/src/hooks/services/useDestinationHook.tsx b/airbyte-webapp/src/hooks/services/useDestinationHook.tsx index ea96e1a28efa2..614289851babc 100644 --- a/airbyte-webapp/src/hooks/services/useDestinationHook.tsx +++ b/airbyte-webapp/src/hooks/services/useDestinationHook.tsx @@ -1,8 +1,9 @@ import { useCallback } from "react"; import { useFetcher, useResource } from "rest-hooks"; +import { useQueryClient } from "react-query"; import DestinationResource from "core/resources/Destination"; -import ConnectionResource, { Connection } from "core/resources/Connection"; +import { Connection } from "core/domain/connection"; import useRouter from "../useRouter"; import SchedulerResource, { Scheduler } from "core/resources/Scheduler"; import { ConnectionConfiguration } from "core/domain/connection"; @@ -10,6 +11,7 @@ import useWorkspace from "./useWorkspace"; import { useAnalyticsService } from "hooks/services/Analytics/useAnalyticsService"; import { Destination } from "core/domain/connector"; import { RoutePaths } from "../../pages/routePaths"; +import { connectionsKeys, ListConnection } from "./useConnectionHook"; type ValuesProps = { name: string; @@ -68,9 +70,7 @@ const useDestination = (): DestinationService => { const destinationDelete = useFetcher(DestinationResource.deleteShape()); - const updateConnectionsStore = useFetcher( - ConnectionResource.updateStoreAfterDeleteShape() - ); + const queryClient = useQueryClient(); const createDestination = async ({ values, @@ -196,9 +196,19 @@ const useDestination = (): DestinationService => { destinationId: destination.destinationId, }); - // To delete connections with current source from local store - connectionsWithDestination.map((item) => - updateConnectionsStore({ connectionId: item.connectionId }, undefined) + // To delete connections with current destination from local store + const connectionIds = connectionsWithDestination.map( + (item) => item.connectionId + ); + + queryClient.setQueryData( + connectionsKeys.lists(), + (ls: ListConnection | undefined) => ({ + connections: + ls?.connections.filter((c) => + connectionIds.includes(c.connectionId) + ) ?? [], + }) ); push(RoutePaths.Destination); diff --git a/airbyte-webapp/src/hooks/services/useSourceHook.tsx b/airbyte-webapp/src/hooks/services/useSourceHook.tsx index c6a21cb94dedb..abf10d28131d3 100644 --- a/airbyte-webapp/src/hooks/services/useSourceHook.tsx +++ b/airbyte-webapp/src/hooks/services/useSourceHook.tsx @@ -1,8 +1,9 @@ import { useCallback } from "react"; import { useFetcher, useResource } from "rest-hooks"; +import { useQueryClient } from "react-query"; import SourceResource from "core/resources/Source"; -import ConnectionResource, { Connection } from "core/resources/Connection"; +import { Connection } from "core/domain/connection"; import SchedulerResource, { Scheduler } from "core/resources/Scheduler"; import { ConnectionConfiguration } from "core/domain/connection"; import useWorkspace from "./useWorkspace"; @@ -11,6 +12,7 @@ import useRouter from "hooks/useRouter"; import { useAnalyticsService } from "hooks/services/Analytics/useAnalyticsService"; import { Source } from "core/domain/connector"; import { RoutePaths } from "../../pages/routePaths"; +import { connectionsKeys, ListConnection } from "./useConnectionHook"; type ValuesProps = { name: string; @@ -54,9 +56,7 @@ const useSource = (): SourceService => { const sourceDelete = useFetcher(SourceResource.deleteShape()); - const updateConnectionsStore = useFetcher( - ConnectionResource.updateStoreAfterDeleteShape() - ); + const queryClient = useQueryClient(); const createSource: SourceService["createSource"] = async ({ values, @@ -169,8 +169,18 @@ const useSource = (): SourceService => { }); // To delete connections with current source from local store - connectionsWithSource.map((item) => - updateConnectionsStore({ connectionId: item.connectionId }, undefined) + const connectionIds = connectionsWithSource.map( + (item) => item.connectionId + ); + + queryClient.setQueryData( + connectionsKeys.lists(), + (ls: ListConnection | undefined) => ({ + connections: + ls?.connections.filter((c) => + connectionIds.includes(c.connectionId) + ) ?? [], + }) ); push(RoutePaths.Source); diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/AllConnectionsPage/components/ConnectionsTable.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/AllConnectionsPage/components/ConnectionsTable.tsx index e039934c3effe..a31e0421f2031 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/AllConnectionsPage/components/ConnectionsTable.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/AllConnectionsPage/components/ConnectionsTable.tsx @@ -2,7 +2,7 @@ import React, { useCallback } from "react"; import { ConnectionTable } from "components/EntityTable"; import useRouter from "hooks/useRouter"; -import { Connection } from "core/resources/Connection"; +import { Connection } from "core/domain/connection"; import useSyncActions from "components/EntityTable/hooks"; import { getConnectionTableData } from "components/EntityTable/utils"; import { ITableDataItem } from "components/EntityTable/types"; diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/ConnectionItemPage.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/ConnectionItemPage.tsx index 39786c3f3398e..749fc609e1376 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/ConnectionItemPage.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/ConnectionItemPage.tsx @@ -1,5 +1,4 @@ import React, { Suspense } from "react"; -import { useResource } from "rest-hooks"; import { Navigate, Route, Routes, useParams } from "react-router-dom"; import { LoadingPage, MainPageWithScroll } from "components"; @@ -8,8 +7,6 @@ import HeadTitle from "components/HeadTitle"; import { useAnalyticsService } from "hooks/services/Analytics/useAnalyticsService"; import FrequencyConfig from "config/FrequencyConfig.json"; - -import ConnectionResource from "core/resources/Connection"; import { equal } from "utils/objects"; import ReplicationView from "./components/ReplicationView"; @@ -18,6 +15,7 @@ import TransformationView from "pages/ConnectionPage/pages/ConnectionItemPage/co import SettingsView from "./components/SettingsView"; import ConnectionPageTitle from "./components/ConnectionPageTitle"; import { ConnectionSettingsRoutes } from "./ConnectionSettingsRoutes"; +import { useGetConnection } from "hooks/services/useConnectionHook"; const ConnectionItemPage: React.FC = () => { const params = useParams<{ @@ -26,9 +24,7 @@ const ConnectionItemPage: React.FC = () => { }>(); const connectionId = params.connectionId || ""; const currentStep = params["*"] || ConnectionSettingsRoutes.STATUS; - const connection = useResource(ConnectionResource.detailShape(), { - connectionId, - }); + const connection = useGetConnection(connectionId); const { source, destination } = connection; 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 95dceeb58f08f..b033437c98a29 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/EnabledControl.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/EnabledControl.tsx @@ -3,8 +3,8 @@ import { FormattedMessage } from "react-intl"; import styled from "styled-components"; import { Toggle } from "components"; -import { Connection } from "core/resources/Connection"; -import useConnection from "hooks/services/useConnectionHook"; +import { Connection } from "core/domain/connection"; +import { useUpdateConnection } from "hooks/services/useConnectionHook"; import { Status } from "components/EntityTable/types"; import { useAnalyticsService } from "hooks/services/Analytics/useAnalyticsService"; @@ -36,7 +36,7 @@ const EnabledControl: React.FC = ({ disabled, frequencyText, }) => { - const { updateConnection } = useConnection(); + const { mutateAsync: updateConnection } = useUpdateConnection(); const analyticsService = useAnalyticsService(); const onChangeStatus = async () => { diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/ReplicationView.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/ReplicationView.tsx index df5e799c21e38..0b3e8999e04d6 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/ReplicationView.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/ReplicationView.tsx @@ -6,9 +6,10 @@ import { faSyncAlt } from "@fortawesome/free-solid-svg-icons"; import { useAsyncFn } from "react-use"; import { Button, Card } from "components"; -import useConnection, { +import { useConnectionLoad, useResetConnection, + useUpdateConnection, ValuesProps, } from "hooks/services/useConnectionHook"; import ConnectionForm from "views/Connection/ConnectionForm"; @@ -63,7 +64,7 @@ const ReplicationView: React.FC = ({ syncCatalog: { streams: [] }, }); - const { updateConnection } = useConnection(); + const { mutateAsync: updateConnection } = useUpdateConnection(); const { mutateAsync: resetConnection } = useResetConnection(); const onReset = () => resetConnection(connectionId); 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 a632c7e3f6a2b..25be2edb11873 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/SettingsView.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/SettingsView.tsx @@ -1,7 +1,7 @@ import React from "react"; import styled from "styled-components"; -import useConnection from "hooks/services/useConnectionHook"; +import { useDeleteConnection } from "hooks/services/useConnectionHook"; import DeleteBlock from "components/DeleteBlock"; type IProps = { @@ -15,9 +15,9 @@ const Content = styled.div` `; const SettingsView: React.FC = ({ connectionId }) => { - const { deleteConnection } = useConnection(); + const { mutateAsync: deleteConnection } = useDeleteConnection(); - const onDelete = () => deleteConnection({ connectionId }); + const onDelete = () => deleteConnection(connectionId); return ( diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/StatusMainInfo.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/StatusMainInfo.tsx index 2df6e12d82d0f..ed121211f823b 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/StatusMainInfo.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/StatusMainInfo.tsx @@ -7,7 +7,7 @@ import { Header, Row, Cell } from "components/SimpleTableComponents"; import EnabledControl from "./EnabledControl"; import { DestinationDefinition, SourceDefinition } from "core/domain/connector"; -import { Connection } from "core/resources/Connection"; +import { Connection } from "core/domain/connection"; import { ReleaseStageBadge } from "components/ReleaseStageBadge"; const MainInfo = styled(ContentCard)` diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/StatusView.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/StatusView.tsx index 9ded960c2a98c..3ebd1096ca2c0 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/StatusView.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/StatusView.tsx @@ -9,7 +9,7 @@ import { useListJobs } from "services/job/JobService"; import { Button, ContentCard, LoadingButton } from "components"; import StatusMainInfo from "./StatusMainInfo"; -import { Connection } from "core/resources/Connection"; +import { Connection } from "core/domain/connection"; import JobsList from "./JobsList"; import EmptyResource from "components/EmptyResourceBlock"; import ResetDataModal from "components/ResetDataModal"; diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/TransformationView.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/TransformationView.tsx index 90574e4b5fdf3..f5b41c233473c 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/TransformationView.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/TransformationView.tsx @@ -19,7 +19,7 @@ import { OperatorType, Transformation, } from "core/domain/connection"; -import useConnection from "hooks/services/useConnectionHook"; +import { useUpdateConnection } from "hooks/services/useConnectionHook"; import { useCurrentWorkspace } from "hooks/services/useWorkspace"; import { ContentCard, H4 } from "components"; import { FeatureItem, useFeatureService } from "hooks/services/Feature"; @@ -110,7 +110,7 @@ const TransformationView: React.FC = ({ const definition = useGetDestinationDefinitionSpecification( connection.destination.destinationDefinitionId ); - const { updateConnection } = useConnection(); + const { mutateAsync: updateConnection } = useUpdateConnection(); const workspace = useCurrentWorkspace(); const { hasFeature } = useFeatureService(); diff --git a/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationConnectionTable.tsx b/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationConnectionTable.tsx index a6ec48538382d..4a6a0c88a701a 100644 --- a/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationConnectionTable.tsx +++ b/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationConnectionTable.tsx @@ -3,7 +3,7 @@ import { useResource } from "rest-hooks"; import { ConnectionTable } from "components/EntityTable"; import useRouter from "hooks/useRouter"; -import { Connection } from "core/resources/Connection"; +import { Connection } from "core/domain/connection"; import useSyncActions from "components/EntityTable/hooks"; import { getConnectionTableData } from "components/EntityTable/utils"; import { ITableDataItem } from "components/EntityTable/types"; diff --git a/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationSettings.tsx b/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationSettings.tsx index 86c897b2b66b9..5c1031d9087d9 100644 --- a/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationSettings.tsx +++ b/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationSettings.tsx @@ -5,7 +5,7 @@ import { useResource } from "rest-hooks"; import DeleteBlock from "components/DeleteBlock"; import useDestination from "hooks/services/useDestinationHook"; -import { Connection } from "core/resources/Connection"; +import { Connection } from "core/domain/connection"; import { ConnectionConfiguration } from "core/domain/connection"; import DestinationDefinitionResource from "core/resources/DestinationDefinition"; diff --git a/airbyte-webapp/src/pages/OnboardingPage/components/FinalStep.tsx b/airbyte-webapp/src/pages/OnboardingPage/components/FinalStep.tsx index f2960ed571a8f..5e570aeb6d100 100644 --- a/airbyte-webapp/src/pages/OnboardingPage/components/FinalStep.tsx +++ b/airbyte-webapp/src/pages/OnboardingPage/components/FinalStep.tsx @@ -1,19 +1,19 @@ import React, { useEffect, useState } from "react"; import styled from "styled-components"; import { FormattedMessage } from "react-intl"; -import { useResource, useSubscription } from "rest-hooks"; + +import { H1 } from "components"; +import { useConfig } from "config"; import VideoItem from "./VideoItem"; import ProgressBlock from "./ProgressBlock"; import HighlightedText from "./HighlightedText"; -import { H1 } from "components/base"; import UseCaseBlock from "./UseCaseBlock"; -import ConnectionResource from "core/resources/Connection"; import SyncCompletedModal from "views/Feedback/SyncCompletedModal"; import { useOnboardingService } from "hooks/services/Onboarding/OnboardingService"; import Status from "core/statuses"; import useWorkspace from "hooks/services/useWorkspace"; -import { useConfig } from "config"; +import { useGetConnection } from "hooks/services/useConnectionHook"; type FinalStepProps = { connectionId: string; @@ -45,11 +45,8 @@ const FinalStep: React.FC = ({ connectionId, onSync }) => { useCaseLinks, skipCase, } = useOnboardingService(); - const connection = useResource(ConnectionResource.detailShape(), { - connectionId, - }); - useSubscription(ConnectionResource.detailShape(), { - connectionId: connectionId, + const connection = useGetConnection(connectionId, { + refetchInterval: 2500, }); const [isOpen, setIsOpen] = useState(false); diff --git a/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceConnectionTable.tsx b/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceConnectionTable.tsx index cf3701c565371..eb048dd1cdc74 100644 --- a/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceConnectionTable.tsx +++ b/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceConnectionTable.tsx @@ -3,7 +3,7 @@ import { useResource } from "rest-hooks"; import { ConnectionTable } from "components/EntityTable"; import useRouter from "hooks/useRouter"; -import { Connection } from "core/resources/Connection"; +import { Connection } from "core/domain/connection"; import useSyncActions from "components/EntityTable/hooks"; import { getConnectionTableData } from "components/EntityTable/utils"; import { ITableDataItem } from "components/EntityTable/types"; diff --git a/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceSettings.tsx b/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceSettings.tsx index 13ca7d7f17ac8..fc65dc2675d0d 100644 --- a/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceSettings.tsx +++ b/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceSettings.tsx @@ -5,7 +5,7 @@ import { useResource } from "rest-hooks"; import useSource from "hooks/services/useSourceHook"; import DeleteBlock from "components/DeleteBlock"; -import { Connection } from "core/resources/Connection"; +import { Connection } from "core/domain/connection"; import { createFormErrorMessage } from "utils/errorStatusMessage"; import { ConnectionConfiguration } from "core/domain/connection"; import SourceDefinitionResource from "core/resources/SourceDefinition"; From 579b5bb183368ff8befeec63f7468dd085936319 Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Tue, 29 Mar 2022 19:57:42 +0300 Subject: [PATCH 03/20] Fix imports --- .../EntityTable/components/FrequencyCell.tsx | 2 +- .../src/components/EntityTable/types.ts | 2 +- .../src/core/resources/Connection.ts | 53 ------------------- .../src/hooks/services/useConnectionHook.tsx | 6 +-- .../ConnectionForm/ConnectionForm.tsx | 7 ++- .../Connection/ConnectionForm/formConfig.tsx | 2 +- 6 files changed, 11 insertions(+), 61 deletions(-) delete mode 100644 airbyte-webapp/src/core/resources/Connection.ts diff --git a/airbyte-webapp/src/components/EntityTable/components/FrequencyCell.tsx b/airbyte-webapp/src/components/EntityTable/components/FrequencyCell.tsx index c0e73dc68d879..816edbc80afa0 100644 --- a/airbyte-webapp/src/components/EntityTable/components/FrequencyCell.tsx +++ b/airbyte-webapp/src/components/EntityTable/components/FrequencyCell.tsx @@ -2,8 +2,8 @@ import React from "react"; import styled from "styled-components"; import FrequencyConfig from "config/FrequencyConfig.json"; -import { ScheduleProperties } from "core/resources/Connection"; import { equal } from "utils/objects"; +import { ScheduleProperties } from "core/domain/connection"; type IProps = { value: ScheduleProperties; diff --git a/airbyte-webapp/src/components/EntityTable/types.ts b/airbyte-webapp/src/components/EntityTable/types.ts index 62ef9965f1e31..005ff7c6185e1 100644 --- a/airbyte-webapp/src/components/EntityTable/types.ts +++ b/airbyte-webapp/src/components/EntityTable/types.ts @@ -1,4 +1,4 @@ -import { ScheduleProperties } from "core/resources/Connection"; +import { ScheduleProperties } from "core/domain/connection"; type EntityTableDataItem = { entityId: string; diff --git a/airbyte-webapp/src/core/resources/Connection.ts b/airbyte-webapp/src/core/resources/Connection.ts deleted file mode 100644 index c6c8f6a39bb5a..0000000000000 --- a/airbyte-webapp/src/core/resources/Connection.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { FetchShape, Resource, SchemaDetail } from "rest-hooks"; - -import { SyncSchema } from "core/domain/catalog"; - -import BaseResource from "./BaseResource"; -import { - Connection, - ConnectionNamespaceDefinition, - Operation, - ScheduleProperties, -} from "core/domain/connection"; -import { Destination, Source } from "core/domain/connector"; - -export type { Connection, ScheduleProperties }; - -export default class ConnectionResource - extends BaseResource - implements Connection { - readonly connectionId: string = ""; - readonly name: string = ""; - readonly prefix: string = ""; - readonly sourceId: string = ""; - readonly destinationId: string = ""; - readonly status: string = ""; - readonly message: string = ""; - readonly namespaceFormat: string = ""; - readonly namespaceDefinition: ConnectionNamespaceDefinition = - ConnectionNamespaceDefinition.Source; - readonly schedule: ScheduleProperties | null = null; - readonly operations: Operation[] = []; - readonly source: Source = {} as Source; - readonly destination: Destination = {} as Destination; - readonly latestSyncJobCreatedAt: number | undefined | null = null; - readonly latestSyncJobStatus: string | null = null; - readonly syncCatalog: SyncSchema = { streams: [] }; - readonly isSyncing: boolean = false; - readonly operationIds: string[] = []; - - pk(): string { - return this.connectionId?.toString(); - } - - static urlRoot = "connections"; - - static updateStoreAfterDeleteShape( - this: T - ): FetchShape> { - return { - ...super.deleteShape(), - fetch: async (): Promise => null, - }; - } -} diff --git a/airbyte-webapp/src/hooks/services/useConnectionHook.tsx b/airbyte-webapp/src/hooks/services/useConnectionHook.tsx index 4a5c746a3a7b4..929b4d7b6de74 100644 --- a/airbyte-webapp/src/hooks/services/useConnectionHook.tsx +++ b/airbyte-webapp/src/hooks/services/useConnectionHook.tsx @@ -13,7 +13,7 @@ import { WebBackendConnectionService, } from "core/domain/connection"; -import { ScheduleProperties } from "core/resources/Connection"; +import { ScheduleProperties } from "core/domain/connection"; import { SyncSchema } from "core/domain/catalog"; import useWorkspace from "./useWorkspace"; import { Operation } from "core/domain/connection/operation"; @@ -73,7 +73,7 @@ function useWebConnectionService(): WebBackendConnectionService { return useInitService( () => new WebBackendConnectionService(config.apiUrl, middlewares), // eslint-disable-next-line react-hooks/exhaustive-deps - [config] + [config, middlewares] ); } @@ -84,7 +84,7 @@ function useConnectionService(): ConnectionService { return useInitService( () => new ConnectionService(config.apiUrl, middlewares), // eslint-disable-next-line react-hooks/exhaustive-deps - [config] + [config, middlewares] ); } diff --git a/airbyte-webapp/src/views/Connection/ConnectionForm/ConnectionForm.tsx b/airbyte-webapp/src/views/Connection/ConnectionForm/ConnectionForm.tsx index 44c65a6f67a31..78d598ab7c278 100644 --- a/airbyte-webapp/src/views/Connection/ConnectionForm/ConnectionForm.tsx +++ b/airbyte-webapp/src/views/Connection/ConnectionForm/ConnectionForm.tsx @@ -19,8 +19,11 @@ import { import useWorkspace from "hooks/services/useWorkspace"; import { createFormErrorMessage } from "utils/errorStatusMessage"; -import { Connection, ScheduleProperties } from "core/resources/Connection"; -import { ConnectionNamespaceDefinition } from "core/domain/connection"; +import { + Connection, + ConnectionNamespaceDefinition, + ScheduleProperties, +} from "core/domain/connection"; import { NamespaceDefinitionField } from "./components/NamespaceDefinitionField"; import CreateControls from "./components/CreateControls"; import SchemaField from "./components/SyncCatalogField"; diff --git a/airbyte-webapp/src/views/Connection/ConnectionForm/formConfig.tsx b/airbyte-webapp/src/views/Connection/ConnectionForm/formConfig.tsx index b31a2c25f6acf..d07e6a93cd54c 100644 --- a/airbyte-webapp/src/views/Connection/ConnectionForm/formConfig.tsx +++ b/airbyte-webapp/src/views/Connection/ConnectionForm/formConfig.tsx @@ -21,7 +21,7 @@ import { } from "core/domain/connection/operation"; import { DropDownRow } from "components"; import FrequencyConfig from "config/FrequencyConfig.json"; -import { Connection, ScheduleProperties } from "core/resources/Connection"; +import { Connection, ScheduleProperties } from "core/domain/connection"; import { ConnectionNamespaceDefinition, ConnectionSchedule, From 7e7a55f38de0271548f5c4ba9b66c16e469d9190 Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Tue, 29 Mar 2022 22:49:43 +0300 Subject: [PATCH 04/20] Refactor workspaces to react-query --- .../.storybook/restHooksDecorator.tsx | 12 +-- .../domain/connection/ConnectionService.ts | 2 +- .../connection/WebBackendConnectionService.ts | 2 +- .../connector/SourceDefinitionService.ts | 18 ++++ .../core/domain/workspace/WorkspaceService.ts | 24 +++++ .../src/core/domain/workspace/index.ts | 2 + .../core/resources/DestinationDefinition.ts | 9 ++ .../src/core/resources/Workspace.ts | 53 ----------- .../src/hooks/services/useConnectionHook.tsx | 7 +- .../services/useDestinationDefinition.tsx | 5 +- .../src/hooks/services/useDestinationHook.tsx | 6 +- .../hooks/services/useSourceDefinition.tsx | 5 +- .../src/hooks/services/useSourceHook.tsx | 8 +- .../src/hooks/services/useWorkspace.tsx | 94 ++++++++----------- .../components/EmailSection/EmailSection.tsx | 4 +- .../components/DestinationForm.tsx | 4 +- .../components/ExistingEntityForm.tsx | 5 +- .../components/SourceForm.tsx | 4 +- .../AllDestinationsPage.tsx | 4 +- .../CreateDestinationPage.tsx | 4 +- .../DestinationItemPage.tsx | 4 +- .../components/DestinationConnectionTable.tsx | 4 +- .../pages/AccountPage/AccountPage.tsx | 5 +- .../pages/ConnectorsPage/DestinationsPage.tsx | 4 +- .../components/CreateConnector.tsx | 4 +- .../pages/MetricsPage/MetricsPage.tsx | 5 +- .../NotificationPage/NotificationPage.tsx | 8 +- .../pages/AllSourcesPage/AllSourcesPage.tsx | 4 +- .../CreateSourcePage/CreateSourcePage.tsx | 5 +- .../pages/SourceItemPage/SourceItemPage.tsx | 4 +- .../components/SourceConnectionTable.tsx | 4 +- .../connector/SourceDefinitionService.tsx | 43 +++++++++ .../services/workspaces/WorkspacesService.tsx | 72 ++++++++++++-- .../ConnectionForm/ConnectionForm.tsx | 5 +- .../Connection/ConnectionForm/formConfig.tsx | 5 +- .../RequestConnectorModal.tsx | 5 +- 36 files changed, 261 insertions(+), 192 deletions(-) create mode 100644 airbyte-webapp/src/core/domain/workspace/WorkspaceService.ts create mode 100644 airbyte-webapp/src/core/domain/workspace/index.ts delete mode 100644 airbyte-webapp/src/core/resources/Workspace.ts create mode 100644 airbyte-webapp/src/services/connector/SourceDefinitionService.tsx diff --git a/airbyte-webapp/.storybook/restHooksDecorator.tsx b/airbyte-webapp/.storybook/restHooksDecorator.tsx index b915c65a94591..d9ebc5a61cf7f 100644 --- a/airbyte-webapp/.storybook/restHooksDecorator.tsx +++ b/airbyte-webapp/.storybook/restHooksDecorator.tsx @@ -3,17 +3,7 @@ import { CacheProvider } from "@rest-hooks/core"; import { Suspense } from "react"; import { NetworkErrorBoundary } from "rest-hooks"; -import WorkspaceResource from "../src/core/resources/Workspace"; - -const fixtures: Fixture[] = [ - { - request: WorkspaceResource.detailShape(), - params: { workspaceId: undefined }, - result: { - workspaceId: "story-book", - }, - }, -]; +const fixtures: Fixture[] = []; export const MockDecorator = (getStory) => ( diff --git a/airbyte-webapp/src/core/domain/connection/ConnectionService.ts b/airbyte-webapp/src/core/domain/connection/ConnectionService.ts index 6066e686819e1..48ff863274723 100644 --- a/airbyte-webapp/src/core/domain/connection/ConnectionService.ts +++ b/airbyte-webapp/src/core/domain/connection/ConnectionService.ts @@ -1,6 +1,6 @@ import { AirbyteRequestService } from "core/request/AirbyteRequestService"; import { Connection } from "./types"; -import { CommonRequestError } from "../../request/CommonRequestError"; +import { CommonRequestError } from "core/request/CommonRequestError"; class ConnectionService extends AirbyteRequestService { get url() { diff --git a/airbyte-webapp/src/core/domain/connection/WebBackendConnectionService.ts b/airbyte-webapp/src/core/domain/connection/WebBackendConnectionService.ts index 041e1d9ddb1cd..c6b243b8f9924 100644 --- a/airbyte-webapp/src/core/domain/connection/WebBackendConnectionService.ts +++ b/airbyte-webapp/src/core/domain/connection/WebBackendConnectionService.ts @@ -1,6 +1,6 @@ import { AirbyteRequestService } from "core/request/AirbyteRequestService"; import { Connection } from "./types"; -import { CommonRequestError } from "../../request/CommonRequestError"; +import { CommonRequestError } from "core/request/CommonRequestError"; class WebBackendConnectionService extends AirbyteRequestService { get url() { diff --git a/airbyte-webapp/src/core/domain/connector/SourceDefinitionService.ts b/airbyte-webapp/src/core/domain/connector/SourceDefinitionService.ts index 20ba570fd8143..1264959d2a90d 100644 --- a/airbyte-webapp/src/core/domain/connector/SourceDefinitionService.ts +++ b/airbyte-webapp/src/core/domain/connector/SourceDefinitionService.ts @@ -6,6 +6,24 @@ class SourceDefinitionService extends AirbyteRequestService { return "source_definitions"; } + public get(sourceDefinitionId: string): Promise { + return this.fetch(`${this.url}/get`, { + sourceDefinitionId, + }); + } + + public list(workspaceId: string): Promise { + return this.fetch(`${this.url}/list`, { + workspaceId, + }); + } + + public listLatest(workspaceId: string): Promise { + return this.fetch(`${this.url}/list_latest`, { + workspaceId, + }); + } + public update(body: SourceDefinition): Promise { return this.fetch(`${this.url}/update`, body); } diff --git a/airbyte-webapp/src/core/domain/workspace/WorkspaceService.ts b/airbyte-webapp/src/core/domain/workspace/WorkspaceService.ts new file mode 100644 index 0000000000000..2c915c6312c95 --- /dev/null +++ b/airbyte-webapp/src/core/domain/workspace/WorkspaceService.ts @@ -0,0 +1,24 @@ +import { AirbyteRequestService } from "core/request/AirbyteRequestService"; +import { Workspace } from "./Workspace"; + +class WorkspaceService extends AirbyteRequestService { + get url() { + return "workspaces"; + } + + public async get(workspaceId: string): Promise { + return await this.fetch(`${this.url}/get`, { + workspaceId, + }); + } + + public async list(): Promise<{ workspaces: Workspace[] }> { + return await this.fetch<{ workspaces: Workspace[] }>(`${this.url}/list`); + } + + public async update(payload: Record): Promise { + return await this.fetch(`${this.url}/update`, payload); + } +} + +export { WorkspaceService }; diff --git a/airbyte-webapp/src/core/domain/workspace/index.ts b/airbyte-webapp/src/core/domain/workspace/index.ts new file mode 100644 index 0000000000000..1b428e4079a4e --- /dev/null +++ b/airbyte-webapp/src/core/domain/workspace/index.ts @@ -0,0 +1,2 @@ +export * from "./Workspace"; +export * from "./WorkspaceService"; diff --git a/airbyte-webapp/src/core/resources/DestinationDefinition.ts b/airbyte-webapp/src/core/resources/DestinationDefinition.ts index 872b8c454fc27..90c0cf4c45b94 100644 --- a/airbyte-webapp/src/core/resources/DestinationDefinition.ts +++ b/airbyte-webapp/src/core/resources/DestinationDefinition.ts @@ -90,4 +90,13 @@ export default class DestinationDefinitionResource schema: this, }; } + + static createShape( + this: T + ): MutateShape> { + return { + ...super.createShape(), + schema: this, + }; + } } diff --git a/airbyte-webapp/src/core/resources/Workspace.ts b/airbyte-webapp/src/core/resources/Workspace.ts deleted file mode 100644 index 39b2b9231e3be..0000000000000 --- a/airbyte-webapp/src/core/resources/Workspace.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { MutateShape, ReadShape, Resource, SchemaDetail } from "rest-hooks"; - -import BaseResource from "./BaseResource"; -import { Notification, Workspace } from "core/domain/workspace/Workspace"; - -export default class WorkspaceResource - extends BaseResource - implements Workspace { - readonly workspaceId: string = ""; - readonly customerId: string = ""; - readonly email: string = ""; - readonly name: string = ""; - readonly slug: string = ""; - readonly initialSetupComplete: boolean = false; - readonly anonymousDataCollection: boolean = false; - readonly news: boolean = false; - readonly securityUpdates: boolean = false; - readonly displaySetupWizard: boolean = true; - readonly notifications: Notification[] = []; - - pk(): string { - return this.workspaceId?.toString(); - } - - static urlRoot = "workspaces"; - - static listShape( - this: T - ): ReadShape> { - return { - ...super.listShape(), - schema: { workspaces: [this] }, - }; - } - - static detailShape( - this: T - ): ReadShape> { - return { - ...super.detailShape(), - schema: this, - }; - } - - static updateShape( - this: T - ): MutateShape> { - return { - ...super.partialUpdateShape(), - schema: this, - }; - } -} diff --git a/airbyte-webapp/src/hooks/services/useConnectionHook.tsx b/airbyte-webapp/src/hooks/services/useConnectionHook.tsx index 929b4d7b6de74..e37050c7d8e26 100644 --- a/airbyte-webapp/src/hooks/services/useConnectionHook.tsx +++ b/airbyte-webapp/src/hooks/services/useConnectionHook.tsx @@ -10,12 +10,10 @@ import { useConfig } from "config"; import { Connection, ConnectionNamespaceDefinition, + ScheduleProperties, WebBackendConnectionService, } from "core/domain/connection"; - -import { ScheduleProperties } from "core/domain/connection"; import { SyncSchema } from "core/domain/catalog"; -import useWorkspace from "./useWorkspace"; import { Operation } from "core/domain/connection/operation"; import { useAnalyticsService } from "hooks/services/Analytics/useAnalyticsService"; @@ -24,6 +22,7 @@ import { Destination, Source, SourceDefinition } from "core/domain/connector"; import { useDefaultRequestMiddlewares } from "services/useDefaultRequestMiddlewares"; import { useInitService } from "services/useInitService"; import { ConnectionService } from "core/domain/connection/ConnectionService"; +import { useCurrentWorkspace } from "./useWorkspace"; export const connectionsKeys = { all: ["connections"] as const, @@ -245,7 +244,7 @@ const useUpdateConnection = () => { }; const useConnectionList = (): ListConnection => { - const { workspace } = useWorkspace(); + const workspace = useCurrentWorkspace(); const service = useWebConnectionService(); return (useQuery(connectionsKeys.lists(), () => diff --git a/airbyte-webapp/src/hooks/services/useDestinationDefinition.tsx b/airbyte-webapp/src/hooks/services/useDestinationDefinition.tsx index 18070d618aeae..97a56e145900f 100644 --- a/airbyte-webapp/src/hooks/services/useDestinationDefinition.tsx +++ b/airbyte-webapp/src/hooks/services/useDestinationDefinition.tsx @@ -1,14 +1,13 @@ import { useResource } from "rest-hooks"; import DestinationDefinitionResource from "core/resources/DestinationDefinition"; -import useWorkspace from "./useWorkspace"; import { DestinationDefinition } from "core/domain/connector"; +import { useCurrentWorkspace } from "./useWorkspace"; const useDestinationDefinitionList = (): { destinationDefinitions: DestinationDefinition[]; } => { - const { workspace } = useWorkspace(); - + const workspace = useCurrentWorkspace(); return useResource(DestinationDefinitionResource.listShape(), { workspaceId: workspace.workspaceId, }); diff --git a/airbyte-webapp/src/hooks/services/useDestinationHook.tsx b/airbyte-webapp/src/hooks/services/useDestinationHook.tsx index 614289851babc..be0b871f214ec 100644 --- a/airbyte-webapp/src/hooks/services/useDestinationHook.tsx +++ b/airbyte-webapp/src/hooks/services/useDestinationHook.tsx @@ -7,11 +7,11 @@ import { Connection } from "core/domain/connection"; import useRouter from "../useRouter"; import SchedulerResource, { Scheduler } from "core/resources/Scheduler"; import { ConnectionConfiguration } from "core/domain/connection"; -import useWorkspace from "./useWorkspace"; import { useAnalyticsService } from "hooks/services/Analytics/useAnalyticsService"; import { Destination } from "core/domain/connector"; import { RoutePaths } from "../../pages/routePaths"; import { connectionsKeys, ListConnection } from "./useConnectionHook"; +import { useCurrentWorkspace } from "./useWorkspace"; type ValuesProps = { name: string; @@ -54,7 +54,7 @@ type DestinationService = { const useDestination = (): DestinationService => { const { push } = useRouter(); - const { workspace } = useWorkspace(); + const workspace = useCurrentWorkspace(); const analyticsService = useAnalyticsService(); const createDestinationsImplementation = useFetcher( DestinationResource.createShape() @@ -223,7 +223,7 @@ const useDestination = (): DestinationService => { }; const useDestinationList = (): { destinations: Destination[] } => { - const { workspace } = useWorkspace(); + const workspace = useCurrentWorkspace(); return useResource(DestinationResource.listShape(), { workspaceId: workspace.workspaceId, }); diff --git a/airbyte-webapp/src/hooks/services/useSourceDefinition.tsx b/airbyte-webapp/src/hooks/services/useSourceDefinition.tsx index 396f96c311de8..e5d21fad9fa3a 100644 --- a/airbyte-webapp/src/hooks/services/useSourceDefinition.tsx +++ b/airbyte-webapp/src/hooks/services/useSourceDefinition.tsx @@ -1,14 +1,13 @@ import { useResource } from "rest-hooks"; import SourceDefinitionResource from "core/resources/SourceDefinition"; -import useWorkspace from "./useWorkspace"; import { SourceDefinition } from "core/domain/connector"; +import { useCurrentWorkspace } from "./useWorkspace"; const useSourceDefinitionList = (): { sourceDefinitions: SourceDefinition[]; } => { - const { workspace } = useWorkspace(); - + const workspace = useCurrentWorkspace(); return useResource(SourceDefinitionResource.listShape(), { workspaceId: workspace.workspaceId, }); diff --git a/airbyte-webapp/src/hooks/services/useSourceHook.tsx b/airbyte-webapp/src/hooks/services/useSourceHook.tsx index abf10d28131d3..4d7f6da76afa3 100644 --- a/airbyte-webapp/src/hooks/services/useSourceHook.tsx +++ b/airbyte-webapp/src/hooks/services/useSourceHook.tsx @@ -6,13 +6,13 @@ import SourceResource from "core/resources/Source"; import { Connection } from "core/domain/connection"; import SchedulerResource, { Scheduler } from "core/resources/Scheduler"; import { ConnectionConfiguration } from "core/domain/connection"; -import useWorkspace from "./useWorkspace"; import useRouter from "hooks/useRouter"; import { useAnalyticsService } from "hooks/services/Analytics/useAnalyticsService"; import { Source } from "core/domain/connector"; -import { RoutePaths } from "../../pages/routePaths"; +import { RoutePaths } from "pages/routePaths"; import { connectionsKeys, ListConnection } from "./useConnectionHook"; +import { useCurrentWorkspace } from "./useWorkspace"; type ValuesProps = { name: string; @@ -44,7 +44,7 @@ type SourceService = { const useSource = (): SourceService => { const { push } = useRouter(); - const { workspace } = useWorkspace(); + const workspace = useCurrentWorkspace(); const createSourcesImplementation = useFetcher(SourceResource.createShape()); const analyticsService = useAnalyticsService(); @@ -195,7 +195,7 @@ const useSource = (): SourceService => { }; const useSourceList = (): { sources: Source[] } => { - const { workspace } = useWorkspace(); + const workspace = useCurrentWorkspace(); return useResource(SourceResource.listShape(), { workspaceId: workspace.workspaceId, }); diff --git a/airbyte-webapp/src/hooks/services/useWorkspace.tsx b/airbyte-webapp/src/hooks/services/useWorkspace.tsx index 2261c2ce7ed82..21938b90503f3 100644 --- a/airbyte-webapp/src/hooks/services/useWorkspace.tsx +++ b/airbyte-webapp/src/hooks/services/useWorkspace.tsx @@ -1,6 +1,5 @@ import { useFetcher } from "rest-hooks"; -import WorkspaceResource from "core/resources/Workspace"; import NotificationsResource, { Notifications, } from "core/resources/Notifications"; @@ -9,6 +8,7 @@ import { useAnalyticsService } from "hooks/services/Analytics"; import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { Destination, Source } from "core/domain/connector"; import { Workspace } from "core/domain/workspace/Workspace"; +import { useUpdateWorkspace } from "services/workspaces/WorkspacesService"; export type WebhookPayload = { webhook: string; @@ -17,7 +17,6 @@ export type WebhookPayload = { }; const useWorkspace = (): { - workspace: Workspace; updatePreferences: (data: { email?: string; anonymousDataCollection: boolean; @@ -43,7 +42,7 @@ const useWorkspace = (): { destination: Destination; }) => Promise; } => { - const updateWorkspace = useFetcher(WorkspaceResource.updateShape()); + const { mutateAsync: updateWorkspace } = useUpdateWorkspace(); const tryWebhookUrl = useFetcher(NotificationsResource.tryShape()); const workspace = useCurrentWorkspace(); @@ -56,17 +55,14 @@ const useWorkspace = (): { }); } - await updateWorkspace( - {}, - { - workspaceId: workspace.workspaceId, - initialSetupComplete: workspace.initialSetupComplete, - anonymousDataCollection: workspace.anonymousDataCollection, - news: workspace.news, - securityUpdates: workspace.securityUpdates, - displaySetupWizard: false, - } - ); + await updateWorkspace({ + workspaceId: workspace.workspaceId, + initialSetupComplete: workspace.initialSetupComplete, + anonymousDataCollection: workspace.anonymousDataCollection, + news: workspace.news, + securityUpdates: workspace.securityUpdates, + displaySetupWizard: false, + }); }; const sendFeedback = async ({ @@ -93,15 +89,12 @@ const useWorkspace = (): { news: boolean; securityUpdates: boolean; }) => { - const result = await updateWorkspace( - {}, - { - workspaceId: workspace.workspaceId, - initialSetupComplete: true, - displaySetupWizard: true, - ...data, - } - ); + const result = await updateWorkspace({ + workspaceId: workspace.workspaceId, + initialSetupComplete: true, + displaySetupWizard: true, + ...data, + }); analyticsService.track("Specified Preferences", { email: data.email, @@ -119,16 +112,13 @@ const useWorkspace = (): { news: boolean; securityUpdates: boolean; }) => - await updateWorkspace( - {}, - { - workspaceId: workspace.workspaceId, - initialSetupComplete: workspace.initialSetupComplete, - displaySetupWizard: workspace.displaySetupWizard, - notifications: workspace.notifications, - ...data, - } - ); + await updateWorkspace({ + workspaceId: workspace.workspaceId, + initialSetupComplete: workspace.initialSetupComplete, + displaySetupWizard: workspace.displaySetupWizard, + notifications: workspace.notifications, + ...data, + }); const testWebhook = async (data: WebhookPayload) => await tryWebhookUrl( @@ -144,30 +134,26 @@ const useWorkspace = (): { ); const updateWebhook = async (data: WebhookPayload) => - await updateWorkspace( - {}, - { - workspaceId: workspace.workspaceId, - initialSetupComplete: workspace.initialSetupComplete, - displaySetupWizard: workspace.displaySetupWizard, - anonymousDataCollection: workspace.anonymousDataCollection, - news: workspace.news, - securityUpdates: workspace.securityUpdates, - notifications: [ - { - notificationType: "slack", - sendOnSuccess: data.sendOnSuccess, - sendOnFailure: data.sendOnFailure, - slackConfiguration: { - webhook: data.webhook, - }, + await updateWorkspace({ + workspaceId: workspace.workspaceId, + initialSetupComplete: workspace.initialSetupComplete, + displaySetupWizard: workspace.displaySetupWizard, + anonymousDataCollection: workspace.anonymousDataCollection, + news: workspace.news, + securityUpdates: workspace.securityUpdates, + notifications: [ + { + notificationType: "slack", + sendOnSuccess: data.sendOnSuccess, + sendOnFailure: data.sendOnFailure, + slackConfiguration: { + webhook: data.webhook, }, - ], - } - ); + }, + ], + }); return { - workspace, finishOnboarding, setInitialSetupConfig, updatePreferences, diff --git a/airbyte-webapp/src/packages/cloud/views/users/AccountSettingsView/components/EmailSection/EmailSection.tsx b/airbyte-webapp/src/packages/cloud/views/users/AccountSettingsView/components/EmailSection/EmailSection.tsx index 9607d79364190..5e0427008ffd8 100644 --- a/airbyte-webapp/src/packages/cloud/views/users/AccountSettingsView/components/EmailSection/EmailSection.tsx +++ b/airbyte-webapp/src/packages/cloud/views/users/AccountSettingsView/components/EmailSection/EmailSection.tsx @@ -13,7 +13,7 @@ import { FieldItem } from "packages/cloud/views/auth/components/FormComponents"; import { LabeledInput } from "components/LabeledInput"; import NotificationsForm from "pages/SettingsPage/pages/NotificationPage/components/NotificationsForm"; import { useCurrentUser } from "packages/cloud/services/auth/AuthService"; -import useWorkspace from "hooks/services/useWorkspace"; +import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import useWorkspaceEditor from "pages/SettingsPage/components/useWorkspaceEditor"; import { FormValues } from "./typings"; @@ -37,7 +37,7 @@ const EmailSection: React.FC = () => { const emailService = useEmail(); - const { workspace } = useWorkspace(); + const workspace = useCurrentWorkspace(); const { errorMessage, successMessage, diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/DestinationForm.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/DestinationForm.tsx index ceffb6046d3e8..3de7523d9f002 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/DestinationForm.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/DestinationForm.tsx @@ -8,7 +8,7 @@ import useDestination from "hooks/services/useDestinationHook"; // TODO: create separate component for source and destinations forms import DestinationForm from "pages/DestinationPage/pages/CreateDestinationPage/components/DestinationForm"; import { ConnectionConfiguration } from "core/domain/connection"; -import useWorkspace from "hooks/services/useWorkspace"; +import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; type IProps = { afterSubmit: () => void; @@ -16,7 +16,7 @@ type IProps = { const CreateDestinationPage: React.FC = ({ afterSubmit }) => { const { push, location } = useRouter(); - const { workspace } = useWorkspace(); + const workspace = useCurrentWorkspace(); const [successRequest, setSuccessRequest] = useState(false); const [errorStatusRequest, setErrorStatusRequest] = useState(null); diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/ExistingEntityForm.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/ExistingEntityForm.tsx index cd1b4d3d1129d..a5b2636d750a1 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/ExistingEntityForm.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/ExistingEntityForm.tsx @@ -7,7 +7,7 @@ import * as yup from "yup"; import ContentCard from "components/ContentCard"; import { DropDown, Button, ControlLabels } from "components"; -import useWorkspace from "hooks/services/useWorkspace"; +import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import SourceResource from "core/resources/Source"; import SourceDefinitionResource from "core/resources/SourceDefinition"; import DestinationResource from "core/resources/Destination"; @@ -42,8 +42,7 @@ const existingEntityValidationSchema = yup.object().shape({ const ExistingEntityForm: React.FC = ({ type, onSubmit }) => { const formatMessage = useIntl().formatMessage; - const { workspace } = useWorkspace(); - + const workspace = useCurrentWorkspace(); const { sources } = useResource(SourceResource.listShape(), { workspaceId: workspace.workspaceId, }); diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/SourceForm.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/SourceForm.tsx index 3a5bc24a36fd1..aef1675f941bd 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/SourceForm.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/SourceForm.tsx @@ -8,7 +8,7 @@ import useSource from "hooks/services/useSourceHook"; // TODO: create separate component for source and destinations forms import SourceForm from "pages/SourcesPage/pages/CreateSourcePage/components/SourceForm"; import { ConnectionConfiguration } from "core/domain/connection"; -import useWorkspace from "hooks/services/useWorkspace"; +import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; type IProps = { afterSubmit: () => void; @@ -18,7 +18,7 @@ const SourceFormComponent: React.FC = ({ afterSubmit }) => { const { push, location } = useRouter(); const [successRequest, setSuccessRequest] = useState(false); const [errorStatusRequest, setErrorStatusRequest] = useState(null); - const { workspace } = useWorkspace(); + const workspace = useCurrentWorkspace(); const { sourceDefinitions } = useResource( SourceDefinitionResource.listShape(), { diff --git a/airbyte-webapp/src/pages/DestinationPage/pages/AllDestinationsPage/AllDestinationsPage.tsx b/airbyte-webapp/src/pages/DestinationPage/pages/AllDestinationsPage/AllDestinationsPage.tsx index d21b5d15c31b7..9d86483eec4b0 100644 --- a/airbyte-webapp/src/pages/DestinationPage/pages/AllDestinationsPage/AllDestinationsPage.tsx +++ b/airbyte-webapp/src/pages/DestinationPage/pages/AllDestinationsPage/AllDestinationsPage.tsx @@ -9,12 +9,12 @@ import DestinationsTable from "./components/DestinationsTable"; import DestinationResource from "core/resources/Destination"; import HeadTitle from "components/HeadTitle"; import Placeholder, { ResourceTypes } from "components/Placeholder"; -import useWorkspace from "hooks/services/useWorkspace"; +import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { RoutePaths } from "../../../routePaths"; const AllDestinationsPage: React.FC = () => { const { push } = useRouter(); - const { workspace } = useWorkspace(); + const workspace = useCurrentWorkspace(); const { destinations } = useResource(DestinationResource.listShape(), { workspaceId: workspace.workspaceId, }); diff --git a/airbyte-webapp/src/pages/DestinationPage/pages/CreateDestinationPage/CreateDestinationPage.tsx b/airbyte-webapp/src/pages/DestinationPage/pages/CreateDestinationPage/CreateDestinationPage.tsx index d3a40b237abbb..f4969d7e55b7a 100644 --- a/airbyte-webapp/src/pages/DestinationPage/pages/CreateDestinationPage/CreateDestinationPage.tsx +++ b/airbyte-webapp/src/pages/DestinationPage/pages/CreateDestinationPage/CreateDestinationPage.tsx @@ -9,12 +9,12 @@ import useDestination from "hooks/services/useDestinationHook"; import { FormPageContent } from "components/ConnectorBlocks"; import { ConnectionConfiguration } from "core/domain/connection"; import HeadTitle from "components/HeadTitle"; -import useWorkspace from "hooks/services/useWorkspace"; +import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { JobInfo } from "../../../../core/domain/job/Job"; const CreateDestinationPage: React.FC = () => { const { push } = useRouter(); - const { workspace } = useWorkspace(); + const workspace = useCurrentWorkspace(); const [successRequest, setSuccessRequest] = useState(false); const [errorStatusRequest, setErrorStatusRequest] = useState<{ status: number; diff --git a/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/DestinationItemPage.tsx b/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/DestinationItemPage.tsx index 92b003b267972..f51f5c3154b6c 100644 --- a/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/DestinationItemPage.tsx +++ b/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/DestinationItemPage.tsx @@ -22,14 +22,14 @@ import { getIcon } from "utils/imageUtils"; import ImageBlock from "components/ImageBlock"; import SourceDefinitionResource from "core/resources/SourceDefinition"; import HeadTitle from "components/HeadTitle"; -import useWorkspace from "hooks/services/useWorkspace"; +import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { DropDownRow } from "components"; import { RoutePaths } from "../../../routePaths"; import { useConnectionList } from "hooks/services/useConnectionHook"; const DestinationItemPage: React.FC = () => { const { params, push } = useRouter(); - const { workspace } = useWorkspace(); + const workspace = useCurrentWorkspace(); const [currentStep, setCurrentStep] = useState(StepsTypes.OVERVIEW); const onSelectStep = (id: string) => setCurrentStep(id); diff --git a/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationConnectionTable.tsx b/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationConnectionTable.tsx index 4a6a0c88a701a..26f817e6d30cd 100644 --- a/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationConnectionTable.tsx +++ b/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationConnectionTable.tsx @@ -9,7 +9,7 @@ import { getConnectionTableData } from "components/EntityTable/utils"; import { ITableDataItem } from "components/EntityTable/types"; import SourceDefinitionResource from "core/resources/SourceDefinition"; import DestinationDefinitionResource from "core/resources/DestinationDefinition"; -import useWorkspace from "hooks/services/useWorkspace"; +import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { RoutePaths } from "../../../../routePaths"; type IProps = { @@ -18,7 +18,7 @@ type IProps = { const DestinationConnectionTable: React.FC = ({ connections }) => { const { push } = useRouter(); - const { workspace } = useWorkspace(); + const workspace = useCurrentWorkspace(); const { changeStatus, syncManualConnection } = useSyncActions(); const { sourceDefinitions } = useResource( diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/AccountPage/AccountPage.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/AccountPage/AccountPage.tsx index 744a512214e2e..25d0ce916604f 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/AccountPage/AccountPage.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/AccountPage/AccountPage.tsx @@ -1,14 +1,13 @@ import React from "react"; import { FormattedMessage } from "react-intl"; -import useWorkspace from "hooks/services/useWorkspace"; +import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import useWorkspaceEditor from "pages/SettingsPage/components/useWorkspaceEditor"; import HeadTitle from "components/HeadTitle"; import AccountForm from "./components/AccountForm"; import { Content, SettingsCard } from "../SettingsComponents"; const AccountPage: React.FC = () => { - const { workspace } = useWorkspace(); - + const workspace = useCurrentWorkspace(); const { errorMessage, successMessage, diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/DestinationsPage.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/DestinationsPage.tsx index ef360b720bdfb..a948b3ccf704d 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/DestinationsPage.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/DestinationsPage.tsx @@ -7,11 +7,11 @@ import DestinationDefinitionResource from "core/resources/DestinationDefinition" import { DestinationResource } from "core/resources/Destination"; import useConnector from "hooks/services/useConnector"; import ConnectorsView from "./components/ConnectorsView"; -import useWorkspace from "hooks/services/useWorkspace"; +import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { DestinationDefinition } from "core/domain/connector"; const DestinationsPage: React.FC = () => { - const { workspace } = useWorkspace(); + const workspace = useCurrentWorkspace(); const [isUpdateSuccess, setIsUpdateSuccess] = useState(false); const formatMessage = useIntl().formatMessage; const { destinationDefinitions } = useResource( diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/CreateConnector.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/CreateConnector.tsx index ca16c46c8ffd6..faacfdef2cf90 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/CreateConnector.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/CreateConnector.tsx @@ -8,7 +8,7 @@ import useRouter from "hooks/useRouter"; import DestinationDefinitionResource from "core/resources/DestinationDefinition"; import CreateConnectorModal from "./CreateConnectorModal"; -import useWorkspace from "hooks/services/useWorkspace"; +import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { RoutePaths } from "../../../../routePaths"; type IProps = { @@ -24,7 +24,7 @@ type ICreateProps = { const CreateConnector: React.FC = ({ type }) => { const { push } = useRouter(); - const { workspace } = useWorkspace(); + const workspace = useCurrentWorkspace(); const [isModalOpen, setIsModalOpen] = useState(false); const [errorMessage, setErrorMessage] = useState(""); const onChangeModalState = () => { diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/MetricsPage.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/MetricsPage.tsx index 9880d51aebdc7..8fa1c848d19d6 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/MetricsPage.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/MetricsPage.tsx @@ -1,6 +1,6 @@ import React from "react"; import { FormattedMessage } from "react-intl"; -import useWorkspace from "hooks/services/useWorkspace"; +import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import HeadTitle from "components/HeadTitle"; import MetricsForm from "./components/MetricsForm"; import useWorkspaceEditor from "../../components/useWorkspaceEditor"; @@ -8,8 +8,7 @@ import useWorkspaceEditor from "../../components/useWorkspaceEditor"; import { Content, SettingsCard } from "../SettingsComponents"; const MetricsPage: React.FC = () => { - const { workspace } = useWorkspace(); - + const workspace = useCurrentWorkspace(); const { errorMessage, successMessage, diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx index b08448aa27a31..f9423f6d049e5 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx @@ -1,6 +1,9 @@ import React, { useMemo, useState, useCallback } from "react"; import { FormattedMessage } from "react-intl"; -import useWorkspace, { WebhookPayload } from "hooks/services/useWorkspace"; +import useWorkspace, { + useCurrentWorkspace, + WebhookPayload, +} from "hooks/services/useWorkspace"; import WebHookForm from "./components/WebHookForm"; import HeadTitle from "components/HeadTitle"; @@ -39,7 +42,8 @@ function useAsyncWithTimeout(f: (data: K) => Promise) { } const NotificationPage: React.FC = () => { - const { workspace, updateWebhook, testWebhook } = useWorkspace(); + const { updateWebhook, testWebhook } = useWorkspace(); + const workspace = useCurrentWorkspace(); const { call: onSubmitWebhook, diff --git a/airbyte-webapp/src/pages/SourcesPage/pages/AllSourcesPage/AllSourcesPage.tsx b/airbyte-webapp/src/pages/SourcesPage/pages/AllSourcesPage/AllSourcesPage.tsx index 4542b3b9367b6..19566db56592a 100644 --- a/airbyte-webapp/src/pages/SourcesPage/pages/AllSourcesPage/AllSourcesPage.tsx +++ b/airbyte-webapp/src/pages/SourcesPage/pages/AllSourcesPage/AllSourcesPage.tsx @@ -9,12 +9,12 @@ import SourcesTable from "./components/SourcesTable"; import SourceResource from "core/resources/Source"; import HeadTitle from "components/HeadTitle"; import Placeholder, { ResourceTypes } from "components/Placeholder"; -import useWorkspace from "hooks/services/useWorkspace"; +import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { RoutePaths } from "../../../routePaths"; const AllSourcesPage: React.FC = () => { const { push } = useRouter(); - const { workspace } = useWorkspace(); + const workspace = useCurrentWorkspace(); const { sources } = useResource(SourceResource.listShape(), { workspaceId: workspace.workspaceId, }); diff --git a/airbyte-webapp/src/pages/SourcesPage/pages/CreateSourcePage/CreateSourcePage.tsx b/airbyte-webapp/src/pages/SourcesPage/pages/CreateSourcePage/CreateSourcePage.tsx index 1efb8b939a3a1..975b7a80fe358 100644 --- a/airbyte-webapp/src/pages/SourcesPage/pages/CreateSourcePage/CreateSourcePage.tsx +++ b/airbyte-webapp/src/pages/SourcesPage/pages/CreateSourcePage/CreateSourcePage.tsx @@ -10,7 +10,7 @@ import useSource from "hooks/services/useSourceHook"; import { FormPageContent } from "components/ConnectorBlocks"; import { ConnectionConfiguration } from "core/domain/connection"; import HeadTitle from "components/HeadTitle"; -import useWorkspace from "hooks/services/useWorkspace"; +import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { JobInfo } from "core/domain/job/Job"; const CreateSourcePage: React.FC = () => { @@ -21,8 +21,7 @@ const CreateSourcePage: React.FC = () => { response: JobInfo; } | null>(null); - const { workspace } = useWorkspace(); - + const workspace = useCurrentWorkspace(); const { sourceDefinitions } = useResource( SourceDefinitionResource.listShape(), { diff --git a/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/SourceItemPage.tsx b/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/SourceItemPage.tsx index 5d2cb7b982411..cf341669e369c 100644 --- a/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/SourceItemPage.tsx +++ b/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/SourceItemPage.tsx @@ -24,13 +24,13 @@ import DestinationsDefinitionResource from "core/resources/DestinationDefinition import { getIcon } from "utils/imageUtils"; import HeadTitle from "components/HeadTitle"; import Placeholder, { ResourceTypes } from "components/Placeholder"; -import useWorkspace from "hooks/services/useWorkspace"; +import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { RoutePaths } from "../../../routePaths"; import { useConnectionList } from "hooks/services/useConnectionHook"; const SourceItemPage: React.FC = () => { const { query, push } = useRouter<{ id: string }>(); - const { workspace } = useWorkspace(); + const workspace = useCurrentWorkspace(); const [currentStep, setCurrentStep] = useState(StepsTypes.OVERVIEW); const onSelectStep = (id: string) => setCurrentStep(id); diff --git a/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceConnectionTable.tsx b/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceConnectionTable.tsx index eb048dd1cdc74..a0e101fe5bd63 100644 --- a/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceConnectionTable.tsx +++ b/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceConnectionTable.tsx @@ -9,7 +9,7 @@ import { getConnectionTableData } from "components/EntityTable/utils"; import { ITableDataItem } from "components/EntityTable/types"; import SourceDefinitionResource from "core/resources/SourceDefinition"; import DestinationDefinitionResource from "core/resources/DestinationDefinition"; -import useWorkspace from "hooks/services/useWorkspace"; +import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { RoutePaths } from "../../../../routePaths"; type IProps = { @@ -18,7 +18,7 @@ type IProps = { const SourceConnectionTable: React.FC = ({ connections }) => { const { push } = useRouter(); - const { workspace } = useWorkspace(); + const workspace = useCurrentWorkspace(); const { changeStatus, syncManualConnection } = useSyncActions(); const { sourceDefinitions } = useResource( diff --git a/airbyte-webapp/src/services/connector/SourceDefinitionService.tsx b/airbyte-webapp/src/services/connector/SourceDefinitionService.tsx new file mode 100644 index 0000000000000..5325c8b552445 --- /dev/null +++ b/airbyte-webapp/src/services/connector/SourceDefinitionService.tsx @@ -0,0 +1,43 @@ +import { QueryObserverSuccessResult, useQuery } from "react-query"; + +import { SourceDefinition } from "core/domain/connector"; +import { useConfig } from "config"; +import { useDefaultRequestMiddlewares } from "services/useDefaultRequestMiddlewares"; +import { useInitService } from "services/useInitService"; +import { SourceDefinitionService } from "core/domain/connector/SourceDefinitionService"; +import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; + +export const sourceDefinitionKeys = { + all: ["sourceDefinition"] as const, + lists: () => [...sourceDefinitionKeys.all, "list"] as const, + detail: (id: string | number) => + [...sourceDefinitionKeys.all, "details", id] as const, +}; + +function useGetSourceDefinitionService(): SourceDefinitionService { + const { apiUrl } = useConfig(); + + const requestAuthMiddleware = useDefaultRequestMiddlewares(); + + return useInitService( + () => new SourceDefinitionService(apiUrl, requestAuthMiddleware), + [apiUrl, requestAuthMiddleware] + ); +} + +export const useGetSourceDefinition = (id: string): SourceDefinition => { + const service = useGetSourceDefinitionService(); + + return (useQuery(sourceDefinitionKeys.detail(id), () => + service.get(id) + ) as QueryObserverSuccessResult).data; +}; + +export const useListSourceDefinitions = (): SourceDefinition[] => { + const service = useGetSourceDefinitionService(); + const workspace = useCurrentWorkspace(); + + return (useQuery(sourceDefinitionKeys.lists(), () => + service.list(workspace.workspaceId) + ) as QueryObserverSuccessResult).data; +}; diff --git a/airbyte-webapp/src/services/workspaces/WorkspacesService.tsx b/airbyte-webapp/src/services/workspaces/WorkspacesService.tsx index f3ad26128b67e..33de0e459ce19 100644 --- a/airbyte-webapp/src/services/workspaces/WorkspacesService.tsx +++ b/airbyte-webapp/src/services/workspaces/WorkspacesService.tsx @@ -1,11 +1,26 @@ import React, { useCallback, useContext, useMemo } from "react"; -import { useQueryClient } from "react-query"; -import { useResetter, useResource } from "rest-hooks"; +import { + QueryObserverSuccessResult, + useMutation, + useQuery, + useQueryClient, +} from "react-query"; +import { useResetter } from "rest-hooks"; -import WorkspaceResource from "core/resources/Workspace"; import useRouter from "hooks/useRouter"; -import { Workspace } from "core/domain/workspace/Workspace"; -import { RoutePaths } from "../../pages/routePaths"; +import { Workspace, WorkspaceService } from "core/domain/workspace"; +import { RoutePaths } from "pages/routePaths"; +import { useConfig } from "config"; +import { useDefaultRequestMiddlewares } from "../useDefaultRequestMiddlewares"; +import { useInitService } from "../useInitService"; + +export const workspaceKeys = { + all: ["workspaces"] as const, + lists: () => [...workspaceKeys.all, "list"] as const, + list: (filters: string) => [...workspaceKeys.lists(), { filters }] as const, + detail: (workspaceId: string) => + [...workspaceKeys.all, "details", workspaceId] as const, +}; type Context = { selectWorkspace: (workspaceId?: string | null | Workspace) => void; @@ -66,6 +81,17 @@ export const useWorkspaceService = (): Context => { return workspaceService; }; +function useWorkspaceApiService(): WorkspaceService { + const config = useConfig(); + const middlewares = useDefaultRequestMiddlewares(); + + return useInitService( + () => new WorkspaceService(config.apiUrl, middlewares), + // eslint-disable-next-line react-hooks/exhaustive-deps + [config] + ); +} + export const useCurrentWorkspaceId = (): string => { const { params } = useRouter(); @@ -75,11 +101,41 @@ export const useCurrentWorkspaceId = (): string => { export const useCurrentWorkspace = (): Workspace => { const workspaceId = useCurrentWorkspaceId(); - return useResource(WorkspaceResource.detailShape(), { - workspaceId, + return useGetWorkspace(workspaceId, { + staleTime: Infinity, }); }; export const useListWorkspaces = (): Workspace[] => { - return useResource(WorkspaceResource.listShape(), {}).workspaces; + const service = useWorkspaceApiService(); + + return (useQuery(workspaceKeys.lists(), () => + service.list() + ) as QueryObserverSuccessResult<{ workspaces: Workspace[] }>).data.workspaces; +}; + +export const useGetWorkspace = ( + workspaceId: string, + options?: { + staleTime: number; + } +): Workspace => { + const service = useWorkspaceApiService(); + + return (useQuery( + workspaceKeys.detail(workspaceId), + () => service.get(workspaceId), + options + ) as QueryObserverSuccessResult).data; +}; + +export const useUpdateWorkspace = () => { + const service = useWorkspaceApiService(); + const queryClient = useQueryClient(); + + return useMutation((workspace: any) => service.update(workspace), { + onSuccess: (data) => { + queryClient.setQueryData(workspaceKeys.detail(data.workspaceId), data); + }, + }); }; diff --git a/airbyte-webapp/src/views/Connection/ConnectionForm/ConnectionForm.tsx b/airbyte-webapp/src/views/Connection/ConnectionForm/ConnectionForm.tsx index 78d598ab7c278..f1c7654d5642f 100644 --- a/airbyte-webapp/src/views/Connection/ConnectionForm/ConnectionForm.tsx +++ b/airbyte-webapp/src/views/Connection/ConnectionForm/ConnectionForm.tsx @@ -16,7 +16,7 @@ import { Label, } from "components"; -import useWorkspace from "hooks/services/useWorkspace"; +import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { createFormErrorMessage } from "utils/errorStatusMessage"; import { @@ -138,8 +138,7 @@ const ConnectionForm: React.FC = ({ isEditMode ); - const { workspace } = useWorkspace(); - + const workspace = useCurrentWorkspace(); const onFormSubmit = useCallback( async (values: FormikConnectionFormValues) => { const formValues: ConnectionFormValues = (connectionValidationSchema.cast( diff --git a/airbyte-webapp/src/views/Connection/ConnectionForm/formConfig.tsx b/airbyte-webapp/src/views/Connection/ConnectionForm/formConfig.tsx index d07e6a93cd54c..67f684d1e761a 100644 --- a/airbyte-webapp/src/views/Connection/ConnectionForm/formConfig.tsx +++ b/airbyte-webapp/src/views/Connection/ConnectionForm/formConfig.tsx @@ -27,7 +27,7 @@ import { ConnectionSchedule, } from "core/domain/connection"; import { SOURCE_NAMESPACE_TAG } from "core/domain/connector/source"; -import useWorkspace from "hooks/services/useWorkspace"; +import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { DestinationDefinitionSpecification } from "core/domain/connector"; type FormikConnectionFormValues = { @@ -55,8 +55,7 @@ const DEFAULT_SCHEDULE: ScheduleProperties = { }; function useDefaultTransformation(): Transformation { - const { workspace } = useWorkspace(); - + const workspace = useCurrentWorkspace(); return { name: "My dbt transformations", workspaceId: workspace.workspaceId, diff --git a/airbyte-webapp/src/views/Connector/RequestConnectorModal/RequestConnectorModal.tsx b/airbyte-webapp/src/views/Connector/RequestConnectorModal/RequestConnectorModal.tsx index b10cf81135784..e52c6956d5f6a 100644 --- a/airbyte-webapp/src/views/Connector/RequestConnectorModal/RequestConnectorModal.tsx +++ b/airbyte-webapp/src/views/Connector/RequestConnectorModal/RequestConnectorModal.tsx @@ -6,7 +6,7 @@ import ConnectorForm from "./components/ConnectorForm"; import { Modal } from "components"; import useRequestConnector from "hooks/services/useRequestConnector"; -import useWorkspace from "hooks/services/useWorkspace"; +import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { Values } from "./types"; type RequestConnectorModalProps = { @@ -26,8 +26,7 @@ const RequestConnectorModal: React.FC = ({ }) => { const [hasFeedback, setHasFeedback] = useState(false); const { requestConnector } = useRequestConnector(); - const { workspace } = useWorkspace(); - + const workspace = useCurrentWorkspace(); const onSubmit = (values: Values) => { requestConnector(values); setHasFeedback(true); From cf908ac6739f4c8877dc305f57b8a6e629ccb174 Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Wed, 30 Mar 2022 01:21:18 +0300 Subject: [PATCH 05/20] Remove SourceDefinition and DestinationDefinition resources --- .../connector/DestinationDefinitionService.ts | 40 ++++- .../connector/SourceDefinitionService.ts | 30 +++- .../core/resources/DestinationDefinition.ts | 102 ------------ .../src/core/resources/SourceDefinition.ts | 92 ----------- .../src/hooks/services/useConnector.test.tsx | 108 +++++------- .../src/hooks/services/useConnector.tsx | 49 +++--- .../services/useDestinationDefinition.tsx | 22 --- .../hooks/services/useSourceDefinition.tsx | 22 --- .../components/UsagePerConnectionTable.tsx | 4 +- .../components/ConnectionsTable.tsx | 4 +- .../components/StatusView.tsx | 24 +-- .../CreationFormPage/CreationFormPage.tsx | 22 +-- .../components/DestinationForm.tsx | 12 +- .../components/ExistingEntityForm.tsx | 23 +-- .../components/SourceForm.tsx | 13 +- .../components/DestinationsTable.tsx | 2 +- .../CreateDestinationPage.tsx | 15 +- .../DestinationItemPage.tsx | 32 ++-- .../components/DestinationConnectionTable.tsx | 24 +-- .../components/DestinationSettings.tsx | 13 +- .../pages/OnboardingPage/OnboardingPage.tsx | 16 +- .../pages/ConnectorsPage/DestinationsPage.tsx | 31 ++-- .../pages/ConnectorsPage/SourcesPage.tsx | 22 +-- .../components/CreateConnector.tsx | 56 ++----- .../components/SourcesTable.tsx | 2 +- .../CreateSourcePage/CreateSourcePage.tsx | 12 +- .../pages/SourceItemPage/SourceItemPage.tsx | 15 +- .../components/SourceConnectionTable.tsx | 23 +-- .../components/SourceSettings.tsx | 13 +- .../connector/DestinationDefinitionService.ts | 155 ++++++++++++++++++ .../connector/SourceDefinitionService.ts | 136 +++++++++++++++ .../connector/SourceDefinitionService.tsx | 43 ----- 32 files changed, 529 insertions(+), 648 deletions(-) delete mode 100644 airbyte-webapp/src/core/resources/DestinationDefinition.ts delete mode 100644 airbyte-webapp/src/core/resources/SourceDefinition.ts delete mode 100644 airbyte-webapp/src/hooks/services/useDestinationDefinition.tsx delete mode 100644 airbyte-webapp/src/hooks/services/useSourceDefinition.tsx create mode 100644 airbyte-webapp/src/services/connector/DestinationDefinitionService.ts create mode 100644 airbyte-webapp/src/services/connector/SourceDefinitionService.ts delete mode 100644 airbyte-webapp/src/services/connector/SourceDefinitionService.tsx diff --git a/airbyte-webapp/src/core/domain/connector/DestinationDefinitionService.ts b/airbyte-webapp/src/core/domain/connector/DestinationDefinitionService.ts index 3cc8690f94473..b86c1ca5b9a19 100644 --- a/airbyte-webapp/src/core/domain/connector/DestinationDefinitionService.ts +++ b/airbyte-webapp/src/core/domain/connector/DestinationDefinitionService.ts @@ -6,9 +6,47 @@ class DestinationDefinitionService extends AirbyteRequestService { return "destination_definitions"; } - public update(body: DestinationDefinition): Promise { + public get(destinationDefinitionId: string): Promise { + return this.fetch(`${this.url}/get`, { + destinationDefinitionId, + }); + } + + public list( + workspaceId: string + ): Promise<{ destinationDefinitions: DestinationDefinition[] }> { + return this.fetch(`${this.url}/list`, { + workspaceId, + }); + } + + public listLatest( + workspaceId: string + ): Promise<{ destinationDefinitions: DestinationDefinition[] }> { + return this.fetch(`${this.url}/list_latest`, { + workspaceId, + }); + } + + public update(body: { + destinationDefinitionId: string; + dockerImageTag: string; + }): Promise { return this.fetch(`${this.url}/update`, body); } + + public create( + body: CreateDestinationDefinitionPayload + ): Promise { + return this.fetch(`${this.url}/create`, body); + } } +export type CreateDestinationDefinitionPayload = { + name: string; + documentationUrl: string; + dockerImageTag: string; + dockerRepository: string; +}; + export { DestinationDefinitionService }; diff --git a/airbyte-webapp/src/core/domain/connector/SourceDefinitionService.ts b/airbyte-webapp/src/core/domain/connector/SourceDefinitionService.ts index 1264959d2a90d..0edd7cbd50a15 100644 --- a/airbyte-webapp/src/core/domain/connector/SourceDefinitionService.ts +++ b/airbyte-webapp/src/core/domain/connector/SourceDefinitionService.ts @@ -12,21 +12,41 @@ class SourceDefinitionService extends AirbyteRequestService { }); } - public list(workspaceId: string): Promise { - return this.fetch(`${this.url}/list`, { + public list( + workspaceId: string + ): Promise<{ sourceDefinitions: SourceDefinition[] }> { + return this.fetch(`${this.url}/list`, { workspaceId, }); } - public listLatest(workspaceId: string): Promise { - return this.fetch(`${this.url}/list_latest`, { + public listLatest( + workspaceId: string + ): Promise<{ sourceDefinitions: SourceDefinition[] }> { + return this.fetch(`${this.url}/list_latest`, { workspaceId, }); } - public update(body: SourceDefinition): Promise { + public update(body: { + sourceDefinitionId: string; + dockerImageTag: string; + }): Promise { return this.fetch(`${this.url}/update`, body); } + + public create( + body: CreateSourceDefinitionPayload + ): Promise { + return this.fetch(`${this.url}/create`, body); + } } +export type CreateSourceDefinitionPayload = { + name: string; + documentationUrl: string; + dockerImageTag: string; + dockerRepository: string; +}; + export { SourceDefinitionService }; diff --git a/airbyte-webapp/src/core/resources/DestinationDefinition.ts b/airbyte-webapp/src/core/resources/DestinationDefinition.ts deleted file mode 100644 index 90c0cf4c45b94..0000000000000 --- a/airbyte-webapp/src/core/resources/DestinationDefinition.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { MutateShape, ReadShape, Resource, SchemaDetail } from "rest-hooks"; - -import { getService } from "core/servicesProvider"; - -import BaseResource from "./BaseResource"; -import { DestinationDefinitionService } from "core/domain/connector/DestinationDefinitionService"; -import { DestinationDefinition } from "core/domain/connector"; - -export default class DestinationDefinitionResource - extends BaseResource - implements DestinationDefinition { - readonly destinationDefinitionId: string = ""; - readonly name: string = ""; - readonly dockerRepository: string = ""; - readonly dockerImageTag: string = ""; - readonly latestDockerImageTag: string = ""; - readonly documentationUrl: string = ""; - readonly icon: string = ""; - - pk(): string { - return this.destinationDefinitionId?.toString(); - } - - static urlRoot = "destination_definitions"; - - static listShape( - this: T - ): ReadShape< - SchemaDetail<{ destinationDefinitions: DestinationDefinition[] }> - > { - return { - ...super.listShape(), - fetch: async ( - params: Readonly> - ): Promise<{ destinationDefinitions: DestinationDefinition[] }> => { - const definition = await this.fetch( - "post", - `${this.url(params)}/list`, - params - ); - const latestDefinition = await this.fetch( - "post", - `${this.url(params)}/list_latest`, - params - ); - - const result: DestinationDefinition[] = definition.destinationDefinitions.map( - (destination: DestinationDefinition) => { - const withLatest = latestDefinition.destinationDefinitions.find( - (latestDestination: DestinationDefinition) => - latestDestination.destinationDefinitionId === - destination.destinationDefinitionId - ); - - return { - ...destination, - latestDockerImageTag: withLatest?.dockerImageTag, - }; - } - ); - - return { destinationDefinitions: result }; - }, - schema: { destinationDefinitions: [this] }, - }; - } - - static detailShape( - this: T - ): ReadShape> { - return { - ...super.detailShape(), - schema: this, - }; - } - - static updateShape( - this: T - ): MutateShape> { - return { - ...super.partialUpdateShape(), - fetch( - _: Readonly>, - body: DestinationDefinition - ): Promise { - return getService( - "DestinationDefinitionService" - ).update(body); - }, - schema: this, - }; - } - - static createShape( - this: T - ): MutateShape> { - return { - ...super.createShape(), - schema: this, - }; - } -} diff --git a/airbyte-webapp/src/core/resources/SourceDefinition.ts b/airbyte-webapp/src/core/resources/SourceDefinition.ts deleted file mode 100644 index 4c71a70b4bd67..0000000000000 --- a/airbyte-webapp/src/core/resources/SourceDefinition.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { MutateShape, ReadShape, Resource, SchemaDetail } from "rest-hooks"; - -import BaseResource from "./BaseResource"; -import { getService } from "core/servicesProvider"; -import { SourceDefinitionService } from "core/domain/connector/SourceDefinitionService"; -import { SourceDefinition } from "core/domain/connector"; - -export default class SourceDefinitionResource - extends BaseResource - implements SourceDefinition { - readonly sourceDefinitionId: string = ""; - readonly name: string = ""; - readonly dockerRepository: string = ""; - readonly dockerImageTag: string = ""; - readonly latestDockerImageTag: string = ""; - readonly documentationUrl: string = ""; - readonly icon: string = ""; - - pk(): string { - return this.sourceDefinitionId?.toString(); - } - - static urlRoot = "source_definitions"; - - static listShape( - this: T - ): ReadShape> { - return { - ...super.listShape(), - fetch: async ( - params: Readonly> - ): Promise<{ sourceDefinitions: SourceDefinition[] }> => { - const [definition, latestDefinition] = await Promise.all([ - this.fetch("post", `${this.url(params)}/list`, params), - this.fetch("post", `${this.url(params)}/list_latest`, params), - ]); - - const result: SourceDefinition[] = definition.sourceDefinitions.map( - (source: SourceDefinition) => { - const withLatest = latestDefinition.sourceDefinitions.find( - (latestSource: SourceDefinition) => - latestSource.sourceDefinitionId === source.sourceDefinitionId - ); - - return { - ...source, - latestDockerImageTag: withLatest?.dockerImageTag, - }; - } - ); - - return { sourceDefinitions: result }; - }, - schema: { sourceDefinitions: [this] }, - }; - } - - static detailShape( - this: T - ): ReadShape> { - return { - ...super.detailShape(), - schema: this, - }; - } - - static updateShape( - this: T - ): MutateShape> { - return { - ...super.partialUpdateShape(), - fetch( - _: Readonly>, - body: SourceDefinition - ): Promise { - return getService( - "SourceDefinitionService" - ).update(body); - }, - schema: this, - }; - } - - static createShape( - this: T - ): MutateShape> { - return { - ...super.createShape(), - schema: this, - }; - } -} diff --git a/airbyte-webapp/src/hooks/services/useConnector.test.tsx b/airbyte-webapp/src/hooks/services/useConnector.test.tsx index 4b789d787a61a..55ddbe163b4b5 100644 --- a/airbyte-webapp/src/hooks/services/useConnector.test.tsx +++ b/airbyte-webapp/src/hooks/services/useConnector.test.tsx @@ -1,93 +1,71 @@ -import { makeCacheProvider, makeRenderRestHook } from "@rest-hooks/test"; -import { act } from "@testing-library/react-hooks"; - +import { act, renderHook } from "@testing-library/react-hooks"; import useConnector from "./useConnector"; -import SourceDefinitionResource from "core/resources/SourceDefinition"; -import DestinationDefinitionResource from "core/resources/DestinationDefinition"; -jest.mock("hooks/services/useWorkspace", () => ({ - useWorkspace: () => ({ - workspace: { - workspaceId: "workspaceId", - }, +jest.mock("services/connector/SourceDefinitionService", () => ({ + useSourceDefinitionList: () => ({ + sourceDefinitions: [ + { + sourceDefinitionId: "sid1", + latestDockerImageTag: "0.0.2", + dockerImageTag: "0.0.1", + }, + { + sourceDefinitionId: "sid2", + latestDockerImageTag: "", + dockerImageTag: "0.0.1", + }, + ], + }), + useUpdateSourceDefinition: () => ({ + mutateAsync: jest.fn(), }), })); -const renderRestHook = makeRenderRestHook(makeCacheProvider); -const results = [ - { - request: SourceDefinitionResource.listShape(), - params: { workspaceId: "workspaceId" }, - result: { - sourceDefinitions: [ - { - sourceDefinitionId: "sid1", - latestDockerImageTag: "0.0.2", - dockerImageTag: "0.0.1", - }, - { - sourceDefinitionId: "sid2", - latestDockerImageTag: "", - dockerImageTag: "0.0.1", - }, - ], - }, - }, - { - request: DestinationDefinitionResource.listShape(), - params: { workspaceId: "workspaceId" }, - result: { - destinationDefinitions: [ - { - destinationDefinitionId: "did1", - latestDockerImageTag: "0.0.2", - dockerImageTag: "0.0.1", - }, - { - destinationDefinitionId: "did2", - latestDockerImageTag: "", - dockerImageTag: "0.0.1", - }, - ], - }, - }, -]; +jest.mock("services/connector/DestinationDefinitionService", () => ({ + useDestinationDefinitionList: () => ({ + destinationDefinitions: [ + { + destinationDefinitionId: "sid1", + latestDockerImageTag: "0.0.2", + dockerImageTag: "0.0.1", + }, + { + destinationDefinitionId: "sid2", + latestDockerImageTag: "", + dockerImageTag: "0.0.1", + }, + ], + }), + useUpdateDestinationDefinition: () => ({ + mutateAsync: jest.fn(), + }), +})); test.skip("should not call sourceDefinition.updateVersion for deprecated call", async () => { - const { result, waitForNextUpdate } = renderRestHook(() => useConnector(), { - results, - }); - - // (sourceDefinitionService.update as jest.Mock).mockResolvedValue([]); + const { result, waitForNextUpdate } = renderHook(() => useConnector()); act(() => { result.current.updateAllSourceVersions(); }); await waitForNextUpdate(); - - // expect(sourceDefinitionService.update).toHaveBeenCalledTimes(1); - // expect(sourceDefinitionService.update).toHaveBeenCalledWith({ + // expect(updateSourceMock).toHaveBeenCalledTimes(1); + // expect(updateSourceMock).toHaveBeenCalledWith({ // dockerImageTag: "0.0.2", // sourceDefinitionId: "sid1", // }); }); test.skip("should not call destinationDefinition.updateVersion for deprecated call", async () => { - const { result, waitForNextUpdate } = renderRestHook(() => useConnector(), { - results, - }); - - // (destinationDefinitionService.update as jest.Mock).mockResolvedValue([]); + const { result, waitForNextUpdate } = renderHook(() => useConnector()); act(() => { result.current.updateAllDestinationVersions(); }); await waitForNextUpdate(); - - // expect(destinationDefinitionService.update).toHaveBeenCalledTimes(1); - // expect(destinationDefinitionService.update).toHaveBeenCalledWith({ + // expect(updateDestinationMock).toHaveBeenCalledTimes(1); + // expect(updateDestinationMock).toHaveBeenCalledWith({ // dockerImageTag: "0.0.2", // destinationDefinitionId: "did1", // }); diff --git a/airbyte-webapp/src/hooks/services/useConnector.tsx b/airbyte-webapp/src/hooks/services/useConnector.tsx index 236bc3d388fb1..922137d2ab705 100644 --- a/airbyte-webapp/src/hooks/services/useConnector.tsx +++ b/airbyte-webapp/src/hooks/services/useConnector.tsx @@ -1,11 +1,14 @@ -import { useFetcher } from "rest-hooks"; import { useMemo } from "react"; -import SourceDefinitionResource from "core/resources/SourceDefinition"; -import DestinationDefinitionResource from "core/resources/DestinationDefinition"; import { Connector } from "core/domain/connector"; -import { useSourceDefinitionList } from "./useSourceDefinition"; -import { useDestinationDefinitionList } from "./useDestinationDefinition"; +import { + useSourceDefinitionList, + useUpdateSourceDefinition, +} from "services/connector/SourceDefinitionService"; +import { + useDestinationDefinitionList, + useUpdateDestinationDefinition, +} from "services/connector/DestinationDefinitionService"; type ConnectorService = { hasNewVersions: boolean; @@ -13,20 +16,18 @@ type ConnectorService = { hasNewDestinationVersion: boolean; countNewSourceVersion: number; countNewDestinationVersion: number; - updateAllSourceVersions: () => void; - updateAllDestinationVersions: () => void; + updateAllSourceVersions: () => unknown; + updateAllDestinationVersions: () => unknown; }; const useConnector = (): ConnectorService => { const { sourceDefinitions } = useSourceDefinitionList(); const { destinationDefinitions } = useDestinationDefinitionList(); - const updateSourceDefinition = useFetcher( - SourceDefinitionResource.updateShape() - ); - const updateDestinationDefinition = useFetcher( - DestinationDefinitionResource.updateShape() - ); + const { mutateAsync: updateSourceDefinition } = useUpdateSourceDefinition(); + const { + mutateAsync: updateDestinationDefinition, + } = useUpdateDestinationDefinition(); const newSourceDefinitions = useMemo( () => sourceDefinitions.filter(Connector.hasNewerVersion), @@ -41,13 +42,10 @@ const useConnector = (): ConnectorService => { const updateAllSourceVersions = async () => { await Promise.all( newSourceDefinitions?.map((item) => - updateSourceDefinition( - {}, - { - sourceDefinitionId: item.sourceDefinitionId, - dockerImageTag: item.latestDockerImageTag, - } - ) + updateSourceDefinition({ + sourceDefinitionId: item.sourceDefinitionId, + dockerImageTag: item.latestDockerImageTag, + }) ) ); }; @@ -55,13 +53,10 @@ const useConnector = (): ConnectorService => { const updateAllDestinationVersions = async () => { await Promise.all( newDestinationDefinitions?.map((item) => - updateDestinationDefinition( - {}, - { - destinationDefinitionId: item.destinationDefinitionId, - dockerImageTag: item.latestDockerImageTag, - } - ) + updateDestinationDefinition({ + destinationDefinitionId: item.destinationDefinitionId, + dockerImageTag: item.latestDockerImageTag, + }) ) ); }; diff --git a/airbyte-webapp/src/hooks/services/useDestinationDefinition.tsx b/airbyte-webapp/src/hooks/services/useDestinationDefinition.tsx deleted file mode 100644 index 97a56e145900f..0000000000000 --- a/airbyte-webapp/src/hooks/services/useDestinationDefinition.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { useResource } from "rest-hooks"; - -import DestinationDefinitionResource from "core/resources/DestinationDefinition"; -import { DestinationDefinition } from "core/domain/connector"; -import { useCurrentWorkspace } from "./useWorkspace"; - -const useDestinationDefinitionList = (): { - destinationDefinitions: DestinationDefinition[]; -} => { - const workspace = useCurrentWorkspace(); - return useResource(DestinationDefinitionResource.listShape(), { - workspaceId: workspace.workspaceId, - }); -}; - -const useDestinationDefinition = (id: string): DestinationDefinition => { - return useResource(DestinationDefinitionResource.detailShape(), { - destinationDefinitionId: id, - }); -}; - -export { useDestinationDefinitionList, useDestinationDefinition }; diff --git a/airbyte-webapp/src/hooks/services/useSourceDefinition.tsx b/airbyte-webapp/src/hooks/services/useSourceDefinition.tsx deleted file mode 100644 index e5d21fad9fa3a..0000000000000 --- a/airbyte-webapp/src/hooks/services/useSourceDefinition.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { useResource } from "rest-hooks"; - -import SourceDefinitionResource from "core/resources/SourceDefinition"; -import { SourceDefinition } from "core/domain/connector"; -import { useCurrentWorkspace } from "./useWorkspace"; - -const useSourceDefinitionList = (): { - sourceDefinitions: SourceDefinition[]; -} => { - const workspace = useCurrentWorkspace(); - return useResource(SourceDefinitionResource.listShape(), { - workspaceId: workspace.workspaceId, - }); -}; - -const useSourceDefinition = (id: string): SourceDefinition => { - return useResource(SourceDefinitionResource.detailShape(), { - sourceDefinitionId: id, - }); -}; - -export { useSourceDefinitionList, useSourceDefinition }; diff --git a/airbyte-webapp/src/packages/cloud/views/credits/CreditsPage/components/UsagePerConnectionTable.tsx b/airbyte-webapp/src/packages/cloud/views/credits/CreditsPage/components/UsagePerConnectionTable.tsx index d16287072e567..8160851a0e66e 100644 --- a/airbyte-webapp/src/packages/cloud/views/credits/CreditsPage/components/UsagePerConnectionTable.tsx +++ b/airbyte-webapp/src/packages/cloud/views/credits/CreditsPage/components/UsagePerConnectionTable.tsx @@ -11,8 +11,8 @@ import SortButton from "components/EntityTable/components/SortButton"; import useRouter from "hooks/useRouter"; import ConnectionCell from "./ConnectionCell"; import UsageCell from "./UsageCell"; -import { useSourceDefinitionList } from "hooks/services/useSourceDefinition"; -import { useDestinationDefinitionList } from "hooks/services/useDestinationDefinition"; +import { useSourceDefinitionList } from "services/connector/SourceDefinitionService"; +import { useDestinationDefinitionList } from "services/connector/DestinationDefinitionService"; const Content = styled.div` padding: 0 60px 0 15px; diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/AllConnectionsPage/components/ConnectionsTable.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/AllConnectionsPage/components/ConnectionsTable.tsx index a31e0421f2031..083fd257feb30 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/AllConnectionsPage/components/ConnectionsTable.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/AllConnectionsPage/components/ConnectionsTable.tsx @@ -6,8 +6,8 @@ import { Connection } from "core/domain/connection"; import useSyncActions from "components/EntityTable/hooks"; import { getConnectionTableData } from "components/EntityTable/utils"; import { ITableDataItem } from "components/EntityTable/types"; -import { useDestinationDefinitionList } from "hooks/services/useDestinationDefinition"; -import { useSourceDefinitionList } from "hooks/services/useSourceDefinition"; +import { useDestinationDefinitionList } from "services/connector/DestinationDefinitionService"; +import { useSourceDefinitionList } from "services/connector/SourceDefinitionService"; type IProps = { connections: Connection[]; diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/StatusView.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/StatusView.tsx index 3ebd1096ca2c0..69df89432c97e 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/StatusView.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/StatusView.tsx @@ -3,7 +3,6 @@ import { FormattedMessage } from "react-intl"; import styled from "styled-components"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faRedoAlt } from "@fortawesome/free-solid-svg-icons"; -import { useResource } from "rest-hooks"; import { useListJobs } from "services/job/JobService"; @@ -18,8 +17,8 @@ import { useSyncConnection, } from "hooks/services/useConnectionHook"; import useLoadingState from "hooks/useLoadingState"; -import SourceDefinitionResource from "core/resources/SourceDefinition"; -import DestinationDefinitionResource from "core/resources/DestinationDefinition"; +import { useSourceDefinition } from "services/connector/SourceDefinitionService"; +import { useDestinationDefinition } from "services/connector/DestinationDefinitionService"; type IProps = { connection: Connection; @@ -57,23 +56,12 @@ const StatusView: React.FC = ({ connection, frequencyText }) => { const [isModalOpen, setIsModalOpen] = useState(false); const { isLoading, showFeedback, startAction } = useLoadingState(); - const sourceDefinition = useResource( - SourceDefinitionResource.detailShape(), - connection.source - ? { - sourceDefinitionId: connection.source.sourceDefinitionId, - } - : null + const sourceDefinition = useSourceDefinition( + connection?.source.sourceDefinitionId ); - const destinationDefinition = useResource( - DestinationDefinitionResource.detailShape(), - connection.destination - ? { - destinationDefinitionId: - connection.destination.destinationDefinitionId, - } - : null + const destinationDefinition = useDestinationDefinition( + connection.destination.destinationDefinitionId ); const jobs = useListJobs({ diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/CreationFormPage.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/CreationFormPage.tsx index 46332c65504ef..d6ae245ab276a 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/CreationFormPage.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/CreationFormPage.tsx @@ -18,8 +18,6 @@ import DestinationForm from "./components/DestinationForm"; import SourceResource from "core/resources/Source"; import DestinationResource from "core/resources/Destination"; -import DestinationDefinitionResource from "core/resources/DestinationDefinition"; -import SourceDefinitionResource from "core/resources/SourceDefinition"; import { Destination, DestinationDefinition, @@ -27,6 +25,8 @@ import { SourceDefinition, } from "core/domain/connector"; import { Connection } from "core/domain/connection"; +import { useSourceDefinition } from "services/connector/SourceDefinitionService"; +import { useDestinationDefinition } from "services/connector/DestinationDefinitionService"; export enum StepsTypes { CREATE_ENTITY = "createEntity", @@ -75,14 +75,7 @@ function usePreloadData(): { : null ); - const sourceDefinition = useResource( - SourceDefinitionResource.detailShape(), - source - ? { - sourceDefinitionId: source.sourceDefinitionId, - } - : null - ); + const sourceDefinition = useSourceDefinition(source?.sourceDefinitionId); const destination = useResource( DestinationResource.detailShape(), @@ -92,13 +85,8 @@ function usePreloadData(): { } : null ); - const destinationDefinition = useResource( - DestinationDefinitionResource.detailShape(), - destination - ? { - destinationDefinitionId: destination.destinationDefinitionId, - } - : null + const destinationDefinition = useDestinationDefinition( + destination?.destinationDefinitionId ); return { source, sourceDefinition, destination, destinationDefinition }; diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/DestinationForm.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/DestinationForm.tsx index 3de7523d9f002..661c9708b5680 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/DestinationForm.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/DestinationForm.tsx @@ -1,14 +1,12 @@ import React, { useState } from "react"; -import { useResource } from "rest-hooks"; import useRouter from "hooks/useRouter"; -import DestinationDefinitionResource from "core/resources/DestinationDefinition"; import useDestination from "hooks/services/useDestinationHook"; // TODO: create separate component for source and destinations forms import DestinationForm from "pages/DestinationPage/pages/CreateDestinationPage/components/DestinationForm"; import { ConnectionConfiguration } from "core/domain/connection"; -import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; +import { useDestinationDefinitionList } from "services/connector/DestinationDefinitionService"; type IProps = { afterSubmit: () => void; @@ -16,16 +14,10 @@ type IProps = { const CreateDestinationPage: React.FC = ({ afterSubmit }) => { const { push, location } = useRouter(); - const workspace = useCurrentWorkspace(); const [successRequest, setSuccessRequest] = useState(false); const [errorStatusRequest, setErrorStatusRequest] = useState(null); - const { destinationDefinitions } = useResource( - DestinationDefinitionResource.listShape(), - { - workspaceId: workspace.workspaceId, - } - ); + const { destinationDefinitions } = useDestinationDefinitionList(); const { createDestination } = useDestination(); const onSubmitDestinationForm = async (values: { diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/ExistingEntityForm.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/ExistingEntityForm.tsx index a5b2636d750a1..778c24d15c60a 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/ExistingEntityForm.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/ExistingEntityForm.tsx @@ -2,17 +2,17 @@ import React, { useMemo } from "react"; import styled from "styled-components"; import { FormattedMessage, useIntl } from "react-intl"; import { useResource } from "rest-hooks"; -import { Formik, Form, Field, FieldProps } from "formik"; +import { Field, FieldProps, Form, Formik } from "formik"; import * as yup from "yup"; import ContentCard from "components/ContentCard"; -import { DropDown, Button, ControlLabels } from "components"; +import { Button, ControlLabels, DropDown } from "components"; import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import SourceResource from "core/resources/Source"; -import SourceDefinitionResource from "core/resources/SourceDefinition"; import DestinationResource from "core/resources/Destination"; -import DestinationDefinitionResource from "core/resources/DestinationDefinition"; import ImageBlock from "components/ImageBlock"; +import { useSourceDefinitionList } from "services/connector/SourceDefinitionService"; +import { useDestinationDefinitionList } from "services/connector/DestinationDefinitionService"; type IProps = { type: "source" | "destination"; @@ -46,22 +46,13 @@ const ExistingEntityForm: React.FC = ({ type, onSubmit }) => { const { sources } = useResource(SourceResource.listShape(), { workspaceId: workspace.workspaceId, }); - const { sourceDefinitions } = useResource( - SourceDefinitionResource.listShape(), - { - workspaceId: workspace.workspaceId, - } - ); + const { sourceDefinitions } = useSourceDefinitionList(); const { destinations } = useResource(DestinationResource.listShape(), { workspaceId: workspace.workspaceId, }); - const { destinationDefinitions } = useResource( - DestinationDefinitionResource.listShape(), - { - workspaceId: workspace.workspaceId, - } - ); + + const { destinationDefinitions } = useDestinationDefinitionList(); const dropDownData = useMemo(() => { if (type === "source") { diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/SourceForm.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/SourceForm.tsx index aef1675f941bd..08386afac6eff 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/SourceForm.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/SourceForm.tsx @@ -1,14 +1,12 @@ import React, { useState } from "react"; -import { useResource } from "rest-hooks"; import useRouter from "hooks/useRouter"; -import SourceDefinitionResource from "core/resources/SourceDefinition"; import useSource from "hooks/services/useSourceHook"; // TODO: create separate component for source and destinations forms import SourceForm from "pages/SourcesPage/pages/CreateSourcePage/components/SourceForm"; import { ConnectionConfiguration } from "core/domain/connection"; -import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; +import { useSourceDefinitionList } from "services/connector/SourceDefinitionService"; type IProps = { afterSubmit: () => void; @@ -18,13 +16,8 @@ const SourceFormComponent: React.FC = ({ afterSubmit }) => { const { push, location } = useRouter(); const [successRequest, setSuccessRequest] = useState(false); const [errorStatusRequest, setErrorStatusRequest] = useState(null); - const workspace = useCurrentWorkspace(); - const { sourceDefinitions } = useResource( - SourceDefinitionResource.listShape(), - { - workspaceId: workspace.workspaceId, - } - ); + + const { sourceDefinitions } = useSourceDefinitionList(); const { createSource } = useSource(); const onSubmitSourceStep = async (values: { diff --git a/airbyte-webapp/src/pages/DestinationPage/pages/AllDestinationsPage/components/DestinationsTable.tsx b/airbyte-webapp/src/pages/DestinationPage/pages/AllDestinationsPage/components/DestinationsTable.tsx index 0ad6d1b0f22db..9741be915c02d 100644 --- a/airbyte-webapp/src/pages/DestinationPage/pages/AllDestinationsPage/components/DestinationsTable.tsx +++ b/airbyte-webapp/src/pages/DestinationPage/pages/AllDestinationsPage/components/DestinationsTable.tsx @@ -4,9 +4,9 @@ import { ImplementationTable } from "components/EntityTable"; import { getEntityTableData } from "components/EntityTable/utils"; import { EntityTableDataItem } from "components/EntityTable/types"; import useRouter from "hooks/useRouter"; -import { useDestinationDefinitionList } from "hooks/services/useDestinationDefinition"; import { useConnectionList } from "hooks/services/useConnectionHook"; import { Destination } from "core/domain/connector"; +import { useDestinationDefinitionList } from "services/connector/DestinationDefinitionService"; type IProps = { destinations: Destination[]; diff --git a/airbyte-webapp/src/pages/DestinationPage/pages/CreateDestinationPage/CreateDestinationPage.tsx b/airbyte-webapp/src/pages/DestinationPage/pages/CreateDestinationPage/CreateDestinationPage.tsx index f4969d7e55b7a..7f8d5e9b552f6 100644 --- a/airbyte-webapp/src/pages/DestinationPage/pages/CreateDestinationPage/CreateDestinationPage.tsx +++ b/airbyte-webapp/src/pages/DestinationPage/pages/CreateDestinationPage/CreateDestinationPage.tsx @@ -1,32 +1,25 @@ import React, { useState } from "react"; import { FormattedMessage } from "react-intl"; -import { useResource } from "rest-hooks"; + import PageTitle from "components/PageTitle"; import DestinationForm from "./components/DestinationForm"; import useRouter from "hooks/useRouter"; -import DestinationDefinitionResource from "core/resources/DestinationDefinition"; import useDestination from "hooks/services/useDestinationHook"; import { FormPageContent } from "components/ConnectorBlocks"; import { ConnectionConfiguration } from "core/domain/connection"; import HeadTitle from "components/HeadTitle"; -import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; -import { JobInfo } from "../../../../core/domain/job/Job"; +import { JobInfo } from "core/domain/job"; +import { useDestinationDefinitionList } from "services/connector/DestinationDefinitionService"; const CreateDestinationPage: React.FC = () => { const { push } = useRouter(); - const workspace = useCurrentWorkspace(); const [successRequest, setSuccessRequest] = useState(false); const [errorStatusRequest, setErrorStatusRequest] = useState<{ status: number; response: JobInfo; } | null>(null); - const { destinationDefinitions } = useResource( - DestinationDefinitionResource.listShape(), - { - workspaceId: workspace.workspaceId, - } - ); + const { destinationDefinitions } = useDestinationDefinitionList(); const { createDestination } = useDestination(); const onSubmitDestinationForm = async (values: { diff --git a/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/DestinationItemPage.tsx b/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/DestinationItemPage.tsx index f51f5c3154b6c..6197d78ab844c 100644 --- a/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/DestinationItemPage.tsx +++ b/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/DestinationItemPage.tsx @@ -2,7 +2,6 @@ import React, { Suspense, useMemo, useState } from "react"; import { FormattedMessage } from "react-intl"; import { useResource } from "rest-hooks"; -import PageTitle from "components/PageTitle"; import useRouter from "hooks/useRouter"; import Placeholder, { ResourceTypes } from "components/Placeholder"; import Breadcrumbs from "components/Breadcrumbs"; @@ -14,18 +13,21 @@ import { TableItemTitle, } from "components/ConnectorBlocks"; import DestinationSettings from "./components/DestinationSettings"; -import LoadingPage from "components/LoadingPage"; import SourceResource from "core/resources/Source"; -import MainPageWithScroll from "components/MainPageWithScroll"; -import DestinationDefinitionResource from "core/resources/DestinationDefinition"; import { getIcon } from "utils/imageUtils"; -import ImageBlock from "components/ImageBlock"; -import SourceDefinitionResource from "core/resources/SourceDefinition"; import HeadTitle from "components/HeadTitle"; import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; -import { DropDownRow } from "components"; -import { RoutePaths } from "../../../routePaths"; +import { + DropDownRow, + MainPageWithScroll, + LoadingPage, + ImageBlock, + PageTitle, +} from "components"; +import { RoutePaths } from "pages/routePaths"; import { useConnectionList } from "hooks/services/useConnectionHook"; +import { useSourceDefinitionList } from "services/connector/SourceDefinitionService"; +import { useDestinationDefinition } from "services/connector/DestinationDefinitionService"; const DestinationItemPage: React.FC = () => { const { params, push } = useRouter(); @@ -37,22 +39,14 @@ const DestinationItemPage: React.FC = () => { workspaceId: workspace.workspaceId, }); - const { sourceDefinitions } = useResource( - SourceDefinitionResource.listShape(), - { - workspaceId: workspace.workspaceId, - } - ); + const { sourceDefinitions } = useSourceDefinitionList(); const destination = useResource(DestinationResource.detailShape(), { destinationId: params.id, }); - const destinationDefinition = useResource( - DestinationDefinitionResource.detailShape(), - { - destinationDefinitionId: destination.destinationDefinitionId, - } + const destinationDefinition = useDestinationDefinition( + destination.destinationDefinitionId ); const { connections } = useConnectionList(); diff --git a/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationConnectionTable.tsx b/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationConnectionTable.tsx index 26f817e6d30cd..0186fd939ec7d 100644 --- a/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationConnectionTable.tsx +++ b/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationConnectionTable.tsx @@ -1,5 +1,4 @@ import React, { useCallback } from "react"; -import { useResource } from "rest-hooks"; import { ConnectionTable } from "components/EntityTable"; import useRouter from "hooks/useRouter"; @@ -7,10 +6,9 @@ import { Connection } from "core/domain/connection"; import useSyncActions from "components/EntityTable/hooks"; import { getConnectionTableData } from "components/EntityTable/utils"; import { ITableDataItem } from "components/EntityTable/types"; -import SourceDefinitionResource from "core/resources/SourceDefinition"; -import DestinationDefinitionResource from "core/resources/DestinationDefinition"; -import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; -import { RoutePaths } from "../../../../routePaths"; +import { RoutePaths } from "pages/routePaths"; +import { useSourceDefinitionList } from "services/connector/SourceDefinitionService"; +import { useDestinationDefinitionList } from "services/connector/DestinationDefinitionService"; type IProps = { connections: Connection[]; @@ -18,22 +16,10 @@ type IProps = { const DestinationConnectionTable: React.FC = ({ connections }) => { const { push } = useRouter(); - const workspace = useCurrentWorkspace(); const { changeStatus, syncManualConnection } = useSyncActions(); - const { sourceDefinitions } = useResource( - SourceDefinitionResource.listShape(), - { - workspaceId: workspace.workspaceId, - } - ); - - const { destinationDefinitions } = useResource( - DestinationDefinitionResource.listShape(), - { - workspaceId: workspace.workspaceId, - } - ); + const { sourceDefinitions } = useSourceDefinitionList(); + const { destinationDefinitions } = useDestinationDefinitionList(); const data = getConnectionTableData( connections, diff --git a/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationSettings.tsx b/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationSettings.tsx index 5c1031d9087d9..59e5c27e8e512 100644 --- a/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationSettings.tsx +++ b/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationSettings.tsx @@ -1,19 +1,17 @@ import React, { useState } from "react"; import { FormattedMessage } from "react-intl"; import styled from "styled-components"; -import { useResource } from "rest-hooks"; import DeleteBlock from "components/DeleteBlock"; import useDestination from "hooks/services/useDestinationHook"; -import { Connection } from "core/domain/connection"; -import { ConnectionConfiguration } from "core/domain/connection"; -import DestinationDefinitionResource from "core/resources/DestinationDefinition"; +import { Connection, ConnectionConfiguration } from "core/domain/connection"; import { createFormErrorMessage } from "utils/errorStatusMessage"; import { LogsRequestError } from "core/request/LogsRequestError"; import { ConnectorCard } from "views/Connector/ConnectorCard"; import { Connector, Destination } from "core/domain/connector"; import { useGetDestinationDefinitionSpecification } from "services/connector/DestinationDefinitionSpecificationService"; +import { useDestinationDefinition } from "services/connector/DestinationDefinitionService"; const Content = styled.div` width: 100%; @@ -39,11 +37,8 @@ const DestinationsSettings: React.FC = ({ currentDestination.destinationDefinitionId ); - const destinationDefinition = useResource( - DestinationDefinitionResource.detailShape(), - { - destinationDefinitionId: currentDestination.destinationDefinitionId, - } + const destinationDefinition = useDestinationDefinition( + currentDestination.destinationDefinitionId ); const { diff --git a/airbyte-webapp/src/pages/OnboardingPage/OnboardingPage.tsx b/airbyte-webapp/src/pages/OnboardingPage/OnboardingPage.tsx index 6f7fb372f4976..96ecf141eb773 100644 --- a/airbyte-webapp/src/pages/OnboardingPage/OnboardingPage.tsx +++ b/airbyte-webapp/src/pages/OnboardingPage/OnboardingPage.tsx @@ -1,6 +1,5 @@ import React, { Suspense, useEffect, useState } from "react"; import styled from "styled-components"; -import { useResource } from "rest-hooks"; import { FormattedMessage } from "react-intl"; import { Button } from "components"; @@ -14,8 +13,6 @@ import { useSyncConnection, } from "hooks/services/useConnectionHook"; import { ConnectionConfiguration } from "core/domain/connection"; -import SourceDefinitionResource from "core/resources/SourceDefinition"; -import DestinationDefinitionResource from "core/resources/DestinationDefinition"; import useGetStepsConfig from "./useStepsConfig"; import SourceStep from "./components/SourceStep"; import DestinationStep from "./components/DestinationStep"; @@ -31,6 +28,8 @@ import useWorkspace from "hooks/services/useWorkspace"; import useRouterHook from "hooks/useRouter"; import { JobInfo } from "core/domain/job/Job"; import { RoutePaths } from "../routePaths"; +import { useSourceDefinitionList } from "services/connector/SourceDefinitionService"; +import { useDestinationDefinitionList } from "services/connector/DestinationDefinitionService"; const Content = styled.div<{ big?: boolean; medium?: boolean }>` width: 100%; @@ -72,15 +71,10 @@ const OnboardingPage: React.FC = () => { const { sources } = useSourceList(); const { destinations } = useDestinationList(); const { connections } = useConnectionList(); + const { sourceDefinitions } = useSourceDefinitionList(); + const { destinationDefinitions } = useDestinationDefinitionList(); + const { mutateAsync: syncConnection } = useSyncConnection(); - const { sourceDefinitions } = useResource( - SourceDefinitionResource.listShape(), - {} - ); - const { destinationDefinitions } = useResource( - DestinationDefinitionResource.listShape(), - {} - ); const { createSource } = useSource(); const { createDestination } = useDestination(); diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/DestinationsPage.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/DestinationsPage.tsx index a948b3ccf704d..9b962775824cd 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/DestinationsPage.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/DestinationsPage.tsx @@ -1,47 +1,42 @@ import React, { useCallback, useMemo, useState } from "react"; import { useIntl } from "react-intl"; -import { useFetcher, useResource } from "rest-hooks"; +import { useResource } from "rest-hooks"; import { useAsyncFn } from "react-use"; -import DestinationDefinitionResource from "core/resources/DestinationDefinition"; import { DestinationResource } from "core/resources/Destination"; import useConnector from "hooks/services/useConnector"; import ConnectorsView from "./components/ConnectorsView"; import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { DestinationDefinition } from "core/domain/connector"; +import { + useDestinationDefinitionList, + useUpdateDestinationDefinition, +} from "services/connector/DestinationDefinitionService"; const DestinationsPage: React.FC = () => { const workspace = useCurrentWorkspace(); const [isUpdateSuccess, setIsUpdateSuccess] = useState(false); const formatMessage = useIntl().formatMessage; - const { destinationDefinitions } = useResource( - DestinationDefinitionResource.listShape(), - { - workspaceId: workspace.workspaceId, - } - ); + const { destinationDefinitions } = useDestinationDefinitionList(); const { destinations } = useResource(DestinationResource.listShape(), { workspaceId: workspace.workspaceId, }); const [feedbackList, setFeedbackList] = useState>({}); - const updateDestinationDefinition = useFetcher( - DestinationDefinitionResource.updateShape() - ); + const { + mutateAsync: updateDestinationDefinition, + } = useUpdateDestinationDefinition(); const { hasNewDestinationVersion } = useConnector(); const onUpdateVersion = useCallback( async ({ id, version }: { id: string; version: string }) => { try { - await updateDestinationDefinition( - {}, - { - destinationDefinitionId: id, - dockerImageTag: version, - } - ); + await updateDestinationDefinition({ + destinationDefinitionId: id, + dockerImageTag: version, + }); setFeedbackList({ ...feedbackList, [id]: "success" }); } catch (e) { const messageId = diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/SourcesPage.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/SourcesPage.tsx index 36670029fab5f..97e23aa02c703 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/SourcesPage.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/SourcesPage.tsx @@ -1,14 +1,15 @@ import React, { useCallback, useMemo, useState } from "react"; import { useIntl } from "react-intl"; -import { useFetcher } from "rest-hooks"; import { useAsyncFn } from "react-use"; -import SourceDefinitionResource from "core/resources/SourceDefinition"; import useConnector from "hooks/services/useConnector"; import ConnectorsView from "./components/ConnectorsView"; -import { useSourceDefinitionList } from "hooks/services/useSourceDefinition"; import { useSourceList } from "hooks/services/useSourceHook"; import { SourceDefinition } from "core/domain/connector"; +import { + useSourceDefinitionList, + useUpdateSourceDefinition, +} from "services/connector/SourceDefinitionService"; const SourcesPage: React.FC = () => { const [isUpdateSuccess, setIsUpdateSucces] = useState(false); @@ -18,22 +19,17 @@ const SourcesPage: React.FC = () => { const { sources } = useSourceList(); const { sourceDefinitions } = useSourceDefinitionList(); - const updateSourceDefinition = useFetcher( - SourceDefinitionResource.updateShape() - ); + const { mutateAsync: updateSourceDefinition } = useUpdateSourceDefinition(); const { hasNewSourceVersion, updateAllSourceVersions } = useConnector(); const onUpdateVersion = useCallback( async ({ id, version }: { id: string; version: string }) => { try { - await updateSourceDefinition( - {}, - { - sourceDefinitionId: id, - dockerImageTag: version, - } - ); + await updateSourceDefinition({ + sourceDefinitionId: id, + dockerImageTag: version, + }); setFeedbackList({ ...feedbackList, [id]: "success" }); } catch (e) { const messageId = diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/CreateConnector.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/CreateConnector.tsx index faacfdef2cf90..a748efc5e033c 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/CreateConnector.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/CreateConnector.tsx @@ -1,15 +1,13 @@ import React, { useState } from "react"; import { FormattedMessage, useIntl } from "react-intl"; -import { useFetcher } from "rest-hooks"; import { Button } from "components"; -import SourceDefinitionResource from "core/resources/SourceDefinition"; import useRouter from "hooks/useRouter"; -import DestinationDefinitionResource from "core/resources/DestinationDefinition"; import CreateConnectorModal from "./CreateConnectorModal"; -import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; -import { RoutePaths } from "../../../../routePaths"; +import { RoutePaths } from "pages/routePaths"; +import { useCreateSourceDefinition } from "services/connector/SourceDefinitionService"; +import { useCreateDestinationDefinition } from "services/connector/DestinationDefinitionService"; type IProps = { type: string; @@ -24,7 +22,6 @@ type ICreateProps = { const CreateConnector: React.FC = ({ type }) => { const { push } = useRouter(); - const workspace = useCurrentWorkspace(); const [isModalOpen, setIsModalOpen] = useState(false); const [errorMessage, setErrorMessage] = useState(""); const onChangeModalState = () => { @@ -34,28 +31,16 @@ const CreateConnector: React.FC = ({ type }) => { const formatMessage = useIntl().formatMessage; - const createSourceDefinition = useFetcher( - SourceDefinitionResource.createShape() - ); + const { mutateAsync: createSourceDefinition } = useCreateSourceDefinition(); + + const { + mutateAsync: createDestinationDefinition, + } = useCreateDestinationDefinition(); const onSubmitSource = async (sourceDefinition: ICreateProps) => { setErrorMessage(""); try { - const result = await createSourceDefinition({}, sourceDefinition, [ - [ - SourceDefinitionResource.listShape(), - { workspaceId: workspace.workspaceId }, - ( - newSourceDefinitionId: string, - sourceDefinitionIds: { sourceDefinitions: string[] } - ) => ({ - sourceDefinitions: [ - ...sourceDefinitionIds.sourceDefinitions, - newSourceDefinitionId, - ], - }), - ], - ]); + const result = await createSourceDefinition(sourceDefinition); push( { @@ -68,31 +53,10 @@ const CreateConnector: React.FC = ({ type }) => { } }; - const createDestinationDefinition = useFetcher( - DestinationDefinitionResource.createShape() - ); const onSubmitDestination = async (destinationDefinition: ICreateProps) => { setErrorMessage(""); try { - const result = await createDestinationDefinition( - {}, - destinationDefinition, - [ - [ - DestinationDefinitionResource.listShape(), - { workspaceId: workspace.workspaceId }, - ( - newDestinationDefinitionId: string, - destinationDefinitionIds: { destinationDefinitions: string[] } - ) => ({ - destinationDefinitions: [ - ...destinationDefinitionIds.destinationDefinitions, - newDestinationDefinitionId, - ], - }), - ], - ] - ); + const result = await createDestinationDefinition(destinationDefinition); push( { diff --git a/airbyte-webapp/src/pages/SourcesPage/pages/AllSourcesPage/components/SourcesTable.tsx b/airbyte-webapp/src/pages/SourcesPage/pages/AllSourcesPage/components/SourcesTable.tsx index 5467495e647c9..a869f82b6d8f6 100644 --- a/airbyte-webapp/src/pages/SourcesPage/pages/AllSourcesPage/components/SourcesTable.tsx +++ b/airbyte-webapp/src/pages/SourcesPage/pages/AllSourcesPage/components/SourcesTable.tsx @@ -5,8 +5,8 @@ import { getEntityTableData } from "components/EntityTable/utils"; import { EntityTableDataItem } from "components/EntityTable/types"; import useRouter from "hooks/useRouter"; import { useConnectionList } from "hooks/services/useConnectionHook"; -import { useSourceDefinitionList } from "hooks/services/useSourceDefinition"; import { Source } from "core/domain/connector"; +import { useSourceDefinitionList } from "../../../../../services/connector/SourceDefinitionService"; type IProps = { sources: Source[]; diff --git a/airbyte-webapp/src/pages/SourcesPage/pages/CreateSourcePage/CreateSourcePage.tsx b/airbyte-webapp/src/pages/SourcesPage/pages/CreateSourcePage/CreateSourcePage.tsx index 975b7a80fe358..2bd6a90271ee0 100644 --- a/airbyte-webapp/src/pages/SourcesPage/pages/CreateSourcePage/CreateSourcePage.tsx +++ b/airbyte-webapp/src/pages/SourcesPage/pages/CreateSourcePage/CreateSourcePage.tsx @@ -1,17 +1,15 @@ import React, { useState } from "react"; import { FormattedMessage } from "react-intl"; -import { useResource } from "rest-hooks"; import PageTitle from "components/PageTitle"; import SourceForm from "./components/SourceForm"; import useRouter from "hooks/useRouter"; -import SourceDefinitionResource from "core/resources/SourceDefinition"; import useSource from "hooks/services/useSourceHook"; import { FormPageContent } from "components/ConnectorBlocks"; import { ConnectionConfiguration } from "core/domain/connection"; import HeadTitle from "components/HeadTitle"; -import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { JobInfo } from "core/domain/job/Job"; +import { useSourceDefinitionList } from "services/connector/SourceDefinitionService"; const CreateSourcePage: React.FC = () => { const { push } = useRouter(); @@ -21,13 +19,7 @@ const CreateSourcePage: React.FC = () => { response: JobInfo; } | null>(null); - const workspace = useCurrentWorkspace(); - const { sourceDefinitions } = useResource( - SourceDefinitionResource.listShape(), - { - workspaceId: workspace.workspaceId, - } - ); + const { sourceDefinitions } = useSourceDefinitionList(); const { createSource } = useSource(); const onSubmitSourceStep = async (values: { diff --git a/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/SourceItemPage.tsx b/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/SourceItemPage.tsx index cf341669e369c..ddfdc1052dc6c 100644 --- a/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/SourceItemPage.tsx +++ b/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/SourceItemPage.tsx @@ -19,14 +19,14 @@ import SourceSettings from "./components/SourceSettings"; import SourceResource from "core/resources/Source"; import DestinationResource from "core/resources/Destination"; -import SourceDefinitionResource from "core/resources/SourceDefinition"; -import DestinationsDefinitionResource from "core/resources/DestinationDefinition"; import { getIcon } from "utils/imageUtils"; import HeadTitle from "components/HeadTitle"; import Placeholder, { ResourceTypes } from "components/Placeholder"; import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { RoutePaths } from "../../../routePaths"; import { useConnectionList } from "hooks/services/useConnectionHook"; +import { useSourceDefinition } from "services/connector/SourceDefinitionService"; +import { useDestinationDefinitionList } from "services/connector/DestinationDefinitionService"; const SourceItemPage: React.FC = () => { const { query, push } = useRouter<{ id: string }>(); @@ -38,20 +38,13 @@ const SourceItemPage: React.FC = () => { workspaceId: workspace.workspaceId, }); - const { destinationDefinitions } = useResource( - DestinationsDefinitionResource.listShape(), - { - workspaceId: workspace.workspaceId, - } - ); + const { destinationDefinitions } = useDestinationDefinitionList(); const source = useResource(SourceResource.detailShape(), { sourceId: query.id, }); - const sourceDefinition = useResource(SourceDefinitionResource.detailShape(), { - sourceDefinitionId: source.sourceDefinitionId, - }); + const sourceDefinition = useSourceDefinition(source?.sourceDefinitionId); const { connections } = useConnectionList(); diff --git a/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceConnectionTable.tsx b/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceConnectionTable.tsx index a0e101fe5bd63..ea03a05873160 100644 --- a/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceConnectionTable.tsx +++ b/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceConnectionTable.tsx @@ -1,5 +1,4 @@ import React, { useCallback } from "react"; -import { useResource } from "rest-hooks"; import { ConnectionTable } from "components/EntityTable"; import useRouter from "hooks/useRouter"; @@ -7,10 +6,9 @@ import { Connection } from "core/domain/connection"; import useSyncActions from "components/EntityTable/hooks"; import { getConnectionTableData } from "components/EntityTable/utils"; import { ITableDataItem } from "components/EntityTable/types"; -import SourceDefinitionResource from "core/resources/SourceDefinition"; -import DestinationDefinitionResource from "core/resources/DestinationDefinition"; -import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; -import { RoutePaths } from "../../../../routePaths"; +import { RoutePaths } from "pages/routePaths"; +import { useSourceDefinitionList } from "services/connector/SourceDefinitionService"; +import { useDestinationDefinitionList } from "services/connector/DestinationDefinitionService"; type IProps = { connections: Connection[]; @@ -18,22 +16,11 @@ type IProps = { const SourceConnectionTable: React.FC = ({ connections }) => { const { push } = useRouter(); - const workspace = useCurrentWorkspace(); const { changeStatus, syncManualConnection } = useSyncActions(); - const { sourceDefinitions } = useResource( - SourceDefinitionResource.listShape(), - { - workspaceId: workspace.workspaceId, - } - ); + const { sourceDefinitions } = useSourceDefinitionList(); - const { destinationDefinitions } = useResource( - DestinationDefinitionResource.listShape(), - { - workspaceId: workspace.workspaceId, - } - ); + const { destinationDefinitions } = useDestinationDefinitionList(); const data = getConnectionTableData( connections, diff --git a/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceSettings.tsx b/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceSettings.tsx index fc65dc2675d0d..fce6a2c09b041 100644 --- a/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceSettings.tsx +++ b/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceSettings.tsx @@ -1,18 +1,16 @@ import React, { useState } from "react"; import styled from "styled-components"; import { FormattedMessage } from "react-intl"; -import { useResource } from "rest-hooks"; import useSource from "hooks/services/useSourceHook"; import DeleteBlock from "components/DeleteBlock"; -import { Connection } from "core/domain/connection"; +import { Connection, ConnectionConfiguration } from "core/domain/connection"; import { createFormErrorMessage } from "utils/errorStatusMessage"; -import { ConnectionConfiguration } from "core/domain/connection"; -import SourceDefinitionResource from "core/resources/SourceDefinition"; import { LogsRequestError } from "core/request/LogsRequestError"; import { ConnectorCard } from "views/Connector/ConnectorCard"; import { Source } from "core/domain/connector"; import { useGetSourceDefinitionSpecification } from "services/connector/SourceDefinitionSpecificationService"; +import { useSourceDefinition } from "services/connector/SourceDefinitionService"; const Content = styled.div` max-width: 813px; @@ -38,9 +36,10 @@ const SourceSettings: React.FC = ({ const sourceDefinitionSpecification = useGetSourceDefinitionSpecification( currentSource.sourceDefinitionId ); - const sourceDefinition = useResource(SourceDefinitionResource.detailShape(), { - sourceDefinitionId: currentSource.sourceDefinitionId, - }); + + const sourceDefinition = useSourceDefinition( + currentSource?.sourceDefinitionId + ); const onSubmit = async (values: { name: string; diff --git a/airbyte-webapp/src/services/connector/DestinationDefinitionService.ts b/airbyte-webapp/src/services/connector/DestinationDefinitionService.ts new file mode 100644 index 0000000000000..20324d005fdf8 --- /dev/null +++ b/airbyte-webapp/src/services/connector/DestinationDefinitionService.ts @@ -0,0 +1,155 @@ +import { + QueryObserverSuccessResult, + useMutation, + useQuery, + useQueryClient, +} from "react-query"; + +import { DestinationDefinition } from "core/domain/connector"; +import { useConfig } from "config"; +import { useDefaultRequestMiddlewares } from "services/useDefaultRequestMiddlewares"; +import { useInitService } from "services/useInitService"; +import { + CreateDestinationDefinitionPayload, + DestinationDefinitionService, +} from "core/domain/connector/DestinationDefinitionService"; +import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; +import { isDefined } from "utils/common"; + +export const destinationDefinitionKeys = { + all: ["destinationDefinition"] as const, + lists: () => [...destinationDefinitionKeys.all, "list"] as const, + detail: (id: string) => + [...destinationDefinitionKeys.all, "details", id] as const, +}; + +function useGetDestinationDefinitionService(): DestinationDefinitionService { + const { apiUrl } = useConfig(); + + const requestAuthMiddleware = useDefaultRequestMiddlewares(); + + return useInitService( + () => new DestinationDefinitionService(apiUrl, requestAuthMiddleware), + [apiUrl, requestAuthMiddleware] + ); +} + +const useDestinationDefinitionList = (): { + destinationDefinitions: DestinationDefinition[]; +} => { + const service = useGetDestinationDefinitionService(); + const workspace = useCurrentWorkspace(); + + return (useQuery(destinationDefinitionKeys.lists(), async () => { + const [definition, latestDefinition] = await Promise.all([ + service.list(workspace.workspaceId), + service.listLatest(workspace.workspaceId), + ]); + + const destinationDefinitions: DestinationDefinition[] = definition.destinationDefinitions.map( + (destination: DestinationDefinition) => { + const withLatest = latestDefinition.destinationDefinitions.find( + (latestDestination: DestinationDefinition) => + latestDestination.destinationDefinitionId === + destination.destinationDefinitionId + ); + + return { + ...destination, + latestDockerImageTag: withLatest?.dockerImageTag ?? "", + }; + } + ); + + return { destinationDefinitions }; + }) as QueryObserverSuccessResult<{ + destinationDefinitions: DestinationDefinition[]; + }>).data; +}; + +const useDestinationDefinition = ( + id: T +): T extends string + ? DestinationDefinition + : DestinationDefinition | undefined => { + const service = useGetDestinationDefinitionService(); + + return (useQuery( + destinationDefinitionKeys.detail(id || ""), + () => service.get(id || ""), + { + enabled: isDefined(id), + } + ) as QueryObserverSuccessResult).data; +}; + +const useCreateDestinationDefinition = () => { + const service = useGetDestinationDefinitionService(); + const queryClient = useQueryClient(); + + return useMutation< + DestinationDefinition, + Error, + CreateDestinationDefinitionPayload + >((destinationDefinition) => service.create(destinationDefinition), { + onSuccess: (data) => { + queryClient.setQueryData( + destinationDefinitionKeys.lists(), + ( + oldData: + | { destinationDefinitions: DestinationDefinition[] } + | undefined + ) => ({ + destinationDefinitions: [ + data, + ...(oldData?.destinationDefinitions ?? []), + ], + }) + ); + }, + }); +}; + +const useUpdateDestinationDefinition = () => { + const service = useGetDestinationDefinitionService(); + const queryClient = useQueryClient(); + + return useMutation< + DestinationDefinition, + Error, + { + destinationDefinitionId: string; + dockerImageTag: string; + } + >((destinationDefinition) => service.update(destinationDefinition), { + onSuccess: (data) => { + queryClient.setQueryData( + destinationDefinitionKeys.detail(data.destinationDefinitionId), + data + ); + + queryClient.setQueryData( + destinationDefinitionKeys.lists(), + ( + oldData: + | { destinationDefinitions: DestinationDefinition[] } + | undefined + ) => ({ + destinationDefinitions: + oldData?.destinationDefinitions.map((sd) => + sd.destinationDefinitionId === data.destinationDefinitionId + ? data + : sd + ) ?? [], + }) + ); + }, + }); +}; + +export { + useDestinationDefinition, + useDestinationDefinitionList, + useCreateDestinationDefinition, + useUpdateDestinationDefinition, +}; diff --git a/airbyte-webapp/src/services/connector/SourceDefinitionService.ts b/airbyte-webapp/src/services/connector/SourceDefinitionService.ts new file mode 100644 index 0000000000000..8bf57cb5e30ed --- /dev/null +++ b/airbyte-webapp/src/services/connector/SourceDefinitionService.ts @@ -0,0 +1,136 @@ +import { + QueryObserverSuccessResult, + useMutation, + useQuery, + useQueryClient, +} from "react-query"; + +import { SourceDefinition } from "core/domain/connector"; +import { useConfig } from "config"; +import { useDefaultRequestMiddlewares } from "services/useDefaultRequestMiddlewares"; +import { useInitService } from "services/useInitService"; +import { + CreateSourceDefinitionPayload, + SourceDefinitionService, +} from "core/domain/connector/SourceDefinitionService"; +import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; +import { isDefined } from "utils/common"; + +export const sourceDefinitionKeys = { + all: ["sourceDefinition"] as const, + lists: () => [...sourceDefinitionKeys.all, "list"] as const, + detail: (id: string) => [...sourceDefinitionKeys.all, "details", id] as const, +}; + +function useGetSourceDefinitionService(): SourceDefinitionService { + const { apiUrl } = useConfig(); + + const requestAuthMiddleware = useDefaultRequestMiddlewares(); + + return useInitService( + () => new SourceDefinitionService(apiUrl, requestAuthMiddleware), + [apiUrl, requestAuthMiddleware] + ); +} + +const useSourceDefinitionList = (): { + sourceDefinitions: SourceDefinition[]; +} => { + const service = useGetSourceDefinitionService(); + const workspace = useCurrentWorkspace(); + + return (useQuery(sourceDefinitionKeys.lists(), async () => { + const [definition, latestDefinition] = await Promise.all([ + service.list(workspace.workspaceId), + service.listLatest(workspace.workspaceId), + ]); + + const sourceDefinitions: SourceDefinition[] = definition.sourceDefinitions.map( + (source: SourceDefinition) => { + const withLatest = latestDefinition.sourceDefinitions.find( + (latestSource: SourceDefinition) => + latestSource.sourceDefinitionId === source.sourceDefinitionId + ); + + return { + ...source, + latestDockerImageTag: withLatest?.dockerImageTag ?? "", + }; + } + ); + + return { sourceDefinitions }; + }) as QueryObserverSuccessResult<{ sourceDefinitions: SourceDefinition[] }>) + .data; +}; + +const useSourceDefinition = ( + id: T +): T extends string ? SourceDefinition : SourceDefinition | undefined => { + const service = useGetSourceDefinitionService(); + + return (useQuery( + sourceDefinitionKeys.detail(id || ""), + () => service.get(id || ""), + { + enabled: isDefined(id), + } + ) as QueryObserverSuccessResult).data; +}; + +const useCreateSourceDefinition = () => { + const service = useGetSourceDefinitionService(); + const queryClient = useQueryClient(); + + return useMutation( + (sourceDefinition) => service.create(sourceDefinition), + { + onSuccess: (data) => { + queryClient.setQueryData( + sourceDefinitionKeys.lists(), + (oldData: { sourceDefinitions: SourceDefinition[] } | undefined) => ({ + sourceDefinitions: [data, ...(oldData?.sourceDefinitions ?? [])], + }) + ); + }, + } + ); +}; + +const useUpdateSourceDefinition = () => { + const service = useGetSourceDefinitionService(); + const queryClient = useQueryClient(); + + return useMutation< + SourceDefinition, + Error, + { + sourceDefinitionId: string; + dockerImageTag: string; + } + >((sourceDefinition) => service.update(sourceDefinition), { + onSuccess: (data) => { + queryClient.setQueryData( + sourceDefinitionKeys.detail(data.sourceDefinitionId), + data + ); + + queryClient.setQueryData( + sourceDefinitionKeys.lists(), + (oldData: { sourceDefinitions: SourceDefinition[] } | undefined) => ({ + sourceDefinitions: + oldData?.sourceDefinitions.map((sd) => + sd.sourceDefinitionId === data.sourceDefinitionId ? data : sd + ) ?? [], + }) + ); + }, + }); +}; + +export { + useSourceDefinition, + useSourceDefinitionList, + useCreateSourceDefinition, + useUpdateSourceDefinition, +}; diff --git a/airbyte-webapp/src/services/connector/SourceDefinitionService.tsx b/airbyte-webapp/src/services/connector/SourceDefinitionService.tsx deleted file mode 100644 index 5325c8b552445..0000000000000 --- a/airbyte-webapp/src/services/connector/SourceDefinitionService.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { QueryObserverSuccessResult, useQuery } from "react-query"; - -import { SourceDefinition } from "core/domain/connector"; -import { useConfig } from "config"; -import { useDefaultRequestMiddlewares } from "services/useDefaultRequestMiddlewares"; -import { useInitService } from "services/useInitService"; -import { SourceDefinitionService } from "core/domain/connector/SourceDefinitionService"; -import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; - -export const sourceDefinitionKeys = { - all: ["sourceDefinition"] as const, - lists: () => [...sourceDefinitionKeys.all, "list"] as const, - detail: (id: string | number) => - [...sourceDefinitionKeys.all, "details", id] as const, -}; - -function useGetSourceDefinitionService(): SourceDefinitionService { - const { apiUrl } = useConfig(); - - const requestAuthMiddleware = useDefaultRequestMiddlewares(); - - return useInitService( - () => new SourceDefinitionService(apiUrl, requestAuthMiddleware), - [apiUrl, requestAuthMiddleware] - ); -} - -export const useGetSourceDefinition = (id: string): SourceDefinition => { - const service = useGetSourceDefinitionService(); - - return (useQuery(sourceDefinitionKeys.detail(id), () => - service.get(id) - ) as QueryObserverSuccessResult).data; -}; - -export const useListSourceDefinitions = (): SourceDefinition[] => { - const service = useGetSourceDefinitionService(); - const workspace = useCurrentWorkspace(); - - return (useQuery(sourceDefinitionKeys.lists(), () => - service.list(workspace.workspaceId) - ) as QueryObserverSuccessResult).data; -}; From 4302e6c384dfa27973dbc7e3ac7db5bc03ed07e9 Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Wed, 30 Mar 2022 18:48:54 +0300 Subject: [PATCH 06/20] Refactor Source and Destination resources --- .../domain/connector/DestinationService.ts | 35 +- .../core/domain/connector/SourceService.ts | 35 +- .../src/core/resources/Destination.tsx | 54 --- airbyte-webapp/src/core/resources/Source.tsx | 53 --- .../src/hooks/services/useDestinationHook.tsx | 318 ++++++++++-------- .../src/hooks/services/useSourceHook.tsx | 304 +++++++++-------- .../CreationFormPage/CreationFormPage.tsx | 24 +- .../components/DestinationForm.tsx | 4 +- .../components/ExistingEntityForm.tsx | 15 +- .../components/SourceForm.tsx | 4 +- .../AllDestinationsPage.tsx | 9 +- .../CreateDestinationPage.tsx | 4 +- .../DestinationItemPage.tsx | 19 +- .../components/DestinationSettings.tsx | 8 +- .../pages/OnboardingPage/OnboardingPage.tsx | 9 +- .../pages/ConnectorsPage/DestinationsPage.tsx | 10 +- .../pages/AllSourcesPage/AllSourcesPage.tsx | 9 +- .../CreateSourcePage/CreateSourcePage.tsx | 4 +- .../pages/SourceItemPage/SourceItemPage.tsx | 17 +- .../components/SourceSettings.tsx | 5 +- 20 files changed, 465 insertions(+), 475 deletions(-) delete mode 100644 airbyte-webapp/src/core/resources/Destination.tsx delete mode 100644 airbyte-webapp/src/core/resources/Source.tsx diff --git a/airbyte-webapp/src/core/domain/connector/DestinationService.ts b/airbyte-webapp/src/core/domain/connector/DestinationService.ts index 5897b0df93512..6e6db6ef25466 100644 --- a/airbyte-webapp/src/core/domain/connector/DestinationService.ts +++ b/airbyte-webapp/src/core/domain/connector/DestinationService.ts @@ -2,7 +2,7 @@ import { AirbyteRequestService } from "core/request/AirbyteRequestService"; import Status from "core/statuses"; import { LogsRequestError } from "core/request/LogsRequestError"; -import { Scheduler } from "./types"; +import { Destination, Scheduler } from "./types"; import { ConnectionConfiguration } from "core/domain/connection"; class DestinationService extends AirbyteRequestService { @@ -39,6 +39,39 @@ class DestinationService extends AirbyteRequestService { return result; } + + public get(destinationId: string): Promise { + return this.fetch(`${this.url}/get`, { + destinationId, + }); + } + + public list(workspaceId: string): Promise<{ destinations: Destination[] }> { + return this.fetch(`${this.url}/list`, { + workspaceId, + }); + } + + public create(body: { + name: string; + destinationDefinitionId?: string; + workspaceId: string; + connectionConfiguration: ConnectionConfiguration; + }): Promise { + return this.fetch(`${this.url}/create`, body); + } + + public update(body: { + destinationId: string; + name: string; + connectionConfiguration: ConnectionConfiguration; + }): Promise { + return this.fetch(`${this.url}/create`, body); + } + + public delete(destinationId: string): Promise { + return this.fetch(`${this.url}/delete`, { destinationId }); + } } export { DestinationService }; diff --git a/airbyte-webapp/src/core/domain/connector/SourceService.ts b/airbyte-webapp/src/core/domain/connector/SourceService.ts index 606e767ff0884..994b2d88ed8e6 100644 --- a/airbyte-webapp/src/core/domain/connector/SourceService.ts +++ b/airbyte-webapp/src/core/domain/connector/SourceService.ts @@ -1,6 +1,6 @@ import { AirbyteRequestService } from "core/request/AirbyteRequestService"; import { ConnectionConfiguration } from "../connection"; -import { Scheduler } from "./types"; +import { Scheduler, Source } from "./types"; import Status from "core/statuses"; import { LogsRequestError } from "core/request/LogsRequestError"; @@ -38,6 +38,39 @@ class SourceService extends AirbyteRequestService { return result; } + + public get(sourceId: string): Promise { + return this.fetch(`${this.url}/get`, { + sourceId, + }); + } + + public list(workspaceId: string): Promise<{ sources: Source[] }> { + return this.fetch(`${this.url}/list`, { + workspaceId, + }); + } + + public create(body: { + name: string; + sourceDefinitionId?: string; + workspaceId: string; + connectionConfiguration: ConnectionConfiguration; + }): Promise { + return this.fetch(`${this.url}/create`, body); + } + + public update(body: { + sourceId: string; + name: string; + connectionConfiguration: ConnectionConfiguration; + }): Promise { + return this.fetch(`${this.url}/create`, body); + } + + public delete(sourceId: string): Promise { + return this.fetch(`${this.url}/delete`, { sourceId }); + } } export { SourceService }; diff --git a/airbyte-webapp/src/core/resources/Destination.tsx b/airbyte-webapp/src/core/resources/Destination.tsx deleted file mode 100644 index 646fb9b6f00aa..0000000000000 --- a/airbyte-webapp/src/core/resources/Destination.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { MutateShape, ReadShape, Resource, SchemaDetail } from "rest-hooks"; - -import { ConnectionConfiguration } from "core/domain/connection"; -import BaseResource from "./BaseResource"; -import { Destination } from "core/domain/connector"; - -export class DestinationResource extends BaseResource implements Destination { - readonly destinationId: string = ""; - readonly name: string = ""; - readonly destinationName: string = ""; - readonly workspaceId: string = ""; - readonly destinationDefinitionId: string = ""; - readonly connectionConfiguration: ConnectionConfiguration = {}; - - pk(): string { - return this.destinationId?.toString(); - } - - static urlRoot = "destinations"; - - static listShape( - this: T - ): ReadShape> { - return { - ...super.listShape(), - schema: { destinations: [this] }, - }; - } - - static detailShape( - this: T - ): ReadShape> { - return { - ...super.detailShape(), - schema: this, - }; - } - - static createShape( - this: T - ): MutateShape> { - return { - ...super.createShape(), - schema: this, - fetch: async ( - _: Readonly>, - body: Readonly> - ): Promise => - await this.fetch("post", `${super.rootUrl()}destinations/create`, body), - }; - } -} - -export default DestinationResource; diff --git a/airbyte-webapp/src/core/resources/Source.tsx b/airbyte-webapp/src/core/resources/Source.tsx deleted file mode 100644 index 2c8838baec617..0000000000000 --- a/airbyte-webapp/src/core/resources/Source.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { MutateShape, ReadShape, Resource, SchemaDetail } from "rest-hooks"; -import BaseResource from "./BaseResource"; -import { ConnectionConfiguration } from "core/domain/connection"; -import { Source } from "core/domain/connector"; - -export class SourceResource extends BaseResource implements Source { - readonly sourceId: string = ""; - readonly name: string = ""; - readonly sourceName: string = ""; - readonly sourceDefinitionId: string = ""; - readonly workspaceId: string = ""; - readonly connectionConfiguration: ConnectionConfiguration = {}; - - pk(): string { - return this.sourceId?.toString(); - } - - static urlRoot = "sources"; - - static listShape( - this: T - ): ReadShape> { - return { - ...super.listShape(), - schema: { sources: [this] }, - }; - } - - static detailShape( - this: T - ): ReadShape> { - return { - ...super.detailShape(), - schema: this, - }; - } - - static createShape( - this: T - ): MutateShape> { - return { - ...super.createShape(), - schema: this, - fetch: async ( - _: Readonly>, - body: Readonly> - ): Promise => - await this.fetch("post", `${super.rootUrl()}sources/create`, body), - }; - } -} - -export default SourceResource; diff --git a/airbyte-webapp/src/hooks/services/useDestinationHook.tsx b/airbyte-webapp/src/hooks/services/useDestinationHook.tsx index 082269d94a8d4..f72ecb76ea879 100644 --- a/airbyte-webapp/src/hooks/services/useDestinationHook.tsx +++ b/airbyte-webapp/src/hooks/services/useDestinationHook.tsx @@ -1,169 +1,209 @@ -import { useFetcher, useResource } from "rest-hooks"; -import { useQueryClient } from "react-query"; - -import DestinationResource from "core/resources/Destination"; -import { Connection } from "core/domain/connection"; -import useRouter from "../useRouter"; -import { ConnectionConfiguration } from "core/domain/connection"; +import { + QueryObserverSuccessResult, + useMutation, + useQuery, + useQueryClient, +} from "react-query"; + +import { Connection, ConnectionConfiguration } from "core/domain/connection"; import { useAnalyticsService } from "hooks/services/Analytics/useAnalyticsService"; import { Destination } from "core/domain/connector"; -import { RoutePaths } from "../../pages/routePaths"; import { connectionsKeys, ListConnection } from "./useConnectionHook"; import { useCurrentWorkspace } from "./useWorkspace"; - +import { isDefined } from "utils/common"; +import { useConfig } from "config"; +import { useDefaultRequestMiddlewares } from "services/useDefaultRequestMiddlewares"; +import { useInitService } from "services/useInitService"; +import { DestinationService } from "core/domain/connector/DestinationService"; + +export const destinationsKeys = { + all: ["destination"] as const, + lists: () => [...destinationsKeys.all, "list"] as const, + list: (filters: string) => + [...destinationsKeys.lists(), { filters }] as const, + detail: (destinationId: string) => + [...destinationsKeys.all, "details", destinationId] as const, +}; +// type ValuesProps = { name: string; serviceType?: string; connectionConfiguration?: ConnectionConfiguration; }; - +// type ConnectorProps = { name: string; destinationDefinitionId: string }; -type DestinationServiceApi = { - updateDestination: ({ - values, - destinationId, - }: { - values: ValuesProps; - destinationId: string; - }) => Promise; - createDestination: ({ - values, - destinationConnector, - }: { - values: ValuesProps; - destinationConnector?: ConnectorProps; - }) => Promise; - deleteDestination: ({ - destination, - connectionsWithDestination, - }: { - destination: Destination; - connectionsWithDestination: Connection[]; - }) => Promise; -}; +function useDestinationService(): DestinationService { + const config = useConfig(); + const middlewares = useDefaultRequestMiddlewares(); -const useDestination = (): DestinationServiceApi => { - const { push } = useRouter(); - const workspace = useCurrentWorkspace(); - const analyticsService = useAnalyticsService(); - const createDestinationsImplementation = useFetcher( - DestinationResource.createShape() + return useInitService( + () => new DestinationService(config.apiUrl, middlewares), + // eslint-disable-next-line react-hooks/exhaustive-deps + [config] ); +} - const updatedestination = useFetcher( - DestinationResource.partialUpdateShape() - ); +type DestinationList = { destinations: Destination[] }; - const destinationDelete = useFetcher(DestinationResource.deleteShape()); +const useDestinationList = (): DestinationList => { + const workspace = useCurrentWorkspace(); + const service = useDestinationService(); + + return (useQuery(destinationsKeys.lists(), () => + service.list(workspace.workspaceId) + ) as QueryObserverSuccessResult).data; +}; +const useGetDestination = ( + destinationId: T +): T extends string ? Destination : Destination | undefined => { + const service = useDestinationService(); + + return (useQuery( + destinationsKeys.detail(destinationId ?? ""), + () => service.get(destinationId ?? ""), + { + enabled: isDefined(destinationId), + } + ) as QueryObserverSuccessResult).data; +}; + +const useCreateDestination = () => { + const service = useDestinationService(); const queryClient = useQueryClient(); + const workspace = useCurrentWorkspace(); - const createDestination: DestinationServiceApi["createDestination"] = async ({ - values, - destinationConnector, - }) => { - try { - // Try to crete destination - const result = await createDestinationsImplementation( - {}, - { + const analyticsService = useAnalyticsService(); + + return useMutation( + async (createDestinationPayload: { + values: ValuesProps; + destinationConnector?: ConnectorProps; + }) => { + const { values, destinationConnector } = createDestinationPayload; + try { + const result = await service.create({ name: values.name, destinationDefinitionId: destinationConnector?.destinationDefinitionId, workspaceId: workspace.workspaceId, connectionConfiguration: values.connectionConfiguration, - }, - [ - [ - DestinationResource.listShape(), - { workspaceId: workspace.workspaceId }, - ( - newdestinationId: string, - destinationIds: { destinations: string[] } - ) => ({ - destinations: [ - ...(destinationIds?.destinations || []), - newdestinationId, - ], - }), - ], - ] - ); - - analyticsService.track("New Destination - Action", { - action: "Tested connector - success", - connector_destination: destinationConnector?.name, - connector_destination_definition_id: - destinationConnector?.destinationDefinitionId, - }); + }); - return result; - } catch (e) { - analyticsService.track("New Destination - Action", { - action: "Tested connector - failure", - connector_destination: destinationConnector?.name, - connector_destination_definition_id: - destinationConnector?.destinationDefinitionId, - }); - throw e; + analyticsService.track("New Destination - Action", { + action: "Tested connector - success", + connector_destination: destinationConnector?.name, + connector_destination_definition_id: + destinationConnector?.destinationDefinitionId, + }); + + return result; + } catch (e) { + analyticsService.track("New Destination - Action", { + action: "Tested connector - failure", + connector_destination: destinationConnector?.name, + connector_destination_definition_id: + destinationConnector?.destinationDefinitionId, + }); + throw e; + } + }, + { + onSuccess: (data) => { + queryClient.setQueryData( + destinationsKeys.lists(), + (lst: DestinationList | undefined) => ({ + destinations: [data, ...(lst?.destinations ?? [])], + }) + ); + }, } - }; - - const updateDestination: DestinationServiceApi["updateDestination"] = async ({ - values, - destinationId, - }) => - await updatedestination( - { - destinationId, + ); +}; + +const useDeleteDestination = () => { + const service = useDestinationService(); + const queryClient = useQueryClient(); + const analyticsService = useAnalyticsService(); + + return useMutation( + (payload: { + destination: Destination; + connectionsWithDestination: Connection[]; + }) => service.delete(payload.destination.destinationId), + { + onSuccess: (_data, ctx) => { + analyticsService.track("Destination - Action", { + action: "Delete destination", + connector_destination: ctx.destination.destinationName, + connector_destination_id: ctx.destination.destinationDefinitionId, + }); + + queryClient.removeQueries( + destinationsKeys.detail(ctx.destination.destinationId) + ); + queryClient.setQueryData( + destinationsKeys.lists(), + (lst: DestinationList | undefined) => + ({ + destinations: + lst?.destinations.filter( + (conn) => conn.destinationId !== ctx.destination.destinationId + ) ?? [], + } as DestinationList) + ); + + // To delete connections with current destination from local store + const connectionIds = ctx.connectionsWithDestination.map( + (item) => item.connectionId + ); + + queryClient.setQueryData( + connectionsKeys.lists(), + (ls: ListConnection | undefined) => ({ + connections: + ls?.connections.filter((c) => + connectionIds.includes(c.connectionId) + ) ?? [], + }) + ); }, - { - name: values.name, - destinationId, - connectionConfiguration: values.connectionConfiguration, - } - ); - - const deleteDestination: DestinationServiceApi["deleteDestination"] = async ({ - destination, - connectionsWithDestination, - }) => { - await destinationDelete({ - destinationId: destination.destinationId, - }); - - // To delete connections with current destination from local store - const connectionIds = connectionsWithDestination.map( - (item) => item.connectionId - ); - - queryClient.setQueryData( - connectionsKeys.lists(), - (ls: ListConnection | undefined) => ({ - connections: - ls?.connections.filter((c) => - connectionIds.includes(c.connectionId) - ) ?? [], - }) - ); - - push(RoutePaths.Destination); - }; - - return { - createDestination, - updateDestination, - deleteDestination, - }; + } + ); }; -const useDestinationList = (): { destinations: Destination[] } => { - const workspace = useCurrentWorkspace(); - return useResource(DestinationResource.listShape(), { - workspaceId: workspace.workspaceId, - }); +const useUpdateDestination = () => { + const service = useDestinationService(); + const queryClient = useQueryClient(); + + return useMutation( + (updateDestinationPayload: { + values: ValuesProps; + destinationId: string; + }) => { + return service.update({ + name: updateDestinationPayload.values.name, + destinationId: updateDestinationPayload.destinationId, + connectionConfiguration: + updateDestinationPayload.values.connectionConfiguration, + }); + }, + { + onSuccess: (data) => { + queryClient.setQueryData( + destinationsKeys.detail(data.destinationId), + data + ); + }, + } + ); }; -export { useDestinationList }; -export default useDestination; +export { + useDestinationList, + useGetDestination, + useCreateDestination, + useDeleteDestination, + useUpdateDestination, +}; diff --git a/airbyte-webapp/src/hooks/services/useSourceHook.tsx b/airbyte-webapp/src/hooks/services/useSourceHook.tsx index 6b133e242e38e..d340a1a6b6278 100644 --- a/airbyte-webapp/src/hooks/services/useSourceHook.tsx +++ b/airbyte-webapp/src/hooks/services/useSourceHook.tsx @@ -1,16 +1,27 @@ -import { useFetcher, useResource } from "rest-hooks"; -import { useQueryClient } from "react-query"; - -import SourceResource from "core/resources/Source"; -import { Connection } from "core/domain/connection"; -import { ConnectionConfiguration } from "core/domain/connection"; - -import useRouter from "hooks/useRouter"; +import { + QueryObserverSuccessResult, + useMutation, + useQuery, + useQueryClient, +} from "react-query"; +import { Connection, ConnectionConfiguration } from "core/domain/connection"; import { useAnalyticsService } from "hooks/services/Analytics/useAnalyticsService"; import { Source } from "core/domain/connector"; -import { RoutePaths } from "pages/routePaths"; import { connectionsKeys, ListConnection } from "./useConnectionHook"; import { useCurrentWorkspace } from "./useWorkspace"; +import { useConfig } from "config"; +import { useDefaultRequestMiddlewares } from "services/useDefaultRequestMiddlewares"; +import { useInitService } from "services/useInitService"; +import { SourceService } from "core/domain/connector/SourceService"; +import { isDefined } from "utils/common"; + +export const sourcesKeys = { + all: ["sources"] as const, + lists: () => [...sourcesKeys.all, "list"] as const, + list: (filters: string) => [...sourcesKeys.lists(), { filters }] as const, + detail: (sourceId: string) => + [...sourcesKeys.all, "details", sourceId] as const, +}; type ValuesProps = { name: string; @@ -21,146 +32,171 @@ type ValuesProps = { type ConnectorProps = { name: string; sourceDefinitionId: string }; -type SourceService = { - createSource: (createSourcePayload: { - values: ValuesProps; - sourceConnector?: ConnectorProps; - }) => Promise; - updateSource: (updateSourcePayload: { - values: ValuesProps; - sourceId: string; - }) => Promise; - deleteSource: (deleteSourcePayload: { - source: Source; - connectionsWithSource: Connection[]; - }) => Promise; -}; +function useSourceService(): SourceService { + const config = useConfig(); + const middlewares = useDefaultRequestMiddlewares(); + + return useInitService( + () => new SourceService(config.apiUrl, middlewares), + // eslint-disable-next-line react-hooks/exhaustive-deps + [config] + ); +} + +type SourceList = { sources: Source[] }; -const useSource = (): SourceService => { - const { push } = useRouter(); +const useSourceList = (): SourceList => { const workspace = useCurrentWorkspace(); - const createSourcesImplementation = useFetcher(SourceResource.createShape()); - const analyticsService = useAnalyticsService(); + const service = useSourceService(); + + return (useQuery(sourcesKeys.lists(), () => + service.list(workspace.workspaceId) + ) as QueryObserverSuccessResult).data; +}; - const updatesource = useFetcher(SourceResource.partialUpdateShape()); +const useGetSource = ( + sourceId: T +): T extends string ? Source : Source | undefined => { + const service = useSourceService(); - const sourceDelete = useFetcher(SourceResource.deleteShape()); + return (useQuery( + sourcesKeys.detail(sourceId ?? ""), + () => service.get(sourceId ?? ""), + { + enabled: isDefined(sourceId), + } + ) as QueryObserverSuccessResult).data; +}; +const useCreateSource = () => { + const service = useSourceService(); const queryClient = useQueryClient(); + const workspace = useCurrentWorkspace(); - const createSource: SourceService["createSource"] = async ({ - values, - sourceConnector, - }) => { - analyticsService.track("New Source - Action", { - action: "Test a connector", - connector_source: sourceConnector?.name, - connector_source_id: sourceConnector?.sourceDefinitionId, - }); - - try { - // analyticsService.track("New Source - Action", { - // action: "Test a connector", - // connector_source: sourceConnector?.name, - // connector_source_definition_id: sourceConnector?.sourceDefinitionId, - // }); - - // Try to crete source - const result = await createSourcesImplementation( - {}, - { - name: values.name, - sourceDefinitionId: sourceConnector?.sourceDefinitionId, - workspaceId: workspace.workspaceId, - connectionConfiguration: values.connectionConfiguration, - }, - [ - [ - SourceResource.listShape(), - { workspaceId: workspace.workspaceId }, - (newsourceId: string, sourceIds: { sources: string[] }) => ({ - sources: [...(sourceIds?.sources || []), newsourceId], - }), - ], - ] - ); - analyticsService.track("New Source - Action", { - action: "Tested connector - success", - connector_source: sourceConnector?.name, - connector_source_id: sourceConnector?.sourceDefinitionId, - }); + const analyticsService = useAnalyticsService(); - return result; - } catch (e) { + return useMutation( + async (createSourcePayload: { + values: ValuesProps; + sourceConnector?: ConnectorProps; + }) => { + const { values, sourceConnector } = createSourcePayload; analyticsService.track("New Source - Action", { - action: "Tested connector - failure", + action: "Test a connector", connector_source: sourceConnector?.name, connector_source_id: sourceConnector?.sourceDefinitionId, }); - throw e; + + try { + // Try to crete source + const result = await service.create({ + name: values.name, + sourceDefinitionId: sourceConnector?.sourceDefinitionId, + workspaceId: workspace.workspaceId, + connectionConfiguration: values.connectionConfiguration, + }); + + analyticsService.track("New Source - Action", { + action: "Tested connector - success", + connector_source: sourceConnector?.name, + connector_source_id: sourceConnector?.sourceDefinitionId, + }); + + return result; + } catch (e) { + analyticsService.track("New Source - Action", { + action: "Tested connector - failure", + connector_source: sourceConnector?.name, + connector_source_id: sourceConnector?.sourceDefinitionId, + }); + throw e; + } + }, + { + onSuccess: (data) => { + queryClient.setQueryData( + sourcesKeys.lists(), + (lst: SourceList | undefined) => ({ + sources: [data, ...(lst?.sources ?? [])], + }) + ); + }, } - }; - - const updateSource: SourceService["updateSource"] = async ({ - values, - sourceId, - }) => - await updatesource( - { - sourceId: sourceId, + ); +}; + +const useDeleteSource = () => { + const service = useSourceService(); + const queryClient = useQueryClient(); + const analyticsService = useAnalyticsService(); + + return useMutation( + (payload: { source: Source; connectionsWithSource: Connection[] }) => + service.delete(payload.source.sourceId), + { + onSuccess: (_data, ctx) => { + analyticsService.track("Source - Action", { + action: "Delete source", + connector_source: ctx.source.sourceName, + connector_source_id: ctx.source.sourceDefinitionId, + }); + + queryClient.removeQueries(sourcesKeys.detail(ctx.source.sourceId)); + queryClient.setQueryData( + sourcesKeys.lists(), + (lst: SourceList | undefined) => + ({ + sources: + lst?.sources.filter( + (conn) => conn.sourceId !== ctx.source.sourceId + ) ?? [], + } as SourceList) + ); + + // To delete connections with current source from local store + const connectionIds = ctx.connectionsWithSource.map( + (item) => item.connectionId + ); + + queryClient.setQueryData( + connectionsKeys.lists(), + (ls: ListConnection | undefined) => ({ + connections: + ls?.connections.filter((c) => + connectionIds.includes(c.connectionId) + ) ?? [], + }) + ); }, - { - name: values.name, - sourceId, - connectionConfiguration: values.connectionConfiguration, - } - ); - - const deleteSource: SourceService["deleteSource"] = async ({ - source, - connectionsWithSource, - }) => { - await sourceDelete({ - sourceId: source.sourceId, - }); - - analyticsService.track("Source - Action", { - action: "Delete source", - connector_source: source.sourceName, - connector_source_id: source.sourceDefinitionId, - }); - - // To delete connections with current source from local store - const connectionIds = connectionsWithSource.map( - (item) => item.connectionId - ); - - queryClient.setQueryData( - connectionsKeys.lists(), - (ls: ListConnection | undefined) => ({ - connections: - ls?.connections.filter((c) => - connectionIds.includes(c.connectionId) - ) ?? [], - }) - ); - - push(RoutePaths.Source); - }; - - return { - createSource, - updateSource, - deleteSource, - }; + } + ); }; -const useSourceList = (): { sources: Source[] } => { - const workspace = useCurrentWorkspace(); - return useResource(SourceResource.listShape(), { - workspaceId: workspace.workspaceId, - }); +const useUpdateSource = () => { + const service = useSourceService(); + const queryClient = useQueryClient(); + + return useMutation( + (updateSourcePayload: { values: ValuesProps; sourceId: string }) => { + return service.update({ + name: updateSourcePayload.values.name, + sourceId: updateSourcePayload.sourceId, + connectionConfiguration: + updateSourcePayload.values.connectionConfiguration, + }); + }, + { + onSuccess: (data) => { + queryClient.setQueryData(sourcesKeys.detail(data.sourceId), data); + }, + } + ); }; -export { useSourceList }; -export default useSource; +export { + useSourceList, + useGetSource, + useCreateSource, + useDeleteSource, + useUpdateSource, +}; diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/CreationFormPage.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/CreationFormPage.tsx index d6ae245ab276a..af60f7d3ed267 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/CreationFormPage.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/CreationFormPage.tsx @@ -1,6 +1,5 @@ import React, { useState } from "react"; import { FormattedMessage } from "react-intl"; -import { useResource } from "rest-hooks"; import useRouter from "hooks/useRouter"; import MainPageWithScroll from "components/MainPageWithScroll"; @@ -15,9 +14,6 @@ import CreateConnectionContent from "components/CreateConnectionContent"; import ExistingEntityForm from "./components/ExistingEntityForm"; import SourceForm from "./components/SourceForm"; import DestinationForm from "./components/DestinationForm"; - -import SourceResource from "core/resources/Source"; -import DestinationResource from "core/resources/Destination"; import { Destination, DestinationDefinition, @@ -27,6 +23,8 @@ import { import { Connection } from "core/domain/connection"; import { useSourceDefinition } from "services/connector/SourceDefinitionService"; import { useDestinationDefinition } from "services/connector/DestinationDefinitionService"; +import { useGetSource } from "hooks/services/useSourceHook"; +import { useGetDestination } from "hooks/services/useDestinationHook"; export enum StepsTypes { CREATE_ENTITY = "createEntity", @@ -66,24 +64,14 @@ function usePreloadData(): { } { const { location } = useRouter(); - const source = useResource( - SourceResource.detailShape(), - hasSourceId(location.state) - ? { - sourceId: location.state.sourceId, - } - : null + const source = useGetSource( + hasSourceId(location.state) ? location.state.sourceId : null ); const sourceDefinition = useSourceDefinition(source?.sourceDefinitionId); - const destination = useResource( - DestinationResource.detailShape(), - hasDestinationId(location.state) - ? { - destinationId: location.state.destinationId, - } - : null + const destination = useGetDestination( + hasDestinationId(location.state) ? location.state.destinationId : null ); const destinationDefinition = useDestinationDefinition( destination?.destinationDefinitionId diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/DestinationForm.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/DestinationForm.tsx index b74317f3da79f..89ce5af3e1996 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/DestinationForm.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/DestinationForm.tsx @@ -1,12 +1,12 @@ import React, { useState } from "react"; import useRouter from "hooks/useRouter"; -import useDestination from "hooks/services/useDestinationHook"; // TODO: create separate component for source and destinations forms import DestinationForm from "pages/DestinationPage/pages/CreateDestinationPage/components/DestinationForm"; import { ConnectionConfiguration } from "core/domain/connection"; import { useDestinationDefinitionList } from "services/connector/DestinationDefinitionService"; +import { useCreateDestination } from "hooks/services/useDestinationHook"; type IProps = { afterSubmit: () => void; @@ -17,7 +17,7 @@ const CreateDestinationPage: React.FC = ({ afterSubmit }) => { const [successRequest, setSuccessRequest] = useState(false); const { destinationDefinitions } = useDestinationDefinitionList(); - const { createDestination } = useDestination(); + const { mutateAsync: createDestination } = useCreateDestination(); const onSubmitDestinationForm = async (values: { name: string; diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/ExistingEntityForm.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/ExistingEntityForm.tsx index 778c24d15c60a..b525e0c45e794 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/ExistingEntityForm.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/ExistingEntityForm.tsx @@ -1,18 +1,16 @@ import React, { useMemo } from "react"; import styled from "styled-components"; import { FormattedMessage, useIntl } from "react-intl"; -import { useResource } from "rest-hooks"; import { Field, FieldProps, Form, Formik } from "formik"; import * as yup from "yup"; import ContentCard from "components/ContentCard"; import { Button, ControlLabels, DropDown } from "components"; -import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; -import SourceResource from "core/resources/Source"; -import DestinationResource from "core/resources/Destination"; import ImageBlock from "components/ImageBlock"; import { useSourceDefinitionList } from "services/connector/SourceDefinitionService"; import { useDestinationDefinitionList } from "services/connector/DestinationDefinitionService"; +import { useSourceList } from "../../../../../hooks/services/useSourceHook"; +import { useDestinationList } from "../../../../../hooks/services/useDestinationHook"; type IProps = { type: "source" | "destination"; @@ -42,15 +40,10 @@ const existingEntityValidationSchema = yup.object().shape({ const ExistingEntityForm: React.FC = ({ type, onSubmit }) => { const formatMessage = useIntl().formatMessage; - const workspace = useCurrentWorkspace(); - const { sources } = useResource(SourceResource.listShape(), { - workspaceId: workspace.workspaceId, - }); + const { sources } = useSourceList(); const { sourceDefinitions } = useSourceDefinitionList(); - const { destinations } = useResource(DestinationResource.listShape(), { - workspaceId: workspace.workspaceId, - }); + const { destinations } = useDestinationList(); const { destinationDefinitions } = useDestinationDefinitionList(); diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/SourceForm.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/SourceForm.tsx index 72e63142b4997..0e0a24d2c2b57 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/SourceForm.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/CreationFormPage/components/SourceForm.tsx @@ -1,12 +1,12 @@ import React, { useState } from "react"; import useRouter from "hooks/useRouter"; -import useSource from "hooks/services/useSourceHook"; // TODO: create separate component for source and destinations forms import SourceForm from "pages/SourcesPage/pages/CreateSourcePage/components/SourceForm"; import { ConnectionConfiguration } from "core/domain/connection"; import { useSourceDefinitionList } from "services/connector/SourceDefinitionService"; +import { useCreateSource } from "hooks/services/useSourceHook"; type IProps = { afterSubmit: () => void; @@ -16,7 +16,7 @@ const SourceFormComponent: React.FC = ({ afterSubmit }) => { const { push, location } = useRouter(); const [successRequest, setSuccessRequest] = useState(false); const { sourceDefinitions } = useSourceDefinitionList(); - const { createSource } = useSource(); + const { mutateAsync: createSource } = useCreateSource(); const onSubmitSourceStep = async (values: { name: string; diff --git a/airbyte-webapp/src/pages/DestinationPage/pages/AllDestinationsPage/AllDestinationsPage.tsx b/airbyte-webapp/src/pages/DestinationPage/pages/AllDestinationsPage/AllDestinationsPage.tsx index 9d86483eec4b0..6e100ba222d8c 100644 --- a/airbyte-webapp/src/pages/DestinationPage/pages/AllDestinationsPage/AllDestinationsPage.tsx +++ b/airbyte-webapp/src/pages/DestinationPage/pages/AllDestinationsPage/AllDestinationsPage.tsx @@ -1,23 +1,18 @@ import React from "react"; import { FormattedMessage } from "react-intl"; -import { useResource } from "rest-hooks"; import { Button, MainPageWithScroll } from "components"; import PageTitle from "components/PageTitle"; import useRouter from "hooks/useRouter"; import DestinationsTable from "./components/DestinationsTable"; -import DestinationResource from "core/resources/Destination"; import HeadTitle from "components/HeadTitle"; import Placeholder, { ResourceTypes } from "components/Placeholder"; -import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { RoutePaths } from "../../../routePaths"; +import { useDestinationList } from "hooks/services/useDestinationHook"; const AllDestinationsPage: React.FC = () => { const { push } = useRouter(); - const workspace = useCurrentWorkspace(); - const { destinations } = useResource(DestinationResource.listShape(), { - workspaceId: workspace.workspaceId, - }); + const { destinations } = useDestinationList(); const onCreateDestination = () => push(`${RoutePaths.DestinationNew}`); diff --git a/airbyte-webapp/src/pages/DestinationPage/pages/CreateDestinationPage/CreateDestinationPage.tsx b/airbyte-webapp/src/pages/DestinationPage/pages/CreateDestinationPage/CreateDestinationPage.tsx index 11a361dbb01c0..b4b72800ba0e5 100644 --- a/airbyte-webapp/src/pages/DestinationPage/pages/CreateDestinationPage/CreateDestinationPage.tsx +++ b/airbyte-webapp/src/pages/DestinationPage/pages/CreateDestinationPage/CreateDestinationPage.tsx @@ -4,18 +4,18 @@ import { FormattedMessage } from "react-intl"; import PageTitle from "components/PageTitle"; import DestinationForm from "./components/DestinationForm"; import useRouter from "hooks/useRouter"; -import useDestination from "hooks/services/useDestinationHook"; import { FormPageContent } from "components/ConnectorBlocks"; import { ConnectionConfiguration } from "core/domain/connection"; import HeadTitle from "components/HeadTitle"; import { useDestinationDefinitionList } from "services/connector/DestinationDefinitionService"; +import { useCreateDestination } from "hooks/services/useDestinationHook"; const CreateDestinationPage: React.FC = () => { const { push } = useRouter(); const [successRequest, setSuccessRequest] = useState(false); const { destinationDefinitions } = useDestinationDefinitionList(); - const { createDestination } = useDestination(); + const { mutateAsync: createDestination } = useCreateDestination(); const onSubmitDestinationForm = async (values: { name: string; diff --git a/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/DestinationItemPage.tsx b/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/DestinationItemPage.tsx index 6197d78ab844c..b49771a0036cb 100644 --- a/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/DestinationItemPage.tsx +++ b/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/DestinationItemPage.tsx @@ -1,49 +1,42 @@ import React, { Suspense, useMemo, useState } from "react"; import { FormattedMessage } from "react-intl"; -import { useResource } from "rest-hooks"; import useRouter from "hooks/useRouter"; import Placeholder, { ResourceTypes } from "components/Placeholder"; import Breadcrumbs from "components/Breadcrumbs"; import DestinationConnectionTable from "./components/DestinationConnectionTable"; -import DestinationResource from "core/resources/Destination"; import { ItemTabs, StepsTypes, TableItemTitle, } from "components/ConnectorBlocks"; import DestinationSettings from "./components/DestinationSettings"; -import SourceResource from "core/resources/Source"; import { getIcon } from "utils/imageUtils"; import HeadTitle from "components/HeadTitle"; -import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { DropDownRow, - MainPageWithScroll, - LoadingPage, ImageBlock, + LoadingPage, + MainPageWithScroll, PageTitle, } from "components"; import { RoutePaths } from "pages/routePaths"; import { useConnectionList } from "hooks/services/useConnectionHook"; import { useSourceDefinitionList } from "services/connector/SourceDefinitionService"; import { useDestinationDefinition } from "services/connector/DestinationDefinitionService"; +import { useSourceList } from "hooks/services/useSourceHook"; +import { useGetDestination } from "../../../../hooks/services/useDestinationHook"; const DestinationItemPage: React.FC = () => { const { params, push } = useRouter(); - const workspace = useCurrentWorkspace(); const [currentStep, setCurrentStep] = useState(StepsTypes.OVERVIEW); const onSelectStep = (id: string) => setCurrentStep(id); - const { sources } = useResource(SourceResource.listShape(), { - workspaceId: workspace.workspaceId, - }); + const { sources } = useSourceList(); const { sourceDefinitions } = useSourceDefinitionList(); - const destination = useResource(DestinationResource.detailShape(), { - destinationId: params.id, - }); + const destination = useGetDestination(params.id); const destinationDefinition = useDestinationDefinition( destination.destinationDefinitionId diff --git a/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationSettings.tsx b/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationSettings.tsx index 812ef2baa5a81..ab0801890ddce 100644 --- a/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationSettings.tsx +++ b/airbyte-webapp/src/pages/DestinationPage/pages/DestinationItemPage/components/DestinationSettings.tsx @@ -3,12 +3,15 @@ import { FormattedMessage } from "react-intl"; import styled from "styled-components"; import DeleteBlock from "components/DeleteBlock"; -import useDestination from "hooks/services/useDestinationHook"; import { Connection, ConnectionConfiguration } from "core/domain/connection"; import { ConnectorCard } from "views/Connector/ConnectorCard"; import { Connector, Destination } from "core/domain/connector"; import { useGetDestinationDefinitionSpecification } from "services/connector/DestinationDefinitionSpecificationService"; import { useDestinationDefinition } from "services/connector/DestinationDefinitionService"; +import { + useDeleteDestination, + useUpdateDestination, +} from "hooks/services/useDestinationHook"; const Content = styled.div` max-width: 813px; @@ -32,7 +35,8 @@ const DestinationsSettings: React.FC = ({ currentDestination.destinationDefinitionId ); - const { updateDestination, deleteDestination } = useDestination(); + const { mutateAsync: updateDestination } = useUpdateDestination(); + const { mutateAsync: deleteDestination } = useDeleteDestination(); const onSubmitForm = async (values: { name: string; diff --git a/airbyte-webapp/src/pages/OnboardingPage/OnboardingPage.tsx b/airbyte-webapp/src/pages/OnboardingPage/OnboardingPage.tsx index a2826940c1b77..dbe1b4b905c1b 100644 --- a/airbyte-webapp/src/pages/OnboardingPage/OnboardingPage.tsx +++ b/airbyte-webapp/src/pages/OnboardingPage/OnboardingPage.tsx @@ -4,8 +4,9 @@ import { FormattedMessage } from "react-intl"; import { Button } from "components"; import HeadTitle from "components/HeadTitle"; -import useSource, { useSourceList } from "hooks/services/useSourceHook"; -import useDestination, { +import { useCreateSource, useSourceList } from "hooks/services/useSourceHook"; +import { + useCreateDestination, useDestinationList, } from "hooks/services/useDestinationHook"; import { @@ -76,8 +77,8 @@ const OnboardingPage: React.FC = () => { const { mutateAsync: syncConnection } = useSyncConnection(); - const { createSource } = useSource(); - const { createDestination } = useDestination(); + const { mutateAsync: createSource } = useCreateSource(); + const { mutateAsync: createDestination } = useCreateDestination(); const { finishOnboarding } = useWorkspace(); const [successRequest, setSuccessRequest] = useState(false); diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/DestinationsPage.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/DestinationsPage.tsx index 9b962775824cd..a19f7716d40f1 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/DestinationsPage.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/DestinationsPage.tsx @@ -1,26 +1,20 @@ import React, { useCallback, useMemo, useState } from "react"; import { useIntl } from "react-intl"; -import { useResource } from "rest-hooks"; import { useAsyncFn } from "react-use"; - -import { DestinationResource } from "core/resources/Destination"; import useConnector from "hooks/services/useConnector"; import ConnectorsView from "./components/ConnectorsView"; -import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { DestinationDefinition } from "core/domain/connector"; import { useDestinationDefinitionList, useUpdateDestinationDefinition, } from "services/connector/DestinationDefinitionService"; +import { useDestinationList } from "../../../../hooks/services/useDestinationHook"; const DestinationsPage: React.FC = () => { - const workspace = useCurrentWorkspace(); const [isUpdateSuccess, setIsUpdateSuccess] = useState(false); const formatMessage = useIntl().formatMessage; const { destinationDefinitions } = useDestinationDefinitionList(); - const { destinations } = useResource(DestinationResource.listShape(), { - workspaceId: workspace.workspaceId, - }); + const { destinations } = useDestinationList(); const [feedbackList, setFeedbackList] = useState>({}); diff --git a/airbyte-webapp/src/pages/SourcesPage/pages/AllSourcesPage/AllSourcesPage.tsx b/airbyte-webapp/src/pages/SourcesPage/pages/AllSourcesPage/AllSourcesPage.tsx index 19566db56592a..4224cde727668 100644 --- a/airbyte-webapp/src/pages/SourcesPage/pages/AllSourcesPage/AllSourcesPage.tsx +++ b/airbyte-webapp/src/pages/SourcesPage/pages/AllSourcesPage/AllSourcesPage.tsx @@ -1,23 +1,18 @@ import React from "react"; import { FormattedMessage } from "react-intl"; -import { useResource } from "rest-hooks"; import { Button, MainPageWithScroll } from "components"; import PageTitle from "components/PageTitle"; import useRouter from "hooks/useRouter"; import SourcesTable from "./components/SourcesTable"; -import SourceResource from "core/resources/Source"; import HeadTitle from "components/HeadTitle"; import Placeholder, { ResourceTypes } from "components/Placeholder"; -import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { RoutePaths } from "../../../routePaths"; +import { useSourceList } from "hooks/services/useSourceHook"; const AllSourcesPage: React.FC = () => { const { push } = useRouter(); - const workspace = useCurrentWorkspace(); - const { sources } = useResource(SourceResource.listShape(), { - workspaceId: workspace.workspaceId, - }); + const { sources } = useSourceList(); const onCreateSource = () => push(`${RoutePaths.SourceNew}`); return ( diff --git a/airbyte-webapp/src/pages/SourcesPage/pages/CreateSourcePage/CreateSourcePage.tsx b/airbyte-webapp/src/pages/SourcesPage/pages/CreateSourcePage/CreateSourcePage.tsx index 0a36193dc7cae..6f36a97383241 100644 --- a/airbyte-webapp/src/pages/SourcesPage/pages/CreateSourcePage/CreateSourcePage.tsx +++ b/airbyte-webapp/src/pages/SourcesPage/pages/CreateSourcePage/CreateSourcePage.tsx @@ -4,18 +4,18 @@ import { FormattedMessage } from "react-intl"; import PageTitle from "components/PageTitle"; import SourceForm from "./components/SourceForm"; import useRouter from "hooks/useRouter"; -import useSource from "hooks/services/useSourceHook"; import { FormPageContent } from "components/ConnectorBlocks"; import { ConnectionConfiguration } from "core/domain/connection"; import HeadTitle from "components/HeadTitle"; import { useSourceDefinitionList } from "services/connector/SourceDefinitionService"; +import { useCreateSource } from "hooks/services/useSourceHook"; const CreateSourcePage: React.FC = () => { const { push } = useRouter(); const [successRequest, setSuccessRequest] = useState(false); const { sourceDefinitions } = useSourceDefinitionList(); - const { createSource } = useSource(); + const { mutateAsync: createSource } = useCreateSource(); const onSubmitSourceStep = async (values: { name: string; diff --git a/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/SourceItemPage.tsx b/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/SourceItemPage.tsx index ddfdc1052dc6c..d6e3503eb3636 100644 --- a/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/SourceItemPage.tsx +++ b/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/SourceItemPage.tsx @@ -1,6 +1,5 @@ import React, { Suspense, useMemo, useState } from "react"; import { FormattedMessage } from "react-intl"; -import { useResource } from "rest-hooks"; import { DropDownRow, ImageBlock } from "components"; import PageTitle from "components/PageTitle"; @@ -16,34 +15,26 @@ import MainPageWithScroll from "components/MainPageWithScroll"; import SourceConnectionTable from "./components/SourceConnectionTable"; import SourceSettings from "./components/SourceSettings"; -import SourceResource from "core/resources/Source"; - -import DestinationResource from "core/resources/Destination"; import { getIcon } from "utils/imageUtils"; import HeadTitle from "components/HeadTitle"; import Placeholder, { ResourceTypes } from "components/Placeholder"; -import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { RoutePaths } from "../../../routePaths"; import { useConnectionList } from "hooks/services/useConnectionHook"; import { useSourceDefinition } from "services/connector/SourceDefinitionService"; import { useDestinationDefinitionList } from "services/connector/DestinationDefinitionService"; +import { useGetSource } from "hooks/services/useSourceHook"; +import { useDestinationList } from "../../../../hooks/services/useDestinationHook"; const SourceItemPage: React.FC = () => { const { query, push } = useRouter<{ id: string }>(); - const workspace = useCurrentWorkspace(); const [currentStep, setCurrentStep] = useState(StepsTypes.OVERVIEW); const onSelectStep = (id: string) => setCurrentStep(id); - const { destinations } = useResource(DestinationResource.listShape(), { - workspaceId: workspace.workspaceId, - }); + const { destinations } = useDestinationList(); const { destinationDefinitions } = useDestinationDefinitionList(); - const source = useResource(SourceResource.detailShape(), { - sourceId: query.id, - }); - + const source = useGetSource(query.id); const sourceDefinition = useSourceDefinition(source?.sourceDefinitionId); const { connections } = useConnectionList(); diff --git a/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceSettings.tsx b/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceSettings.tsx index 51b71204e1c73..1414209ea452a 100644 --- a/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceSettings.tsx +++ b/airbyte-webapp/src/pages/SourcesPage/pages/SourceItemPage/components/SourceSettings.tsx @@ -2,13 +2,13 @@ import React from "react"; import styled from "styled-components"; import { FormattedMessage } from "react-intl"; -import useSource from "hooks/services/useSourceHook"; import DeleteBlock from "components/DeleteBlock"; import { Connection, ConnectionConfiguration } from "core/domain/connection"; import { ConnectorCard } from "views/Connector/ConnectorCard"; import { Source } from "core/domain/connector"; import { useGetSourceDefinitionSpecification } from "services/connector/SourceDefinitionSpecificationService"; import { useSourceDefinition } from "services/connector/SourceDefinitionService"; +import { useDeleteSource, useUpdateSource } from "hooks/services/useSourceHook"; const Content = styled.div` max-width: 813px; @@ -24,7 +24,8 @@ const SourceSettings: React.FC = ({ currentSource, connectionsWithSource, }) => { - const { updateSource, deleteSource } = useSource(); + const { mutateAsync: updateSource } = useUpdateSource(); + const { mutateAsync: deleteSource } = useDeleteSource(); const sourceDefinitionSpecification = useGetSourceDefinitionSpecification( currentSource.sourceDefinitionId From c3648881ccd56b27601c9bb2bbc21c6d0dcedf6e Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Wed, 30 Mar 2022 18:59:42 +0300 Subject: [PATCH 07/20] Move discover_schema request to react-query --- .../CreateConnectionContent.tsx | 2 +- .../domain/connector/DestinationService.ts | 2 +- .../core/domain/connector/SourceService.ts | 27 ++- .../src/core/domain/connector/types.ts | 9 +- .../src/core/resources/BaseResource.tsx | 181 ------------------ airbyte-webapp/src/core/resources/Schema.ts | 55 ------ .../src/hooks/services/useSchemaHook.tsx | 47 ----- .../src/hooks/services/useSourceHook.tsx | 45 +++++ 8 files changed, 80 insertions(+), 288 deletions(-) delete mode 100644 airbyte-webapp/src/core/resources/BaseResource.tsx delete mode 100644 airbyte-webapp/src/core/resources/Schema.ts delete mode 100644 airbyte-webapp/src/hooks/services/useSchemaHook.tsx diff --git a/airbyte-webapp/src/components/CreateConnectionContent/CreateConnectionContent.tsx b/airbyte-webapp/src/components/CreateConnectionContent/CreateConnectionContent.tsx index 88020049185ff..33a7026f56f12 100644 --- a/airbyte-webapp/src/components/CreateConnectionContent/CreateConnectionContent.tsx +++ b/airbyte-webapp/src/components/CreateConnectionContent/CreateConnectionContent.tsx @@ -14,12 +14,12 @@ import { useCreateConnection, ValuesProps, } from "hooks/services/useConnectionHook"; -import { useDiscoverSchema } from "hooks/services/useSchemaHook"; import { IDataItem } from "components/base/DropDown/components/Option"; import { useAnalyticsService } from "hooks/services/Analytics/useAnalyticsService"; import { LogsRequestError } from "core/request/LogsRequestError"; import { Destination, Source } from "core/domain/connector"; import { Connection } from "core/domain/connection"; +import { useDiscoverSchema } from "../../hooks/services/useSourceHook"; const SkipButton = styled.div` margin-top: 6px; diff --git a/airbyte-webapp/src/core/domain/connector/DestinationService.ts b/airbyte-webapp/src/core/domain/connector/DestinationService.ts index 6e6db6ef25466..d314ff8fbb4af 100644 --- a/airbyte-webapp/src/core/domain/connector/DestinationService.ts +++ b/airbyte-webapp/src/core/domain/connector/DestinationService.ts @@ -66,7 +66,7 @@ class DestinationService extends AirbyteRequestService { name: string; connectionConfiguration: ConnectionConfiguration; }): Promise { - return this.fetch(`${this.url}/create`, body); + return this.fetch(`${this.url}/update`, body); } public delete(destinationId: string): Promise { diff --git a/airbyte-webapp/src/core/domain/connector/SourceService.ts b/airbyte-webapp/src/core/domain/connector/SourceService.ts index 994b2d88ed8e6..08c558a7c58d9 100644 --- a/airbyte-webapp/src/core/domain/connector/SourceService.ts +++ b/airbyte-webapp/src/core/domain/connector/SourceService.ts @@ -1,8 +1,9 @@ import { AirbyteRequestService } from "core/request/AirbyteRequestService"; import { ConnectionConfiguration } from "../connection"; -import { Scheduler, Source } from "./types"; +import { Scheduler, Schema, Source } from "./types"; import Status from "core/statuses"; import { LogsRequestError } from "core/request/LogsRequestError"; +import { CommonRequestError } from "core/request/CommonRequestError"; class SourceService extends AirbyteRequestService { get url(): string { @@ -65,12 +66,34 @@ class SourceService extends AirbyteRequestService { name: string; connectionConfiguration: ConnectionConfiguration; }): Promise { - return this.fetch(`${this.url}/create`, body); + return this.fetch(`${this.url}/update`, body); } public delete(sourceId: string): Promise { return this.fetch(`${this.url}/delete`, { sourceId }); } + + public async discoverSchema(sourceId: string): Promise { + const result = await this.fetch(`${this.url}/discover_schema`, { + sourceId, + }); + + if (result.jobInfo?.status === Status.FAILED || !result.catalog) { + // @ts-ignore address this case + const e = new CommonRequestError(result); + // Generate error with failed status and received logs + e._status = 400; + // @ts-ignore address this case + e.response = result.jobInfo; + throw e; + } + + return { + catalog: result.catalog, + jobInfo: result.jobInfo, + id: sourceId, + }; + } } export { SourceService }; diff --git a/airbyte-webapp/src/core/domain/connector/types.ts b/airbyte-webapp/src/core/domain/connector/types.ts index a182c26a5ad33..1fe2867cd3321 100644 --- a/airbyte-webapp/src/core/domain/connector/types.ts +++ b/airbyte-webapp/src/core/domain/connector/types.ts @@ -2,7 +2,10 @@ import { ConnectionConfiguration, ConnectionSpecification, } from "core/domain/connection"; -import { DestinationSyncMode } from "core/domain/catalog"; +import { + DestinationSyncMode, + SourceDiscoverSchemaRead, +} from "core/domain/catalog"; import { JobInfo } from "../job"; export enum ReleaseStage { @@ -128,3 +131,7 @@ export interface Scheduler { message: string; jobInfo?: JobInfo; } + +export interface Schema extends SourceDiscoverSchemaRead { + id: string; +} diff --git a/airbyte-webapp/src/core/resources/BaseResource.tsx b/airbyte-webapp/src/core/resources/BaseResource.tsx deleted file mode 100644 index c6c24f614cc30..0000000000000 --- a/airbyte-webapp/src/core/resources/BaseResource.tsx +++ /dev/null @@ -1,181 +0,0 @@ -import { - AbstractInstanceType, - Method, - MutateShape, - ReadShape, - Resource, - SchemaDetail, - SchemaList, - schemas, -} from "rest-hooks"; - -import { parseResponse } from "core/request/AirbyteRequestService"; -import { getService } from "core/servicesProvider"; -import { RequestMiddleware } from "core/request/RequestMiddleware"; - -// TODO: rename to crud resource after upgrade to rest-hook 5.0.0 -export default abstract class BaseResource extends Resource { - static async useFetchInit(init: RequestInit): Promise { - let preparedOptions: RequestInit = init; - const middlewares = - getService("DefaultRequestMiddlewares") ?? []; - - for (const middleware of middlewares) { - preparedOptions = await middleware(preparedOptions); - } - - return preparedOptions; - } - - /** Perform network request and resolve with HTTP Response */ - static async fetchResponse( - _: Method, - url: string, - body?: Readonly | Array | string> - ): Promise { - let options: RequestInit = { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - }; - if (this.fetchOptionsPlugin) { - options = this.fetchOptionsPlugin(options); - } - if (body) { - options.body = JSON.stringify(body); - } - - const op = await this.useFetchInit(options); - return fetch(url, op); - } - - /** Perform network request and resolve with json body */ - static async fetch( - method: Method, - url: string, - body?: Readonly | Array | string> - ): Promise { - const response = await this.fetchResponse(method, url, body); - - return parseResponse(response); - } - - static listUrl(_?: Readonly>): string { - return `${this.rootUrl()}${this.urlRoot}`; - } - - static url(_: Readonly>): string { - return `${this.rootUrl()}${this.urlRoot}`; - } - - static rootUrl(): string { - return window._API_URL; - } - - static listShape( - this: T - ): ReadShape>> { - return { - ...super.listShape(), - getFetchKey: (params: Readonly>) => - "POST " + this.url(params) + "/list" + JSON.stringify(params), - fetch: async ( - params: Readonly> - ): Promise => { - const response = await this.fetch( - "post", - `${this.listUrl(params)}/list`, - { ...params } - ); - return response; - }, - }; - } - - static detailShape( - this: T - ): ReadShape>> { - return { - ...super.detailShape(), - getFetchKey: (params: Readonly>) => - "POST " + this.url(params) + "/get" + JSON.stringify(params), - fetch: async ( - params: Readonly> - ): Promise => { - const response = await this.fetch( - "post", - `${this.url(params)}/get`, - params - ); - return response; - }, - }; - } - - static createShape( - this: T - ): MutateShape>> { - return { - ...super.createShape(), - getFetchKey: (params: Readonly>) => - "POST " + this.url(params) + "/create" + JSON.stringify(params), - fetch: async ( - params: Readonly>, - body: Readonly> - ): Promise => { - const response = await this.fetch( - "post", - `${this.listUrl(params)}/create`, - body - ); - return response; - }, - }; - } - - static deleteShape( - this: T - ): MutateShape< - schemas.Delete, - Readonly>, - unknown - > { - return { - ...super.deleteShape(), - getFetchKey: (params: Readonly>) => - "POST " + this.url(params) + "/delete" + JSON.stringify(params), - fetch: async ( - params: Readonly> - ): Promise => { - const response = await this.fetch( - "post", - `${this.url(params)}/delete`, - params - ); - return response; - }, - }; - } - - static partialUpdateShape( - this: T - ): MutateShape>> { - return { - ...super.partialUpdateShape(), - getFetchKey: (params: Readonly>) => - "POST " + this.url(params) + "/partial-update" + JSON.stringify(params), - fetch: async ( - params: Readonly>, - body: Readonly> - ): Promise => { - const response = await this.fetch( - "post", - `${this.url(params)}/update`, - body - ); - return response; - }, - }; - } -} diff --git a/airbyte-webapp/src/core/resources/Schema.ts b/airbyte-webapp/src/core/resources/Schema.ts deleted file mode 100644 index ad0fefb8033aa..0000000000000 --- a/airbyte-webapp/src/core/resources/Schema.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { ReadShape, Resource, SchemaDetail } from "rest-hooks"; - -import BaseResource from "./BaseResource"; -import { SourceDiscoverSchemaRead, SyncSchema } from "core/domain/catalog"; -import { JobInfo } from "core/domain/job"; -import Status from "../statuses"; -import { CommonRequestError } from "../request/CommonRequestError"; - -export interface Schema extends SourceDiscoverSchemaRead { - id: string; -} - -export default class SchemaResource extends BaseResource implements Schema { - readonly catalog: SyncSchema = { streams: [] }; - readonly id: string = ""; - readonly jobInfo?: JobInfo = undefined; - - pk(): string { - return this.id?.toString(); - } - - static urlRoot = "sources"; - - static schemaShape( - this: T - ): ReadShape> { - return { - ...super.detailShape(), - fetch: async (params: { sourceId: string }): Promise => { - const result = await this.fetch( - "post", - `${this.url(params)}/discover_schema`, - params - ); - - if (result.jobInfo?.status === Status.FAILED || !result.catalog) { - // @ts-ignore address this case - const e = new CommonRequestError(result); - // Generate error with failed status and received logs - e._status = 400; - // @ts-ignore address this case - e.response = result.jobInfo; - throw e; - } - - return { - catalog: result.catalog, - jobInfo: result.jobInfo, - id: params.sourceId, - }; - }, - schema: this, - }; - } -} diff --git a/airbyte-webapp/src/hooks/services/useSchemaHook.tsx b/airbyte-webapp/src/hooks/services/useSchemaHook.tsx deleted file mode 100644 index 4434a8f6b82f6..0000000000000 --- a/airbyte-webapp/src/hooks/services/useSchemaHook.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { useEffect, useState, useCallback } from "react"; -import { useFetcher } from "rest-hooks"; - -import { SyncSchema } from "core/domain/catalog"; -import SchemaResource from "core/resources/Schema"; -import { JobInfo } from "core/domain/job"; - -export const useDiscoverSchema = ( - sourceId?: string -): { - isLoading: boolean; - schema: SyncSchema; - schemaErrorStatus: { status: number; response: JobInfo } | null; - onDiscoverSchema: () => Promise; -} => { - const [schema, setSchema] = useState({ streams: [] }); - const [isLoading, setIsLoading] = useState(false); - const [schemaErrorStatus, setSchemaErrorStatus] = useState<{ - status: number; - response: JobInfo; - } | null>(null); - - const fetchDiscoverSchema = useFetcher(SchemaResource.schemaShape(), true); - - const onDiscoverSchema = useCallback(async () => { - setIsLoading(true); - setSchemaErrorStatus(null); - try { - const data = await fetchDiscoverSchema({ sourceId: sourceId || "" }); - setSchema(data.catalog); - } catch (e) { - setSchemaErrorStatus(e); - } finally { - setIsLoading(false); - } - }, [fetchDiscoverSchema, sourceId]); - - useEffect(() => { - (async () => { - if (sourceId) { - await onDiscoverSchema(); - } - })(); - }, [onDiscoverSchema, sourceId]); - - return { schemaErrorStatus, isLoading, schema, onDiscoverSchema }; -}; diff --git a/airbyte-webapp/src/hooks/services/useSourceHook.tsx b/airbyte-webapp/src/hooks/services/useSourceHook.tsx index d340a1a6b6278..d1ea75dd1a0e6 100644 --- a/airbyte-webapp/src/hooks/services/useSourceHook.tsx +++ b/airbyte-webapp/src/hooks/services/useSourceHook.tsx @@ -4,6 +4,8 @@ import { useQuery, useQueryClient, } from "react-query"; +import { useCallback, useEffect, useState } from "react"; + import { Connection, ConnectionConfiguration } from "core/domain/connection"; import { useAnalyticsService } from "hooks/services/Analytics/useAnalyticsService"; import { Source } from "core/domain/connector"; @@ -14,6 +16,8 @@ import { useDefaultRequestMiddlewares } from "services/useDefaultRequestMiddlewa import { useInitService } from "services/useInitService"; import { SourceService } from "core/domain/connector/SourceService"; import { isDefined } from "utils/common"; +import { SyncSchema } from "core/domain/catalog"; +import { JobInfo } from "core/domain/job"; export const sourcesKeys = { all: ["sources"] as const, @@ -193,10 +197,51 @@ const useUpdateSource = () => { ); }; +const useDiscoverSchema = ( + sourceId?: string +): { + isLoading: boolean; + schema: SyncSchema; + schemaErrorStatus: { status: number; response: JobInfo } | null; + onDiscoverSchema: () => Promise; +} => { + const service = useSourceService(); + const [schema, setSchema] = useState({ streams: [] }); + const [isLoading, setIsLoading] = useState(false); + const [schemaErrorStatus, setSchemaErrorStatus] = useState<{ + status: number; + response: JobInfo; + } | null>(null); + + const onDiscoverSchema = useCallback(async () => { + setIsLoading(true); + setSchemaErrorStatus(null); + try { + const data = await service.discoverSchema(sourceId || ""); + setSchema(data.catalog); + } catch (e) { + setSchemaErrorStatus(e); + } finally { + setIsLoading(false); + } + }, [sourceId]); + + useEffect(() => { + (async () => { + if (sourceId) { + await onDiscoverSchema(); + } + })(); + }, [onDiscoverSchema, sourceId]); + + return { schemaErrorStatus, isLoading, schema, onDiscoverSchema }; +}; + export { useSourceList, useGetSource, useCreateSource, useDeleteSource, useUpdateSource, + useDiscoverSchema, }; From 59978f1cfbfe3f823b65ba35ec5e9cabb7bfaed5 Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Wed, 30 Mar 2022 19:06:23 +0300 Subject: [PATCH 08/20] Remove rest-hooks specific code --- airbyte-webapp/.storybook/preview.ts | 2 - .../.storybook/restHooksDecorator.tsx | 16 ------ airbyte-webapp/.storybook/withProvider.tsx | 51 +++++++++++++------ airbyte-webapp/package.json | 2 - airbyte-webapp/src/config/types.ts | 3 -- airbyte-webapp/src/core/ApiServices.tsx | 6 +-- airbyte-webapp/src/core/servicesProvider.tsx | 11 ---- .../cloud/services/auth/AuthService.tsx | 3 -- .../services/workspaces/WorkspacesService.tsx | 5 +- .../src/views/common/StoreProvider.tsx | 5 +- 10 files changed, 38 insertions(+), 66 deletions(-) delete mode 100644 airbyte-webapp/.storybook/restHooksDecorator.tsx diff --git a/airbyte-webapp/.storybook/preview.ts b/airbyte-webapp/.storybook/preview.ts index b1f04c2e5fa4f..339b28592c027 100644 --- a/airbyte-webapp/.storybook/preview.ts +++ b/airbyte-webapp/.storybook/preview.ts @@ -1,9 +1,7 @@ import { addDecorator } from "@storybook/react"; import { withProviders } from "./withProvider"; -import { MockDecorator } from "./restHooksDecorator"; addDecorator(withProviders); -addDecorator(MockDecorator); export const parameters = {}; diff --git a/airbyte-webapp/.storybook/restHooksDecorator.tsx b/airbyte-webapp/.storybook/restHooksDecorator.tsx deleted file mode 100644 index d9ebc5a61cf7f..0000000000000 --- a/airbyte-webapp/.storybook/restHooksDecorator.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { Fixture, MockResolver } from "@rest-hooks/test"; -import { CacheProvider } from "@rest-hooks/core"; -import { Suspense } from "react"; -import { NetworkErrorBoundary } from "rest-hooks"; - -const fixtures: Fixture[] = []; - -export const MockDecorator = (getStory) => ( - - - - {getStory()} - - - -); diff --git a/airbyte-webapp/.storybook/withProvider.tsx b/airbyte-webapp/.storybook/withProvider.tsx index 01727d65d9670..9d7e1e7a65057 100644 --- a/airbyte-webapp/.storybook/withProvider.tsx +++ b/airbyte-webapp/.storybook/withProvider.tsx @@ -1,6 +1,9 @@ +import React from "react"; + import { MemoryRouter } from "react-router-dom"; import { IntlProvider } from "react-intl"; import { ThemeProvider } from "styled-components"; +import { QueryClientProvider, QueryClient } from "react-query"; // TODO: theme was not working correctly so imported directly import { theme } from "../src/theme"; @@ -24,21 +27,37 @@ const AnalyticsContextMock: AnalyticsServiceProviderValue = ({ }, } as unknown) as AnalyticsServiceProviderValue; +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: false, + suspense: true, + }, + }, +}); + export const withProviders = (getStory) => ( - - - - - - - - - {getStory()} - - - - - - - + + + + + + + + + + + {getStory()} + + + + + + + + + ); diff --git a/airbyte-webapp/package.json b/airbyte-webapp/package.json index f167027628238..6647ddcd3ca12 100644 --- a/airbyte-webapp/package.json +++ b/airbyte-webapp/package.json @@ -49,14 +49,12 @@ "rehype-urls": "^1.1.1", "remark-frontmatter": "^4.0.1", "remark-gfm": "^3.0.0", - "rest-hooks": "^5.0.20", "sanitize-html": "^2.7.0", "styled-components": "^5.3.3", "typesafe-actions": "^5.1.0", "yup": "^0.32.11" }, "devDependencies": { - "@rest-hooks/test": "^6.2.0", "@storybook/addon-essentials": "^6.4.19", "@storybook/builder-webpack5": "^6.4.19", "@storybook/manager-webpack5": "^6.4.19", diff --git a/airbyte-webapp/src/config/types.ts b/airbyte-webapp/src/config/types.ts index d31ad3e28410c..7232f8fc3e390 100644 --- a/airbyte-webapp/src/config/types.ts +++ b/airbyte-webapp/src/config/types.ts @@ -15,9 +15,6 @@ declare global { REACT_APP_INTEGRATION_DOCS_URLS?: string; SEGMENT_TOKEN?: string; analytics: SegmentAnalytics; - - // API_URL to hack rest-hooks resources - _API_URL: string; } } diff --git a/airbyte-webapp/src/core/ApiServices.tsx b/airbyte-webapp/src/core/ApiServices.tsx index 85dbbd1ff5e57..8c13a2fce7208 100644 --- a/airbyte-webapp/src/core/ApiServices.tsx +++ b/airbyte-webapp/src/core/ApiServices.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useMemo } from "react"; +import React, { useMemo } from "react"; import { useConfig } from "config"; import { RequestMiddleware } from "./request/RequestMiddleware"; @@ -15,10 +15,6 @@ export const ApiServices: React.FC = React.memo(({ children }) => { "DefaultRequestMiddlewares" ); - useEffect(() => { - window._API_URL = config.apiUrl; - }, [config]); - const services = useMemo( () => ({ SourceDefinitionService: new SourceDefinitionService( diff --git a/airbyte-webapp/src/core/servicesProvider.tsx b/airbyte-webapp/src/core/servicesProvider.tsx index 08886a5dce2e6..7bed579832f20 100644 --- a/airbyte-webapp/src/core/servicesProvider.tsx +++ b/airbyte-webapp/src/core/servicesProvider.tsx @@ -38,10 +38,6 @@ export const ServicesProvider: React.FC<{ inject?: ServiceContainer }> = ({ [registeredServices] ); - useEffect(() => { - services = registeredServices; - }, [registeredServices]); - return ( {children} @@ -100,11 +96,4 @@ export function useGetService(serviceToken: string): T { ]); } -// This is workaround for rest-hooks -let services: ServiceContainer = {}; - -export function getService(serviceId: string): T { - return services[serviceId]; -} - // diff --git a/airbyte-webapp/src/packages/cloud/services/auth/AuthService.tsx b/airbyte-webapp/src/packages/cloud/services/auth/AuthService.tsx index 40e0fe0a1c6be..bf1235a2a5450 100644 --- a/airbyte-webapp/src/packages/cloud/services/auth/AuthService.tsx +++ b/airbyte-webapp/src/packages/cloud/services/auth/AuthService.tsx @@ -1,6 +1,5 @@ import React, { useContext, useEffect, useMemo } from "react"; import { useQueryClient } from "react-query"; -import { useResetter } from "rest-hooks"; import { User as FbUser } from "firebase/auth"; import { GoogleAuthService } from "packages/cloud/lib/auth/GoogleAuthService"; @@ -165,7 +164,6 @@ export const AuthenticationProvider: React.FC = ({ children }) => { }, [state.currentUser, loggedIn, authInited]); const queryClient = useQueryClient(); - const reset = useResetter(); const ctx: AuthContextApi = useMemo( () => ({ @@ -179,7 +177,6 @@ export const AuthenticationProvider: React.FC = ({ children }) => { await authService.signOut(); loggedOut(); await queryClient.invalidateQueries(); - await reset(); }, async updateEmail(email, password): Promise { await userService.changeEmail(email); diff --git a/airbyte-webapp/src/services/workspaces/WorkspacesService.tsx b/airbyte-webapp/src/services/workspaces/WorkspacesService.tsx index 33de0e459ce19..51b5ee4f45d29 100644 --- a/airbyte-webapp/src/services/workspaces/WorkspacesService.tsx +++ b/airbyte-webapp/src/services/workspaces/WorkspacesService.tsx @@ -5,7 +5,6 @@ import { useQuery, useQueryClient, } from "react-query"; -import { useResetter } from "rest-hooks"; import useRouter from "hooks/useRouter"; import { Workspace, WorkspaceService } from "core/domain/workspace"; @@ -35,7 +34,6 @@ const useSelectWorkspace = (): (( workspace?: string | null | Workspace ) => void) => { const queryClient = useQueryClient(); - const resetCache = useResetter(); const { push } = useRouter(); return useCallback( @@ -46,9 +44,8 @@ const useSelectWorkspace = (): (( push(`/${RoutePaths.Workspaces}/${workspace}`); } await queryClient.resetQueries(); - resetCache(); }, - [push, queryClient, resetCache] + [push, queryClient] ); }; diff --git a/airbyte-webapp/src/views/common/StoreProvider.tsx b/airbyte-webapp/src/views/common/StoreProvider.tsx index 192443bf02d74..f3d81a9f69d1d 100644 --- a/airbyte-webapp/src/views/common/StoreProvider.tsx +++ b/airbyte-webapp/src/views/common/StoreProvider.tsx @@ -1,6 +1,5 @@ import { QueryClient, QueryClientProvider } from "react-query"; import React from "react"; -import { CacheProvider } from "rest-hooks"; const queryClient = new QueryClient({ defaultOptions: { @@ -13,9 +12,7 @@ const queryClient = new QueryClient({ }); const StoreProvider: React.FC = ({ children }) => ( - - {children} - + {children} ); export { StoreProvider }; From 668afbac6997d9048264f777b4370bc43d4fafe2 Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Wed, 30 Mar 2022 19:30:38 +0300 Subject: [PATCH 09/20] Fix lint errors --- .../connection/WebBackendConnectionService.ts | 4 ++++ .../src/core/domain/connector/SourceService.ts | 2 ++ airbyte-webapp/src/core/domain/connector/types.ts | 1 + airbyte-webapp/src/hooks/services/useSourceHook.tsx | 1 + .../src/services/workspaces/WorkspacesService.tsx | 13 ++++++++----- 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/airbyte-webapp/src/core/domain/connection/WebBackendConnectionService.ts b/airbyte-webapp/src/core/domain/connection/WebBackendConnectionService.ts index c6b243b8f9924..84ce103150f8e 100644 --- a/airbyte-webapp/src/core/domain/connection/WebBackendConnectionService.ts +++ b/airbyte-webapp/src/core/domain/connection/WebBackendConnectionService.ts @@ -26,6 +26,8 @@ class WebBackendConnectionService extends AirbyteRequestService { } public async update(payload: Record): Promise { + // needs proper type and refactor of CommonRequestError + // eslint-disable-next-line @typescript-eslint/no-explicit-any const result = await this.fetch(`${this.url}/update`, payload); if (result.status === "failure") { @@ -36,6 +38,8 @@ class WebBackendConnectionService extends AirbyteRequestService { } public async create(payload: Record): Promise { + // needs proper type and refactor of CommonRequestError + // eslint-disable-next-line @typescript-eslint/no-explicit-any const result = await this.fetch(`${this.url}/create`, payload); if (result.status === "failure") { diff --git a/airbyte-webapp/src/core/domain/connector/SourceService.ts b/airbyte-webapp/src/core/domain/connector/SourceService.ts index 08c558a7c58d9..e0101cd08543c 100644 --- a/airbyte-webapp/src/core/domain/connector/SourceService.ts +++ b/airbyte-webapp/src/core/domain/connector/SourceService.ts @@ -74,6 +74,8 @@ class SourceService extends AirbyteRequestService { } public async discoverSchema(sourceId: string): Promise { + // needs proper type and refactor of CommonRequestError + // eslint-disable-next-line @typescript-eslint/no-explicit-any const result = await this.fetch(`${this.url}/discover_schema`, { sourceId, }); diff --git a/airbyte-webapp/src/core/domain/connector/types.ts b/airbyte-webapp/src/core/domain/connector/types.ts index 1fe2867cd3321..1672c5c1af235 100644 --- a/airbyte-webapp/src/core/domain/connector/types.ts +++ b/airbyte-webapp/src/core/domain/connector/types.ts @@ -133,5 +133,6 @@ export interface Scheduler { } export interface Schema extends SourceDiscoverSchemaRead { + // TODO: probably this could be removed. Legacy proper that was used in rest-hooks id: string; } diff --git a/airbyte-webapp/src/hooks/services/useSourceHook.tsx b/airbyte-webapp/src/hooks/services/useSourceHook.tsx index d1ea75dd1a0e6..40d6902cb609b 100644 --- a/airbyte-webapp/src/hooks/services/useSourceHook.tsx +++ b/airbyte-webapp/src/hooks/services/useSourceHook.tsx @@ -224,6 +224,7 @@ const useDiscoverSchema = ( } finally { setIsLoading(false); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [sourceId]); useEffect(() => { diff --git a/airbyte-webapp/src/services/workspaces/WorkspacesService.tsx b/airbyte-webapp/src/services/workspaces/WorkspacesService.tsx index 51b5ee4f45d29..d02d199eeb74c 100644 --- a/airbyte-webapp/src/services/workspaces/WorkspacesService.tsx +++ b/airbyte-webapp/src/services/workspaces/WorkspacesService.tsx @@ -130,9 +130,12 @@ export const useUpdateWorkspace = () => { const service = useWorkspaceApiService(); const queryClient = useQueryClient(); - return useMutation((workspace: any) => service.update(workspace), { - onSuccess: (data) => { - queryClient.setQueryData(workspaceKeys.detail(data.workspaceId), data); - }, - }); + return useMutation( + (workspace: Record) => service.update(workspace), + { + onSuccess: (data) => { + queryClient.setQueryData(workspaceKeys.detail(data.workspaceId), data); + }, + } + ); }; From 85a75d2789b80129d9bacf27b08b31f3eedc5db3 Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Wed, 30 Mar 2022 22:20:07 +0300 Subject: [PATCH 10/20] Fix lint --- .../src/core/domain/connection/ConnectionService.ts | 4 +++- .../src/core/domain/connection/OperationService.ts | 11 +++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/airbyte-webapp/src/core/domain/connection/ConnectionService.ts b/airbyte-webapp/src/core/domain/connection/ConnectionService.ts index 48ff863274723..30447fc53c305 100644 --- a/airbyte-webapp/src/core/domain/connection/ConnectionService.ts +++ b/airbyte-webapp/src/core/domain/connection/ConnectionService.ts @@ -23,7 +23,9 @@ class ConnectionService extends AirbyteRequestService { return rs; } - public async delete(connectionId: string): Promise { + public async delete(connectionId: string): Promise { + // needs proper type and refactor of CommonRequestError + // eslint-disable-next-line @typescript-eslint/no-explicit-any const result = await this.fetch(`${this.url}/delete`, { connectionId, }); diff --git a/airbyte-webapp/src/core/domain/connection/OperationService.ts b/airbyte-webapp/src/core/domain/connection/OperationService.ts index 8e34889683b6c..4ba438ea5b25d 100644 --- a/airbyte-webapp/src/core/domain/connection/OperationService.ts +++ b/airbyte-webapp/src/core/domain/connection/OperationService.ts @@ -10,22 +10,17 @@ class OperationService extends AirbyteRequestService { public async check( operation: Operation ): Promise<{ status: "succeeded" | "failed"; message: string }> { - const rs = ((await this.fetch( - `${this.url}/check`, - operation.operatorConfiguration - // eslint-disable-next-line @typescript-eslint/no-explicit-any - )) as any) as { + const rs = await this.fetch<{ status: "succeeded" | "failed"; message: string; - }; + }>(`${this.url}/check`, operation.operatorConfiguration); if (rs.status === Status.FAILED) { // TODO: place proper error throw new Error("failed"); } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return rs as any; + return rs; } } From 2862cb3f2bfc6f4bace30b2ba62d95ecc9200301 Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Wed, 30 Mar 2022 22:23:22 +0300 Subject: [PATCH 11/20] Update package.lock --- airbyte-webapp/package-lock.json | 179 ------------------------------- 1 file changed, 179 deletions(-) diff --git a/airbyte-webapp/package-lock.json b/airbyte-webapp/package-lock.json index 25a0b568a3813..1a76f6255d6ee 100644 --- a/airbyte-webapp/package-lock.json +++ b/airbyte-webapp/package-lock.json @@ -42,14 +42,12 @@ "rehype-urls": "^1.1.1", "remark-frontmatter": "^4.0.1", "remark-gfm": "^3.0.0", - "rest-hooks": "^5.0.20", "sanitize-html": "^2.7.0", "styled-components": "^5.3.3", "typesafe-actions": "^5.1.0", "yup": "^0.32.11" }, "devDependencies": { - "@rest-hooks/test": "^6.2.0", "@storybook/addon-essentials": "^6.4.19", "@storybook/builder-webpack5": "^6.4.19", "@storybook/manager-webpack5": "^6.4.19", @@ -5491,91 +5489,6 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" }, - "node_modules/@rest-hooks/core": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/@rest-hooks/core/-/core-1.0.16.tgz", - "integrity": "sha512-9Owt4hACJL6uwarEfQGn05/+hTa/GIFiSsQA1+ZGd5aIOX3LDC5gEXbk1AI4mScDmHrorWAVAy2YsLg6oHWoVA==", - "dependencies": { - "@babel/runtime": "^7.7.2", - "@rest-hooks/endpoint": "^1.0.12", - "@rest-hooks/normalizr": "^6.0.9", - "@rest-hooks/use-enhanced-reducer": "^1.0.5", - "flux-standard-action": "^2.1.1" - }, - "engines": { - "node": ">=0.12" - }, - "peerDependencies": { - "@types/react": "^16.8.4 || ^17.0.0", - "react": "^16.8.4 || ^17.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@rest-hooks/endpoint": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@rest-hooks/endpoint/-/endpoint-1.0.12.tgz", - "integrity": "sha512-PYTkBa9tvX/fytscd16WokYO6d1iuYvd8Cj17T8C4Re0GmJXAxsinA+rfyViw3cxXiMO7f4B0rWkDkdwQJhNRw==", - "dependencies": { - "@babel/runtime": "^7.7.2", - "@rest-hooks/normalizr": "^6.0.9" - } - }, - "node_modules/@rest-hooks/normalizr": { - "version": "6.0.9", - "resolved": "https://registry.npmjs.org/@rest-hooks/normalizr/-/normalizr-6.0.9.tgz", - "integrity": "sha512-TFV8lEpktlevVqwvMOsIQ4EMbRseRjglJX8Sbq4UtYbBruH2OKmKQThYjDttOHZrTlH55sQ0qCtP/U2dCt9Tyg==", - "dependencies": { - "@babel/runtime": "^7.7.2" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/@rest-hooks/test": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@rest-hooks/test/-/test-6.2.0.tgz", - "integrity": "sha512-WPrjFeLvsc+OJM1VNwr5NM4fc0EoDo/qmbTi7rb27c21EJI1IIENXj/4EWg1WRtjMmY77sbq6IJTxxADr4u2OQ==", - "dev": true, - "dependencies": { - "@testing-library/react-hooks": "~7.0.0" - }, - "engines": { - "node": "^12.20 || ^13.7 || >=14" - }, - "peerDependencies": { - "@rest-hooks/core": "^1.0.0-0 || ^2.0.0-0", - "@types/react": "^16.8.4 || ^17.0.0 || ^18.0.0-0", - "react": "^16.8.4 || ^17.0.0 || ^18.0.0-0", - "redux": "^4.0.0", - "rest-hooks": "^5.0.11 || ^6.0.0-0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "redux": { - "optional": true - } - } - }, - "node_modules/@rest-hooks/use-enhanced-reducer": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@rest-hooks/use-enhanced-reducer/-/use-enhanced-reducer-1.0.5.tgz", - "integrity": "sha512-WZANpcS+FLNqzW409zzgOgdw6tdEKEJMLdGT6y6sZ9g6ZRriaZF0L+v7ybeVN6+W6t6p/EliNSBXHJhu6/pvTA==", - "peerDependencies": { - "@types/react": "^16.8.4 || ^17.0.0", - "react": "^16.8.4 || ^17.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@rollup/plugin-babel": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz", @@ -22334,14 +22247,6 @@ "safe-buffer": "~5.1.0" } }, - "node_modules/flux-standard-action": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/flux-standard-action/-/flux-standard-action-2.1.1.tgz", - "integrity": "sha512-W86GzmXmIiTVq/dpYVd2HtTIUX9c35Iq3ao3xR6qcKtuXgbu+BDEj72op5VnEIe/kpuSbhl+I8kT1iS2hpcusw==", - "dependencies": { - "lodash": "^4.17.15" - } - }, "node_modules/follow-redirects": { "version": "1.14.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", @@ -38725,28 +38630,6 @@ "node": ">=10" } }, - "node_modules/rest-hooks": { - "version": "5.0.20", - "resolved": "https://registry.npmjs.org/rest-hooks/-/rest-hooks-5.0.20.tgz", - "integrity": "sha512-FXgN8I1Jd/zv/HIJ/7tpZjkl6bH2mtNh6wmSJYL3KypDSyysq5SFS8khXN8eoo6CzGDSYYWqmpHi6XN37cflwQ==", - "dependencies": { - "@babel/runtime": "^7.7.2", - "@rest-hooks/core": "^1.0.16", - "@rest-hooks/endpoint": "^1.0.12" - }, - "engines": { - "node": ">=0.12" - }, - "peerDependencies": { - "@types/react": "^16.8.4 || ^17.0.0", - "react": "^16.8.4 || ^17.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -48902,50 +48785,6 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" }, - "@rest-hooks/core": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/@rest-hooks/core/-/core-1.0.16.tgz", - "integrity": "sha512-9Owt4hACJL6uwarEfQGn05/+hTa/GIFiSsQA1+ZGd5aIOX3LDC5gEXbk1AI4mScDmHrorWAVAy2YsLg6oHWoVA==", - "requires": { - "@babel/runtime": "^7.7.2", - "@rest-hooks/endpoint": "^1.0.12", - "@rest-hooks/normalizr": "^6.0.9", - "@rest-hooks/use-enhanced-reducer": "^1.0.5", - "flux-standard-action": "^2.1.1" - } - }, - "@rest-hooks/endpoint": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@rest-hooks/endpoint/-/endpoint-1.0.12.tgz", - "integrity": "sha512-PYTkBa9tvX/fytscd16WokYO6d1iuYvd8Cj17T8C4Re0GmJXAxsinA+rfyViw3cxXiMO7f4B0rWkDkdwQJhNRw==", - "requires": { - "@babel/runtime": "^7.7.2", - "@rest-hooks/normalizr": "^6.0.9" - } - }, - "@rest-hooks/normalizr": { - "version": "6.0.9", - "resolved": "https://registry.npmjs.org/@rest-hooks/normalizr/-/normalizr-6.0.9.tgz", - "integrity": "sha512-TFV8lEpktlevVqwvMOsIQ4EMbRseRjglJX8Sbq4UtYbBruH2OKmKQThYjDttOHZrTlH55sQ0qCtP/U2dCt9Tyg==", - "requires": { - "@babel/runtime": "^7.7.2" - } - }, - "@rest-hooks/test": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@rest-hooks/test/-/test-6.2.0.tgz", - "integrity": "sha512-WPrjFeLvsc+OJM1VNwr5NM4fc0EoDo/qmbTi7rb27c21EJI1IIENXj/4EWg1WRtjMmY77sbq6IJTxxADr4u2OQ==", - "dev": true, - "requires": { - "@testing-library/react-hooks": "~7.0.0" - } - }, - "@rest-hooks/use-enhanced-reducer": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@rest-hooks/use-enhanced-reducer/-/use-enhanced-reducer-1.0.5.tgz", - "integrity": "sha512-WZANpcS+FLNqzW409zzgOgdw6tdEKEJMLdGT6y6sZ9g6ZRriaZF0L+v7ybeVN6+W6t6p/EliNSBXHJhu6/pvTA==", - "requires": {} - }, "@rollup/plugin-babel": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz", @@ -62011,14 +61850,6 @@ } } }, - "flux-standard-action": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/flux-standard-action/-/flux-standard-action-2.1.1.tgz", - "integrity": "sha512-W86GzmXmIiTVq/dpYVd2HtTIUX9c35Iq3ao3xR6qcKtuXgbu+BDEj72op5VnEIe/kpuSbhl+I8kT1iS2hpcusw==", - "requires": { - "lodash": "^4.17.15" - } - }, "follow-redirects": { "version": "1.14.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", @@ -74098,16 +73929,6 @@ "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", "dev": true }, - "rest-hooks": { - "version": "5.0.20", - "resolved": "https://registry.npmjs.org/rest-hooks/-/rest-hooks-5.0.20.tgz", - "integrity": "sha512-FXgN8I1Jd/zv/HIJ/7tpZjkl6bH2mtNh6wmSJYL3KypDSyysq5SFS8khXN8eoo6CzGDSYYWqmpHi6XN37cflwQ==", - "requires": { - "@babel/runtime": "^7.7.2", - "@rest-hooks/core": "^1.0.16", - "@rest-hooks/endpoint": "^1.0.12" - } - }, "restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", From 11753712cc3f22731c5446d710b4b587660c7edd Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Wed, 30 Mar 2022 23:14:10 +0300 Subject: [PATCH 12/20] Disable refetch --- airbyte-webapp/src/views/common/StoreProvider.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/airbyte-webapp/src/views/common/StoreProvider.tsx b/airbyte-webapp/src/views/common/StoreProvider.tsx index f3d81a9f69d1d..104073c3b3bcd 100644 --- a/airbyte-webapp/src/views/common/StoreProvider.tsx +++ b/airbyte-webapp/src/views/common/StoreProvider.tsx @@ -7,6 +7,7 @@ const queryClient = new QueryClient({ suspense: true, refetchOnWindowFocus: false, refetchOnReconnect: false, + retry: 0, }, }, }); From ac111888d6c5e8fd85d4a2aa476df8bc5f60d555 Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Thu, 31 Mar 2022 11:26:34 +0300 Subject: [PATCH 13/20] Minor code update --- .../cypress/support/commands/source.js | 2 +- .../src/hooks/services/useDestinationHook.tsx | 45 +++++++++---------- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/airbyte-webapp-e2e-tests/cypress/support/commands/source.js b/airbyte-webapp-e2e-tests/cypress/support/commands/source.js index 72bc1a959569a..7750f92017eb0 100644 --- a/airbyte-webapp-e2e-tests/cypress/support/commands/source.js +++ b/airbyte-webapp-e2e-tests/cypress/support/commands/source.js @@ -10,7 +10,7 @@ Cypress.Commands.add("fillPgSourceForm", (name) => { cy.get("input[name=name]").type(name); cy.get("input[name='connectionConfiguration.host']").type("localhost"); - cy.get("input[name='connectionConfiguration.port']").type("{selectAll}{del}5433"); + cy.get("input[name='connectionConfiguration.port']").type("{selectAll}{del}5432"); cy.get("input[name='connectionConfiguration.database']").type("airbyte_ci"); cy.get("input[name='connectionConfiguration.username']").type("postgres"); cy.get("input[name='connectionConfiguration.password']").type( diff --git a/airbyte-webapp/src/hooks/services/useDestinationHook.tsx b/airbyte-webapp/src/hooks/services/useDestinationHook.tsx index f72ecb76ea879..d85ba75c443be 100644 --- a/airbyte-webapp/src/hooks/services/useDestinationHook.tsx +++ b/airbyte-webapp/src/hooks/services/useDestinationHook.tsx @@ -17,7 +17,7 @@ import { useInitService } from "services/useInitService"; import { DestinationService } from "core/domain/connector/DestinationService"; export const destinationsKeys = { - all: ["destination"] as const, + all: ["destinations"] as const, lists: () => [...destinationsKeys.all, "list"] as const, list: (filters: string) => [...destinationsKeys.lists(), { filters }] as const, @@ -82,35 +82,22 @@ const useCreateDestination = () => { destinationConnector?: ConnectorProps; }) => { const { values, destinationConnector } = createDestinationPayload; - try { - const result = await service.create({ - name: values.name, - destinationDefinitionId: - destinationConnector?.destinationDefinitionId, - workspaceId: workspace.workspaceId, - connectionConfiguration: values.connectionConfiguration, - }); + return service.create({ + name: values.name, + destinationDefinitionId: destinationConnector?.destinationDefinitionId, + workspaceId: workspace.workspaceId, + connectionConfiguration: values.connectionConfiguration, + }); + }, + { + onSuccess: (data, ctx) => { analyticsService.track("New Destination - Action", { action: "Tested connector - success", - connector_destination: destinationConnector?.name, - connector_destination_definition_id: - destinationConnector?.destinationDefinitionId, - }); - - return result; - } catch (e) { - analyticsService.track("New Destination - Action", { - action: "Tested connector - failure", - connector_destination: destinationConnector?.name, + connector_destination: ctx.destinationConnector?.name, connector_destination_definition_id: - destinationConnector?.destinationDefinitionId, + ctx.destinationConnector?.destinationDefinitionId, }); - throw e; - } - }, - { - onSuccess: (data) => { queryClient.setQueryData( destinationsKeys.lists(), (lst: DestinationList | undefined) => ({ @@ -118,6 +105,14 @@ const useCreateDestination = () => { }) ); }, + onError: (_, ctx) => { + analyticsService.track("New Destination - Action", { + action: "Tested connector - failure", + connector_destination: ctx.destinationConnector?.name, + connector_destination_definition_id: + ctx.destinationConnector?.destinationDefinitionId, + }); + }, } ); }; From a446f080cc30bc1e0a4ce9d77dc14a56df5929bd Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Thu, 31 Mar 2022 11:36:13 +0300 Subject: [PATCH 14/20] Fix wrong port --- airbyte-webapp-e2e-tests/cypress/support/commands/source.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airbyte-webapp-e2e-tests/cypress/support/commands/source.js b/airbyte-webapp-e2e-tests/cypress/support/commands/source.js index 7750f92017eb0..72bc1a959569a 100644 --- a/airbyte-webapp-e2e-tests/cypress/support/commands/source.js +++ b/airbyte-webapp-e2e-tests/cypress/support/commands/source.js @@ -10,7 +10,7 @@ Cypress.Commands.add("fillPgSourceForm", (name) => { cy.get("input[name=name]").type(name); cy.get("input[name='connectionConfiguration.host']").type("localhost"); - cy.get("input[name='connectionConfiguration.port']").type("{selectAll}{del}5432"); + cy.get("input[name='connectionConfiguration.port']").type("{selectAll}{del}5433"); cy.get("input[name='connectionConfiguration.database']").type("airbyte_ci"); cy.get("input[name='connectionConfiguration.username']").type("postgres"); cy.get("input[name='connectionConfiguration.password']").type( From 83667dd9e9fbfafaaf7d2cd862a144d040b2a801 Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Thu, 31 Mar 2022 11:52:44 +0300 Subject: [PATCH 15/20] set initialSetupComplete for /get request in e2e --- .../cypress/support/commands/workspaces.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/airbyte-webapp-e2e-tests/cypress/support/commands/workspaces.js b/airbyte-webapp-e2e-tests/cypress/support/commands/workspaces.js index 3111fc247cf25..c69aeee9dec0f 100644 --- a/airbyte-webapp-e2e-tests/cypress/support/commands/workspaces.js +++ b/airbyte-webapp-e2e-tests/cypress/support/commands/workspaces.js @@ -1,9 +1,9 @@ Cypress.Commands.add("initialSetupCompleted", (completed = true) => { // Modify the workspaces/list response to mark every workspace as "initialSetupComplete" to ensure we're not showing // the setup/preference page for any workspace if this method got called. - cy.intercept("POST", "/api/v1/workspaces/list", (req) => { + cy.intercept("POST", "/api/v1/workspaces/get", (req) => { req.continue(res => { - res.body.workspaces = res.body.workspaces.map(ws => ({ ...ws, initialSetupComplete: completed })); + res.body.workspaces = res.body.initialSetupComplete = true; res.send(res.body); }); }); From c101c2c7ba0a6e441710668954a53867707e9e97 Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Thu, 31 Mar 2022 12:01:36 +0300 Subject: [PATCH 16/20] Add query mocks --- airbyte-webapp/.storybook/main.ts | 3 +- airbyte-webapp/package-lock.json | 82 +++++++++++++++++++ airbyte-webapp/package.json | 1 + .../Connector/ServiceForm/index.stories.tsx | 16 +++- 4 files changed, 100 insertions(+), 2 deletions(-) diff --git a/airbyte-webapp/.storybook/main.ts b/airbyte-webapp/.storybook/main.ts index 46e593d31e009..9d87a917494ca 100644 --- a/airbyte-webapp/.storybook/main.ts +++ b/airbyte-webapp/.storybook/main.ts @@ -1,12 +1,13 @@ module.exports = { core: { - builder: 'webpack5', + builder: "webpack5", }, stories: ["../src/**/*.stories.@(ts|tsx)"], addons: [ "@storybook/addon-links", "@storybook/addon-essentials", "@storybook/preset-create-react-app", + "storybook-addon-mock/register", ], webpackFinal: (config) => { config.resolve.modules.push(process.cwd() + "/node_modules"); diff --git a/airbyte-webapp/package-lock.json b/airbyte-webapp/package-lock.json index 1a76f6255d6ee..09a6b64ae028f 100644 --- a/airbyte-webapp/package-lock.json +++ b/airbyte-webapp/package-lock.json @@ -86,6 +86,7 @@ "prettier": "2.2.1", "react-scripts": "^5.0.0", "react-select-event": "^5.3.0", + "storybook-addon-mock": "^2.3.1", "tar": "^6.1.11", "tmpl": "^1.0.5", "typescript": "^4.5.0" @@ -31004,6 +31005,15 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mock-xmlhttprequest": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/mock-xmlhttprequest/-/mock-xmlhttprequest-7.0.4.tgz", + "integrity": "sha512-hA0fIHy/74p5DE0rdmrpU0sV1U+gnWTcgShWequGRLy0L1eT+zY0ozFukawpLaxMwIA+orRcqFRElYwT+5p81A==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -32200,6 +32210,12 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "node_modules/path-to-regexp": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.0.tgz", + "integrity": "sha512-f66KywYG6+43afgE/8j/GoiNyygk/bnoCbps++3ErRKsIYkGGupyv07R2Ok5m9i67Iqc+T2g1eAUGUPzWhYTyg==", + "dev": true + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -33592,6 +33608,19 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-json-editor-ajrm": { + "version": "2.5.13", + "resolved": "https://registry.npmjs.org/react-json-editor-ajrm/-/react-json-editor-ajrm-2.5.13.tgz", + "integrity": "sha512-uYRJFzY34w7coLxeWPFZGyQpWdBKK5e8R9jBZTJ5gAFp3WuGVG2DdGZ8oJKOVJy0hqkxS9DzJIzGmmxHHQ9afA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.0.0-rc.0" + }, + "peerDependencies": { + "react": ">=16.2.0", + "react-dom": ">=16.2.0" + } + }, "node_modules/react-lazylog": { "version": "4.5.3", "resolved": "https://registry.npmjs.org/react-lazylog/-/react-lazylog-4.5.3.tgz", @@ -40295,6 +40324,24 @@ "integrity": "sha512-iJtHSGmNgAUx0b/MCS6ASGxb//hGrHHRgzvN+K5bvkBTN7A9RTpPSf1WSp+nPGvWCJ1jRnvY7MKnuqfoi3OEqg==", "dev": true }, + "node_modules/storybook-addon-mock": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/storybook-addon-mock/-/storybook-addon-mock-2.3.1.tgz", + "integrity": "sha512-NJsRDGWAFssMKyxExLDJqjns7H1ZUsHQXT+cU8RjrtHucaYtiA+RzYzBMSpsmCPVlmEELyQx/MF125sJNLCC7A==", + "dev": true, + "dependencies": { + "@storybook/addons": "^6.2.9", + "@storybook/api": "^6.2.9", + "@storybook/components": "^6.2.9", + "mock-xmlhttprequest": "^7.0.3", + "path-to-regexp": "^6.2.0", + "react-json-editor-ajrm": "^2.5.13" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + } + }, "node_modules/stream-browserify": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", @@ -68410,6 +68457,12 @@ "minimist": "^1.2.5" } }, + "mock-xmlhttprequest": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/mock-xmlhttprequest/-/mock-xmlhttprequest-7.0.4.tgz", + "integrity": "sha512-hA0fIHy/74p5DE0rdmrpU0sV1U+gnWTcgShWequGRLy0L1eT+zY0ozFukawpLaxMwIA+orRcqFRElYwT+5p81A==", + "dev": true + }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -69388,6 +69441,12 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "path-to-regexp": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.0.tgz", + "integrity": "sha512-f66KywYG6+43afgE/8j/GoiNyygk/bnoCbps++3ErRKsIYkGGupyv07R2Ok5m9i67Iqc+T2g1eAUGUPzWhYTyg==", + "dev": true + }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -70461,6 +70520,15 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-json-editor-ajrm": { + "version": "2.5.13", + "resolved": "https://registry.npmjs.org/react-json-editor-ajrm/-/react-json-editor-ajrm-2.5.13.tgz", + "integrity": "sha512-uYRJFzY34w7coLxeWPFZGyQpWdBKK5e8R9jBZTJ5gAFp3WuGVG2DdGZ8oJKOVJy0hqkxS9DzJIzGmmxHHQ9afA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.0.0-rc.0" + } + }, "react-lazylog": { "version": "4.5.3", "resolved": "https://registry.npmjs.org/react-lazylog/-/react-lazylog-4.5.3.tgz", @@ -75300,6 +75368,20 @@ "integrity": "sha512-iJtHSGmNgAUx0b/MCS6ASGxb//hGrHHRgzvN+K5bvkBTN7A9RTpPSf1WSp+nPGvWCJ1jRnvY7MKnuqfoi3OEqg==", "dev": true }, + "storybook-addon-mock": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/storybook-addon-mock/-/storybook-addon-mock-2.3.1.tgz", + "integrity": "sha512-NJsRDGWAFssMKyxExLDJqjns7H1ZUsHQXT+cU8RjrtHucaYtiA+RzYzBMSpsmCPVlmEELyQx/MF125sJNLCC7A==", + "dev": true, + "requires": { + "@storybook/addons": "^6.2.9", + "@storybook/api": "^6.2.9", + "@storybook/components": "^6.2.9", + "mock-xmlhttprequest": "^7.0.3", + "path-to-regexp": "^6.2.0", + "react-json-editor-ajrm": "^2.5.13" + } + }, "stream-browserify": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", diff --git a/airbyte-webapp/package.json b/airbyte-webapp/package.json index 6647ddcd3ca12..8e8a993485029 100644 --- a/airbyte-webapp/package.json +++ b/airbyte-webapp/package.json @@ -93,6 +93,7 @@ "prettier": "2.2.1", "react-scripts": "^5.0.0", "react-select-event": "^5.3.0", + "storybook-addon-mock": "^2.3.1", "tar": "^6.1.11", "tmpl": "^1.0.5", "typescript": "^4.5.0" diff --git a/airbyte-webapp/src/views/Connector/ServiceForm/index.stories.tsx b/airbyte-webapp/src/views/Connector/ServiceForm/index.stories.tsx index f79d39d601018..c3096e431e100 100644 --- a/airbyte-webapp/src/views/Connector/ServiceForm/index.stories.tsx +++ b/airbyte-webapp/src/views/Connector/ServiceForm/index.stories.tsx @@ -4,6 +4,7 @@ import { ServiceForm } from "./ServiceForm"; import { ContentCard } from "components"; import { ConnectorSpecification } from "core/domain/connector"; import { isSourceDefinitionSpecification } from "core/domain/connector/source"; +import withMock from "storybook-addon-mock"; const TempConnector = { name: "Service", @@ -18,7 +19,19 @@ const TempConnector = { export default { title: "Views/ServiceForm", component: ServiceForm, - parameters: { actions: { argTypesRegex: "^on.*" } }, + parameters: { + actions: { argTypesRegex: "^on.*" }, + mockData: [ + { + url: "http://localhost:8001/api/v1/workspaces/get", + method: "POST", + status: 200, + response: { + workspaceId: "", + }, + }, + ], + }, args: { formType: "source", formValues: { @@ -27,6 +40,7 @@ export default { onSubmit: (v) => console.log(v), availableServices: [TempConnector], }, + decorators: [withMock], } as ComponentMeta; const Template: ComponentStory = (args) => { From 8f3320e9e4141e11c37a871c46851c8ff9bf5e2a Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Thu, 31 Mar 2022 12:15:22 +0300 Subject: [PATCH 17/20] Use completed setup prop instead of true --- airbyte-webapp-e2e-tests/cypress/support/commands/workspaces.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airbyte-webapp-e2e-tests/cypress/support/commands/workspaces.js b/airbyte-webapp-e2e-tests/cypress/support/commands/workspaces.js index c69aeee9dec0f..ba797b68b0612 100644 --- a/airbyte-webapp-e2e-tests/cypress/support/commands/workspaces.js +++ b/airbyte-webapp-e2e-tests/cypress/support/commands/workspaces.js @@ -3,7 +3,7 @@ Cypress.Commands.add("initialSetupCompleted", (completed = true) => { // the setup/preference page for any workspace if this method got called. cy.intercept("POST", "/api/v1/workspaces/get", (req) => { req.continue(res => { - res.body.workspaces = res.body.initialSetupComplete = true; + res.body.workspaces = res.body.initialSetupComplete = completed; res.send(res.body); }); }); From 8dfce1c9588cb8790d4595f0b0673bc6942fe328 Mon Sep 17 00:00:00 2001 From: Artem Astapenko <3767150+Jamakase@users.noreply.github.com> Date: Fri, 1 Apr 2022 17:59:10 +0300 Subject: [PATCH 18/20] Fix e2e assignment Co-authored-by: Tim Roes --- airbyte-webapp-e2e-tests/cypress/support/commands/workspaces.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airbyte-webapp-e2e-tests/cypress/support/commands/workspaces.js b/airbyte-webapp-e2e-tests/cypress/support/commands/workspaces.js index ba797b68b0612..3c017517e3ffc 100644 --- a/airbyte-webapp-e2e-tests/cypress/support/commands/workspaces.js +++ b/airbyte-webapp-e2e-tests/cypress/support/commands/workspaces.js @@ -3,7 +3,7 @@ Cypress.Commands.add("initialSetupCompleted", (completed = true) => { // the setup/preference page for any workspace if this method got called. cy.intercept("POST", "/api/v1/workspaces/get", (req) => { req.continue(res => { - res.body.workspaces = res.body.initialSetupComplete = completed; + res.body.initialSetupComplete = completed; res.send(res.body); }); }); From 8a2a87bd04310b795b10d5b36a1c601332b34401 Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Fri, 1 Apr 2022 19:22:09 +0300 Subject: [PATCH 19/20] Add invalidation scopes --- .../src/hooks/services/useConnectionHook.tsx | 3 ++- .../src/hooks/services/useDestinationHook.tsx | 3 ++- airbyte-webapp/src/hooks/services/useSourceHook.tsx | 3 ++- .../src/packages/cloud/services/users/UseUserHook.ts | 3 ++- .../cloud/services/workspaces/WorkspacesService.tsx | 5 +++-- airbyte-webapp/src/services/Scope.ts | 2 ++ .../connector/DestinationDefinitionService.ts | 3 ++- .../DestinationDefinitionSpecificationService.tsx | 3 ++- .../src/services/connector/SourceDefinitionService.ts | 3 ++- .../SourceDefinitionSpecificationService.tsx | 3 ++- .../src/services/workspaces/WorkspacesService.tsx | 11 ++++++++--- 11 files changed, 29 insertions(+), 13 deletions(-) create mode 100644 airbyte-webapp/src/services/Scope.ts diff --git a/airbyte-webapp/src/hooks/services/useConnectionHook.tsx b/airbyte-webapp/src/hooks/services/useConnectionHook.tsx index e37050c7d8e26..425ce17908432 100644 --- a/airbyte-webapp/src/hooks/services/useConnectionHook.tsx +++ b/airbyte-webapp/src/hooks/services/useConnectionHook.tsx @@ -23,9 +23,10 @@ import { useDefaultRequestMiddlewares } from "services/useDefaultRequestMiddlewa import { useInitService } from "services/useInitService"; import { ConnectionService } from "core/domain/connection/ConnectionService"; import { useCurrentWorkspace } from "./useWorkspace"; +import { SCOPE_WORKSPACE } from "../../services/Scope"; export const connectionsKeys = { - all: ["connections"] as const, + all: [SCOPE_WORKSPACE, "connections"] as const, lists: () => [...connectionsKeys.all, "list"] as const, list: (filters: string) => [...connectionsKeys.lists(), { filters }] as const, detail: (connectionId: string) => diff --git a/airbyte-webapp/src/hooks/services/useDestinationHook.tsx b/airbyte-webapp/src/hooks/services/useDestinationHook.tsx index d85ba75c443be..494b9a264b670 100644 --- a/airbyte-webapp/src/hooks/services/useDestinationHook.tsx +++ b/airbyte-webapp/src/hooks/services/useDestinationHook.tsx @@ -15,9 +15,10 @@ import { useConfig } from "config"; import { useDefaultRequestMiddlewares } from "services/useDefaultRequestMiddlewares"; import { useInitService } from "services/useInitService"; import { DestinationService } from "core/domain/connector/DestinationService"; +import { SCOPE_WORKSPACE } from "../../services/Scope"; export const destinationsKeys = { - all: ["destinations"] as const, + all: [SCOPE_WORKSPACE, "destinations"] as const, lists: () => [...destinationsKeys.all, "list"] as const, list: (filters: string) => [...destinationsKeys.lists(), { filters }] as const, diff --git a/airbyte-webapp/src/hooks/services/useSourceHook.tsx b/airbyte-webapp/src/hooks/services/useSourceHook.tsx index 40d6902cb609b..a57e3780f06c9 100644 --- a/airbyte-webapp/src/hooks/services/useSourceHook.tsx +++ b/airbyte-webapp/src/hooks/services/useSourceHook.tsx @@ -18,9 +18,10 @@ import { SourceService } from "core/domain/connector/SourceService"; import { isDefined } from "utils/common"; import { SyncSchema } from "core/domain/catalog"; import { JobInfo } from "core/domain/job"; +import { SCOPE_WORKSPACE } from "../../services/Scope"; export const sourcesKeys = { - all: ["sources"] as const, + all: [SCOPE_WORKSPACE, "sources"] as const, lists: () => [...sourcesKeys.all, "list"] as const, list: (filters: string) => [...sourcesKeys.lists(), { filters }] as const, detail: (sourceId: string) => diff --git a/airbyte-webapp/src/packages/cloud/services/users/UseUserHook.ts b/airbyte-webapp/src/packages/cloud/services/users/UseUserHook.ts index e9ed39923deb9..201109f219034 100644 --- a/airbyte-webapp/src/packages/cloud/services/users/UseUserHook.ts +++ b/airbyte-webapp/src/packages/cloud/services/users/UseUserHook.ts @@ -2,9 +2,10 @@ import { useMutation, useQuery, useQueryClient } from "react-query"; import { useCurrentWorkspace } from "hooks/services/useWorkspace"; import { useGetUserService } from "./UserService"; +import { SCOPE_WORKSPACE } from "services/Scope"; export const userKeys = { - all: ["users"] as const, + all: [SCOPE_WORKSPACE, "users"] as const, lists: () => [...userKeys.all, "list"] as const, list: (filters: string) => [...userKeys.lists(), { filters }] as const, details: () => [...userKeys.all, "detail"] as const, diff --git a/airbyte-webapp/src/packages/cloud/services/workspaces/WorkspacesService.tsx b/airbyte-webapp/src/packages/cloud/services/workspaces/WorkspacesService.tsx index 771353afabb8e..d2e80701e0903 100644 --- a/airbyte-webapp/src/packages/cloud/services/workspaces/WorkspacesService.tsx +++ b/airbyte-webapp/src/packages/cloud/services/workspaces/WorkspacesService.tsx @@ -4,6 +4,7 @@ import { useQuery, useQueryClient, } from "react-query"; +import { useCallback } from "react"; import type { CloudWorkspace, @@ -16,10 +17,10 @@ import { useConfig } from "packages/cloud/services/config"; import { useDefaultRequestMiddlewares } from "services/useDefaultRequestMiddlewares"; import { useInitService } from "services/useInitService"; import { QueryObserverSuccessResult } from "react-query/types/core/types"; -import { useCallback } from "react"; +import { SCOPE_USER } from "services/Scope"; export const workspaceKeys = { - all: ["cloud_workspaces"] as const, + all: [SCOPE_USER, "cloud_workspaces"] as const, lists: () => [...workspaceKeys.all, "list"] as const, list: (filters: string) => [...workspaceKeys.lists(), { filters }] as const, details: () => [...workspaceKeys.all, "detail"] as const, diff --git a/airbyte-webapp/src/services/Scope.ts b/airbyte-webapp/src/services/Scope.ts new file mode 100644 index 0000000000000..a63568636c855 --- /dev/null +++ b/airbyte-webapp/src/services/Scope.ts @@ -0,0 +1,2 @@ +export const SCOPE_WORKSPACE = "scope:workspace"; +export const SCOPE_USER = "scope:user"; diff --git a/airbyte-webapp/src/services/connector/DestinationDefinitionService.ts b/airbyte-webapp/src/services/connector/DestinationDefinitionService.ts index 20324d005fdf8..1af0bde94ccf9 100644 --- a/airbyte-webapp/src/services/connector/DestinationDefinitionService.ts +++ b/airbyte-webapp/src/services/connector/DestinationDefinitionService.ts @@ -15,9 +15,10 @@ import { } from "core/domain/connector/DestinationDefinitionService"; import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { isDefined } from "utils/common"; +import { SCOPE_WORKSPACE } from "../Scope"; export const destinationDefinitionKeys = { - all: ["destinationDefinition"] as const, + all: [SCOPE_WORKSPACE, "destinationDefinition"] as const, lists: () => [...destinationDefinitionKeys.all, "list"] as const, detail: (id: string) => [...destinationDefinitionKeys.all, "details", id] as const, diff --git a/airbyte-webapp/src/services/connector/DestinationDefinitionSpecificationService.tsx b/airbyte-webapp/src/services/connector/DestinationDefinitionSpecificationService.tsx index 25ed0e57231fc..c520d38cca6f5 100644 --- a/airbyte-webapp/src/services/connector/DestinationDefinitionSpecificationService.tsx +++ b/airbyte-webapp/src/services/connector/DestinationDefinitionSpecificationService.tsx @@ -10,9 +10,10 @@ import { useDefaultRequestMiddlewares } from "services/useDefaultRequestMiddlewa import { useInitService } from "services/useInitService"; import { DestinationDefinitionSpecificationService } from "core/domain/connector/DestinationDefinitionSpecificationService"; import { isDefined } from "utils/common"; +import { SCOPE_WORKSPACE } from "../Scope"; export const destinationDefinitionSpecificationKeys = { - all: ["destinationDefinitionSpecification"] as const, + all: [SCOPE_WORKSPACE, "destinationDefinitionSpecification"] as const, detail: (id: string | number) => [...destinationDefinitionSpecificationKeys.all, "details", id] as const, }; diff --git a/airbyte-webapp/src/services/connector/SourceDefinitionService.ts b/airbyte-webapp/src/services/connector/SourceDefinitionService.ts index 8bf57cb5e30ed..c36816b5f6341 100644 --- a/airbyte-webapp/src/services/connector/SourceDefinitionService.ts +++ b/airbyte-webapp/src/services/connector/SourceDefinitionService.ts @@ -15,9 +15,10 @@ import { } from "core/domain/connector/SourceDefinitionService"; import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; import { isDefined } from "utils/common"; +import { SCOPE_WORKSPACE } from "../Scope"; export const sourceDefinitionKeys = { - all: ["sourceDefinition"] as const, + all: [SCOPE_WORKSPACE, "sourceDefinition"] as const, lists: () => [...sourceDefinitionKeys.all, "list"] as const, detail: (id: string) => [...sourceDefinitionKeys.all, "details", id] as const, }; diff --git a/airbyte-webapp/src/services/connector/SourceDefinitionSpecificationService.tsx b/airbyte-webapp/src/services/connector/SourceDefinitionSpecificationService.tsx index f4033b0f6b214..71593d5b37220 100644 --- a/airbyte-webapp/src/services/connector/SourceDefinitionSpecificationService.tsx +++ b/airbyte-webapp/src/services/connector/SourceDefinitionSpecificationService.tsx @@ -10,9 +10,10 @@ import { useDefaultRequestMiddlewares } from "services/useDefaultRequestMiddlewa import { useInitService } from "services/useInitService"; import { SourceDefinitionSpecificationService } from "core/domain/connector/SourceDefinitionSpecificationService"; import { isDefined } from "utils/common"; +import { SCOPE_WORKSPACE } from "../Scope"; export const sourceDefinitionSpecificationKeys = { - all: ["sourceDefinitionSpecification"] as const, + all: [SCOPE_WORKSPACE, "sourceDefinitionSpecification"] as const, detail: (id: string | number) => [...sourceDefinitionSpecificationKeys.all, "details", id] as const, }; diff --git a/airbyte-webapp/src/services/workspaces/WorkspacesService.tsx b/airbyte-webapp/src/services/workspaces/WorkspacesService.tsx index d02d199eeb74c..66115a8791b5a 100644 --- a/airbyte-webapp/src/services/workspaces/WorkspacesService.tsx +++ b/airbyte-webapp/src/services/workspaces/WorkspacesService.tsx @@ -12,9 +12,10 @@ import { RoutePaths } from "pages/routePaths"; import { useConfig } from "config"; import { useDefaultRequestMiddlewares } from "../useDefaultRequestMiddlewares"; import { useInitService } from "../useInitService"; +import { SCOPE_USER, SCOPE_WORKSPACE } from "../Scope"; export const workspaceKeys = { - all: ["workspaces"] as const, + all: [SCOPE_USER, "workspaces"] as const, lists: () => [...workspaceKeys.all, "list"] as const, list: (filters: string) => [...workspaceKeys.lists(), { filters }] as const, detail: (workspaceId: string) => @@ -43,7 +44,7 @@ const useSelectWorkspace = (): (( } else { push(`/${RoutePaths.Workspaces}/${workspace}`); } - await queryClient.resetQueries(); + await queryClient.removeQueries(SCOPE_WORKSPACE); }, [push, queryClient] ); @@ -51,11 +52,15 @@ const useSelectWorkspace = (): (( export const WorkspaceServiceProvider: React.FC = ({ children }) => { const selectWorkspace = useSelectWorkspace(); + const queryClient = useQueryClient(); const ctx = useMemo( () => ({ selectWorkspace, - exitWorkspace: () => selectWorkspace(""), + exitWorkspace: () => { + selectWorkspace(""); + queryClient.removeQueries(SCOPE_WORKSPACE); + }, }), [selectWorkspace] ); From cf6e877a26baef50e14b83d1a7c2597ab30b022c Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Fri, 1 Apr 2022 19:29:15 +0300 Subject: [PATCH 20/20] Remove not required invalidation --- airbyte-webapp/src/services/workspaces/WorkspacesService.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/airbyte-webapp/src/services/workspaces/WorkspacesService.tsx b/airbyte-webapp/src/services/workspaces/WorkspacesService.tsx index 66115a8791b5a..c0901f7756b66 100644 --- a/airbyte-webapp/src/services/workspaces/WorkspacesService.tsx +++ b/airbyte-webapp/src/services/workspaces/WorkspacesService.tsx @@ -52,14 +52,12 @@ const useSelectWorkspace = (): (( export const WorkspaceServiceProvider: React.FC = ({ children }) => { const selectWorkspace = useSelectWorkspace(); - const queryClient = useQueryClient(); const ctx = useMemo( () => ({ selectWorkspace, exitWorkspace: () => { selectWorkspace(""); - queryClient.removeQueries(SCOPE_WORKSPACE); }, }), [selectWorkspace]