Skip to content

Commit

Permalink
oAuth input parameters UI (#7325)
Browse files Browse the repository at this point in the history
* oAuth input parameters UI

* fix review comments part 1

* fix review comments part 2

* Oauth UI style fixes (#7574)

* Oauth UI codestyle fixes

Co-authored-by: Artem Astapenko <jamakase54@gmail.com>
Co-authored-by: Artem Astapenko <3767150+Jamakase@users.noreply.github.com>
  • Loading branch information
3 people authored Nov 12, 2021
1 parent b295868 commit 0c41542
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 20 deletions.
14 changes: 14 additions & 0 deletions airbyte-webapp/package-lock.json

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

2 changes: 2 additions & 0 deletions airbyte-webapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"lodash.get": "^4.4.2",
"lodash.isequal": "^4.5.0",
"lodash.merge": "^4.6.2",
"lodash.pick": "^4.4.0",
"query-string": "^6.13.1",
"react": "17.0.1",
"react-dom": "17.0.1",
Expand Down Expand Up @@ -67,6 +68,7 @@
"@types/lodash.get": "^4.4.6",
"@types/lodash.isequal": "^4.5.5",
"@types/lodash.merge": "^4.6.6",
"@types/lodash.pick": "^4.4.6",
"@types/node": "^12.0.0",
"@types/query-string": "^6.3.0",
"@types/react": "17.0.2",
Expand Down
3 changes: 3 additions & 0 deletions airbyte-webapp/src/core/domain/connector/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface ConnectorDefinitionSpecificationBase {
rootObject?: string[];
oauthFlowInitParameters?: string[][];
oauthFlowOutputParameters?: string[][];
oauthFlowInputFields?: string[][];
};
};
}
Expand All @@ -39,10 +40,12 @@ export interface SourceGetConsentPayload {
redirectUrl: string;
sourceDefinitionId: string;
workspaceId: string;
inputParams: Record<string, unknown>;
}

export interface DestinationGetConsentPayload {
redirectUrl: string;
destinationDefinitionId: string;
workspaceId: string;
inputParams: Record<string, unknown>;
}
43 changes: 26 additions & 17 deletions airbyte-webapp/src/hooks/services/useConnectorAuth.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { useFormikContext } from "formik";
import { useCallback, useEffect, useMemo, useRef } from "react";
import { useAsyncFn } from "react-use";
import merge from "lodash.merge";

