2020-07-03 00:02:49 +02:00
|
|
|
import React from 'react';
|
2020-07-02 12:13:51 +02:00
|
|
|
|
|
|
|
import { makeStyles } from '@material-ui/core/styles';
|
|
|
|
import Paper from '@material-ui/core/Paper';
|
|
|
|
import Typography from '@material-ui/core/Typography';
|
|
|
|
import TextField from '@material-ui/core/TextField';
|
2020-07-08 00:30:17 +02:00
|
|
|
import MenuItem from '@material-ui/core/MenuItem';
|
2020-07-02 12:13:51 +02:00
|
|
|
import Container from '@material-ui/core/Container';
|
2020-07-08 00:30:17 +02:00
|
|
|
import Grid from '@material-ui/core/Grid';
|
2020-07-02 23:55:56 +02:00
|
|
|
import Pagination from '@material-ui/lab/Pagination';
|
2020-07-02 12:13:51 +02:00
|
|
|
|
|
|
|
import { useSearch } from './utils/useSearch';
|
2020-07-02 23:55:56 +02:00
|
|
|
import {
|
|
|
|
useLocationSearch,
|
|
|
|
useLocationSearchState
|
|
|
|
} from './utils/routing';
|
2020-07-02 12:13:51 +02:00
|
|
|
import { SearchHit } from './SearchHit';
|
|
|
|
|
|
|
|
|
|
|
|
const classes = makeStyles((theme) => ({
|
|
|
|
root: {
|
|
|
|
display: 'flex',
|
|
|
|
flexWrap: 'wrap',
|
|
|
|
},
|
|
|
|
textField: {
|
|
|
|
marginLeft: theme.spacing(1),
|
|
|
|
marginRight: theme.spacing(1),
|
|
|
|
width: '25ch',
|
2020-07-08 00:30:17 +02:00
|
|
|
}
|
2020-07-02 12:13:51 +02:00
|
|
|
}));
|
|
|
|
|
|
|
|
export const SearchPage = () => {
|
2020-07-02 23:55:56 +02:00
|
|
|
const pageSize = 20;
|
|
|
|
const [query, setQuery] = useLocationSearchState('query', '');
|
2020-07-08 00:30:17 +02:00
|
|
|
|
2020-07-08 00:35:03 +02:00
|
|
|
const [ruleType, setRuleType] = useLocationSearchState('types', 'ALL');
|
2020-07-08 00:30:17 +02:00
|
|
|
const allRuleTypes = {'BUG': 'Bug', 'CODE_SMELL': 'Code Smell', 'SECURITY_HOTSPOT': 'Security Hotspot', 'VULNERABILITY': 'Vulnerability'};
|
|
|
|
|
|
|
|
const [ruleTags, setRuleTags] = useLocationSearchState('tags', [], value => value ? value.split(',') : []);
|
|
|
|
const allRuleTags = ["confusing", 'pitfall', 'clumsy', 'junit', 'tests']; // TODO: generate this list
|
|
|
|
|
2020-07-02 23:55:56 +02:00
|
|
|
const [pageNumber, setPageNumber] = useLocationSearchState('page', 1, parseInt);
|
2020-07-03 00:02:49 +02:00
|
|
|
const [, setLocationSearch] = useLocationSearch();
|
2020-07-02 23:55:56 +02:00
|
|
|
|
2020-07-02 12:13:51 +02:00
|
|
|
|
2020-07-08 00:30:17 +02:00
|
|
|
const [results, numberOfHits, error, resultsAreLoading] = useSearch(query,
|
|
|
|
ruleType === "ALL" ? null : ruleType,
|
|
|
|
ruleTags,
|
|
|
|
pageSize, pageNumber);
|
2020-07-02 23:55:56 +02:00
|
|
|
const totalPages = Math.ceil(numberOfHits/pageSize);
|
2020-07-02 12:13:51 +02:00
|
|
|
|
|
|
|
let resultsDisplay="No rule found...";
|
|
|
|
if (resultsAreLoading) {
|
|
|
|
resultsDisplay = "Searching";
|
|
|
|
}
|
|
|
|
else if (results.length > 0) {
|
|
|
|
resultsDisplay = results.map(result => <SearchHit key={result.id} data={result}/>)
|
|
|
|
}
|
2020-07-02 23:55:56 +02:00
|
|
|
|
2020-07-09 10:44:35 +02:00
|
|
|
const paramSetters = {types: setRuleType, tags: setRuleTags, query: setQuery};
|
|
|
|
function handleUpdate(field) {
|
|
|
|
return function(event) {
|
|
|
|
if (pageNumber > 1) {
|
|
|
|
const uriSearch = {query: query, types: ruleType, tags: ruleTags, page: 1};
|
|
|
|
uriSearch[field] = event.target.value;
|
|
|
|
setLocationSearch(uriSearch);
|
|
|
|
} else {
|
|
|
|
paramSetters[field](event.target.value, {push: false});
|
|
|
|
}
|
2020-07-02 23:55:56 +02:00
|
|
|
}
|
|
|
|
}
|
2020-07-08 00:30:17 +02:00
|
|
|
|
2020-07-02 12:13:51 +02:00
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<Paper className={classes.languagesBar}>
|
|
|
|
<Container maxWidth="md">
|
2020-07-08 00:30:17 +02:00
|
|
|
<Grid container spacing={3}>
|
|
|
|
<Grid item xs={12}>
|
|
|
|
<Typography variant="h4" className={classes.searchBar}>Search Rule Specifications</Typography>
|
|
|
|
</Grid>
|
|
|
|
<Grid item xs={12}>
|
|
|
|
<TextField
|
|
|
|
id="title-query"
|
|
|
|
label="Rule Title and Description"
|
|
|
|
placeholder="Search in rule titles and descriptions"
|
|
|
|
fullWidth
|
|
|
|
margin="normal"
|
|
|
|
InputLabelProps={{
|
|
|
|
shrink: true,
|
|
|
|
}}
|
|
|
|
variant="outlined"
|
|
|
|
value={query}
|
2020-07-09 10:44:35 +02:00
|
|
|
onChange={handleUpdate("query")}
|
2020-07-08 00:30:17 +02:00
|
|
|
error={error}
|
|
|
|
helperText={error}
|
|
|
|
/>
|
|
|
|
</Grid>
|
|
|
|
<Grid item xs={3}>
|
|
|
|
<TextField
|
|
|
|
select
|
2020-07-02 12:13:51 +02:00
|
|
|
fullWidth
|
|
|
|
margin="normal"
|
2020-07-08 00:30:17 +02:00
|
|
|
variant="outlined"
|
|
|
|
label="Rule types"
|
|
|
|
value={ruleType}
|
2020-07-09 10:44:35 +02:00
|
|
|
onChange={handleUpdate("types")}
|
2020-07-08 00:30:17 +02:00
|
|
|
>
|
|
|
|
<MenuItem key="All" value="ALL">
|
|
|
|
All
|
|
|
|
</MenuItem>
|
|
|
|
{Object.keys(allRuleTypes).map((ruleType) => (
|
|
|
|
<MenuItem key={ruleType} value={ruleType}>
|
|
|
|
{allRuleTypes[ruleType]}
|
|
|
|
</MenuItem>
|
|
|
|
))}
|
|
|
|
</TextField>
|
|
|
|
</Grid>
|
|
|
|
<Grid item xs={9}>
|
|
|
|
<TextField
|
|
|
|
select
|
|
|
|
fullWidth
|
|
|
|
SelectProps={{
|
|
|
|
multiple: true,
|
2020-07-02 12:13:51 +02:00
|
|
|
}}
|
2020-07-08 00:30:17 +02:00
|
|
|
margin="normal"
|
2020-07-02 12:13:51 +02:00
|
|
|
variant="outlined"
|
2020-07-08 00:30:17 +02:00
|
|
|
label="Rule Tags"
|
|
|
|
value={ruleTags}
|
2020-07-09 10:44:35 +02:00
|
|
|
onChange={handleUpdate("tags")}
|
2020-07-08 00:30:17 +02:00
|
|
|
renderValue={(selected) => {
|
|
|
|
return selected.join(', ');
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
{allRuleTags.map((ruleType) => (
|
|
|
|
<MenuItem key={ruleType} value={ruleType}>
|
|
|
|
{ruleType}
|
|
|
|
</MenuItem>
|
|
|
|
))}
|
|
|
|
</TextField>
|
|
|
|
</Grid>
|
|
|
|
</Grid>
|
2020-07-02 12:13:51 +02:00
|
|
|
</Container>
|
|
|
|
</Paper>
|
2020-07-08 00:30:17 +02:00
|
|
|
<Container maxWidth="md" className={classes.searchHitsContainer}>
|
|
|
|
<Grid container spacing={3}>
|
|
|
|
<Grid item xs={12}>
|
|
|
|
<Typography variant="h5" className={classes.searchBar}>Number of rules found: {numberOfHits}</Typography>
|
|
|
|
<ul>
|
|
|
|
{resultsDisplay}
|
|
|
|
</ul>
|
|
|
|
<Pagination count={totalPages} page={pageNumber} siblingCount={2}
|
|
|
|
onChange={(event, value) => setPageNumber(value)}
|
|
|
|
/>
|
|
|
|
</Grid>
|
|
|
|
</Grid>
|
|
|
|
</Container>
|
|
|
|
</div>
|
2020-07-02 12:13:51 +02:00
|
|
|
)
|
|
|
|
}
|