[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
This commit is contained in:
Denys Vuika
2023-08-31 14:36:21 +01:00
committed by GitHub
parent c73e95c9bf
commit c83d92c539
18 changed files with 340 additions and 480 deletions

View File

@@ -15,15 +15,14 @@
* limitations under the License. * limitations under the License.
*/ */
import { QueryBody } from '@alfresco/js-api'; import { SearchRequest } from '@alfresco/js-api';
import { SearchConfigurationInterface } from '@alfresco/adf-content-services'; import { SearchConfigurationInterface } from '@alfresco/adf-content-services';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
@Injectable() @Injectable()
export class TestSearchConfigurationService implements SearchConfigurationInterface { export class TestSearchConfigurationService implements SearchConfigurationInterface {
public generateQueryBody(searchTerm: string, maxResults: number, skipCount: number): SearchRequest {
public generateQueryBody(searchTerm: string, maxResults: number, skipCount: number): QueryBody { return {
const defaultQueryBody: QueryBody = {
query: { query: {
query: searchTerm ? `${searchTerm}* OR name:${searchTerm}*` : searchTerm query: searchTerm ? `${searchTerm}* OR name:${searchTerm}*` : searchTerm
}, },
@@ -35,9 +34,8 @@ export class TestSearchConfigurationService implements SearchConfigurationInterf
filterQueries: [ filterQueries: [
/* eslint-disable-next-line */ /* eslint-disable-next-line */
{ query: "TYPE:'cm:folder'" }, { query: "TYPE:'cm:folder'" },
{ query: 'NOT cm:creator:System' }] { query: 'NOT cm:creator:System' }
]
}; };
return defaultQueryBody;
} }
} }

View File

@@ -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. 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): - [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 `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). [custom search configuration interface](../core/interfaces/search-configuration.interface.md).
The inputs were deprecated in v2.1.0. The inputs were deprecated in v2.1.0.

View File

@@ -151,8 +151,7 @@ By doing this, you can get the results as the user types into the input text.
### Custom search configuration ### Custom search configuration
You can get finer control over the parameters of a search by defining them in a custom 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) **SearchRequest** object. The recommended way to do this is with a custom implementation of the
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 [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 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 [Search Configuration interface](../../core/interfaces/search-configuration.interface.md) page for full details of how to

View File

@@ -20,12 +20,12 @@ Stores information from all the custom search and faceted search widgets, compil
Adds a facet bucket to a field. Adds a facet bucket to a field.
- _field:_ [`FacetField`](../../../lib/content-services/src/lib/search/models/facet-field.interface.ts) - The target 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 - _bucket:_ [`FacetFieldBucket`](../../../lib/content-services/src/lib/search/models/facet-field-bucket.interface.ts) - Bucket to add
- **buildQuery**(): `QueryBody`<br/> - **buildQuery**(): `SearchRequest`<br/>
Builds the current query. Builds the current query.
- **Returns** `QueryBody` - The finished query - **Returns** `SearchRequest` - The finished query
- **execute**(queryBody?: `QueryBody`)<br/> - **execute**(queryBody?: `SearchRequest`)<br/>
Builds and executes the current query. 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`<br/> - **getDefaultConfiguration**(): [`SearchConfiguration`](../../../lib/content-services/src/lib/search/models/search-configuration.interface.ts)`|undefined`<br/>
- **Returns** [`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 - _bucket:_ [`FacetFieldBucket`](../../../lib/content-services/src/lib/search/models/facet-field-bucket.interface.ts) - Bucket to remove
- **resetToDefaults**()<br/> - **resetToDefaults**()<br/>
- **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)`>`<br/> - **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)`>`<br/>
- _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)`>` - - **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`)<br/> - **setScope**(scope: `RequestScope`)<br/>
- _scope:_ `RequestScope` - - _scope:_ `RequestScope` -
- **update**(queryBody?: `QueryBody`)<br/> - **update**(queryBody?: `SearchRequest`)<br/>
Builds the current query and triggers the `updated` event. Builds the current query and triggers the `updated` event.
- _queryBody:_ `QueryBody` - (Optional) - _queryBody:_ `SearchRequest` - (Optional)
- **updateSelectedConfiguration**(index: `number`)<br/> - **updateSelectedConfiguration**(index: `number`)<br/>
- _index:_ `number` - - _index:_ `number` -

View File

@@ -10,14 +10,13 @@ Provides fine control of parameters to a search.
## Methods ## Methods
`generateQueryBody(searchTerm: string, maxResults: string, skipCount: string): QueryBody`<br/> `generateQueryBody(searchTerm: string, maxResults: string, skipCount: string): SearchRequest`<br/>
Generates a QueryBody object with custom search parameters. Generates a request object with custom search parameters.
## Details ## Details
The interface defines a service that generates a custom 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) **SearchRequest** object. This object can then be supplied to a search operation to refine the search parameters.
object. This object can then be supplied to a search operation to refine the search parameters.
A standard implementation, the A standard implementation, the
[Search Configuration service](../services/search-configuration.service.md) is provided in the ADF Core library [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 1. Implement the service class
Create your own service class to implement the [`SearchConfigurationInterface`](../../core/interfaces/search-configuration.interface.md). This defines the 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 `generateQueryBody` method that returns the query object.
[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.
An example implementation is given below: An example implementation is given below:
```ts ```ts
import { QueryBody } from '@alfresco/js-api'; import { SearchRequest } from '@alfresco/js-api';
import { SearchConfigurationInterface } from '@alfresco/adf-core'; import { SearchConfigurationInterface } from '@alfresco/adf-core';
export class TestSearchConfigurationService implements SearchConfigurationInterface { export class TestSearchConfigurationService implements SearchConfigurationInterface {
constructor() { generateQueryBody(searchTerm: string, maxItems: string, skipCount: string): SearchRequest {
} return {
public generateQueryBody(searchTerm: string, maxResults: string, skipCount: string): QueryBody {
const defaultQueryBody: QueryBody = {
query: { query: {
query: searchTerm ? `${searchTerm}* OR name:${searchTerm}*` : searchTerm query: searchTerm ? `${searchTerm}* OR name:${searchTerm}*` : searchTerm
}, },
include: ['path', 'allowableOperations'], include: ['path', 'allowableOperations'],
paging: { paging: {
maxItems: maxResults, maxItems,
skipCount: skipCount skipCount
}, },
filterQueries: [ filterQueries: [
{ query: "TYPE:'cm:folder'" }, { query: "TYPE:'cm:folder'" },
{ query: 'NOT cm:creator:System' }] { query: 'NOT cm:creator:System' }]
}; };
return defaultQueryBody;
} }
} }
``` ```

View File

@@ -13,18 +13,17 @@ Provides fine control of parameters to a search.
### Methods ### Methods
- **generateQueryBody**(searchTerm: `string`, maxResults: `number`, skipCount: `number`): `QueryBody`<br/> - **generateQueryBody**(searchTerm: `string`, maxResults: `number`, skipCount: `number`): `SearchRequest`<br/>
Generates a QueryBody object with custom search parameters. Generates a request object with custom search parameters.
- _searchTerm:_ `string` - Term text to search for - _searchTerm:_ `string` - Term text to search for
- _maxResults:_ `number` - Maximum number of search results to show in a page - _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 - _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 ## Details
The `generateQueryBody` method returns a The `generateQueryBody` method returns a **SearchRequest** object.
[QueryBody](https://github.com/Alfresco/alfresco-js-api/blob/1.6.0/src/alfresco-search-rest-api/docs/QueryBody.md) This configures the search to use `searchTerm` along with `maxResults` and `skipCount`
object. This configures the search to use `searchTerm` along with `maxResults` and `skipCount`
specified for the paging of the search results. specified for the paging of the search results.
This service is a standard implementation of the This service is a standard implementation of the

View File

@@ -24,9 +24,9 @@ Accesses the Content Services Search API.
- _maxResults:_ `number` - Maximum number of items in the list of results - _maxResults:_ `number` - Maximum number of items in the list of results
- _skipCount:_ `number` - Number of higher-ranked items to skip over in the list - _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 - **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)`>`<br/> - **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)`>`<br/>
Performs a search with its parameters supplied by a QueryBody object. Performs a search with its parameters supplied by a SearchRequest object.
- _queryBody:_ `QueryBody` - Object containing the search parameters - _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 - **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 ## Details

View File

@@ -15,18 +15,16 @@
* limitations under the License. * limitations under the License.
*/ */
import { QueryBody } from '@alfresco/js-api'; import { SearchRequest } from '@alfresco/js-api';
export interface SearchConfigurationInterface { 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 searchTerm Term text to search for
* @param maxResults Maximum number of search results to show in a page * @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 * @param skipCount The offset of the start of the page within the results list
* @returns Query body defined by the parameters * @returns Query body defined by the parameters
*/ */
generateQueryBody(searchTerm: string, maxResults: number, skipCount: number): QueryBody; generateQueryBody(searchTerm: string, maxResults: number, skipCount: number): SearchRequest;
} }

