diff --git a/docs/content-services/services/tag.service.md b/docs/content-services/services/tag.service.md index 5ad49b779f..18e22d2ae5 100644 --- a/docs/content-services/services/tag.service.md +++ b/docs/content-services/services/tag.service.md @@ -18,9 +18,10 @@ Manages tags in Content Services. - _nodeId:_ `string` - ID of the target node - _tagName:_ `string` - Name of the tag to add - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`TagEntry`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/TagEntry.md)`>` - TagEntry object (defined in JS-API) with details of the new tag -- **getAllTheTags**(opts?: `any`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`TagPaging`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/TagPaging.md)`>`
+- **getAllTheTags**(opts?: `any`, includedCounts?: `boolean`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`TagPaging`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/TagPaging.md)`>`
Gets a list of all the tags already defined in the repository. - _opts:_ `any` - (Optional) Options supported by JS-API + - _includedCounts:_ `boolean` - (Optional) True if count field should be included in response object for each tag, false otherwise. - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`TagPaging`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/TagPaging.md)`>` - TagPaging object (defined in JS-API) containing the tags - **getTagsByNodeId**(nodeId: `string`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`TagPaging`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/TagPaging.md)`>`
Gets a list of tags added to a node. @@ -44,11 +45,13 @@ Manages tags in Content Services. - _tagId:_ `string` - The identifier of a tag. - _tagBody:_ `TagBody` - The updated tag. - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`` - Updated tag. -- **searchTags**(name: `string`, skipCount: `number`): [`Observable`](http://reactivex.io/documentation/observable.html)``
+- **searchTags**(name: `string`, sorting?: `{ orderBy: string, direction: string }`, includedCounts?: `boolean`, skipCount: `number`): [`Observable`](http://reactivex.io/documentation/observable.html)``
Find tags which name contains searched name. - - _name:_ `string` - Value for name which should be used during searching tags. - - _skipCount:_ `number` - Specify how many first results should be skipped. Default 0. - - _maxItems:_ `number` - Specify max number of returned tags. Default is specified by UserPreferencesService. + - _name:_ `string` - Value for name which should be used during searching tags. + - _sorting:_ `{ orderBy: string, direction: string }` - Object which configures sorting. OrderBy field specifies field used for sorting, direction specified ascending or descending direction. Default sorting is ascending by tag field. + - _includedCounts:_ `boolean` - True if count field should be included in response object for each tag, false otherwise. + - _skipCount:_ `number` - Specify how many first results should be skipped. Default 0. + - _maxItems:_ `number` - Specify max number of returned tags. Default is specified by UserPreferencesService. - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`` - Found tags which name contains searched name. - **getCountersForTags**(tags: `string[]`): [`Observable`](http://reactivex.io/documentation/observable.html)``
Get usage counters for passed tags. diff --git a/lib/content-services/src/lib/tag/services/tag.service.spec.ts b/lib/content-services/src/lib/tag/services/tag.service.spec.ts index e6a204d11e..f3b3a8b1f0 100644 --- a/lib/content-services/src/lib/tag/services/tag.service.spec.ts +++ b/lib/content-services/src/lib/tag/services/tag.service.spec.ts @@ -21,22 +21,7 @@ import { fakeAsync, TestBed, tick } from '@angular/core/testing'; import { ContentTestingModule } from '../../testing/content.testing.module'; import { TranslateModule } from '@ngx-translate/core'; import { throwError } from 'rxjs'; -import { - Pagination, - RequestQuery, - RequestSortDefinitionInner, - ResultNode, - ResultSetContext, - ResultSetContextFacetQueries, - ResultSetPaging, - ResultSetPagingList, - ResultSetRowEntry, - Tag, - TagBody, - TagEntry, - TagPaging, - TagPagingList -} from '@alfresco/js-api'; +import { Pagination, Tag, TagBody, TagEntry, TagPaging, TagPagingList } from '@alfresco/js-api'; describe('TagService', () => { @@ -44,6 +29,18 @@ describe('TagService', () => { let logService: LogService; let userPreferencesService: UserPreferencesService; + const mockTagPaging = (): TagPaging => { + const tagPaging = new TagPaging(); + tagPaging.list = new TagPagingList(); + const tag = new TagEntry(); + tag.entry = new Tag(); + tag.entry.id = 'some id'; + tag.entry.tag = 'some name'; + tagPaging.list.entries = [tag]; + tagPaging.list.pagination = new Pagination(); + return tagPaging; + }; + setupTestBed({ imports: [ TranslateModule.forRoot(), @@ -135,58 +132,44 @@ describe('TagService', () => { })); }); - describe('searchTags', () => { - let result: ResultSetPaging; + describe('getAllTheTags', () => { + let result: TagPaging; beforeEach(() => { - result = new ResultSetPaging(); - result.list = new ResultSetPagingList(); - const tag = new ResultSetRowEntry(); - tag.entry = new ResultNode(); - tag.entry.id = 'some id'; - tag.entry.name = 'some name'; - result.list.entries = [tag]; - result.list.pagination = new Pagination(); + result = mockTagPaging(); }); - it('should call search on searchApi with correct parameters', () => { - const searchSpy = spyOn(service.searchApi, 'search').and.returnValue(Promise.resolve(result)); - const name = 'test'; - const maxItems = 25; - spyOnProperty(userPreferencesService, 'paginationSize').and.returnValue(maxItems); + it('should call listTags on TagsApi with correct parameters when includedCounts is true', () => { + spyOn(service.tagsApi, 'listTags').and.returnValue(Promise.resolve(result)); + const skipCount = 10; - const sortingByName = new RequestSortDefinitionInner(); - sortingByName.field = 'cm:name'; - sortingByName.ascending = true; - sortingByName.type = RequestSortDefinitionInner.TypeEnum.FIELD; + service.getAllTheTags({ + skipCount + }, true); + expect(service.tagsApi.listTags).toHaveBeenCalledWith({ + include: ['count'], + skipCount + }); + }); - service.searchTags(name); - expect(searchSpy).toHaveBeenCalledWith({ - query: { - language: RequestQuery.LanguageEnum.Afts, - query: `PATH:"/cm:categoryRoot/cm:taggable/*" AND cm:name:"*${name}*"` - }, - paging: { - skipCount: 0, - maxItems - }, - sort: [sortingByName] + it('should call listTags on TagsApi with correct parameters when includedCounts is false', () => { + spyOn(service.tagsApi, 'listTags').and.returnValue(Promise.resolve(result)); + const skipCount = 10; + + service.getAllTheTags({ + skipCount + }, false); + expect(service.tagsApi.listTags).toHaveBeenCalledWith({ + include: undefined, + skipCount }); }); it('should return observable which emits paging object for tags', fakeAsync(() => { - spyOn(service.searchApi, 'search').and.returnValue(Promise.resolve(result)); + spyOn(service.tagsApi, 'listTags').and.returnValue(Promise.resolve(result)); - service.searchTags('test').subscribe((tagsResult) => { - const tagPaging = new TagPaging(); - tagPaging.list = new TagPagingList(); - const tagEntry = new TagEntry(); - tagEntry.entry = new Tag(); - tagEntry.entry.id = 'some id'; - tagEntry.entry.tag = 'some name'; - tagPaging.list.entries = [tagEntry]; - tagPaging.list.pagination = new Pagination(); - expect(tagsResult).toEqual(tagPaging); + service.getAllTheTags().subscribe((tagsResult) => { + expect(tagsResult).toEqual(result); }); tick(); })); @@ -194,7 +177,78 @@ describe('TagService', () => { it('should call error on logService when error occurs during fetching paging object for tags', fakeAsync(() => { spyOn(logService, 'error'); const error: string = 'Some error'; - spyOn(service.searchApi, 'search').and.returnValue(Promise.reject(error)); + spyOn(service.tagsApi, 'listTags').and.returnValue(Promise.reject(error)); + service.getAllTheTags().subscribe({ + error: () => { + expect(logService.error).toHaveBeenCalledWith(error); + } + }); + tick(); + })); + }); + + describe('searchTags', () => { + let result: TagPaging; + + beforeEach(() => { + result = mockTagPaging(); + }); + + it('should call listTags on TagsApi with correct default parameters', () => { + spyOn(service.tagsApi, 'listTags').and.returnValue(Promise.resolve(result)); + const name = 'test'; + const maxItems = 25; + spyOnProperty(userPreferencesService, 'paginationSize').and.returnValue(maxItems); + + service.searchTags(name); + expect(service.tagsApi.listTags).toHaveBeenCalledWith({ + tag: `*${name}*`, + skipCount: 0, + maxItems, + sorting: { + orderBy: 'tag', + direction: 'asc' + }, + matching: true, + include: undefined + }); + }); + + it('should call listTags on TagsApi with correct specified parameters', () => { + spyOn(service.tagsApi, 'listTags').and.returnValue(Promise.resolve(result)); + const name = 'test'; + spyOnProperty(userPreferencesService, 'paginationSize').and.returnValue(25); + const maxItems = 100; + const skipCount = 200; + const sorting = { + orderBy: 'id', + direction: 'asc' + }; + + service.searchTags(name, sorting, true, skipCount, maxItems); + expect(service.tagsApi.listTags).toHaveBeenCalledWith({ + tag: `*${name}*`, + skipCount, + maxItems, + sorting, + matching: true, + include: ['count'] + }); + }); + + it('should return observable which emits paging object for tags', fakeAsync(() => { + spyOn(service.tagsApi, 'listTags').and.returnValue(Promise.resolve(result)); + + service.searchTags('test').subscribe((tagsResult) => { + expect(tagsResult).toEqual(result); + }); + tick(); + })); + + it('should call error on logService when error occurs during fetching paging object for tags', fakeAsync(() => { + spyOn(logService, 'error'); + const error: string = 'Some error'; + spyOn(service.tagsApi, 'listTags').and.returnValue(Promise.reject(error)); service.searchTags('test').subscribe({ error: () => { expect(logService.error).toHaveBeenCalledWith(error); @@ -244,83 +298,6 @@ describe('TagService', () => { })); }); - describe('getCountersForTags', () => { - let result: ResultSetPaging; - const tag1 = 'tag 1'; - - beforeEach(() => { - result = new ResultSetPaging(); - result.list = new ResultSetPagingList(); - result.list.context = new ResultSetContext(); - const facetQuery = new ResultSetContextFacetQueries(); - facetQuery.count = 2; - facetQuery.label = tag1; - facetQuery.filterQuery = `TAG:"${tag1}"`; - result.list.context.facetQueries = [facetQuery]; - }); - - it('should call search on searchApi with correct parameters', () => { - const tag2 = 'tag 2'; - spyOn(service.searchApi, 'search').and.returnValue(Promise.resolve(result)); - - service.getCountersForTags([tag1, tag2]); - expect(service.searchApi.search).toHaveBeenCalledWith({ - query: { - language: RequestQuery.LanguageEnum.Afts, - query: `*` - }, - facetQueries: [{ - query: `TAG:"${tag1}"`, - label: tag1 - }, { - query: `TAG:"${tag2}"`, - label: tag2 - }] - }); - }); - - it('should return observable which emits facet queries with counters for tags', (done) => { - spyOn(service.searchApi, 'search').and.returnValue(Promise.resolve(result)); - service.getCountersForTags([tag1]).subscribe((counters) => { - expect(counters).toBe(result.list.context.facetQueries); - done(); - }); - }); - - it('should return observable which emits undefined if context is not present', (done) => { - result.list.context = undefined; - - spyOn(service.searchApi, 'search').and.returnValue(Promise.resolve(result)); - service.getCountersForTags([tag1]).subscribe((counters) => { - expect(counters).toBeUndefined(); - done(); - }); - }); - - it('should return observable which emits undefined if list is not present', (done) => { - result.list = undefined; - - spyOn(service.searchApi, 'search').and.returnValue(Promise.resolve(result)); - service.getCountersForTags([tag1]).subscribe((counters) => { - expect(counters).toBeUndefined(); - done(); - }); - }); - - it('should call error on logService when error occurs during fetching counters for tags', fakeAsync(() => { - spyOn(logService, 'error'); - const error = 'Some error'; - spyOn(service.searchApi, 'search').and.returnValue(Promise.reject(error)); - - service.getCountersForTags([tag1]).subscribe({ - error: () => { - expect(logService.error).toHaveBeenCalledWith(error); - } - }); - tick(); - })); - }); - describe('findTagByName', () => { let tagPaging: TagPaging; const tagName = 'some tag'; @@ -335,7 +312,8 @@ describe('TagService', () => { service.findTagByName(tagName); expect(service.tagsApi.listTags).toHaveBeenCalledWith({ - name: tagName + tag: tagName, + include: undefined }); }); diff --git a/lib/content-services/src/lib/tag/services/tag.service.ts b/lib/content-services/src/lib/tag/services/tag.service.ts index c93a8a6fb7..ff0747d270 100644 --- a/lib/content-services/src/lib/tag/services/tag.service.ts +++ b/lib/content-services/src/lib/tag/services/tag.service.ts @@ -19,18 +19,7 @@ import { AlfrescoApiService, LogService, UserPreferencesService } from '@alfresc import { EventEmitter, Injectable, Output } from '@angular/core'; import { from, Observable, throwError } from 'rxjs'; import { catchError, map, tap } from 'rxjs/operators'; -import { - RequestQuery, - RequestSortDefinitionInner, - ResultSetContextFacetQueries, - SearchApi, - Tag, - TagBody, - TagEntry, - TagPaging, - TagPagingList, - TagsApi -} from '@alfresco/js-api'; +import { TagBody, TagEntry, TagPaging, TagsApi } from '@alfresco/js-api'; @Injectable({ providedIn: 'root' @@ -43,12 +32,6 @@ export class TagService { return this._tagsApi; } - private _searchApi: SearchApi; - get searchApi(): SearchApi { - this._searchApi = this._searchApi ?? new SearchApi(this.apiService.getInstance()); - return this._searchApi; - } - /** Emitted when tag information is updated. */ @Output() refresh = new EventEmitter(); @@ -74,11 +57,14 @@ export class TagService { * Gets a list of all the tags already defined in the repository. * * @param opts Options supported by JS-API + * @param includedCounts True if count field should be included in response object for each tag, false otherwise. * @returns TagPaging object (defined in JS-API) containing the tags */ - getAllTheTags(opts?: any): Observable { - return from(this.tagsApi.listTags(opts)) - .pipe(catchError((err) => this.handleError(err))); + getAllTheTags(opts?: any, includedCounts?: boolean): Observable { + return from(this.tagsApi.listTags({ + include: includedCounts ? ['count'] : undefined, + ...opts + })).pipe(catchError((err) => this.handleError(err))); } /** @@ -157,61 +143,25 @@ export class TagService { * Find tags which name contains searched name. * * @param name Value for name which should be used during searching tags. + * @param sorting Object which configures sorting. OrderBy field specifies field used for sorting, direction specified ascending or descending direction. + * Default sorting is ascending by tag field. + * @param includedCounts True if count field should be included in response object for each tag, false otherwise. * @param skipCount Specify how many first results should be skipped. Default 0. * @param maxItems Specify max number of returned tags. Default is specified by UserPreferencesService. * @returns Found tags which name contains searched name. */ - searchTags(name: string, skipCount = 0, maxItems?: number): Observable { + searchTags(name: string, sorting = { + orderBy: 'tag', + direction: 'asc' + }, includedCounts?: boolean, skipCount = 0, maxItems?: number): Observable { maxItems = maxItems || this.userPreferencesService.paginationSize; - const sortingByName: RequestSortDefinitionInner = new RequestSortDefinitionInner(); - sortingByName.field = 'cm:name'; - sortingByName.ascending = true; - sortingByName.type = RequestSortDefinitionInner.TypeEnum.FIELD; - return from(this.searchApi.search({ - query: { - language: RequestQuery.LanguageEnum.Afts, - query: `PATH:"/cm:categoryRoot/cm:taggable/*" AND cm:name:"*${name}*"` - }, - paging: { - skipCount, - maxItems - }, - sort: [sortingByName] - })).pipe(map((resultSetPaging) => { - const tagPaging = new TagPaging(); - tagPaging.list = new TagPagingList(); - tagPaging.list.pagination = resultSetPaging.list.pagination; - tagPaging.list.entries = resultSetPaging.list.entries.map((resultEntry) => { - const tagEntry = new TagEntry(); - tagEntry.entry = new Tag(); - tagEntry.entry.tag = resultEntry.entry.name; - tagEntry.entry.id = resultEntry.entry.id; - return tagEntry; - }); - return tagPaging; - }), catchError((error) => this.handleError(error))); - } - - /** - * Get usage counters for passed tags. - * - * @param tags Array of tags names for which there should be returned counters. - * @returns Array of usage counters for specified tags. - */ - getCountersForTags(tags: string[]): Observable { - return from(this.searchApi.search({ - query: { - language: RequestQuery.LanguageEnum.Afts, - query: `*` - }, - facetQueries: tags.map((tag) => ({ - query: `TAG:"${tag}"`, - label: tag - })) - })).pipe( - map((paging) => paging.list?.context?.facetQueries), - catchError((error) => this.handleError(error)) - ); + return this.getAllTheTags({ + tag: `*${name}*`, + skipCount, + maxItems, + sorting, + matching: true + }, includedCounts).pipe(catchError((err) => this.handleError(err))); } /** @@ -221,7 +171,7 @@ export class TagService { * @returns Found tag which name matches exactly to passed name. */ findTagByName(name: string): Observable { - return this.getAllTheTags({ name }).pipe( + return this.getAllTheTags({ tag: name }).pipe( map((result) => result.list.entries[0]), catchError((error) => this.handleError(error)) );