From c83d92c53900a4000fbbe8d495bc9c0dc48d2f3a Mon Sep 17 00:00:00 2001 From: Denys Vuika Date: Thu, 31 Aug 2023 14:36:21 +0100 Subject: [PATCH] [ACS-5839] migrate from QueryBody to SearchResult type (#8861) * [ci:force] migrate from QueryBody to SearchQuery * [ci:force] migrate from QueryBody to SearchQuery * [ci:force] migrate from QueryBody to SearchQuery * [ci:force] update docs and variables as per code review --- .../search/search-config-test.service.ts | 12 +- .../breaking-change-2.6.0-3.0.0.md | 2 +- .../components/search.component.md | 3 +- .../services/search-query-builder.service.md | 16 +- .../search-configuration.interface.md | 26 +-- .../services/search-configuration.service.md | 11 +- docs/core/services/search.service.md | 6 +- .../search-configuration.interface.ts | 8 +- ...de-selector-panel.component-search.spec.ts | 44 ++--- .../content-node-selector-panel.component.ts | 163 +++++++----------- .../src/lib/mock/search-query.mock.ts | 6 +- .../src/lib/mock/search.component.mock.ts | 72 ++++---- .../search-config-permission.service.ts | 21 +-- .../services/node-permission.service.ts | 106 ++++++------ .../services/base-query-builder.service.ts | 117 +++++++------ .../services/search-configuration.service.ts | 17 +- .../search-query-builder.service.spec.ts | 153 +++++----------- .../src/lib/search/services/search.service.ts | 37 ++-- 18 files changed, 340 insertions(+), 480 deletions(-) diff --git a/demo-shell/src/app/components/search/search-config-test.service.ts b/demo-shell/src/app/components/search/search-config-test.service.ts index 3b6e97fb057..9d0c3664be2 100644 --- a/demo-shell/src/app/components/search/search-config-test.service.ts +++ b/demo-shell/src/app/components/search/search-config-test.service.ts @@ -15,15 +15,14 @@ * limitations under the License. */ -import { QueryBody } from '@alfresco/js-api'; +import { SearchRequest } from '@alfresco/js-api'; import { SearchConfigurationInterface } from '@alfresco/adf-content-services'; import { Injectable } from '@angular/core'; @Injectable() export class TestSearchConfigurationService implements SearchConfigurationInterface { - - public generateQueryBody(searchTerm: string, maxResults: number, skipCount: number): QueryBody { - const defaultQueryBody: QueryBody = { + public generateQueryBody(searchTerm: string, maxResults: number, skipCount: number): SearchRequest { + return { query: { query: searchTerm ? `${searchTerm}* OR name:${searchTerm}*` : searchTerm }, @@ -35,9 +34,8 @@ export class TestSearchConfigurationService implements SearchConfigurationInterf filterQueries: [ /* eslint-disable-next-line */ { query: "TYPE:'cm:folder'" }, - { query: 'NOT cm:creator:System' }] + { query: 'NOT cm:creator:System' } + ] }; - - return defaultQueryBody; } } diff --git a/docs/breaking-changes/breaking-change-2.6.0-3.0.0.md b/docs/breaking-changes/breaking-change-2.6.0-3.0.0.md index 12894dec37e..1099f466dbe 100644 --- a/docs/breaking-changes/breaking-change-2.6.0-3.0.0.md +++ b/docs/breaking-changes/breaking-change-2.6.0-3.0.0.md @@ -30,7 +30,7 @@ This document lists all the deprecated ADF v2.x components that were removed for This modification has enabled us to remove some code duplication between the two modules. - [PR ADF-1873](https://github.com/Alfresco/alfresco-ng2-components/pull/4145): - - `adf-search-control`: The `QueryBody`, and + - `adf-search-control`: The `SearchRequest`, and `customQueryBody` inputs of the [`SearchControlComponent`](../content-services/components/search-control.component.md) have been removed in favor of the [custom search configuration interface](../core/interfaces/search-configuration.interface.md). The inputs were deprecated in v2.1.0. diff --git a/docs/content-services/components/search.component.md b/docs/content-services/components/search.component.md index 40eeb978b9f..f6fa7cc0a41 100644 --- a/docs/content-services/components/search.component.md +++ b/docs/content-services/components/search.component.md @@ -151,8 +151,7 @@ By doing this, you can get the results as the user types into the input text. ### Custom search configuration You can get finer control over the parameters of a search by defining them in a custom -[QueryBody](https://github.com/Alfresco/alfresco-js-api/blob/1.6.0/src/alfresco-search-rest-api/docs/QueryBody.md) -object. The recommended way to do this is with a custom implementation of the +**SearchRequest** object. The recommended way to do this is with a custom implementation of the [Search Configuration interface](../../core/interfaces/search-configuration.interface.md). The ADF source provides a standard implementation of this interface, [`SearchConfigurationService`](../../core/services/search-configuration.service.md) that you can use as a base to adapt to your needs. See the [Search Configuration interface](../../core/interfaces/search-configuration.interface.md) page for full details of how to diff --git a/docs/content-services/services/search-query-builder.service.md b/docs/content-services/services/search-query-builder.service.md index 923db7f0371..67d0e719eca 100644 --- a/docs/content-services/services/search-query-builder.service.md +++ b/docs/content-services/services/search-query-builder.service.md @@ -20,12 +20,12 @@ Stores information from all the custom search and faceted search widgets, compil Adds a facet bucket to a field. - _field:_ [`FacetField`](../../../lib/content-services/src/lib/search/models/facet-field.interface.ts) - The target field - _bucket:_ [`FacetFieldBucket`](../../../lib/content-services/src/lib/search/models/facet-field-bucket.interface.ts) - Bucket to add -- **buildQuery**(): `QueryBody`
+- **buildQuery**(): `SearchRequest`
Builds the current query. - - **Returns** `QueryBody` - The finished query -- **execute**(queryBody?: `QueryBody`)
+ - **Returns** `SearchRequest` - The finished query +- **execute**(queryBody?: `SearchRequest`)
Builds and executes the current query. - - _queryBody:_ `QueryBody` - (Optional) + - _queryBody:_ `SearchRequest` - (Optional) - **getDefaultConfiguration**(): [`SearchConfiguration`](../../../lib/content-services/src/lib/search/models/search-configuration.interface.ts)`|undefined`
- **Returns** [`SearchConfiguration`](../../../lib/content-services/src/lib/search/models/search-configuration.interface.ts)`|undefined` - @@ -81,18 +81,18 @@ Stores information from all the custom search and faceted search widgets, compil - _bucket:_ [`FacetFieldBucket`](../../../lib/content-services/src/lib/search/models/facet-field-bucket.interface.ts) - Bucket to remove - **resetToDefaults**()
-- **search**(queryBody: `QueryBody`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`ResultSetPaging`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/search-rest-api/docs/ResultSetPaging.md)`>`
+- **search**(queryBody: `SearchRequest`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`ResultSetPaging`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/search-rest-api/docs/ResultSetPaging.md)`>`
- - _queryBody:_ `QueryBody` - + - _queryBody:_ `SearchRequest` - - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`ResultSetPaging`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/search-rest-api/docs/ResultSetPaging.md)`>` - - **setScope**(scope: `RequestScope`)
- _scope:_ `RequestScope` - -- **update**(queryBody?: `QueryBody`)
+- **update**(queryBody?: `SearchRequest`)
Builds the current query and triggers the `updated` event. - - _queryBody:_ `QueryBody` - (Optional) + - _queryBody:_ `SearchRequest` - (Optional) - **updateSelectedConfiguration**(index: `number`)
- _index:_ `number` - diff --git a/docs/core/interfaces/search-configuration.interface.md b/docs/core/interfaces/search-configuration.interface.md index 621e02cef9c..e51945511e9 100644 --- a/docs/core/interfaces/search-configuration.interface.md +++ b/docs/core/interfaces/search-configuration.interface.md @@ -10,14 +10,13 @@ Provides fine control of parameters to a search. ## Methods -`generateQueryBody(searchTerm: string, maxResults: string, skipCount: string): QueryBody`
-Generates a QueryBody object with custom search parameters. +`generateQueryBody(searchTerm: string, maxResults: string, skipCount: string): SearchRequest`
+Generates a request object with custom search parameters. ## Details The interface defines a service that generates a custom -[QueryBody](https://github.com/Alfresco/alfresco-js-api/blob/1.6.0/src/alfresco-search-rest-api/docs/QueryBody.md) -object. This object can then be supplied to a search operation to refine the search parameters. +**SearchRequest** object. This object can then be supplied to a search operation to refine the search parameters. A standard implementation, the [Search Configuration service](../services/search-configuration.service.md) is provided in the ADF Core library @@ -29,37 +28,30 @@ described below. 1. Implement the service class Create your own service class to implement the [`SearchConfigurationInterface`](../../core/interfaces/search-configuration.interface.md). This defines the - the `generateQueryBody` method that returns the QueryBody object. See the - [QueryBody](https://github.com/Alfresco/alfresco-js-api/blob/1.6.0/src/alfresco-search-rest-api/docs/QueryBody.md) - page in the Alfresco JS API for further details about the options this object provides. + `generateQueryBody` method that returns the query object. An example implementation is given below: ```ts - import { QueryBody } from '@alfresco/js-api'; + import { SearchRequest } from '@alfresco/js-api'; import { SearchConfigurationInterface } from '@alfresco/adf-core'; export class TestSearchConfigurationService implements SearchConfigurationInterface { - constructor() { - } - - public generateQueryBody(searchTerm: string, maxResults: string, skipCount: string): QueryBody { - const defaultQueryBody: QueryBody = { + generateQueryBody(searchTerm: string, maxItems: string, skipCount: string): SearchRequest { + return { query: { query: searchTerm ? `${searchTerm}* OR name:${searchTerm}*` : searchTerm }, include: ['path', 'allowableOperations'], paging: { - maxItems: maxResults, - skipCount: skipCount + maxItems, + skipCount }, filterQueries: [ { query: "TYPE:'cm:folder'" }, { query: 'NOT cm:creator:System' }] }; - - return defaultQueryBody; } } ``` diff --git a/docs/core/services/search-configuration.service.md b/docs/core/services/search-configuration.service.md index db7e9469f8a..770d5b7899f 100644 --- a/docs/core/services/search-configuration.service.md +++ b/docs/core/services/search-configuration.service.md @@ -13,18 +13,17 @@ Provides fine control of parameters to a search. ### Methods -- **generateQueryBody**(searchTerm: `string`, maxResults: `number`, skipCount: `number`): `QueryBody`
- Generates a QueryBody object with custom search parameters. +- **generateQueryBody**(searchTerm: `string`, maxResults: `number`, skipCount: `number`): `SearchRequest`
+ Generates a request object with custom search parameters. - _searchTerm:_ `string` - Term text to search for - _maxResults:_ `number` - Maximum number of search results to show in a page - _skipCount:_ `number` - The offset of the start of the page within the results list - - **Returns** `QueryBody` - Query body defined by the parameters + - **Returns** `SearchRequest` - Query body defined by the parameters ## Details -The `generateQueryBody` method returns a -[QueryBody](https://github.com/Alfresco/alfresco-js-api/blob/1.6.0/src/alfresco-search-rest-api/docs/QueryBody.md) -object. This configures the search to use `searchTerm` along with `maxResults` and `skipCount` +The `generateQueryBody` method returns a **SearchRequest** object. +This configures the search to use `searchTerm` along with `maxResults` and `skipCount` specified for the paging of the search results. This service is a standard implementation of the diff --git a/docs/core/services/search.service.md b/docs/core/services/search.service.md index a4c0dd9de43..9a5c3401422 100644 --- a/docs/core/services/search.service.md +++ b/docs/core/services/search.service.md @@ -24,9 +24,9 @@ Accesses the Content Services Search API. - _maxResults:_ `number` - Maximum number of items in the list of results - _skipCount:_ `number` - Number of higher-ranked items to skip over in the list - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`ResultSetPaging`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/search-rest-api/docs/ResultSetPaging.md)`>` - List of search results -- **searchByQueryBody**(queryBody: `QueryBody`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`ResultSetPaging`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/search-rest-api/docs/ResultSetPaging.md)`>`
- Performs a search with its parameters supplied by a QueryBody object. - - _queryBody:_ `QueryBody` - Object containing the search parameters +- **searchByQueryBody**(queryBody: `SearchRequest`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`ResultSetPaging`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/search-rest-api/docs/ResultSetPaging.md)`>`
+ Performs a search with its parameters supplied by a SearchRequest object. + - _queryBody:_ `SearchRequest` - Object containing the search parameters - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`ResultSetPaging`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/search-rest-api/docs/ResultSetPaging.md)`>` - List of search results ## Details diff --git a/lib/content-services/src/lib/common/interfaces/search-configuration.interface.ts b/lib/content-services/src/lib/common/interfaces/search-configuration.interface.ts index 728e51a74ab..389dd533725 100644 --- a/lib/content-services/src/lib/common/interfaces/search-configuration.interface.ts +++ b/lib/content-services/src/lib/common/interfaces/search-configuration.interface.ts @@ -15,18 +15,16 @@ * limitations under the License. */ -import { QueryBody } from '@alfresco/js-api'; +import { SearchRequest } from '@alfresco/js-api'; export interface SearchConfigurationInterface { - /** - * Generates a QueryBody object with custom search parameters. + * Generates a query object with custom search parameters. * * @param searchTerm Term text to search for * @param maxResults Maximum number of search results to show in a page * @param skipCount The offset of the start of the page within the results list * @returns Query body defined by the parameters */ - generateQueryBody(searchTerm: string, maxResults: number, skipCount: number): QueryBody; - + generateQueryBody(searchTerm: string, maxResults: number, skipCount: number): SearchRequest; } diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component-search.spec.ts b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component-search.spec.ts index 4f1d78c4372..d4f46a848c1 100644 --- a/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component-search.spec.ts +++ b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component-search.spec.ts @@ -28,7 +28,7 @@ import { CustomResourcesService } from '../document-list/services/custom-resourc import { NodeEntryEvent, ShareDataRow } from '../document-list'; import { TranslateModule } from '@ngx-translate/core'; import { SearchQueryBuilderService } from '../search'; -import { mockQueryBody } from '../mock/search-query.mock'; +import { mockSearchRequest } from '../mock/search-query.mock'; import { SitesService } from '../common/services/sites.service'; import { NodesApiService } from '../common/services/nodes-api.service'; @@ -168,16 +168,16 @@ describe('ContentNodeSelectorPanelComponent', () => { expect(component.searchTerm).toEqual('search-term'); })); - it('should perform a search when the queryBody gets updated and it is defined', fakeAsync(() => { + it('should perform a search when the search request gets updated and it is defined', fakeAsync(() => { typeToSearchBox('search-term'); tick(debounceSearch); fixture.detectChanges(); - expect(searchSpy).toHaveBeenCalledWith(mockQueryBody); + expect(searchSpy).toHaveBeenCalledWith(mockSearchRequest); })); - it('should NOT perform a search and clear the results when the queryBody gets updated and it is NOT defined', async () => { + it('should NOT perform a search and clear the results when the search request gets updated and it is NOT defined', async () => { spyOn(component, 'clearSearch'); searchQueryBuilderService.userQuery = ''; @@ -212,22 +212,22 @@ describe('ContentNodeSelectorPanelComponent', () => { tick(debounceSearch); fixture.detectChanges(); - expect(searchSpy).toHaveBeenCalledWith(mockQueryBody); + expect(searchSpy).toHaveBeenCalledWith(mockSearchRequest); })); it('should the query include the show files filterQuery', fakeAsync(() => { component.showFilesInResult = true; typeToSearchBox('search-term'); - const expectedQueryBody = mockQueryBody; - expectedQueryBody.filterQueries.push({ + const expectedRequest = mockSearchRequest; + expectedRequest.filterQueries.push({ query: `TYPE:'cm:folder' OR TYPE:'cm:content'` }); tick(debounceSearch); fixture.detectChanges(); - expect(searchSpy).toHaveBeenCalledWith(expectedQueryBody); + expect(searchSpy).toHaveBeenCalledWith(expectedRequest); })); it('should reset the currently chosen node in case of starting a new search', fakeAsync(() => { @@ -257,11 +257,11 @@ describe('ContentNodeSelectorPanelComponent', () => { component.siteChanged({ entry: { guid: 'namek' } } as SiteEntry); - const expectedQueryBody = mockQueryBody; - expectedQueryBody.filterQueries = [{ query: `ANCESTOR:'workspace://SpacesStore/namek'` }]; + const expectedRequest = mockSearchRequest; + expectedRequest.filterQueries = [{ query: `ANCESTOR:'workspace://SpacesStore/namek'` }]; expect(searchSpy.calls.count()).toBe(2); - expect(searchSpy).toHaveBeenCalledWith(expectedQueryBody); + expect(searchSpy).toHaveBeenCalledWith(expectedRequest); })); it('should create the query with the right parameters on changing the site selectBox value from a custom dropdown menu', fakeAsync(() => { @@ -277,8 +277,8 @@ describe('ContentNodeSelectorPanelComponent', () => { component.siteChanged({ entry: { guid: '-sites-' } } as SiteEntry); - const expectedQueryBodyWithSiteChange = mockQueryBody; - expectedQueryBodyWithSiteChange.filterQueries = [ + const expectedRequest = mockSearchRequest; + expectedRequest.filterQueries = [ { query: `ANCESTOR:'workspace://SpacesStore/-sites-' OR ANCESTOR:'workspace://SpacesStore/123456testId' OR ANCESTOR:'workspace://SpacesStore/09876543testId'` } @@ -286,8 +286,8 @@ describe('ContentNodeSelectorPanelComponent', () => { expect(searchSpy).toHaveBeenCalled(); expect(searchSpy.calls.count()).toBe(2); - expect(searchSpy).toHaveBeenCalledWith(mockQueryBody); - expect(searchSpy).toHaveBeenCalledWith(expectedQueryBodyWithSiteChange); + expect(searchSpy).toHaveBeenCalledWith(mockSearchRequest); + expect(searchSpy).toHaveBeenCalledWith(expectedRequest); })); it('should get the corresponding node ids on search when a known alias is selected from dropdown', fakeAsync(() => { @@ -404,10 +404,10 @@ describe('ContentNodeSelectorPanelComponent', () => { typeToSearchBox('search-term'); tick(debounceSearch); - const expectedQueryBody = mockQueryBody; - expectedQueryBody.filterQueries = [{ query: `ANCESTOR:'workspace://SpacesStore/my-root-id'` }]; + const expectedRequest = mockSearchRequest; + expectedRequest.filterQueries = [{ query: `ANCESTOR:'workspace://SpacesStore/my-root-id'` }]; - expect(searchSpy).toHaveBeenCalledWith(expectedQueryBody); + expect(searchSpy).toHaveBeenCalledWith(expectedRequest); })); it('should emit showingSearch event with true while searching', async () => { @@ -476,10 +476,10 @@ describe('ContentNodeSelectorPanelComponent', () => { component.restrictRootToCurrentFolderId = true; component.siteChanged({ entry: { guid: 'my-site-id' } } as SiteEntry); - const expectedQueryBodyWithSiteChange = mockQueryBody; - expectedQueryBodyWithSiteChange.filterQueries = [{ query: `ANCESTOR:'workspace://SpacesStore/my-site-id'` }]; + const expectedRequest = mockSearchRequest; + expectedRequest.filterQueries = [{ query: `ANCESTOR:'workspace://SpacesStore/my-site-id'` }]; - expect(searchSpy).toHaveBeenCalledWith(expectedQueryBodyWithSiteChange); + expect(searchSpy).toHaveBeenCalledWith(expectedRequest); }); it('should restrict the breadcrumb to the currentFolderId in case restrictedRoot is true', async () => { @@ -735,7 +735,7 @@ describe('ContentNodeSelectorPanelComponent', () => { }); it('should set its loading state to true to perform a new search', async () => { - component.prepareDialogForNewSearch(mockQueryBody); + component.prepareDialogForNewSearch(mockSearchRequest); fixture.detectChanges(); await fixture.whenStable(); diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.ts b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.ts index 14575eee0cb..97a76fd6977 100644 --- a/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.ts +++ b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.ts @@ -15,22 +15,13 @@ * limitations under the License. */ -import { - Component, - EventEmitter, - Input, - OnInit, - Output, - ViewChild, - ViewEncapsulation, - OnDestroy, - Inject -} from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation, OnDestroy, Inject } from '@angular/core'; import { HighlightDirective, UserPreferencesService, UserPreferenceValues, - InfinitePaginationComponent, PaginatedComponent, + InfinitePaginationComponent, + PaginatedComponent, AppConfigService, DataSorting, ShowHeaderMode @@ -39,7 +30,7 @@ import { NodesApiService } from '../common/services/nodes-api.service'; import { UploadService } from '../common/services/upload.service'; import { FileUploadCompleteEvent, FileUploadDeleteEvent } from '../common/events/file.event'; import { UntypedFormControl } from '@angular/forms'; -import { Node, NodePaging, Pagination, SiteEntry, SitePaging, NodeEntry, QueryBody, RequestScope } from '@alfresco/js-api'; +import { Node, NodePaging, Pagination, SiteEntry, SitePaging, NodeEntry, SearchRequest, RequestScope } from '@alfresco/js-api'; import { DocumentListComponent } from '../document-list/components/document-list.component'; import { RowFilter } from '../document-list/data/row-filter.model'; import { ImageResolver } from '../document-list/data/image-resolver.model'; @@ -63,13 +54,14 @@ export const defaultValidation = () => true; styleUrls: ['./content-node-selector-panel.component.scss'], encapsulation: ViewEncapsulation.None, host: { class: 'adf-content-node-selector-panel' }, - providers: [{ - provide: SEARCH_QUERY_SERVICE_TOKEN, - useClass: SearchQueryBuilderService - }] + providers: [ + { + provide: SEARCH_QUERY_SERVICE_TOKEN, + useClass: SearchQueryBuilderService + } + ] }) export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { - // eslint-disable-next-line @typescript-eslint/naming-convention DEFAULT_PAGINATION: Pagination = new Pagination({ maxItems: 25, @@ -276,15 +268,16 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { private onDestroy$ = new Subject(); - constructor(private customResourcesService: CustomResourcesService, - @Inject(SEARCH_QUERY_SERVICE_TOKEN) public queryBuilderService: SearchQueryBuilderService, - private userPreferencesService: UserPreferencesService, - private nodesApiService: NodesApiService, - private uploadService: UploadService, - private sitesService: SitesService, - private appConfigService: AppConfigService, - private contentNodeSelectorPanelService: ContentNodeSelectorPanelService) { - } + constructor( + private customResourcesService: CustomResourcesService, + @Inject(SEARCH_QUERY_SERVICE_TOKEN) public queryBuilderService: SearchQueryBuilderService, + private userPreferencesService: UserPreferencesService, + private nodesApiService: NodesApiService, + private uploadService: UploadService, + private sitesService: SitesService, + private appConfigService: AppConfigService, + private contentNodeSelectorPanelService: ContentNodeSelectorPanelService + ) {} set chosenNode(value: Node[]) { this._chosenNode = value; @@ -304,43 +297,34 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { } ngOnInit() { - this.searchInput.valueChanges - .pipe( - debounceTime(this.debounceSearch), - takeUntil(this.onDestroy$) - ) - .subscribe((searchValue: string) => { - this.searchTerm = searchValue; - this.queryBuilderService.userQuery = searchValue.length > 0 ? `${searchValue}*` : searchValue ; - this.queryBuilderService.update(); - }); + this.searchInput.valueChanges.pipe(debounceTime(this.debounceSearch), takeUntil(this.onDestroy$)).subscribe((searchValue: string) => { + this.searchTerm = searchValue; + this.queryBuilderService.userQuery = searchValue.length > 0 ? `${searchValue}*` : searchValue; + this.queryBuilderService.update(); + }); - this.queryBuilderService.updated - .pipe(takeUntil(this.onDestroy$)) - .subscribe((queryBody: QueryBody) => { - if (queryBody) { - this.hasValidQuery = true; - this.prepareDialogForNewSearch(queryBody); - this.queryBuilderService.execute(queryBody); - } else { - this.hasValidQuery = false; - this.resetFolderToShow(); - this.clearSearch(); - } - }); + this.queryBuilderService.updated.pipe(takeUntil(this.onDestroy$)).subscribe((searchRequest) => { + if (searchRequest) { + this.hasValidQuery = true; + this.prepareDialogForNewSearch(searchRequest); + this.queryBuilderService.execute(searchRequest); + } else { + this.hasValidQuery = false; + this.resetFolderToShow(); + this.clearSearch(); + } + }); - this.queryBuilderService.executed - .pipe(takeUntil(this.onDestroy$)) - .subscribe((results: NodePaging) => { - if (this.hasValidQuery) { - this.showSearchResults(results); - } - }); + this.queryBuilderService.executed.pipe(takeUntil(this.onDestroy$)).subscribe((results: NodePaging) => { + if (this.hasValidQuery) { + this.showSearchResults(results); + } + }); this.userPreferencesService .select(UserPreferenceValues.PaginationSize) .pipe(takeUntil(this.onDestroy$)) - .subscribe(pagSize => this.pageSize = pagSize); + .subscribe((pagSize) => (this.pageSize = pagSize)); this.target = this.documentList; this.folderIdToShow = this.currentFolderId; @@ -360,11 +344,9 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { this.resetPagination(); this.setSearchScopeToNodes(); - this.documentList.$folderNode - .pipe(takeUntil(this.onDestroy$)) - .subscribe((currentNode: Node) => { + this.documentList.$folderNode.pipe(takeUntil(this.onDestroy$)).subscribe((currentNode: Node) => { this.currentFolder.emit(currentNode); - }); + }); this.sorting = this.appConfigService.get('adf-content-node-selector.sorting', ['createdAt', 'desc']); } @@ -384,10 +366,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { private onFileUploadEvent() { this.uploadService.fileUploadComplete - .pipe( - debounceTime(500), - takeUntil(this.onDestroy$) - ) + .pipe(debounceTime(500), takeUntil(this.onDestroy$)) .subscribe((fileUploadEvent: FileUploadCompleteEvent) => { this.currentUploadBatch.push(fileUploadEvent.data); if (!this.uploadService.isUploading()) { @@ -399,12 +378,10 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { } private onFileUploadDeletedEvent() { - this.uploadService.fileUploadDeleted - .pipe(takeUntil(this.onDestroy$)) - .subscribe((deletedFileEvent: FileUploadDeleteEvent) => { - this.documentList.unselectRowFromNodeId(deletedFileEvent.file.data.entry.id); - this.documentList.reloadWithoutResettingSelection(); - }); + this.uploadService.fileUploadDeleted.pipe(takeUntil(this.onDestroy$)).subscribe((deletedFileEvent: FileUploadDeleteEvent) => { + this.documentList.unselectRowFromNodeId(deletedFileEvent.file.data.entry.id); + this.documentList.reloadWithoutResettingSelection(); + }); } private getStartSite() { @@ -424,19 +401,14 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { if (!filter) { filter = () => true; } - this._rowFilter = (value: ShareDataRow, index: number, array: ShareDataRow[]) => filter(value, index, array) && - !this.isExcludedSiteContent(value); + this._rowFilter = (value: ShareDataRow, index: number, array: ShareDataRow[]) => + filter(value, index, array) && !this.isExcludedSiteContent(value); } private isExcludedSiteContent(row: ShareDataRow): boolean { const entry = row.node.entry; - if (this._excludeSiteContent && this._excludeSiteContent.length && - entry && - entry.properties && - entry.properties['st:componentId']) { - const excludedItem = this._excludeSiteContent.find( - (id: string) => entry.properties['st:componentId'] === id - ); + if (this._excludeSiteContent && this._excludeSiteContent.length && entry && entry.properties && entry.properties['st:componentId']) { + const excludedItem = this._excludeSiteContent.find((id: string) => entry.properties['st:componentId'] === id); return !!excludedItem; } return false; @@ -472,8 +444,8 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { /** * Prepares the dialog for a new search */ - prepareDialogForNewSearch(queryBody: QueryBody): void { - this.target = queryBody ? null : this.documentList; + prepareDialogForNewSearch(searchRequest: SearchRequest): void { + this.target = searchRequest ? null : this.documentList; if (this.target) { this.infinitePaginationComponent.reset(); } @@ -516,18 +488,17 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { let extraParentFiltering = ''; if (this.customResourcesService.hasCorrespondingNodeIds(this.siteId)) { - this.customResourcesService.getCorrespondingNodeIds(this.siteId) - .subscribe((nodeIds) => { - if (nodeIds && nodeIds.length) { - nodeIds - .filter((id) => id !== this.siteId) - .forEach((extraId) => { - extraParentFiltering += ` OR ANCESTOR:'workspace://SpacesStore/${extraId}'`; - }); - } - const parentFiltering = this.siteId ? `ANCESTOR:'workspace://SpacesStore/${this.siteId}'${extraParentFiltering}` : ''; - this.queryBuilderService.addFilterQuery(parentFiltering); - }); + this.customResourcesService.getCorrespondingNodeIds(this.siteId).subscribe((nodeIds) => { + if (nodeIds && nodeIds.length) { + nodeIds + .filter((id) => id !== this.siteId) + .forEach((extraId) => { + extraParentFiltering += ` OR ANCESTOR:'workspace://SpacesStore/${extraId}'`; + }); + } + const parentFiltering = this.siteId ? `ANCESTOR:'workspace://SpacesStore/${this.siteId}'${extraParentFiltering}` : ''; + this.queryBuilderService.addFilterQuery(parentFiltering); + }); } else { const parentFiltering = this.siteId ? `ANCESTOR:'workspace://SpacesStore/${this.siteId}'` : ''; this.queryBuilderService.addFilterQuery(parentFiltering); @@ -638,7 +609,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { onCurrentSelection(nodesEntries: NodeEntry[]): void { const validNodesEntity = nodesEntries.filter((node) => this.isSelectionValid(node.entry)); this.chosenNode = validNodesEntity.map((node) => node.entry); - this.selectionWithoutValidation = nodesEntries.map(node => node.entry); + this.selectionWithoutValidation = nodesEntries.map((node) => node.entry); } setTitleIfCustomSite(site: SiteEntry) { diff --git a/lib/content-services/src/lib/mock/search-query.mock.ts b/lib/content-services/src/lib/mock/search-query.mock.ts index e36ef2ac120..0787968b4e3 100644 --- a/lib/content-services/src/lib/mock/search-query.mock.ts +++ b/lib/content-services/src/lib/mock/search-query.mock.ts @@ -15,9 +15,9 @@ * limitations under the License. */ -import { QueryBody } from '@alfresco/js-api'; +import { SearchRequest } from '@alfresco/js-api'; -export const mockQueryBody: QueryBody = { +export const mockSearchRequest = { query: { query: '(search-term*)', language: 'afts' @@ -38,4 +38,4 @@ export const mockQueryBody: QueryBody = { }, highlight: null, facetFormat: 'V2' -} as QueryBody; +} as SearchRequest; diff --git a/lib/content-services/src/lib/mock/search.component.mock.ts b/lib/content-services/src/lib/mock/search.component.mock.ts index 7c16c51dca0..47162b3c436 100644 --- a/lib/content-services/src/lib/mock/search.component.mock.ts +++ b/lib/content-services/src/lib/mock/search.component.mock.ts @@ -17,13 +17,13 @@ import { Component, ViewChild } from '@angular/core'; import { SearchComponent } from '../search/components/search.component'; -import { QueryBody, ResultSetPaging } from '@alfresco/js-api'; +import { SearchRequest, ResultSetPaging } from '@alfresco/js-api'; const entryItem = { entry: { id: '123', name: 'MyDoc', - isFile : true, + isFile: true, content: { mimeType: 'text/plain' }, @@ -40,7 +40,7 @@ const entryDifferentItem = { entry: { id: '999', name: 'TEST_DOC', - isFile : true, + isFile: true, content: { mimeType: 'text/plain' }, @@ -55,27 +55,19 @@ const entryDifferentItem = { export const result = new ResultSetPaging({ list: { - entries: [ - entryItem - ] + entries: [entryItem] } }); export const differentResult = new ResultSetPaging({ list: { - entries: [ - entryDifferentItem - ] + entries: [entryDifferentItem] } }); export const results = { list: { - entries: [ - entryItem, - entryItem, - entryItem - ] + entries: [entryItem, entryItem, entryItem] } }; @@ -86,8 +78,8 @@ export const folderResult = { entry: { id: '123', name: 'MyFolder', - isFile : false, - isFolder : true, + isFile: false, + isFolder: true, createdByUser: { displayName: 'John Doe' }, @@ -118,35 +110,36 @@ export const errorJson = { @Component({ template: ` - - -
    -
  • -
    - {{ item?.entry.name }} -
    -
  • -
