Skip to content

Commit

Permalink
Merge branch 'lmossman/connector-builder-pagination-slicing' into lmo…
Browse files Browse the repository at this point in the history
…ssman/connector-builder-stream-slicer
  • Loading branch information
Joe Reuter committed Dec 21, 2022
2 parents ab0a4c0 + 134fc17 commit 560cccf
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 115 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ export const AuthenticationSection: React.FC = () => {
<BuilderOneOf
path="global.authenticator"
label="Authentication"
tooltip="Authentication method to use for requests send to the API"
tooltip="Authentication method to use for requests sent to the API"
options={[
{ label: "No Auth", typeValue: "NoAuth" },
{
label: "API Key",
typeValue: "ApiKeyAuthenticator",
default: {
api_token: "{{ config['api_key'] }}",
header: "",
},
children: (
<>
Expand Down Expand Up @@ -70,6 +71,7 @@ export const AuthenticationSection: React.FC = () => {
client_secret: "{{ config['client_secret'] }}",
refresh_token: "{{ config['client_refresh_token'] }}",
refresh_request_body: [],
token_refresh_endpoint: "",
},
children: (
<>
Expand All @@ -79,7 +81,7 @@ export const AuthenticationSection: React.FC = () => {
label="Token refresh endpoint"
tooltip="The URL to call to obtain a new access token"
/>
<UserInputField label="Client ID" tooltip="The OAuth client id" />
<UserInputField label="Client ID" tooltip="The OAuth client ID" />
<UserInputField label="Client secret" tooltip="The OAuth client secret" />
<UserInputField label="Refresh token" tooltip="The OAuth refresh token" />
<BuilderOptional>
Expand All @@ -88,7 +90,7 @@ export const AuthenticationSection: React.FC = () => {
path="global.authenticator.scopes"
optional
label="Scopes"
tooltip="Scopes to rquest"
tooltip="Scopes to request"
/>
<BuilderField
type="array"
Expand Down Expand Up @@ -141,7 +143,7 @@ export const AuthenticationSection: React.FC = () => {
type="string"
path="global.authenticator.header"
label="Header"
tooltip="Specific header of source for providing session token"
tooltip="Specific HTTP header of source API for providing session token"
/>
<BuilderField
type="string"
Expand All @@ -153,7 +155,7 @@ export const AuthenticationSection: React.FC = () => {
type="string"
path="global.authenticator.login_url"
label="Login url"
tooltip="Url fot getting a specific session token"
tooltip="Url for getting a specific session token"
/>
<BuilderField
type="string"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ function getView(selectedView: BuilderView) {
export const Builder: React.FC<BuilderProps> = ({ values, toggleYamlEditor }) => {
const { setBuilderFormValues, selectedView } = useConnectorBuilderState();
useEffect(() => {
if (builderFormValidationSchema.isValidSync(values)) {
setBuilderFormValues(values);
}
setBuilderFormValues(values, builderFormValidationSchema.isValidSync(values));
}, [values, setBuilderFormValues]);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,12 @@ interface BaseFieldProps {
}

type BuilderFieldProps = BaseFieldProps &
({ type: "string" | "integer" | "number" | "boolean" | "array" } | { type: "enum"; options: string[] });
(
| { type: "string" | "number" | "integer"; onChange?: (newValue: string) => void }
| { type: "boolean"; onChange?: (newValue: boolean) => void }
| { type: "array"; onChange?: (newValue: string[]) => void }
| { type: "enum"; onChange?: (newValue: string) => void; options: string[] }
);

const EnumField: React.FC<EnumFieldProps> = ({ options, value, setValue, error, ...props }) => {
return (
Expand Down Expand Up @@ -80,21 +85,31 @@ export const BuilderField: React.FC<BuilderFieldProps> = ({
);
}

const setValue = (newValue: unknown) => {
props.onChange?.(newValue as string & string[]);
helpers.setValue(newValue);
};

return (
<ControlLabels className={styles.container} label={label} infoTooltipContent={tooltip} optional={optional}>
{(props.type === "number" || props.type === "string" || props.type === "integer") && (
<Input {...field} type={props.type} value={field.value ?? ""} error={hasError} readOnly={readOnly} />
<Input
{...field}
onChange={(e) => {
field.onChange(e);
props.onChange?.(e.target.value);
}}
type={props.type}
value={field.value ?? ""}
error={hasError}
readOnly={readOnly}
/>
)}
{props.type === "array" && (
<ArrayField name={path} value={field.value ?? []} setValue={helpers.setValue} error={hasError} />
<ArrayField name={path} value={field.value ?? []} setValue={setValue} error={hasError} />
)}
{props.type === "enum" && (
<EnumField
options={props.options}
value={field.value ?? props.options[0]}
setValue={helpers.setValue}
error={hasError}
/>
<EnumField options={props.options} value={field.value} setValue={setValue} error={hasError} />
)}
{hasError && (
<Text className={styles.error}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useField } from "formik";
import React, { useEffect } from "react";
import React from "react";

import GroupControls from "components/GroupControls";
import { ControlLabels } from "components/LabeledControl";
Expand Down Expand Up @@ -28,12 +28,8 @@ interface BuilderOneOfProps {
export const BuilderOneOf: React.FC<BuilderOneOfProps> = ({ options, path, label, tooltip }) => {
const [, , oneOfPathHelpers] = useField(path);
const typePath = `${path}.type`;
const [typePathField, , typePathHelpers] = useField(typePath);
const value = typePathField.value ?? options[0].typeValue;

useEffect(() => {
typePathHelpers.setValue(value);
}, []); // eslint-disable-line react-hooks/exhaustive-deps
const [typePathField] = useField(typePath);
const value = typePathField.value;

const selectedOption = options.find((option) => option.typeValue === value);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

.itemLabel {
flex-grow: 1;
font-size: 16px;
}

.itemButton {
Expand Down
148 changes: 79 additions & 69 deletions airbyte-webapp/src/components/connectorBuilder/Builder/InputsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { faGear, faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Form, Formik, useField, useFormikContext } from "formik";
import { JSONSchema7 } from "json-schema";
import { useEffect, useMemo, useState } from "react";
import { useMemo, useState } from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { useEffectOnce } from "react-use";
import * as yup from "yup";

import { Button } from "components/ui/Button";
Expand Down Expand Up @@ -97,8 +98,10 @@ export const InputsView: React.FC = () => {
// make sure key can only occur once
key: yup
.string()
.required("form.empty.error")
.notOneOf(inputInEditing?.isNew ? usedKeys : usedKeys.filter((key) => key !== inputInEditing?.key)),
.notOneOf(
inputInEditing?.isNew ? usedKeys : usedKeys.filter((key) => key !== inputInEditing?.key),
"connectorBuilder.duplicateFieldID"
),
required: yup.bool(),
definition: yup.object().shape({
title: yup.string().required("form.empty.error"),
Expand All @@ -116,36 +119,10 @@ export const InputsView: React.FC = () => {
<Card withPadding className={styles.inputsCard}>
<ol className={styles.list}>
{inferredInputs.map((input) => (
<li className={styles.listItem} key={input.key}>
<div className={styles.itemLabel}>{input.definition.title || input.key}</div>
<Button
className={styles.itemButton}
size="sm"
variant="secondary"
aria-label="Edit"
onClick={() => {
setInputInEditing(formInputToInputInEditing(input, true));
}}
>
<FontAwesomeIcon className={styles.icon} icon={faGear} />
</Button>
</li>
<InputItem input={input} setInputInEditing={setInputInEditing} isInferredInput />
))}
{inputs.value.map((input) => (
<li className={styles.listItem} key={input.key}>
<div className={styles.itemLabel}>{input.definition.title || input.key}</div>
<Button
className={styles.itemButton}
size="sm"
variant="secondary"
aria-label="Edit"
onClick={() => {
setInputInEditing(formInputToInputInEditing(input, false));
}}
>
<FontAwesomeIcon className={styles.icon} icon={faGear} />
</Button>
</li>
<InputItem input={input} setInputInEditing={setInputInEditing} isInferredInput={false} />
))}
</ol>
</Card>
Expand Down Expand Up @@ -197,6 +174,7 @@ export const InputsView: React.FC = () => {
</BuilderConfigView>
);
};

const InputModal = ({
inputInEditing,
onClose,
Expand All @@ -207,14 +185,13 @@ const InputModal = ({
onClose: () => void;
}) => {
const isInferredInputOverride = inputInEditing.isInferredInputOverride;
const { isValid, values, setFieldValue } = useFormikContext<InputInEditing>();
const { isValid, values, setFieldValue, setTouched } = useFormikContext<InputInEditing>();

const { formatMessage } = useIntl();
const [title, titleMeta] = useField<string | undefined>("definition.title");
useEffect(() => {
if (titleMeta.touched && !isInferredInputOverride) {
setFieldValue("key", sluggify(title.value || ""));
}
}, [setFieldValue, title.value, titleMeta.touched, isInferredInputOverride]);
useEffectOnce(() => {
// key input is always touched so errors are shown right away as it will be auto-set by the user changing the title
setTouched({ key: true });
});

return (
<Modal
Expand All @@ -231,6 +208,11 @@ const InputModal = ({
<BuilderField
path="definition.title"
type="string"
onChange={(newValue) => {
if (!isInferredInputOverride) {
setFieldValue("key", sluggify(newValue || ""), true);
}
}}
label={formatMessage({ id: "connectorBuilder.inputModal.inputName" })}
tooltip={formatMessage({ id: "connectorBuilder.inputModal.inputNameTooltip" })}
/>
Expand All @@ -242,7 +224,7 @@ const InputModal = ({
tooltip={formatMessage(
{ id: "connectorBuilder.inputModal.fieldIdTooltip" },
{
syntaxExample: "{{my_input}}",
syntaxExample: `{{config['${values.key || "my_input"}']}}`,
}
)}
/>
Expand All @@ -253,27 +235,26 @@ const InputModal = ({
label={formatMessage({ id: "connectorBuilder.inputModal.description" })}
tooltip={formatMessage({ id: "connectorBuilder.inputModal.descriptionTooltip" })}
/>
{values.type !== "unknown" ? (
{values.type !== "unknown" && !isInferredInputOverride ? (
<>
{!isInferredInputOverride && (
<>
<BuilderField
path="type"
type="enum"
options={["string", "number", "integer", "array", "boolean", "enum"]}
label={formatMessage({ id: "connectorBuilder.inputModal.type" })}
tooltip={formatMessage({ id: "connectorBuilder.inputModal.typeTooltip" })}
/>
{values.type === "enum" && (
<BuilderField
path="definition.enum"
type="array"
optional
label={formatMessage({ id: "connectorBuilder.inputModal.enum" })}
tooltip={formatMessage({ id: "connectorBuilder.inputModal.enumTooltip" })}
/>
)}
</>
<BuilderField
path="type"
type="enum"
options={["string", "number", "integer", "array", "boolean", "enum"]}
onChange={() => {
setFieldValue("definition.default", undefined);
}}
label={formatMessage({ id: "connectorBuilder.inputModal.type" })}
tooltip={formatMessage({ id: "connectorBuilder.inputModal.typeTooltip" })}
/>
{values.type === "enum" && (
<BuilderField
path="definition.enum"
type="array"
optional
label={formatMessage({ id: "connectorBuilder.inputModal.enum" })}
tooltip={formatMessage({ id: "connectorBuilder.inputModal.enumTooltip" })}
/>
)}
<BuilderField
path="definition.airbyte_secret"
Expand All @@ -282,15 +263,13 @@ const InputModal = ({
label={formatMessage({ id: "connectorBuilder.inputModal.secret" })}
tooltip={formatMessage({ id: "connectorBuilder.inputModal.secretTooltip" })}
/>
{!isInferredInputOverride && (
<BuilderField
path="required"
type="boolean"
optional
label={formatMessage({ id: "connectorBuilder.inputModal.required" })}
tooltip={formatMessage({ id: "connectorBuilder.inputModal.requiredTooltip" })}
/>
)}
<BuilderField
path="required"
type="boolean"
optional
label={formatMessage({ id: "connectorBuilder.inputModal.required" })}
tooltip={formatMessage({ id: "connectorBuilder.inputModal.requiredTooltip" })}
/>
<BuilderField
path="showDefaultValueField"
type="boolean"
Expand All @@ -317,7 +296,11 @@ const InputModal = ({
</>
) : (
<InfoBox>
<FormattedMessage id="connectorBuilder.inputModal.unsupportedInput" />
{isInferredInputOverride ? (
<FormattedMessage id="connectorBuilder.inputModal.inferredInputMessage" />
) : (
<FormattedMessage id="connectorBuilder.inputModal.unsupportedInput" />
)}
</InfoBox>
)}
</ModalBody>
Expand All @@ -340,3 +323,30 @@ const InputModal = ({
</Modal>
);
};

const InputItem = ({
input,
setInputInEditing,
isInferredInput,
}: {
input: BuilderFormInput;
setInputInEditing: (inputInEditing: InputInEditing) => void;
isInferredInput: boolean;
}): JSX.Element => {
return (
<li className={styles.listItem} key={input.key}>
<div className={styles.itemLabel}>{input.definition.title || input.key}</div>
<Button
className={styles.itemButton}
size="sm"
variant="secondary"
aria-label="Edit"
onClick={() => {
setInputInEditing(formInputToInputInEditing(input, isInferredInput));
}}
>
<FontAwesomeIcon className={styles.icon} icon={faGear} />
</Button>
</li>
);
};
Loading

0 comments on commit 560cccf

Please sign in to comment.