Skip to content

Commit

Permalink
🪟 🎉 Refactor auto-created Builder inputs (#11852)
Browse files Browse the repository at this point in the history
Co-authored-by: Tim Roes <[email protected]>
  • Loading branch information
lmossman and timroes committed Apr 16, 2024
1 parent d29834d commit 325e8a1
Show file tree
Hide file tree
Showing 15 changed files with 1,147 additions and 695 deletions.
1 change: 1 addition & 0 deletions airbyte-webapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"date-fns": "^2.29.3",
"dayjs": "^1.11.3",
"diff": "^5.1.0",
"escape-string-regexp": "^5.0.0",
"firebase": "^10.5.0",
"framer-motion": "^6.3.11",
"js-yaml": "^4.1.0",
Expand Down
3 changes: 3 additions & 0 deletions airbyte-webapp/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { useFormContext } from "react-hook-form";
import { useIntl } from "react-intl";

import GroupControls from "components/GroupControls";
import { ControlLabels } from "components/LabeledControl";

import {
OAuthAuthenticatorRefreshTokenUpdater,
SessionTokenAuthenticatorRequestAuthentication,
} from "core/api/types/ConnectorManifest";
import { SessionTokenAuthenticatorRequestAuthentication } from "core/api/types/ConnectorManifest";
import { Action, Namespace, useAnalyticsService } from "core/services/analytics";
import { links } from "core/utils/links";

Expand All @@ -26,11 +24,7 @@ import {
API_KEY_AUTHENTICATOR,
BASIC_AUTHENTICATOR,
BEARER_AUTHENTICATOR,
extractInterpolatedConfigKey,
inferredAuthValues,
OAUTH_ACCESS_TOKEN_INPUT,
OAUTH_AUTHENTICATOR,
OAUTH_TOKEN_EXPIRY_DATE_INPUT,
SESSION_TOKEN_AUTHENTICATOR,
useBuilderWatch,
BuilderErrorHandler,
Expand All @@ -39,14 +33,18 @@ import {
SESSION_TOKEN_REQUEST_BEARER_AUTHENTICATOR,
NO_AUTH,
BuilderFormAuthenticator,
interpolateConfigKey,
BuilderFormOAuthAuthenticator,
} from "../types";
import { LOCKED_INPUT_BY_FIELD_NAME_BY_AUTH_TYPE, useGetUniqueKey } from "../useLockedInputs";

const AUTH_PATH = "formValues.global.authenticator";
const authPath = <T extends string>(path: T) => `${AUTH_PATH}.${path}` as const;

export const AuthenticationSection: React.FC = () => {
const { formatMessage } = useIntl();
const analyticsService = useAnalyticsService();
const getUniqueKey = useGetUniqueKey();

return (
<BuilderCard
Expand All @@ -63,24 +61,26 @@ export const AuthenticationSection: React.FC = () => {
"BasicHttpAuthenticator",
"OAuthAuthenticator",
]}
onSelect={(type) =>
onSelect={(newType) => {
analyticsService.track(Namespace.CONNECTOR_BUILDER, Action.AUTHENTICATION_METHOD_SELECT, {
actionDescription: "Authentication method selected",
auth_type: type,
})
}
auth_type: newType,
});
}}
options={[
{ label: formatMessage({ id: "connectorBuilder.authentication.method.noAuth" }), default: { type: NO_AUTH } },
{
label: formatMessage({ id: "connectorBuilder.authentication.method.apiKey" }),
default: {
type: API_KEY_AUTHENTICATOR,
...inferredAuthValues("ApiKeyAuthenticator"),
inject_into: {
type: "RequestOption",
inject_into: "header",
field_name: "",
},
api_token: interpolateConfigKey(
getUniqueKey(LOCKED_INPUT_BY_FIELD_NAME_BY_AUTH_TYPE[API_KEY_AUTHENTICATOR].api_token.key)
),
},
children: (
<>
Expand All @@ -97,15 +97,22 @@ export const AuthenticationSection: React.FC = () => {
label: formatMessage({ id: "connectorBuilder.authentication.method.bearer" }),
default: {
type: BEARER_AUTHENTICATOR,
...(inferredAuthValues("BearerAuthenticator") as Record<"api_token", string>),
api_token: interpolateConfigKey(
getUniqueKey(LOCKED_INPUT_BY_FIELD_NAME_BY_AUTH_TYPE[BEARER_AUTHENTICATOR].api_token.key)
),
},
children: <BuilderInputPlaceholder manifestPath="BearerAuthenticator.properties.api_token" />,
},
{
label: formatMessage({ id: "connectorBuilder.authentication.method.basicHttp" }),
default: {
type: BASIC_AUTHENTICATOR,
...(inferredAuthValues("BasicHttpAuthenticator") as Record<"username" | "password", string>),
username: interpolateConfigKey(
getUniqueKey(LOCKED_INPUT_BY_FIELD_NAME_BY_AUTH_TYPE[BASIC_AUTHENTICATOR].username.key)
),
password: interpolateConfigKey(
getUniqueKey(LOCKED_INPUT_BY_FIELD_NAME_BY_AUTH_TYPE[BASIC_AUTHENTICATOR].password.key)
),
},
children: (
<>
Expand All @@ -118,13 +125,18 @@ export const AuthenticationSection: React.FC = () => {
label: formatMessage({ id: "connectorBuilder.authentication.method.oAuth" }),
default: {
type: OAUTH_AUTHENTICATOR,
...(inferredAuthValues("OAuthAuthenticator") as Record<
"client_id" | "client_secret" | "refresh_token" | "oauth_access_token" | "oauth_token_expiry_date",
string
>),
refresh_request_body: [],
token_refresh_endpoint: "",
grant_type: "refresh_token",
client_id: interpolateConfigKey(
getUniqueKey(LOCKED_INPUT_BY_FIELD_NAME_BY_AUTH_TYPE[OAUTH_AUTHENTICATOR].client_id.key)
),
client_secret: interpolateConfigKey(
getUniqueKey(LOCKED_INPUT_BY_FIELD_NAME_BY_AUTH_TYPE[OAUTH_AUTHENTICATOR].client_secret.key)
),
refresh_token: interpolateConfigKey(
getUniqueKey(LOCKED_INPUT_BY_FIELD_NAME_BY_AUTH_TYPE[OAUTH_AUTHENTICATOR].refresh_token.key)
),
},
children: <OAuthForm />,
},
Expand Down Expand Up @@ -168,8 +180,10 @@ export const AuthenticationSection: React.FC = () => {

const OAuthForm = () => {
const { formatMessage } = useIntl();
const { setValue } = useFormContext();
const grantType = useBuilderWatch(authPath("grant_type"));
const refreshToken = useBuilderWatch(authPath("refresh_token"));
const getUniqueKey = useGetUniqueKey();

return (
<>
<BuilderFieldWithInputs
Expand All @@ -182,21 +196,42 @@ const OAuthForm = () => {
path={authPath("grant_type")}
options={["refresh_token", "client_credentials"]}
manifestPath="OAuthAuthenticator.properties.grant_type"
onChange={(newValue) => {
if (newValue === "client_credentials") {
setValue(authPath("refresh_token"), undefined);
} else if (newValue === "refresh_token") {
setValue(
authPath("refresh_token"),
interpolateConfigKey(
getUniqueKey(LOCKED_INPUT_BY_FIELD_NAME_BY_AUTH_TYPE[OAUTH_AUTHENTICATOR].refresh_token.key)
)
);
}
}}
/>
<BuilderInputPlaceholder manifestPath="OAuthAuthenticator.properties.client_id" />
<BuilderInputPlaceholder manifestPath="OAuthAuthenticator.properties.client_secret" />
{grantType === "refresh_token" && (
<>
<BuilderInputPlaceholder manifestPath="OAuthAuthenticator.properties.refresh_token" />
<ToggleGroupField<OAuthAuthenticatorRefreshTokenUpdater>
<ToggleGroupField<BuilderFormOAuthAuthenticator["refresh_token_updater"]>
label={formatMessage({ id: "connectorBuilder.authentication.refreshTokenUpdater.label" })}
tooltip={formatMessage({ id: "connectorBuilder.authentication.refreshTokenUpdater.tooltip" })}
fieldPath={authPath("refresh_token_updater")}
initialValues={{
refresh_token_name: "",
access_token_config_path: [OAUTH_ACCESS_TOKEN_INPUT],
refresh_token_config_path: [extractInterpolatedConfigKey(refreshToken) || ""],
token_expiry_date_config_path: [OAUTH_TOKEN_EXPIRY_DATE_INPUT],
access_token: interpolateConfigKey(
getUniqueKey(
LOCKED_INPUT_BY_FIELD_NAME_BY_AUTH_TYPE[OAUTH_AUTHENTICATOR].refresh_token_updater
.access_token_config_path.key
)
),
token_expiry_date: interpolateConfigKey(
getUniqueKey(
LOCKED_INPUT_BY_FIELD_NAME_BY_AUTH_TYPE[OAUTH_AUTHENTICATOR].refresh_token_updater
.token_expiry_date_config_path.key
)
),
}}
>
<BuilderField
Expand Down Expand Up @@ -251,6 +286,8 @@ const SessionTokenForm = () => {
authPath("login_requester"),
true
);
const getUniqueKey = useGetUniqueKey();

return (
<>
<GroupControls label={<ControlLabels label={loginRequesterLabel} infoTooltipContent={loginRequesterTooltip} />}>
Expand All @@ -270,7 +307,7 @@ const SessionTokenForm = () => {
path={authPath("login_requester.authenticator")}
label={formatMessage({ id: "connectorBuilder.authentication.loginRequester.authenticator.label" })}
manifestPath="HttpRequester.properties.authenticator"
manifestOptionPaths={["ApiKeyAuthenticator", "BearerAuthenticator", "BasicHttpAuthenticator"]}
manifestOptionPaths={[API_KEY_AUTHENTICATOR, BEARER_AUTHENTICATOR, BASIC_AUTHENTICATOR]}
options={[
{
label: formatMessage({ id: "connectorBuilder.authentication.method.noAuth" }),
Expand All @@ -280,12 +317,14 @@ const SessionTokenForm = () => {
label: formatMessage({ id: "connectorBuilder.authentication.method.apiKey" }),
default: {
type: API_KEY_AUTHENTICATOR,
...inferredAuthValues("ApiKeyAuthenticator"),
inject_into: {
type: "RequestOption",
inject_into: "header",
field_name: "",
},
api_token: interpolateConfigKey(
getUniqueKey(LOCKED_INPUT_BY_FIELD_NAME_BY_AUTH_TYPE[API_KEY_AUTHENTICATOR].api_token.key)
),
},
children: (
<>
Expand All @@ -302,15 +341,22 @@ const SessionTokenForm = () => {
label: formatMessage({ id: "connectorBuilder.authentication.method.bearer" }),
default: {
type: BEARER_AUTHENTICATOR,
...(inferredAuthValues(BEARER_AUTHENTICATOR) as Record<"api_token", string>),
api_token: interpolateConfigKey(
getUniqueKey(LOCKED_INPUT_BY_FIELD_NAME_BY_AUTH_TYPE[BEARER_AUTHENTICATOR].api_token.key)
),
},
children: <BuilderInputPlaceholder manifestPath="BearerAuthenticator.properties.api_token" />,
},
{
label: formatMessage({ id: "connectorBuilder.authentication.method.basicHttp" }),
default: {
type: BASIC_AUTHENTICATOR,
...(inferredAuthValues(BASIC_AUTHENTICATOR) as Record<"username" | "password", string>),
username: interpolateConfigKey(
getUniqueKey(LOCKED_INPUT_BY_FIELD_NAME_BY_AUTH_TYPE[BASIC_AUTHENTICATOR].username.key)
),
password: interpolateConfigKey(
getUniqueKey(LOCKED_INPUT_BY_FIELD_NAME_BY_AUTH_TYPE[BASIC_AUTHENTICATOR].password.key)
),
},
children: (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { ConnectorBuilderMainRHFContext } from "services/connectorBuilder/Connec
import { BuilderField, BuilderFieldProps } from "./BuilderField";
import styles from "./BuilderFieldWithInputs.module.scss";
import { InputForm, newInputInEditing } from "./InputsForm";
import { useInferredInputs } from "../useInferredInputs";

export const BuilderFieldWithInputs: React.FC<BuilderFieldProps> = (props) => {
return (
Expand All @@ -31,14 +30,13 @@ const UserInputHelper = (props: UserInputHelperProps) => {
throw new Error("rhf context not available");
}
const inputs = watch("formValues.inputs");
const inferredInputs = useInferredInputs();
const listOptions = useMemo(() => {
const options: Array<Option<string | undefined>> = [...inputs, ...inferredInputs].map((input) => ({
const options: Array<Option<string | undefined>> = inputs.map((input) => ({
label: input.definition.title || input.key,
value: input.key,
}));
return options;
}, [inputs, inferredInputs]);
}, [inputs]);
return <InnerUserInputHelper {...props} listOptions={listOptions} />;
};

Expand All @@ -57,7 +55,7 @@ const InnerUserInputHelper = React.memo(
selectedValue={undefined}
onSelect={(selectedValue) => {
if (selectedValue) {
setValue(path, `${getValues(path) || ""}{{ config['${selectedValue}'] }}`, {
setValue(path, `${getValues(path) || ""}{{ config["${selectedValue}"] }}`, {
shouldDirty: true,
shouldTouch: true,
shouldValidate: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import styles from "./BuilderSidebar.module.scss";
import { Sidebar } from "../Sidebar";
import { useBuilderWatch } from "../types";
import { useBuilderErrors } from "../useBuilderErrors";
import { useInferredInputs } from "../useInferredInputs";

interface ViewSelectButtonProps {
className?: string;
Expand Down Expand Up @@ -66,8 +65,6 @@ export const BuilderSidebar: React.FC<BuilderSidebarProps> = () => {
setValue("view", selectedView);
};

const inferredInputsLength = useInferredInputs().length;

return (
<Sidebar yamlSelected={false}>
<FlexContainer direction="column" alignItems="stretch" gap="none">
Expand Down Expand Up @@ -104,7 +101,7 @@ export const BuilderSidebar: React.FC<BuilderSidebarProps> = () => {
<FormattedMessage
id="connectorBuilder.userInputs"
values={{
number: formValues.inputs.length + inferredInputsLength,
number: formValues.inputs.length,
}}
/>
</Text>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ import {
SMALL_DURATION_OPTIONS,
StreamPathFn,
builderIncrementalSyncToManifest,
interpolateConfigKey,
useBuilderWatch,
} from "../types";
import { LOCKED_INPUT_BY_INCREMENTAL_FIELD_NAME, useGetUniqueKey } from "../useLockedInputs";

interface IncrementalSectionProps {
streamFieldPath: StreamPathFn;
Expand All @@ -39,6 +41,7 @@ interface IncrementalSectionProps {
export const IncrementalSection: React.FC<IncrementalSectionProps> = ({ streamFieldPath, currentStreamIndex }) => {
const { formatMessage } = useIntl();
const filterMode = useBuilderWatch(streamFieldPath("incrementalSync.filter_mode"));
const getExistingOrUniqueKey = useGetUniqueKey();
return (
<BuilderCard
docLink={links.connectorBuilderIncrementalSync}
Expand All @@ -50,7 +53,12 @@ export const IncrementalSection: React.FC<IncrementalSectionProps> = ({ streamFi
defaultValue: {
datetime_format: "",
cursor_datetime_formats: [],
start_datetime: { type: "user_input" },
start_datetime: {
type: "user_input",
value: interpolateConfigKey(
getExistingOrUniqueKey(LOCKED_INPUT_BY_INCREMENTAL_FIELD_NAME.start_datetime.key, "start_datetime")
),
},
end_datetime: { type: "now" },
step: "",
cursor_field: "",
Expand Down Expand Up @@ -131,7 +139,12 @@ export const IncrementalSection: React.FC<IncrementalSectionProps> = ({ streamFi
options={[
{
label: formatMessage({ id: "connectorBuilder.incremental.userInput" }),
default: { type: "user_input" },
default: {
type: "user_input",
value: interpolateConfigKey(
getExistingOrUniqueKey(LOCKED_INPUT_BY_INCREMENTAL_FIELD_NAME.start_datetime.key, "start_datetime")
),
},
children: (
<BuilderInputPlaceholder
label={formatMessage({
Expand Down Expand Up @@ -194,7 +207,12 @@ export const IncrementalSection: React.FC<IncrementalSectionProps> = ({ streamFi
options={[
{
label: formatMessage({ id: "connectorBuilder.incremental.userInput" }),
default: { type: "user_input" },
default: {
type: "user_input",
value: interpolateConfigKey(
getExistingOrUniqueKey(LOCKED_INPUT_BY_INCREMENTAL_FIELD_NAME.end_datetime.key, "end_datetime")
),
},
children: (
<BuilderInputPlaceholder
label={formatMessage({ id: "connectorBuilder.incremental.userInput.endDatetime.label" })}
Expand Down
Loading

0 comments on commit 325e8a1

Please sign in to comment.