Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🪟 🔧 Use experiment for new streams table design #21230

Merged
merged 2 commits into from
Jan 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@ 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 {
createConnectionValidationSchema,
FormikConnectionFormValues,
} from "views/Connection/ConnectionForm/formConfig";
import { useConnectionValidationSchema, FormikConnectionFormValues } from "views/Connection/ConnectionForm/formConfig";

import styles from "./CreateConnectionForm.module.scss";
import { CreateConnectionNameField } from "./CreateConnectionNameField";
Expand All @@ -42,24 +39,17 @@ const CreateConnectionFormInner: React.FC<CreateConnectionPropsInner> = ({ schem
const navigate = useNavigate();
const canEditDataGeographies = useFeature(FeatureItem.AllowChangeDataGeographies);
const { mutateAsync: createConnection } = useCreateConnection();
const allowAutoDetectSchema = useFeature(FeatureItem.AllowAutoDetectSchema);
const { clearFormChange } = useFormChangeTrackerService();

const workspaceId = useCurrentWorkspaceId();

const { connection, initialValues, mode, formId, getErrorMessage, setSubmitError } = useConnectionFormService();
const [editingTransformation, setEditingTransformation] = useState(false);
const allowSubOneHourCronExpressions = useFeature(FeatureItem.AllowSyncSubOneHourCronExpressions);
const validationSchema = useConnectionValidationSchema({ mode });

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

try {
const createdConnection = await createConnection({
Expand Down Expand Up @@ -91,9 +81,7 @@ const CreateConnectionFormInner: React.FC<CreateConnectionPropsInner> = ({ schem
},
[
workspaceId,
mode,
allowSubOneHourCronExpressions,
allowAutoDetectSchema,
validationSchema,
createConnection,
connection.source,
connection.destination,
Expand All @@ -113,15 +101,7 @@ const CreateConnectionFormInner: React.FC<CreateConnectionPropsInner> = ({ schem
return (
<Suspense fallback={<LoadingSchema />}>
<div className={styles.connectionFormContainer}>
<Formik
initialValues={initialValues}
validationSchema={createConnectionValidationSchema({
mode,
allowSubOneHourCronExpressions,
allowAutoDetectSchema,
})}
onSubmit={onFormSubmit}
>
<Formik initialValues={initialValues} validationSchema={validationSchema} onSubmit={onFormSubmit}>
{({ values, isSubmitting, isValid, dirty }) => (
<Form>
<CreateConnectionNameField />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
SelectedFieldInfo,
} from "core/request/AirbyteClient";
import { useDestinationNamespace } from "hooks/connection/useDestinationNamespace";
import { useNewTableDesignExperiment } from "hooks/connection/useNewTableDesignExperiment";
import { useConnectionFormService } from "hooks/services/ConnectionForm/ConnectionFormService";
import { naturalComparatorBy } from "utils/objects";
import { ConnectionFormValues, SUPPORTED_MODES } from "views/Connection/ConnectionForm/formConfig";
Expand Down Expand Up @@ -50,7 +51,7 @@ const CatalogSectionInner: React.FC<CatalogSectionInnerProps> = ({
errors,
changedSelected,
}) => {
const isNewStreamsTableEnabled = process.env.REACT_APP_NEW_STREAMS_TABLE ?? false;
const isNewTableDesignEnabled = useNewTableDesignExperiment();

const numberOfFieldsInStream = Object.keys(streamNode?.stream?.jsonSchema?.properties).length ?? 0;

Expand Down Expand Up @@ -187,17 +188,15 @@ const CatalogSectionInner: React.FC<CatalogSectionInnerProps> = ({
const destName = prefix + (streamNode.stream?.name ?? "");
const configErrors = getIn(
errors,
isNewStreamsTableEnabled
? `syncCatalog.streams[${streamNode.id}].config`
: `schema.streams[${streamNode.id}].config`
isNewTableDesignEnabled ? `syncCatalog.streams[${streamNode.id}].config` : `schema.streams[${streamNode.id}].config`
);
const hasError = configErrors && Object.keys(configErrors).length > 0;
const pkType = getPathType(pkRequired, shouldDefinePk);
const cursorType = getPathType(cursorRequired, shouldDefineCursor);
const hasFields = fields?.length > 0;
const disabled = mode === "readonly";

const StreamComponent = isNewStreamsTableEnabled ? CatalogTreeTableRow : StreamHeader;
const StreamComponent = isNewTableDesignEnabled ? CatalogTreeTableRow : StreamHeader;

return (
<div className={styles.catalogSection}>
Expand All @@ -223,7 +222,7 @@ const CatalogSectionInner: React.FC<CatalogSectionInnerProps> = ({
/>
{isRowExpanded &&
hasFields &&
(isNewStreamsTableEnabled ? (
(isNewTableDesignEnabled ? (
<StreamDetailsPanel
config={config}
disabled={mode === "readonly"}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React, { useCallback, useMemo, useState } from "react";
import { LoadingBackdrop } from "components/ui/LoadingBackdrop";

import { SyncSchemaStream } from "core/domain/catalog";
import { useNewTableDesignExperiment } from "hooks/connection/useNewTableDesignExperiment";
import { BulkEditServiceProvider } from "hooks/services/BulkEdit/BulkEditService";
import { useConnectionFormService } from "hooks/services/ConnectionForm/ConnectionFormService";
import { naturalComparatorBy } from "utils/objects";
Expand All @@ -24,7 +25,7 @@ const CatalogTreeComponent: React.FC<React.PropsWithChildren<CatalogTreeProps>>
onStreamsChanged,
isLoading,
}) => {
const isNewStreamsTableEnabled = process.env.REACT_APP_NEW_STREAMS_TABLE ?? false;
const isNewTableDesignEnabled = useNewTableDesignExperiment();
const { initialValues, mode } = useConnectionFormService();

const [searchString, setSearchString] = useState("");
Expand Down Expand Up @@ -72,17 +73,15 @@ const CatalogTreeComponent: React.FC<React.PropsWithChildren<CatalogTreeProps>>
<BulkEditServiceProvider nodes={streams} update={onStreamsChanged}>
<LoadingBackdrop loading={isLoading}>
{mode !== "readonly" && <CatalogTreeSearch onSearch={setSearchString} />}
<div
className={classNames(styles.catalogTreeTable, { [styles.newCatalogTreeTable]: isNewStreamsTableEnabled })}
>
<div className={classNames(styles.catalogTreeTable, { [styles.newCatalogTreeTable]: isNewTableDesignEnabled })}>
<CatalogTreeBody
streams={filteredStreams}
changedStreams={changedStreams}
onStreamChanged={onSingleStreamChanged}
/>
</div>
</LoadingBackdrop>
{isNewStreamsTableEnabled && <BulkEditPanel />}
{isNewTableDesignEnabled && <BulkEditPanel />}
</BulkEditServiceProvider>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, { useCallback } from "react";

import { SyncSchemaStream } from "core/domain/catalog";
import { AirbyteStreamConfiguration } from "core/request/AirbyteClient";
import { useNewTableDesignExperiment } from "hooks/connection/useNewTableDesignExperiment";
import { useConnectionFormService } from "hooks/services/ConnectionForm/ConnectionFormService";
import { FormikConnectionFormValues } from "views/Connection/ConnectionForm/formConfig";

Expand All @@ -20,10 +21,9 @@ interface CatalogTreeBodyProps {
onStreamChanged: (stream: SyncSchemaStream) => void;
}

const isNewStreamsTableEnabled = process.env.REACT_APP_NEW_STREAMS_TABLE ?? false;

export const CatalogTreeBody: React.FC<CatalogTreeBodyProps> = ({ streams, changedStreams, onStreamChanged }) => {
const { mode } = useConnectionFormService();
const isNewTableDesignEnabled = useNewTableDesignExperiment();

const onUpdateStream = useCallback(
(id: string | undefined, newConfig: Partial<AirbyteStreamConfiguration>) => {
Expand All @@ -40,7 +40,7 @@ export const CatalogTreeBody: React.FC<CatalogTreeBodyProps> = ({ streams, chang

return (
<div className={styles.container}>
{isNewStreamsTableEnabled ? (
{isNewTableDesignEnabled ? (
<>
<StreamConnectionHeader />
<CatalogTreeTableHeader />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Text } from "components/ui/Text";
import { InfoTooltip, TooltipLearnMoreLink } from "components/ui/Tooltip";

import { NamespaceDefinitionType } from "core/request/AirbyteClient";
import { useNewTableDesignExperiment } from "hooks/connection/useNewTableDesignExperiment";
import { useBulkEditService } from "hooks/services/BulkEdit/BulkEditService";
import { useConnectionFormService } from "hooks/services/ConnectionForm/ConnectionFormService";
import { useModalService } from "hooks/services/Modal";
Expand Down Expand Up @@ -43,13 +44,12 @@ const HeaderCell: React.FC<React.PropsWithChildren<Parameters<typeof CatalogTree
);
};

const isNewStreamsTableEnabled = process.env.REACT_APP_NEW_STREAMS_TABLE ?? false;

export const CatalogTreeTableHeader: React.FC = () => {
const { mode } = useConnectionFormService();
const { openModal, closeModal } = useModalService();
const { onCheckAll, selectedBatchNodeIds, allChecked } = useBulkEditService();
const formikProps = useFormikContext<FormikConnectionFormValues>();
const isNewTableDesignEnabled = useNewTableDesignExperiment();

const destinationNamespaceChange = (value: DestinationNamespaceFormValueType) => {
formikProps.setFieldValue("namespaceDefinition", value.namespaceDefinition);
Expand All @@ -67,7 +67,7 @@ export const CatalogTreeTableHeader: React.FC = () => {
};

return (
<Header className={classNames(styles.headerContainer, { [styles.newTable]: !!isNewStreamsTableEnabled })}>
<Header className={classNames(styles.headerContainer, { [styles.newTable]: !!isNewTableDesignEnabled })}>
<CatalogTreeTableCell size="small" className={styles.checkboxCell}>
{mode !== "readonly" && (
<CheckBox
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { FormattedMessage } from "react-intl";
import { ArrowRightIcon } from "components/icons/ArrowRightIcon";
import { Heading } from "components/ui/Heading";

import { useNewTableDesignExperiment } from "hooks/connection/useNewTableDesignExperiment";
import { useConnectionFormService } from "hooks/services/ConnectionForm/ConnectionFormService";
import { useDestinationDefinition } from "services/connector/DestinationDefinitionService";
import { useSourceDefinition } from "services/connector/SourceDefinitionService";
Expand All @@ -12,19 +13,19 @@ import { getIcon } from "utils/imageUtils";
import styles from "./StreamConnectionHeader.module.scss";

export const renderIcon = (icon?: string): JSX.Element => <div className={styles.icon}>{getIcon(icon)}</div>;
const isNewStreamsTableEnabled = process.env.REACT_APP_NEW_STREAMS_TABLE ?? false;

export const StreamConnectionHeader: React.FC = () => {
const {
connection: { source, destination },
} = useConnectionFormService();
const sourceDefinition = useSourceDefinition(source.sourceDefinitionId);
const destinationDefinition = useDestinationDefinition(destination.destinationDefinitionId);
const isNewTableDesignEnabled = useNewTableDesignExperiment();
const sourceStyles = classnames(styles.connector, styles.source);
const destinationStyles = classnames(styles.connector, styles.destination);

return (
<div className={classnames(styles.container, { [styles.newTableContainer]: !!isNewStreamsTableEnabled })}>
<div className={classnames(styles.container, { [styles.newTableContainer]: !!isNewTableDesignEnabled })}>
<div className={sourceStyles}>
{renderIcon(sourceDefinition.icon)}{" "}
<Heading as="h5" size="sm">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { useExperiment } from "hooks/services/Experiment";

export const useNewTableDesignExperiment = () =>
useExperiment("connection.newTableDesign", process.env.REACT_APP_NEW_STREAMS_TABLE === "true");
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ import {
OperationRead,
WebBackendConnectionRead,
} from "core/request/AirbyteClient";
import { useNewTableDesignExperiment } from "hooks/connection/useNewTableDesignExperiment";
import { useDestinationDefinition } from "services/connector/DestinationDefinitionService";
import { useGetDestinationDefinitionSpecification } from "services/connector/DestinationDefinitionSpecificationService";
import { FormError, generateMessageFromError } from "utils/errorStatusMessage";
import {
ConnectionFormValues,
createConnectionValidationSchema,
FormikConnectionFormValues,
mapFormPropsToOperation,
useInitialValues,
ConnectionValidationSchema,
} from "views/Connection/ConnectionForm/formConfig";

import { useUniqueFormId } from "../FormChangeTracker";
Expand All @@ -39,17 +40,11 @@ interface ConnectionServiceProps {
export const tidyConnectionFormValues = (
values: FormikConnectionFormValues,
workspaceId: string,
mode: ConnectionFormMode,
allowSubOneHourCronExpressions: boolean,
allowAutoDetectSchema: boolean,
validationSchema: ConnectionValidationSchema,
operations?: OperationRead[]
): ValuesProps => {
// TODO (https://github.com/airbytehq/airbyte/issues/17279): We should try to fix the types so we don't need the casting.
const formValues: ConnectionFormValues = createConnectionValidationSchema({
mode,
allowSubOneHourCronExpressions,
allowAutoDetectSchema,
}).cast(values, {
const formValues: ConnectionFormValues = validationSchema.cast(values, {
context: { isRequest: true },
}) as unknown as ConnectionFormValues;

Expand Down Expand Up @@ -89,12 +84,11 @@ const useConnectionForm = ({
const { formatMessage } = useIntl();
const [submitError, setSubmitError] = useState<FormError | null>(null);
const formId = useUniqueFormId();
const isNewTableDesignEnabled = useNewTableDesignExperiment();

const getErrorMessage = useCallback(
(formValid: boolean, connectionDirty: boolean) => {
const isNewStreamsTableEnabled = process.env.REACT_APP_NEW_STREAMS_TABLE ?? false;

if (isNewStreamsTableEnabled) {
if (isNewTableDesignEnabled) {
// There is a case when some fields could be dropped in the database. We need to validate the form without property dirty
return submitError
? generateMessageFromError(submitError)
Expand All @@ -108,7 +102,7 @@ const useConnectionForm = ({
? formatMessage({ id: "connectionForm.validation.error" })
: null;
},
[formatMessage, submitError]
[formatMessage, isNewTableDesignEnabled, submitError]
);

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ export interface Experiments {
"connection.onboarding.destinations": string;
"connection.autoDetectSchemaChanges": boolean;
"connection.columnSelection": boolean;
"connection.newTableDesign": boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,14 @@ import {
tidyConnectionFormValues,
useConnectionFormService,
} from "hooks/services/ConnectionForm/ConnectionFormService";
import { FeatureItem, useFeature } from "hooks/services/Feature";
import { useModalService } from "hooks/services/Modal";
import { useConnectionService, ValuesProps } from "hooks/services/useConnectionHook";
import { useCurrentWorkspaceId } from "services/workspaces/WorkspacesService";
import { equal } from "utils/objects";
import EditControls from "views/Connection/ConnectionForm/components/EditControls";
import { SchemaChangeBackdrop } from "views/Connection/ConnectionForm/components/SchemaChangeBackdrop";
import { ConnectionFormFields } from "views/Connection/ConnectionForm/ConnectionFormFields";
import {
createConnectionValidationSchema,
FormikConnectionFormValues,
} from "views/Connection/ConnectionForm/formConfig";
import { useConnectionValidationSchema, FormikConnectionFormValues } from "views/Connection/ConnectionForm/formConfig";

import styles from "./ConnectionReplicationTab.module.scss";
import { ResetWarningModal } from "./ResetWarningModal";
Expand All @@ -46,7 +42,6 @@ const ValidateFormOnSchemaRefresh: React.FC = () => {
};

export const ConnectionReplicationTab: React.FC = () => {
const allowAutoDetectSchema = useFeature(FeatureItem.AllowAutoDetectSchema);
const analyticsService = useAnalyticsService();
const connectionService = useConnectionService();
const workspaceId = useCurrentWorkspaceId();
Expand All @@ -59,7 +54,7 @@ export const ConnectionReplicationTab: React.FC = () => {
const { connection, schemaRefreshing, schemaHasBeenRefreshed, updateConnection, discardRefreshedSchema } =
useConnectionEditService();
const { initialValues, mode, schemaError, getErrorMessage, setSubmitError } = useConnectionFormService();
const allowSubOneHourCronExpressions = useFeature(FeatureItem.AllowSyncSubOneHourCronExpressions);
const validationSchema = useConnectionValidationSchema({ mode });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is done both here and in the create form I think this should be moved to the Connection Form Service.


useTrackPage(PageTrackingCodes.CONNECTIONS_ITEM_REPLICATION);

Expand Down Expand Up @@ -94,14 +89,7 @@ export const ConnectionReplicationTab: React.FC = () => {

const onFormSubmit = useCallback(
async (values: FormikConnectionFormValues, _: FormikHelpers<FormikConnectionFormValues>) => {
const formValues = tidyConnectionFormValues(
values,
workspaceId,
mode,
allowSubOneHourCronExpressions,
allowAutoDetectSchema,
connection.operations
);
const formValues = tidyConnectionFormValues(values, workspaceId, validationSchema, connection.operations);

// Check if the user refreshed the catalog and there was any change in a currently enabled stream
const hasDiffInEnabledStream = connection.catalogDiff?.transforms.some(({ streamDescriptor }) => {
Expand Down Expand Up @@ -156,9 +144,7 @@ export const ConnectionReplicationTab: React.FC = () => {
},
[
workspaceId,
mode,
allowSubOneHourCronExpressions,
allowAutoDetectSchema,
validationSchema,
connection.operations,
connection.catalogDiff?.transforms,
connection.syncCatalog.streams,
Expand All @@ -185,11 +171,7 @@ export const ConnectionReplicationTab: React.FC = () => {
<Formik
initialStatus={{ editControlsVisible: true }}
initialValues={initialValues}
validationSchema={createConnectionValidationSchema({
mode,
allowSubOneHourCronExpressions,
allowAutoDetectSchema,
})}
validationSchema={validationSchema}
onSubmit={onFormSubmit}
enableReinitialize
>
Expand Down
Loading