diff --git a/lib/content-services/src/lib/search/services/base-query-builder.service.ts b/lib/content-services/src/lib/search/services/base-query-builder.service.ts index 6d838d13891..e127de44f64 100644 --- a/lib/content-services/src/lib/search/services/base-query-builder.service.ts +++ b/lib/content-services/src/lib/search/services/base-query-builder.service.ts @@ -73,20 +73,44 @@ export abstract class BaseQueryBuilderService { /* Stream that emits the initial value for some or all search filters */ populateFilters = new BehaviorSubject<{ [key: string]: any }>({}); + /* Stream that emits every time queryFragments change */ + queryFragmentsUpdate = new BehaviorSubject<{ [key: string]: any }>({}); + + /* Stream that emits every time userFacetBuckets change */ + userFacetBucketsUpdate = new BehaviorSubject<{ [key: string]: FacetFieldBucket[] }>({}); + categories: SearchCategory[] = []; - queryFragments: { [id: string]: string } = {}; filterQueries: FilterQuery[] = []; filterRawParams: { [key: string]: any } = {}; paging: { maxItems?: number; skipCount?: number } = null; sorting: SearchSortingDefinition[] = []; sortingOptions: SearchSortingDefinition[] = []; + private encodedQuery: string; private scope: RequestScope; private selectedConfiguration: number; private _userQuery = ''; + private _queryFragments: { [id: string]: string } = {}; + + private readonly queryFragmentsHandler: ProxyHandler<{ [key: string]: any }> = { + set: (target: { [key: string]: any }, property: string, value: any) => { + target[property as keyof typeof target] = value; + this.queryFragmentsUpdate.next(this._queryFragments); + return true; + } + }; protected userFacetBuckets: { [key: string]: FacetFieldBucket[] } = {}; + get queryFragments(): { [key: string]: any } { + return this._queryFragments; + } + + set queryFragments(value: { [key: string]: any }) { + this._queryFragments = this.createQueryFragmentsProxy(value); + this.queryFragmentsUpdate.next(this._queryFragments); + } + get userQuery(): string { return this._userQuery; } @@ -108,6 +132,7 @@ export abstract class BaseQueryBuilderService { protected readonly alfrescoApiService: AlfrescoApiService ) { this.resetToDefaults(); + this._queryFragments = this.createQueryFragmentsProxy({}); } public abstract loadConfiguration(): SearchConfiguration | SearchConfiguration[]; @@ -146,10 +171,10 @@ export abstract class BaseQueryBuilderService { const currentConfig = this.loadConfiguration(); if (Array.isArray(currentConfig) && currentConfig[index] !== undefined) { this.selectedConfiguration = index; - this.configUpdated.next(currentConfig[index]); this.searchForms.next(this.getSearchFormDetails()); this.resetSearchOptions(); this.setUpSearchConfiguration(currentConfig[index]); + this.configUpdated.next(currentConfig[index]); this.update(); } } @@ -216,6 +241,7 @@ export abstract class BaseQueryBuilderService { buckets.push(bucket); } this.userFacetBuckets[field] = buckets; + this.userFacetBucketsUpdate.next(this.userFacetBuckets); } } @@ -239,6 +265,7 @@ export abstract class BaseQueryBuilderService { if (field && bucket) { const buckets = this.userFacetBuckets[field] || []; this.userFacetBuckets[field] = buckets.filter((facetBucket) => facetBucket.label !== bucket.label); + this.userFacetBucketsUpdate.next(this.userFacetBuckets); } } @@ -615,4 +642,8 @@ export abstract class BaseQueryBuilderService { queryParamsHandling: 'merge' }); } + + private createQueryFragmentsProxy(target: { [key: string]: any }): { [key: string]: any } { + return new Proxy(target, this.queryFragmentsHandler); + } } diff --git a/lib/content-services/src/lib/search/services/search-query-builder.service.spec.ts b/lib/content-services/src/lib/search/services/search-query-builder.service.spec.ts index bb3c7d6c6cb..b720c762165 100644 --- a/lib/content-services/src/lib/search/services/search-query-builder.service.spec.ts +++ b/lib/content-services/src/lib/search/services/search-query-builder.service.spec.ts @@ -24,6 +24,7 @@ import { TestBed } from '@angular/core/testing'; import { ContentTestingModule } from '../../testing/content.testing.module'; import { ADF_SEARCH_CONFIGURATION } from '../search-configuration.token'; import { ActivatedRoute, Router } from '@angular/router'; +import { skip } from 'rxjs/operators'; const buildConfig = (searchSettings = {}): AppConfigService => { let config: AppConfigService; @@ -840,4 +841,67 @@ describe('SearchQueryBuilder', () => { }); }); }); + + describe('userFacetBucketsUpdate', () => { + it('should emit updated list of UserFacetBuckets on adding the bucket', (done) => { + const service = TestBed.inject(SearchQueryBuilderService); + + service.userFacetBucketsUpdate.pipe(skip(1)).subscribe((buckets) => { + expect(buckets).toEqual({ test: [{ checked: true, filterQuery: 'f1-q1', label: 'f1-q1', count: 1 }] }); + done(); + }); + + const currentBuckets = service.getUserFacetBuckets('test'); + expect(currentBuckets).toEqual([]); + service.addUserFacetBucket('test', { checked: true, filterQuery: 'f1-q1', label: 'f1-q1', count: 1 }); + }); + + it('should emit updated list of UserFacetBuckets on removing the bucket', (done) => { + const service = TestBed.inject(SearchQueryBuilderService); + service.addUserFacetBucket('test', { checked: true, filterQuery: 'f1-q1', label: 'toStay', count: 1 }); + service.addUserFacetBucket('test', { checked: true, filterQuery: 'f1-q1', label: 'toLeave', count: 1 }); + + service.userFacetBucketsUpdate.pipe(skip(1)).subscribe((buckets) => { + expect(buckets).toEqual({ test: [{ checked: true, filterQuery: 'f1-q1', label: 'toStay', count: 1 }] }); + done(); + }); + + const currentBuckets = service.getUserFacetBuckets('test'); + expect(currentBuckets).toEqual([ + { checked: true, filterQuery: 'f1-q1', label: 'toStay', count: 1 }, + { checked: true, filterQuery: 'f1-q1', label: 'toLeave', count: 1 } + ]); + service.removeUserFacetBucket('test', { checked: true, filterQuery: 'f1-q1', label: 'toLeave', count: 1 }); + }); + }); + + describe('queryFragments proxy set up', () => { + it('should emit queryFragmentsUpdate when proxy property is set', (done) => { + const service = TestBed.inject(SearchQueryBuilderService); + + service.queryFragmentsUpdate.pipe(skip(1)).subscribe((fragments) => { + expect(fragments).toEqual({ test: 'test_fragment' }); + done(); + }); + + const currentFragments = service.queryFragments; + expect(currentFragments).toEqual({}); + service.queryFragments['test'] = 'test_fragment'; + }); + + it('should emit queryFragmentsUpdate when setter replaces the proxy', (done) => { + const service = TestBed.inject(SearchQueryBuilderService); + + service.queryFragments['test'] = 'test_fragment'; + const currentFragments = service.queryFragments; + + expect(currentFragments).toEqual({ test: 'test_fragment' }); + + service.queryFragmentsUpdate.pipe(skip(1)).subscribe((fragments) => { + expect(fragments).toEqual({}); + done(); + }); + service.queryFragments = {}; + }); + }); });