View File

@@ -28,7 +28,7 @@ import { CustomResourcesService } from '../document-list/services/custom-resourc
import { NodeEntryEvent, ShareDataRow } from '../document-list'; import { NodeEntryEvent, ShareDataRow } from '../document-list';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { SearchQueryBuilderService } from '../search'; 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 { SitesService } from '../common/services/sites.service';
import { NodesApiService } from '../common/services/nodes-api.service'; import { NodesApiService } from '../common/services/nodes-api.service';
@@ -168,16 +168,16 @@ describe('ContentNodeSelectorPanelComponent', () => {
expect(component.searchTerm).toEqual('search-term'); 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'); typeToSearchBox('search-term');
tick(debounceSearch); tick(debounceSearch);
fixture.detectChanges(); 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'); spyOn(component, 'clearSearch');
searchQueryBuilderService.userQuery = ''; searchQueryBuilderService.userQuery = '';
@@ -212,22 +212,22 @@ describe('ContentNodeSelectorPanelComponent', () => {
tick(debounceSearch); tick(debounceSearch);
fixture.detectChanges(); fixture.detectChanges();
expect(searchSpy).toHaveBeenCalledWith(mockQueryBody); expect(searchSpy).toHaveBeenCalledWith(mockSearchRequest);
})); }));
it('should the query include the show files filterQuery', fakeAsync(() => { it('should the query include the show files filterQuery', fakeAsync(() => {
component.showFilesInResult = true; component.showFilesInResult = true;
typeToSearchBox('search-term'); typeToSearchBox('search-term');
const expectedQueryBody = mockQueryBody; const expectedRequest = mockSearchRequest;
expectedQueryBody.filterQueries.push({ expectedRequest.filterQueries.push({
query: `TYPE:'cm:folder' OR TYPE:'cm:content'` query: `TYPE:'cm:folder' OR TYPE:'cm:content'`
}); });
tick(debounceSearch); tick(debounceSearch);
fixture.detectChanges(); 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(() => { 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); component.siteChanged({ entry: { guid: 'namek' } } as SiteEntry);
const expectedQueryBody = mockQueryBody; const expectedRequest = mockSearchRequest;
expectedQueryBody.filterQueries = [{ query: `ANCESTOR:'workspace://SpacesStore/namek'` }]; expectedRequest.filterQueries = [{ query: `ANCESTOR:'workspace://SpacesStore/namek'` }];
expect(searchSpy.calls.count()).toBe(2); 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(() => { 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); component.siteChanged({ entry: { guid: '-sites-' } } as SiteEntry);
const expectedQueryBodyWithSiteChange = mockQueryBody; const expectedRequest = mockSearchRequest;
expectedQueryBodyWithSiteChange.filterQueries = [ expectedRequest.filterQueries = [
{ {
query: `ANCESTOR:'workspace://SpacesStore/-sites-' OR ANCESTOR:'workspace://SpacesStore/123456testId' OR ANCESTOR:'workspace://SpacesStore/09876543testId'` 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).toHaveBeenCalled();
expect(searchSpy.calls.count()).toBe(2); expect(searchSpy.calls.count()).toBe(2);
expect(searchSpy).toHaveBeenCalledWith(mockQueryBody); expect(searchSpy).toHaveBeenCalledWith(mockSearchRequest);
expect(searchSpy).toHaveBeenCalledWith(expectedQueryBodyWithSiteChange); expect(searchSpy).toHaveBeenCalledWith(expectedRequest);
})); }));
it('should get the corresponding node ids on search when a known alias is selected from dropdown', fakeAsync(() => { 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'); typeToSearchBox('search-term');
tick(debounceSearch); tick(debounceSearch);
const expectedQueryBody = mockQueryBody; const expectedRequest = mockSearchRequest;
expectedQueryBody.filterQueries = [{ query: `ANCESTOR:'workspace://SpacesStore/my-root-id'` }]; 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 () => { it('should emit showingSearch event with true while searching', async () => {
@@ -476,10 +476,10 @@ describe('ContentNodeSelectorPanelComponent', () => {
component.restrictRootToCurrentFolderId = true; component.restrictRootToCurrentFolderId = true;
component.siteChanged({ entry: { guid: 'my-site-id' } } as SiteEntry); component.siteChanged({ entry: { guid: 'my-site-id' } } as SiteEntry);
const expectedQueryBodyWithSiteChange = mockQueryBody; const expectedRequest = mockSearchRequest;
expectedQueryBodyWithSiteChange.filterQueries = [{ query: `ANCESTOR:'workspace://SpacesStore/my-site-id'` }]; 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 () => { 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 () => { it('should set its loading state to true to perform a new search', async () => {
component.prepareDialogForNewSearch(mockQueryBody); component.prepareDialogForNewSearch(mockSearchRequest);
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable(); await fixture.whenStable();

View File

@@ -15,22 +15,13 @@
* limitations under the License. * limitations under the License.
*/ */
import { import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation, OnDestroy, Inject } from '@angular/core';
Component,
EventEmitter,
Input,
OnInit,
Output,
ViewChild,
ViewEncapsulation,
OnDestroy,
Inject
} from '@angular/core';
import { import {
HighlightDirective, HighlightDirective,
UserPreferencesService, UserPreferencesService,
UserPreferenceValues, UserPreferenceValues,
InfinitePaginationComponent, PaginatedComponent, InfinitePaginationComponent,
PaginatedComponent,
AppConfigService, AppConfigService,
DataSorting, DataSorting,
ShowHeaderMode ShowHeaderMode
@@ -39,7 +30,7 @@ import { NodesApiService } from '../common/services/nodes-api.service';
import { UploadService } from '../common/services/upload.service'; import { UploadService } from '../common/services/upload.service';
import { FileUploadCompleteEvent, FileUploadDeleteEvent } from '../common/events/file.event'; import { FileUploadCompleteEvent, FileUploadDeleteEvent } from '../common/events/file.event';
import { UntypedFormControl } from '@angular/forms'; 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 { DocumentListComponent } from '../document-list/components/document-list.component';
import { RowFilter } from '../document-list/data/row-filter.model'; import { RowFilter } from '../document-list/data/row-filter.model';
import { ImageResolver } from '../document-list/data/image-resolver.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'], styleUrls: ['./content-node-selector-panel.component.scss'],
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
host: { class: 'adf-content-node-selector-panel' }, host: { class: 'adf-content-node-selector-panel' },
providers: [{ providers: [
{
provide: SEARCH_QUERY_SERVICE_TOKEN, provide: SEARCH_QUERY_SERVICE_TOKEN,
useClass: SearchQueryBuilderService useClass: SearchQueryBuilderService
}] }
]
}) })
export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
DEFAULT_PAGINATION: Pagination = new Pagination({ DEFAULT_PAGINATION: Pagination = new Pagination({
maxItems: 25, maxItems: 25,
@@ -276,15 +268,16 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
private onDestroy$ = new Subject<boolean>(); private onDestroy$ = new Subject<boolean>();
constructor(private customResourcesService: CustomResourcesService, constructor(
private customResourcesService: CustomResourcesService,
@Inject(SEARCH_QUERY_SERVICE_TOKEN) public queryBuilderService: SearchQueryBuilderService, @Inject(SEARCH_QUERY_SERVICE_TOKEN) public queryBuilderService: SearchQueryBuilderService,
private userPreferencesService: UserPreferencesService, private userPreferencesService: UserPreferencesService,
private nodesApiService: NodesApiService, private nodesApiService: NodesApiService,
private uploadService: UploadService, private uploadService: UploadService,
private sitesService: SitesService, private sitesService: SitesService,
private appConfigService: AppConfigService, private appConfigService: AppConfigService,
private contentNodeSelectorPanelService: ContentNodeSelectorPanelService) { private contentNodeSelectorPanelService: ContentNodeSelectorPanelService
} ) {}
set chosenNode(value: Node[]) { set chosenNode(value: Node[]) {
this._chosenNode = value; this._chosenNode = value;
@@ -304,24 +297,17 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
} }
ngOnInit() { ngOnInit() {
this.searchInput.valueChanges this.searchInput.valueChanges.pipe(debounceTime(this.debounceSearch), takeUntil(this.onDestroy$)).subscribe((searchValue: string) => {
.pipe(
debounceTime(this.debounceSearch),
takeUntil(this.onDestroy$)
)
.subscribe((searchValue: string) => {
this.searchTerm = searchValue; this.searchTerm = searchValue;
this.queryBuilderService.userQuery = searchValue.length > 0 ? `${searchValue}*` : searchValue; this.queryBuilderService.userQuery = searchValue.length > 0 ? `${searchValue}*` : searchValue;
this.queryBuilderService.update(); this.queryBuilderService.update();
}); });
this.queryBuilderService.updated this.queryBuilderService.updated.pipe(takeUntil(this.onDestroy$)).subscribe((searchRequest) => {
.pipe(takeUntil(this.onDestroy$)) if (searchRequest) {
.subscribe((queryBody: QueryBody) => {
if (queryBody) {
this.hasValidQuery = true; this.hasValidQuery = true;
this.prepareDialogForNewSearch(queryBody); this.prepareDialogForNewSearch(searchRequest);
this.queryBuilderService.execute(queryBody); this.queryBuilderService.execute(searchRequest);
} else { } else {
this.hasValidQuery = false; this.hasValidQuery = false;
this.resetFolderToShow(); this.resetFolderToShow();
@@ -329,9 +315,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
} }
}); });
this.queryBuilderService.executed this.queryBuilderService.executed.pipe(takeUntil(this.onDestroy$)).subscribe((results: NodePaging) => {
.pipe(takeUntil(this.onDestroy$))
.subscribe((results: NodePaging) => {
if (this.hasValidQuery) { if (this.hasValidQuery) {
this.showSearchResults(results); this.showSearchResults(results);
} }
@@ -340,7 +324,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
this.userPreferencesService this.userPreferencesService
.select(UserPreferenceValues.PaginationSize) .select(UserPreferenceValues.PaginationSize)
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntil(this.onDestroy$))
.subscribe(pagSize => this.pageSize = pagSize); .subscribe((pagSize) => (this.pageSize = pagSize));
this.target = this.documentList; this.target = this.documentList;
this.folderIdToShow = this.currentFolderId; this.folderIdToShow = this.currentFolderId;
@@ -360,9 +344,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
this.resetPagination(); this.resetPagination();
this.setSearchScopeToNodes(); this.setSearchScopeToNodes();
this.documentList.$folderNode this.documentList.$folderNode.pipe(takeUntil(this.onDestroy$)).subscribe((currentNode: Node) => {
.pipe(takeUntil(this.onDestroy$))
.subscribe((currentNode: Node) => {
this.currentFolder.emit(currentNode); this.currentFolder.emit(currentNode);
}); });
@@ -384,10 +366,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
private onFileUploadEvent() { private onFileUploadEvent() {
this.uploadService.fileUploadComplete this.uploadService.fileUploadComplete
.pipe( .pipe(debounceTime(500), takeUntil(this.onDestroy$))
debounceTime(500),
takeUntil(this.onDestroy$)
)
.subscribe((fileUploadEvent: FileUploadCompleteEvent) => { .subscribe((fileUploadEvent: FileUploadCompleteEvent) => {
this.currentUploadBatch.push(fileUploadEvent.data); this.currentUploadBatch.push(fileUploadEvent.data);
if (!this.uploadService.isUploading()) { if (!this.uploadService.isUploading()) {
@@ -399,9 +378,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
} }
private onFileUploadDeletedEvent() { private onFileUploadDeletedEvent() {
this.uploadService.fileUploadDeleted this.uploadService.fileUploadDeleted.pipe(takeUntil(this.onDestroy$)).subscribe((deletedFileEvent: FileUploadDeleteEvent) => {
.pipe(takeUntil(this.onDestroy$))
.subscribe((deletedFileEvent: FileUploadDeleteEvent) => {
this.documentList.unselectRowFromNodeId(deletedFileEvent.file.data.entry.id); this.documentList.unselectRowFromNodeId(deletedFileEvent.file.data.entry.id);
this.documentList.reloadWithoutResettingSelection(); this.documentList.reloadWithoutResettingSelection();
}); });
@@ -424,19 +401,14 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
if (!filter) { if (!filter) {
filter = () => true; filter = () => true;
} }
this._rowFilter = (value: ShareDataRow, index: number, array: ShareDataRow[]) => filter(value, index, array) && this._rowFilter = (value: ShareDataRow, index: number, array: ShareDataRow[]) =>
!this.isExcludedSiteContent(value); filter(value, index, array) && !this.isExcludedSiteContent(value);
} }
private isExcludedSiteContent(row: ShareDataRow): boolean { private isExcludedSiteContent(row: ShareDataRow): boolean {
const entry = row.node.entry; const entry = row.node.entry;
if (this._excludeSiteContent && this._excludeSiteContent.length && if (this._excludeSiteContent && this._excludeSiteContent.length && entry && entry.properties && entry.properties['st:componentId']) {
entry && const excludedItem = this._excludeSiteContent.find((id: string) => entry.properties['st:componentId'] === id);
entry.properties &&
entry.properties['st:componentId']) {
const excludedItem = this._excludeSiteContent.find(
(id: string) => entry.properties['st:componentId'] === id
);
return !!excludedItem; return !!excludedItem;
} }
return false; return false;
@@ -472,8 +444,8 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
/** /**
* Prepares the dialog for a new search * Prepares the dialog for a new search
*/ */
prepareDialogForNewSearch(queryBody: QueryBody): void { prepareDialogForNewSearch(searchRequest: SearchRequest): void {
this.target = queryBody ? null : this.documentList; this.target = searchRequest ? null : this.documentList;
if (this.target) { if (this.target) {
this.infinitePaginationComponent.reset(); this.infinitePaginationComponent.reset();
} }
@@ -516,8 +488,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
let extraParentFiltering = ''; let extraParentFiltering = '';
if (this.customResourcesService.hasCorrespondingNodeIds(this.siteId)) { if (this.customResourcesService.hasCorrespondingNodeIds(this.siteId)) {
this.customResourcesService.getCorrespondingNodeIds(this.siteId) this.customResourcesService.getCorrespondingNodeIds(this.siteId).subscribe((nodeIds) => {
.subscribe((nodeIds) => {
if (nodeIds && nodeIds.length) { if (nodeIds && nodeIds.length) {
nodeIds nodeIds
.filter((id) => id !== this.siteId) .filter((id) => id !== this.siteId)
@@ -638,7 +609,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
onCurrentSelection(nodesEntries: NodeEntry[]): void { onCurrentSelection(nodesEntries: NodeEntry[]): void {
const validNodesEntity = nodesEntries.filter((node) => this.isSelectionValid(node.entry)); const validNodesEntity = nodesEntries.filter((node) => this.isSelectionValid(node.entry));
this.chosenNode = validNodesEntity.map((node) => 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) { setTitleIfCustomSite(site: SiteEntry) {

View File

@@ -15,9 +15,9 @@
* limitations under the License. * 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: {
query: '(search-term*)', query: '(search-term*)',
language: 'afts' language: 'afts'
@@ -38,4 +38,4 @@ export const mockQueryBody: QueryBody = {
}, },
highlight: null, highlight: null,
facetFormat: 'V2' facetFormat: 'V2'
} as QueryBody; } as SearchRequest;

View File

@@ -17,7 +17,7 @@
import { Component, ViewChild } from '@angular/core'; import { Component, ViewChild } from '@angular/core';
import { SearchComponent } from '../search/components/search.component'; import { SearchComponent } from '../search/components/search.component';
import { QueryBody, ResultSetPaging } from '@alfresco/js-api'; import { SearchRequest, ResultSetPaging } from '@alfresco/js-api';
const entryItem = { const entryItem = {
entry: { entry: {
@@ -55,27 +55,19 @@ const entryDifferentItem = {
export const result = new ResultSetPaging({ export const result = new ResultSetPaging({
list: { list: {
entries: [ entries: [entryItem]
entryItem
]
} }
}); });
export const differentResult = new ResultSetPaging({ export const differentResult = new ResultSetPaging({
list: { list: {
entries: [ entries: [entryDifferentItem]
entryDifferentItem
]
} }
}); });
export const results = { export const results = {
list: { list: {
entries: [ entries: [entryItem, entryItem, entryItem]
entryItem,
entryItem,
entryItem
]
} }
}; };
@@ -118,12 +110,16 @@ export const errorJson = {
@Component({ @Component({
template: ` template: `
<adf-search [searchTerm]="searchedWord" [maxResults]="maxResults" <adf-search
[searchTerm]="searchedWord"
[maxResults]="maxResults"
(error)="showSearchResult('ERROR')" (error)="showSearchResult('ERROR')"
(success)="showSearchResult('success')" #search> (success)="showSearchResult('success')"
#search
>
<ng-template let-data> <ng-template let-data>
<ul id="autocomplete-search-result-list"> <ul id="autocomplete-search-result-list">
<li *ngFor="let item of data?.list?.entries; let idx = index" (click)="elementClicked(item)"> <li *ngFor="let item of data?.list?.entries; let idx = index" (click)="elementClicked()">
<div id="result_option_{{ idx }}"> <div id="result_option_{{ idx }}">
<span>{{ item?.entry.name }}</span> <span>{{ item?.entry.name }}</span>
</div> </div>
@@ -134,19 +130,16 @@ export const errorJson = {
<span id="component-result-message">{{ message }}</span> <span id="component-result-message">{{ message }}</span>
` `
}) })
export class SimpleSearchTestComponent { export class SimpleSearchTestComponent {
@ViewChild('search', { static: true }) @ViewChild('search', { static: true })
search: SearchComponent; search: SearchComponent;
message: string = ''; message: string = '';
searchedWord = ''; searchedWord = '';
maxResults: number = 5; maxResults: number = 5;
searchNode: QueryBody; searchNode: SearchRequest;
constructor() { constructor() {}
}
showSearchResult(event: any) { showSearchResult(event: any) {
this.message = event; this.message = event;
@@ -160,7 +153,7 @@ export const errorJson = {
this.searchedWord = str; this.searchedWord = str;
} }
setSearchNodeTo(searchNode: QueryBody) { setSearchNodeTo(searchNode: SearchRequest) {
this.searchNode = searchNode; this.searchNode = searchNode;
} }
@@ -171,5 +164,4 @@ export const errorJson = {
forceHidePanel() { forceHidePanel() {
this.search.hidePanel(); this.search.hidePanel();
} }
} }

View File

@@ -15,7 +15,7 @@
* limitations under the License. * 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 { Injectable, Optional, Inject, InjectionToken } from '@angular/core';
import { SearchConfigurationInterface } from '../../../common/interfaces/search-configuration.interface'; import { SearchConfigurationInterface } from '../../../common/interfaces/search-configuration.interface';
@@ -26,15 +26,14 @@ export interface QueryProvider {
@Injectable() @Injectable()
export class SearchPermissionConfigurationService implements SearchConfigurationInterface { export class SearchPermissionConfigurationService implements SearchConfigurationInterface {
constructor( constructor(
@Optional() @Optional()
@Inject(SEARCH_QUERY_TOKEN) @Inject(SEARCH_QUERY_TOKEN)
private queryProvider: QueryProvider) { private queryProvider: QueryProvider
} ) {}
public generateQueryBody(searchTerm: string, maxResults: number, skipCount: number): QueryBody { public generateQueryBody(searchTerm: string, maxResults: number, skipCount: number): SearchRequest {
const defaultQueryBody: QueryBody = { return new SearchRequest({
query: { query: {
query: this.getQuery(searchTerm) query: this.getQuery(searchTerm)
}, },
@@ -45,17 +44,15 @@ export class SearchPermissionConfigurationService implements SearchConfiguration
}, },
filterQueries: [ filterQueries: [
/* eslint-disable-next-line */ /* eslint-disable-next-line */
{ query: "TYPE:'cm:authority'" }] { query: "TYPE:'cm:authority'" }
}; ]
});
return defaultQueryBody;
} }
private getQuery(searchTerm: string) { private getQuery(searchTerm: string) {
let query: string; let query: string;
if (this.queryProvider && this.queryProvider.query) { if (this.queryProvider && this.queryProvider.query) {
query = this.queryProvider.query.replace( query = this.queryProvider.query.replace(new RegExp(/\${([^}]+)}/g), searchTerm);
new RegExp(/\${([^}]+)}/g), searchTerm);
} else { } else {
query = `(email:*${searchTerm}* OR firstName:*${searchTerm}* OR lastName:*${searchTerm}* OR displayName:*${searchTerm}* OR authorityName:*${searchTerm}* OR authorityDisplayName:*${searchTerm}*) AND ANAME:(\"0/APP.DEFAULT\")`; query = `(email:*${searchTerm}* OR firstName:*${searchTerm}* OR lastName:*${searchTerm}* OR displayName:*${searchTerm}* OR authorityName:*${searchTerm}* OR authorityDisplayName:*${searchTerm}*) AND ANAME:(\"0/APP.DEFAULT\")`;
} }

View File

@@ -15,21 +15,10 @@
* limitations under the License. * limitations under the License.
*/ */
import { import { AlfrescoApiService, TranslationService } from '@alfresco/adf-core';
AlfrescoApiService,
TranslationService
} from '@alfresco/adf-core';
import { NodesApiService } from '../../common/services/nodes-api.service'; import { NodesApiService } from '../../common/services/nodes-api.service';
import { EcmUserModel } from '../../common/models/ecm-user.model'; import { EcmUserModel } from '../../common/models/ecm-user.model';
import { import { Group, GroupMemberEntry, GroupMemberPaging, GroupsApi, Node, PathElement, PermissionElement, SearchRequest } from '@alfresco/js-api';
Group,
GroupMemberEntry,
GroupMemberPaging, GroupsApi,
Node,
PathElement,
PermissionElement,
QueryBody
} from '@alfresco/js-api';
import { SearchService } from '../../search/services/search.service'; import { SearchService } from '../../search/services/search.service';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { forkJoin, from, Observable, of, throwError } from 'rxjs'; import { forkJoin, from, Observable, of, throwError } from 'rxjs';
@@ -41,18 +30,18 @@ import { RoleModel } from '../models/role.model';
providedIn: 'root' providedIn: 'root'
}) })
export class NodePermissionService { export class NodePermissionService {
private _groupsApi: GroupsApi; private _groupsApi: GroupsApi;
get groupsApi(): GroupsApi { get groupsApi(): GroupsApi {
this._groupsApi = this._groupsApi ?? new GroupsApi(this.apiService.getInstance()); this._groupsApi = this._groupsApi ?? new GroupsApi(this.apiService.getInstance());
return this._groupsApi; return this._groupsApi;
} }
constructor(private apiService: AlfrescoApiService, constructor(
private apiService: AlfrescoApiService,
private searchApiService: SearchService, private searchApiService: SearchService,
private nodeService: NodesApiService, private nodeService: NodesApiService,
private translation: TranslationService) { private translation: TranslationService
} ) {}
/** /**
* Gets a list of roles for the current node. * Gets a list of roles for the current node.
@@ -61,9 +50,8 @@ export class NodePermissionService {
* @returns Array of strings representing the roles * @returns Array of strings representing the roles
*/ */
getNodeRoles(node: Node): Observable<string[]> { getNodeRoles(node: Node): Observable<string[]> {
const retrieveSiteQueryBody: QueryBody = this.buildRetrieveSiteQueryBody(node.path.elements); const searchRequest = this.buildRetrieveSiteQueryBody(node.path.elements);
return this.searchApiService.searchByQueryBody(retrieveSiteQueryBody) return this.searchApiService.searchByQueryBody(searchRequest).pipe(
.pipe(
switchMap((siteNodeList: any) => { switchMap((siteNodeList: any) => {
if (siteNodeList.list.entries.length > 0) { if (siteNodeList.list.entries.length > 0) {
const siteName = siteNodeList.list.entries[0].entry.name; const siteName = siteNodeList.list.entries[0].entry.name;
@@ -75,6 +63,10 @@ export class NodePermissionService {
); );
} }
/**
* Get permissions for a given node
* @param node Node to check permissions for
*/
getNodePermissions(node: Node): PermissionDisplayModel[] { getNodePermissions(node: Node): PermissionDisplayModel[] {
const result: PermissionDisplayModel[] = []; const result: PermissionDisplayModel[] = [];
@@ -121,9 +113,7 @@ export class NodePermissionService {
* @returns Node with updated permissions * @returns Node with updated permissions
*/ */
updateNodePermissions(nodeId: string, permissionList: PermissionElement[]): Observable<Node> { updateNodePermissions(nodeId: string, permissionList: PermissionElement[]): Observable<Node> {
return this.nodeService.getNode(nodeId).pipe( return this.nodeService.getNode(nodeId).pipe(switchMap((node) => this.updateLocallySetPermissions(node, permissionList)));
switchMap((node) => this.updateLocallySetPermissions(node, permissionList))
);
} }
/** /**
@@ -138,7 +128,9 @@ export class NodePermissionService {
const permissionList = permissions; const permissionList = permissions;
const duplicatedPermissions = this.getDuplicatedPermissions(node.permissions.locallySet, permissionList); const duplicatedPermissions = this.getDuplicatedPermissions(node.permissions.locallySet, permissionList);
if (duplicatedPermissions.length > 0) { 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 }); const duplicatePermissionMessage: string = this.translation.instant('PERMISSION_MANAGER.ERROR.DUPLICATE-PERMISSION', { list });
return throwError(duplicatePermissionMessage); return throwError(duplicatePermissionMessage);
} }
@@ -160,9 +152,11 @@ export class NodePermissionService {
} }
private isEqualPermission(oldPermission: PermissionElement, newPermission: PermissionElement): boolean { private isEqualPermission(oldPermission: PermissionElement, newPermission: PermissionElement): boolean {
return oldPermission.accessStatus === newPermission.accessStatus && return (
oldPermission.accessStatus === newPermission.accessStatus &&
oldPermission.authorityId === newPermission.authorityId && oldPermission.authorityId === newPermission.authorityId &&
oldPermission.name === newPermission.name; oldPermission.name === newPermission.name
);
} }
/** /**
@@ -187,8 +181,7 @@ export class NodePermissionService {
private getGroupMembersBySiteName(siteName: string): Observable<string[]> { private getGroupMembersBySiteName(siteName: string): Observable<string[]> {
const groupName = 'GROUP_site_' + siteName; const groupName = 'GROUP_site_' + siteName;
return this.getGroupMemberByGroupName(groupName) return this.getGroupMemberByGroupName(groupName).pipe(
.pipe(
map((groupMemberPaging: GroupMemberPaging) => { map((groupMemberPaging: GroupMemberPaging) => {
const displayResult: string[] = []; const displayResult: string[] = [];
groupMemberPaging.list.entries.forEach((member: GroupMemberEntry) => { groupMemberPaging.list.entries.forEach((member: GroupMemberEntry) => {
@@ -214,7 +207,7 @@ export class NodePermissionService {
return displayName.replace(siteName + '_', ''); return displayName.replace(siteName + '_', '');
} }
private buildRetrieveSiteQueryBody(nodePath: PathElement[]): QueryBody { private buildRetrieveSiteQueryBody(nodePath: PathElement[]): SearchRequest {
const pathNames = nodePath.map((node: PathElement) => 'name: "' + node.name + '"'); const pathNames = nodePath.map((node: PathElement) => 'name: "' + node.name + '"');
const builtPathNames = pathNames.join(' OR '); const builtPathNames = pathNames.join(' OR ');
@@ -229,8 +222,7 @@ export class NodePermissionService {
include: ['aspectNames', 'properties'], include: ['aspectNames', 'properties'],
filterQueries: [ filterQueries: [
{ {
query: query: `TYPE:'st:site'`
`TYPE:'st:site'`
} }
] ]
}; };
@@ -302,15 +294,15 @@ export class NodePermissionService {
*/ */
getNodeWithRoles(nodeId: string): Observable<{ node: Node; roles: RoleModel[] }> { getNodeWithRoles(nodeId: string): Observable<{ node: Node; roles: RoleModel[] }> {
return this.nodeService.getNode(nodeId).pipe( return this.nodeService.getNode(nodeId).pipe(
switchMap(node => forkJoin({ switchMap((node) =>
forkJoin({
node: of(node), node: of(node),
roles: this.getNodeRoles(node) roles: this.getNodeRoles(node).pipe(
.pipe(
catchError(() => of(node.permissions?.settable)), catchError(() => of(node.permissions?.settable)),
map(_roles => _roles.map(role => ({ role, label: role })) map((_roles) => _roles.map((role) => ({ role, label: role })))
) )
})
) )
}))
); );
} }

View File

@@ -19,7 +19,7 @@ import { Injectable } from '@angular/core';
import { Subject, Observable, from, ReplaySubject } from 'rxjs'; import { Subject, Observable, from, ReplaySubject } from 'rxjs';
import { AlfrescoApiService, AppConfigService } from '@alfresco/adf-core'; import { AlfrescoApiService, AppConfigService } from '@alfresco/adf-core';
import { import {
QueryBody, SearchRequest,
RequestFacetFields, RequestFacetFields,
RequestSortDefinitionInner, RequestSortDefinitionInner,
ResultSetPaging, ResultSetPaging,
@@ -41,7 +41,6 @@ import { SearchForm } from '../models/search-form.interface';
providedIn: 'root' providedIn: 'root'
}) })
export abstract class BaseQueryBuilderService { export abstract class BaseQueryBuilderService {
private _searchApi: SearchApi; private _searchApi: SearchApi;
get searchApi(): SearchApi { get searchApi(): SearchApi {
this._searchApi = this._searchApi ?? new SearchApi(this.alfrescoApiService.getInstance()); this._searchApi = this._searchApi ?? new SearchApi(this.alfrescoApiService.getInstance());
@@ -52,7 +51,7 @@ export abstract class BaseQueryBuilderService {
configUpdated = new Subject<SearchConfiguration>(); configUpdated = new Subject<SearchConfiguration>();
/* Stream that emits the query before search whenever user search */ /* Stream that emits the query before search whenever user search */
updated = new Subject<QueryBody>(); updated = new Subject<SearchRequest>();
/* Stream that emits the results whenever user search */ /* Stream that emits the results whenever user search */
executed = new Subject<ResultSetPaging>(); executed = new Subject<ResultSetPaging>();
@@ -152,12 +151,14 @@ export abstract class BaseQueryBuilderService {
selected: this.selectedConfiguration !== undefined ? index === this.selectedConfiguration : configuration.default selected: this.selectedConfiguration !== undefined ? index === this.selectedConfiguration : configuration.default
})); }));
} else if (!!configurations) { } else if (!!configurations) {
return [{ return [
{
index: 0, index: 0,
name: configurations.name || 'SEARCH.UNKNOWN_CONFIGURATION', name: configurations.name || 'SEARCH.UNKNOWN_CONFIGURATION',
default: true, default: true,
selected: true selected: true
}]; }
];
} }
return []; return [];
} }
@@ -165,9 +166,7 @@ export abstract class BaseQueryBuilderService {
private setUpSearchConfiguration(currentConfiguration: SearchConfiguration) { private setUpSearchConfiguration(currentConfiguration: SearchConfiguration) {
if (currentConfiguration) { if (currentConfiguration) {
this.config = JSON.parse(JSON.stringify(currentConfiguration)); this.config = JSON.parse(JSON.stringify(currentConfiguration));
this.categories = (this.config.categories || []).filter( this.categories = (this.config.categories || []).filter((category) => category.enabled);
category => category.enabled
);
this.filterQueries = this.config.filterQueries || []; this.filterQueries = this.config.filterQueries || [];
this.userFacetBuckets = {}; this.userFacetBuckets = {};
if (this.config.sorting) { if (this.config.sorting) {
@@ -213,8 +212,7 @@ export abstract class BaseQueryBuilderService {
removeUserFacetBucket(field: string, bucket: FacetFieldBucket) { removeUserFacetBucket(field: string, bucket: FacetFieldBucket) {
if (field && bucket) { if (field && bucket) {
const buckets = this.userFacetBuckets[field] || []; const buckets = this.userFacetBuckets[field] || [];
this.userFacetBuckets[field] = buckets this.userFacetBuckets[field] = buckets.filter((facetBucket) => facetBucket.label !== bucket.label);
.filter((facetBucket) => facetBucket.label !== bucket.label);
} }
} }
@@ -239,8 +237,7 @@ export abstract class BaseQueryBuilderService {
*/ */
removeFilterQuery(query: string): void { removeFilterQuery(query: string): void {
if (query) { if (query) {
this.filterQueries = this.filterQueries this.filterQueries = this.filterQueries.filter((filterQuery) => filterQuery.query !== query);
.filter((filterQuery) => filterQuery.query !== query);
} }
} }
@@ -289,7 +286,7 @@ export abstract class BaseQueryBuilderService {
/** /**
* Builds the current query and triggers the `updated` event. * Builds the current query and triggers the `updated` event.
*/ */
update(queryBody?: QueryBody): void { update(queryBody?: SearchRequest): void {
const query = queryBody ? queryBody : this.buildQuery(); const query = queryBody ? queryBody : this.buildQuery();
this.updated.next(query); this.updated.next(query);
} }
@@ -299,7 +296,7 @@ export abstract class BaseQueryBuilderService {
* *
* @returns Nothing * @returns Nothing
*/ */
async execute(queryBody?: QueryBody) { async execute(queryBody?: SearchRequest) {
try { try {
const query = queryBody ? queryBody : this.buildQuery(); const query = queryBody ? queryBody : this.buildQuery();
if (query) { if (query) {
@@ -320,7 +317,7 @@ export abstract class BaseQueryBuilderService {
} }
} }
search(queryBody: QueryBody): Observable<ResultSetPaging> { search(queryBody: SearchRequest): Observable<ResultSetPaging> {
const promise = this.searchApi.search(queryBody); const promise = this.searchApi.search(queryBody);
promise.then((resultSetPaging) => { promise.then((resultSetPaging) => {
@@ -335,7 +332,7 @@ export abstract class BaseQueryBuilderService {
* *
* @returns The finished query * @returns The finished query
*/ */
buildQuery(): QueryBody { buildQuery(): SearchRequest {
const query = this.getFinalQuery(); const query = this.getFinalQuery();
const include = this.config.include || []; const include = this.config.include || [];
@@ -344,8 +341,7 @@ export abstract class BaseQueryBuilderService {
} }
if (query) { if (query) {
const result: SearchRequest = {
const result: QueryBody = {
query: { query: {
query, query,
language: 'afts' language: 'afts'
@@ -362,7 +358,7 @@ export abstract class BaseQueryBuilderService {
}; };
if (this.scope) { if (this.scope) {
result['scope'] = this.scope; result.scope = this.scope;
} }
result['facetFormat'] = 'V2'; result['facetFormat'] = 'V2';
@@ -412,10 +408,7 @@ export abstract class BaseQueryBuilderService {
* @returns True if defined, false otherwise * @returns True if defined, false otherwise
*/ */
get hasFacetQueries(): boolean { get hasFacetQueries(): boolean {
if (this.config if (this.config && this.config.facetQueries && this.config.facetQueries.queries && this.config.facetQueries.queries.length > 0) {
&& this.config.facetQueries
&& this.config.facetQueries.queries
&& this.config.facetQueries.queries.length > 0) {
return true; return true;
} }
return false; return false;
@@ -427,11 +420,7 @@ export abstract class BaseQueryBuilderService {
* @returns True if defined, false otherwise * @returns True if defined, false otherwise
*/ */
get hasFacetIntervals(): boolean { get hasFacetIntervals(): boolean {
return this.config return this.config && this.config.facetIntervals && this.config.facetIntervals.intervals && this.config.facetIntervals.intervals.length > 0;
&& this.config.facetIntervals
&& this.config.facetIntervals.intervals
&& this.config.facetIntervals.intervals.length > 0;
} }
get hasFacetHighlight(): boolean { get hasFacetHighlight(): boolean {
@@ -439,11 +428,14 @@ export abstract class BaseQueryBuilderService {
} }
protected get sort(): RequestSortDefinitionInner[] { protected get sort(): RequestSortDefinitionInner[] {
return this.sorting.map((def) => new RequestSortDefinitionInner({ return this.sorting.map(
(def) =>
new RequestSortDefinitionInner({
type: def.type, type: def.type,
field: def.field, field: def.field,
ascending: def.ascending ascending: def.ascending
})); })
);
} }
protected get facetQueries(): FacetQuery[] { protected get facetQueries(): FacetQuery[] {
@@ -462,17 +454,23 @@ export abstract class BaseQueryBuilderService {
const configIntervals = this.config.facetIntervals; const configIntervals = this.config.facetIntervals;
return { return {
intervals: configIntervals.intervals.map((interval) => ({ intervals: configIntervals.intervals.map(
(interval) =>
({
label: this.getSupportedLabel(interval.label), label: this.getSupportedLabel(interval.label),
field: interval.field, field: interval.field,
sets: interval.sets.map((set) => ({ sets: interval.sets.map(
(set) =>
({
label: this.getSupportedLabel(set.label), label: this.getSupportedLabel(set.label),
start: set.start, start: set.start,
end: set.end, end: set.end,
startInclusive: set.startInclusive, startInclusive: set.startInclusive,
endInclusive: set.endInclusive endInclusive: set.endInclusive
} as any)) } as any)
} as any)) )
} as any)
)
}; };
} }
@@ -496,9 +494,7 @@ export abstract class BaseQueryBuilderService {
} }
}); });
let result = [this.userQuery, query] let result = [this.userQuery, query].filter((entry) => entry).join(' AND ');
.filter((entry) => entry)
.join(' AND ');
if (this.userFacetBuckets) { if (this.userFacetBuckets) {
Object.keys(this.userFacetBuckets).forEach((key) => { Object.keys(this.userFacetBuckets).forEach((key) => {
@@ -523,14 +519,17 @@ export abstract class BaseQueryBuilderService {
if (facetFields && facetFields.length > 0) { if (facetFields && facetFields.length > 0) {
return { return {
facets: facetFields.map((facet) => ({ facets: facetFields.map(
(facet) =>
({
field: facet.field, field: facet.field,
mincount: facet.mincount, mincount: facet.mincount,
label: this.getSupportedLabel(facet.label), label: this.getSupportedLabel(facet.label),
limit: facet.limit, limit: facet.limit,
offset: facet.offset, offset: facet.offset,
prefix: facet.prefix prefix: facet.prefix
} as any)) } as any)
)
}; };
} }

View File

@@ -16,24 +16,23 @@
*/ */
import { Injectable } from '@angular/core'; 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'; import { SearchConfigurationInterface } from '../../common/interfaces/search-configuration.interface';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class SearchConfigurationService implements SearchConfigurationInterface { 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 searchTerm Term text to search for
* @param maxResults Maximum number of search results to show in a page * @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 * @param skipCount The offset of the start of the page within the results list
* @returns Query body defined by the parameters * @returns Query body defined by the parameters
*/ */
generateQueryBody(searchTerm: string, maxResults: number, skipCount: number): QueryBody { generateQueryBody(searchTerm: string, maxResults: number, skipCount: number): SearchRequest {
const defaultQueryBody: QueryBody = { return new SearchRequest({
query: { query: {
query: searchTerm ? `'${searchTerm}*' OR name:'${searchTerm}*'` : searchTerm query: searchTerm ? `'${searchTerm}*' OR name:'${searchTerm}*'` : searchTerm
}, },
@@ -42,11 +41,7 @@ export class SearchConfigurationService implements SearchConfigurationInterface
maxItems: maxResults, maxItems: maxResults,
skipCount skipCount
}, },
filterQueries: [ filterQueries: [{ query: `TYPE:'cm:folder' OR TYPE:'cm:content'` }, { query: 'NOT cm:creator:System' }]
{ query: `TYPE:'cm:folder' OR TYPE:'cm:content'` }, });
{ query: 'NOT cm:creator:System' }]
};
return defaultQueryBody;
} }
} }

View File

@@ -23,7 +23,6 @@ import { TestBed } from '@angular/core/testing';
import { ContentTestingModule } from '../../testing/content.testing.module'; import { ContentTestingModule } from '../../testing/content.testing.module';
describe('SearchQueryBuilder', () => { describe('SearchQueryBuilder', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ContentTestingModule] imports: [ContentTestingModule]
@@ -38,14 +37,8 @@ describe('SearchQueryBuilder', () => {
it('should reset to defaults', () => { it('should reset to defaults', () => {
const config: SearchConfiguration = { const config: SearchConfiguration = {
categories: [ categories: [{ id: 'cat1', enabled: true } as any, { id: 'cat2', enabled: true } as any],
{ id: 'cat1', enabled: true } as any, filterQueries: [{ query: 'query1' }, { query: 'query2' }]
{ id: 'cat2', enabled: true } as any
],
filterQueries: [
{ query: 'query1' },
{ query: 'query2' }
]
}; };
const alfrescoApiService = TestBed.inject(AlfrescoApiService); const alfrescoApiService = TestBed.inject(AlfrescoApiService);
const builder = new SearchQueryBuilderService(buildConfig(config), alfrescoApiService); const builder = new SearchQueryBuilderService(buildConfig(config), alfrescoApiService);
@@ -86,11 +79,7 @@ describe('SearchQueryBuilder', () => {
it('should use only enabled categories', () => { it('should use only enabled categories', () => {
const config: SearchConfiguration = { const config: SearchConfiguration = {
categories: [ categories: [{ id: 'cat1', enabled: true } as any, { id: 'cat2', enabled: false } as any, { id: 'cat3', enabled: true } as any]
{ id: 'cat1', enabled: true } as any,
{ id: 'cat2', enabled: false } as any,
{ id: 'cat3', enabled: true } as any
]
}; };
const alfrescoApiService = TestBed.inject(AlfrescoApiService); const alfrescoApiService = TestBed.inject(AlfrescoApiService);
@@ -104,10 +93,7 @@ describe('SearchQueryBuilder', () => {
it('should fetch filter queries from config', () => { it('should fetch filter queries from config', () => {
const config: SearchConfiguration = { const config: SearchConfiguration = {
categories: [], categories: [],
filterQueries: [ filterQueries: [{ query: 'query1' }, { query: 'query2' }]
{ query: 'query1' },
{ query: 'query2' }
]
}; };
const alfrescoApiService = TestBed.inject(AlfrescoApiService); const alfrescoApiService = TestBed.inject(AlfrescoApiService);
const builder = new SearchQueryBuilderService(buildConfig(config), alfrescoApiService); const builder = new SearchQueryBuilderService(buildConfig(config), alfrescoApiService);
@@ -202,9 +188,7 @@ describe('SearchQueryBuilder', () => {
const config: SearchConfiguration = { const config: SearchConfiguration = {
categories: [], categories: [],
facetQueries: { facetQueries: {
queries: [ queries: [{ query: 'q1', label: 'query1' }]
{ query: 'q1', label: 'query1' }
]
} }
}; };
const alfrescoApiService = TestBed.inject(AlfrescoApiService); const alfrescoApiService = TestBed.inject(AlfrescoApiService);
@@ -259,9 +243,7 @@ describe('SearchQueryBuilder', () => {
const config: SearchConfiguration = { const config: SearchConfiguration = {
categories: [], categories: [],
facetFields: { facetFields: {
fields: [ fields: [{ field: 'content.size', mincount: 1, label: 'Label with spaces' }]
{ field: 'content.size', mincount: 1, label: 'Label with spaces' }
]
} }
}; };
const alfrescoApiService = TestBed.inject(AlfrescoApiService); const alfrescoApiService = TestBed.inject(AlfrescoApiService);
@@ -275,9 +257,7 @@ describe('SearchQueryBuilder', () => {
it('should require a query fragment to build query', () => { it('should require a query fragment to build query', () => {
const config: SearchConfiguration = { const config: SearchConfiguration = {
categories: [ categories: [{ id: 'cat1', enabled: true } as any]
{ id: 'cat1', enabled: true } as any
]
}; };
const alfrescoApiService = TestBed.inject(AlfrescoApiService); const alfrescoApiService = TestBed.inject(AlfrescoApiService);
@@ -290,9 +270,7 @@ describe('SearchQueryBuilder', () => {
it('should build query with single fragment', () => { it('should build query with single fragment', () => {
const config: SearchConfiguration = { const config: SearchConfiguration = {
categories: [ categories: [{ id: 'cat1', enabled: true } as any]
{ id: 'cat1', enabled: true } as any
]
}; };
const alfrescoApiService = TestBed.inject(AlfrescoApiService); const alfrescoApiService = TestBed.inject(AlfrescoApiService);
@@ -306,10 +284,7 @@ describe('SearchQueryBuilder', () => {
it('should build query with multiple fragments', () => { it('should build query with multiple fragments', () => {
const config: SearchConfiguration = { const config: SearchConfiguration = {
categories: [ categories: [{ id: 'cat1', enabled: true } as any, { id: 'cat2', enabled: true } as any]
{ id: 'cat1', enabled: true } as any,
{ id: 'cat2', enabled: true } as any
]
}; };
const alfrescoApiService = TestBed.inject(AlfrescoApiService); const alfrescoApiService = TestBed.inject(AlfrescoApiService);
@@ -319,18 +294,13 @@ describe('SearchQueryBuilder', () => {
builder.queryFragments['cat2'] = 'NOT cm:creator:System'; builder.queryFragments['cat2'] = 'NOT cm:creator:System';
const compiled = builder.buildQuery(); const compiled = builder.buildQuery();
expect(compiled.query.query).toBe( expect(compiled.query.query).toBe('(cm:name:test) AND (NOT cm:creator:System)');
'(cm:name:test) AND (NOT cm:creator:System)'
);
}); });
it('should build query with custom fields', () => { it('should build query with custom fields', () => {
const config: SearchConfiguration = { const config: SearchConfiguration = {
fields: ['field1', 'field2'], fields: ['field1', 'field2'],
categories: [ categories: [{ id: 'cat1', enabled: true } as any, { id: 'cat2', enabled: true } as any]
{ id: 'cat1', enabled: true } as any,
{ id: 'cat2', enabled: true } as any
]
}; };
const alfrescoApiService = TestBed.inject(AlfrescoApiService); const alfrescoApiService = TestBed.inject(AlfrescoApiService);
@@ -345,10 +315,7 @@ describe('SearchQueryBuilder', () => {
it('should build query with empty custom fields', () => { it('should build query with empty custom fields', () => {
const config: SearchConfiguration = { const config: SearchConfiguration = {
fields: [], fields: [],
categories: [ categories: [{ id: 'cat1', enabled: true } as any, { id: 'cat2', enabled: true } as any]
{ id: 'cat1', enabled: true } as any,
{ id: 'cat2', enabled: true } as any
]
}; };
const alfrescoApiService = TestBed.inject(AlfrescoApiService); const alfrescoApiService = TestBed.inject(AlfrescoApiService);
@@ -362,9 +329,7 @@ describe('SearchQueryBuilder', () => {
it('should build query with custom filter queries', () => { it('should build query with custom filter queries', () => {
const config: SearchConfiguration = { const config: SearchConfiguration = {
categories: [ categories: [{ id: 'cat1', enabled: true } as any]
{ id: 'cat1', enabled: true } as any
]
}; };
const alfrescoApiService = TestBed.inject(AlfrescoApiService); const alfrescoApiService = TestBed.inject(AlfrescoApiService);
@@ -373,20 +338,14 @@ describe('SearchQueryBuilder', () => {
builder.addFilterQuery('query1'); builder.addFilterQuery('query1');
const compiled = builder.buildQuery(); const compiled = builder.buildQuery();
expect(compiled.filterQueries).toEqual( expect(compiled.filterQueries).toEqual([{ query: 'query1' }]);
[{ query: 'query1' }]
);
}); });
it('should build query with custom facet queries', () => { it('should build query with custom facet queries', () => {
const config: SearchConfiguration = { const config: SearchConfiguration = {
categories: [ categories: [{ id: 'cat1', enabled: true } as any],
{ id: 'cat1', enabled: true } as any
],
facetQueries: { facetQueries: {
queries: [ queries: [{ query: 'q1', label: 'q2', group: 'group-name' }]
{ query: 'q1', label: 'q2', group: 'group-name' }
]
} }
}; };
const alfrescoApiService = TestBed.inject(AlfrescoApiService); const alfrescoApiService = TestBed.inject(AlfrescoApiService);
@@ -400,9 +359,7 @@ describe('SearchQueryBuilder', () => {
it('should build query with custom facet fields', () => { it('should build query with custom facet fields', () => {
const config: SearchConfiguration = { const config: SearchConfiguration = {
categories: [ categories: [{ id: 'cat1', enabled: true } as any],
{ id: 'cat1', enabled: true } as any
],
facetFields: { facetFields: {
fields: [ fields: [
{ field: 'field1', label: 'field1', mincount: 1, limit: null, offset: 0, prefix: null }, { field: 'field1', label: 'field1', mincount: 1, limit: null, offset: 0, prefix: null },
@@ -430,9 +387,7 @@ describe('SearchQueryBuilder', () => {
}; };
const config: SearchConfiguration = { const config: SearchConfiguration = {
categories: [ categories: [{ id: 'cat1', enabled: true } as any],
{ id: 'cat1', enabled: true } as any
],
facetFields: { facetFields: {
fields: [ fields: [
{ {
@@ -468,9 +423,7 @@ describe('SearchQueryBuilder', () => {
it('should build query with custom facet intervals', () => { it('should build query with custom facet intervals', () => {
const config: SearchConfiguration = { const config: SearchConfiguration = {
categories: [ categories: [{ id: 'cat1', enabled: true } as any],
{ id: 'cat1', enabled: true } as any
],
facetIntervals: { facetIntervals: {
intervals: [ intervals: [
{ {
@@ -516,9 +469,7 @@ describe('SearchQueryBuilder', () => {
}; };
const config: SearchConfiguration = { const config: SearchConfiguration = {
categories: [ categories: [{ id: 'cat1', enabled: true } as any],
{ id: 'cat1', enabled: true } as any
],
facetIntervals: { facetIntervals: {
intervals: [ intervals: [
{ {
@@ -557,10 +508,7 @@ describe('SearchQueryBuilder', () => {
it('should build query with sorting', () => { it('should build query with sorting', () => {
const config: SearchConfiguration = { const config: SearchConfiguration = {
fields: [], fields: [],
categories: [ categories: [{ id: 'cat1', enabled: true } as any, { id: 'cat2', enabled: true } as any]
{ id: 'cat1', enabled: true } as any,
{ id: 'cat2', enabled: true } as any
]
}; };
const alfrescoApiService = TestBed.inject(AlfrescoApiService); const alfrescoApiService = TestBed.inject(AlfrescoApiService);
const builder = new SearchQueryBuilderService(buildConfig(config), alfrescoApiService); const builder = new SearchQueryBuilderService(buildConfig(config), alfrescoApiService);
@@ -575,9 +523,7 @@ describe('SearchQueryBuilder', () => {
it('should use pagination settings', () => { it('should use pagination settings', () => {
const config: SearchConfiguration = { const config: SearchConfiguration = {
categories: [ categories: [{ id: 'cat1', enabled: true } as any]
{ id: 'cat1', enabled: true } as any
]
}; };
const alfrescoApiService = TestBed.inject(AlfrescoApiService); const alfrescoApiService = TestBed.inject(AlfrescoApiService);
const builder = new SearchQueryBuilderService(buildConfig(config), alfrescoApiService); const builder = new SearchQueryBuilderService(buildConfig(config), alfrescoApiService);
@@ -593,9 +539,7 @@ describe('SearchQueryBuilder', () => {
it('should build final request with user and custom queries', () => { it('should build final request with user and custom queries', () => {
const config: SearchConfiguration = { const config: SearchConfiguration = {
categories: [ categories: [{ id: 'cat1', enabled: true } as any]
{ id: 'cat1', enabled: true } as any
]
}; };
const alfrescoApiService = TestBed.inject(AlfrescoApiService); const alfrescoApiService = TestBed.inject(AlfrescoApiService);
const builder = new SearchQueryBuilderService(buildConfig(config), alfrescoApiService); const builder = new SearchQueryBuilderService(buildConfig(config), alfrescoApiService);
@@ -629,9 +573,7 @@ describe('SearchQueryBuilder', () => {
]; ];
const config: SearchConfiguration = { const config: SearchConfiguration = {
categories: [ categories: [{ id: 'cat1', enabled: true } as any]
{ id: 'cat1', enabled: true } as any
]
}; };
const alfrescoApiService = TestBed.inject(AlfrescoApiService); const alfrescoApiService = TestBed.inject(AlfrescoApiService);
@@ -671,9 +613,7 @@ describe('SearchQueryBuilder', () => {
it('should emit error event', () => { it('should emit error event', () => {
const config: SearchConfiguration = { const config: SearchConfiguration = {
categories: [ categories: [{ id: 'cat1', enabled: true } as any]
{ id: 'cat1', enabled: true } as any
]
}; };
const alfrescoApiService = TestBed.inject(AlfrescoApiService); const alfrescoApiService = TestBed.inject(AlfrescoApiService);
@@ -689,9 +629,7 @@ describe('SearchQueryBuilder', () => {
it('should emit empty results on error', (done) => { it('should emit empty results on error', (done) => {
const config: SearchConfiguration = { const config: SearchConfiguration = {
categories: [ categories: [{ id: 'cat1', enabled: true } as any]
{ id: 'cat1', enabled: true } as any
]
}; };
const alfrescoApiService = TestBed.inject(AlfrescoApiService); const alfrescoApiService = TestBed.inject(AlfrescoApiService);
@@ -712,9 +650,9 @@ describe('SearchQueryBuilder', () => {
const builder = new SearchQueryBuilderService(buildConfig({}), alfrescoApiService); const builder = new SearchQueryBuilderService(buildConfig({}), alfrescoApiService);
builder.userQuery = 'nuka cola quantum'; 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', () => { it('should fetch the include config from the app config', () => {
@@ -726,9 +664,9 @@ describe('SearchQueryBuilder', () => {
const builder = new SearchQueryBuilderService(buildConfig(config), alfrescoApiService); const builder = new SearchQueryBuilderService(buildConfig(config), alfrescoApiService);
builder.userQuery = 'nuka cola quantum'; 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', () => { it('should the query contain the pagination', () => {
@@ -741,9 +679,9 @@ describe('SearchQueryBuilder', () => {
skipCount: 0 skipCount: 0
}; };
builder.paging = mockPagination; 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', () => { it('should the query contain the scope in case it is defined', () => {
@@ -753,9 +691,9 @@ describe('SearchQueryBuilder', () => {
const mockScope = { locations: 'mock-location' }; const mockScope = { locations: 'mock-location' };
builder.userQuery = 'nuka cola quantum'; builder.userQuery = 'nuka cola quantum';
builder.setScope(mockScope); 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) => { it('should return empty if array of search config not found', (done) => {
@@ -774,32 +712,19 @@ describe('SearchQueryBuilder', () => {
beforeEach(() => { beforeEach(() => {
configs = [ configs = [
{ {
categories: [ categories: [{ id: 'cat1', enabled: true } as any, { id: 'cat2', enabled: true } as any],
{ id: 'cat1', enabled: true } as any, filterQueries: [{ query: 'query1' }, { query: 'query2' }],
{ id: 'cat2', enabled: true } as any
],
filterQueries: [
{ query: 'query1' },
{ query: 'query2' }
],
name: 'config1', name: 'config1',
default: true default: true
}, },
{ {
categories: [ categories: [{ id: 'mouse', enabled: true } as any],
{ id: 'mouse', enabled: true } as any filterQueries: [{ query: 'query1' }, { query: 'query2' }],
],
filterQueries: [
{ query: 'query1' },
{ query: 'query2' }
],
name: 'config2', name: 'config2',
default: false default: false
}, },
{ {
categories: [ categories: [{ id: 'cat_and_mouse', enabled: true } as any],
{ id: 'cat_and_mouse', enabled: true } as any
],
default: false default: false
} }
]; ];

View File

@@ -16,7 +16,7 @@
*/ */
import { Injectable } from '@angular/core'; 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 { Observable, Subject, from, throwError } from 'rxjs';
import { AlfrescoApiService } from '@alfresco/adf-core'; import { AlfrescoApiService } from '@alfresco/adf-core';
import { SearchConfigurationService } from './search-configuration.service'; import { SearchConfigurationService } from './search-configuration.service';
@@ -25,7 +25,6 @@ import { SearchConfigurationService } from './search-configuration.service';
providedIn: 'root' providedIn: 'root'
}) })
export class SearchService { export class SearchService {
dataLoaded: Subject<ResultSetPaging> = new Subject(); dataLoaded: Subject<ResultSetPaging> = new Subject();
private _queriesApi: QueriesApi; private _queriesApi: QueriesApi;
@@ -40,9 +39,7 @@ export class SearchService {
return this._searchApi; return this._searchApi;
} }
constructor(private apiService: AlfrescoApiService, constructor(private apiService: AlfrescoApiService, private searchConfigurationService: SearchConfigurationService) {}
private searchConfigurationService: SearchConfigurationService) {
}
/** /**
* Gets a list of nodes that match the given search criteria. * Gets a list of nodes that match the given search criteria.
@@ -54,9 +51,11 @@ export class SearchService {
getNodeQueryResults(term: string, options?: SearchOptions): Observable<NodePaging> { getNodeQueryResults(term: string, options?: SearchOptions): Observable<NodePaging> {
const promise = this.queriesApi.findNodes(term, options); const promise = this.queriesApi.findNodes(term, options);
promise.then((nodePaging: NodePaging) => { promise
.then((nodePaging) => {
this.dataLoaded.next(nodePaging); this.dataLoaded.next(nodePaging);
}).catch((err) => this.handleError(err)); })
.catch((err) => this.handleError(err));
return from(promise); return from(promise);
} }
@@ -70,28 +69,32 @@ export class SearchService {
* @returns List of search results * @returns List of search results
*/ */
search(searchTerm: string, maxResults: number, skipCount: number): Observable<ResultSetPaging> { search(searchTerm: string, maxResults: number, skipCount: number): Observable<ResultSetPaging> {
const searchQuery = Object.assign(this.searchConfigurationService.generateQueryBody(searchTerm, maxResults, skipCount)); const searchQuery = this.searchConfigurationService.generateQueryBody(searchTerm, maxResults, skipCount);
const promise = this.searchApi.search(searchQuery); const promise = this.searchApi.search(searchQuery);
promise.then((nodePaging: NodePaging) => { promise
.then((nodePaging) => {
this.dataLoaded.next(nodePaging); this.dataLoaded.next(nodePaging);
}).catch((err) => this.handleError(err)); })
.catch((err) => this.handleError(err));
return from(promise); 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 * @param queryBody Object containing the search parameters
* @returns List of search results * @returns List of search results
*/ */
searchByQueryBody(queryBody: QueryBody): Observable<ResultSetPaging> { searchByQueryBody(queryBody: SearchRequest): Observable<ResultSetPaging> {
const promise = this.searchApi.search(queryBody); const promise = this.searchApi.search(queryBody);
promise.then((nodePaging: NodePaging) => { promise
.then((nodePaging) => {
this.dataLoaded.next(nodePaging); this.dataLoaded.next(nodePaging);
}).catch((err) => this.handleError(err)); })
.catch((err) => this.handleError(err));
return from(promise); return from(promise);
} }