Skip to content

Commit

Permalink
🪟Persist unsaved changes on schema refresh (#13895)
Browse files Browse the repository at this point in the history
* add form values tracker context

* add clarifying comment

* add same functionality to create connection

* Update airbyte-webapp/src/components/CreateConnectionContent/CreateConnectionContent.tsx

Co-authored-by: Edmundo Ruiz Ghanem <168664+edmundito@users.noreply.github.com>

Co-authored-by: Edmundo Ruiz Ghanem <168664+edmundito@users.noreply.github.com>
  • Loading branch information
teallarson and edmundito authored Jun 23, 2022
1 parent e348d01 commit a9f216c
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { faRedoAlt } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import React, { Suspense, useMemo } from "react";
import React, { Suspense, useMemo, useState } from "react";
import { FormattedMessage } from "react-intl";
import styled from "styled-components";

Expand All @@ -13,6 +13,7 @@ import { LogsRequestError } from "core/request/LogsRequestError";
import { useAnalyticsService } from "hooks/services/Analytics/useAnalyticsService";
import { useCreateConnection, ValuesProps } from "hooks/services/useConnectionHook";
import ConnectionForm from "views/Connection/ConnectionForm";
import { FormikConnectionFormValues } from "views/Connection/ConnectionForm/formConfig";

import { DestinationRead, SourceRead, WebBackendConnectionRead } from "../../core/request/AirbyteClient";
import { useDiscoverSchema } from "../../hooks/services/useSourceHook";
Expand Down Expand Up @@ -52,14 +53,21 @@ const CreateConnectionContent: React.FC<CreateConnectionContentProps> = ({

const { schema, isLoading, schemaErrorStatus, catalogId, onDiscoverSchema } = useDiscoverSchema(source.sourceId);

const [connectionFormValues, setConnectionFormValues] = useState<FormikConnectionFormValues>();

const connection = useMemo(
() => ({
name: connectionFormValues?.name ?? "",
namespaceDefinition: connectionFormValues?.namespaceDefinition,
namespaceFormat: connectionFormValues?.namespaceFormat,
prefix: connectionFormValues?.prefix,
schedule: connectionFormValues?.schedule,
syncCatalog: schema,
destination,
source,
catalogId,
}),
[schema, destination, source, catalogId]
[connectionFormValues, schema, destination, source, catalogId]
);

const onSubmitConnectionStep = async (values: ValuesProps) => {
Expand Down Expand Up @@ -126,6 +134,7 @@ const CreateConnectionContent: React.FC<CreateConnectionContentProps> = ({
</Button>
}
onSubmit={onSubmitConnectionStep}
onChangeValues={setConnectionFormValues}
/>
</Suspense>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { faSyncAlt } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { FormikHelpers } from "formik";
import React, { useState } from "react";
import React, { useMemo, useState } from "react";
import { FormattedMessage } from "react-intl";
import { useAsyncFn } from "react-use";
import styled from "styled-components";
Expand All @@ -20,6 +20,7 @@ import {
} from "hooks/services/useConnectionHook";
import { equal } from "utils/objects";
import ConnectionForm from "views/Connection/ConnectionForm";
import { FormikConnectionFormValues } from "views/Connection/ConnectionForm/formConfig";

interface ReplicationViewProps {
onAfterSaveSchema: () => void;
Expand Down Expand Up @@ -52,6 +53,7 @@ export const ReplicationView: React.FC<ReplicationViewProps> = ({ onAfterSaveSch
const { openConfirmationModal, closeConfirmationModal } = useConfirmationModalService();
const [activeUpdatingSchemaMode, setActiveUpdatingSchemaMode] = useState(false);
const [saved, setSaved] = useState(false);
const [connectionFormValues, setConnectionFormValues] = useState<FormikConnectionFormValues>();

const { mutateAsync: updateConnection } = useUpdateConnection();
const { mutateAsync: resetConnection } = useResetConnection();
Expand All @@ -65,7 +67,24 @@ export const ReplicationView: React.FC<ReplicationViewProps> = ({ onAfterSaveSch
[connectionId]
);

const connection = activeUpdatingSchemaMode ? connectionWithRefreshCatalog : initialConnection;
const connection = useMemo(() => {
if (activeUpdatingSchemaMode && connectionWithRefreshCatalog) {
// merge connectionFormValues (unsaved previous form state) with the refreshed connection data:
// 1. if there is a namespace definition, format, prefix, or schedule in connectionFormValues,
// use those and fill in the rest from the database
// 2. otherwise, use the values from the database
// 3. if none of the above, use the default values.
return {
...connectionWithRefreshCatalog,
namespaceDefinition:
connectionFormValues?.namespaceDefinition ?? connectionWithRefreshCatalog.namespaceDefinition,
namespaceFormat: connectionFormValues?.namespaceFormat ?? connectionWithRefreshCatalog.namespaceFormat,
prefix: connectionFormValues?.prefix ?? connectionWithRefreshCatalog.prefix,
schedule: connectionFormValues?.schedule ?? connectionWithRefreshCatalog.schedule,
};
}
return initialConnection;
}, [activeUpdatingSchemaMode, connectionWithRefreshCatalog, initialConnection, connectionFormValues]);

const onSubmit = async (values: ValuesProps, formikHelpers?: FormikHelpers<ValuesProps>) => {
if (!connection) {
Expand Down Expand Up @@ -125,7 +144,7 @@ export const ReplicationView: React.FC<ReplicationViewProps> = ({ onAfterSaveSch
await refreshCatalog();
};

const onExitRefreshCatalogMode = () => {
const onCancelConnectionFormEdit = () => {
setActiveUpdatingSchemaMode(false);
};

Expand Down Expand Up @@ -158,9 +177,10 @@ export const ReplicationView: React.FC<ReplicationViewProps> = ({ onAfterSaveSch
onSubmit={onSubmitForm}
onReset={onReset}
successMessage={saved && <FormattedMessage id="form.changesSaved" />}
onCancel={onExitRefreshCatalogMode}
onCancel={onCancelConnectionFormEdit}
editSchemeMode={activeUpdatingSchemaMode}
additionalSchemaControl={renderUpdateSchemaButton()}
onChangeValues={setConnectionFormValues}
/>
) : (
<LoadingSchema />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Field, FieldProps, Form, Formik, FormikHelpers } from "formik";
import { Field, FieldProps, Form, Formik, FormikHelpers, useFormikContext } from "formik";
import React, { useCallback, useState } from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { useToggle } from "react-use";
import { useDebounce } from "react-use";
import styled from "styled-components";

import { ControlLabels, DropDown, DropDownRow, H5, Input, Label } from "components";
Expand Down Expand Up @@ -93,6 +94,19 @@ interface ConnectionFormSubmitResult {

export type ConnectionFormMode = "create" | "edit" | "readonly";

function FormValuesChangeTracker<T>({ onChangeValues }: { onChangeValues?: (values: T) => void }) {
// Grab values from context
const { values } = useFormikContext<T>();
useDebounce(
() => {
onChangeValues?.(values);
},
200,
[values, onChangeValues]
);
return null;
}

interface ConnectionFormProps {
onSubmit: (values: ConnectionFormValues) => Promise<ConnectionFormSubmitResult | void>;
className?: string;
Expand All @@ -101,6 +115,7 @@ interface ConnectionFormProps {
onReset?: (connectionId?: string) => void;
onDropDownSelect?: (item: DropDownRow.IDataItem) => void;
onCancel?: () => void;
onChangeValues?: (values: FormikConnectionFormValues) => void;

/** Should be passed when connection is updated with withRefreshCatalog flag */
editSchemeMode?: boolean;
Expand All @@ -124,6 +139,7 @@ const ConnectionForm: React.FC<ConnectionFormProps> = ({
editSchemeMode,
additionalSchemaControl,
connection,
onChangeValues,
}) => {
const { openConfirmationModal, closeConfirmationModal } = useConfirmationModalService();
const destDefinition = useGetDestinationDefinitionSpecification(connection.destination.destinationDefinitionId);
Expand Down Expand Up @@ -204,6 +220,7 @@ const ConnectionForm: React.FC<ConnectionFormProps> = ({
{({ isSubmitting, setFieldValue, isValid, dirty, resetForm, values }) => (
<FormContainer className={className}>
<FormChangeTracker changed={dirty} formId={formId} />
<FormValuesChangeTracker onChangeValues={onChangeValues} />
{!isEditMode && (
<StyledSection>
<Field name="name">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ const useInitialValues = (
const initialValues: FormikConnectionFormValues = {
name: connection.name ?? `${connection.source.name} <> ${connection.destination.name}`,
syncCatalog: initialSchema,
schedule: connection.connectionId ? connection.schedule ?? null : DEFAULT_SCHEDULE,
schedule: connection.connectionId || connection.schedule ? connection.schedule ?? null : DEFAULT_SCHEDULE,
prefix: connection.prefix || "",
namespaceDefinition: connection.namespaceDefinition || NamespaceDefinitionType.source,
namespaceFormat: connection.namespaceFormat ?? SOURCE_NAMESPACE_TAG,
Expand Down

0 comments on commit a9f216c

Please sign in to comment.