| /******************************************************************************** |
| * Copyright (c) 2015, 2023 Contributors to the Eclipse Foundation |
| * |
| * See the NOTICE file(s) distributed with this work for additional |
| * information regarding copyright ownership. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| ********************************************************************************/ |
| |
| import { HttpClient, HttpHeaders } from "@angular/common/http"; |
| import { Injectable } from "@angular/core"; |
| import { addErrorDescription } from "@core/core.functions"; |
| import { MDMItem } from "@core/mdm-item"; |
| import { PropertyService } from "@core/property.service"; |
| import { Observable, of as observableOf } from "rxjs"; |
| import { catchError, map } from "rxjs/operators"; |
| |
| export class Filter { |
| sourceName: string; |
| filter: string; |
| searchString: string; |
| |
| constructor(sourceName: string, filter: string, searchString: string) { |
| this.sourceName = sourceName; |
| this.filter = filter; |
| this.searchString = searchString; |
| } |
| } |
| export class Query { |
| resultType: string; |
| filters: Filter[] = []; |
| columns: string[] = []; |
| resultOffset: number; |
| resultLimit: number; |
| |
| addFilter(sourceName: string, filter: string) { |
| const f = this.filters.find((i) => i.sourceName === sourceName); |
| if (f) { |
| f.filter += " or " + filter; // TODO |
| } else { |
| this.filters.push(new Filter(sourceName, filter, "")); |
| } |
| } |
| } |
| |
| export class Columns { |
| type: string; |
| attribute: string; |
| valueType: string; |
| value: any; |
| orderedValue: string; |
| } |
| |
| export class Row { |
| source: string; |
| type: string; |
| id: string; |
| columns: Columns[] = []; |
| |
| public static getColumn(row: Row, col: string) { |
| const column = row.columns.find((c) => c.type + "." + c.attribute === col); |
| if (column) { |
| return column.value; |
| } else { |
| return ""; |
| } |
| } |
| |
| public static equals(a: Row, b: Row) { |
| return a.source === b.source && a.type === b.type && a.id === b.id; |
| } |
| |
| public static getItem(row: Row) { |
| return new MDMItem(row.source, row.type, row.id); |
| } |
| |
| /** |
| * A complete id is needed as cross-source and cross-type items can be used in the basket |
| */ |
| public getVirtualId() { |
| return this.source + "_" + this.type + "_" + this.id; |
| } |
| } |
| |
| export class SearchResult { |
| rows: Row[] = []; |
| totalRecords: { [source: string]: number }; |
| } |
| |
| @Injectable() |
| export class QueryService { |
| private queryUrl: string; |
| |
| constructor(private http: HttpClient, private _prop: PropertyService) { |
| this.queryUrl = _prop.getUrl("mdm/query"); |
| } |
| |
| query(query: Query): Observable<SearchResult> { |
| return this.http.post<any>(this.queryUrl, query).pipe( |
| map((res) => <SearchResult>res), |
| catchError((e) => addErrorDescription(e, "Could not request search result!")), |
| ); |
| } |
| |
| /** |
| * Invoked from mdm basket component only when itemsAdded is emitted on basket service |
| * @param items |
| * @param columns |
| * @param caseSensitive |
| * @param patchFilter true if the filter should be patched for case sensitivity |
| */ |
| queryItems(items: MDMItem[], columns: string[], caseSensitive = false, patchFilter = false): Observable<SearchResult>[] { |
| const byType = items.reduce((acc: { [byType: string]: MDMItem[] }, item: MDMItem) => { |
| const key = item.type; |
| acc[key] = acc[key] || []; |
| acc[key].push(item); |
| return acc; |
| }, {}); |
| |
| return Object.keys(byType).map((type) => this.queryType(type, byType[type], columns, caseSensitive, patchFilter)); |
| } |
| |
| queryType(type: string, items: MDMItem[], columns: string[], caseSensitive = false, patchFilter = false) { |
| if (items && items.length > 0) { |
| const query = new Query(); |
| query.resultType = type; |
| query.columns = columns; |
| // the id of the type |
| query.columns.push(type + ".Id"); |
| |
| const equalStr = caseSensitive ? "eq" : "ci_eq"; |
| |
| // add filter for the item |
| items.forEach((i) => { |
| if (i.id === undefined) { |
| // use the original filter from the virtual node and query all elements of the provided type |
| query.addFilter(i.source, this.patchFilterForCase(i.filter, caseSensitive, patchFilter)); |
| } else { |
| // non-virtual node, query exact the requested item |
| query.addFilter(i.source, i.type + ".Id " + equalStr + ' "' + i.id + '"'); |
| } |
| }); |
| |
| return this.query(query); |
| } else { |
| return observableOf(new SearchResult()); |
| } |
| } |
| |
| patchFilterForCase(filter: any, caseSensitive: boolean, patchFilter: boolean) { |
| let patched = filter; |
| // filters are case sensitive by default from the nodeprovider implementation |
| // also remove measured and ordered context |
| if (!caseSensitive && patchFilter) { |
| patched = patched.replace(/\seq\s/g, " ci_eq "); |
| patched = patched.replace(/MEASURED\./g, ""); |
| patched = patched.replace(/ORDERED\./g, ""); |
| } |
| return patched; |
| } |
| |
| suggestValues(environments: string[], type: string, attribute: string) { |
| const body = JSON.stringify({ |
| sourceNames: environments, |
| type: type, |
| attrName: attribute, |
| }); |
| const headers = new HttpHeaders({ "Content-Type": "application/json" }); |
| const options = { headers: headers }; |
| const url = this._prop.getUrl("mdm/suggestions"); |
| return this.http.post<any>(url, body, options).pipe( |
| map((res) => res.data), |
| catchError((e) => addErrorDescription(e, "Could not request suggestions!")), |
| ); |
| } |
| } |