Skip to content

Commit 786e04f

Browse files
authored
🪟Auto detect schema: Remove backdrop on non-breaking change, add toast on no refresh (#21998)
* Don't show schema changes backdrop for non-breaking changes * Show toast when there are no schema updates detected after refresh * Remove commented out test in SchemaChangesBackdrop * Add NotificationService to TestWrapper
1 parent 6660b13 commit 786e04f

File tree

7 files changed

+49
-124
lines changed

7 files changed

+49
-124
lines changed

‎airbyte-webapp/src/components/connection/ConnectionForm/SchemaChangeBackdrop.tsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { useMemo } from "react";
21
import { FormattedMessage } from "react-intl";
32

43
import { Text } from "components/ui/Text";
@@ -21,19 +20,17 @@ export const SchemaChangeBackdrop: React.FC<React.PropsWithChildren<unknown>> =
2120

2221
const { hasBreakingSchemaChange, hasNonBreakingSchemaChange } = useSchemaChanges(schemaChange);
2322

24-
const schemaChangeImage = useMemo(() => {
25-
return hasBreakingSchemaChange ? <OctaviaRedFlag /> : hasNonBreakingSchemaChange ? <OctaviaYellowFlag /> : null;
26-
}, [hasBreakingSchemaChange, hasNonBreakingSchemaChange]);
27-
28-
if (!allowAutoDetectSchema || (!hasBreakingSchemaChange && !hasNonBreakingSchemaChange) || schemaHasBeenRefreshed) {
23+
if (!allowAutoDetectSchema || !hasBreakingSchemaChange || schemaHasBeenRefreshed) {
2924
return <>{children}</>;
3025
}
3126

3227
return (
3328
<div className={styles.schemaChangeBackdropContainer} data-testid="schemaChangesBackdrop">
3429
<div className={styles.backdrop}>
3530
<div className={styles.contentContainer}>
36-
<div>{schemaChangeImage}</div>
31+
<div>
32+
{hasBreakingSchemaChange ? <OctaviaRedFlag /> : hasNonBreakingSchemaChange ? <OctaviaYellowFlag /> : null}
33+
</div>
3734
<Text className={styles.text}>
3835
<FormattedMessage id="connectionForm.schemaChangesBackdrop.message" />
3936
</Text>

‎airbyte-webapp/src/components/connection/ConnectionForm/SchemaChangesBackdrop.test.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,22 @@ describe("SchemaChangesBackdrop", () => {
4343
userEvent.click(getByTestId("bg-button"));
4444
expect(buttonSpy).not.toHaveBeenCalled();
4545
});
46-
it("renders if there are non-breaking changes and prevents background interaction", () => {
46+
47+
it("does not render if there are non-breaking changes", () => {
4748
mockUseConnectionEditService.mockReturnValue({
4849
connection: { mockConnection, schemaChange: SchemaChange.non_breaking },
4950
schemaHasBeenRefreshed: false,
5051
schemaRefreshing: false,
5152
});
5253

53-
const { getByTestId } = renderComponent();
54+
const { queryByTestId, getByTestId } = renderComponent();
55+
56+
expect(queryByTestId("schemaChangesBackdrop")).toBeFalsy();
5457

55-
expect(getByTestId("schemaChangesBackdrop")).toMatchSnapshot();
5658
userEvent.click(getByTestId("bg-button"));
5759
expect(buttonSpy).not.toHaveBeenCalled();
5860
});
61+
5962
it("does not render if there are no changes", () => {
6063
mockUseConnectionEditService.mockReturnValue({
6164
connection: { mockConnection, schemaChange: SchemaChange.no_change },
@@ -70,6 +73,7 @@ describe("SchemaChangesBackdrop", () => {
7073
userEvent.click(getByTestId("bg-button"));
7174
expect(buttonSpy).not.toHaveBeenCalled();
7275
});
76+
7377
it("does not render if schema has been refreshed", () => {
7478
mockUseConnectionEditService.mockReturnValue({
7579
connection: mockConnection,

‎airbyte-webapp/src/components/connection/ConnectionForm/__snapshots__/SchemaChangesBackdrop.test.tsx.snap

Lines changed: 0 additions & 98 deletions
Large diffs are not rendered by default.

‎airbyte-webapp/src/hooks/services/ConnectionEdit/ConnectionEditService.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import pick from "lodash/pick";
22
import { useContext, useState, createContext, useCallback } from "react";
3+
import { useIntl } from "react-intl";
34
import { useAsyncFn } from "react-use";
45

56
import {
@@ -10,6 +11,7 @@ import {
1011
} from "core/request/AirbyteClient";
1112

1213
import { ConnectionFormServiceProvider } from "../ConnectionForm/ConnectionFormService";
14+
import { useNotificationService } from "../Notification/NotificationService";
1315
import { useGetConnection, useUpdateConnection, useWebConnectionService } from "../useConnectionHook";
1416
import { SchemaError } from "../useSourceHook";
1517

@@ -37,16 +39,30 @@ const getConnectionCatalog = (connection: WebBackendConnectionRead): ConnectionC
3739
pick(connection, ["syncCatalog", "catalogId"]);
3840

3941
const useConnectionEdit = ({ connectionId }: ConnectionEditProps): ConnectionEditHook => {
42+
const { formatMessage } = useIntl();
43+
const { registerNotification, unregisterNotificationById } = useNotificationService();
4044
const connectionService = useWebConnectionService();
4145
const [connection, setConnection] = useState(useGetConnection(connectionId));
4246
const [catalog, setCatalog] = useState<ConnectionCatalog>(() => getConnectionCatalog(connection));
4347
const [schemaHasBeenRefreshed, setSchemaHasBeenRefreshed] = useState(false);
4448

4549
const [{ loading: schemaRefreshing, error: schemaError }, refreshSchema] = useAsyncFn(async () => {
50+
unregisterNotificationById("connection.noDiff");
51+
4652
const refreshedConnection = await connectionService.getConnection(connectionId, true);
4753
if (refreshedConnection.catalogDiff && refreshedConnection.catalogDiff.transforms?.length > 0) {
4854
setConnection(refreshedConnection);
4955
setSchemaHasBeenRefreshed(true);
56+
} else {
57+
setConnection((connection) => ({
58+
...connection,
59+
schemaChange: refreshedConnection.schemaChange,
60+
}));
61+
62+
registerNotification({
63+
id: "connection.noDiff",
64+
text: formatMessage({ id: "connection.updateSchema.noDiff" }),
65+
});
5066
}
5167
}, [connectionId]);
5268

‎airbyte-webapp/src/hooks/services/Notification/NotificationService.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,16 @@ const NotificationService = ({ children }: { children: React.ReactNode }) => {
4949
);
5050
};
5151

52-
export const useNotificationService: (
53-
notification?: Notification,
54-
dependencies?: []
55-
) => {
52+
interface NotificationServiceHook {
5653
registerNotification: (notification: Notification) => void;
5754
unregisterAllNotifications: () => void;
5855
unregisterNotificationById: (notificationId: string | number) => void;
59-
} = (notification, dependencies) => {
56+
}
57+
58+
export const useNotificationService: (notification?: Notification, dependencies?: []) => NotificationServiceHook = (
59+
notification,
60+
dependencies
61+
) => {
6062
const notificationService = useContext(notificationServiceContext);
6163
if (!notificationService) {
6264
throw new Error("useNotificationService must be used within a NotificationService.");

‎airbyte-webapp/src/locales/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@
382382
"connection.updateSchema.streamName": "Stream name",
383383
"connection.updateSchema.namespace": "Namespace",
384384
"connection.updateSchema.dataType": "Data type",
385+
"connection.updateSchema.noDiff": "No changes were detected in the source schema.",
385386

386387
"connection.catalogTree.sourceDefined": "'<source defined>",
387388
"connection.catalogTree.sourceSchema": "'<source schema>",

‎airbyte-webapp/src/test-utils/testutils.tsx

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { ServicesProvider } from "core/servicesProvider";
1717
import { ConfirmationModalService } from "hooks/services/ConfirmationModal";
1818
import { defaultOssFeatures, FeatureItem, FeatureService } from "hooks/services/Feature";
1919
import { ModalServiceProvider } from "hooks/services/Modal";
20+
import { NotificationService } from "hooks/services/Notification";
2021
import en from "locales/en.json";
2122
import { AnalyticsProvider } from "views/common/AnalyticsProvider";
2223

@@ -56,17 +57,19 @@ export const TestWrapper: React.FC<React.PropsWithChildren<TestWrapperOptions>>
5657
<IntlProvider locale="en" messages={en} onError={() => null}>
5758
<ConfigContext.Provider value={{ config }}>
5859
<AnalyticsProvider>
59-
<FeatureService features={features}>
60-
<ServicesProvider>
61-
<ModalServiceProvider>
62-
<ConfirmationModalService>
63-
<QueryClientProvider client={new QueryClient()}>
64-
<MemoryRouter>{children}</MemoryRouter>
65-
</QueryClientProvider>
66-
</ConfirmationModalService>
67-
</ModalServiceProvider>
68-
</ServicesProvider>
69-
</FeatureService>
60+
<NotificationService>
61+
<FeatureService features={features}>
62+
<ServicesProvider>
63+
<ModalServiceProvider>
64+
<ConfirmationModalService>
65+
<QueryClientProvider client={new QueryClient()}>
66+
<MemoryRouter>{children}</MemoryRouter>
67+
</QueryClientProvider>
68+
</ConfirmationModalService>
69+
</ModalServiceProvider>
70+
</ServicesProvider>
71+
</FeatureService>
72+
</NotificationService>
7073
</AnalyticsProvider>
7174
</ConfigContext.Provider>
7275
</IntlProvider>

0 commit comments

Comments
 (0)