Skip to content

Commit

Permalink
Add search (Autocomplete) in environment/group dropdown in create/imp…
Browse files Browse the repository at this point in the history
…ort forms (#1347)

### Feature or Bugfix
- Feature

### Detail
Autocomplete for environments and teams in the following frontend views
as requested in 1012.

This PR introduces a 2 new Shared frontend components:
EnvironmentTeamDropdown and EnvironmentTeamDatasetsDropdown. They are
generic formik fields that can be reused in Formik forms in several
modules. Some creation forms include custom parameters that are fetched
alongside the environments and teams (e.g. Notebooks sets VPCs). For
those cases, we implement the Autocomplete logic in the view itself

- [X] DashboardImportForm.js - using EnvironmentTeamDropdown
- [X] MLStudioCreateForm.js - using EnvironmentTeamDropdown
- [X] NotebookCreateForm.js - custom
- [X] OmicsRunCreateForm.js - using EnvironmentTeamDatasetsDropdown
- [X] DatasetCreateForm.js - custom
- [X] DatasetImportForm.js - custom

Pipelines in a separate PR

In all forms we use `SamlAdminGroupName` instead of `SamlGroupName` as a
standard that allows us to use Shared components

### Relates
- #1335
- #1012 


### Security
Please answer the questions below briefly where applicable, or write
`N/A`. Based on
[OWASP 10](https://owasp.org/Top10/en/).

- Does this PR introduce or modify any input fields or queries - this
includes
fetching data from storage outside the application (e.g. a database, an
S3 bucket)?
  - Is the input sanitized?
- What precautions are you taking before deserializing the data you
consume?
  - Is injection prevented by parametrizing queries?
  - Have you ensured no `eval` or similar functions are used?
- Does this PR introduce any functionality or component that requires
authorization?
- How have you ensured it respects the existing AuthN/AuthZ mechanisms?
  - Are you logging failed auth attempts?
- Are you using or adding any cryptographic features?
  - Do you use a standard proven implementations?
  - Are the used keys controlled by the customer? Where are they stored?
- Are you introducing any new policies/roles/users?
  - Have you used the least-privilege principle? How?


By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
  • Loading branch information
dlpzx committed Jun 25, 2024
1 parent cc927d0 commit 194770c
Show file tree
Hide file tree
Showing 10 changed files with 896 additions and 707 deletions.
165 changes: 13 additions & 152 deletions frontend/src/modules/Dashboards/views/DashboardImportForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@ import {
CardContent,
CardHeader,
Chip,
CircularProgress,
Container,
FormHelperText,
Grid,
Link,
MenuItem,
TextField,
Typography
} from '@mui/material';
Expand All @@ -31,45 +29,18 @@ import {
useSettings
} from 'design';
import { SET_ERROR, useDispatch } from 'globalErrors';
import {
listEnvironmentGroups,
listValidEnvironments,
searchGlossary,
useClient
} from 'services';
import { searchGlossary, useClient } from 'services';
import { importDashboard } from '../services';
import { EnvironmentTeamDropdown } from 'modules/Shared';

const DashboardImportForm = (props) => {
const navigate = useNavigate();
const { enqueueSnackbar } = useSnackbar();
const dispatch = useDispatch();
const client = useClient();
const { settings } = useSettings();
const [loading, setLoading] = useState(true);
const [groupOptions, setGroupOptions] = useState([]);
const [environmentOptions, setEnvironmentOptions] = useState([]);
const [selectableTerms, setSelectableTerms] = useState([]);

const fetchEnvironments = useCallback(async () => {
setLoading(true);
const response = await client.query(
listValidEnvironments({
filter: Defaults.selectListFilter
})
);
if (!response.errors) {
setEnvironmentOptions(
response.data.listValidEnvironments.nodes.map((e) => ({
...e,
value: e.environmentUri,
label: e.label
}))
);
} else {
dispatch({ type: SET_ERROR, error: response.errors[0].message });
}
setLoading(false);
}, [client, dispatch]);
const fetchTerms = useCallback(async () => {
const response = await client.query(
searchGlossary(Defaults.selectListFilter)
Expand All @@ -95,37 +66,11 @@ const DashboardImportForm = (props) => {
}, [client, dispatch]);
useEffect(() => {
if (client) {
fetchEnvironments().catch((e) =>
dispatch({ type: SET_ERROR, error: e.message })
);
fetchTerms().catch((e) =>
dispatch({ type: SET_ERROR, error: e.message })
);
}
}, [client, fetchTerms, fetchEnvironments, dispatch]);

const fetchGroups = async (environmentUri) => {
try {
const response = await client.query(
listEnvironmentGroups({
filter: Defaults.selectListFilter,
environmentUri
})
);
if (!response.errors) {
setGroupOptions(
response.data.listEnvironmentGroups.nodes.map((g) => ({
value: g.groupUri,
label: g.groupUri
}))
);
} else {
dispatch({ type: SET_ERROR, error: response.errors[0].message });
}
} catch (e) {
dispatch({ type: SET_ERROR, error: e.message });
}
};
}, [client, fetchTerms, dispatch]);

async function submit(values, setStatus, setSubmitting, setErrors) {
try {
Expand All @@ -136,7 +81,7 @@ const DashboardImportForm = (props) => {
dashboardId: values.dashboardId,
environmentUri: values.environment.environmentUri,
description: values.description,
SamlGroupName: values.SamlGroupName,
SamlGroupName: values.SamlAdminGroupName,
tags: values.tags,
terms: values.terms.nodes
? values.terms.nodes.map((t) => t.nodeUri)
Expand Down Expand Up @@ -168,9 +113,6 @@ const DashboardImportForm = (props) => {
dispatch({ type: SET_ERROR, error: err.message });
}
}
if (loading) {
return <CircularProgress />;
}

return (
<>
Expand Down Expand Up @@ -239,7 +181,7 @@ const DashboardImportForm = (props) => {
label: '',
dashboardId: '',
description: '',
SamlGroupName: '',
SamlAdminGroupName: '',
environment: '',
tags: [],
terms: []
Expand All @@ -252,7 +194,7 @@ const DashboardImportForm = (props) => {
.max(255)
.required('*QuickSight dashboard identifier is required'),
description: Yup.string().max(5000),
SamlGroupName: Yup.string()
SamlAdminGroupName: Yup.string()
.max(255)
.required('*Team is required'),
environment: Yup.object().required('*Environment is required'),
Expand Down Expand Up @@ -392,94 +334,13 @@ const DashboardImportForm = (props) => {
</Card>
</Grid>
<Grid item lg={5} md={6} xs={12}>
<Card sx={{ mb: 3 }}>
<CardHeader title="Deployment" />
<CardContent>
<TextField
fullWidth
error={Boolean(
touched.environment && errors.environment
)}
helperText={
touched.environment && errors.environment
}
label="Environment"
name="environment"
onChange={(event) => {
setFieldValue('SamlGroupName', '');
fetchGroups(
event.target.value.environmentUri
).catch((e) =>
dispatch({ type: SET_ERROR, error: e.message })
);
setFieldValue('environment', event.target.value);
}}
select
value={values.environment}
variant="outlined"
>
{environmentOptions.map((environment) => (
<MenuItem
key={environment.environmentUri}
value={environment}
>
{environment.label}
</MenuItem>
))}
</TextField>
</CardContent>
<CardContent>
<TextField
disabled
fullWidth
label="Region"
name="region"
value={
values.environment
? values.environment.region
: ''
}
variant="outlined"
/>
</CardContent>
<CardContent>
<TextField
disabled
fullWidth
label="Organization"
name="organization"
value={
values.environment
? values.environment.organization.label
: ''
}
variant="outlined"
/>
</CardContent>
<CardContent>
<TextField
fullWidth
error={Boolean(
touched.SamlGroupName && errors.SamlGroupName
)}
helperText={
touched.SamlGroupName && errors.SamlGroupName
}
label="Team"
name="SamlGroupName"
onChange={handleChange}
select
value={values.SamlGroupName}
variant="outlined"
>
{groupOptions.map((group) => (
<MenuItem key={group.value} value={group.value}>
{group.label}
</MenuItem>
))}
</TextField>
</CardContent>
</Card>
<EnvironmentTeamDropdown
setFieldValue={setFieldValue}
handleChange={handleChange}
values={values}
touched={touched}
errors={errors}
/>
{errors.submit && (
<Box sx={{ mt: 3 }}>
<FormHelperText error>{errors.submit}</FormHelperText>
Expand Down
Loading

0 comments on commit 194770c

Please sign in to comment.