Skip to content

Commit

Permalink
Connection Form Refactor - Part Two (#16748)
Browse files Browse the repository at this point in the history
* Work started

* Minor cleanup

* Some cleanup

* Lots moved into context

* WIP, stepping in the right direction

* WIP testing

* Post-merge fix

* Observables!

* WIP tests

* Tests!

* CI test

* CI?

* perhaps

* Only show name field during create

* Fix Build

* Fix build

* Fixing a bug

* Fix failing test

* Fixes e2e

* Type consolidation

* useCallback, improvements to connection create onAfter, and removing dirty tracking

* cleanup

* Removing an unused prop

* errorStatusMessage and mapFormPropsToOperation tests

* useUniqueFormId and useInitialValues tests

* Cleanup, onFrequencySelect is moved to its use location, better test wrapper,  and expanding use of FormError

* Better formSubmit handling for new connection

* Commenting and some cleanup

* Comments!

* Fixing errors from the merge

* mock data cleanup

* Better TODO

* onFrequencySelect is now always called

* Edmundo CR

* Remove whitespace

* Move connection into state so it gets updated

* Consolidate on connection object

* Remove ModalCancel throw + form clearing in create submit function

* Some cleanup

* Fixing error

* Fixing build error

* Rename file

* Bridging changes to bring things inline

* Builds and tests run

* replication view almost works as expected

* About to embark on another huge change! Committing here before I break more things.

* slowly, unity

* Code appears to be working

* Some minor cleanup

* Form dirty change tracking

* WIP styling moving and fixing

* skip refresh on update if status !== active

* Fixing styles

* Minor

* More cleanup and improvements

* Rename CreateConnectionContent -> CreateConnection

* refreshSchema moved into the connection form service

* Re-add TODO

* schemaErrorStatus => schemaError

* CreateConnectionName -> CreateConnectionNameField

* Logic for readonly vs edit

* Added TODO, fixed a re-initialize issue

* Remove unused ConnectionForm component

* Improved refresh schema logic

* undoing some of the cancel work as it felt like the wrong place to do it

* No longer need to know if the status is updating, that info is available from the form edit context

* Move close modal & confirmation modal logic to where it's used

* Remove unnecessary useUnmount

* Cleanup and removal of unused

* Somehow missed removing this

* Fixing failing test

* Added missing initialvalues logic. Added connection form service tests.

* Connection Edit tests!

* connection replication test pt 1

* Moving files to make master merge easier

* Remove snapshot

* non-default export

* Remove unused

* Minor improvements

* Edmundo CR.

* name now optional for connection editing

* Schema refresh errors handled on connection replication view

* schema refreshing logic, confirm catalog dialog abstraction

* CreateConnection -> CreateConnectionForm

* form config test cleanup

* connection replication tab tests

* Create Connection Form Tests

* Better jest mocking!

* Fixing issue setting schedule type to manual

* Tests fixed, and test-utils is better!

* Changing back to Once

* Fixing build from master merge

* Updating snapshots. If these change a lot it may be worth altering the tests to avoid snapshots.

* Edmundo & Lake Code Review Comments

* Tim CR

* Fixing a styling issue

* ?? is less clever than I thought

* Form change tracking has been improved to not leave dirty forms around. As well as it has been moved into ConnectionFormFields for DRY purposes

* Update airbyte-webapp/src/hooks/services/ConnectionForm/ConnectionFormService.tsx

Co-authored-by: Lake Mossman <lake@airbyte.io>

* Update airbyte-webapp/src/hooks/services/ConnectionEdit/ConnectionEditService.test.tsx

Co-authored-by: Lake Mossman <lake@airbyte.io>

* Update airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/ConnectionReplicationTab.tsx

Co-authored-by: Lake Mossman <lake@airbyte.io>

* DRY out tests

* remove unknowns and fix mocked hook to be patch-style (#17698)

Co-authored-by: Krishna (kc) Glick <krishna@airbyte.io>

* Self-CR

* idk why this was an issue but whatever

* Update airbyte-webapp/src/hooks/services/ConnectionForm/ConnectionFormService.test.tsx

Co-authored-by: Teal Larson <LARSON.TEAL@GMAIL.COM>

* Update airbyte-webapp/src/hooks/services/ConnectionEdit/ConnectionEditService.test.tsx

Co-authored-by: Teal Larson <LARSON.TEAL@GMAIL.COM>

Co-authored-by: Lake Mossman <lake@airbyte.io>
Co-authored-by: tealjulia <teal@airbyte.io>
Co-authored-by: Teal Larson <LARSON.TEAL@GMAIL.COM>
  • Loading branch information
4 people authored Oct 7, 2022
1 parent 4d73eb0 commit 33f0cc6
Show file tree
Hide file tree
Showing 53 changed files with 4,506 additions and 4,049 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { FormattedMessage } from "react-intl";

import { Modal, ModalProps } from "components/ui/Modal";

import { ConnectionFormMode } from "views/Connection/ConnectionForm/ConnectionForm";
import { ConnectionFormMode } from "hooks/services/ConnectionForm/ConnectionFormService";

import styles from "./ArrayOfObjectsEditor.module.scss";
import { EditorHeader } from "./components/EditorHeader";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import styled from "styled-components";

import { Button } from "components/ui/Button";

import { ConnectionFormMode } from "views/Connection/ConnectionForm/ConnectionForm";
import { ConnectionFormMode } from "hooks/services/ConnectionForm/ConnectionFormService";

const Content = styled.div`
display: flex;
Expand Down

This file was deleted.

112 changes: 0 additions & 112 deletions airbyte-webapp/src/components/CreateConnection/CreateConnection.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@use "../../scss/variables";

.connectionFormContainer {
width: 100%;
padding: 0 variables.$spacing-xl;

> form {
display: flex;
flex-direction: column;
gap: variables.$spacing-md;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { act, render as tlr } from "@testing-library/react";
import mockConnection from "test-utils/mock-data/mockConnection.json";
import mockDest from "test-utils/mock-data/mockDestinationDefinition.json";
import { TestWrapper } from "test-utils/testutils";

import { AirbyteCatalog } from "core/request/AirbyteClient";
import * as sourceHook from "hooks/services/useSourceHook";

import { CreateConnectionForm } from "./CreateConnectionForm";

jest.mock("services/connector/DestinationDefinitionSpecificationService", () => ({
useGetDestinationDefinitionSpecification: () => mockDest,
}));

jest.mock("services/workspaces/WorkspacesService", () => ({
useCurrentWorkspace: () => ({}),
useCurrentWorkspaceId: () => "workspace-id",
}));

describe("CreateConnectionForm", () => {
const Wrapper: React.FC = ({ children }) => <TestWrapper>{children}</TestWrapper>;
const render = async () => {
let renderResult: ReturnType<typeof tlr>;

await act(async () => {
renderResult = tlr(
<Wrapper>
<CreateConnectionForm source={mockConnection.source} destination={mockConnection.destination} />
</Wrapper>
);
});
return renderResult!;
};

const baseUseDiscoverSchema = {
schemaErrorStatus: null,
isLoading: false,
schema: mockConnection.syncCatalog as AirbyteCatalog,
catalogId: "",
onDiscoverSchema: () => Promise.resolve(),
};

it("should render", async () => {
jest.spyOn(sourceHook, "useDiscoverSchema").mockImplementationOnce(() => baseUseDiscoverSchema);
const renderResult = await render();
expect(renderResult.container).toMatchSnapshot();
expect(renderResult.queryByText("Please wait a little bit more…")).toBeFalsy();
});

it("should render when loading", async () => {
jest
.spyOn(sourceHook, "useDiscoverSchema")
.mockImplementationOnce(() => ({ ...baseUseDiscoverSchema, isLoading: true }));

const renderResult = await render();
expect(renderResult.container).toMatchSnapshot();
});

it("should render with an error", async () => {
jest.spyOn(sourceHook, "useDiscoverSchema").mockImplementationOnce(() => ({
...baseUseDiscoverSchema,
schemaErrorStatus: new Error("Test Error") as sourceHook.SchemaError,
}));

const renderResult = await render();
expect(renderResult.container).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import { Form, Formik, FormikHelpers } from "formik";
import React, { Suspense, useCallback, useState } from "react";
import { useNavigate } from "react-router-dom";

import LoadingSchema from "components/LoadingSchema";

import { DestinationRead, SourceRead } from "core/request/AirbyteClient";
import {
ConnectionFormServiceProvider,
tidyConnectionFormValues,
useConnectionFormService,
} from "hooks/services/ConnectionForm/ConnectionFormService";
import { useFormChangeTrackerService } from "hooks/services/FormChangeTracker";
import { useCreateConnection } from "hooks/services/useConnectionHook";
import { SchemaError as SchemaErrorType, useDiscoverSchema } from "hooks/services/useSourceHook";
import { useCurrentWorkspaceId } from "services/workspaces/WorkspacesService";
import CreateControls from "views/Connection/ConnectionForm/components/CreateControls";
import { OperationsSection } from "views/Connection/ConnectionForm/components/OperationsSection";
import { ConnectionFormFields } from "views/Connection/ConnectionForm/ConnectionFormFields";
import { connectionValidationSchema, FormikConnectionFormValues } from "views/Connection/ConnectionForm/formConfig";

import styles from "./CreateConnectionForm.module.scss";
import { CreateConnectionNameField } from "./CreateConnectionNameField";
import { SchemaError } from "./SchemaError";

interface CreateConnectionProps {
source: SourceRead;
destination: DestinationRead;
afterSubmitConnection?: () => void;
}

interface CreateConnectionPropsInner extends Pick<CreateConnectionProps, "afterSubmitConnection"> {
schemaError: SchemaErrorType;
}

const CreateConnectionFormInner: React.FC<CreateConnectionPropsInner> = ({ schemaError, afterSubmitConnection }) => {
const navigate = useNavigate();

const { mutateAsync: createConnection } = useCreateConnection();

const { clearFormChange } = useFormChangeTrackerService();

const workspaceId = useCurrentWorkspaceId();

const { connection, initialValues, mode, formId, getErrorMessage, setSubmitError } = useConnectionFormService();
const [editingTransformation, setEditingTransformation] = useState(false);

const onFormSubmit = useCallback(
async (formValues: FormikConnectionFormValues, formikHelpers: FormikHelpers<FormikConnectionFormValues>) => {
const values = tidyConnectionFormValues(formValues, workspaceId, mode);

try {
const createdConnection = await createConnection({
values,
source: connection.source,
destination: connection.destination,
sourceDefinition: {
sourceDefinitionId: connection.source?.sourceDefinitionId ?? "",
},
destinationDefinition: {
name: connection.destination?.name ?? "",
destinationDefinitionId: connection.destination?.destinationDefinitionId ?? "",
},
sourceCatalogId: connection.catalogId,
});

formikHelpers.resetForm();
// We need to clear the form changes otherwise the dirty form intercept service will prevent navigation
clearFormChange(formId);

if (afterSubmitConnection) {
afterSubmitConnection();
} else {
navigate(`../../connections/${createdConnection.connectionId}`);
}
} catch (e) {
setSubmitError(e);
}
},
[
workspaceId,
mode,
createConnection,
connection.source,
connection.destination,
connection.catalogId,
clearFormChange,
formId,
afterSubmitConnection,
navigate,
setSubmitError,
]
);

if (schemaError) {
return <SchemaError schemaError={schemaError} />;
}

return (
<Suspense fallback={<LoadingSchema />}>
<div className={styles.connectionFormContainer}>
<Formik
initialValues={initialValues}
validationSchema={connectionValidationSchema(mode)}
onSubmit={onFormSubmit}
>
{({ values, isSubmitting, isValid, dirty }) => (
<Form>
<CreateConnectionNameField />
<ConnectionFormFields values={values} isSubmitting={isSubmitting} dirty={dirty} />
<OperationsSection
onStartEditTransformation={() => setEditingTransformation(true)}
onEndEditTransformation={() => setEditingTransformation(false)}
/>
<CreateControls
isSubmitting={isSubmitting}
isValid={isValid && !editingTransformation}
errorMessage={getErrorMessage(isValid, dirty)}
/>
</Form>
)}
</Formik>
</div>
</Suspense>
);
};

export const CreateConnectionForm: React.FC<CreateConnectionProps> = ({
source,
destination,
afterSubmitConnection,
}) => {
const { schema, isLoading, schemaErrorStatus, catalogId, onDiscoverSchema } = useDiscoverSchema(
source.sourceId,
true
);

const partialConnection = {
syncCatalog: schema,
destination,
source,
catalogId,
};

return (
<ConnectionFormServiceProvider
connection={partialConnection}
mode="create"
refreshSchema={onDiscoverSchema}
schemaError={schemaErrorStatus}
>
{isLoading ? (
<LoadingSchema />
) : (
<CreateConnectionFormInner afterSubmitConnection={afterSubmitConnection} schemaError={schemaErrorStatus} />
)}
</ConnectionFormServiceProvider>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
@forward "../../views/Connection/ConnectionForm/ConnectionFormFields.module.scss";
@use "../../scss/variables";

.labelHeading {
line-height: 16px;
display: inline;
}

.connectionLabel {
max-width: 328px;
margin-right: variables.$spacing-xl;
vertical-align: top;
}
Loading

0 comments on commit 33f0cc6

Please sign in to comment.