import {
ConnectorDefinitionSpecification,
Expand All @@ -18,6 +16,7 @@ import { useGetService } from "core/servicesProvider";
import { RequestMiddleware } from "core/request/RequestMiddleware";
import { isSourceDefinitionSpecification } from "core/domain/connector/source";
import useRouter from "../useRouter";
import { ConnectionConfiguration } from "core/domain/connection";

export function useConnectorAuth() {
const { workspaceId } = useCurrentWorkspace();
Expand All @@ -37,7 +36,8 @@ export function useConnectorAuth() {

return {
getConsentUrl: async (
connector: ConnectorDefinitionSpecification
connector: ConnectorDefinitionSpecification,
inputParams: Record<string, unknown>
): Promise<{
payload: SourceGetConsentPayload | DestinationGetConsentPayload;
consentUrl: string;
Expand All @@ -47,15 +47,16 @@ export function useConnectorAuth() {
workspaceId,
sourceDefinitionId: ConnectorSpecification.id(connector),
redirectUrl: `${oauthRedirectUrl}/auth_flow`,
inputParams,
};
const response = await sourceAuthService.getConsentUrl(payload);

return { consentUrl: response.consentUrl, payload };
} else {
const payload = {
workspaceId,
destinationDefinitionId: ConnectorSpecification.id(connector),
redirectUrl: `${oauthRedirectUrl}/auth_flow`,
inputParams,
};
const response = await destinationAuthService.getConsentUrl(payload);

Expand Down Expand Up @@ -99,27 +100,36 @@ function openWindow(url: string): void {
}

export function useRunOauthFlow(
connector: ConnectorDefinitionSpecification
connector: ConnectorDefinitionSpecification,
onDone?: (values: {
connectionConfiguration: ConnectionConfiguration;
}) => void
): {
loading: boolean;
done?: boolean;
run: () => void;
run: (oauthInputParams: Record<string, unknown>) => void;
} {
const { values, setValues } = useFormikContext();
const { getConsentUrl, completeOauthRequest } = useConnectorAuth();

const param = useRef<
SourceGetConsentPayload | DestinationGetConsentPayload
>();

const [{ loading }, onStartOauth] = useAsyncFn(async () => {
const consentRequestInProgress = await getConsentUrl(connector);
const [{ loading }, onStartOauth] = useAsyncFn(
async (oauthInputParams: Record<string, unknown>) => {
const consentRequestInProgress = await getConsentUrl(
connector,
oauthInputParams
);

param.current = consentRequestInProgress.payload;
openWindow(consentRequestInProgress.consentUrl);
}, [connector]);
param.current = consentRequestInProgress.payload;
openWindow(consentRequestInProgress.consentUrl);
},
[connector]
);

const [{ loading: loadingCompleteOauth, value }, completeOauth] = useAsyncFn(
async (queryParams: Record<string, unknown>) => {
async (queryParams: Record<string, unknown>): Promise<boolean> => {
const oauthStartedPayload = param.current;

if (oauthStartedPayload) {
Expand All @@ -128,13 +138,12 @@ export function useRunOauthFlow(
queryParams
);

setValues(merge(values, { connectionConfiguration }));
return true;
onDone?.({ connectionConfiguration });
}

return false;
return !!oauthStartedPayload;
},
[connector, values]
[connector]
);

const onOathGranted = useCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { FormattedMessage } from "react-intl";
import styled from "styled-components";

import { Button } from "components";
import { useRunOauthFlow } from "hooks/services/useConnectorAuth";
import { useServiceForm } from "../serviceFormContext";
import { useFormikOauthAdapter } from "./useOauthFlowAdapter";

const AuthSectionRow = styled.div`
display: flex;
Expand All @@ -23,11 +23,11 @@ const SuccessMessage = styled.div`
export const AuthButton: React.FC = () => {
const { selectedService, selectedConnector } = useServiceForm();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const { loading, done, run } = useRunOauthFlow(selectedConnector!);
const { loading, done, run } = useFormikOauthAdapter(selectedConnector!);

return (
<AuthSectionRow>
<Button isLoading={loading} type="button" onClick={run}>
<Button isLoading={loading} type="button" onClick={() => run()}>
{done ? (
<>
<FormattedMessage id="connectorForm.reauthenticate" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { useFormikContext } from "formik";
import merge from "lodash.merge";
import pick from "lodash.pick";

import { ServiceFormValues } from "../types";
import { ConnectorDefinitionSpecification } from "core/domain/connector";
import { useRunOauthFlow } from "hooks/services/useConnectorAuth";

function useFormikOauthAdapter(
connector: ConnectorDefinitionSpecification
): {
loading: boolean;
done?: boolean;
run: () => Promise<void>;
} {
const {
values,
setValues,
setFieldTouched,
errors,
} = useFormikContext<ServiceFormValues>();

const onDone = (v: Pick<ServiceFormValues, "connectionConfiguration">) =>
setValues(merge(values, v));

const { run, loading, done } = useRunOauthFlow(connector, onDone);

return {
loading,
done,
run: async () => {
const oauthInputFields =
connector?.authSpecification?.oauth2Specification?.oauthFlowInputFields?.map(
(value) => value.join(".")
) ?? [];

if (oauthInputFields.length) {
oauthInputFields.forEach((path) =>
setFieldTouched(`connectionConfiguration.${path}`, true, true)
);

const oAuthErrors = pick(
errors.connectionConfiguration,
oauthInputFields
);

if (Object.keys(oAuthErrors).length) {
return;
}
}

const oauthInputParams = pick(
values.connectionConfiguration,
oauthInputFields
);

await run(oauthInputParams);
},
};
}

export { useFormikOauthAdapter };

0 comments on commit 0c41542

Please sign in to comment.