-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🪟🎉 Connector builder authentication (#20645)
* allow auth configuration * check for conflicts with the inferred inputs * fix invisible inputs * reduce redundancy and hide advanced input options for inferred inputs * unnecessary validation * typo * unnecessary effect hook * build spec even for invalid forms but do not update stream list * fix keys * 🪟🎉 Connector builder: Session token and oauth authentication (#20712) * session token and oauth authentication * fill in session token variable * typos * make sure validation error does not go away * 🪟🎉 Connector builder: Always validate inputs form (#20664) * validate user input outside of form * review comments Co-authored-by: lmossman <lake@airbyte.io>
- Loading branch information
Showing
20 changed files
with
765 additions
and
91 deletions.
There are no files selected for viewing
179 changes: 179 additions & 0 deletions
179
airbyte-webapp/src/components/connectorBuilder/Builder/AuthenticationSection.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
import { BuilderCard } from "./BuilderCard"; | ||
import { BuilderField } from "./BuilderField"; | ||
import { BuilderOneOf } from "./BuilderOneOf"; | ||
import { BuilderOptional } from "./BuilderOptional"; | ||
import { KeyValueListField } from "./KeyValueListField"; | ||
import { UserInputField } from "./UserInputField"; | ||
|
||
export const AuthenticationSection: React.FC = () => { | ||
return ( | ||
<BuilderCard> | ||
<BuilderOneOf | ||
path="global.authenticator" | ||
label="Authentication" | ||
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: ( | ||
<> | ||
<BuilderField | ||
type="string" | ||
path="global.authenticator.header" | ||
label="Header" | ||
tooltip="HTTP header which should be set to the API Key" | ||
/> | ||
<UserInputField | ||
label="API Key" | ||
tooltip="The API key issued by the service. Fill it in in the user inputs" | ||
/> | ||
</> | ||
), | ||
}, | ||
{ | ||
label: "Bearer", | ||
typeValue: "BearerAuthenticator", | ||
default: { | ||
api_token: "{{ config['api_key'] }}", | ||
}, | ||
children: ( | ||
<UserInputField | ||
label="API Key" | ||
tooltip="The API key issued by the service. Fill it in in the user inputs" | ||
/> | ||
), | ||
}, | ||
{ | ||
label: "Basic HTTP", | ||
typeValue: "BasicHttpAuthenticator", | ||
default: { | ||
username: "{{ config['username'] }}", | ||
password: "{{ config['password'] }}", | ||
}, | ||
children: ( | ||
<> | ||
<UserInputField label="Username" tooltip="The username for the login. Fill it in in the user inputs" /> | ||
<UserInputField label="Password" tooltip="The password for the login. Fill it in in the user inputs" /> | ||
</> | ||
), | ||
}, | ||
{ | ||
label: "OAuth", | ||
typeValue: "OAuthAuthenticator", | ||
default: { | ||
client_id: "{{ config['client_id'] }}", | ||
client_secret: "{{ config['client_secret'] }}", | ||
refresh_token: "{{ config['client_refresh_token'] }}", | ||
refresh_request_body: [], | ||
token_refresh_endpoint: "", | ||
}, | ||
children: ( | ||
<> | ||
<BuilderField | ||
type="string" | ||
path="global.authenticator.token_refresh_endpoint" | ||
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 secret" tooltip="The OAuth client secret" /> | ||
<UserInputField label="Refresh token" tooltip="The OAuth refresh token" /> | ||
<BuilderOptional> | ||
<BuilderField | ||
type="array" | ||
path="global.authenticator.scopes" | ||
optional | ||
label="Scopes" | ||
tooltip="Scopes to request" | ||
/> | ||
<BuilderField | ||
type="array" | ||
path="global.authenticator.token_expiry_date_format" | ||
optional | ||
label="Token expiry date format" | ||
tooltip="The format of the expiry date of the access token as obtained from the refresh endpoint" | ||
/> | ||
<BuilderField | ||
type="string" | ||
path="global.authenticator.expires_in_name" | ||
optional | ||
label="Token expiry property name" | ||
tooltip="The name of the property which contains the token exipiry date in the response from the token refresh endpoint" | ||
/> | ||
<BuilderField | ||
type="string" | ||
path="global.authenticator.access_token_name" | ||
optional | ||
label="Access token property name" | ||
tooltip="The name of the property which contains the access token in the response from the token refresh endpoint" | ||
/> | ||
<BuilderField | ||
type="string" | ||
path="global.authenticator.grant_type" | ||
optional | ||
label="Grant type" | ||
tooltip="The grant type to request for access_token" | ||
/> | ||
<KeyValueListField | ||
path="global.authenticator.refresh_request_body" | ||
label="Request Parameters" | ||
tooltip="The request body to send in the refresh request" | ||
/> | ||
</BuilderOptional> | ||
</> | ||
), | ||
}, | ||
{ | ||
label: "Session token", | ||
typeValue: "SessionTokenAuthenticator", | ||
default: { | ||
username: "{{ config['username'] }}", | ||
password: "{{ config['password'] }}", | ||
session_token: "{{ config['session_token'] }}", | ||
}, | ||
children: ( | ||
<> | ||
<BuilderField | ||
type="string" | ||
path="global.authenticator.header" | ||
label="Header" | ||
tooltip="Specific HTTP header of source API for providing session token" | ||
/> | ||
<BuilderField | ||
type="string" | ||
path="global.authenticator.session_token_response_key" | ||
label="Session token response key" | ||
tooltip="Key for retrieving session token from api response" | ||
/> | ||
<BuilderField | ||
type="string" | ||
path="global.authenticator.login_url" | ||
label="Login url" | ||
tooltip="Url for getting a specific session token" | ||
/> | ||
<BuilderField | ||
type="string" | ||
path="global.authenticator.validate_session_url" | ||
label="Validate session url" | ||
tooltip="Url to validate passed session token" | ||
/> | ||
<UserInputField label="Username" tooltip="The username" /> | ||
<UserInputField label="Password" tooltip="The password" /> | ||
<UserInputField | ||
label="Session token" | ||
tooltip="Session token generated by user (if provided username and password are not required)" | ||
/> | ||
</> | ||
), | ||
}, | ||
]} | ||
/> | ||
</BuilderCard> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 62 additions & 0 deletions
62
airbyte-webapp/src/components/connectorBuilder/Builder/BuilderOneOf.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { useField } from "formik"; | ||
import React from "react"; | ||
|
||
import GroupControls from "components/GroupControls"; | ||
import { ControlLabels } from "components/LabeledControl"; | ||
import { DropDown } from "components/ui/DropDown"; | ||
|
||
interface Option { | ||
label: string; | ||
value: string; | ||
default?: object; | ||
} | ||
|
||
interface OneOfOption { | ||
label: string; // label shown in the dropdown menu | ||
typeValue: string; // value to set on the `type` field for this component - should match the oneOf type definition | ||
default?: object; // default values for the path | ||
children?: React.ReactNode; | ||
} | ||
|
||
interface BuilderOneOfProps { | ||
options: OneOfOption[]; | ||
path: string; // path to the oneOf component in the json schema | ||
label: string; | ||
tooltip: string; | ||
} | ||
|
||
export const BuilderOneOf: React.FC<BuilderOneOfProps> = ({ options, path, label, tooltip }) => { | ||
const [, , oneOfPathHelpers] = useField(path); | ||
const typePath = `${path}.type`; | ||
const [typePathField] = useField(typePath); | ||
const value = typePathField.value; | ||
|
||
const selectedOption = options.find((option) => option.typeValue === value); | ||
|
||
return ( | ||
<GroupControls | ||
label={<ControlLabels label={label} infoTooltipContent={tooltip} />} | ||
dropdown={ | ||
<DropDown | ||
{...typePathField} | ||
options={options.map((option) => { | ||
return { label: option.label, value: option.typeValue, default: option.default }; | ||
})} | ||
value={value ?? options[0].typeValue} | ||
onChange={(selectedOption: Option) => { | ||
if (selectedOption.value === value) { | ||
return; | ||
} | ||
// clear all values for this oneOf and set selected option and default values | ||
oneOfPathHelpers.setValue({ | ||
type: selectedOption.value, | ||
...selectedOption.default, | ||
}); | ||
}} | ||
/> | ||
} | ||
> | ||
{selectedOption?.children} | ||
</GroupControls> | ||
); | ||
}; |
34 changes: 34 additions & 0 deletions
34
airbyte-webapp/src/components/connectorBuilder/Builder/BuilderOptional.module.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
@use "scss/variables"; | ||
@use "scss/colors"; | ||
@use "scss/mixins"; | ||
|
||
.wrapper { | ||
display: flex; | ||
flex-direction: column; | ||
align-items: flex-start; | ||
border-top: variables.$border-thin solid colors.$grey-100; | ||
gap: variables.$spacing-lg; | ||
} | ||
|
||
.container { | ||
padding-left: variables.$spacing-xl; | ||
display: flex; | ||
flex-direction: column; | ||
align-items: stretch; | ||
align-self: stretch; | ||
gap: variables.$spacing-lg; | ||
} | ||
|
||
.label { | ||
cursor: pointer; | ||
background: none; | ||
border: none; | ||
display: flex; | ||
gap: variables.$spacing-sm; | ||
margin-top: variables.$spacing-lg; | ||
align-items: center; | ||
|
||
&.closed { | ||
color: colors.$grey-400; | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
airbyte-webapp/src/components/connectorBuilder/Builder/BuilderOptional.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { faAngleDown, faAngleRight } from "@fortawesome/free-solid-svg-icons"; | ||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; | ||
import classNames from "classnames"; | ||
import React, { useState } from "react"; | ||
import { FormattedMessage } from "react-intl"; | ||
|
||
import styles from "./BuilderOptional.module.scss"; | ||
|
||
export const BuilderOptional: React.FC<React.PropsWithChildren<unknown>> = ({ children }) => { | ||
const [isOpen, setIsOpen] = useState(false); | ||
return ( | ||
<div className={styles.wrapper}> | ||
<button | ||
onClick={() => { | ||
setIsOpen(!isOpen); | ||
}} | ||
className={classNames(styles.label, { [styles.closed]: !isOpen })} | ||
> | ||
{isOpen ? <FontAwesomeIcon icon={faAngleDown} /> : <FontAwesomeIcon icon={faAngleRight} />} | ||
<FormattedMessage id="connectorBuilder.optionalFieldsLabel" /> | ||
</button> | ||
{isOpen && <div className={styles.container}>{children}</div>} | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.