You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ab...@apache.org on 2018/02/05 11:32:23 UTC
[ambari] branch trunk updated: AMBARI-22909 Log Search UI:
implement filter by username for access logs
This is an automated email from the ASF dual-hosted git repository.
ababiichuk pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git
The following commit(s) were added to refs/heads/trunk by this push:
new fee42d5 AMBARI-22909 Log Search UI: implement filter by username for access logs
fee42d5 is described below
commit fee42d5ce53ce4426cd25f58ff93d65cd92f6f03
Author: ababiichuk <ab...@hortonworks.com>
AuthorDate: Mon Feb 5 12:26:02 2018 +0200
AMBARI-22909 Log Search UI: implement filter by username for access logs
---
.../src/app/classes/models/store.ts | 33 ++++--
.../app/classes/queries/audit-logs-query-params.ts | 33 +-----
.../audit-logs-top-resources-query-params.ts | 4 +-
...t-logs-query-params.ts => logs-query-params.ts} | 12 +-
.../classes/queries/service-logs-query-params.ts | 4 +-
.../queries/service-logs-truncated-query-params.ts | 2 +-
.../filters-panel/filters-panel.component.html | 3 +
.../ambari-logsearch-web/src/app/mock-data.ts | 16 +--
.../src/app/services/logs-container.service.ts | 53 +++++++--
.../src/app/services/mock-api-data.service.ts | 55 ++++++---
.../src/app/services/utils.service.spec.ts | 125 +++++++++++++++++++++
.../src/app/services/utils.service.ts | 20 ++++
.../ambari-logsearch-web/src/assets/i18n/en.json | 3 +-
13 files changed, 277 insertions(+), 86 deletions(-)
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/store.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/store.ts
index 1764c93..eb87714 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/store.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/store.ts
@@ -16,6 +16,7 @@
* limitations under the License.
*/
+import {ReflectiveInjector} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import {Store, Action} from '@ngrx/store';
import {AppSettings} from '@app/classes/models/app-settings';
@@ -29,17 +30,22 @@ import {UserConfig} from '@app/classes/models/user-config';
import {AuditLogField} from '@app/classes/models/audit-log-field';
import {ServiceLogField} from '@app/classes/models/service-log-field';
import {Tab} from '@app/classes/models/tab';
+import {UtilsService} from '@app/services/utils.service';
const storeActions = {
- 'ARRAY.ADD': 'ADD',
- 'ARRAY.ADD.START': 'ADD_TO_START',
- 'ARRAY.DELETE.PRIMITIVE': 'DELETE_PRIMITIVE',
- 'ARRAY.DELETE.OBJECT': 'DELETE_OBJECT',
- 'ARRAY.CLEAR': 'CLEAR',
- 'ARRAY.MAP': 'MAP',
-
- 'OBJECT.SET': 'SET'
-};
+ 'ARRAY.ADD': 'ADD',
+ 'ARRAY.ADD.START': 'ADD_TO_START',
+ 'ARRAY.ADD.UNIQUE': 'ADD_UNIQUE',
+ 'ARRAY.DELETE.PRIMITIVE': 'DELETE_PRIMITIVE',
+ 'ARRAY.DELETE.OBJECT': 'DELETE_OBJECT',
+ 'ARRAY.CLEAR': 'CLEAR',
+ 'ARRAY.MAP': 'MAP',
+
+ 'OBJECT.SET': 'SET'
+ },
+ provider = ReflectiveInjector.resolve([UtilsService]),
+ injector = ReflectiveInjector.fromResolvedProviders(provider),
+ utils = injector.get(UtilsService);
export interface AppStore {
appSettings: AppSettings;
@@ -96,6 +102,13 @@ export class CollectionModelService extends ModelService {
});
}
+ addUniqueInstances(instances: any[]): void {
+ this.store.dispatch({
+ type: `${storeActions['ARRAY.ADD.UNIQUE']}_${this.modelName}`,
+ payload: instances
+ });
+ }
+
deleteObjectInstance(instance: any): void {
this.store.dispatch({
type: `${storeActions['ARRAY.DELETE.OBJECT']}_${this.modelName}`,
@@ -163,6 +176,8 @@ export function getCollectionReducer(modelName: string, defaultState: any = []):
return [...state, ...action.payload];
case `${storeActions['ARRAY.ADD.START']}_${modelName}`:
return [...action.payload, ...state];
+ case `${storeActions['ARRAY.ADD.UNIQUE']}_${modelName}`:
+ return utils.pushUniqueValues(state.slice(), action.payload);
case `${storeActions['ARRAY.DELETE.OBJECT']}_${modelName}`:
return state.filter(instance => instance.id !== action.payload.id);
case `${storeActions['ARRAY.DELETE.PRIMITIVE']}_${modelName}`:
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-query-params.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-query-params.ts
index dc82b9e..e9c0149 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-query-params.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-query-params.ts
@@ -16,35 +16,8 @@
* limitations under the License.
*/
-import {QueryParams} from '@app/classes/queries/query-params';
-import {SortingType} from '@app/classes/string';
+import {LogsListQueryParams} from '@app/classes/queries/logs-query-params';
-export const defaultParams = {
- page: '0',
- pageSize: '10'
-};
-
-export class AuditLogsQueryParams extends QueryParams {
- clusters?: string;
- mustBe?: string;
- mustNot?: string;
- includeQuery?: string;
- excludeQuery?: string;
- from?: string;
- to?: string;
-}
-
-export class AuditLogsListQueryParams extends AuditLogsQueryParams {
- constructor(options: AuditLogsListQueryParams) {
- let finalParams = Object.assign({}, defaultParams, options);
- const page = parseInt(finalParams.page),
- pageSize = parseInt(finalParams.pageSize);
- finalParams.startIndex = isNaN(page) || isNaN(pageSize) ? '' : (page * pageSize).toString();
- super(finalParams);
- }
- page: string;
- pageSize: string;
- startIndex: string;
- sortBy?: string;
- sortType?: SortingType;
+export class AuditLogsListQueryParams extends LogsListQueryParams {
+ userList?: string;
}
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-top-resources-query-params.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-top-resources-query-params.ts
index 0d12539..d5e9cc5 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-top-resources-query-params.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-top-resources-query-params.ts
@@ -16,8 +16,8 @@
* limitations under the License.
*/
-import {AuditLogsQueryParams} from '@app/classes/queries/audit-logs-query-params';
+import {LogsQueryParams} from '@app/classes/queries/logs-query-params';
-export class AuditLogsTopResourcesQueryParams extends AuditLogsQueryParams {
+export class AuditLogsTopResourcesQueryParams extends LogsQueryParams {
field: string;
}
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-query-params.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/logs-query-params.ts
similarity index 83%
copy from ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-query-params.ts
copy to ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/logs-query-params.ts
index dc82b9e..9e7f5d0 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-query-params.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/logs-query-params.ts
@@ -16,15 +16,15 @@
* limitations under the License.
*/
-import {QueryParams} from '@app/classes/queries/query-params';
import {SortingType} from '@app/classes/string';
+import {QueryParams} from '@app/classes/queries/query-params';
-export const defaultParams = {
+const defaultListParams = {
page: '0',
pageSize: '10'
};
-export class AuditLogsQueryParams extends QueryParams {
+export class LogsQueryParams extends QueryParams {
clusters?: string;
mustBe?: string;
mustNot?: string;
@@ -34,9 +34,9 @@ export class AuditLogsQueryParams extends QueryParams {
to?: string;
}
-export class AuditLogsListQueryParams extends AuditLogsQueryParams {
- constructor(options: AuditLogsListQueryParams) {
- let finalParams = Object.assign({}, defaultParams, options);
+export class LogsListQueryParams extends LogsQueryParams {
+ constructor(options: LogsListQueryParams) {
+ let finalParams = Object.assign({}, defaultListParams, options);
const page = parseInt(finalParams.page),
pageSize = parseInt(finalParams.pageSize);
finalParams.startIndex = isNaN(page) || isNaN(pageSize) ? '' : (page * pageSize).toString();
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-query-params.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-query-params.ts
index 60c3d5c..ad88449 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-query-params.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-query-params.ts
@@ -16,9 +16,9 @@
* limitations under the License.
*/
-import {AuditLogsListQueryParams} from '@app/classes/queries/audit-logs-query-params';
+import {LogsListQueryParams} from '@app/classes/queries/logs-query-params';
-export class ServiceLogsQueryParams extends AuditLogsListQueryParams {
+export class ServiceLogsQueryParams extends LogsListQueryParams {
level?: string;
file_name?: string;
bundle_id?: string;
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-truncated-query-params.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-truncated-query-params.ts
index 3b08e11..071a443 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-truncated-query-params.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-truncated-query-params.ts
@@ -19,7 +19,7 @@
import {QueryParams} from '@app/classes/queries/query-params';
import {ScrollType} from '@app/classes/string';
-export const defaultParams = {
+const defaultParams = {
numberRows: '10',
scrollType: ''
};
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.html
index 51233ff..db1cb3e 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.html
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.html
@@ -36,6 +36,9 @@
label="{{filters.hosts.label | translate}}" [iconClass]="filters.hosts.iconClass"
[subItems]="filters.hosts.options" [isMultipleChoice]="true" [isRightAlign]="true"
additionalLabelComponentSetter="getDataForHostsNodeBar"></filter-button>
+ <filter-button *ngIf="isFilterConditionDisplayed('users')" formControlName="users"
+ label="{{filters.users.label | translate}}" [iconClass]="filters.users.iconClass"
+ [subItems]="filters.users.options" [isMultipleChoice]="true" [isRightAlign]="true"></filter-button>
<filter-button *ngIf="isFilterConditionDisplayed('components')" formControlName="components"
label="{{filters.components.label | translate}}" [iconClass]="filters.components.iconClass"
[subItems]="filters.components.options" [isMultipleChoice]="true" [isRightAlign]="true"
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/mock-data.ts b/ambari-logsearch/ambari-logsearch-web/src/app/mock-data.ts
index fd4270b..cf5d2a5 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/mock-data.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/mock-data.ts
@@ -49,7 +49,7 @@ export const mockData = {
proxyUsers: [
'admin'
],
- evtTime: 1496057422531,
+ evtTime: currentTime.valueOf(),
enforcer: 'ambari-acl',
reqContext: 'ambari',
cliType: 'GET',
@@ -72,7 +72,7 @@ export const mockData = {
event_md5: '09876543211234567890',
event_dur_ms: 100,
_ttl_: '+7DAYS',
- _expire_at_: '2017-05-29T11:30:22.531Z',
+ _expire_at_: currentTime.clone().add(2, 'd').valueOf(),
_router_field_: 5
},
{
@@ -96,7 +96,7 @@ export const mockData = {
proxyUsers: [
'user'
],
- evtTime: 1496057422531,
+ evtTime: currentTime.clone().subtract(2, 'h').valueOf(),
enforcer: 'hdfs',
reqContext: 'ambari_server',
cliType: 'PUT',
@@ -119,7 +119,7 @@ export const mockData = {
event_md5: '01928374650192837465',
event_dur_ms: 500,
_ttl_: '+7DAYS',
- _expire_at_: '2017-05-29T11:30:22.531Z',
+ _expire_at_: currentTime.clone().add(1, 'd').valueOf(),
_router_field_: 10
}
],
@@ -995,7 +995,7 @@ export const mockData = {
path: '/var/log/ambari-metrics-collector/ambari-metrics-collector.log',
host: 'h0',
level: 'WARN',
- logtime: '2017-05-28T11:30:22.531Z',
+ logtime: currentTime.valueOf(),
ip: '192.168.0.1',
logfile_line_number: 8,
type: 'ams_collector',
@@ -1012,14 +1012,14 @@ export const mockData = {
event_md5: '1908755391',
event_dur_ms: 200,
_ttl_: '+5DAYS',
- _expire_at_: '2017-05-29T11:30:22.531Z',
+ _expire_at_: currentTime.clone().add(1, 'd').valueOf(),
_router_field_: 20
},
{
path: '/var/log/ambari-metrics-collector/ambari-metrics-collector.log',
host: 'h1',
level: 'ERROR',
- logtime: '2017-05-28T10:30:22.531Z',
+ logtime: currentTime.clone().subtract(1, 'd').valueOf(),
ip: '192.168.0.2',
type: 'ams_collector',
_version_: 14,
@@ -1036,7 +1036,7 @@ export const mockData = {
event_md5: '1029384756',
event_dur_ms: 700,
_ttl_: '+5DAYS',
- _expire_at_: '2017-05-29T10:30:22.531Z',
+ _expire_at_: currentTime.clone().add(2, 'd').valueOf(),
_router_field_: 5
}
]
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts
index a523e03..7664b3d 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts
@@ -122,6 +122,13 @@ export class LogsContainerService {
this.loadLogs();
});
});
+ this.auditLogsSource.subscribe((logs: AuditLog[]): void => {
+ const userNames = logs.map((log: AuditLog): string => log.reqUser);
+ this.utils.pushUniqueValues(
+ this.filters.users.options, userNames.map(this.utils.getListItemFromString),
+ this.compareFilterOptions
+ );
+ });
}
private readonly paginationOptions: string[] = ['10', '25', '50', '100'];
@@ -172,7 +179,7 @@ export class LogsContainerService {
fieldName: 'cluster'
},
timeRange: {
- label: 'filter.duration',
+ label: 'logs.duration',
options: [
[
{
@@ -496,6 +503,13 @@ export class LogsContainerService {
query: {
defaultSelection: []
},
+ users: {
+ label: 'filter.users',
+ iconClass: 'fa fa-server',
+ options: [],
+ defaultSelection: [],
+ fieldName: 'reqUser'
+ },
isUndoOrRedo: {
defaultSelection: false
}
@@ -511,7 +525,8 @@ export class LogsContainerService {
serviceLogsSorting: ['sortType', 'sortBy'],
pageSize: ['pageSize'],
page: ['page'],
- query: ['includeQuery', 'excludeQuery']
+ query: ['includeQuery', 'excludeQuery'],
+ users: ['userList']
};
private readonly graphFilters = {
@@ -520,7 +535,8 @@ export class LogsContainerService {
components: ['mustBe'],
levels: ['level'],
hosts: ['hostList'],
- query: ['includeQuery', 'excludeQuery']
+ query: ['includeQuery', 'excludeQuery'],
+ users: ['userList']
};
readonly customTimeRangeKey: string = 'filter.timeRange.custom';
@@ -533,8 +549,7 @@ export class LogsContainerService {
auditLogs: {
logsModel: this.auditLogsStorage,
fieldsModel: this.auditLogsFieldsStorage,
- // TODO add all the required fields
- listFilters: ['clusters', 'timeRange', 'auditLogsSorting', 'pageSize', 'page', 'query'],
+ listFilters: ['clusters', 'timeRange', 'auditLogsSorting', 'pageSize', 'page', 'query', 'users'],
topResourcesFilters: ['clusters', 'timeRange', 'query'],
graphFilters: ['clusters', 'timeRange', 'query'],
graphRequestName: 'auditLogsGraph',
@@ -612,13 +627,21 @@ export class LogsContainerService {
}
}
+ private auditLogsSource: Observable<AuditLog[]> = this.auditLogsStorage.getAll();
+
+ private serviceLogsSource: Observable<ServiceLog[]> = this.serviceLogsStorage.getAll();
+
auditLogsColumns: Observable<ListItem[]> = this.auditLogsFieldsStorage.getAll().map(this.columnsMapper);
serviceLogsColumns: Observable<ListItem[]> = this.serviceLogsFieldsStorage.getAll().map(this.columnsMapper);
- serviceLogs: Observable<ServiceLog[]> = Observable.combineLatest(this.serviceLogsStorage.getAll(), this.serviceLogsColumns).map(this.logsMapper);
+ serviceLogs: Observable<ServiceLog[]> = Observable.combineLatest(
+ this.serviceLogsSource, this.serviceLogsColumns
+ ).map(this.logsMapper);
- auditLogs: Observable<AuditLog[]> = Observable.combineLatest(this.auditLogsStorage.getAll(), this.auditLogsColumns).map(this.logsMapper);
+ auditLogs: Observable<AuditLog[]> = Observable.combineLatest(
+ this.auditLogsSource, this.auditLogsColumns
+ ).map(this.logsMapper);
queryParameterNameChange: Subject<SearchBoxParameterTriggered> = new Subject();
@@ -642,6 +665,16 @@ export class LogsContainerService {
topResourcesGraphData: HomogeneousObject<HomogeneousObject<number>> = {};
+ /**
+ * Compares two options list items by values (so that isChecked flags are ignored)
+ * @param {ListItem} sourceItem
+ * @param {ListItem} newItem
+ * @returns {boolean}
+ */
+ private compareFilterOptions = (sourceItem: ListItem, newItem: ListItem): boolean => {
+ return this.utils.isEqual(sourceItem.value, newItem.value);
+ };
+
private isFormUnchanged = (valueA: object, valueB: object): boolean => {
const trackedControlNames = this.logsTypeMap[this.activeLogsType].listFilters;
for (let name of trackedControlNames) {
@@ -956,7 +989,7 @@ export class LogsContainerService {
request.subscribe((response: Response): void => {
const clusterNames = response.json();
if (clusterNames) {
- this.filters.clusters.options.push(...clusterNames.map(this.utils.getListItemFromString));
+ this.utils.pushUniqueValues(this.filters.clusters.options, clusterNames.map(this.utils.getListItemFromString));
this.clustersStorage.addInstances(clusterNames);
}
});
@@ -973,7 +1006,7 @@ export class LogsContainerService {
}, 0)
}));
if (components) {
- this.filters.components.options.push(...components.map(this.utils.getListItemFromNode));
+ this.utils.pushUniqueValues(this.filters.components.options, components.map(this.utils.getListItemFromNode));
this.componentsStorage.addInstances(components);
}
});
@@ -986,7 +1019,7 @@ export class LogsContainerService {
const jsonResponse = response.json(),
hosts = jsonResponse && jsonResponse.vNodeList;
if (hosts) {
- this.filters.hosts.options.push(...hosts.map(this.utils.getListItemFromNode));
+ this.utils.pushUniqueValues(this.filters.hosts.options, hosts.map(this.utils.getListItemFromNode));
this.hostsStorage.addInstances(hosts);
}
});
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/mock-api-data.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/mock-api-data.service.ts
index 985b52f..b7c93a1 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/services/mock-api-data.service.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/mock-api-data.service.ts
@@ -31,6 +31,18 @@ export class mockBackendService extends InMemoryBackendService {
export class mockApiDataService implements InMemoryDbService {
+ private filterByMessage = (value: string, filterValue: string): boolean => {
+ return value.toLowerCase().indexOf(filterValue.toLowerCase()) > -1;
+ };
+
+ private filterByStartTime = (value: number, filterValue: number | string | Date | moment.Moment): boolean => {
+ return value >= moment(filterValue).valueOf();
+ };
+
+ private filterByEndTime = (value: number, filterValue: number | string | Date | moment.Moment): boolean => {
+ return value <= moment(filterValue).valueOf();
+ };
+
private readonly filterMap = {
'api/v1/service/logs': {
pathToCollection: 'logList',
@@ -50,15 +62,15 @@ export class mockApiDataService implements InMemoryDbService {
},
iMessage: {
key: 'log_message',
- filterFunction: (value, filterValue) => value.toLowerCase().indexOf(filterValue.toLowerCase()) > -1
+ filterFunction: this.filterByMessage
},
from: {
key: 'logtime',
- filterFunction: (value, filterValue) => value >= moment(filterValue).valueOf()
+ filterFunction: this.filterByStartTime
},
to: {
key: 'logtime',
- filterFunction: (value, filterValue) => value < moment(filterValue).valueOf()
+ filterFunction: this.filterByEndTime
},
hostList: {
key: 'host',
@@ -76,15 +88,19 @@ export class mockApiDataService implements InMemoryDbService {
},
iMessage: {
key: 'log_message',
- filterFunction: (value, filterValue) => value.toLowerCase().indexOf(filterValue.toLowerCase()) > -1
+ filterFunction: this.filterByMessage
},
from: {
key: 'evtTime',
- filterFunction: (value, filterValue) => value >= moment(filterValue).valueOf()
+ filterFunction: this.filterByStartTime
},
to: {
key: 'evtTime',
- filterFunction: (value, filterValue) => value < moment(filterValue).valueOf()
+ filterFunction: this.filterByEndTime
+ },
+ userList: {
+ key: 'reqUser',
+ isValuesList: true
}
}
}
@@ -126,9 +142,11 @@ export class mockApiDataService implements InMemoryDbService {
}
}, interceptorArgs.db);
if (is404) {
- return new Observable<Response>((subscriber: Subscriber<Response>) => subscriber.error(new Response(createErrorResponse(
- interceptorArgs.requestInfo.req, 404, 'Not found'
- ))));
+ return new Observable<Response>((subscriber: Subscriber<Response>) => subscriber.error(
+ new Response(createErrorResponse(
+ interceptorArgs.requestInfo.req, 404, 'Not found'
+ )))
+ );
} else {
let filteredData;
const filterMapItem = this.filterMap[path];
@@ -143,10 +161,11 @@ export class mockApiDataService implements InMemoryDbService {
paramFilter = filterMapItem.filters[key],
paramValuesList = paramFilter && paramFilter.isValuesList && paramValue ? paramValue.split(',') : [],
currentValue = paramFilter && item[paramFilter.key];
- if (paramFilter &&
- ((paramFilter.filterFunction && !paramFilter.filterFunction(currentValue, paramValue)) ||
+ if (
+ paramFilter && ((paramFilter.filterFunction && !paramFilter.filterFunction(currentValue, paramValue)) ||
(!paramFilter.filterFunction && !paramFilter.isValuesList && currentValue !== paramValue) ||
- (!paramFilter.filterFunction && paramFilter.isValuesList && paramValuesList.indexOf(currentValue) === -1))) {
+ (!paramFilter.filterFunction && paramFilter.isValuesList && paramValuesList.indexOf(currentValue) === -1))
+ ) {
result = false;
}
});
@@ -181,15 +200,17 @@ export class mockApiDataService implements InMemoryDbService {
} else {
filteredData = allData;
}
- return new Observable<Response>((subscriber: Subscriber<Response>) => subscriber.next(new Response(new ResponseOptions({
- status: 200,
- body: filteredData
- }))));
+ return new Observable<Response>((subscriber: Subscriber<Response>) => subscriber.next(
+ new Response(new ResponseOptions({
+ status: 200,
+ body: filteredData
+ })))
+ );
}
}
}
- post(interceptorArgs: any) {
+ post(interceptorArgs: any): Observable<Response> {
// TODO implement posting data to mock object except login call
return this.get(interceptorArgs);
}
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/utils.service.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/utils.service.spec.ts
index 861e5c3..33b6b7c 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/services/utils.service.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/utils.service.spec.ts
@@ -441,4 +441,129 @@ describe('UtilsService', () => {
}));
});
+ describe('#pushUniqueValues()', () => {
+ const cases = [
+ {
+ source: [1, 2, 3],
+ itemsToPush: [2, 4, 5, 1],
+ compareFunction: undefined,
+ result: [1, 2, 3, 4, 5],
+ title: 'primitives array'
+ },
+ {
+ source: [
+ {
+ p0: 'v0'
+ },
+ {
+ p1: 'v1'
+ },
+ {
+ p2: 'v2'
+ }
+ ],
+ itemsToPush: [
+ {
+ p3: 'v3'
+ },
+ {
+ p2: 'v2'
+ },
+ {
+ p2: 'v3'
+ },
+ {
+ p4: 'v4'
+ }
+ ],
+ compareFunction: undefined,
+ result: [
+ {
+ p0: 'v0'
+ },
+ {
+ p1: 'v1'
+ },
+ {
+ p2: 'v2'
+ },
+ {
+ p3: 'v3'
+ },
+ {
+ p2: 'v3'
+ },
+ {
+ p4: 'v4'
+ }
+ ],
+ title: 'objects array'
+ },
+ {
+ source: [
+ {
+ id: 0,
+ value: 'v0'
+ },
+ {
+ id: 1,
+ value: 'v1'
+ },
+ {
+ id: 2,
+ value: 'v2'
+ }
+ ],
+ itemsToPush: [
+ {
+ id: 3,
+ value: 'v3'
+ },
+ {
+ id: 4,
+ value: 'v4'
+ },
+ {
+ id: 0,
+ value: 'v5'
+ },
+ {
+ id: 1,
+ value: 'v6'
+ }
+ ],
+ compareFunction: (itemA: any, itemB: any): boolean => itemA.id === itemB.id,
+ result: [
+ {
+ id: 0,
+ value: 'v0'
+ },
+ {
+ id: 1,
+ value: 'v1'
+ },
+ {
+ id: 2,
+ value: 'v2'
+ },
+ {
+ id: 3,
+ value: 'v3'
+ },
+ {
+ id: 4,
+ value: 'v4'
+ }
+ ],
+ title: 'custom comparison function'
+ }
+ ];
+
+ cases.forEach(test => {
+ it(test.title, inject([UtilsService], (service: UtilsService) => {
+ expect(service.pushUniqueValues(test.source, test.itemsToPush, test.compareFunction)).toEqual(test.result);
+ }));
+ });
+ });
+
});
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/utils.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/utils.service.ts
index 952a1d1..c960cd1 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/services/utils.service.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/utils.service.ts
@@ -131,4 +131,24 @@ export class UtilsService {
};
}
+ /**
+ * Method that updates source array with only the values which aren't already present there
+ * @param {Array} sourceArray
+ * @param {Array} itemsToPush
+ * @param {Function} [compareFunction=this.isEqual] - custom comparison function;
+ * item is skipped if it returns true, and pushed - if false
+ * @returns {Array}
+ */
+ pushUniqueValues = (
+ sourceArray: any[], itemsToPush: any[], compareFunction: (x: any, y: any) => boolean = this.isEqual
+ ): any[] => {
+ itemsToPush.forEach((item: any) => {
+ const itemExists = sourceArray.some((sourceItem: any): boolean => compareFunction(item, sourceItem));
+ if (!itemExists) {
+ sourceArray.push(item);
+ }
+ });
+ return sourceArray;
+ }
+
}
diff --git a/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json b/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json
index 7f16078..3627e12 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json
+++ b/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json
@@ -34,7 +34,7 @@
"filter.include": "Include",
"filter.exclude": "Exclude",
"filter.hosts": "Hosts",
- "filter.duration": "Duration",
+ "filter.users": "Users",
"filter.capture": "Capture",
"filter.capture.triggeringRefresh": "Triggering auto-refresh in {{remainingSeconds}} sec",
@@ -173,6 +173,7 @@
"logs.tableLayoutBtnTooltip": "Table layout. Optimal when you want to display only few columns.",
"logs.flexLayoutBtnTooltip": "Flexible layout. Optimal when your screen is narrow or you want to display more columns.",
"logs.toggleLabels": "Turn on/off the labels.",
+ "logs.duration": "Duration",
"histogram.gap": "gap",
"histogram.gaps": "gaps",
--
To stop receiving notification emails like this one, please contact
ababiichuk@apache.org.