mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[AAE-3719][AAE-3720] - Fix content node selector search should align with the filtering (#6214)
* Fix content node selector search should align with the filtering * Add a flag to disable optionally the search execution in the filters * Add unit tests * refactoring - Remove content node selector service and use the main search service instead * Fix existing unit tests to align with the new changes * Fix bug when copying a file * add include properties in demo shell search config * revert attach-file-cloud widget * Remove unused import * More unit tests
This commit is contained in:
@@ -119,7 +119,8 @@
|
|||||||
],
|
],
|
||||||
"include": [
|
"include": [
|
||||||
"path",
|
"path",
|
||||||
"allowableOperations"
|
"allowableOperations",
|
||||||
|
"properties"
|
||||||
],
|
],
|
||||||
"sorting": {
|
"sorting": {
|
||||||
"options": [
|
"options": [
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
import { tick, ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing';
|
import { tick, ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { NodeEntry, Node, SiteEntry, SitePaging, NodePaging, ResultSetPaging } from '@alfresco/js-api';
|
import { NodeEntry, Node, SiteEntry, SitePaging, NodePaging, ResultSetPaging, RequestScope } from '@alfresco/js-api';
|
||||||
import { SitesService, setupTestBed, NodesApiService } from '@alfresco/adf-core';
|
import { SitesService, setupTestBed, NodesApiService } from '@alfresco/adf-core';
|
||||||
import { of, throwError } from 'rxjs';
|
import { of, throwError } from 'rxjs';
|
||||||
import { DropdownBreadcrumbComponent } from '../breadcrumb';
|
import { DropdownBreadcrumbComponent } from '../breadcrumb';
|
||||||
@@ -31,7 +31,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 { ContentNodeSelectorService } from './content-node-selector.service';
|
import { mockQueryBody } from '../mock/search-query.mock';
|
||||||
|
|
||||||
const fakeResultSetPaging: ResultSetPaging = {
|
const fakeResultSetPaging: ResultSetPaging = {
|
||||||
list: {
|
list: {
|
||||||
@@ -59,11 +59,9 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
|||||||
let nodeService: NodesApiService;
|
let nodeService: NodesApiService;
|
||||||
let sitesService: SitesService;
|
let sitesService: SitesService;
|
||||||
let searchSpy: jasmine.Spy;
|
let searchSpy: jasmine.Spy;
|
||||||
let cnSearchSpy: jasmine.Spy;
|
|
||||||
const fakeNodeEntry = new Node({ id: 'fakeId' });
|
const fakeNodeEntry = new Node({ id: 'fakeId' });
|
||||||
const nodeEntryEvent = new NodeEntryEvent(fakeNodeEntry);
|
const nodeEntryEvent = new NodeEntryEvent(fakeNodeEntry);
|
||||||
let searchQueryBuilderService: SearchQueryBuilderService;
|
let searchQueryBuilderService: SearchQueryBuilderService;
|
||||||
let contentNodeSelectorService: ContentNodeSelectorService;
|
|
||||||
|
|
||||||
function typeToSearchBox(searchTerm = 'string-to-search') {
|
function typeToSearchBox(searchTerm = 'string-to-search') {
|
||||||
const searchInput = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-input"]'));
|
const searchInput = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-input"]'));
|
||||||
@@ -93,11 +91,9 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
|||||||
|
|
||||||
nodeService = TestBed.inject(NodesApiService);
|
nodeService = TestBed.inject(NodesApiService);
|
||||||
sitesService = TestBed.inject(SitesService);
|
sitesService = TestBed.inject(SitesService);
|
||||||
contentNodeSelectorService = TestBed.inject(ContentNodeSelectorService);
|
|
||||||
searchQueryBuilderService = component.queryBuilderService;
|
searchQueryBuilderService = component.queryBuilderService;
|
||||||
|
|
||||||
spyOn(nodeService, 'getNode').and.returnValue(of({ id: 'fake-node', path: { elements: [{ nodeType: 'st:site', name: 'fake-site'}] } }));
|
spyOn(nodeService, 'getNode').and.returnValue(of({ id: 'fake-node', path: { elements: [{ nodeType: 'st:site', name: 'fake-site'}] } }));
|
||||||
cnSearchSpy = spyOn(contentNodeSelectorService, 'createQuery').and.callThrough();
|
|
||||||
searchSpy = spyOn(searchQueryBuilderService, 'execute');
|
searchSpy = spyOn(searchQueryBuilderService, 'execute');
|
||||||
const fakeSite = new SiteEntry({ entry: { id: 'fake-site', guid: 'fake-site', title: 'fake-site', visibility: 'visible' } });
|
const fakeSite = new SiteEntry({ entry: { id: 'fake-site', guid: 'fake-site', title: 'fake-site', visibility: 'visible' } });
|
||||||
spyOn(sitesService, 'getSite').and.returnValue(of(fakeSite));
|
spyOn(sitesService, 'getSite').and.returnValue(of(fakeSite));
|
||||||
@@ -362,32 +358,6 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
|||||||
let customResourcesService: CustomResourcesService;
|
let customResourcesService: CustomResourcesService;
|
||||||
const entry: Node = <Node> { id: 'fakeid'};
|
const entry: Node = <Node> { id: 'fakeid'};
|
||||||
|
|
||||||
const defaultSearchOptions = (searchTerm, rootNodeId = undefined, skipCount = 0, showFiles = false) => {
|
|
||||||
|
|
||||||
const parentFiltering = rootNodeId ? [{ query: `ANCESTOR:'workspace://SpacesStore/${rootNodeId}'` }] : [];
|
|
||||||
|
|
||||||
const defaultSearchNode: any = {
|
|
||||||
query: {
|
|
||||||
query: searchTerm ? `${searchTerm}*` : searchTerm
|
|
||||||
},
|
|
||||||
include: ['path', 'allowableOperations', 'properties'],
|
|
||||||
paging: {
|
|
||||||
maxItems: 25,
|
|
||||||
skipCount: skipCount
|
|
||||||
},
|
|
||||||
filterQueries: [
|
|
||||||
{ query: `TYPE:'cm:folder'${ showFiles ? " OR TYPE:'cm:content'" : '' }` },
|
|
||||||
{ query: 'NOT cm:creator:System' },
|
|
||||||
...parentFiltering
|
|
||||||
],
|
|
||||||
scope: {
|
|
||||||
locations: 'nodes'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return defaultSearchNode;
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const documentListService = TestBed.inject(DocumentListService);
|
const documentListService = TestBed.inject(DocumentListService);
|
||||||
const expectedDefaultFolderNode = <NodeEntry> { entry: { path: { elements: [] } } };
|
const expectedDefaultFolderNode = <NodeEntry> { entry: { path: { elements: [] } } };
|
||||||
@@ -418,23 +388,79 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should load the results by calling the search api on search change', fakeAsync(() => {
|
it('should the user query get updated when the user types in the search input', fakeAsync(() => {
|
||||||
typeToSearchBox('kakarot');
|
const updateSpy = spyOn(searchQueryBuilderService, 'update');
|
||||||
|
typeToSearchBox('search-term');
|
||||||
|
|
||||||
tick(debounceSearch);
|
tick(debounceSearch);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(searchSpy).toHaveBeenCalledWith(defaultSearchOptions('kakarot'));
|
expect(updateSpy).toHaveBeenCalled();
|
||||||
|
expect(searchQueryBuilderService.userQuery).toEqual('(search-term)');
|
||||||
|
expect(component.searchTerm).toEqual('search-term');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should show files in results by calling the search api on search change', fakeAsync(() => {
|
it('should perform a search when the queryBody gets updated and it is defined', async () => {
|
||||||
component.showFilesInResult = true;
|
searchQueryBuilderService.userQuery = 'search-term';
|
||||||
typeToSearchBox('kakarot');
|
searchQueryBuilderService.update();
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
expect(searchSpy).toHaveBeenCalledWith(mockQueryBody);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should NOT perform a search and clear the results when the queryBody gets updated and it is NOT defined', async () => {
|
||||||
|
spyOn(component, 'clearSearch');
|
||||||
|
|
||||||
|
searchQueryBuilderService.userQuery = '';
|
||||||
|
searchQueryBuilderService.update();
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
expect(searchSpy).not.toHaveBeenCalled();
|
||||||
|
expect(component.clearSearch).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reset the search term when clicking the clear icon', () => {
|
||||||
|
component.searchTerm = 'search-term';
|
||||||
|
searchQueryBuilderService.userQuery = 'search-term';
|
||||||
|
spyOn(component, 'clearSearch');
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
const clearIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-clear"]'));
|
||||||
|
clearIcon.nativeElement.click();
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(searchQueryBuilderService.userQuery).toEqual('');
|
||||||
|
expect(component.searchTerm).toEqual('');
|
||||||
|
expect(component.clearSearch).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load the results by calling the search api on search change', fakeAsync(() => {
|
||||||
|
typeToSearchBox('search-term');
|
||||||
|
|
||||||
tick(debounceSearch);
|
tick(debounceSearch);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(searchSpy).toHaveBeenCalledWith(defaultSearchOptions('kakarot', undefined, 0, true));
|
expect(searchSpy).toHaveBeenCalledWith(mockQueryBody);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should the query include the show files filterQuery', fakeAsync(() => {
|
||||||
|
component.showFilesInResult = true;
|
||||||
|
typeToSearchBox('search-term');
|
||||||
|
|
||||||
|
const expectedQueryBody = mockQueryBody;
|
||||||
|
expectedQueryBody.filterQueries.push({
|
||||||
|
query: `TYPE:'cm:folder' OR TYPE:'cm:content'`
|
||||||
|
});
|
||||||
|
|
||||||
|
tick(debounceSearch);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(searchSpy).toHaveBeenCalledWith(expectedQueryBody);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
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(() => {
|
||||||
@@ -453,8 +479,8 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
|||||||
expect(component.breadcrumbFolderTitle).toBe('My Sites');
|
expect(component.breadcrumbFolderTitle).toBe('My Sites');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call the search api on changing the site selectBox value', fakeAsync(() => {
|
it('should perform a search when selecting a site with the correct query', fakeAsync(() => {
|
||||||
typeToSearchBox('vegeta');
|
typeToSearchBox('search-term');
|
||||||
|
|
||||||
tick(debounceSearch);
|
tick(debounceSearch);
|
||||||
|
|
||||||
@@ -462,40 +488,34 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
|||||||
|
|
||||||
component.siteChanged(<SiteEntry> { entry: { guid: 'namek' } });
|
component.siteChanged(<SiteEntry> { entry: { guid: 'namek' } });
|
||||||
|
|
||||||
|
const expectedQueryBody = mockQueryBody;
|
||||||
|
expectedQueryBody.filterQueries = [ { query: `ANCESTOR:'workspace://SpacesStore/namek'`} ];
|
||||||
|
|
||||||
expect(searchSpy.calls.count()).toBe(2, 'Search count should be two after the site change');
|
expect(searchSpy.calls.count()).toBe(2, 'Search count should be two after the site change');
|
||||||
expect(searchSpy.calls.argsFor(1)).toEqual([defaultSearchOptions('vegeta', 'namek')]);
|
expect(searchSpy).toHaveBeenCalledWith(expectedQueryBody);
|
||||||
}));
|
|
||||||
|
|
||||||
it('should create the query with the right parameters on changing the site selectbox\'s value', fakeAsync(() => {
|
|
||||||
typeToSearchBox('vegeta');
|
|
||||||
|
|
||||||
tick(debounceSearch);
|
|
||||||
expect(cnSearchSpy.calls.count()).toBe(1);
|
|
||||||
|
|
||||||
component.siteChanged(<SiteEntry> { entry: { guid: '-sites-' } });
|
|
||||||
|
|
||||||
expect(cnSearchSpy).toHaveBeenCalled();
|
|
||||||
expect(cnSearchSpy.calls.count()).toBe(2);
|
|
||||||
expect(cnSearchSpy).toHaveBeenCalledWith('vegeta', undefined, 0, 25, [], false);
|
|
||||||
expect(cnSearchSpy).toHaveBeenCalledWith('vegeta', '-sites-', 0, 25, ['123456testId', '09876543testId'], false);
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
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(() => {
|
||||||
component.dropdownSiteList = <SitePaging> { list: { entries: [<SiteEntry> { entry: { guid: '-sites-' } }, <SiteEntry> { entry: { guid: 'namek' } }] } };
|
component.dropdownSiteList = <SitePaging> { list: { entries: [<SiteEntry> { entry: { guid: '-sites-' } }, <SiteEntry> { entry: { guid: 'namek' } }] } };
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
typeToSearchBox('vegeta');
|
typeToSearchBox('search-term');
|
||||||
|
|
||||||
tick(debounceSearch);
|
tick(debounceSearch);
|
||||||
|
|
||||||
expect(cnSearchSpy.calls.count()).toBe(1);
|
expect(searchSpy.calls.count()).toBe(1);
|
||||||
|
|
||||||
component.siteChanged(<SiteEntry> { entry: { guid: '-sites-' } });
|
component.siteChanged(<SiteEntry> { entry: { guid: '-sites-' } });
|
||||||
|
|
||||||
expect(cnSearchSpy).toHaveBeenCalled();
|
const expectedQueryBodyWithSiteChange = mockQueryBody;
|
||||||
expect(cnSearchSpy.calls.count()).toBe(2);
|
expectedQueryBodyWithSiteChange.filterQueries = [
|
||||||
expect(cnSearchSpy).toHaveBeenCalledWith('vegeta', undefined, 0, 25, [], false);
|
{ query: `ANCESTOR:'workspace://SpacesStore/-sites-' OR ANCESTOR:'workspace://SpacesStore/123456testId' OR ANCESTOR:'workspace://SpacesStore/09876543testId'` }
|
||||||
expect(cnSearchSpy).toHaveBeenCalledWith('vegeta', '-sites-', 0, 25, ['123456testId', '09876543testId'], false);
|
];
|
||||||
|
|
||||||
|
expect(searchSpy).toHaveBeenCalled();
|
||||||
|
expect(searchSpy.calls.count()).toBe(2);
|
||||||
|
expect(searchSpy).toHaveBeenCalledWith(mockQueryBody);
|
||||||
|
expect(searchSpy).toHaveBeenCalledWith(expectedQueryBodyWithSiteChange);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
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(() => {
|
||||||
@@ -601,19 +621,24 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
|||||||
expect(component.showingSearchResults).toBeFalsy();
|
expect(component.showingSearchResults).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should the query restrict the search to the currentFolderId in case is defined', () => {
|
it('should the query restrict the search to the currentFolderId in case is defined', fakeAsync(() => {
|
||||||
component.currentFolderId = 'my-root-id';
|
component.currentFolderId = 'my-root-id';
|
||||||
component.restrictRootToCurrentFolderId = true;
|
component.restrictRootToCurrentFolderId = true;
|
||||||
component.ngOnInit();
|
component.ngOnInit();
|
||||||
component.search('search');
|
typeToSearchBox('search-term');
|
||||||
|
tick(debounceSearch);
|
||||||
|
|
||||||
expect(cnSearchSpy).toHaveBeenCalledWith('search', 'my-root-id', 0, 25, [], false);
|
const expectedQueryBody = mockQueryBody;
|
||||||
});
|
expectedQueryBody.filterQueries = [ { query: `ANCESTOR:'workspace://SpacesStore/my-root-id'`} ];
|
||||||
|
|
||||||
|
expect(searchSpy).toHaveBeenCalledWith(expectedQueryBody);
|
||||||
|
}));
|
||||||
|
|
||||||
it('should emit showingSearch event with true while searching', async () => {
|
it('should emit showingSearch event with true while searching', async () => {
|
||||||
spyOn(customResourcesService, 'hasCorrespondingNodeIds').and.returnValue(true);
|
spyOn(customResourcesService, 'hasCorrespondingNodeIds').and.returnValue(true);
|
||||||
const showingSearchSpy = spyOn(component.showingSearch, 'emit');
|
const showingSearchSpy = spyOn(component.showingSearch, 'emit');
|
||||||
component.search('search');
|
|
||||||
|
component.queryBuilderService.execute({ query: { query: 'search' } });
|
||||||
|
|
||||||
triggerSearchResults(fakeResultSetPaging);
|
triggerSearchResults(fakeResultSetPaging);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@@ -623,15 +648,16 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
|||||||
expect(showingSearchSpy).toHaveBeenCalledWith(true);
|
expect(showingSearchSpy).toHaveBeenCalledWith(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emit showingSearch event with false if you remove search term without clicking on X (icon) icon', async () => {
|
it('should emit showingSearch event with false if you remove search term without clicking on X (icon) icon', fakeAsync(() => {
|
||||||
const showingSearchSpy = spyOn(component.showingSearch, 'emit');
|
const showingSearchSpy = spyOn(component.showingSearch, 'emit');
|
||||||
component.search('');
|
typeToSearchBox('');
|
||||||
|
tick(debounceSearch);
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
await fixture.whenStable();
|
|
||||||
|
|
||||||
expect(component.showingSearchResults).toBe(false);
|
expect(component.showingSearchResults).toBe(false);
|
||||||
expect(showingSearchSpy).toHaveBeenCalledWith(false);
|
expect(showingSearchSpy).toHaveBeenCalledWith(false);
|
||||||
});
|
}));
|
||||||
|
|
||||||
it('should emit showingResults event with false when clicking on the X (clear) icon', () => {
|
it('should emit showingResults event with false when clicking on the X (clear) icon', () => {
|
||||||
const showingSearchSpy = spyOn(component.showingSearch, 'emit');
|
const showingSearchSpy = spyOn(component.showingSearch, 'emit');
|
||||||
@@ -654,7 +680,7 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
|||||||
it('should emit showingResults event with false if search api fails', async () => {
|
it('should emit showingResults event with false if search api fails', async () => {
|
||||||
getCorrespondingNodeIdsSpy.and.throwError('Failed');
|
getCorrespondingNodeIdsSpy.and.throwError('Failed');
|
||||||
const showingSearchSpy = spyOn(component.showingSearch, 'emit');
|
const showingSearchSpy = spyOn(component.showingSearch, 'emit');
|
||||||
component.search('search');
|
component.queryBuilderService.execute({ query: { query: 'search' } });
|
||||||
|
|
||||||
triggerSearchResults(fakeResultSetPaging);
|
triggerSearchResults(fakeResultSetPaging);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@@ -665,13 +691,17 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should the query restrict the search to the site and not to the currentFolderId in case is changed', () => {
|
it('should the query restrict the search to the site and not to the currentFolderId in case is changed', () => {
|
||||||
|
component.queryBuilderService.userQuery = 'search-term';
|
||||||
component.currentFolderId = 'my-root-id';
|
component.currentFolderId = 'my-root-id';
|
||||||
component.restrictRootToCurrentFolderId = true;
|
component.restrictRootToCurrentFolderId = true;
|
||||||
component.ngOnInit();
|
|
||||||
component.siteChanged(<SiteEntry> { entry: { guid: 'my-site-id' } });
|
component.siteChanged(<SiteEntry> { entry: { guid: 'my-site-id' } });
|
||||||
component.search('search');
|
|
||||||
|
|
||||||
expect(cnSearchSpy).toHaveBeenCalledWith('search', 'my-site-id', 0, 25, [], false);
|
const expectedQueryBodyWithSiteChange = mockQueryBody;
|
||||||
|
expectedQueryBodyWithSiteChange.filterQueries = [
|
||||||
|
{ query: `ANCESTOR:'workspace://SpacesStore/my-site-id'` }
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(searchSpy).toHaveBeenCalledWith(expectedQueryBodyWithSiteChange);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should restrict the breadcrumb to the currentFolderId in case restrictedRoot is true', () => {
|
it('should restrict the breadcrumb to the currentFolderId in case restrictedRoot is true', () => {
|
||||||
@@ -689,7 +719,7 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should clear the search field, nodes and chosenNode when deleting the search input', fakeAsync (() => {
|
it('should clear the search field, nodes and chosenNode when deleting the search input', fakeAsync (() => {
|
||||||
spyOn(component, 'clear').and.callThrough();
|
spyOn(component, 'clearSearch').and.callThrough();
|
||||||
typeToSearchBox('a');
|
typeToSearchBox('a');
|
||||||
|
|
||||||
tick(debounceSearch);
|
tick(debounceSearch);
|
||||||
@@ -703,7 +733,7 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(searchSpy.calls.count()).toBe(1, 'no other search has been performed');
|
expect(searchSpy.calls.count()).toBe(1, 'no other search has been performed');
|
||||||
expect(component.clear).toHaveBeenCalled();
|
expect(component.clearSearch).toHaveBeenCalled();
|
||||||
expect(component.folderIdToShow).toBe('cat-girl-nuku-nuku', 'back to the folder in which the search was performed');
|
expect(component.folderIdToShow).toBe('cat-girl-nuku-nuku', 'back to the folder in which the search was performed');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -728,7 +758,6 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
|||||||
component.siteChanged(<SiteEntry> { entry: { guid: 'namek' } });
|
component.siteChanged(<SiteEntry> { entry: { guid: 'namek' } });
|
||||||
|
|
||||||
expect(searchSpy.calls.count()).toBe(2);
|
expect(searchSpy.calls.count()).toBe(2);
|
||||||
expect(searchSpy.calls.argsFor(1)).toEqual([defaultSearchOptions('piccolo', 'namek')]);
|
|
||||||
|
|
||||||
component.clear();
|
component.clear();
|
||||||
|
|
||||||
@@ -845,21 +874,19 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
|||||||
}, 300);
|
}, 300);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reload the original folderId when clearing the search input', async() => {
|
it('should reload the original folderId when clearing the search input', fakeAsync(() => {
|
||||||
component.search('mock-type-search');
|
typeToSearchBox('search-term');
|
||||||
|
tick(debounceSearch);
|
||||||
triggerSearchResults(fakeResultSetPaging);
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
await fixture.whenStable();
|
|
||||||
|
|
||||||
expect(component.folderIdToShow).toBe(null);
|
expect(component.folderIdToShow).toBe(null);
|
||||||
|
|
||||||
component.clear();
|
typeToSearchBox('');
|
||||||
|
tick(debounceSearch);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
await fixture.whenStable();
|
|
||||||
|
|
||||||
expect(component.folderIdToShow).toBe('cat-girl-nuku-nuku');
|
expect(component.folderIdToShow).toBe('cat-girl-nuku-nuku');
|
||||||
});
|
}));
|
||||||
|
|
||||||
it('should set the folderIdToShow to the default "currentFolderId" if siteId is undefined', (done) => {
|
it('should set the folderIdToShow to the default "currentFolderId" if siteId is undefined', (done) => {
|
||||||
component.siteChanged(<SiteEntry> { entry: { guid: 'Kame-Sennin Muten Roshi' } });
|
component.siteChanged(<SiteEntry> { entry: { guid: 'Kame-Sennin Muten Roshi' } });
|
||||||
@@ -900,63 +927,50 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
|||||||
expect(component.searchTerm).toBe('');
|
expect(component.searchTerm).toBe('');
|
||||||
|
|
||||||
expect(component.infiniteScroll).toBeTruthy();
|
expect(component.infiniteScroll).toBeTruthy();
|
||||||
expect(component.pagination.maxItems).toBe(45);
|
expect(component.queryBuilderService.paging.maxItems).toBe(45);
|
||||||
expect(searchSpy).not.toHaveBeenCalled();
|
expect(searchSpy).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set its loading state to true after search was started', fakeAsync (() => {
|
it('should set its loading state to true to perform a new search', async() => {
|
||||||
component.showingSearchResults = true;
|
component.prepareDialogForNewSearch(mockQueryBody);
|
||||||
|
|
||||||
typeToSearchBox('shenron');
|
|
||||||
|
|
||||||
tick(debounceSearch);
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
tick(debounceSearch);
|
|
||||||
|
|
||||||
const spinnerSelector = By.css('[data-automation-id="content-node-selector-search-pagination"] [data-automation-id="adf-infinite-pagination-spinner"]');
|
const spinnerSelector = By.css('[data-automation-id="content-node-selector-search-pagination"] [data-automation-id="adf-infinite-pagination-spinner"]');
|
||||||
const paginationLoading = fixture.debugElement.query(spinnerSelector);
|
const paginationLoading = fixture.debugElement.query(spinnerSelector);
|
||||||
|
|
||||||
expect(paginationLoading).not.toBeNull();
|
expect(paginationLoading).not.toBeNull();
|
||||||
}));
|
});
|
||||||
|
|
||||||
it('Should infinite pagination target be null when we use it for search ', fakeAsync (() => {
|
it('Should infinite pagination target be null when we use it for search ', fakeAsync (() => {
|
||||||
component.showingSearchResults = true;
|
component.showingSearchResults = true;
|
||||||
|
|
||||||
typeToSearchBox('shenron');
|
typeToSearchBox('shenron');
|
||||||
|
|
||||||
tick(debounceSearch);
|
tick(debounceSearch);
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
tick(debounceSearch);
|
|
||||||
|
|
||||||
expect(component.target).toBeNull();
|
expect(component.target).toBeNull();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('Should infinite pagination target be present when search finish', fakeAsync (() => {
|
it('Should infinite pagination target be present when search finish', () => {
|
||||||
component.showingSearchResults = true;
|
triggerSearchResults(fakeResultSetPaging);
|
||||||
|
|
||||||
typeToSearchBox('shenron');
|
|
||||||
|
|
||||||
tick(debounceSearch);
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
typeToSearchBox('');
|
|
||||||
|
|
||||||
tick(debounceSearch);
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(component.target).not.toBeNull();
|
expect(component.target).not.toBeNull();
|
||||||
}));
|
});
|
||||||
|
|
||||||
it('Should infinite pagination target on init be the document list', fakeAsync(() => {
|
it('Should infinite pagination target on init be the document list', fakeAsync(() => {
|
||||||
component.showingSearchResults = true;
|
component.showingSearchResults = true;
|
||||||
|
|
||||||
expect(component.target).toEqual(component.documentList);
|
expect(component.target).toEqual(component.documentList);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('Should set the scope to nodes when the component inits', () => {
|
||||||
|
const expectedScope: RequestScope = { locations: 'nodes' };
|
||||||
|
const setScopeSpy = spyOn(component.queryBuilderService, 'setScope');
|
||||||
|
component.ngOnInit();
|
||||||
|
|
||||||
|
expect(setScopeSpy).toHaveBeenCalledWith(expectedScope);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -29,7 +29,6 @@ import {
|
|||||||
import {
|
import {
|
||||||
HighlightDirective,
|
HighlightDirective,
|
||||||
UserPreferencesService,
|
UserPreferencesService,
|
||||||
PaginationModel,
|
|
||||||
UserPreferenceValues,
|
UserPreferenceValues,
|
||||||
InfinitePaginationComponent, PaginatedComponent,
|
InfinitePaginationComponent, PaginatedComponent,
|
||||||
NodesApiService,
|
NodesApiService,
|
||||||
@@ -38,11 +37,10 @@ import {
|
|||||||
FileUploadCompleteEvent
|
FileUploadCompleteEvent
|
||||||
} from '@alfresco/adf-core';
|
} from '@alfresco/adf-core';
|
||||||
import { FormControl } from '@angular/forms';
|
import { FormControl } from '@angular/forms';
|
||||||
import { Node, NodePaging, Pagination, SiteEntry, SitePaging, NodeEntry } from '@alfresco/js-api';
|
import { Node, NodePaging, Pagination, SiteEntry, SitePaging, NodeEntry, QueryBody, 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';
|
||||||
import { ContentNodeSelectorService } from './content-node-selector.service';
|
|
||||||
import { debounceTime, takeUntil, scan } from 'rxjs/operators';
|
import { debounceTime, takeUntil, scan } from 'rxjs/operators';
|
||||||
import { CustomResourcesService } from '../document-list/services/custom-resources.service';
|
import { CustomResourcesService } from '../document-list/services/custom-resources.service';
|
||||||
import { NodeEntryEvent, ShareDataRow } from '../document-list';
|
import { NodeEntryEvent, ShareDataRow } from '../document-list';
|
||||||
@@ -69,14 +67,11 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
DEFAULT_PAGINATION: Pagination = new Pagination({
|
DEFAULT_PAGINATION: Pagination = new Pagination({
|
||||||
maxItems: 25,
|
maxItems: 25,
|
||||||
skipCount: 0,
|
skipCount: 0
|
||||||
totalItems: 0,
|
|
||||||
hasMoreItems: false
|
|
||||||
});
|
});
|
||||||
|
|
||||||
private showSiteList = true;
|
private showSiteList = true;
|
||||||
private showSearchField = true;
|
private showSearchField = true;
|
||||||
private showFiles = false;
|
|
||||||
|
|
||||||
/** If true will restrict the search and breadcrumbs to the currentFolderId */
|
/** If true will restrict the search and breadcrumbs to the currentFolderId */
|
||||||
@Input()
|
@Input()
|
||||||
@@ -196,7 +191,8 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
|
|||||||
@Input()
|
@Input()
|
||||||
set showFilesInResult(value: boolean) {
|
set showFilesInResult(value: boolean) {
|
||||||
if (value !== undefined && value !== null) {
|
if (value !== undefined && value !== null) {
|
||||||
this.showFiles = value;
|
const showFilesQuery = `TYPE:'cm:folder'${value ? " OR TYPE:'cm:content'" : ''}`;
|
||||||
|
this.queryBuilderService.addFilterQuery(showFilesQuery);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,8 +230,6 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
|
|||||||
breadcrumbFolderTitle: string | null = null;
|
breadcrumbFolderTitle: string | null = null;
|
||||||
startSiteGuid: string | null = null;
|
startSiteGuid: string | null = null;
|
||||||
|
|
||||||
pagination: PaginationModel = this.DEFAULT_PAGINATION;
|
|
||||||
|
|
||||||
@ViewChild(InfinitePaginationComponent, { static: true })
|
@ViewChild(InfinitePaginationComponent, { static: true })
|
||||||
infinitePaginationComponent: InfinitePaginationComponent;
|
infinitePaginationComponent: InfinitePaginationComponent;
|
||||||
|
|
||||||
@@ -248,8 +242,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
private onDestroy$ = new Subject<boolean>();
|
private onDestroy$ = new Subject<boolean>();
|
||||||
|
|
||||||
constructor(private contentNodeSelectorService: ContentNodeSelectorService,
|
constructor(private customResourcesService: CustomResourcesService,
|
||||||
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,
|
||||||
@@ -272,7 +265,23 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
|
|||||||
debounceTime(this.debounceSearch),
|
debounceTime(this.debounceSearch),
|
||||||
takeUntil(this.onDestroy$)
|
takeUntil(this.onDestroy$)
|
||||||
)
|
)
|
||||||
.subscribe(searchValue => this.search(searchValue));
|
.subscribe(searchValue => {
|
||||||
|
this.searchTerm = searchValue;
|
||||||
|
this.queryBuilderService.userQuery = searchValue;
|
||||||
|
this.queryBuilderService.update();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.queryBuilderService.updated
|
||||||
|
.pipe(takeUntil(this.onDestroy$))
|
||||||
|
.subscribe((queryBody: QueryBody) => {
|
||||||
|
if (queryBody) {
|
||||||
|
this.prepareDialogForNewSearch(queryBody);
|
||||||
|
this.queryBuilderService.execute(queryBody);
|
||||||
|
} else {
|
||||||
|
this.resetFolderToShow();
|
||||||
|
this.clearSearch();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.queryBuilderService.executed
|
this.queryBuilderService.executed
|
||||||
.pipe(takeUntil(this.onDestroy$))
|
.pipe(takeUntil(this.onDestroy$))
|
||||||
@@ -299,6 +308,8 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
|
|||||||
this.breadcrumbTransform = this.breadcrumbTransform ? this.breadcrumbTransform : null;
|
this.breadcrumbTransform = this.breadcrumbTransform ? this.breadcrumbTransform : null;
|
||||||
this.isSelectionValid = this.isSelectionValid ? this.isSelectionValid : defaultValidation;
|
this.isSelectionValid = this.isSelectionValid ? this.isSelectionValid : defaultValidation;
|
||||||
this.onFileUploadEvent();
|
this.onFileUploadEvent();
|
||||||
|
this.resetPagination();
|
||||||
|
this.setSearchScopeToNodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
@@ -365,17 +376,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
|
|||||||
this.siteId = chosenSite.entry.guid;
|
this.siteId = chosenSite.entry.guid;
|
||||||
this.setTitleIfCustomSite(chosenSite);
|
this.setTitleIfCustomSite(chosenSite);
|
||||||
this.siteChange.emit(chosenSite.entry.title);
|
this.siteChange.emit(chosenSite.entry.title);
|
||||||
this.updateResults();
|
this.queryBuilderService.update();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the searchTerm attribute and starts a new search
|
|
||||||
*
|
|
||||||
* @param searchTerm string value to search against
|
|
||||||
*/
|
|
||||||
search(searchTerm: string): void {
|
|
||||||
this.searchTerm = searchTerm;
|
|
||||||
this.updateResults();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -393,11 +394,33 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
|
|||||||
return folderNode;
|
return folderNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares the dialog for a new search
|
||||||
|
*/
|
||||||
|
prepareDialogForNewSearch(queryBody: QueryBody): void {
|
||||||
|
this.target = queryBody ? null : this.documentList;
|
||||||
|
if (this.target) {
|
||||||
|
this.infinitePaginationComponent.reset();
|
||||||
|
}
|
||||||
|
this.folderIdToShow = null;
|
||||||
|
this.loadingSearchResults = true;
|
||||||
|
this.addCorrespondingNodeIdsQuery();
|
||||||
|
this.resetChosenNode();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the search input and reset to last folder node in which search was performed
|
* Clear the search input and reset to last folder node in which search was performed
|
||||||
*/
|
*/
|
||||||
clear(): void {
|
clear(): void {
|
||||||
this.clearSearch();
|
this.searchTerm = '';
|
||||||
|
this.queryBuilderService.userQuery = '';
|
||||||
|
this.queryBuilderService.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the folder to be shown with the site selection or the initial landing folder
|
||||||
|
*/
|
||||||
|
resetFolderToShow(): void {
|
||||||
this.folderIdToShow = this.siteId || this.currentFolderId;
|
this.folderIdToShow = this.siteId || this.currentFolderId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,71 +430,52 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
|
|||||||
clearSearch() {
|
clearSearch() {
|
||||||
this.searchTerm = '';
|
this.searchTerm = '';
|
||||||
this.nodePaging = null;
|
this.nodePaging = null;
|
||||||
this.pagination.maxItems = this.pageSize;
|
this.resetPagination();
|
||||||
this.resetChosenNode();
|
this.resetChosenNode();
|
||||||
this.showingSearchResults = false;
|
this.showingSearchResults = false;
|
||||||
this.showingSearch.emit(this.showingSearchResults);
|
this.showingSearch.emit(this.showingSearchResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private addCorrespondingNodeIdsQuery() {
|
||||||
* Update the result list depending on the criteria
|
let extraParentFiltering = '';
|
||||||
*/
|
|
||||||
private updateResults(): void {
|
|
||||||
this.target = this.searchTerm.length > 0 ? null : this.documentList;
|
|
||||||
|
|
||||||
if (this.searchTerm.length === 0) {
|
|
||||||
this.clear();
|
|
||||||
} else {
|
|
||||||
this.startNewSearch();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load the first page of a new search result
|
|
||||||
*/
|
|
||||||
private startNewSearch(): void {
|
|
||||||
this.nodePaging = null;
|
|
||||||
this.pagination.maxItems = this.pageSize;
|
|
||||||
if (this.target) {
|
|
||||||
this.infinitePaginationComponent.reset();
|
|
||||||
}
|
|
||||||
this.chosenNode = null;
|
|
||||||
this.folderIdToShow = null;
|
|
||||||
this.querySearch();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform the call to searchService with the proper parameters
|
|
||||||
*/
|
|
||||||
private querySearch(): void {
|
|
||||||
this.loadingSearchResults = true;
|
|
||||||
|
|
||||||
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) => {
|
||||||
const query = this.contentNodeSelectorService.createQuery(this.searchTerm, this.siteId, this.pagination.skipCount, this.pagination.maxItems, nodeIds, this.showFiles);
|
if (nodeIds && nodeIds.length) {
|
||||||
this.queryBuilderService.execute(query);
|
nodeIds
|
||||||
},
|
.filter((id) => id !== this.siteId)
|
||||||
() => {
|
.forEach((extraId) => {
|
||||||
this.showSearchResults({ list: { entries: [] } });
|
extraParentFiltering += ` OR ANCESTOR:'workspace://SpacesStore/${extraId}'`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const parentFiltering = this.siteId ? `ANCESTOR:'workspace://SpacesStore/${this.siteId}'${extraParentFiltering}` : '';
|
||||||
|
this.queryBuilderService.addFilterQuery(parentFiltering);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const query = this.contentNodeSelectorService.createQuery(this.searchTerm, this.siteId, this.pagination.skipCount, this.pagination.maxItems, [], this.showFiles);
|
const parentFiltering = this.siteId ? `ANCESTOR:'workspace://SpacesStore/${this.siteId}'` : '';
|
||||||
this.queryBuilderService.execute(query);
|
this.queryBuilderService.addFilterQuery(parentFiltering);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setSearchScopeToNodes() {
|
||||||
|
const scope: RequestScope = {
|
||||||
|
locations: 'nodes'
|
||||||
|
};
|
||||||
|
this.queryBuilderService.setScope(scope);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the results of the search
|
* Show the results of the search
|
||||||
*
|
*
|
||||||
* @param results Search results
|
* @param results Search results
|
||||||
*/
|
*/
|
||||||
private showSearchResults(nodePaging: NodePaging): void {
|
private showSearchResults(results: NodePaging): void {
|
||||||
this.showingSearchResults = true;
|
this.showingSearchResults = true;
|
||||||
this.loadingSearchResults = false;
|
this.loadingSearchResults = false;
|
||||||
this.showingSearch.emit(this.showingSearchResults);
|
this.showingSearch.emit(this.showingSearchResults);
|
||||||
|
|
||||||
this.nodePaging = nodePaging;
|
this.nodePaging = results;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -505,14 +509,15 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
|
|||||||
/**
|
/**
|
||||||
* Loads the next batch of search results
|
* Loads the next batch of search results
|
||||||
*
|
*
|
||||||
* @param event Pagination object
|
* @param pagination Pagination object
|
||||||
*/
|
*/
|
||||||
getNextPageOfSearch(pagination: Pagination): void {
|
getNextPageOfSearch(pagination: Pagination): void {
|
||||||
this.infiniteScroll = true;
|
this.infiniteScroll = true;
|
||||||
this.pagination = pagination;
|
this.queryBuilderService.paging.maxItems = pagination.maxItems;
|
||||||
|
this.queryBuilderService.paging.skipCount = pagination.skipCount;
|
||||||
|
|
||||||
if (this.searchTerm.length > 0) {
|
if (this.searchTerm.length > 0) {
|
||||||
this.querySearch();
|
this.queryBuilderService.update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -541,8 +546,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));
|
||||||
const nodes: Node[] = validNodesEntity.map((node) => node.entry );
|
this.chosenNode = validNodesEntity.map((node) => node.entry );
|
||||||
this.chosenNode = nodes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setTitleIfCustomSite(site: SiteEntry) {
|
setTitleIfCustomSite(site: SiteEntry) {
|
||||||
@@ -578,4 +582,11 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
return selectedNodes;
|
return selectedNodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private resetPagination(): void {
|
||||||
|
this.queryBuilderService.paging = {
|
||||||
|
maxItems: this.pageSize,
|
||||||
|
skipCount: this.DEFAULT_PAGINATION.skipCount
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,120 +0,0 @@
|
|||||||
/*!
|
|
||||||
* @license
|
|
||||||
* Copyright 2019 Alfresco Software, Ltd.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { TestBed } from '@angular/core/testing';
|
|
||||||
import { setupTestBed } from '@alfresco/adf-core';
|
|
||||||
import { ContentNodeSelectorService } from './content-node-selector.service';
|
|
||||||
import { ContentTestingModule } from '../testing/content.testing.module';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
|
|
||||||
describe('ContentNodeSelectorService', () => {
|
|
||||||
|
|
||||||
let service: ContentNodeSelectorService;
|
|
||||||
|
|
||||||
setupTestBed({
|
|
||||||
imports: [
|
|
||||||
TranslateModule.forRoot(),
|
|
||||||
ContentTestingModule
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
service = TestBed.inject(ContentNodeSelectorService);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have the proper main query for search string', () => {
|
|
||||||
const queryBody = service.createQuery('nuka cola quantum');
|
|
||||||
|
|
||||||
expect(queryBody.query).toEqual({
|
|
||||||
query: 'nuka cola quantum*'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should make it including the path and allowableOperations', () => {
|
|
||||||
const queryBody = service.createQuery('nuka cola quantum');
|
|
||||||
|
|
||||||
expect(queryBody.include).toEqual(['path', 'allowableOperations', 'properties']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should make the search restricted to nodes only', () => {
|
|
||||||
const queryBody = service.createQuery('nuka cola quantum');
|
|
||||||
|
|
||||||
expect(queryBody.scope.locations).toEqual('nodes');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set the maxItems and paging properly by parameters', () => {
|
|
||||||
const queryBody = service.createQuery('nuka cola quantum', null, 10, 100);
|
|
||||||
|
|
||||||
expect(queryBody.paging.maxItems).toEqual(100);
|
|
||||||
expect(queryBody.paging.skipCount).toEqual(10);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set the maxItems and paging properly by default', () => {
|
|
||||||
const queryBody = service.createQuery('nuka cola quantum');
|
|
||||||
|
|
||||||
expect(queryBody.paging.maxItems).toEqual(25);
|
|
||||||
expect(queryBody.paging.skipCount).toEqual(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should filter the search for folders', () => {
|
|
||||||
const queryBody = service.createQuery('nuka cola quantum');
|
|
||||||
|
|
||||||
expect(queryBody.filterQueries).toContain({ query: "TYPE:'cm:folder'" });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should filter the search for files', () => {
|
|
||||||
const queryBody = service.createQuery('nuka cola quantum', null, 0, 25, [], true);
|
|
||||||
|
|
||||||
expect(queryBody.filterQueries).toContain({ query: "TYPE:'cm:folder' OR TYPE:'cm:content'" });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should filter out the "system-base" entries', () => {
|
|
||||||
const queryBody = service.createQuery('nuka cola quantum');
|
|
||||||
|
|
||||||
expect(queryBody.filterQueries).toContain({ query: 'NOT cm:creator:System' });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should filter for the provided ancestor if defined', () => {
|
|
||||||
const queryBody = service.createQuery('nuka cola quantum', 'diamond-city');
|
|
||||||
|
|
||||||
expect(queryBody.filterQueries).toContain({ query: 'ANCESTOR:\'workspace://SpacesStore/diamond-city\'' });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should NOT filter for the ancestor if NOT defined', () => {
|
|
||||||
const queryBody = service.createQuery('nuka cola quantum');
|
|
||||||
|
|
||||||
expect(queryBody.filterQueries).not.toContain({ query: 'ANCESTOR:\'workspace://SpacesStore/null\'' });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should filter for the extra provided ancestors if defined', () => {
|
|
||||||
const queryBody = service.createQuery('nuka cola quantum', 'diamond-city', 0, 25, ['extra-diamond-city']);
|
|
||||||
|
|
||||||
expect(queryBody.filterQueries).toContain({ query: 'ANCESTOR:\'workspace://SpacesStore/diamond-city\' OR ANCESTOR:\'workspace://SpacesStore/extra-diamond-city\'' });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should NOT filter for extra ancestors if an empty list of ids is provided', () => {
|
|
||||||
const queryBody = service.createQuery('nuka cola quantum', 'diamond-city', 0, 25, []);
|
|
||||||
|
|
||||||
expect(queryBody.filterQueries).toContain({ query: 'ANCESTOR:\'workspace://SpacesStore/diamond-city\'' });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should NOT filter for the extra provided ancestor if it\'s the same as the rootNodeId', () => {
|
|
||||||
const queryBody = service.createQuery('nuka cola quantum', 'diamond-city', 0, 25, ['diamond-city']);
|
|
||||||
|
|
||||||
expect(queryBody.filterQueries).toContain({ query: 'ANCESTOR:\'workspace://SpacesStore/diamond-city\'' });
|
|
||||||
});
|
|
||||||
});
|
|
@@ -1,62 +0,0 @@
|
|||||||
/*!
|
|
||||||
* @license
|
|
||||||
* Copyright 2019 Alfresco Software, Ltd.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { QueryBody } from '@alfresco/js-api';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal service used by ContentNodeSelector component.
|
|
||||||
*/
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class ContentNodeSelectorService {
|
|
||||||
|
|
||||||
createQuery(searchTerm: string, rootNodeId: string = null, skipCount: number = 0, maxItems: number = 25, extraNodeIds?: string[], showFiles?: boolean): QueryBody {
|
|
||||||
let extraParentFiltering = '';
|
|
||||||
|
|
||||||
if (extraNodeIds && extraNodeIds.length) {
|
|
||||||
extraNodeIds
|
|
||||||
.filter((id) => id !== rootNodeId)
|
|
||||||
.forEach((extraId) => {
|
|
||||||
extraParentFiltering += ` OR ANCESTOR:'workspace://SpacesStore/${extraId}'`;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const parentFiltering = rootNodeId ? [{ query: `ANCESTOR:'workspace://SpacesStore/${rootNodeId}'${extraParentFiltering}` }] : [];
|
|
||||||
|
|
||||||
return {
|
|
||||||
query: {
|
|
||||||
query: `${searchTerm}*`
|
|
||||||
},
|
|
||||||
include: ['path', 'allowableOperations', 'properties'],
|
|
||||||
paging: {
|
|
||||||
maxItems: maxItems,
|
|
||||||
skipCount: skipCount
|
|
||||||
},
|
|
||||||
filterQueries: [
|
|
||||||
{ query: `TYPE:'cm:folder'${showFiles ? " OR TYPE:'cm:content'" : ''}` },
|
|
||||||
{ query: 'NOT cm:creator:System' },
|
|
||||||
...parentFiltering
|
|
||||||
],
|
|
||||||
scope: {
|
|
||||||
locations: 'nodes'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@@ -19,7 +19,6 @@ export * from './name-location-cell/name-location-cell.component';
|
|||||||
export * from './content-node-selector.component-data.interface';
|
export * from './content-node-selector.component-data.interface';
|
||||||
export * from './content-node-selector-panel.component';
|
export * from './content-node-selector-panel.component';
|
||||||
export * from './content-node-selector.component';
|
export * from './content-node-selector.component';
|
||||||
export * from './content-node-selector.service';
|
|
||||||
export * from './content-node-dialog.service';
|
export * from './content-node-dialog.service';
|
||||||
export * from './content-node-selector-panel.service';
|
export * from './content-node-selector-panel.service';
|
||||||
|
|
||||||
|
@@ -21,3 +21,4 @@ export * from './search.component.mock';
|
|||||||
export * from './search.service.mock';
|
export * from './search.service.mock';
|
||||||
export * from './search-filter-mock';
|
export * from './search-filter-mock';
|
||||||
export * from './sites-dropdown.component.mock';
|
export * from './sites-dropdown.component.mock';
|
||||||
|
export * from './search-query.mock';
|
||||||
|
41
lib/content-services/src/lib/mock/search-query.mock.ts
Normal file
41
lib/content-services/src/lib/mock/search-query.mock.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { QueryBody } from '@alfresco/js-api';
|
||||||
|
|
||||||
|
export const mockQueryBody: QueryBody = <QueryBody> {
|
||||||
|
query: {
|
||||||
|
query: '(search-term)',
|
||||||
|
language: 'afts'
|
||||||
|
},
|
||||||
|
include: ['path', 'allowableOperations'],
|
||||||
|
paging: {
|
||||||
|
maxItems: 25,
|
||||||
|
skipCount: 0
|
||||||
|
},
|
||||||
|
fields: undefined,
|
||||||
|
filterQueries: [],
|
||||||
|
facetQueries: null,
|
||||||
|
facetIntervals: null,
|
||||||
|
facetFields: null,
|
||||||
|
sort: [],
|
||||||
|
scope: {
|
||||||
|
locations: 'nodes'
|
||||||
|
},
|
||||||
|
highlight: null,
|
||||||
|
facetFormat: 'V2'
|
||||||
|
};
|
@@ -24,7 +24,8 @@ import {
|
|||||||
RequestFacetField,
|
RequestFacetField,
|
||||||
RequestSortDefinitionInner,
|
RequestSortDefinitionInner,
|
||||||
ResultSetPaging,
|
ResultSetPaging,
|
||||||
RequestHighlight
|
RequestHighlight,
|
||||||
|
RequestScope
|
||||||
} from '@alfresco/js-api';
|
} from '@alfresco/js-api';
|
||||||
import { SearchCategory } from './search-category.interface';
|
import { SearchCategory } from './search-category.interface';
|
||||||
import { FilterQuery } from './filter-query.interface';
|
import { FilterQuery } from './filter-query.interface';
|
||||||
@@ -52,6 +53,7 @@ export abstract class BaseQueryBuilderService {
|
|||||||
paging: { maxItems?: number; skipCount?: number } = null;
|
paging: { maxItems?: number; skipCount?: number } = null;
|
||||||
sorting: SearchSortingDefinition[] = [];
|
sorting: SearchSortingDefinition[] = [];
|
||||||
sortingOptions: SearchSortingDefinition[] = [];
|
sortingOptions: SearchSortingDefinition[] = [];
|
||||||
|
private scope: RequestScope;
|
||||||
|
|
||||||
protected userFacetBuckets: { [key: string]: FacetFieldBucket[] } = {};
|
protected userFacetBuckets: { [key: string]: FacetFieldBucket[] } = {};
|
||||||
|
|
||||||
@@ -193,6 +195,14 @@ export abstract class BaseQueryBuilderService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setScope(scope: RequestScope) {
|
||||||
|
this.scope = scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
getScope(): RequestScope {
|
||||||
|
return this.scope;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the current query and triggers the `updated` event.
|
* Builds the current query and triggers the `updated` event.
|
||||||
*/
|
*/
|
||||||
@@ -265,6 +275,10 @@ export abstract class BaseQueryBuilderService {
|
|||||||
highlight: this.highlight
|
highlight: this.highlight
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (this.scope) {
|
||||||
|
result['scope'] = this.scope;
|
||||||
|
}
|
||||||
|
|
||||||
result['facetFormat'] = 'V2';
|
result['facetFormat'] = 'V2';
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@@ -618,4 +618,47 @@ describe('SearchQueryBuilder', () => {
|
|||||||
|
|
||||||
builder.execute();
|
builder.execute();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should include contain the path and allowableOperations by default', () => {
|
||||||
|
const builder = new SearchQueryBuilderService(buildConfig({}), null);
|
||||||
|
builder.userQuery = 'nuka cola quantum';
|
||||||
|
const queryBody = builder.buildQuery();
|
||||||
|
|
||||||
|
expect(queryBody.include).toEqual(['path', 'allowableOperations']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch the include config from the app config', () => {
|
||||||
|
const includeConfig = ['path', 'allowableOperations', 'properties'];
|
||||||
|
const config: SearchConfiguration = {
|
||||||
|
include: includeConfig
|
||||||
|
};
|
||||||
|
const builder = new SearchQueryBuilderService(buildConfig(config), null);
|
||||||
|
builder.userQuery = 'nuka cola quantum';
|
||||||
|
const queryBody = builder.buildQuery();
|
||||||
|
|
||||||
|
expect(queryBody.include).toEqual(includeConfig);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should the query contain the pagination', () => {
|
||||||
|
const builder = new SearchQueryBuilderService(buildConfig({}), null);
|
||||||
|
builder.userQuery = 'nuka cola quantum';
|
||||||
|
const mockPagination = {
|
||||||
|
maxItems: 10,
|
||||||
|
skipCount: 0
|
||||||
|
};
|
||||||
|
builder.paging = mockPagination;
|
||||||
|
const queryBody = builder.buildQuery();
|
||||||
|
|
||||||
|
expect(queryBody.paging).toEqual(mockPagination);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should the query contain the scope in case it is defined', () => {
|
||||||
|
const builder = new SearchQueryBuilderService(buildConfig({}), null);
|
||||||
|
const mockScope = { locations: 'mock-location' };
|
||||||
|
builder.userQuery = 'nuka cola quantum';
|
||||||
|
builder.setScope(mockScope);
|
||||||
|
const queryBody = builder.buildQuery();
|
||||||
|
|
||||||
|
expect(queryBody.scope).toEqual(mockScope);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -19,7 +19,7 @@ import { Component, Inject, ViewEncapsulation, ViewChild } from '@angular/core';
|
|||||||
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||||
import { ExternalAlfrescoApiService, AlfrescoApiService, LoginDialogPanelComponent, SearchService, TranslationService, AuthenticationService, SitesService } from '@alfresco/adf-core';
|
import { ExternalAlfrescoApiService, AlfrescoApiService, LoginDialogPanelComponent, SearchService, TranslationService, AuthenticationService, SitesService } from '@alfresco/adf-core';
|
||||||
import { AttachFileWidgetDialogComponentData } from './attach-file-widget-dialog-component.interface';
|
import { AttachFileWidgetDialogComponentData } from './attach-file-widget-dialog-component.interface';
|
||||||
import { DocumentListService, ContentNodeSelectorService } from '@alfresco/adf-content-services';
|
import { DocumentListService } from '@alfresco/adf-content-services';
|
||||||
import { Node } from '@alfresco/js-api';
|
import { Node } from '@alfresco/js-api';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -31,7 +31,6 @@ import { Node } from '@alfresco/js-api';
|
|||||||
AuthenticationService,
|
AuthenticationService,
|
||||||
DocumentListService,
|
DocumentListService,
|
||||||
SitesService,
|
SitesService,
|
||||||
ContentNodeSelectorService,
|
|
||||||
SearchService,
|
SearchService,
|
||||||
{ provide: AlfrescoApiService, useClass: ExternalAlfrescoApiService} ]
|
{ provide: AlfrescoApiService, useClass: ExternalAlfrescoApiService} ]
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user