Asymmetrik/mean2-starter

View on GitHub
src/client/app/audit/audit.component.ts

Summary

Maintainability
A
2 hrs
Test Coverage
import { Component } from '@angular/core';
import { DatePipe } from '@angular/common';

import * as _ from 'lodash';
import * as moment from 'moment';
import { BsModalRef, BsModalService } from 'ngx-bootstrap';
import { Observable } from 'rxjs/Observable';

import { AuditService } from './audit.service';
import { AuditViewChangeModal, AuditViewDetailModal } from './audit-view-change.component';
import { PagingOptions } from '../shared/pager.component';
import { TableSortOptions } from '../shared/pageable-table/pageable-table.component';
import { SortDisplayOption, SortDirection } from '../shared/result-utils.class';
import { UserService } from '../admin/users.service';
import { AuditOption } from './audit.classes';


@Component({
    selector: 'audit',
    templateUrl: './audit-list.component.html'
})
export class AuditComponent {

    // List of audit entries
    auditEntries: any[] = [];

    actionOptions: AuditOption[] = [];

    auditTypeOptions: AuditOption[] = [];

    queryUserSearchTerm: string = '';

    // Search phrase
    search: string = '';

    pagingOpts: PagingOptions;

    sortOpts: TableSortOptions = {
        created: new SortDisplayOption('Created', 'created', SortDirection.desc),
        actor: new SortDisplayOption('Actor', 'audit.actor.name', SortDirection.asc),
        type: new SortDisplayOption('Type', 'audit.auditType', SortDirection.desc)
    };

    dateRangeOptions: any[];

    dateRangeFilter: any;

    // Date picker
    showGteDatepicker: boolean = false;

    showLteDatepicker: boolean = false;

    queryStartDate: Date = moment.utc().subtract(1, 'days').toDate();

    queryEndDate: Date = moment.utc().toDate();

    searchUsersRef: Observable<any>;

    private userPagingOpts: PagingOptions;

    private queryUserObj: any;

    private auditEntriesLoaded: boolean = false;

    private auditModalRef: BsModalRef;

    constructor(
        private auditService: AuditService,
        private userService: UserService,
        private modalService: BsModalService
    ) {}

    ngOnInit() {
        this.dateRangeOptions = [
            { value: -1, display: 'Last 24 Hours' },
            { value: -3, display: 'Last 3 Days' },
            { value: -7, display: 'Last 7 Days' },
            { value: 'everything', display: 'Everything' },
            { value: 'choose', display: 'Select Date Range' }
        ];

        this.pagingOpts = new PagingOptions();
        this.pagingOpts.sortField = this.sortOpts['created'].sortField;
        this.pagingOpts.sortDir = this.sortOpts['created'].sortDir;

        this.userPagingOpts = new PagingOptions(0, 20);
        this.userPagingOpts.sortField = 'username';
        this.userPagingOpts.sortDir = SortDirection.asc;

        // Default date range to last day
        this.dateRangeFilter = {
            selected: this.dateRangeOptions[0].value
        };

        // Bind the search users typeahead to a function
        this.searchUsersRef = Observable.create((observer: any) => {
            this.userService.match({}, this.queryUserSearchTerm, this.userPagingOpts)
                .subscribe((result: any) => {
                    let formatted = result.elements
                        .map((r: any) => {
                            r.displayName = r.name + ' [' + r.username + ']';
                            return r;
                        });
                    observer.next(formatted);
                });
        });

        // Load action and audit type options from the server
        Observable.forkJoin([this.auditService.getDistinctAuditValues('audit.action'), this.auditService.getDistinctAuditValues('audit.auditType')])
            .subscribe((results: any[]) => {
                this.actionOptions = results[0].filter((r: any) => _.isString(r)).sort().map((r: any) => new AuditOption(r));
                this.auditTypeOptions = results[1].filter((r: any) => _.isString(r)).sort().map((r: any) => new AuditOption(r));
            });

        this.loadAuditEntries();
    }

    goToPage(event: any) {
        this.pagingOpts.update(event.pageNumber, event.pageSize);
        this.loadAuditEntries();
    }

