UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

297 lines (290 loc) 22 kB
import * as i0 from '@angular/core'; import { Injectable, ViewChild, Component, NgModule } from '@angular/core'; import { gettext } from '@c8y/ngx-components/gettext'; import * as i1$1 from '@c8y/ngx-components'; import { PX_ACTIONS, Status, ActionBarItemComponent, IconDirective, DataGridComponent, EmptyStateComponent, ColumnDirective, CellRendererDefDirective, C8yTranslatePipe, DatePipe, CoreModule, hookRoute, ViewContext } from '@c8y/ngx-components'; import { PopoverModule } from 'ngx-bootstrap/popover'; import { TooltipModule } from 'ngx-bootstrap/tooltip'; import { ButtonsModule } from 'ngx-bootstrap/buttons'; import { ReactiveFormsModule } from '@angular/forms'; import * as i1 from '@c8y/client'; import * as i4 from '@angular/router'; import { BehaviorSubject } from 'rxjs'; import { tap, map, switchMap, shareReplay, finalize } from 'rxjs/operators'; import * as i5 from '@ngx-translate/core'; import { NgClass, NgIf, NgTemplateOutlet, AsyncPipe } from '@angular/common'; class DeviceProvisionedCertificatesService { constructor(userService, client) { this.userService = userService; this.client = client; } async getDeviceProvisionedCertificates(device) { const deviceUserName = await this.getDeviceUserName(device); // Avoid additional requests if the device does not have a user. // This case does not really occur in real-life scenarios, but e.g. in cypress tests only if (!deviceUserName) { return undefined; } const { data } = await this.userService.detail(deviceUserName, { withCertificates: true }); return data.provisionedCertificates; } async getDeviceUserName(device) { const method = 'GET'; const url = `/inventory/managedObjects/${device.id}/user`; const res = await this.client.fetch(url, { method }); const data = await res.json(); if (res.status >= 400) { throw new Error(data.message); } return data.userName; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DeviceProvisionedCertificatesService, deps: [{ token: i1.UserService }, { token: i1.FetchClient }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DeviceProvisionedCertificatesService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DeviceProvisionedCertificatesService, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: i1.UserService }, { type: i1.FetchClient }] }); class DeviceProvisionedCertificatesGuard { constructor(deviceProvisionedCertificatesService) { this.deviceProvisionedCertificatesService = deviceProvisionedCertificatesService; } async canActivate(route) { const contextData = route.data.contextData || route.parent.data.contextData; if (!contextData) { return false; } try { const provisionedCertificates = await this.deviceProvisionedCertificatesService.getDeviceProvisionedCertificates(contextData); return provisionedCertificates?.length > 0; } catch (error) { return false; } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DeviceProvisionedCertificatesGuard, deps: [{ token: DeviceProvisionedCertificatesService }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DeviceProvisionedCertificatesGuard }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DeviceProvisionedCertificatesGuard, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: DeviceProvisionedCertificatesService }] }); class DeviceTabProvisionedCertificatesComponent { constructor(alertService, modalService, deviceSerialsService, crlService, route, translateService) { this.alertService = alertService; this.modalService = modalService; this.deviceSerialsService = deviceSerialsService; this.crlService = crlService; this.route = route; this.translateService = translateService; this.reloading = false; this.reload$ = new BehaviorSubject(null); this.tableTitle = gettext('Provisioned certificates'); this.columns = [ { name: 'serialNumber', path: 'serialNumber', header: gettext('Serial number') }, { name: 'notBefore', path: 'notBefore', header: gettext('Valid from') }, { name: 'notAfter', path: 'notAfter', header: gettext('Expiration date') } ]; this.rows$ = this.reload$.pipe(tap(() => { this.reloading = true; }), map(() => this.route.snapshot.parent.data.contextData), switchMap(device => this.deviceSerialsService.getDeviceProvisionedCertificates(device)), map(provisionedCertificates => provisionedCertificates.map(cert => ({ ...cert, id: cert.serialNumber }))), tap(() => { this.reloading = false; }), shareReplay(1), finalize(() => { this.reloading = false; })); this.pagination = { pageSize: 20, currentPage: 1 }; this.actionControls = [ { type: 'REVOKE', text: gettext('Revoke'), icon: 'trash', iconClasses: 'text-danger', showOnHover: false, callback: (item) => this.revokeProvisionedCertificates([item.serialNumber]) } ]; this.bulkActionControls = [ { type: 'REVOKE', text: gettext('Revoke'), icon: 'trash', callback: (selectedItemsIds) => this.revokeProvisionedCertificates(selectedItemsIds) } ]; this.displayOptions = { filter: false, striped: true, hover: true, bordered: false, gridHeader: true }; this.PX_ACTIONS = PX_ACTIONS; } ngOnInit() { this.reload(); } reload() { this.reload$.next(); } getExpirationHighlight(item) { const now = Date.now(); const notAfter = new Date(item.notAfter).getTime(); const ninetyDays = 90 * 24 * 60 * 60 * 1000; const result = { expired: false, icon: 'calendar', textClass: '', tooltipText: '' }; if (notAfter < now) { result.expired = true; result.icon = 'warning'; result.textClass = 'text-muted'; result.tooltipText = gettext('Certificate expired'); } else if (notAfter < now + ninetyDays) { result.icon = 'warning'; result.textClass = 'text-danger'; result.tooltipText = gettext('Certificate expires in less than 90 days'); } return result; } async revokeProvisionedCertificates(serialNumbersInHex) { if (serialNumbersInHex && serialNumbersInHex.length > 0) { try { await this.confirmRevocation(serialNumbersInHex); await this.crlService.uploadCrls(serialNumbersInHex.map(serialNumber => ({ serialNumberInHex: serialNumber }))); if (serialNumbersInHex.length === 1) { this.alertService.success(gettext('Selected provisioned certificate was revoked.')); } else { this.alertService.success(gettext('Selected provisioned certificates were revoked.')); } this.dataGrid.setAllItemsSelected(false); } catch (error) { this.alertService.addServerFailure(error); } finally { this.reload(); } } } async confirmRevocation(serialNumbers) { const hasMany = serialNumbers.length > 1; const serialsDisplay = serialNumbers.join(', '); const title = hasMany ? gettext('Revoke provisioned certificates?') : gettext('Revoke provisioned certificate?'); const messageTpl = hasMany ? gettext(`You are about to revoke provisioned certificates: {{ serialNumbers }}. Do you want to proceed?`) : gettext(`You are about to revoke provisioned certificate {{ serialNumber }}. Do you want to proceed?`); const message = this.translateService.instant(messageTpl, { serialNumbers: serialsDisplay, serialNumber: serialsDisplay }); return await this.modalService.confirm(title, message, Status.DANGER, { ok: gettext('Revoke'), cancel: gettext('Cancel') }); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DeviceTabProvisionedCertificatesComponent, deps: [{ token: i1$1.AlertService }, { token: i1$1.ModalService }, { token: DeviceProvisionedCertificatesService }, { token: i1.CrlService }, { token: i4.ActivatedRoute }, { token: i5.TranslateService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: DeviceTabProvisionedCertificatesComponent, isStandalone: true, selector: "device-tab-serials-component", viewQueries: [{ propertyName: "dataGrid", first: true, predicate: ["dataGrid"], descendants: true }], ngImport: i0, template: "<c8y-action-bar-item [placement]=\"'right'\">\n <button\n class=\"btn btn-link\"\n title=\"{{ 'Reload' | translate }}\"\n type=\"button\"\n (click)=\"reload()\"\n >\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': reloading }\"\n ></i>\n {{ 'Reload' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<c8y-data-grid\n [title]=\"tableTitle | translate\"\n #dataGrid\n [columns]=\"columns\"\n [rows]=\"rows$ | async\"\n [pagination]=\"pagination\"\n [actionControls]=\"actionControls\"\n [bulkActionControls]=\"bulkActionControls\"\n [displayOptions]=\"displayOptions\"\n [hideReload]=\"true\"\n [selectable]=\"true\"\n>\n <c8y-ui-empty-state\n [icon]=\"'certificate'\"\n [title]=\"'No provisioned certificates to display.' | translate\"\n *ngIf=\"(rows$ | async)?.length === 0\"\n ></c8y-ui-empty-state>\n\n <c8y-column name=\"serialNumber\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span\n class=\"text-truncate\"\n title=\"{{ context.property.header | translate }}: {{ context.value }}\"\n >\n {{ context.value }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"notBefore\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span\n class=\"text-truncate\"\n title=\"{{ context.property.header | translate }}: {{ context.value | c8yDate }}\"\n >\n <span *ngIf=\"context.value\">\n <i\n class=\"m-r-4\"\n c8yIcon=\"calendar\"\n ></i>\n <span>{{ context.value | c8yDate }}</span>\n </span>\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"notAfter\">\n <ng-container *c8yCellRendererDef=\"let context\">\n @let expirationHighlight = getExpirationHighlight(context.item);\n <span\n [title]=\"\n (context.property.header | translate) +\n ': ' +\n (context.value | c8yDate) +\n (expirationHighlight?.tooltipText\n ? ' - ' + (expirationHighlight?.tooltipText | translate)\n : '')\n \"\n [ngClass]=\"expirationHighlight?.textClass\"\n >\n <ng-container *ngIf=\"expirationHighlight?.expired; else notExpiredYet\">\n <del>\n <ng-container *ngTemplateOutlet=\"expirationDateTpl\"></ng-container>\n </del>\n </ng-container>\n <ng-template #notExpiredYet>\n <ng-container *ngTemplateOutlet=\"expirationDateTpl\"></ng-container>\n </ng-template>\n <ng-template #expirationDateTpl>\n <i\n class=\"m-r-4\"\n c8yIcon=\"{{ expirationHighlight?.icon }}\"\n *ngIf=\"expirationHighlight?.icon\"\n ></i>\n <span>{{ context.value | c8yDate }}</span>\n </ng-template>\n </span>\n </ng-container>\n </c8y-column>\n</c8y-data-grid>\n", dependencies: [{ kind: "component", type: ActionBarItemComponent, selector: "c8y-action-bar-item", inputs: ["placement", "priority", "itemClass", "injector", "groupId", "inGroupPriority"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: DataGridComponent, selector: "c8y-data-grid", inputs: ["title", "loadMoreItemsLabel", "loadingItemsLabel", "showSearch", "refresh", "columns", "rows", "pagination", "childNodePagination", "infiniteScroll", "serverSideDataCallback", "selectable", "singleSelection", "selectionPrimaryKey", "displayOptions", "actionControls", "bulkActionControls", "headerActionControls", "searchText", "configureColumnsEnabled", "showCounterWarning", "activeClassName", "expandableRows", "treeGrid", "hideReload", "childNodesProperty", "parentNodeLabelProperty"], outputs: ["rowMouseOver", "rowMouseLeave", "rowClick", "onConfigChange", "onBeforeFilter", "onBeforeSearch", "onFilter", "itemsSelect", "onReload", "onAddCustomColumn", "onRemoveCustomColumn", "onColumnFilterReset", "onSort", "onPageSizeChange", "onColumnReordered", "onColumnVisibilityChange"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: ColumnDirective, selector: "c8y-column", inputs: ["name"] }, { kind: "directive", type: CellRendererDefDirective, selector: "[c8yCellRendererDef]" }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: DatePipe, name: "c8yDate" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DeviceTabProvisionedCertificatesComponent, decorators: [{ type: Component, args: [{ selector: 'device-tab-serials-component', imports: [ ActionBarItemComponent, IconDirective, NgClass, DataGridComponent, NgIf, EmptyStateComponent, ColumnDirective, CellRendererDefDirective, C8yTranslatePipe, AsyncPipe, DatePipe, NgTemplateOutlet ], template: "<c8y-action-bar-item [placement]=\"'right'\">\n <button\n class=\"btn btn-link\"\n title=\"{{ 'Reload' | translate }}\"\n type=\"button\"\n (click)=\"reload()\"\n >\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': reloading }\"\n ></i>\n {{ 'Reload' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<c8y-data-grid\n [title]=\"tableTitle | translate\"\n #dataGrid\n [columns]=\"columns\"\n [rows]=\"rows$ | async\"\n [pagination]=\"pagination\"\n [actionControls]=\"actionControls\"\n [bulkActionControls]=\"bulkActionControls\"\n [displayOptions]=\"displayOptions\"\n [hideReload]=\"true\"\n [selectable]=\"true\"\n>\n <c8y-ui-empty-state\n [icon]=\"'certificate'\"\n [title]=\"'No provisioned certificates to display.' | translate\"\n *ngIf=\"(rows$ | async)?.length === 0\"\n ></c8y-ui-empty-state>\n\n <c8y-column name=\"serialNumber\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span\n class=\"text-truncate\"\n title=\"{{ context.property.header | translate }}: {{ context.value }}\"\n >\n {{ context.value }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"notBefore\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span\n class=\"text-truncate\"\n title=\"{{ context.property.header | translate }}: {{ context.value | c8yDate }}\"\n >\n <span *ngIf=\"context.value\">\n <i\n class=\"m-r-4\"\n c8yIcon=\"calendar\"\n ></i>\n <span>{{ context.value | c8yDate }}</span>\n </span>\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"notAfter\">\n <ng-container *c8yCellRendererDef=\"let context\">\n @let expirationHighlight = getExpirationHighlight(context.item);\n <span\n [title]=\"\n (context.property.header | translate) +\n ': ' +\n (context.value | c8yDate) +\n (expirationHighlight?.tooltipText\n ? ' - ' + (expirationHighlight?.tooltipText | translate)\n : '')\n \"\n [ngClass]=\"expirationHighlight?.textClass\"\n >\n <ng-container *ngIf=\"expirationHighlight?.expired; else notExpiredYet\">\n <del>\n <ng-container *ngTemplateOutlet=\"expirationDateTpl\"></ng-container>\n </del>\n </ng-container>\n <ng-template #notExpiredYet>\n <ng-container *ngTemplateOutlet=\"expirationDateTpl\"></ng-container>\n </ng-template>\n <ng-template #expirationDateTpl>\n <i\n class=\"m-r-4\"\n c8yIcon=\"{{ expirationHighlight?.icon }}\"\n *ngIf=\"expirationHighlight?.icon\"\n ></i>\n <span>{{ context.value | c8yDate }}</span>\n </ng-template>\n </span>\n </ng-container>\n </c8y-column>\n</c8y-data-grid>\n" }] }], ctorParameters: () => [{ type: i1$1.AlertService }, { type: i1$1.ModalService }, { type: DeviceProvisionedCertificatesService }, { type: i1.CrlService }, { type: i4.ActivatedRoute }, { type: i5.TranslateService }], propDecorators: { dataGrid: [{ type: ViewChild, args: ['dataGrid'] }] } }); class DeviceProvisionedCertificatesModule { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DeviceProvisionedCertificatesModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); } static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.15", ngImport: i0, type: DeviceProvisionedCertificatesModule, imports: [CoreModule, TooltipModule, ReactiveFormsModule, ButtonsModule, PopoverModule, DeviceTabProvisionedCertificatesComponent] }); } static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DeviceProvisionedCertificatesModule, providers: [ hookRoute([ { context: ViewContext.Device, path: 'device-provisioned-certificates', component: DeviceTabProvisionedCertificatesComponent, label: gettext('x509'), icon: 'c8y-device-profile', canActivate: [DeviceProvisionedCertificatesGuard] } ]), DeviceProvisionedCertificatesService, DeviceProvisionedCertificatesGuard ], imports: [CoreModule, TooltipModule, ReactiveFormsModule, ButtonsModule, PopoverModule, DeviceTabProvisionedCertificatesComponent] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DeviceProvisionedCertificatesModule, decorators: [{ type: NgModule, args: [{ exports: [], imports: [ CoreModule, TooltipModule, ReactiveFormsModule, ButtonsModule, PopoverModule, DeviceTabProvisionedCertificatesComponent ], providers: [ hookRoute([ { context: ViewContext.Device, path: 'device-provisioned-certificates', component: DeviceTabProvisionedCertificatesComponent, label: gettext('x509'), icon: 'c8y-device-profile', canActivate: [DeviceProvisionedCertificatesGuard] } ]), DeviceProvisionedCertificatesService, DeviceProvisionedCertificatesGuard ] }] }] }); /** * Generated bundle index. Do not edit. */ export { DeviceProvisionedCertificatesModule }; //# sourceMappingURL=c8y-ngx-components-device-provisioned-certificates.mjs.map