diff --git a/gravitee-apim-console-webui/src/components/logs/logs-filters.controller.spec.ts b/gravitee-apim-console-webui/src/components/logs/logs-filters.controller.spec.ts index 019cfa5ae31..c631efc1232 100644 --- a/gravitee-apim-console-webui/src/components/logs/logs-filters.controller.spec.ts +++ b/gravitee-apim-console-webui/src/components/logs/logs-filters.controller.spec.ts @@ -111,8 +111,8 @@ describe('LogsFiltersController', () => { expect(controller.filters.api).toEqual(['api-id']); expect(controller.filters.method).toEqual(['GET']); - // status uses push(v) where v is an array, so it becomes [[...]] - expect(controller.filters.status).toEqual([['200']]); + // status now correctly pushes string values, not nested arrays + expect(controller.filters.status).toEqual(['200']); }); it('should set display mode based on query filters', () => { @@ -320,15 +320,13 @@ describe('LogsFiltersController', () => { }); it('should decode multiple api filters with OR', () => { - // Note: The decodeQueryFilters processes filters sequentially and overwrites - // So with "api:api-1 OR api:api-2", it processes api:api-1 first (sets to ['api-1']), - // then processes api:api-2 (sets to ['api-2']), overwriting the first - // This is actually a limitation of the current implementation + // flatMap splits OR conditions, so each filter is processed separately + // Both values should be accumulated in the array const query = 'api:api-1 OR api:api-2'; controller['decodeQueryFilters'](query); - // Only the last one will remain due to overwriting behavior - expect(controller.filters.api).toEqual(['api-2']); + // Both values should be accumulated + expect(controller.filters.api).toEqual(['api-1', 'api-2']); }); it('should decode application filter', () => { @@ -384,8 +382,8 @@ describe('LogsFiltersController', () => { controller.filters.status = []; controller['decodeQueryFilters'](query); - // status uses push(v) where v is an array, so it becomes [[...]] - expect(controller.filters.status).toEqual([['200']]); + // status now correctly pushes string values, not nested arrays + expect(controller.filters.status).toEqual(['200']); }); it('should decode response-time filter', () => { diff --git a/gravitee-apim-console-webui/src/components/logs/logs-filters.controller.ts b/gravitee-apim-console-webui/src/components/logs/logs-filters.controller.ts index 83043b164ca..948ffd798e4 100644 --- a/gravitee-apim-console-webui/src/components/logs/logs-filters.controller.ts +++ b/gravitee-apim-console-webui/src/components/logs/logs-filters.controller.ts @@ -239,19 +239,16 @@ class LogsFiltersController { for (let i = 0; i < filters.length; i++) { const filter = filters[i].replace(/[()]/g, ''); const k = filter.split(':')[0].trim(); - const v = filter - .substring(filter.indexOf(':') + 1) - .split('OR') - .map((x) => x.trim()); + const v = filter.substring(filter.indexOf(':') + 1).trim(); switch (k) { case 'api': - this.filters.api = v; + this.filters.api.push(v); break; case 'application': - this.filters.application = v; + this.filters.application.push(v); break; case 'path': { - const value = v[0].replace(/\\"/g, ''); + const value = v.replace(/\\"/g, ''); if (this.api) { this.filters.uri = this.api.proxy.virtual_hosts[0].path + value; } else { @@ -260,46 +257,74 @@ class LogsFiltersController { break; } case 'uri': - this.filters.uri = v[0].replace(/\*|\\\\/g, ''); + this.filters.uri = v.replace(/\*|\\\\/g, ''); break; case 'plan': - this.filters.plan = v; + // Initialize as array if not already, then push the value + if (!Array.isArray(this.filters.plan)) { + this.filters.plan = []; + } + this.filters.plan.push(v); break; case 'response-time': - this.filters.responseTime = v; + // Initialize as array if not already, then push the value + if (!Array.isArray(this.filters.responseTime)) { + this.filters.responseTime = []; + } + this.filters.responseTime.push(v); break; case 'method': - this.filters.method = v; + // Initialize as array if not already, then push the value + if (!Array.isArray(this.filters.method)) { + this.filters.method = []; + } + this.filters.method.push(v); break; case 'status': this.filters.status.push(v); break; case '_id': - this.filters.id = v[0]; + this.filters.id = v; break; case 'transaction': - this.filters.transaction = v[0]; + this.filters.transaction = v; break; case 'tenant': - this.filters.tenant = v; + // Initialize as array if not already, then push the value + if (!Array.isArray(this.filters.tenant)) { + this.filters.tenant = []; + } + this.filters.tenant.push(v); break; case '_exists_': - this.filters._exists_ = v; + // Initialize as array if not already, then push the value + if (!Array.isArray(this.filters._exists_)) { + this.filters._exists_ = []; + } + this.filters._exists_.push(v); break; case '!_exists_': - this.filters['!_exists_'] = v; + // Initialize as array if not already, then push the value + if (!Array.isArray(this.filters['!_exists_'])) { + this.filters['!_exists_'] = []; + } + this.filters['!_exists_'].push(v); break; case 'body': - this.filters.body = v[0].replace(/^\*(.*)\*$/g, '$1'); + this.filters.body = v.replace(/^\*(.*)\*$/g, '$1'); break; case 'endpoint': - this.filters.endpoint = v[0].replace(/\*|\\\\/g, ''); + this.filters.endpoint = v.replace(/\*|\\\\/g, ''); break; case 'remote-address': - this.filters['remote-address'] = v; + // Initialize as array if not already, then push the value + if (!Array.isArray(this.filters['remote-address'])) { + this.filters['remote-address'] = []; + } + this.filters['remote-address'].push(v); break; case 'host': - this.filters.host = v[0].replace(/\\"/g, ''); + this.filters.host = v.replace(/\\"/g, ''); break; default: this.$log.error('unknown filter: ', k); diff --git a/gravitee-apim-console-webui/src/components/logs/search-and-select/search-and-select.controller.ts b/gravitee-apim-console-webui/src/components/logs/search-and-select/search-and-select.controller.ts index 1ed7bf86bd6..2eabf70c79c 100644 --- a/gravitee-apim-console-webui/src/components/logs/search-and-select/search-and-select.controller.ts +++ b/gravitee-apim-console-webui/src/components/logs/search-and-select/search-and-select.controller.ts @@ -59,6 +59,12 @@ export class SearchAndSelectController { } async $onInit() { + // Normalize selectModel if it has nested arrays (fix for decodeQueryFilters bug) + // Check if any nested arrays exist before flattening for better performance + if (this.selectModel && Array.isArray(this.selectModel) && this.selectModel.some(Array.isArray)) { + this.selectModel = this.flattenArray(this.selectModel); + } + if (this.init) { const options = await this.init(); this.selector.updateOptions(options); @@ -66,13 +72,36 @@ export class SearchAndSelectController { this.select(); } + /** + * Recursively flattens nested arrays + * Example: [[1], [2]] -> [1, 2] + */ + private flattenArray(arr: any[]): string[] { + const result: string[] = []; + for (const item of arr) { + if (Array.isArray(item)) { + result.push(...this.flattenArray(item)); + } else if (item !== null && item !== undefined) { + result.push(item); + } + } + return result; + } + async onSearch() { const options = await this.search({ term: this.searchTerm }); this.selector.updateOptions(options); } select() { - this.selector.updateSelection(this.selectModel, () => this.onSearch()); + // Normalize selectModel before using it (defensive check) + // Only flatten if nested arrays are detected for better performance + let normalizedModel = this.selectModel || []; + if (Array.isArray(normalizedModel) && normalizedModel.some(Array.isArray)) { + normalizedModel = this.flattenArray(normalizedModel); + } + + this.selector.updateSelection(normalizedModel, () => this.onSearch()); this.onSelect({ selection: this.selection }); } diff --git a/gravitee-apim-console-webui/src/components/logs/search-and-select/search-and-select.html b/gravitee-apim-console-webui/src/components/logs/search-and-select/search-and-select.html index 40db10b8ce5..54dcec13f6b 100644 --- a/gravitee-apim-console-webui/src/components/logs/search-and-select/search-and-select.html +++ b/gravitee-apim-console-webui/src/components/logs/search-and-select/search-and-select.html @@ -21,7 +21,7 @@ data-md-container-class="selectdemoSelectHeader" md-on-open="$ctrl.onSearch()" multiple - ng-change="$ctrl.select(item)" + ng-change="$ctrl.select()" >