Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancements made according to ISC #42

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@
"[javascript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
"source.fixAll.eslint": "explicit"
},
"editor.formatOnSave": false
},
"[typescript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
"source.fixAll.eslint": "explicit"
},
"editor.formatOnSave": false
},
"[json]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
"source.fixAll.eslint": "explicit"
},
"editor.formatOnSave": false
}
Expand Down
7 changes: 7 additions & 0 deletions src/models/EplRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export class EplRule {
id: number;
name: string;
description: string;
state: string;
contents: string;
}
22 changes: 22 additions & 0 deletions src/models/analytics-builder-model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export class AnalyticsBuilderModel {
id: number;
name: string;
description: string;
state: string;
builderVersion: string;
mode: string;
modeProperties: {};
builderModel: {
nodeDataArray: any[];
linkDataArray: any[];
};
c8y_analyticsModel: {};
type: string;
templateParameters: any[];
userInterfaceProperties: {
displayGrid: boolean;
};
tags: any[];
runtimeError: string;
runtimeErrorLocalized: any[];
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
<c8y-title>Device Registration Lookup</c8y-title>


<c8y-action-bar-item [placement]="'right'">
<button class="btn btn-link" (click)="openBulkDeviceRegistrationModal()">
<i c8yIcon="c8y-device-connect"></i>
Bulk Device Registration
</button>
</c8y-action-bar-item>

<c8y-action-bar-item [placement]="'right'">
<button class="btn btn-link" (click)="registerDevice()">
<i c8yIcon="c8y-device-connect"></i>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { BsModalService } from 'ngx-bootstrap/modal';
import { filter, take } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { AddDeviceRegistrationModalComponent } from '../modals/add-device-registration-modal/add-device-registration-modal.component';
import { BulkDeviceRegistrationModalComponent } from '../modals/bulk-device-registration-modal/bulk-device-registration-modal.component';

@Component({
providers: [DeviceRegistrationTableDatasourceService],
Expand Down Expand Up @@ -104,6 +105,27 @@ export class DeviceRegistrationLookupComponent {
];
}

async openBulkDeviceRegistrationModal() {
const response = new Subject<boolean>();
response
.asObservable()
.pipe(
take(1),
filter((tmp) => !!tmp)
)
.subscribe(() => {
this.datasource.clearCache();
this.refresh.emit();
});
const credentials = await this.credService.prepareCachedDummyMicroserviceForAllSubtenants();
const clients = await this.credService.createClients(credentials);

this.modalService.show(BulkDeviceRegistrationModalComponent, {
initialState: { clients, response },
ignoreBackdropClick: true
});
}

acceptRequest(request: TenantSpecificDetails<IDeviceRegistration>): void {
this.c8yModalService
.confirm(
Expand Down
3 changes: 3 additions & 0 deletions src/modules/lookup/lookup.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { AddDeviceRegistrationModalComponent } from './modals/add-device-registr
import { CustomFirmwareUpdateModalComponent } from './modals/custom-firmware-update-modal/custom-firmware-update-modal.component';
import { FirmwareUpdateHistoryComponent } from './firmware-update-history/firmware-update-history.component';
import { RouterModule } from '@angular/router';
import { BulkDeviceRegistrationModalComponent } from './modals/bulk-device-registration-modal/bulk-device-registration-modal.component';

@NgModule({
imports: [
Expand Down Expand Up @@ -65,6 +66,7 @@ import { RouterModule } from '@angular/router';
UserPasswordChangeModalComponent,
AddUserModalComponent,
AddDeviceRegistrationModalComponent,
BulkDeviceRegistrationModalComponent,
CustomFirmwareUpdateModalComponent,
FirmwareUpdateHistoryComponent
],
Expand All @@ -79,6 +81,7 @@ import { RouterModule } from '@angular/router';
UserPasswordChangeModalComponent,
AddUserModalComponent,
AddDeviceRegistrationModalComponent,
BulkDeviceRegistrationModalComponent,
CustomFirmwareUpdateModalComponent,
FirmwareUpdateHistoryComponent
],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<div class="modal-header dialog-header">
<i class="c8y-icon c8y-icon-device-connect"></i>
<h4 translate>Create Bulk Device Registration Request</h4>
</div>
<div class="modal-body dialog-body">
<br>
<div class="form-group">
<label for="tenantSelection">Tenant</label>
<div class="c8y-select-wrapper">
<select id="tenantSelection" class="form-control" [(ngModel)]="selectedTenant" (change)="onTenantSelect()">
<option *ngFor="let item of clients" [value]="item.core.tenant">{{item.core.tenant}}</option>
</select>
<span></span>
</div>
</div>

<div class="form-group">
<c8y-file-picker (onFilesPicked)="onFile($event)" [maxAllowedFiles]="1"></c8y-file-picker>
</div>

<div class="col-6 p-16 p-b-0 d-flex d-col j-c-between">
<div>
<p class="m-b-8">
<strong translate>Full registration</strong>
</p>
<small class="text-muted" translate>
Creates all device credentials and devices using provided list of property values.
Devices can start communicating with the platform immediately.
</small>
</div>
<div class="m-b-16 m-t-16">
<a title="{{ 'Download template' | translate }}" class="btn btn-default btn-sm" target="_self"
(click)="downloadFull()">
<i c8yIcon="download" translate></i>
{{ 'Download template' | translate }}
</a>
</div>
</div>


<div class="col-6 p-b-0 d-flex d-col j-c-between" *ngIf="bulkResponse">
<div class="row">

<div class="col-md-6">
<c8y-operation-result type="success" text="{{ bulkResponse?.numberOfSuccessful + ' device(s)
successfuly registered' | translate }}" [size]="20" [vertical]="false"></c8y-operation-result>
</div>
<div class="col-md-6">
<c8y-operation-result type="warning" text="{{ bulkResponse?.numberOfFailed + ' device(s)
failed to register' | translate }}" [size]="20" [vertical]="false"></c8y-operation-result>
</div>

</div>
<div class="row">


<!-- The record list wrapper -->
<div class="card-group interact-list" *ngIf="bulkResponse?.numberOfFailed > 0">
<!-- The sticky header -->
<div class="page-sticky-header hidden-xs">
<div class="d-flex">
<div class="card-header card-column-20">
<p>Device ID</p>
</div>
<div class="card-header card-column-50">
<p>Reason</p>
</div>
</div>
</div>
<!-- The records list -->
<div *ngFor="let log of bulkResponse?.failedCreationList" class="col-sm-6 col-md-4 col-lg-4 col-xs-12">
<div class="card pointer">
<div class="card-header separator card-column-20">
<span class="card-title text-truncate" title="{{log?.deviceId}}">
{{log?.deviceId}}
</span>
</div>
<div class="card-block card-column-50">
<small>
{{log?.failureReason}}
</small>
</div>

</div>
</div>
</div>
</div>

</div>
<div class="modal-footer">
<button class="btn btn-default" translate type="button" title="{{ 'Cancel' | translate }}"
(click)="onDismiss($event)">
{{bulkResponse ? 'Cancel' : 'OK' }}
</button>
<button *ngIf="!bulkResponse" class="btn btn-primary" [disabled]="!selectedClient || !selectedTenant" translate
type="submit" title="{{ 'Create Device Registration' | translate }}" (click)="onSave($event)">
Create Bulk Device Registration
</button>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { Component } from '@angular/core';
import { Client, IDeviceRegistrationBulkResult, IDeviceRegistrationCreate } from '@c8y/client';
import { AlertService, PickedFiles } from '@c8y/ngx-components';
import { DeviceRegistrationDetailsService } from '@services/device-registration-details.service';
import { isEmpty } from 'lodash-es';
import { BsModalRef } from 'ngx-bootstrap/modal';
import { Subject } from 'rxjs';
import { saveAs } from 'file-saver';

const fullCsvHeaders: string[] = ['ID', 'CREDENTIALS', 'TYPE', 'NAME'];

@Component({
selector: 'ps-bulk-device-registration-modal',
templateUrl: './bulk-device-registration-modal.component.html'
})
export class BulkDeviceRegistrationModalComponent {
clients: Client[];
response: Subject<boolean>;
registration: Partial<IDeviceRegistrationCreate> = {};
selectedTenant: string;
selectedClient: Client;
bulkResponse: IDeviceRegistrationBulkResult;
file: File;

autoAccept = false;

constructor(
private bsModalRef: BsModalRef,
private alertService: AlertService,
private deviceRegistrationDetailsService: DeviceRegistrationDetailsService
) {}

onTenantSelect(): void {
if (!this.selectedClient || this.selectedTenant !== this.selectedClient.core.tenant) {
this.selectedClient = this.clients.find((tmp) => tmp.core.tenant === this.selectedTenant);
}
}

onDismiss(): void {
if (this.response) {
this.response.next(null);
}
this.bsModalRef.hide();
}

onFile(dropped: PickedFiles) {
if (!isEmpty(dropped.url)) {
this.file = null;
return;
} else if (dropped.droppedFiles) {
this.file = dropped.droppedFiles[0].file;
return;
} else {
this.file = null;
}
}

downloadFull() {
return this.download(fullCsvHeaders, 'Full bulk registration - template.csv');
}

download(headers: string[], fileName: string) {
const headerRaw = headers.map((header) => `${header}`).join(';');
const binaryFile = new Blob([headerRaw], { type: 'text/csv' });
saveAs(binaryFile, fileName);
}

onSave(): void {
this.deviceRegistrationDetailsService.createBulkRegistrationRequest(this.selectedClient, this.file).then(
(res) => {
this.bulkResponse = res.data;
},
(error) => {
this.alertService.danger('Failed to create Device Registration.', JSON.stringify(error));
if (this.response) {
this.response.next();
}
}
);
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
<c8y-title>Alarm Mapping</c8y-title>
<c8y-title>Alarm Transformation</c8y-title>
<c8y-action-bar-item [placement]="'right'">
<button class="btn btn-link" [class.btn-pending]="savingAlarmSettings" (click)="saveAlarmMapping()" [disabled]="savingAlarmSettings || loadingSomething || !alarms.length">
<i c8yIcon="floppy-o"></i>
Save Mapping
Save Transformation
</button>
</c8y-action-bar-item>
<c8y-action-bar-item [placement]="'right'">
<button class="btn btn-link" [class.btn-pending]="applyingAlarmSettings" (click)="applyAlarmMapping()" [disabled]="applyingAlarmSettings || loadingSomething || !alarms.length">
<i c8yIcon="bolt"></i>
Apply Mapping
Apply Transformation
</button>
</c8y-action-bar-item>
<c8y-action-bar-item [placement]="'right'">
<button class="btn btn-link" [class.btn-pending]="removingExistingMappings" (click)="removeExistingMappings()" [disabled]="removingExistingMappings || loadingSomething || !clients.length">
<i c8yIcon="trash"></i>
Remove Existing Mappings
Remove Existing Transformation
</button>
</c8y-action-bar-item>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<c8y-title>Analytics Builder</c8y-title>

<div class="split-scroll">

<c8y-data-grid [title]="'Analytics Builder - ' + tenant.name | translate" class="scroll-column sort-fix fit-w" [columns]="columns"
[rows]="rows" [selectable]="false" [actionControls]="actions" [pagination]="pagination" [showSearch]="false"
[bulkActionControls]="bulkActionControls">

<c8y-column name="name">
<ng-container *c8yCellRendererDef="let context">
<c8y-app-icon class="icon-16" [app]="context.item" [name]="context.item?.name"
[contextPath]="context.item?.contextPath"></c8y-app-icon>
<span class="p-l-4" [title]="context.value | humanizeAppName | async">{{ context.value | humanizeAppName | async
}}</span>
</ng-container>
</c8y-column>


<c8y-column name="actions1">
<ng-container *c8yCellRendererDef="let context">
<div class="btn-group btn-group-sm" role="group">
<button class="btn btn-default" title="Deploy to specific subtenants" (click)="createAnalyticsBuilderModel([context.item])">
<i c8yIcon="refresh"></i>
Deploy
</button>
<button class="btn btn-danger" title="Unsubscribe from specific subtenants" (click)="removeAnalyticsBuilderModels([context.item])">
<i c8yIcon="trash"></i>
</button>
</div>
</ng-container>
</c8y-column>
</c8y-data-grid>
</div>
Loading