Skip to content

Commit afaa6c1

Browse files
committed
Merge branch '434-display-config-error' into 'master'
fix(ui): display configuration errors, prevent configuration requests (#434) Closes #434 See merge request postgres-ai/database-lab!612
2 parents 4b61941 + 8374e0d commit afaa6c1

File tree

5 files changed

+83
-22
lines changed

5 files changed

+83
-22
lines changed

Diff for: ui/packages/shared/pages/Configuration/InputWithTooltip/index.tsx

+8-2
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,28 @@ export const InputWithTooltip = ({
2121
error,
2222
onChange,
2323
tooltipText,
24+
disabled,
2425
}: {
2526
value?: string
2627
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
2728
tooltipText: () => React.ReactNode
2829
label: string
2930
error?: string
31+
disabled: boolean
3032
}) => {
3133
const classes = useStyles()
3234

3335
return (
3436
<Box mt={2} mb={2} display="flex" alignItems="center">
3537
<TextField
36-
className={classNames(classes.textField, styles.textField)}
38+
className={classNames(!disabled && classes.textField, styles.textField)}
3739
label={label}
3840
variant="outlined"
3941
size="small"
4042
value={value}
4143
error={Boolean(error)}
4244
onChange={onChange}
45+
disabled={disabled}
4346
/>
4447
<Tooltip content={<p className={styles.tooltipText}>{tooltipText()}</p>}>
4548
<InfoIcon className={styles.infoIcon} />
@@ -54,6 +57,7 @@ export const InputWithChip = ({
5457
id,
5558
onChange,
5659
tooltipText,
60+
disabled,
5761
handleDeleteDatabase,
5862
}: {
5963
value: string
@@ -62,18 +66,20 @@ export const InputWithChip = ({
6266
handleDeleteDatabase: (event: any, database: string) => void
6367
label: string
6468
id: string
69+
disabled: boolean
6570
}) => {
6671
const classes = useStyles()
6772

6873
return (
6974
<Box mt={2} mb={2}>
7075
<Box display="flex" alignItems="center">
7176
<TextField
72-
className={classNames(classes.textField, styles.textField)}
77+
className={classNames(!disabled && classes.textField, styles.textField)}
7378
variant="outlined"
7479
onChange={onChange}
7580
value={value}
7681
multiline
82+
disabled={disabled}
7783
label={label}
7884
inputProps={{
7985
name: id,

Diff for: ui/packages/shared/pages/Configuration/index.tsx

+40-10
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@
55
*--------------------------------------------------------------------------
66
*/
77

8-
import { Box, Checkbox, FormControlLabel, Typography } from '@material-ui/core'
8+
import {
9+
Box,
10+
Checkbox,
11+
FormControlLabel,
12+
Typography,
13+
Snackbar,
14+
} from '@material-ui/core'
915
import { useState, useEffect } from 'react'
1016
import { withStyles, makeStyles } from '@material-ui/core/styles'
1117
import { Modal } from '@postgres.ai/shared/components/Modal'
@@ -56,10 +62,13 @@ export const Configuration = observer(
5662
getFullConfig,
5763
fullConfig,
5864
testDbSource,
59-
updateConfigError,
65+
configError,
66+
dbSourceError,
6067
getFullConfigError,
68+
instanceRetrieval,
6169
} = stores.main
6270
const configData = config && JSON.parse(JSON.stringify(config))
71+
const isConfigurationActive = instanceRetrieval?.mode !== 'physical'
6372
const [submitMessage, setSubmitMessage] = useState<
6473
string | React.ReactNode | null
6574
>('')
@@ -165,6 +174,12 @@ export const Configuration = observer(
165174

166175
return (
167176
<div className={styles.root}>
177+
<Snackbar
178+
anchorOrigin={{ vertical: "bottom", horizontal: 'right' }}
179+
open={!isConfigurationActive && !isOpen}
180+
message={'Configuration editing is only available in logical mode'}
181+
className={styles.snackbar}
182+
/>
168183
<Box>
169184
<Header retrievalMode="logical" setOpen={handleModalClick} />
170185
<Box>
@@ -174,6 +189,7 @@ export const Configuration = observer(
174189
<Checkbox
175190
name="debug"
176191
checked={formik.values.debug}
192+
disabled={!isConfigurationActive}
177193
onChange={(e) =>
178194
formik.setFieldValue('debug', e.target.checked)
179195
}
@@ -196,6 +212,7 @@ export const Configuration = observer(
196212
value={formik.values.dockerImage}
197213
error={formik.errors.dockerImage}
198214
tooltipText={tooltipText.dockerImage}
215+
disabled={!isConfigurationActive}
199216
onChange={(e) =>
200217
formik.setFieldValue('dockerImage', e.target.value)
201218
}
@@ -211,6 +228,7 @@ export const Configuration = observer(
211228
label="configs.shared_buffers"
212229
value={formik.values.sharedBuffers}
213230
tooltipText={tooltipText.sharedBuffers}
231+
disabled={!isConfigurationActive}
214232
onChange={(e) =>
215233
formik.setFieldValue('sharedBuffers', e.target.value)
216234
}
@@ -219,6 +237,7 @@ export const Configuration = observer(
219237
label="configs.shared_preload_libraries"
220238
value={formik.values.sharedPreloadLibraries}
221239
tooltipText={tooltipText.sharedPreloadLibraries}
240+
disabled={!isConfigurationActive}
222241
onChange={(e) =>
223242
formik.setFieldValue('sharedPreloadLibraries', e.target.value)
224243
}
@@ -238,27 +257,31 @@ export const Configuration = observer(
238257
value={formik.values.host}
239258
error={formik.errors.host}
240259
tooltipText={tooltipText.host}
260+
disabled={!isConfigurationActive}
241261
onChange={(e) => formik.setFieldValue('host', e.target.value)}
242262
/>
243263
<InputWithTooltip
244264
label="source.connection.port"
245265
value={formik.values.port}
246266
error={formik.errors.port}
247267
tooltipText={tooltipText.port}
268+
disabled={!isConfigurationActive}
248269
onChange={(e) => formik.setFieldValue('port', e.target.value)}
249270
/>
250271
<InputWithTooltip
251272
label="source.connection.username"
252273
value={formik.values.username}
253274
error={formik.errors.username}
254275
tooltipText={tooltipText.username}
276+
disabled={!isConfigurationActive}
255277
onChange={(e) =>
256278
formik.setFieldValue('username', e.target.value)
257279
}
258280
/>
259281
<InputWithTooltip
260282
label="source.connection.password"
261283
tooltipText={tooltipText.password}
284+
disabled={!isConfigurationActive}
262285
onChange={(e) =>
263286
formik.setFieldValue('password', e.target.value)
264287
}
@@ -268,6 +291,7 @@ export const Configuration = observer(
268291
value={formik.values.dbname}
269292
error={formik.errors.dbname}
270293
tooltipText={tooltipText.dbname}
294+
disabled={!isConfigurationActive}
271295
onChange={(e) =>
272296
formik.setFieldValue('dbname', e.target.value)
273297
}
@@ -278,6 +302,7 @@ export const Configuration = observer(
278302
id="databases"
279303
tooltipText={tooltipText.databases}
280304
handleDeleteDatabase={handleDeleteDatabase}
305+
disabled={!isConfigurationActive}
281306
onChange={(e) =>
282307
formik.setFieldValue('databases', e.target.value)
283308
}
@@ -287,18 +312,20 @@ export const Configuration = observer(
287312
variant="primary"
288313
size="medium"
289314
onClick={onTestConnectionClick}
290-
isDisabled={isTestConnectionLoading}
315+
isDisabled={
316+
isTestConnectionLoading || !isConfigurationActive
317+
}
291318
>
292319
Test connection
293320
{isTestConnectionLoading && (
294321
<Spinner size="sm" className={styles.spinner} />
295322
)}
296323
</Button>
297324
</Box>
298-
{connectionStatus && connectionResponse ? (
325+
{(connectionStatus && connectionResponse) || dbSourceError ? (
299326
<ResponseMessage
300-
type={connectionStatus}
301-
message={connectionResponse}
327+
type={dbSourceError ? 'error' : connectionStatus}
328+
message={dbSourceError || connectionResponse}
302329
/>
303330
) : null}
304331
</Box>
@@ -307,12 +334,14 @@ export const Configuration = observer(
307334
label="pg_dump jobs"
308335
value={formik.values.pg_dump}
309336
tooltipText={tooltipText.pg_dump}
337+
disabled={!isConfigurationActive}
310338
onChange={(e) => formik.setFieldValue('pg_dump', e.target.value)}
311339
/>
312340
<InputWithTooltip
313341
label="pg_restore jobs"
314342
value={formik.values.pg_restore}
315343
tooltipText={tooltipText.pg_restore}
344+
disabled={!isConfigurationActive}
316345
onChange={(e) =>
317346
formik.setFieldValue('pg_restore', e.target.value)
318347
}
@@ -340,6 +369,7 @@ export const Configuration = observer(
340369
label="timetable"
341370
value={formik.values.timetable}
342371
tooltipText={tooltipText.timetable}
372+
disabled={!isConfigurationActive}
343373
onChange={(e) =>
344374
formik.setFieldValue('timetable', e.target.value)
345375
}
@@ -357,7 +387,7 @@ export const Configuration = observer(
357387
variant="primary"
358388
size="medium"
359389
onClick={formik.submitForm}
360-
isDisabled={formik.isSubmitting}
390+
isDisabled={formik.isSubmitting || !isConfigurationActive}
361391
>
362392
Apply changes
363393
{formik.isSubmitting && (
@@ -374,10 +404,10 @@ export const Configuration = observer(
374404
</Button>
375405
</Box>
376406
</Box>
377-
{(submitStatus && submitMessage) || updateConfigError ? (
407+
{(submitStatus && submitMessage) || configError ? (
378408
<ResponseMessage
379-
type={updateConfigError ? 'error' : submitStatus}
380-
message={updateConfigError || submitMessage}
409+
type={configError ? 'error' : submitStatus}
410+
message={configError || submitMessage}
381411
/>
382412
) : null}
383413
</Box>

Diff for: ui/packages/shared/pages/Configuration/styles.module.scss

+13
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
label {
1212
font-size: 10px;
1313
}
14+
15+
:disabled {
16+
cursor: not-allowed;
17+
color: rgba(0, 0, 0, 0.38)
18+
}
1419
}
1520

1621
.databasesContainer {
@@ -89,3 +94,11 @@
8994
.firaCodeFont {
9095
font-family: 'Fira Code', monospace !important;
9196
}
97+
98+
.snackbar {
99+
div {
100+
background-color: #ff6212;
101+
color: #fff;
102+
border-radius: 4px;
103+
}
104+
}

Diff for: ui/packages/shared/pages/Instance/index.tsx

+8-3
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,15 @@ export const Instance = observer((props: Props) => {
7676
stores.main.load(instanceId)
7777
}, [instanceId])
7878

79-
const { instance, instanceError } = stores.main
79+
const { instance, instanceError, instanceRetrieval } = stores.main
80+
const isConfigurationActive = instanceRetrieval?.mode !== 'physical'
8081

8182
useEffect(() => {
82-
if (instance && instance?.state.retrieving?.status === "pending") {
83+
if (
84+
instance &&
85+
instance?.state.retrieving?.status === 'pending' &&
86+
isConfigurationActive
87+
) {
8388
setActiveTab(2)
8489
}
8590
if (instance && !instance?.state?.pools) {
@@ -94,7 +99,7 @@ export const Instance = observer((props: Props) => {
9499

95100
const [isLogConnectionEnabled, enableLogConnection] = React.useState(false);
96101

97-
const switchTab = (_event: React.ChangeEvent<{}>, tabID: number) => {
102+
const switchTab = (_: React.ChangeEvent<{}>, tabID: number) => {
98103
if (tabID == 1 && api.initWS != undefined && !isLogConnectionEnabled) {
99104
establishConnection(api).then(() => {
100105
enableLogConnection(true)

Diff for: ui/packages/shared/pages/Instance/stores/Main.ts

+14-7
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ export class MainStore {
5555
config: Config | null = null
5656
fullConfig?: string
5757
instanceError: Error | null = null
58-
updateConfigError: string | null = null
58+
configError: string | null = null
59+
dbSourceError: string | null = null
5960
getFullConfigError: string | null = null
6061

6162
unstableClones = new Set<string>()
@@ -83,8 +84,11 @@ export class MainStore {
8384
load = (instanceId: string) => {
8485
this.instance = null
8586
this.loadInstance(instanceId)
86-
this.loadInstanceRetrieval(instanceId)
87-
this.getConfig()
87+
this.loadInstanceRetrieval(instanceId).then(() => {
88+
if (this.instanceRetrieval?.mode !== "physical") {
89+
this.getConfig()
90+
}
91+
})
8892
this.snapshots.load(instanceId)
8993
}
9094

@@ -168,9 +172,11 @@ export class MainStore {
168172
this.config = response
169173
}
170174

171-
if (error) await getTextFromUnknownApiError(error)
175+
if (error) {
176+
this.configError = await error.json().then((err) => err.message)
177+
}
172178

173-
return !!response
179+
return response
174180
}
175181

176182
updateConfig = async (values: Config) => {
@@ -179,7 +185,7 @@ export class MainStore {
179185
const { response, error } = await this.api.updateConfig({ ...values })
180186

181187
if (error)
182-
this.updateConfigError = await error.json().then((err) => err.message)
188+
this.configError = await error.json().then((err) => err.message)
183189

184190
return response
185191
}
@@ -203,7 +209,8 @@ export class MainStore {
203209

204210
const { response, error } = await this.api.testDbSource(values)
205211

206-
if (error) await getTextFromUnknownApiError(error)
212+
if (error)
213+
this.dbSourceError = await error.json().then((err) => err.message)
207214

208215
return response
209216
}

0 commit comments

Comments
 (0)