    setSort(name: string) {
        if (name === this.pagingOpts.sortField) {
            // Same column, reverse direction
            this.pagingOpts.sortDir = (this.pagingOpts.sortDir === SortDirection.asc) ? SortDirection.desc : SortDirection.asc;
        }
        else {
            // New column selected, default to ascending sort
            this.pagingOpts.sortField = name;
            this.pagingOpts.sortDir = SortDirection.asc;
        }
        this.loadAuditEntries();
    }

    updateDateRange() {
        this.showGteDatepicker = false;
        this.showLteDatepicker = false;
        this.loadAuditEntries();
    }

    typeaheadOnSelect(e: any) {
        this.queryUserObj = e;
        this.refresh();
    }

    viewMore(auditEntry: any, type: string) {
        switch (type) {
            case 'viewDetails':
                this.auditModalRef = this.modalService.show(AuditViewDetailModal, { ignoreBackdropClick: true, class: 'modal-lg' });
                this.auditModalRef.content.auditEntry = auditEntry;
                break;
            case 'viewChanges':
                this.auditModalRef = this.modalService.show(AuditViewChangeModal, { ignoreBackdropClick: true, class: 'modal-lg' });
                this.auditModalRef.content.auditEntry = auditEntry;
                break;
            default:
                break;
        }
    }

    refresh() {
        this.pagingOpts.reset();

        // If actor search bar is empty, clear the actor object, otherwise retain it
        if (null == this.queryUserSearchTerm || this.queryUserSearchTerm.length === 0) {
            this.queryUserObj = null;
        }

        this.loadAuditEntries();
    }

    formatDate(date: Date) {
        return new DatePipe('en-US').transform(date, 'shortDate');
    }

    private getTimeFilterQueryObject(): any {
        let timeQuery: any = null;

        if (this.dateRangeFilter.selected === 'choose') {
            if (null != this.queryStartDate) {
                timeQuery = (null == timeQuery) ? {} : timeQuery;
                timeQuery.$gte = moment.utc(this.queryStartDate).startOf('day');
            }
            if (null != this.queryEndDate) {
                timeQuery = (null == timeQuery) ? {} : timeQuery;
                timeQuery.$lt = moment.utc(this.queryEndDate).endOf('day');
            }
        }
        else if (this.dateRangeFilter.selected !== 'everything') {
            timeQuery = {
                $gte: moment.utc().add(this.dateRangeFilter.selected, 'days'),
                $lt: moment.utc()
            };
        }

        return timeQuery;
    }

    private buildSearchQuery(): any {
        let query: any = {};

        if (null != this.queryUserObj && null != this.queryUserObj.item._id) {
            query['audit.actor._id'] = {
                $obj: this.queryUserObj.item._id
            };
        }

        let selectedActions = this.actionOptions.filter((opt) => opt.selected);
        if (selectedActions.length > 0) {
            query['audit.action'] = {
                $in: selectedActions.map((opt) => opt.display)
            };
        }

        let selectedAuditTypes = this.auditTypeOptions.filter((opt) => opt.selected);
        if (selectedAuditTypes.length > 0) {
            query['audit.auditType'] = {
                $in: selectedAuditTypes.map((opt) => opt.display)
            };
        }

        let created = this.getTimeFilterQueryObject();
        if (null != created) {
            query.created = created;
        }

        return query;
    }

    private loadAuditEntries() {
        let query = this.buildSearchQuery();

        this.auditService.search(query, '', this.pagingOpts)
            .subscribe((result: any) => {
                if (null != result && null != result.elements && result.elements.length > 0) {
                    // Defensively filter out bad audit entries (null or audit or audit.object object is null)
                    this.auditEntries = result.elements
                        .filter((e: any) => (null != e && null != e.audit && null != e.audit.object));

                    this.pagingOpts.set(result.pageNumber, result.pageSize, result.totalPages, result.totalSize);
                    this.auditEntriesLoaded = true;
                }
                else {
                    this.auditEntries = [];
                    this.pagingOpts.reset();
                    this.auditEntriesLoaded = false;
                }
            });
    }
}