Skip to content

Commit f846a3f

Browse files
committed
add filter for response header
1 parent 2e76420 commit f846a3f

File tree

1 file changed

+168
-48
lines changed

1 file changed

+168
-48
lines changed

webui/src/components/dashboard/http/Requests.vue

Lines changed: 168 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,19 @@ const { services, close: closeServices } = fetchServices('http', true);
3535
const dialogRef = useTemplateRef('dialogRef')
3636
let dialog: Modal | undefined;
3737
const urlValue = useTemplateRef<ComponentPublicInstance<typeof RegexInput>>('urlValue');
38-
const headerValueRefs = ref<any[]>([])
38+
const requestHeaderValueRefs = ref<any[]>([])
39+
const responseHeaderValueRefs = ref<any[]>([])
3940
type CheckboxFilter = 'Not' | 'Single' | 'Multi'
4041
interface Filter {
4142
service: MultiFilterItem
4243
method: MultiFilterItem & { custom?: string}
4344
url: FilterItem
44-
headers: FilterItem
45+
request: {
46+
headers: FilterItem
47+
}
48+
response: {
49+
headers: FilterItem
50+
}
4551
}
4652
interface FilterItem {
4753
checkbox: boolean
@@ -56,15 +62,25 @@ const filter = reactive<Filter>({
5662
service: { state: 'Not', checkbox: false, value: [] },
5763
method: { state: 'Not', checkbox: false, value: ['GET']},
5864
url: { checkbox: false, value: null},
59-
headers: { checkbox: false, value: [{ name: '', value: '' }]}
65+
request: {
66+
headers: { checkbox: false, value: [{ name: '', value: '' }]}
67+
},
68+
response: {
69+
headers: { checkbox: false, value: [{ name: '', value: '' }]}
70+
}
6071
})
6172
6273
onMounted(() => {
6374
dialog = Modal.getOrCreateInstance(dialogRef.value!);
6475
const s = localStorage.getItem(`http-requests-${getFilterCacheKey()}`)
6576
if (s && s !== '') {
6677
const saved = JSON.parse(s)
67-
Object.assign(filter, saved)
78+
type Key = keyof Filter
79+
for (const key in filter) {
80+
if (saved[key] !== undefined) {
81+
filter[key as Key] = saved[key]
82+
}
83+
}
6884
}
6985
})
7086
@@ -128,10 +144,22 @@ const events = computed<ServiceEvent[]>(() => {
128144
})
129145
}
130146
131-
if (filter.headers.checkbox && headerFilter.value?.length > 0) {
147+
if (filter.request.headers.checkbox && requestHeaderFilter.value?.length > 0) {
148+
result = result.filter(x => {
149+
const data = x.data as HttpEventData
150+
for (const filter of requestHeaderFilter.value) {
151+
if (!filter(data)) {
152+
return false
153+
}
154+
}
155+
return true
156+
})
157+
}
158+
159+
if (filter.response.headers.checkbox && responseHeaderFilter.value?.length > 0) {
132160
result = result.filter(x => {
133161
const data = x.data as HttpEventData
134-
for (const filter of headerFilter.value) {
162+
for (const filter of responseHeaderFilter.value) {
135163
if (!filter(data)) {
136164
return false
137165
}
@@ -240,7 +268,10 @@ const activeFiltersCount = computed(() => {
240268
if (filter.url.checkbox && filter.url.value) {
241269
counter++;
242270
}
243-
if (filter.headers.checkbox && filter.headers.value.length > 1) {
271+
if (filter.request.headers.checkbox && filter.request.headers.value.length > 1) {
272+
counter++;
273+
}
274+
if (filter.response.headers.checkbox && filter.response.headers.value.length > 1) {
244275
counter++;
245276
}
246277
return counter;
@@ -254,73 +285,84 @@ function getFilterCacheKey() {
254285
}
255286
return `filter-${props.serviceName}-${props.path}-${props.method}`
256287
}
257-
function onHeaderInput(index: number) {
258-
const last = filter.headers.value[filter.headers.value.length - 1]
288+
function onHeaderInput(headers: FilterItem, index: number) {
289+
const last = headers.value[headers.value.length - 1]
259290
260-
// If currently editing the last row and it has a name → add new empty row
261-
if (index === filter.headers.value.length - 1 && last.name.trim() !== '') {
262-
filter.headers.value.push({ name: '', value: '' })
263-
}
291+
// If currently editing the last row and it has a name → add new empty row
292+
if (index === headers.value?.length - 1 && (last.name?.trim() !== '' || last.value?.trim())) {
293+
headers.value.push({ name: '', value: '' })
294+
}
264295
265-
// Optional: remove rows where both fields are empty (except last)
266-
for (let i = 0; i < filter.headers.value.length - 1; i++) {
267-
const hf = filter.headers.value[i]
268-
if (!hf.name?.trim() && !hf.value?.trim()) {
269-
filter.headers.value.splice(i, 1)
270-
break
296+
for (let i = 0; i < headers.value.length - 1; i++) {
297+
const hf = headers.value[i]
298+
if (!hf.name?.trim() && !hf.value?.trim()) {
299+
headers.value.splice(i, 1)
300+
break
301+
}
271302
}
272-
}
273303
}
274-
function removeHeaderFilter(index: number) {
275-
filter.headers.value.splice(index, 1)
304+
function removeHeaderFilter(headers: FilterItem, index: number) {
305+
headers.value.splice(index, 1)
276306
277307
// Ensure at least one empty placeholder row
278-
const last = filter.headers.value[filter.headers.value.length - 1]
308+
const last = headers.value[headers.value.length - 1]
279309
if (last && last.name) {
280-
filter.headers.value.push({ name: '', value: '' })
310+
headers.value.push({ name: '', value: '' })
281311
}
282312
}
283-
const headerErrors = computed(() => {
313+
const requestHeaderErrors = computed(() => {
284314
const result = []
285-
for (const ref of headerValueRefs.value) {
286-
if (ref.regexError) {
315+
for (const ref of requestHeaderValueRefs.value) {
316+
if (ref && ref.regexError) {
317+
result.push(ref.regexError)
318+
}
319+
}
320+
return result
321+
})
322+
const responseHeaderErrors = computed(() => {
323+
const result: string[] = []
324+
if (!filter.response.headers.checkbox) {
325+
return result
326+
}
327+
for (const ref of responseHeaderValueRefs.value) {
328+
if (ref && ref.regexError) {
287329
result.push(ref.regexError)
288330
}
289331
}
290332
return result
291333
})
292-
const headerFilter = computed(() => {
334+
const requestHeaderFilter = computed(() => {
293335
const result: ((data: HttpEventData) => boolean)[] = []
294-
if (!headerValueRefs.value) {
336+
if (!requestHeaderValueRefs.value) {
295337
return result;
296338
}
297339
298-
for (let i = 0; i < filter.headers.value.length - 1; i++) {
299-
if (headerValueRefs.value[i]?.regexError) {
340+
for (let i = 0; i < filter.request.headers.value.length - 1; i++) {
341+
if (requestHeaderValueRefs.value[i]?.regexError) {
300342
continue
301343
}
302-
const fh = filter.headers.value[i];
344+
const fh = filter.request.headers.value[i];
303345
result.push((data: HttpEventData) => {
304346
const params = data.request.parameters
305347
if (!params) {
306348
return false
307349
}
308350
351+
const name = fh.name?.toLowerCase();
309352
for (const param of params) {
310353
if (param.type !== 'header') {
311354
continue
312355
}
313-
const name = fh.name?.toLowerCase();
314356
if (!name) {
315-
const regex = headerValueRefs.value[i].regex
357+
const regex = requestHeaderValueRefs.value[i].regex
316358
if (regex && regex.test(param.raw)) {
317359
return true
318360
}
319361
} else if (param.name.toLowerCase() === name) {
320362
if (!fh.value) {
321363
return true
322364
} else {
323-
const regex = headerValueRefs.value[i].regex
365+
const regex = requestHeaderValueRefs.value[i].regex
324366
if (regex && regex.test(param.raw)) {
325367
return true
326368
}
@@ -332,6 +374,47 @@ const headerFilter = computed(() => {
332374
}
333375
return result
334376
})
377+
const responseHeaderFilter = computed(() => {
378+
const result: ((data: HttpEventData) => boolean)[] = []
379+
if (!responseHeaderValueRefs.value) {
380+
return result;
381+
}
382+
383+
for (let i = 0; i < filter.response.headers.value.length - 1; i++) {
384+
if (responseHeaderValueRefs.value[i]?.regexError) {
385+
continue
386+
}
387+
const fh = filter.response.headers.value[i];
388+
result.push((data: HttpEventData) => {
389+
const headers = data.response.headers
390+
if (!headers) {
391+
return false
392+
}
393+
394+
const name = fh.name?.toLowerCase();
395+
for (const fieldName in headers) {
396+
397+
if (!name) {
398+
const regex = responseHeaderValueRefs.value[i].regex
399+
if (regex && regex.test(headers[name])) {
400+
return true
401+
}
402+
} else if (fieldName.toLowerCase() === name) {
403+
if (!fh.value) {
404+
return true
405+
} else {
406+
const regex = responseHeaderValueRefs.value[i].regex
407+
if (regex && regex.test(headers[fieldName])) {
408+
return true
409+
}
410+
}
411+
}
412+
}
413+
return false
414+
})
415+
}
416+
return result
417+
})
335418
</script>
336419

337420
<template>
@@ -460,39 +543,76 @@ const headerFilter = computed(() => {
460543
</div>
461544
</div>
462545

463-
<!-- Headers -->
464-
<div class="row" :class="{ 'mb-3': headerErrors.length == 0 }">
546+
<!-- Request Headers -->
547+
<div class="row" :class="{ 'mb-3': requestHeaderErrors.length == 0 }">
548+
<div class="col-4">
549+
<div class="form-check">
550+
<input class="form-check-input" type="checkbox" v-model="filter.request.headers.checkbox" id="request-headers">
551+
<label class="form-check-label" for="request-headers">Request Header</label>
552+
</div>
553+
</div>
554+
<div class="col" v-if="filter.request.headers.checkbox">
555+
<div v-for="(hf, i) in filter.request.headers.value">
556+
<div class="row me-0":class="{ 'mb-2': i < filter.request.headers.value.length - 1 }" >
557+
<div class="col ps-0 pe-1">
558+
<input type="text" class="form-control form-control-sm" id="reqeuest-header-name" v-model="hf.name" placeholder="Name" @input="onHeaderInput(filter.request.headers, i)">
559+
</div>
560+
<div class="col ps-1 pe-0">
561+
<RegexInput v-model="hf.value" :ref="el => requestHeaderValueRefs[i] = el" placeholder="Value [Regex]" @input="onHeaderInput(filter.request.headers, i)" />
562+
</div>
563+
<div class="col-1">
564+
<button v-if="i < filter.request.headers.value.length -1"
565+
class="btn btn-outline-danger btn-sm"
566+
@click="removeHeaderFilter(filter.request.headers, i)">
567+
<i class="bi bi-x"></i>
568+
</button>
569+
</div>
570+
</div>
571+
</div>
572+
</div>
573+
</div>
574+
<div class="row mb-3" v-if="requestHeaderErrors.length > 0">
575+
<div class="invalid-feedback" style="display: inline;">
576+
Invalid regular expression:
577+
<ul>
578+
<li v-for="err in requestHeaderErrors">{{ err }}</li>
579+
</ul>
580+
</div>
581+
</div>
582+
583+
<!-- Response Headers -->
584+
<div class="row" :class="{ 'mb-3': responseHeaderErrors.length == 0 }">
465585
<div class="col-4">
466586
<div class="form-check">
467-
<input class="form-check-input" type="checkbox" v-model="filter.headers.checkbox" id="headers">
468-
<label class="form-check-label" for="url">Request Header</label>
587+
<input class="form-check-input" type="checkbox" v-model="filter.response.headers.checkbox" id="response-headers">
588+
<label class="form-check-label" for="response-headers">Response Header</label>
469589
</div>
470590
</div>
471-
<div class="col" v-if="filter.headers.checkbox">
472-
<div v-for="(hf, i) in filter.headers.value">
473-
<div class="row me-0":class="{ 'mb-2': i < filter.headers.value.length - 1 }" >
591+
<div class="col" v-if="filter.response.headers.checkbox">
592+
<div v-for="(hf, i) in filter.response.headers.value">
593+
<div class="row me-0":class="{ 'mb-2': i < filter.response.headers.value.length - 1 }" >
474594
<div class="col ps-0 pe-1">
475-
<input type="text" class="form-control form-control-sm" id="header-name" v-model="hf.name" placeholder="Name" @input="onHeaderInput(i)">
595+
<input type="text" class="form-control form-control-sm" id="response-header-name" v-model="hf.name" placeholder="Name" @input="onHeaderInput(filter.response.headers, i)">
476596
</div>
477597
<div class="col ps-1 pe-0">
478-
<RegexInput v-model="hf.value" :ref="el => headerValueRefs[i] = el" placeholder="Value [Regex]" @input="onHeaderInput(i)" />
598+
<RegexInput v-model="hf.value" :ref="el => responseHeaderValueRefs[i] = el" placeholder="Value [Regex]" @input="onHeaderInput(filter.response.headers, i)" />
479599
</div>
480600
<div class="col-1">
481-
<button v-if="i < filter.headers.value.length -1"
601+
<button v-if="i < filter.response.headers.value.length -1"
482602
class="btn btn-outline-danger btn-sm"
483-
@click="removeHeaderFilter(i)">
603+
@click="removeHeaderFilter(filter.response.headers, i)">
484604
<i class="bi bi-x"></i>
485605
</button>
486606
</div>
487607
</div>
488608
</div>
489609
</div>
490610
</div>
491-
<div class="row mb-3" v-if="headerErrors.length > 0">
611+
<div class="row mb-3" v-if="responseHeaderErrors.length > 0">
492612
<div class="invalid-feedback" style="display: inline;">
493613
Invalid regular expression:
494614
<ul>
495-
<li v-for="err in headerErrors">{{ err }}</li>
615+
<li v-for="err in responseHeaderErrors">{{ err }}</li>
496616
</ul>
497617
</div>
498618
</div>

0 commit comments

Comments
 (0)