Skip to content

Commit 55e19f5

Browse files
authored
Test connection errors (#23)
1 parent a56209e commit 55e19f5

2 files changed

Lines changed: 327 additions & 224 deletions

File tree

web-app/pontoon/src/app/destinations/new/page.js

Lines changed: 180 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ import {
99
Checkbox,
1010
CircularProgress,
1111
FormHelperText,
12+
Alert,
1213
} from "@mui/material";
1314
import { useState } from "react";
1415
import { Form, Formik } from "formik";
1516
import * as Yup from "yup";
1617
import useSWRMutation from "swr/mutation";
18+
import { mutate } from "swr";
1719
import Link from "next/link";
1820
import { ChevronLeft } from "@mui/icons-material";
1921
import { useRouter } from "next/navigation";
@@ -51,133 +53,159 @@ import {
5153
getPostgresInitialValues,
5254
} from "@/app/components/forms/connection-details/PostgresConnectionDetails";
5355

54-
const AddDestination = () => {
55-
const Status = Object.freeze({
56-
NOT_STARTED: 0,
57-
LOADING: 1,
58-
SUCCESS: 2,
59-
FAILED: 3,
60-
});
61-
62-
// state of the connection test
63-
const [testConnectionStatus, setTestConnectionStatus] = useState(
64-
Status.NOT_STARTED
65-
);
66-
67-
// state for the source being created
68-
const [destinationCreated, setDestinationCreated] = useState(false);
69-
const [destinationId, setDestinationId] = useState("");
70-
71-
const router = useRouter();
72-
73-
const createDestination = (params) => {
74-
return postRequest("/destinations", { arg: params });
75-
};
76-
77-
const updateDestination = (destinationId, params) => {
78-
return putRequest(`/destinations/${destinationId}`, { arg: params });
79-
};
80-
81-
const startDestinationCheck = (destinationId) => {
82-
return postRequest(`/destinations/${destinationId}/check`, { arg: {} });
56+
const testConnection = async (key, { arg: values, destinationId }) => {
57+
const params = {
58+
destination_name: values.destination_name,
59+
vendor_type: values.vendor_type,
60+
recipient_id: values.recipient_id,
61+
schedule: {
62+
type: "INCREMENTAL",
63+
frequency: values.schedule_frequency,
64+
day: values.schedule_day,
65+
hour: values.schedule_hour,
66+
minute: 0,
67+
},
68+
models: values.selectedModels,
69+
connection_info: {
70+
vendor_type: values.vendor_type,
71+
...values[values.vendor_type],
72+
},
8373
};
8474

85-
const waitForDestinationCheck = (destinationId, taskId) => {
86-
return pollTaskStatus(`/destinations/${destinationId}/check/${taskId}`);
87-
};
75+
try {
76+
if (!destinationId) {
77+
console.log("Creating destination");
78+
const result = await postRequest("/destinations", { arg: params });
79+
const check = await postRequest(
80+
`/destinations/${result.destination_id}/check`,
81+
{
82+
arg: {},
83+
}
84+
);
85+
const status = await pollTaskStatus(
86+
`/destinations/${result.destination_id}/check/${check.task_id}`
87+
);
88+
89+
if (
90+
status.success === undefined ||
91+
status.success === null ||
92+
status.success === false
93+
) {
94+
throw new Error(status.cause);
95+
}
8896

89-
const updateCheckState = (result) => {
90-
if (!result.success || result.success === false) {
91-
setTestConnectionStatus(Status.FAILED);
97+
return {
98+
destination_id: result.destination_id,
99+
success: status.success,
100+
};
92101
} else {
93-
setTestConnectionStatus(Status.SUCCESS);
102+
console.log("Updating destination");
103+
const result = await putRequest(`/destinations/${destinationId}`, {
104+
arg: params,
105+
});
106+
const check = await postRequest(`/destinations/${destinationId}/check`, {
107+
arg: {},
108+
});
109+
const status = await pollTaskStatus(
110+
`/destinations/${destinationId}/check/${check.task_id}`
111+
);
112+
113+
if (
114+
status.success === undefined ||
115+
status.success === null ||
116+
status.success === false
117+
) {
118+
throw new Error(status.cause);
119+
}
120+
121+
return {
122+
destination_id: result.destination_id,
123+
success: status.success,
124+
};
94125
}
95-
};
126+
} catch (e) {
127+
console.warn("Error testing connection: ", e);
128+
throw e;
129+
}
130+
};
131+
132+
const enableDestination = async (key, { arg: destinationId }) => {
133+
try {
134+
const result = await putRequest(`/destinations/${destinationId}`, {
135+
arg: { is_enabled: true, state: "CREATED" },
136+
});
137+
// Invalidate the destinations cache since there's a new destination
138+
mutate("/destinations");
139+
return result;
140+
} catch (e) {
141+
console.warn("Error enabling destination: ", e);
142+
throw e;
143+
}
144+
};
96145

146+
const AddDestination = () => {
97147
const {
98-
data: recipients,
99-
error: recipientsError,
100-
isLoading: recipientsLoading,
101-
} = useSWR("/recipients", getRequest);
148+
trigger: testConnectionTrigger,
149+
data: testConnectionResult,
150+
error: testConnectionError,
151+
isMutating: isTestConnectionMutating,
152+
} = useSWRMutation("/destinations/test_connection", testConnection);
153+
const destinationId = testConnectionResult?.destination_id;
102154

103155
const {
104-
data: models,
105-
error: modelsError,
106-
isLoading: modelsLoading,
107-
} = useSWR("/models", getRequest);
108-
const modelIds = models?.map((m) => m.model_id);
156+
trigger: enableDestinationTrigger,
157+
data: enableDestinationResult,
158+
error: enableDestinationError,
159+
isMutating: isEnableDestinationMutating,
160+
} = useSWRMutation(
161+
(destinationId) => `/destinations/${destinationId}`,
162+
enableDestination
163+
);
164+
165+
const router = useRouter();
109166

110167
// form submission handler
111168
const handleCreateAndCheckDestination = async (values, validateForm) => {
112-
setTestConnectionStatus(Status.LOADING);
113169
const errors = await validateForm(values);
114170
if (Object.keys(errors).length > 0) {
115171
console.log(errors);
116-
setTestConnectionStatus(Status.FAILED);
117172
return;
118173
}
119-
const params = {
120-
destination_name: values.destination_name,
121-
vendor_type: values.vendor_type,
122-
recipient_id: values.recipient_id,
123-
schedule: {
124-
type: "INCREMENTAL",
125-
frequency: values.schedule_frequency,
126-
day: values.schedule_day,
127-
hour: values.schedule_hour,
128-
minute: 0,
129-
},
130-
models: values.selectedModels,
131-
connection_info: {
132-
vendor_type: values.vendor_type,
133-
...values[values.vendor_type],
134-
},
135-
};
136174

137175
try {
138-
if (!destinationCreated) {
139-
console.log("Creating destination");
140-
const result = await createDestination(params);
141-
const check = await startDestinationCheck(result.destination_id);
142-
const status = await waitForDestinationCheck(
143-
result.destination_id,
144-
check.task_id
145-
);
146-
147-
setDestinationCreated(true);
148-
setDestinationId(result.destination_id);
149-
updateCheckState(status);
150-
} else {
151-
console.log("Updating destination");
152-
await updateDestination(destinationId, params);
153-
const check = await startDestinationCheck(destinationId);
154-
const status = await waitForDestinationCheck(
155-
destinationId,
156-
check.task_id
157-
);
158-
updateCheckState(status);
159-
}
176+
const result = await testConnectionTrigger(values, destinationId);
160177
} catch (e) {
161-
setTestConnectionStatus(Status.FAILED);
162-
} finally {
178+
console.warn("Error testing connection: ", e);
179+
return;
163180
}
164181
};
165182

166183
// when the create button is clicked
167-
const handleEnableDestination = async () => {
168-
if (destinationCreated) {
184+
const handleEnableDestination = async (values) => {
185+
console.log("Submitting form");
186+
if (destinationId) {
169187
try {
170-
await updateDestination(destinationId, {
171-
is_enabled: true,
172-
state: "CREATED",
173-
});
188+
await enableDestinationTrigger(destinationId);
174189
router.push("/destinations");
175190
} catch (e) {
176191
console.log("Enabling destination failed: ", e);
177192
}
178193
}
179194
};
180195

196+
const {
197+
data: recipients,
198+
error: recipientsError,
199+
isLoading: recipientsLoading,
200+
} = useSWR("/recipients", getRequest);
201+
202+
const {
203+
data: models,
204+
error: modelsError,
205+
isLoading: modelsLoading,
206+
} = useSWR("/models", getRequest);
207+
const modelIds = models?.map((m) => m.model_id);
208+
181209
if (recipientsError || modelsError) {
182210
return <Typography>Error with API</Typography>;
183211
}
@@ -389,7 +417,8 @@ const AddDestination = () => {
389417
variant="contained"
390418
disabled={
391419
isValidating ||
392-
testConnectionStatus == Status.LOADING ||
420+
isTestConnectionMutating ||
421+
isEnableDestinationMutating ||
393422
!isValid ||
394423
!dirty
395424
}
@@ -399,31 +428,50 @@ const AddDestination = () => {
399428
>
400429
Test Connection
401430
</Button>
402-
{testConnectionStatus == Status.LOADING ? (
403-
<CircularProgress size={28} />
404-
) : null}
405-
{testConnectionStatus == Status.SUCCESS ? (
406-
<Typography fontSize={26}></Typography>
407-
) : null}
408-
{testConnectionStatus == Status.FAILED ? (
409-
<Typography fontSize={26}></Typography>
410-
) : null}
431+
{renderTestConnectionStatus(
432+
testConnectionResult,
433+
testConnectionError,
434+
isTestConnectionMutating
435+
)}
411436
</Stack>
412437
<FormHelperText>
413438
Testing the connection may take a few minutes.
414439
</FormHelperText>
440+
{testConnectionError ? (
441+
<Alert severity="error">
442+
<Typography>{testConnectionError.message}</Typography>
443+
</Alert>
444+
) : null}
445+
{enableDestinationError ? (
446+
<Alert severity="error">
447+
<Typography>
448+
{enableDestinationError.message}
449+
</Typography>
450+
</Alert>
451+
) : null}
415452
</Stack>
416453

417-
<Button
418-
type="submit"
419-
variant="contained"
420-
disabled={
421-
isSubmitting || !(testConnectionStatus == Status.SUCCESS)
422-
}
423-
sx={{ width: "fit-content" }}
424-
>
425-
Create
426-
</Button>
454+
<Stack direction="row" alignItems="center" spacing={1.5}>
455+
<Button
456+
type="submit"
457+
variant="contained"
458+
disabled={
459+
isValidating ||
460+
isTestConnectionMutating ||
461+
isEnableDestinationMutating ||
462+
testConnectionError ||
463+
testConnectionResult?.success === false ||
464+
!isValid ||
465+
!dirty
466+
}
467+
sx={{ width: "fit-content" }}
468+
>
469+
Create
470+
</Button>
471+
{isEnableDestinationMutating ? (
472+
<CircularProgress size={28} />
473+
) : null}
474+
</Stack>
427475
</Stack>
428476
</Form>
429477
);
@@ -449,6 +497,23 @@ const renderConnectionDetails = (vendor_type, setFieldValue, values) => {
449497
}
450498
};
451499

500+
const renderTestConnectionStatus = (
501+
testConnectionResult,
502+
testConnectionError,
503+
isTestConnectionMutating
504+
) => {
505+
if (isTestConnectionMutating) {
506+
return <CircularProgress size={28} />;
507+
}
508+
if (testConnectionError) {
509+
return <Typography fontSize={26}></Typography>;
510+
}
511+
if (testConnectionResult?.success === true) {
512+
return <Typography fontSize={26}></Typography>;
513+
}
514+
return null;
515+
};
516+
452517
const renderScheduleDetails = (schedule_frequency) => {
453518
return (
454519
<>

0 commit comments

Comments
 (0)