Skip to content

Commit

Permalink
🪟 🔧 Use experiment for new streams table design (#21230)
Browse files Browse the repository at this point in the history
* Use experiment for new streams table design

* Update formConfig validationSchema to use a hook to get the feature flags directly
* Add useNewTableDesignExperiment helper hook
* Rename isNewStreamsTableEnabled -> isNewTableDesignEnabld
* Cleanup tidyFormValues to take the existing schema in

* Fix var name in CatalogTreeTableHeader
  • Loading branch information
edmundito authored Jan 11, 2023
1 parent f3a70be commit b14ca94
Show file tree
Hide file tree
Showing 12 changed files with 76 additions and 97 deletions.
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 });

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

0 comments on commit b14ca94

Please sign in to comment.