[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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 340 additions and 480 deletions

View File

@ -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;
}
}

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.
- [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.

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
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

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.
- _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`<br/>
- **buildQuery**(): `SearchRequest`<br/>
Builds the current query.
- **Returns** `QueryBody` - The finished query
- **execute**(queryBody?: `QueryBody`)<br/>
- **Returns** `SearchRequest` - The finished query
- **execute**(queryBody?: `SearchRequest`)<br/>
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/>
- **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**()<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)`>` -
- **setScope**(scope: `RequestScope`)<br/>
- _scope:_ `RequestScope` -
- **update**(queryBody?: `QueryBody`)<br/>
- **update**(queryBody?: `SearchRequest`)<br/>
Builds the current query and triggers the `updated` event.
- _queryBody:_ `QueryBody` - (Optional)
- _queryBody:_ `SearchRequest` - (Optional)
- **updateSelectedConfiguration**(index: `number`)<br/>
- _index:_ `number` -

View File

@ -10,14 +10,13 @@ Provides fine control of parameters to a search.
## Methods
`generateQueryBody(searchTerm: string, maxResults: string, skipCount: string): QueryBody`<br/>
Generates a QueryBody object with custom search parameters.
`generateQueryBody(searchTerm: string, maxResults: string, skipCount: string): SearchRequest`<br/>
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;
}
}
```

View File

@ -13,18 +13,17 @@ Provides fine control of parameters to a search.
### Methods
- **generateQueryBody**(searchTerm: `string`, maxResults: `number`, skipCount: `number`): `QueryBody`<br/>
Generates a QueryBody object with custom search parameters.
- **generateQueryBody**(searchTerm: `string`, maxResults: `number`, skipCount: `number`): `SearchRequest`<br/>
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

View File

@ -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)`>`<br/>
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)`>`<br/>
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

View File

@ -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;
}

View File

@ -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();

View File

@ -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<boolean>();
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) {

View File

@ -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;

View File

@ -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: `
<adf-search [searchTerm]="searchedWord" [maxResults]="maxResults"
(error)="showSearchResult('ERROR')"
(success)="showSearchResult('success')" #search>
<ng-template let-data>
<ul id="autocomplete-search-result-list">
<li *ngFor="let item of data?.list?.entries; let idx = index" (click)="elementClicked(item)">
<div id="result_option_{{idx}}">
<span>{{ item?.entry.name }}</span>
</div>
</li>
</ul>
</ng-template>
</adf-search>
<span id="component-result-message">{{message}}</span>
<adf-search
[searchTerm]="searchedWord"
[maxResults]="maxResults"
(error)="showSearchResult('ERROR')"
(success)="showSearchResult('success')"
#search
>
<ng-template let-data>
<ul id="autocomplete-search-result-list">
<li *ngFor="let item of data?.list?.entries; let idx = index" (click)="elementClicked()">
<div id="result_option_{{ idx }}">
<span>{{ item?.entry.name }}</span>
</div>
</li>
</ul>
</ng-template>
</adf-search>
<span id="component-result-message">{{ message }}</span>
`
})
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();
}
}
}

View File

@ -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\")`;
}

View File

@ -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<string[]> {
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<Node> {
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<string[]> {
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 })))
)
}))
})
)
);
}

View File

@ -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<SearchConfiguration>();
/* 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 */
executed = new Subject<ResultSetPaging>();
@ -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<ResultSetPaging> {
search(queryBody: SearchRequest): Observable<ResultSetPaging> {
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)
)
};
}

View File

@ -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' }]
});
}
}

View File

@ -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
}
];

View File

@ -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<ResultSetPaging> = 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<NodePaging> {
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<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);
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<ResultSetPaging> {
searchByQueryBody(queryBody: SearchRequest): Observable<ResultSetPaging> {
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);
}