-
-
- {{message}} + + +
    +
  • +
    + {{ item?.entry.name }} +
    +
  • +
+
+
+ {{ message }} ` - }) - - export class SimpleSearchTestComponent { - +}) +export class SimpleSearchTestComponent { @ViewChild('search', { static: true }) search: SearchComponent; message: string = ''; searchedWord = ''; maxResults: number = 5; - searchNode: QueryBody; + searchNode: SearchRequest; - constructor() { - } + constructor() {} showSearchResult(event: any) { this.message = event; @@ -160,7 +153,7 @@ export const errorJson = { this.searchedWord = str; } - setSearchNodeTo(searchNode: QueryBody) { + setSearchNodeTo(searchNode: SearchRequest) { this.searchNode = searchNode; } @@ -171,5 +164,4 @@ export const errorJson = { forceHidePanel() { this.search.hidePanel(); } - - } +} diff --git a/lib/content-services/src/lib/permission-manager/components/add-permission/search-config-permission.service.ts b/lib/content-services/src/lib/permission-manager/components/add-permission/search-config-permission.service.ts index 896d9d4d02d..0ae8f48885b 100644 --- a/lib/content-services/src/lib/permission-manager/components/add-permission/search-config-permission.service.ts +++ b/lib/content-services/src/lib/permission-manager/components/add-permission/search-config-permission.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { QueryBody } from '@alfresco/js-api'; +import { SearchRequest } from '@alfresco/js-api'; import { Injectable, Optional, Inject, InjectionToken } from '@angular/core'; import { SearchConfigurationInterface } from '../../../common/interfaces/search-configuration.interface'; @@ -26,15 +26,14 @@ export interface QueryProvider { @Injectable() export class SearchPermissionConfigurationService implements SearchConfigurationInterface { - constructor( @Optional() @Inject(SEARCH_QUERY_TOKEN) - private queryProvider: QueryProvider) { - } + private queryProvider: QueryProvider + ) {} - public generateQueryBody(searchTerm: string, maxResults: number, skipCount: number): QueryBody { - const defaultQueryBody: QueryBody = { + public generateQueryBody(searchTerm: string, maxResults: number, skipCount: number): SearchRequest { + return new SearchRequest({ query: { query: this.getQuery(searchTerm) }, @@ -45,17 +44,15 @@ export class SearchPermissionConfigurationService implements SearchConfiguration }, filterQueries: [ /* eslint-disable-next-line */ - { query: "TYPE:'cm:authority'" }] - }; - - return defaultQueryBody; + { query: "TYPE:'cm:authority'" } + ] + }); } private getQuery(searchTerm: string) { let query: string; if (this.queryProvider && this.queryProvider.query) { - query = this.queryProvider.query.replace( - new RegExp(/\${([^}]+)}/g), searchTerm); + query = this.queryProvider.query.replace(new RegExp(/\${([^}]+)}/g), searchTerm); } else { query = `(email:*${searchTerm}* OR firstName:*${searchTerm}* OR lastName:*${searchTerm}* OR displayName:*${searchTerm}* OR authorityName:*${searchTerm}* OR authorityDisplayName:*${searchTerm}*) AND ANAME:(\"0/APP.DEFAULT\")`; } diff --git a/lib/content-services/src/lib/permission-manager/services/node-permission.service.ts b/lib/content-services/src/lib/permission-manager/services/node-permission.service.ts index fdace58dcea..33af266509e 100644 --- a/lib/content-services/src/lib/permission-manager/services/node-permission.service.ts +++ b/lib/content-services/src/lib/permission-manager/services/node-permission.service.ts @@ -15,21 +15,10 @@ * limitations under the License. */ -import { - AlfrescoApiService, - TranslationService -} from '@alfresco/adf-core'; +import { AlfrescoApiService, TranslationService } from '@alfresco/adf-core'; import { NodesApiService } from '../../common/services/nodes-api.service'; import { EcmUserModel } from '../../common/models/ecm-user.model'; -import { - Group, - GroupMemberEntry, - GroupMemberPaging, GroupsApi, - Node, - PathElement, - PermissionElement, - QueryBody -} from '@alfresco/js-api'; +import { Group, GroupMemberEntry, GroupMemberPaging, GroupsApi, Node, PathElement, PermissionElement, SearchRequest } from '@alfresco/js-api'; import { SearchService } from '../../search/services/search.service'; import { Injectable } from '@angular/core'; import { forkJoin, from, Observable, of, throwError } from 'rxjs'; @@ -41,18 +30,18 @@ import { RoleModel } from '../models/role.model'; providedIn: 'root' }) export class NodePermissionService { - private _groupsApi: GroupsApi; get groupsApi(): GroupsApi { this._groupsApi = this._groupsApi ?? new GroupsApi(this.apiService.getInstance()); return this._groupsApi; } - constructor(private apiService: AlfrescoApiService, - private searchApiService: SearchService, - private nodeService: NodesApiService, - private translation: TranslationService) { - } + constructor( + private apiService: AlfrescoApiService, + private searchApiService: SearchService, + private nodeService: NodesApiService, + private translation: TranslationService + ) {} /** * Gets a list of roles for the current node. @@ -61,20 +50,23 @@ export class NodePermissionService { * @returns Array of strings representing the roles */ getNodeRoles(node: Node): Observable { - const retrieveSiteQueryBody: QueryBody = this.buildRetrieveSiteQueryBody(node.path.elements); - return this.searchApiService.searchByQueryBody(retrieveSiteQueryBody) - .pipe( - switchMap((siteNodeList: any) => { - if (siteNodeList.list.entries.length > 0) { - const siteName = siteNodeList.list.entries[0].entry.name; - return this.getGroupMembersBySiteName(siteName); - } else { - return of(node.permissions?.settable); - } - }) - ); + const searchRequest = this.buildRetrieveSiteQueryBody(node.path.elements); + return this.searchApiService.searchByQueryBody(searchRequest).pipe( + switchMap((siteNodeList: any) => { + if (siteNodeList.list.entries.length > 0) { + const siteName = siteNodeList.list.entries[0].entry.name; + return this.getGroupMembersBySiteName(siteName); + } else { + return of(node.permissions?.settable); + } + }) + ); } + /** + * Get permissions for a given node + * @param node Node to check permissions for + */ getNodePermissions(node: Node): PermissionDisplayModel[] { const result: PermissionDisplayModel[] = []; @@ -121,9 +113,7 @@ export class NodePermissionService { * @returns Node with updated permissions */ updateNodePermissions(nodeId: string, permissionList: PermissionElement[]): Observable { - return this.nodeService.getNode(nodeId).pipe( - switchMap((node) => this.updateLocallySetPermissions(node, permissionList)) - ); + return this.nodeService.getNode(nodeId).pipe(switchMap((node) => this.updateLocallySetPermissions(node, permissionList))); } /** @@ -138,7 +128,9 @@ export class NodePermissionService { const permissionList = permissions; const duplicatedPermissions = this.getDuplicatedPermissions(node.permissions.locallySet, permissionList); if (duplicatedPermissions.length > 0) { - const list = duplicatedPermissions.map((permission) => 'authority -> ' + permission.authorityId + ' / role -> ' + permission.name).join(', '); + const list = duplicatedPermissions + .map((permission) => 'authority -> ' + permission.authorityId + ' / role -> ' + permission.name) + .join(', '); const duplicatePermissionMessage: string = this.translation.instant('PERMISSION_MANAGER.ERROR.DUPLICATE-PERMISSION', { list }); return throwError(duplicatePermissionMessage); } @@ -160,9 +152,11 @@ export class NodePermissionService { } private isEqualPermission(oldPermission: PermissionElement, newPermission: PermissionElement): boolean { - return oldPermission.accessStatus === newPermission.accessStatus && + return ( + oldPermission.accessStatus === newPermission.accessStatus && oldPermission.authorityId === newPermission.authorityId && - oldPermission.name === newPermission.name; + oldPermission.name === newPermission.name + ); } /** @@ -187,16 +181,15 @@ export class NodePermissionService { private getGroupMembersBySiteName(siteName: string): Observable { const groupName = 'GROUP_site_' + siteName; - return this.getGroupMemberByGroupName(groupName) - .pipe( - map((groupMemberPaging: GroupMemberPaging) => { - const displayResult: string[] = []; - groupMemberPaging.list.entries.forEach((member: GroupMemberEntry) => { - displayResult.push(this.formattedRoleName(member.entry.displayName, 'site_' + siteName)); - }); - return displayResult; - }) - ); + return this.getGroupMemberByGroupName(groupName).pipe( + map((groupMemberPaging: GroupMemberPaging) => { + const displayResult: string[] = []; + groupMemberPaging.list.entries.forEach((member: GroupMemberEntry) => { + displayResult.push(this.formattedRoleName(member.entry.displayName, 'site_' + siteName)); + }); + return displayResult; + }) + ); } /** @@ -214,7 +207,7 @@ export class NodePermissionService { return displayName.replace(siteName + '_', ''); } - private buildRetrieveSiteQueryBody(nodePath: PathElement[]): QueryBody { + private buildRetrieveSiteQueryBody(nodePath: PathElement[]): SearchRequest { const pathNames = nodePath.map((node: PathElement) => 'name: "' + node.name + '"'); const builtPathNames = pathNames.join(' OR '); @@ -229,8 +222,7 @@ export class NodePermissionService { include: ['aspectNames', 'properties'], filterQueries: [ { - query: - `TYPE:'st:site'` + query: `TYPE:'st:site'` } ] }; @@ -302,15 +294,15 @@ export class NodePermissionService { */ getNodeWithRoles(nodeId: string): Observable<{ node: Node; roles: RoleModel[] }> { return this.nodeService.getNode(nodeId).pipe( - switchMap(node => forkJoin({ - node: of(node), - roles: this.getNodeRoles(node) - .pipe( + switchMap((node) => + forkJoin({ + node: of(node), + roles: this.getNodeRoles(node).pipe( catchError(() => of(node.permissions?.settable)), - map(_roles => _roles.map(role => ({ role, label: role })) - ) + map((_roles) => _roles.map((role) => ({ role, label: role }))) ) - })) + }) + ) ); } 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 c0446f98a08..3299e601998 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 @@ -19,7 +19,7 @@ import { Injectable } from '@angular/core'; import { Subject, Observable, from, ReplaySubject } from 'rxjs'; import { AlfrescoApiService, AppConfigService } from '@alfresco/adf-core'; import { - QueryBody, + SearchRequest, RequestFacetFields, RequestSortDefinitionInner, ResultSetPaging, @@ -41,7 +41,6 @@ import { SearchForm } from '../models/search-form.interface'; providedIn: 'root' }) export abstract class BaseQueryBuilderService { - private _searchApi: SearchApi; get searchApi(): SearchApi { this._searchApi = this._searchApi ?? new SearchApi(this.alfrescoApiService.getInstance()); @@ -52,7 +51,7 @@ export abstract class BaseQueryBuilderService { configUpdated = new Subject(); /* Stream that emits the query before search whenever user search */ - updated = new Subject(); + updated = new Subject(); /* Stream that emits the results whenever user search */ executed = new Subject(); @@ -152,12 +151,14 @@ export abstract class BaseQueryBuilderService { selected: this.selectedConfiguration !== undefined ? index === this.selectedConfiguration : configuration.default })); } else if (!!configurations) { - return [{ - index: 0, - name: configurations.name || 'SEARCH.UNKNOWN_CONFIGURATION', - default: true, - selected: true - }]; + return [ + { + index: 0, + name: configurations.name || 'SEARCH.UNKNOWN_CONFIGURATION', + default: true, + selected: true + } + ]; } return []; } @@ -165,9 +166,7 @@ export abstract class BaseQueryBuilderService { private setUpSearchConfiguration(currentConfiguration: SearchConfiguration) { if (currentConfiguration) { this.config = JSON.parse(JSON.stringify(currentConfiguration)); - this.categories = (this.config.categories || []).filter( - category => category.enabled - ); + this.categories = (this.config.categories || []).filter((category) => category.enabled); this.filterQueries = this.config.filterQueries || []; this.userFacetBuckets = {}; if (this.config.sorting) { @@ -213,8 +212,7 @@ export abstract class BaseQueryBuilderService { removeUserFacetBucket(field: string, bucket: FacetFieldBucket) { if (field && bucket) { const buckets = this.userFacetBuckets[field] || []; - this.userFacetBuckets[field] = buckets - .filter((facetBucket) => facetBucket.label !== bucket.label); + this.userFacetBuckets[field] = buckets.filter((facetBucket) => facetBucket.label !== bucket.label); } } @@ -239,8 +237,7 @@ export abstract class BaseQueryBuilderService { */ removeFilterQuery(query: string): void { if (query) { - this.filterQueries = this.filterQueries - .filter((filterQuery) => filterQuery.query !== query); + this.filterQueries = this.filterQueries.filter((filterQuery) => filterQuery.query !== query); } } @@ -289,7 +286,7 @@ export abstract class BaseQueryBuilderService { /** * Builds the current query and triggers the `updated` event. */ - update(queryBody?: QueryBody): void { + update(queryBody?: SearchRequest): void { const query = queryBody ? queryBody : this.buildQuery(); this.updated.next(query); } @@ -299,7 +296,7 @@ export abstract class BaseQueryBuilderService { * * @returns Nothing */ - async execute(queryBody?: QueryBody) { + async execute(queryBody?: SearchRequest) { try { const query = queryBody ? queryBody : this.buildQuery(); if (query) { @@ -320,7 +317,7 @@ export abstract class BaseQueryBuilderService { } } - search(queryBody: QueryBody): Observable { + search(queryBody: SearchRequest): Observable { const promise = this.searchApi.search(queryBody); promise.then((resultSetPaging) => { @@ -335,7 +332,7 @@ export abstract class BaseQueryBuilderService { * * @returns The finished query */ - buildQuery(): QueryBody { + buildQuery(): SearchRequest { const query = this.getFinalQuery(); const include = this.config.include || []; @@ -344,8 +341,7 @@ export abstract class BaseQueryBuilderService { } if (query) { - - const result: QueryBody = { + const result: SearchRequest = { query: { query, language: 'afts' @@ -362,7 +358,7 @@ export abstract class BaseQueryBuilderService { }; if (this.scope) { - result['scope'] = this.scope; + result.scope = this.scope; } result['facetFormat'] = 'V2'; @@ -412,10 +408,7 @@ export abstract class BaseQueryBuilderService { * @returns True if defined, false otherwise */ get hasFacetQueries(): boolean { - if (this.config - && this.config.facetQueries - && this.config.facetQueries.queries - && this.config.facetQueries.queries.length > 0) { + if (this.config && this.config.facetQueries && this.config.facetQueries.queries && this.config.facetQueries.queries.length > 0) { return true; } return false; @@ -427,11 +420,7 @@ export abstract class BaseQueryBuilderService { * @returns True if defined, false otherwise */ get hasFacetIntervals(): boolean { - return this.config - && this.config.facetIntervals - && this.config.facetIntervals.intervals - && this.config.facetIntervals.intervals.length > 0; - + return this.config && this.config.facetIntervals && this.config.facetIntervals.intervals && this.config.facetIntervals.intervals.length > 0; } get hasFacetHighlight(): boolean { @@ -439,11 +428,14 @@ export abstract class BaseQueryBuilderService { } protected get sort(): RequestSortDefinitionInner[] { - return this.sorting.map((def) => new RequestSortDefinitionInner({ - type: def.type, - field: def.field, - ascending: def.ascending - })); + return this.sorting.map( + (def) => + new RequestSortDefinitionInner({ + type: def.type, + field: def.field, + ascending: def.ascending + }) + ); } protected get facetQueries(): FacetQuery[] { @@ -462,17 +454,23 @@ export abstract class BaseQueryBuilderService { const configIntervals = this.config.facetIntervals; return { - intervals: configIntervals.intervals.map((interval) => ({ - label: this.getSupportedLabel(interval.label), - field: interval.field, - sets: interval.sets.map((set) => ({ - label: this.getSupportedLabel(set.label), - start: set.start, - end: set.end, - startInclusive: set.startInclusive, - endInclusive: set.endInclusive - } as any)) - } as any)) + intervals: configIntervals.intervals.map( + (interval) => + ({ + label: this.getSupportedLabel(interval.label), + field: interval.field, + sets: interval.sets.map( + (set) => + ({ + label: this.getSupportedLabel(set.label), + start: set.start, + end: set.end, + startInclusive: set.startInclusive, + endInclusive: set.endInclusive + } as any) + ) + } as any) + ) }; } @@ -496,9 +494,7 @@ export abstract class BaseQueryBuilderService { } }); - let result = [this.userQuery, query] - .filter((entry) => entry) - .join(' AND '); + let result = [this.userQuery, query].filter((entry) => entry).join(' AND '); if (this.userFacetBuckets) { Object.keys(this.userFacetBuckets).forEach((key) => { @@ -523,14 +519,17 @@ export abstract class BaseQueryBuilderService { if (facetFields && facetFields.length > 0) { return { - facets: facetFields.map((facet) => ({ - field: facet.field, - mincount: facet.mincount, - label: this.getSupportedLabel(facet.label), - limit: facet.limit, - offset: facet.offset, - prefix: facet.prefix - } as any)) + facets: facetFields.map( + (facet) => + ({ + field: facet.field, + mincount: facet.mincount, + label: this.getSupportedLabel(facet.label), + limit: facet.limit, + offset: facet.offset, + prefix: facet.prefix + } as any) + ) }; } diff --git a/lib/content-services/src/lib/search/services/search-configuration.service.ts b/lib/content-services/src/lib/search/services/search-configuration.service.ts index 786a0be732c..286f722b0d0 100644 --- a/lib/content-services/src/lib/search/services/search-configuration.service.ts +++ b/lib/content-services/src/lib/search/services/search-configuration.service.ts @@ -16,24 +16,23 @@ */ import { Injectable } from '@angular/core'; -import { QueryBody } from '@alfresco/js-api'; +import { SearchRequest } from '@alfresco/js-api'; import { SearchConfigurationInterface } from '../../common/interfaces/search-configuration.interface'; @Injectable({ providedIn: 'root' }) export class SearchConfigurationService implements SearchConfigurationInterface { - /** - * Generates a QueryBody object with custom search parameters. + * Generates a request object with custom search parameters. * * @param searchTerm Term text to search for * @param maxResults Maximum number of search results to show in a page * @param skipCount The offset of the start of the page within the results list * @returns Query body defined by the parameters */ - generateQueryBody(searchTerm: string, maxResults: number, skipCount: number): QueryBody { - const defaultQueryBody: QueryBody = { + generateQueryBody(searchTerm: string, maxResults: number, skipCount: number): SearchRequest { + return new SearchRequest({ query: { query: searchTerm ? `'${searchTerm}*' OR name:'${searchTerm}*'` : searchTerm }, @@ -42,11 +41,7 @@ export class SearchConfigurationService implements SearchConfigurationInterface maxItems: maxResults, skipCount }, - filterQueries: [ - { query: `TYPE:'cm:folder' OR TYPE:'cm:content'` }, - { query: 'NOT cm:creator:System' }] - }; - - return defaultQueryBody; + filterQueries: [{ query: `TYPE:'cm:folder' OR TYPE:'cm:content'` }, { query: 'NOT cm:creator:System' }] + }); } } 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 2bde2193dbe..2e3ffade732 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 @@ -23,7 +23,6 @@ import { TestBed } from '@angular/core/testing'; import { ContentTestingModule } from '../../testing/content.testing.module'; describe('SearchQueryBuilder', () => { - beforeEach(() => { TestBed.configureTestingModule({ imports: [ContentTestingModule] @@ -38,14 +37,8 @@ describe('SearchQueryBuilder', () => { it('should reset to defaults', () => { const config: SearchConfiguration = { - categories: [ - { id: 'cat1', enabled: true } as any, - { id: 'cat2', enabled: true } as any - ], - filterQueries: [ - { query: 'query1' }, - { query: 'query2' } - ] + categories: [{ id: 'cat1', enabled: true } as any, { id: 'cat2', enabled: true } as any], + filterQueries: [{ query: 'query1' }, { query: 'query2' }] }; const alfrescoApiService = TestBed.inject(AlfrescoApiService); const builder = new SearchQueryBuilderService(buildConfig(config), alfrescoApiService); @@ -86,11 +79,7 @@ describe('SearchQueryBuilder', () => { it('should use only enabled categories', () => { const config: SearchConfiguration = { - categories: [ - { id: 'cat1', enabled: true } as any, - { id: 'cat2', enabled: false } as any, - { id: 'cat3', enabled: true } as any - ] + categories: [{ id: 'cat1', enabled: true } as any, { id: 'cat2', enabled: false } as any, { id: 'cat3', enabled: true } as any] }; const alfrescoApiService = TestBed.inject(AlfrescoApiService); @@ -104,10 +93,7 @@ describe('SearchQueryBuilder', () => { it('should fetch filter queries from config', () => { const config: SearchConfiguration = { categories: [], - filterQueries: [ - { query: 'query1' }, - { query: 'query2' } - ] + filterQueries: [{ query: 'query1' }, { query: 'query2' }] }; const alfrescoApiService = TestBed.inject(AlfrescoApiService); const builder = new SearchQueryBuilderService(buildConfig(config), alfrescoApiService); @@ -202,9 +188,7 @@ describe('SearchQueryBuilder', () => { const config: SearchConfiguration = { categories: [], facetQueries: { - queries: [ - { query: 'q1', label: 'query1' } - ] + queries: [{ query: 'q1', label: 'query1' }] } }; const alfrescoApiService = TestBed.inject(AlfrescoApiService); @@ -259,9 +243,7 @@ describe('SearchQueryBuilder', () => { const config: SearchConfiguration = { categories: [], facetFields: { - fields: [ - { field: 'content.size', mincount: 1, label: 'Label with spaces' } - ] + fields: [{ field: 'content.size', mincount: 1, label: 'Label with spaces' }] } }; const alfrescoApiService = TestBed.inject(AlfrescoApiService); @@ -275,9 +257,7 @@ describe('SearchQueryBuilder', () => { it('should require a query fragment to build query', () => { const config: SearchConfiguration = { - categories: [ - { id: 'cat1', enabled: true } as any - ] + categories: [{ id: 'cat1', enabled: true } as any] }; const alfrescoApiService = TestBed.inject(AlfrescoApiService); @@ -290,9 +270,7 @@ describe('SearchQueryBuilder', () => { it('should build query with single fragment', () => { const config: SearchConfiguration = { - categories: [ - { id: 'cat1', enabled: true } as any - ] + categories: [{ id: 'cat1', enabled: true } as any] }; const alfrescoApiService = TestBed.inject(AlfrescoApiService); @@ -306,10 +284,7 @@ describe('SearchQueryBuilder', () => { it('should build query with multiple fragments', () => { const config: SearchConfiguration = { - categories: [ - { id: 'cat1', enabled: true } as any, - { id: 'cat2', enabled: true } as any - ] + categories: [{ id: 'cat1', enabled: true } as any, { id: 'cat2', enabled: true } as any] }; const alfrescoApiService = TestBed.inject(AlfrescoApiService); @@ -319,18 +294,13 @@ describe('SearchQueryBuilder', () => { builder.queryFragments['cat2'] = 'NOT cm:creator:System'; const compiled = builder.buildQuery(); - expect(compiled.query.query).toBe( - '(cm:name:test) AND (NOT cm:creator:System)' - ); + expect(compiled.query.query).toBe('(cm:name:test) AND (NOT cm:creator:System)'); }); it('should build query with custom fields', () => { const config: SearchConfiguration = { fields: ['field1', 'field2'], - categories: [ - { id: 'cat1', enabled: true } as any, - { id: 'cat2', enabled: true } as any - ] + categories: [{ id: 'cat1', enabled: true } as any, { id: 'cat2', enabled: true } as any] }; const alfrescoApiService = TestBed.inject(AlfrescoApiService); @@ -345,10 +315,7 @@ describe('SearchQueryBuilder', () => { it('should build query with empty custom fields', () => { const config: SearchConfiguration = { fields: [], - categories: [ - { id: 'cat1', enabled: true } as any, - { id: 'cat2', enabled: true } as any - ] + categories: [{ id: 'cat1', enabled: true } as any, { id: 'cat2', enabled: true } as any] }; const alfrescoApiService = TestBed.inject(AlfrescoApiService); @@ -362,9 +329,7 @@ describe('SearchQueryBuilder', () => { it('should build query with custom filter queries', () => { const config: SearchConfiguration = { - categories: [ - { id: 'cat1', enabled: true } as any - ] + categories: [{ id: 'cat1', enabled: true } as any] }; const alfrescoApiService = TestBed.inject(AlfrescoApiService); @@ -373,20 +338,14 @@ describe('SearchQueryBuilder', () => { builder.addFilterQuery('query1'); const compiled = builder.buildQuery(); - expect(compiled.filterQueries).toEqual( - [{ query: 'query1' }] - ); + expect(compiled.filterQueries).toEqual([{ query: 'query1' }]); }); it('should build query with custom facet queries', () => { const config: SearchConfiguration = { - categories: [ - { id: 'cat1', enabled: true } as any - ], + categories: [{ id: 'cat1', enabled: true } as any], facetQueries: { - queries: [ - { query: 'q1', label: 'q2', group: 'group-name' } - ] + queries: [{ query: 'q1', label: 'q2', group: 'group-name' }] } }; const alfrescoApiService = TestBed.inject(AlfrescoApiService); @@ -400,9 +359,7 @@ describe('SearchQueryBuilder', () => { it('should build query with custom facet fields', () => { const config: SearchConfiguration = { - categories: [ - { id: 'cat1', enabled: true } as any - ], + categories: [{ id: 'cat1', enabled: true } as any], facetFields: { fields: [ { field: 'field1', label: 'field1', mincount: 1, limit: null, offset: 0, prefix: null }, @@ -430,9 +387,7 @@ describe('SearchQueryBuilder', () => { }; const config: SearchConfiguration = { - categories: [ - { id: 'cat1', enabled: true } as any - ], + categories: [{ id: 'cat1', enabled: true } as any], facetFields: { fields: [ { @@ -468,9 +423,7 @@ describe('SearchQueryBuilder', () => { it('should build query with custom facet intervals', () => { const config: SearchConfiguration = { - categories: [ - { id: 'cat1', enabled: true } as any - ], + categories: [{ id: 'cat1', enabled: true } as any], facetIntervals: { intervals: [ { @@ -516,9 +469,7 @@ describe('SearchQueryBuilder', () => { }; const config: SearchConfiguration = { - categories: [ - { id: 'cat1', enabled: true } as any - ], + categories: [{ id: 'cat1', enabled: true } as any], facetIntervals: { intervals: [ { @@ -557,10 +508,7 @@ describe('SearchQueryBuilder', () => { it('should build query with sorting', () => { const config: SearchConfiguration = { fields: [], - categories: [ - { id: 'cat1', enabled: true } as any, - { id: 'cat2', enabled: true } as any - ] + categories: [{ id: 'cat1', enabled: true } as any, { id: 'cat2', enabled: true } as any] }; const alfrescoApiService = TestBed.inject(AlfrescoApiService); const builder = new SearchQueryBuilderService(buildConfig(config), alfrescoApiService); @@ -575,9 +523,7 @@ describe('SearchQueryBuilder', () => { it('should use pagination settings', () => { const config: SearchConfiguration = { - categories: [ - { id: 'cat1', enabled: true } as any - ] + categories: [{ id: 'cat1', enabled: true } as any] }; const alfrescoApiService = TestBed.inject(AlfrescoApiService); const builder = new SearchQueryBuilderService(buildConfig(config), alfrescoApiService); @@ -593,9 +539,7 @@ describe('SearchQueryBuilder', () => { it('should build final request with user and custom queries', () => { const config: SearchConfiguration = { - categories: [ - { id: 'cat1', enabled: true } as any - ] + categories: [{ id: 'cat1', enabled: true } as any] }; const alfrescoApiService = TestBed.inject(AlfrescoApiService); const builder = new SearchQueryBuilderService(buildConfig(config), alfrescoApiService); @@ -629,9 +573,7 @@ describe('SearchQueryBuilder', () => { ]; const config: SearchConfiguration = { - categories: [ - { id: 'cat1', enabled: true } as any - ] + categories: [{ id: 'cat1', enabled: true } as any] }; const alfrescoApiService = TestBed.inject(AlfrescoApiService); @@ -671,9 +613,7 @@ describe('SearchQueryBuilder', () => { it('should emit error event', () => { const config: SearchConfiguration = { - categories: [ - { id: 'cat1', enabled: true } as any - ] + categories: [{ id: 'cat1', enabled: true } as any] }; const alfrescoApiService = TestBed.inject(AlfrescoApiService); @@ -689,9 +629,7 @@ describe('SearchQueryBuilder', () => { it('should emit empty results on error', (done) => { const config: SearchConfiguration = { - categories: [ - { id: 'cat1', enabled: true } as any - ] + categories: [{ id: 'cat1', enabled: true } as any] }; const alfrescoApiService = TestBed.inject(AlfrescoApiService); @@ -712,9 +650,9 @@ describe('SearchQueryBuilder', () => { const builder = new SearchQueryBuilderService(buildConfig({}), alfrescoApiService); builder.userQuery = 'nuka cola quantum'; - const queryBody = builder.buildQuery(); + const searchRequest = builder.buildQuery(); - expect(queryBody.include).toEqual(['path', 'allowableOperations']); + expect(searchRequest.include).toEqual(['path', 'allowableOperations']); }); it('should fetch the include config from the app config', () => { @@ -726,9 +664,9 @@ describe('SearchQueryBuilder', () => { const builder = new SearchQueryBuilderService(buildConfig(config), alfrescoApiService); builder.userQuery = 'nuka cola quantum'; - const queryBody = builder.buildQuery(); + const searchRequest = builder.buildQuery(); - expect(queryBody.include).toEqual(includeConfig); + expect(searchRequest.include).toEqual(includeConfig); }); it('should the query contain the pagination', () => { @@ -741,9 +679,9 @@ describe('SearchQueryBuilder', () => { skipCount: 0 }; builder.paging = mockPagination; - const queryBody = builder.buildQuery(); + const searchRequest = builder.buildQuery(); - expect(queryBody.paging).toEqual(mockPagination); + expect(searchRequest.paging).toEqual(mockPagination); }); it('should the query contain the scope in case it is defined', () => { @@ -753,9 +691,9 @@ describe('SearchQueryBuilder', () => { const mockScope = { locations: 'mock-location' }; builder.userQuery = 'nuka cola quantum'; builder.setScope(mockScope); - const queryBody = builder.buildQuery(); + const searchRequest = builder.buildQuery(); - expect(queryBody.scope).toEqual(mockScope); + expect(searchRequest.scope).toEqual(mockScope); }); it('should return empty if array of search config not found', (done) => { @@ -774,32 +712,19 @@ describe('SearchQueryBuilder', () => { beforeEach(() => { configs = [ { - categories: [ - { id: 'cat1', enabled: true } as any, - { id: 'cat2', enabled: true } as any - ], - filterQueries: [ - { query: 'query1' }, - { query: 'query2' } - ], + categories: [{ id: 'cat1', enabled: true } as any, { id: 'cat2', enabled: true } as any], + filterQueries: [{ query: 'query1' }, { query: 'query2' }], name: 'config1', default: true }, { - categories: [ - { id: 'mouse', enabled: true } as any - ], - filterQueries: [ - { query: 'query1' }, - { query: 'query2' } - ], + categories: [{ id: 'mouse', enabled: true } as any], + filterQueries: [{ query: 'query1' }, { query: 'query2' }], name: 'config2', default: false }, { - categories: [ - { id: 'cat_and_mouse', enabled: true } as any - ], + categories: [{ id: 'cat_and_mouse', enabled: true } as any], default: false } ]; diff --git a/lib/content-services/src/lib/search/services/search.service.ts b/lib/content-services/src/lib/search/services/search.service.ts index 044b826cf4a..792670c0fc9 100644 --- a/lib/content-services/src/lib/search/services/search.service.ts +++ b/lib/content-services/src/lib/search/services/search.service.ts @@ -16,7 +16,7 @@ */ import { Injectable } from '@angular/core'; -import { NodePaging, QueriesApi, QueryBody, ResultSetPaging, SearchApi } from '@alfresco/js-api'; +import { NodePaging, QueriesApi, SearchRequest, ResultSetPaging, SearchApi } from '@alfresco/js-api'; import { Observable, Subject, from, throwError } from 'rxjs'; import { AlfrescoApiService } from '@alfresco/adf-core'; import { SearchConfigurationService } from './search-configuration.service'; @@ -25,7 +25,6 @@ import { SearchConfigurationService } from './search-configuration.service'; providedIn: 'root' }) export class SearchService { - dataLoaded: Subject = new Subject(); private _queriesApi: QueriesApi; @@ -40,9 +39,7 @@ export class SearchService { return this._searchApi; } - constructor(private apiService: AlfrescoApiService, - private searchConfigurationService: SearchConfigurationService) { - } + constructor(private apiService: AlfrescoApiService, private searchConfigurationService: SearchConfigurationService) {} /** * Gets a list of nodes that match the given search criteria. @@ -54,9 +51,11 @@ export class SearchService { getNodeQueryResults(term: string, options?: SearchOptions): Observable { const promise = this.queriesApi.findNodes(term, options); - promise.then((nodePaging: NodePaging) => { - this.dataLoaded.next(nodePaging); - }).catch((err) => this.handleError(err)); + promise + .then((nodePaging) => { + this.dataLoaded.next(nodePaging); + }) + .catch((err) => this.handleError(err)); return from(promise); } @@ -70,28 +69,32 @@ export class SearchService { * @returns List of search results */ search(searchTerm: string, maxResults: number, skipCount: number): Observable { - const searchQuery = Object.assign(this.searchConfigurationService.generateQueryBody(searchTerm, maxResults, skipCount)); + const searchQuery = this.searchConfigurationService.generateQueryBody(searchTerm, maxResults, skipCount); const promise = this.searchApi.search(searchQuery); - promise.then((nodePaging: NodePaging) => { - this.dataLoaded.next(nodePaging); - }).catch((err) => this.handleError(err)); + promise + .then((nodePaging) => { + this.dataLoaded.next(nodePaging); + }) + .catch((err) => this.handleError(err)); return from(promise); } /** - * Performs a search with its parameters supplied by a QueryBody object. + * Performs a search with its parameters supplied by a request object. * * @param queryBody Object containing the search parameters * @returns List of search results */ - searchByQueryBody(queryBody: QueryBody): Observable { + searchByQueryBody(queryBody: SearchRequest): Observable { const promise = this.searchApi.search(queryBody); - promise.then((nodePaging: NodePaging) => { - this.dataLoaded.next(nodePaging); - }).catch((err) => this.handleError(err)); + promise + .then((nodePaging) => { + this.dataLoaded.next(nodePaging); + }) + .catch((err) => this.handleError(err)); return from(promise); }