diff --git a/scilog/src/app/core/remote-data.service.spec.ts b/scilog/src/app/core/remote-data.service.spec.ts index d23a96bc..dac047e5 100644 --- a/scilog/src/app/core/remote-data.service.spec.ts +++ b/scilog/src/app/core/remote-data.service.spec.ts @@ -3,8 +3,9 @@ import { TestBed } from '@angular/core/testing'; import { RemoteDataService } from '@shared/remote-data.service'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { AppConfigService } from '../app-config.service'; -import { LogbookDataService, LogbookItemDataService } from './remote-data.service'; +import { LogbookDataService, LogbookItemDataService, SearchDataService } from './remote-data.service'; import { of } from 'rxjs'; +import { WidgetItemConfig } from './model/config'; const getConfig = () => ({}); @@ -23,6 +24,36 @@ describe('RemoteDataService', () => { expect(service).toBeTruthy(); }); + it('should test staticFilters', () => { + expect(service['staticFilters']()).toEqual([{ snippetType: { inq: ["paragraph", "image"] } }, { deleted: false }]); + }); + + it('should test addIncludeScope', () => { + expect(service['addIncludeScope']()).toEqual({ + scope: + { + include: [{ + relation: 'subsnippets', + scope: { + where: { snippetType: 'edit' } + } + }] + } + }); + }); + + it('should test tagsFilter', () => { + expect(service['tagsFilter']({ tags: ['a', 'b'], excludeTags: ['c', 'd'] })).toEqual( + [{ tags: { inq: ['a', 'b'] } }, { tags: { nin: ['c', 'd'] } }] + ); + }); + + it('should test parentFilter', () => { + expect(service['parentFilter']({ targetId: 'target', additionalLogbooks: ['add1', 'add2'] })).toEqual( + [{ parentId: { inq: ['target', 'add1', 'add2'] } }] + ); + }); + }); @@ -58,11 +89,17 @@ describe('LogbookItemDataService', () => { showSnippetHeader: false } }; - let filter = LogbookItemDataService._prepareFilters(config); - expect(filter["where"].and[0]).toEqual({ "and": [{ "or": [{ "snippetType": "paragraph" }, { "snippetType": "image" }] }, { "deleted": false }] }); + let filter = service['_prepareFilters'](config); + expect(filter["where"]).toEqual({ + and: [ + { snippetType: { inq: ["paragraph", "image"] } }, + { deleted: false }, + { parentId: { inq: ['target'] } } + ] + }); }); - it('should include paragraph targetId filter', () => { + it('should include paragraph targetIds and tags', () => { let config = { general: { type: 'logbook', @@ -70,8 +107,9 @@ describe('LogbookItemDataService', () => { }, filter: { targetId: "target", - additionalLogbooks: [], - tags: [] + additionalLogbooks: ['target2'], + tags: ['a', 'b'], + excludeTags: ['c', 'd'] }, view: { order: ['defaultOrder ASC'], @@ -79,8 +117,28 @@ describe('LogbookItemDataService', () => { showSnippetHeader: false } }; - let filter = LogbookItemDataService._prepareFilters(config); - expect(filter["where"].and[1]).toEqual({ "or": [{ "parentId": { "eq": config.filter.targetId } }] }); + let filter = service['_prepareFilters'](config); + expect(filter["where"]).toEqual({ + and: [ + { snippetType: { inq: ["paragraph", "image"] } }, + { deleted: false }, + { tags: { inq: ['a', 'b'] } }, + { tags: { nin: ['c', 'd'] } }, + { parentId: { inq: ['target', 'target2'] } } + ] + }); + expect(filter['include']).toEqual([{ + relation: 'subsnippets', + scope: + { + include: [{ + relation: 'subsnippets', + scope: { + where: { snippetType: 'edit' } + } + }] + } + }]); }); it('should include tag filter', () => { @@ -100,7 +158,7 @@ describe('LogbookItemDataService', () => { showSnippetHeader: false } }; - let filter = LogbookItemDataService._prepareFilters(config); + let filter = service['_prepareFilters'](config); expect(filter["where"].and[2]).toEqual({ "tags": { "inq": config.filter.tags } }); }); @@ -121,7 +179,7 @@ describe('LogbookItemDataService', () => { showSnippetHeader: false } }; - let filter = LogbookItemDataService._prepareFilters(config); + let filter = service['_prepareFilters'](config); expect(filter["where"].and[2]).toEqual({ "tags": { "inq": config.filter.tags } }); }); @@ -143,7 +201,7 @@ describe('LogbookItemDataService', () => { showSnippetHeader: false } }; - let filter = LogbookItemDataService._prepareFilters(config); + let filter = service['_prepareFilters'](config); expect(filter["where"].and[3]).toEqual({ "tags": { "nin": config.filter.excludeTags } }); }); @@ -165,7 +223,7 @@ describe('LogbookItemDataService', () => { showSnippetHeader: false } }; - let filter = LogbookItemDataService._prepareFilters(config); + let filter = service['_prepareFilters'](config); expect(filter["where"].and[2]).toEqual({ "tags": { "inq": config.filter.tags } }); expect(filter["where"].and[3]).toEqual({ "tags": { "nin": config.filter.excludeTags } }); }); @@ -208,3 +266,74 @@ describe('LogbookDataService', () => { }); }); + +describe('SearchDataService', () => { + let service: SearchDataService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [{ provide: AppConfigService, useValue: { getConfig } }], + }); + service = TestBed.inject(SearchDataService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('should be created', () => { + const config = { + general: { + type: 'logbook', + title: 'Logbook view', + }, + filter: { + targetId: "target", + additionalLogbooks: ['target2'], + tags: ['a', 'b'], + excludeTags: ['c', 'd'] + }, + view: { + order: ['defaultOrder ASC'], + hideMetadata: false, + showSnippetHeader: false + } + }; + const filter = service['_prepareFilters'](config); + expect(filter["where"]).toEqual({ + and: [ + { snippetType: { inq: ["paragraph", "image"] } }, + { deleted: false }, + { tags: { inq: ['a', 'b'] } }, + { tags: { nin: ['c', 'd'] } }, + { parentId: { inq: ['target', 'target2'] } } + ] + }); + + expect(filter['include']).toEqual([{ + relation: 'subsnippets', + }]); + }); + + it('should test addIncludeScope', () => { + expect(service['addIncludeScope']()).toEqual({}); + }); + + [ + ['#search', '%23search'], + ['@search', '%40search'], + ['search', 'search'], + ].forEach(t => { + it(`should test getDataBuffer ${t[0]}`, () => { + service.searchString = t[0]; + const getSnippetSpy = spyOn(service, 'getSnippets').and.returnValue({ toPromise: () => { } }); + spyOn(service, '_prepareParams'); + service.getDataBuffer(0, 0, {} as WidgetItemConfig); + expect(getSnippetSpy.calls.mostRecent().args[0]).toEqual( + `basesnippets/search=${t[1]}` + ) + }); + }); + +}); diff --git a/scilog/src/app/core/remote-data.service.ts b/scilog/src/app/core/remote-data.service.ts index 137e4ea1..e0b9496e 100644 --- a/scilog/src/app/core/remote-data.service.ts +++ b/scilog/src/app/core/remote-data.service.ts @@ -1,6 +1,6 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Basesnippets, Filecontainer } from '@model/basesnippets'; +import { Basesnippets } from '@model/basesnippets'; import { Filesnippet } from '@model/filesnippet'; import { Logbooks } from '@model/logbooks'; import { LinkType } from '@model/paragraphs'; @@ -67,28 +67,13 @@ export class RemoteDataService { return this.postSnippet("filesnippet/files", formData, headersFile).toPromise(); } - // getFile(imageSnippetUrl: string): Promise { - // return this.getSnippets(imageSnippetUrl, { responseType: 'blob' }).toPromise(); - // } - -} - -@Injectable({ - providedIn: 'root' -}) -export class LogbookItemDataService extends RemoteDataService { - - private _searchString = ""; - - public get searchString(): string { - return this._searchString; - } - public set searchString(value: string) { - this._searchString = value; + protected _prepareParams(config: WidgetItemConfig, index: number = 0, count: number = Infinity): HttpParams { + let params = new HttpParams(); + params = params.set('filter', JSON.stringify(this._prepareFilters(config, index, count))); + return params; } - - static _prepareFilters(config: WidgetItemConfig, index: number = 0, count: number = Infinity): Object { + protected _prepareFilters(config: WidgetItemConfig, index: number = 0, count: number = Infinity): Object { let httpFilter: Object = {}; if (typeof config.view.order != 'undefined') { httpFilter["order"] = config.view.order; @@ -96,32 +81,8 @@ export class LogbookItemDataService extends RemoteDataService { httpFilter["order"] = ["defaultOrder ASC"]; } - let whereFilter: Object[] = []; - whereFilter.push({ "and": [{ "or": [{ "snippetType": "paragraph" }, { "snippetType": "image" }] }, { "deleted": false }] }); - - let parentIds: string[] = []; - if ((config.filter?.targetId) && (config.filter.targetId.length > 1)) { - parentIds.push(config.filter.targetId); - } - if (config.filter.additionalLogbooks.length > 0) { - parentIds.push(...config.filter.additionalLogbooks); - } - if (parentIds.length > 0) { - let parentFilter = []; - for (let parent of parentIds) { - parentFilter.push({ "parentId": { "eq": parent } }); - } - whereFilter.push({ "or": parentFilter }); - } - - - if (config.filter.tags?.length > 0) { - whereFilter.push({ "tags": { "inq": config.filter.tags } }) - } - if (config.filter.excludeTags?.length > 0) { - whereFilter.push({ "tags": { "nin": config.filter.excludeTags } }) - } - httpFilter["where"] = { "and": whereFilter }; + httpFilter["include"] = [{ relation: "subsnippets", ...this.addIncludeScope() }]; + httpFilter["where"] = { "and": [...this.staticFilters(), ...this.tagsFilter(config.filter), ...this.parentFilter(config.filter)] }; if (count < Infinity) { httpFilter["limit"] = count; @@ -129,16 +90,58 @@ export class LogbookItemDataService extends RemoteDataService { if (index > 0) { httpFilter["skip"] = index; } - // console.log(httpFilter); - // console.log(tagContainer); - httpFilter["include"] = [{ "relation": "subsnippets", "scope": {include: [{"relation": "subsnippets", scope: {where: {snippetType: 'edit'}}}]} }]; return httpFilter; } - static _prepareParams(config: WidgetItemConfig, index: number = 0, count: number = Infinity): HttpParams { - let params = new HttpParams(); - params = params.set('filter', JSON.stringify(LogbookItemDataService._prepareFilters(config, index, count))); - return params; + private staticFilters() { + return [{ snippetType: { inq: ["paragraph", "image"] } }, { deleted: false }]; + } + + private tagsFilter(configFilter: { tags?: string[], excludeTags?: string[] }) { + const tagFilter = []; + if (configFilter?.tags?.length > 0) { + tagFilter.push({ tags: { inq: configFilter.tags } }); + } + if (configFilter?.excludeTags?.length > 0) { + tagFilter.push({ tags: { nin: configFilter.excludeTags } }); + } + return tagFilter; + } + + private parentFilter(configFilter: { targetId?: string, additionalLogbooks?: string[] }) { + const parentIds = [configFilter?.targetId, ...(configFilter?.additionalLogbooks ?? [])].filter(parentId => parentId); + if (parentIds.length === 0) return []; + return [{ parentId: { inq: parentIds } }]; + } + + protected addIncludeScope(): Object { + return { + scope: + { + include: [{ + relation: 'subsnippets', + scope: { + where: { snippetType: 'edit' } + } + }] + } + }; + } + +} + +@Injectable({ + providedIn: 'root' +}) +export class LogbookItemDataService extends RemoteDataService { + + private _searchString = ""; + + public get searchString(): string { + return this._searchString; + } + public set searchString(value: string) { + this._searchString = value; } getDataBuffer(index: number, count: number, config: WidgetItemConfig) { @@ -148,9 +151,9 @@ export class LogbookItemDataService extends RemoteDataService { headers = headers.set('Content-Type', 'application/json; charset=utf-8'); this._searchString = this._searchString.trim(); if (this._searchString.length == 0) { - return this.getSnippets('basesnippets', { headers: headers, params: LogbookItemDataService._prepareParams(config, index, count) }).toPromise(); + return this.getSnippets('basesnippets', { headers: headers, params: this._prepareParams(config, index, count) }).toPromise(); } else { - return this.getSnippets('basesnippets/search=' + this._searchString, { headers: headers, params: LogbookItemDataService._prepareParams(config, index, count) }).toPromise(); + return this.getSnippets('basesnippets/search=' + this._searchString, { headers: headers, params: this._prepareParams(config, index, count) }).toPromise(); } } @@ -195,7 +198,7 @@ export class LogbookItemDataService extends RemoteDataService { } async getCount(config: any) { - let filter = LogbookItemDataService._prepareFilters(config); + let filter = this._prepareFilters(config); // let whereFilter = filter["where"]; console.log(filter); let params = new HttpParams(); @@ -205,7 +208,7 @@ export class LogbookItemDataService extends RemoteDataService { } async getIndex(id: string, config: any) { - let filter = LogbookItemDataService._prepareFilters(config); + let filter = this._prepareFilters(config); console.log(filter); let params = new HttpParams(); params = params.set('filter', JSON.stringify(filter)); @@ -274,7 +277,7 @@ export class LogbookItemDataService extends RemoteDataService { exportLogbook(exportType: string, config: any, skip: number, limit: number): Promise { let headers = new HttpHeaders(); headers = headers.set('Content-Type', 'application/json'); - return this.getSnippets("basesnippets/export=" + exportType + "", { headers: headers, responseType: 'blob', params: LogbookItemDataService._prepareParams(config, skip, limit) }).toPromise(); + return this.getSnippets("basesnippets/export=" + exportType + "", { headers: headers, responseType: 'blob', params: this._prepareParams(config, skip, limit) }).toPromise(); } } @@ -533,7 +536,7 @@ export class TagDataService extends RemoteDataService { _config.view.order = ["defaultOrder DESC"]; } console.log(_config); - return this.getSnippets('basesnippets', { headers: headers, params: LogbookItemDataService._prepareParams(_config, 0, 1) }).toPromise(); + return this.getSnippets('basesnippets', { headers: headers, params: this._prepareParams(_config, 0, 1) }).toPromise(); } } @@ -608,6 +611,10 @@ export class SearchDataService extends RemoteDataService { this._searchString = value; } + protected addIncludeScope(): Object { + return {}; + } + getDataBuffer(index: number, count: number, config: WidgetItemConfig) { console.log(index, count) @@ -615,9 +622,9 @@ export class SearchDataService extends RemoteDataService { headers = headers.set('Content-Type', 'application/json; charset=utf-8'); this._searchString = this._searchString.trim(); if (this._searchString.length == 0) { - return this.getSnippets('basesnippets', { headers: headers, params: LogbookItemDataService._prepareParams(config, index, count) }).toPromise(); + return this.getSnippets('basesnippets', { headers: headers, params: this._prepareParams(config, index, count) }).toPromise(); } else { - return this.getSnippets('basesnippets/search=' + this._searchString, { headers: headers, params: LogbookItemDataService._prepareParams(config, index, count) }).toPromise(); + return this.getSnippets(`basesnippets/search=${encodeURIComponent(this._searchString)}`, { headers: headers, params: this._prepareParams(config, index, count) }).toPromise(); } }