Skip to content

Commit

Permalink
🪟 🎉 Display service token validation errors in the UI (#19578)
Browse files Browse the repository at this point in the history
Note: the backend returns a 500 status (should be 400, or at least 4xx), so the frontend has to remove the error message's unsightly "Internal Server Error: " prefix in post.

In the future, we want to move this to a 400 error.
  • Loading branch information
ambirdsall authored and SofiiaZaitseva committed Nov 24, 2022
1 parent 1190fb0 commit 8d94c3a
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 16 deletions.
1 change: 1 addition & 0 deletions airbyte-webapp/src/packages/cloud/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"settings.integrationSettings.dbtCloudSettings.form.singleTenantUrl": "Single-tenant URL",
"settings.integrationSettings.dbtCloudSettings.form.testConnection": "Test connection",
"settings.integrationSettings.dbtCloudSettings.form.submit": "Save changes",
"settings.integrationSettings.dbtCloudSettings.form.success": "Service Token saved successfully",
"settings.generalSettings": "General Settings",
"settings.generalSettings.changeWorkspace": "Change Workspace",
"settings.generalSettings.form.name.label": "Workspace name",
Expand Down
28 changes: 17 additions & 11 deletions airbyte-webapp/src/packages/cloud/services/dbtCloud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
OperationRead,
OperatorWebhookWebhookType,
WebhookConfigRead,
WorkspaceRead,
} from "core/request/AirbyteClient";
import { useWebConnectionService } from "hooks/services/useConnectionHook";
import { useCurrentWorkspace } from "hooks/services/useWorkspace";
Expand Down Expand Up @@ -61,21 +62,26 @@ const isDbtCloudJob = (operation: OperationRead): boolean =>
export const isSameJob = (remoteJob: DbtCloudJobInfo, savedJob: DbtCloudJob): boolean =>
savedJob.accountId === remoteJob.accountId && savedJob.jobId === remoteJob.jobId;

type ServiceToken = string;

export const useSubmitDbtCloudIntegrationConfig = () => {
const { workspaceId } = useCurrentWorkspace();
const { mutateAsync: updateWorkspace } = useUpdateWorkspace();

return useMutation(async (authToken: string) => {
await updateWorkspace({
workspaceId,
webhookConfigs: [
{
name: webhookConfigName,
authToken,
},
],
});
});
return useMutation<WorkspaceRead, Error, ServiceToken>(
["submitWorkspaceDbtCloudToken", workspaceId],
async (authToken: string) => {
return await updateWorkspace({
workspaceId,
webhookConfigs: [
{
name: webhookConfigName,
authToken,
},
],
});
}
);
};

export const useDbtIntegration = (connection: WebBackendConnectionRead) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Field, FieldProps, Form, Formik } from "formik";
import React from "react";
import { FormattedMessage } from "react-intl";
import React, { useState } from "react";
import { FormattedMessage, useIntl } from "react-intl";

import { LabeledInput } from "components/LabeledInput";
import { Button } from "components/ui/Button";
Expand All @@ -11,22 +11,43 @@ import { Content, SettingsCard } from "pages/SettingsPage/pages/SettingsComponen
import styles from "./DbtCloudSettingsView.module.scss";

export const DbtCloudSettingsView: React.FC = () => {
const { formatMessage } = useIntl();
const { mutate: submitDbtCloudIntegrationConfig, isLoading } = useSubmitDbtCloudIntegrationConfig();
const [hasValidationError, setHasValidationError] = useState(false);
const [validationMessage, setValidationMessage] = useState("");
return (
<SettingsCard title={<FormattedMessage id="settings.integrationSettings.dbtCloudSettings" />}>
<Content>
<Formik
initialValues={{
serviceToken: "",
}}
onSubmit={({ serviceToken }) => submitDbtCloudIntegrationConfig(serviceToken)}
onSubmit={({ serviceToken }, { resetForm }) => {
setHasValidationError(false);
setValidationMessage("");
return submitDbtCloudIntegrationConfig(serviceToken, {
onError: (e) => {
setHasValidationError(true);

setValidationMessage(e.message.replace("Internal Server Error: ", ""));
},
onSuccess: () => {
setValidationMessage(
formatMessage({ id: "settings.integrationSettings.dbtCloudSettings.form.success" })
);
resetForm();
},
});
}}
>
<Form>
<Field name="serviceToken">
{({ field }: FieldProps<string>) => (
<LabeledInput
{...field}
label={<FormattedMessage id="settings.integrationSettings.dbtCloudSettings.form.serviceToken" />}
error={hasValidationError}
message={validationMessage}
type="text"
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ const DbtJobsList = ({ jobs, remove, dirty, isLoading }: DbtJobsListProps) => {
<FormattedMessage id="connection.dbtCloudJobs.explanation" />
</Text>
{jobs.map((job, i) => (
<JobsListItem key={i} job={job} removeJob={() => remove(i)} />
<JobsListItem key={i} job={job} removeJob={() => remove(i)} isLoading={isLoading} />
))}
</>
) : (
Expand Down Expand Up @@ -184,8 +184,9 @@ const DbtJobsList = ({ jobs, remove, dirty, isLoading }: DbtJobsListProps) => {
interface JobsListItemProps {
job: DbtCloudJob;
removeJob: () => void;
isLoading: boolean;
}
const JobsListItem = ({ job, removeJob }: JobsListItemProps) => {
const JobsListItem = ({ job, removeJob, isLoading }: JobsListItemProps) => {
const { formatMessage } = useIntl();
// TODO if `job.jobName` is undefined, that means we failed to match any of the
// dbt-Cloud-supplied jobs with the saved job. This means one of two things has
Expand Down Expand Up @@ -218,6 +219,7 @@ const JobsListItem = ({ job, removeJob }: JobsListItemProps) => {
size="lg"
className={styles.jobListItemDelete}
onClick={removeJob}
disabled={isLoading}
aria-label={formatMessage({ id: "connection.dbtCloudJobs.job.deleteButton" })}
>
<FontAwesomeIcon icon={faXmark} height="21" width="21" />
Expand Down

0 comments on commit 8d94c3a

Please sign in to comment.