Skip to content

Commit faf0cb0

Browse files
authored
Version 3.5.2: Add (secret) filter on filename (#49)
1 parent 483dfc5 commit faf0cb0

File tree

3 files changed

+99
-35
lines changed

3 files changed

+99
-35
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "resc-frontend",
3-
"version": "3.5.1",
3+
"version": "3.5.2",
44
"author": "ABN AMRO Bank",
55
"description": "Repository Scanner Frontend",
66
"license": "MIT",

src/components/Findings/FindingsTable.vue

+91-33
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
11
<template>
2-
<div class="ml-3">
3-
<!-- Button to audit multiple findings -->
4-
<!-- Audit Modal -->
5-
<AuditModal
6-
ref="auditModal"
7-
:selectedCheckBoxIds="selectedCheckBoxIds"
8-
@update-audit="updateAudit"
9-
/>
10-
<!-- Column Modal -->
11-
<ColumnSelector ref="columnModal" @update-columns="setTableFields" />
12-
</div>
2+
<AuditModal
3+
ref="auditModal"
4+
:selectedCheckBoxIds="selectedCheckBoxIds"
5+
@update-audit="updateAudit"
6+
/>
7+
<ColumnSelector ref="columnModal" @update-columns="setTableFields" />
138

149
<div class="py-3">
1510
<BTable
1611
ref="auditTable"
1712
id="rule-analysis-table"
18-
:items="findingList"
13+
:items="filteredList"
1914
:fields="fields as TableField[]"
2015
:current-page="1"
2116
:per-page="0"
@@ -29,23 +24,36 @@
2924
:caption-top="true"
3025
>
3126
<template #table-caption>
32-
<BButton
33-
class="d-inline-block me-3"
34-
variant="primary"
35-
size="sm"
36-
id="AuditButton"
37-
v-on:click="showAuditModal()"
38-
:disabled="auditButtonDisabled"
39-
>AUDIT</BButton
40-
>
41-
<BButton
42-
class="d-inline-block"
43-
variant="primary"
44-
size="sm"
45-
id="AuditButton"
46-
v-on:click="showColumnSelect()"
47-
>Columns</BButton
48-
>
27+
<div class="row me-0 mr-0">
28+
<div class="col-md-4">
29+
<BButton
30+
class="d-inline-block me-3"
31+
variant="primary"
32+
size="sm"
33+
id="AuditButton"
34+
v-on:click="showAuditModal()"
35+
:disabled="auditButtonDisabled"
36+
>AUDIT</BButton
37+
>
38+
<BButton
39+
class="d-inline-block"
40+
variant="primary"
41+
size="sm"
42+
id="AuditButton"
43+
v-on:click="showColumnSelect()"
44+
>Columns</BButton
45+
>
46+
</div>
47+
<div class="col-md-4">
48+
<BFormInput
49+
class="hover-opacity"
50+
id="filter-files"
51+
placeholder="filename"
52+
v-model="filterString"
53+
:required="true"
54+
/>
55+
</div>
56+
</div>
4957
</template>
5058

5159
<!-- Select all checkboxes -->
@@ -129,6 +137,7 @@ import {
129137
BButton,
130138
type TableField,
131139
type TableItem,
140+
BFormInput,
132141
} from 'bootstrap-vue-next';
133142
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
134143
import FindingsService from '@/services/findings-service';
@@ -153,6 +162,7 @@ const props = defineProps<Props>();
153162
const auditModal = ref();
154163
const columnModal = ref();
155164
const auditTable = ref();
165+
const filterString = ref('');
156166
157167
const findingList = ref(props.findings as TableItemDetailedFindingRead[]);
158168
const selectedCheckBoxIds = ref([] as number[]);
@@ -164,6 +174,36 @@ const selectedIndex = ref(undefined as number | undefined);
164174
const store = useAuthUserStore();
165175
const emit = defineEmits(['refresh-table']);
166176
177+
// Simple filter function
178+
// if start with * we check the ending of the file path.
179+
// if end with * we check the beginning of the file path.
180+
// if does not contain * we only check if the needle is in the string.
181+
function applyFilter() {
182+
if (filterString.value === '') {
183+
return findingList.value;
184+
}
185+
186+
const token = filterString.value;
187+
188+
if (token.startsWith('*')) {
189+
return findingList.value.filter((finding) => {
190+
return finding.file_path.endsWith(token.substring(1));
191+
});
192+
}
193+
194+
if (token.endsWith('*')) {
195+
return findingList.value.filter((finding) => {
196+
return finding.file_path.startsWith(token.substring(0, token.length - 1));
197+
});
198+
}
199+
200+
return findingList.value.filter((finding) => {
201+
return finding.file_path.includes(token);
202+
});
203+
}
204+
205+
const filteredList = computed(applyFilter);
206+
167207
function setTableFields(selectedColumns: TableColumn[] = []) {
168208
// @ts-ignore ignore TS2589
169209
fields.value = ColumnUtils.getColumns(selectedColumns, store.tableColumns, props.is_rule_finding);
@@ -178,7 +218,7 @@ function selectSingleCheckbox() {
178218
function selectAllCheckboxes() {
179219
selectedCheckBoxIds.value = [];
180220
if (allSelected.value) {
181-
for (const finding of findingList.value) {
221+
for (const finding of filteredList.value) {
182222
selectedCheckBoxIds.value.push(finding.id_);
183223
}
184224
}
@@ -188,7 +228,7 @@ function toggleAllCheckboxes() {
188228
selectedCheckBoxIds.value = [];
189229
allSelected.value = !allSelected.value;
190230
if (allSelected.value) {
191-
for (const finding of findingList.value) {
231+
for (const finding of filteredList.value) {
192232
selectedCheckBoxIds.value.push(finding.id_);
193233
}
194234
}
@@ -243,14 +283,14 @@ function getCurrentFindingSelected(): TableItemDetailedFindingRead | undefined {
243283
return undefined;
244284
}
245285
246-
return findingList.value[selectedIndex.value];
286+
return filteredList.value[selectedIndex.value];
247287
}
248288
249289
function selectDown(): boolean {
250290
const detailsStatus = getCurrentFindingSelected()?._showDetails;
251291
closeAllDetails();
252292
253-
selectedIndex.value = ((selectedIndex.value ?? -1) + 1) % props.findings.length;
293+
selectedIndex.value = ((selectedIndex.value ?? -1) + 1) % filteredList.value.length;
254294
auditTable.value.clearSelected();
255295
auditTable.value.selectRow(selectedIndex.value);
256296
@@ -444,3 +484,21 @@ watch(
444484
},
445485
);
446486
</script>
487+
<style lang="scss">
488+
.hover-opacity {
489+
opacity: 0;
490+
transition: all 0.2s ease-in-out !important;
491+
492+
&:hover {
493+
opacity: 1;
494+
}
495+
496+
&:focus {
497+
opacity: 1;
498+
}
499+
}
500+
501+
input.hover-opacity:valid {
502+
opacity: 1;
503+
}
504+
</style>

tests/unit/components/Findings/FindingsTable.spec.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ importFA();
1616
vi.mock('axios');
1717

1818
describe('FindingsTable tests', () => {
19-
it('Given a FindingsTable in rule findings then FindingsTable will be displayed', () => {
19+
it('Given a FindingsTable in rule findings then FindingsTable will be displayed', async () => {
2020
axios.get.mockResolvedValueOnce({ data: rule_packs });
2121
axios.get.mockResolvedValueOnce({ data: allProjects });
2222
axios.get.mockResolvedValueOnce({ data: allRepos });
@@ -95,6 +95,12 @@ describe('FindingsTable tests', () => {
9595
expect(() => wrapper.vm.markAllAsGone()).not.toThrow();
9696
expect(() => wrapper.vm.auditThis()).not.toThrow();
9797

98+
expect(() => wrapper.find('#filter-files').setValue('file1')).not.toThrow();
99+
await wrapper.vm.$nextTick();
100+
expect(() => wrapper.find('#filter-files').setValue('fi*')).not.toThrow();
101+
await wrapper.vm.$nextTick();
102+
expect(() => wrapper.find('#filter-files').setValue('*e1')).not.toThrow();
103+
98104
axios.get.mockResolvedValueOnce({ data: detailed_findings });
99105
expect(() => wrapper.vm.updateAudit('NOT_ANALYZED', 'Rien a declarer')).not.toThrow();
100106
});

0 commit comments

Comments
 (0)