Showing
33 changed files
with
1551 additions
and
42 deletions
@@ -68,7 +68,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt | @@ -68,7 +68,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt | ||
68 | public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login"; | 68 | public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login"; |
69 | public static final String PUBLIC_LOGIN_ENTRY_POINT = "/api/auth/login/public"; | 69 | public static final String PUBLIC_LOGIN_ENTRY_POINT = "/api/auth/login/public"; |
70 | public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token"; | 70 | public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token"; |
71 | - protected static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/static/**", "/api/noauth/**", "/webjars/**"}; | 71 | + protected static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/assets/**", "/static/**", "/api/noauth/**", "/webjars/**"}; |
72 | public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**"; | 72 | public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**"; |
73 | public static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**"; | 73 | public static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**"; |
74 | 74 | ||
@@ -155,7 +155,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt | @@ -155,7 +155,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt | ||
155 | 155 | ||
156 | @Override | 156 | @Override |
157 | public void configure(WebSecurity web) throws Exception { | 157 | public void configure(WebSecurity web) throws Exception { |
158 | - web.ignoring().antMatchers("/static/**"); | 158 | + web.ignoring().antMatchers("/*.js","/*.css","/*.ico","/assets/**","/static/**"); |
159 | } | 159 | } |
160 | 160 | ||
161 | @Override | 161 | @Override |
@@ -21,7 +21,7 @@ import org.springframework.web.bind.annotation.RequestMapping; | @@ -21,7 +21,7 @@ import org.springframework.web.bind.annotation.RequestMapping; | ||
21 | @Controller | 21 | @Controller |
22 | public class WebConfig { | 22 | public class WebConfig { |
23 | 23 | ||
24 | - @RequestMapping(value = "/{path:^(?!api$)(?!static$)(?!webjars$)[^\\.]*}/**") | 24 | + @RequestMapping(value = "/{path:^(?!api$)(?!assets$)(?!static$)(?!webjars$)[^\\.]*}/**") |
25 | public String redirect() { | 25 | public String redirect() { |
26 | return "forward:/index.html"; | 26 | return "forward:/index.html"; |
27 | } | 27 | } |
@@ -31,9 +31,9 @@ export class CustomerService { | @@ -31,9 +31,9 @@ export class CustomerService { | ||
31 | private http: HttpClient | 31 | private http: HttpClient |
32 | ) { } | 32 | ) { } |
33 | 33 | ||
34 | - public getCustomers(tenantId: string, pageLink: PageLink, ignoreErrors: boolean = false, | 34 | + public getCustomers(pageLink: PageLink, ignoreErrors: boolean = false, |
35 | ignoreLoading: boolean = false): Observable<PageData<Customer>> { | 35 | ignoreLoading: boolean = false): Observable<PageData<Customer>> { |
36 | - return this.http.get<PageData<Customer>>(`/api/tenant/${tenantId}/customers${pageLink.toQuery()}`, | 36 | + return this.http.get<PageData<Customer>>(`/api/customers${pageLink.toQuery()}`, |
37 | defaultHttpOptions(ignoreLoading, ignoreErrors)); | 37 | defaultHttpOptions(ignoreLoading, ignoreErrors)); |
38 | } | 38 | } |
39 | 39 |
@@ -16,15 +16,16 @@ | @@ -16,15 +16,16 @@ | ||
16 | 16 | ||
17 | import { Injectable } from '@angular/core'; | 17 | import { Injectable } from '@angular/core'; |
18 | import { defaultHttpOptions } from './http-utils'; | 18 | import { defaultHttpOptions } from './http-utils'; |
19 | -import { Observable } from 'rxjs/index'; | 19 | +import { Observable, Subject, ReplaySubject } from 'rxjs/index'; |
20 | import { HttpClient } from '@angular/common/http'; | 20 | import { HttpClient } from '@angular/common/http'; |
21 | import { PageLink } from '@shared/models/page/page-link'; | 21 | import { PageLink } from '@shared/models/page/page-link'; |
22 | import { PageData } from '@shared/models/page/page-data'; | 22 | import { PageData } from '@shared/models/page/page-data'; |
23 | import { Tenant } from '@shared/models/tenant.model'; | 23 | import { Tenant } from '@shared/models/tenant.model'; |
24 | import {DashboardInfo, Dashboard} from '@shared/models/dashboard.models'; | 24 | import {DashboardInfo, Dashboard} from '@shared/models/dashboard.models'; |
25 | import {map} from 'rxjs/operators'; | 25 | import {map} from 'rxjs/operators'; |
26 | -import {DeviceInfo, Device} from '@app/shared/models/device.models'; | 26 | +import {DeviceInfo, Device, DeviceCredentials} from '@app/shared/models/device.models'; |
27 | import {EntitySubtype} from '@app/shared/models/entity-type.models'; | 27 | import {EntitySubtype} from '@app/shared/models/entity-type.models'; |
28 | +import {AuthService} from '../auth/auth.service'; | ||
28 | 29 | ||
29 | @Injectable({ | 30 | @Injectable({ |
30 | providedIn: 'root' | 31 | providedIn: 'root' |
@@ -67,6 +68,40 @@ export class DeviceService { | @@ -67,6 +68,40 @@ export class DeviceService { | ||
67 | return this.http.get<Array<EntitySubtype>>('/api/device/types', defaultHttpOptions(ignoreLoading, ignoreErrors)); | 68 | return this.http.get<Array<EntitySubtype>>('/api/device/types', defaultHttpOptions(ignoreLoading, ignoreErrors)); |
68 | } | 69 | } |
69 | 70 | ||
71 | + public getDeviceCredentials(deviceId: string, sync: boolean = false, ignoreErrors: boolean = false, | ||
72 | + ignoreLoading: boolean = false): Observable<DeviceCredentials> { | ||
73 | + const url = `/api/device/${deviceId}/credentials`; | ||
74 | + if (sync) { | ||
75 | + const responseSubject = new ReplaySubject<DeviceCredentials>(); | ||
76 | + const request = new XMLHttpRequest(); | ||
77 | + request.open('GET', url, false); | ||
78 | + request.setRequestHeader('Accept', 'application/json, text/plain, */*'); | ||
79 | + const jwtToken = AuthService.getJwtToken(); | ||
80 | + if (jwtToken) { | ||
81 | + request.setRequestHeader('X-Authorization', 'Bearer ' + jwtToken); | ||
82 | + } | ||
83 | + request.send(null); | ||
84 | + if (request.status === 200) { | ||
85 | + const credentials = JSON.parse(request.responseText) as DeviceCredentials; | ||
86 | + responseSubject.next(credentials); | ||
87 | + } else { | ||
88 | + responseSubject.error(null); | ||
89 | + } | ||
90 | + return responseSubject.asObservable(); | ||
91 | + } else { | ||
92 | + return this.http.get<DeviceCredentials>(url, defaultHttpOptions(ignoreLoading, ignoreErrors)); | ||
93 | + } | ||
94 | + } | ||
95 | + | ||
96 | + public saveDeviceCredentials(deviceCredentials: DeviceCredentials, ignoreErrors: boolean = false, | ||
97 | + ignoreLoading: boolean = false): Observable<DeviceCredentials> { | ||
98 | + return this.http.post<DeviceCredentials>('/api/device/credentials', deviceCredentials, defaultHttpOptions(ignoreLoading, ignoreErrors)); | ||
99 | + } | ||
100 | + | ||
101 | + public makeDevicePublic(deviceId: string, ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<Device> { | ||
102 | + return this.http.post<Device>(`/api/customer/public/device/${deviceId}`, null, defaultHttpOptions(ignoreLoading, ignoreErrors)); | ||
103 | + } | ||
104 | + | ||
70 | public unassignDeviceFromCustomer(deviceId: string, ignoreErrors: boolean = false, ignoreLoading: boolean = false) { | 105 | public unassignDeviceFromCustomer(deviceId: string, ignoreErrors: boolean = false, ignoreLoading: boolean = false) { |
71 | return this.http.delete(`/api/customer/device/${deviceId}`, defaultHttpOptions(ignoreLoading, ignoreErrors)); | 106 | return this.http.delete(`/api/customer/device/${deviceId}`, defaultHttpOptions(ignoreLoading, ignoreErrors)); |
72 | } | 107 | } |
ui-ngx/src/app/core/http/entity.service.ts
0 → 100644
1 | +/// | ||
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import {Injectable} from '@angular/core'; | ||
18 | +import {Observable, throwError, of, empty, EMPTY} from 'rxjs/index'; | ||
19 | +import {HttpClient} from '@angular/common/http'; | ||
20 | +import {PageLink} from '@shared/models/page/page-link'; | ||
21 | +import {EntityType} from '@shared/models/entity-type.models'; | ||
22 | +import {BaseData} from '@shared/models/base-data'; | ||
23 | +import {EntityId} from '@shared/models/id/entity-id'; | ||
24 | +import {DeviceService} from '@core/http/device.service'; | ||
25 | +import {TenantService} from '@core/http/tenant.service'; | ||
26 | +import {CustomerService} from '@core/http/customer.service'; | ||
27 | +import {UserService} from './user.service'; | ||
28 | +import {DashboardService} from '@core/http/dashboard.service'; | ||
29 | +import {Direction} from '@shared/models/page/sort-order'; | ||
30 | +import {PageData} from '@shared/models/page/page-data'; | ||
31 | +import {getCurrentAuthUser} from '../auth/auth.selectors'; | ||
32 | +import {Store} from '@ngrx/store'; | ||
33 | +import {AppState} from '@core/core.state'; | ||
34 | +import {Authority} from '@shared/models/authority.enum'; | ||
35 | +import {Tenant} from '@shared/models/tenant.model'; | ||
36 | +import {concatMap, expand, map, toArray} from 'rxjs/operators'; | ||
37 | +import {Customer} from '@app/shared/models/customer.model'; | ||
38 | + | ||
39 | +@Injectable({ | ||
40 | + providedIn: 'root' | ||
41 | +}) | ||
42 | +export class EntityService { | ||
43 | + | ||
44 | + constructor( | ||
45 | + private http: HttpClient, | ||
46 | + private store: Store<AppState>, | ||
47 | + private deviceService: DeviceService, | ||
48 | + private tenantService: TenantService, | ||
49 | + private customerService: CustomerService, | ||
50 | + private userService: UserService, | ||
51 | + private dashboardService: DashboardService | ||
52 | + ) { } | ||
53 | + | ||
54 | + private getEntityObservable(entityType: EntityType, entityId: string, | ||
55 | + ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<BaseData<EntityId>> { | ||
56 | + | ||
57 | + let observable: Observable<BaseData<EntityId>>; | ||
58 | + switch (entityType) { | ||
59 | + case EntityType.DEVICE: | ||
60 | + observable = this.deviceService.getDevice(entityId, ignoreErrors, ignoreLoading); | ||
61 | + break; | ||
62 | + case EntityType.ASSET: | ||
63 | + // TODO: | ||
64 | + break; | ||
65 | + case EntityType.ENTITY_VIEW: | ||
66 | + // TODO: | ||
67 | + break; | ||
68 | + case EntityType.TENANT: | ||
69 | + observable = this.tenantService.getTenant(entityId, ignoreErrors, ignoreLoading); | ||
70 | + break; | ||
71 | + case EntityType.CUSTOMER: | ||
72 | + observable = this.customerService.getCustomer(entityId, ignoreErrors, ignoreLoading); | ||
73 | + break; | ||
74 | + case EntityType.DASHBOARD: | ||
75 | + observable = this.dashboardService.getDashboardInfo(entityId, ignoreErrors, ignoreLoading); | ||
76 | + break; | ||
77 | + case EntityType.USER: | ||
78 | + observable = this.userService.getUser(entityId, ignoreErrors, ignoreLoading); | ||
79 | + break; | ||
80 | + case EntityType.RULE_CHAIN: | ||
81 | + // TODO: | ||
82 | + break; | ||
83 | + case EntityType.ALARM: | ||
84 | + console.error('Get Alarm Entity is not implemented!'); | ||
85 | + break; | ||
86 | + } | ||
87 | + return observable; | ||
88 | + } | ||
89 | + | ||
90 | + public getEntity(entityType: EntityType, entityId: string, | ||
91 | + ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<BaseData<EntityId>> { | ||
92 | + const entityObservable = this.getEntityObservable(entityType, entityId, ignoreErrors, ignoreLoading); | ||
93 | + if (entityObservable) { | ||
94 | + return entityObservable; | ||
95 | + } else { | ||
96 | + return throwError(null); | ||
97 | + } | ||
98 | + } | ||
99 | + | ||
100 | + private getSingleTenantByPageLinkObservable(pageLink: PageLink, | ||
101 | + ignoreErrors: boolean = false, | ||
102 | + ignoreLoading: boolean = false): Observable<PageData<Tenant>> { | ||
103 | + const authUser = getCurrentAuthUser(this.store); | ||
104 | + const tenantId = authUser.tenantId; | ||
105 | + return this.tenantService.getTenant(tenantId, ignoreErrors, ignoreLoading).pipe( | ||
106 | + map((tenant) => { | ||
107 | + const result = { | ||
108 | + data: [], | ||
109 | + totalPages: 0, | ||
110 | + totalElements: 0, | ||
111 | + hasNext: false | ||
112 | + } as PageData<Tenant>; | ||
113 | + if (tenant.title.toLowerCase().startsWith(pageLink.textSearch.toLowerCase())) { | ||
114 | + result.data.push(tenant); | ||
115 | + result.totalPages = 1; | ||
116 | + result.totalElements = 1; | ||
117 | + } | ||
118 | + return result; | ||
119 | + }) | ||
120 | + ); | ||
121 | + } | ||
122 | + | ||
123 | + private getSingleCustomerByPageLinkObservable(pageLink: PageLink, | ||
124 | + ignoreErrors: boolean = false, | ||
125 | + ignoreLoading: boolean = false): Observable<PageData<Customer>> { | ||
126 | + const authUser = getCurrentAuthUser(this.store); | ||
127 | + const customerId = authUser.customerId; | ||
128 | + return this.customerService.getCustomer(customerId, ignoreErrors, ignoreLoading).pipe( | ||
129 | + map((customer) => { | ||
130 | + const result = { | ||
131 | + data: [], | ||
132 | + totalPages: 0, | ||
133 | + totalElements: 0, | ||
134 | + hasNext: false | ||
135 | + } as PageData<Customer>; | ||
136 | + if (customer.title.toLowerCase().startsWith(pageLink.textSearch.toLowerCase())) { | ||
137 | + result.data.push(customer); | ||
138 | + result.totalPages = 1; | ||
139 | + result.totalElements = 1; | ||
140 | + } | ||
141 | + return result; | ||
142 | + }) | ||
143 | + ); | ||
144 | + } | ||
145 | + | ||
146 | + private getEntitiesByPageLinkObservable(entityType: EntityType, pageLink: PageLink, subType: string = '', | ||
147 | + ignoreErrors: boolean = false, | ||
148 | + ignoreLoading: boolean = false): Observable<PageData<BaseData<EntityId>>> { | ||
149 | + let entitiesObservable: Observable<PageData<BaseData<EntityId>>>; | ||
150 | + const authUser = getCurrentAuthUser(this.store); | ||
151 | + const customerId = authUser.customerId; | ||
152 | + switch (entityType) { | ||
153 | + case EntityType.DEVICE: | ||
154 | + pageLink.sortOrder.property = 'name'; | ||
155 | + if (authUser.authority === Authority.CUSTOMER_USER) { | ||
156 | + entitiesObservable = this.deviceService.getCustomerDeviceInfos(customerId, pageLink, subType, ignoreErrors, ignoreLoading); | ||
157 | + } else { | ||
158 | + entitiesObservable = this.deviceService.getTenantDeviceInfos(pageLink, subType, ignoreErrors, ignoreLoading); | ||
159 | + } | ||
160 | + break; | ||
161 | + case EntityType.ASSET: | ||
162 | + pageLink.sortOrder.property = 'name'; | ||
163 | + if (authUser.authority === Authority.CUSTOMER_USER) { | ||
164 | + // TODO: | ||
165 | + } else { | ||
166 | + // TODO: | ||
167 | + } | ||
168 | + break; | ||
169 | + case EntityType.ENTITY_VIEW: | ||
170 | + pageLink.sortOrder.property = 'name'; | ||
171 | + if (authUser.authority === Authority.CUSTOMER_USER) { | ||
172 | + // TODO: | ||
173 | + } else { | ||
174 | + // TODO: | ||
175 | + } | ||
176 | + break; | ||
177 | + case EntityType.TENANT: | ||
178 | + pageLink.sortOrder.property = 'title'; | ||
179 | + if (authUser.authority === Authority.TENANT_ADMIN) { | ||
180 | + entitiesObservable = this.getSingleTenantByPageLinkObservable(pageLink, ignoreErrors, ignoreLoading); | ||
181 | + } else { | ||
182 | + entitiesObservable = this.tenantService.getTenants(pageLink, ignoreErrors, ignoreLoading); | ||
183 | + } | ||
184 | + break; | ||
185 | + case EntityType.CUSTOMER: | ||
186 | + pageLink.sortOrder.property = 'title'; | ||
187 | + if (authUser.authority === Authority.CUSTOMER_USER) { | ||
188 | + entitiesObservable = this.getSingleCustomerByPageLinkObservable(pageLink, ignoreErrors, ignoreLoading); | ||
189 | + } else { | ||
190 | + entitiesObservable = this.customerService.getCustomers(pageLink, ignoreErrors, ignoreLoading); | ||
191 | + } | ||
192 | + break; | ||
193 | + case EntityType.RULE_CHAIN: | ||
194 | + pageLink.sortOrder.property = 'name'; | ||
195 | + // TODO: | ||
196 | + break; | ||
197 | + case EntityType.DASHBOARD: | ||
198 | + pageLink.sortOrder.property = 'title'; | ||
199 | + if (authUser.authority === Authority.CUSTOMER_USER) { | ||
200 | + entitiesObservable = this.dashboardService.getCustomerDashboards(customerId, pageLink, ignoreErrors, ignoreLoading); | ||
201 | + } else { | ||
202 | + entitiesObservable = this.dashboardService.getTenantDashboards(pageLink, ignoreErrors, ignoreLoading); | ||
203 | + } | ||
204 | + break; | ||
205 | + case EntityType.USER: | ||
206 | + console.error('Get User Entities is not implemented!'); | ||
207 | + break; | ||
208 | + case EntityType.ALARM: | ||
209 | + console.error('Get Alarm Entities is not implemented!'); | ||
210 | + break; | ||
211 | + } | ||
212 | + return entitiesObservable; | ||
213 | + } | ||
214 | + | ||
215 | + private getEntitiesByPageLink(entityType: EntityType, pageLink: PageLink, subType: string = '', | ||
216 | + ignoreErrors: boolean = false, | ||
217 | + ignoreLoading: boolean = false): Observable<Array<BaseData<EntityId>>> { | ||
218 | + const entitiesObservable: Observable<PageData<BaseData<EntityId>>> = | ||
219 | + this.getEntitiesByPageLinkObservable(entityType, pageLink, subType, ignoreErrors, ignoreLoading); | ||
220 | + if (entitiesObservable) { | ||
221 | + return entitiesObservable.pipe( | ||
222 | + expand((data) => { | ||
223 | + if (data.hasNext) { | ||
224 | + pageLink.page += 1; | ||
225 | + return this.getEntitiesByPageLinkObservable(entityType, pageLink, subType, ignoreErrors, ignoreLoading); | ||
226 | + } else { | ||
227 | + return EMPTY; | ||
228 | + } | ||
229 | + }), | ||
230 | + map((data) => data.data), | ||
231 | + concatMap((data) => data), | ||
232 | + toArray() | ||
233 | + ); | ||
234 | + } else { | ||
235 | + return of(null); | ||
236 | + } | ||
237 | + } | ||
238 | + | ||
239 | + public getEntitiesByNameFilter(entityType: EntityType, entityNameFilter: string, | ||
240 | + pageSize: number, subType: string = '', | ||
241 | + ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<Array<BaseData<EntityId>>> { | ||
242 | + const pageLink = new PageLink(pageSize, 0, entityNameFilter, { | ||
243 | + property: 'name', | ||
244 | + direction: Direction.ASC | ||
245 | + }); | ||
246 | + if (pageSize === -1) { // all | ||
247 | + pageLink.pageSize = 100; | ||
248 | + return this.getEntitiesByPageLink(entityType, pageLink, subType, ignoreErrors, ignoreLoading).pipe( | ||
249 | + map((data) => data && data.length ? data : null) | ||
250 | + ); | ||
251 | + } else { | ||
252 | + const entitiesObservable: Observable<PageData<BaseData<EntityId>>> = | ||
253 | + this.getEntitiesByPageLinkObservable(entityType, pageLink, subType, ignoreErrors, ignoreLoading); | ||
254 | + if (entitiesObservable) { | ||
255 | + return entitiesObservable.pipe( | ||
256 | + map((data) => data && data.data.length ? data.data : null) | ||
257 | + ); | ||
258 | + } else { | ||
259 | + return of(null); | ||
260 | + } | ||
261 | + } | ||
262 | + } | ||
263 | +} |
1 | +<!-- | ||
2 | + | ||
3 | + Copyright © 2016-2019 The Thingsboard Authors | ||
4 | + | ||
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + you may not use this file except in compliance with the License. | ||
7 | + You may obtain a copy of the License at | ||
8 | + | ||
9 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + | ||
11 | + Unless required by applicable law or agreed to in writing, software | ||
12 | + distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + See the License for the specific language governing permissions and | ||
15 | + limitations under the License. | ||
16 | + | ||
17 | +--> | ||
18 | +<form #deviceCredentialsForm="ngForm" [formGroup]="deviceCredentialsFormGroup" (ngSubmit)="save()"> | ||
19 | + <mat-toolbar fxLayout="row" color="primary"> | ||
20 | + <h2 translate>device.device-credentials</h2> | ||
21 | + <span fxFlex></span> | ||
22 | + <button mat-button mat-icon-button | ||
23 | + (click)="cancel()" | ||
24 | + type="button"> | ||
25 | + <mat-icon class="material-icons">close</mat-icon> | ||
26 | + </button> | ||
27 | + </mat-toolbar> | ||
28 | + <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async"> | ||
29 | + </mat-progress-bar> | ||
30 | + <div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div> | ||
31 | + <div mat-dialog-content> | ||
32 | + <fieldset [disabled]="(isLoading$ | async) || isReadOnly"> | ||
33 | + <mat-form-field class="mat-block"> | ||
34 | + <mat-label translate>device.credentials-type</mat-label> | ||
35 | + <mat-select matInput formControlName="credentialsType" | ||
36 | + (ngModelChange)="credentialsTypeChanged()"> | ||
37 | + <mat-option *ngFor="let credentialsType of credentialsTypes" [value]="credentialsType"> | ||
38 | + {{ credentialTypeNamesMap.get(credentialsType) }} | ||
39 | + </mat-option> | ||
40 | + </mat-select> | ||
41 | + </mat-form-field> | ||
42 | + <mat-form-field *ngIf="deviceCredentialsFormGroup.get('credentialsType').value === deviceCredentialsType.ACCESS_TOKEN" | ||
43 | + class="mat-block"> | ||
44 | + <mat-label translate>device.access-token</mat-label> | ||
45 | + <input matInput formControlName="credentialsId" required> | ||
46 | + <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsId').hasError('required')"> | ||
47 | + {{ 'device.access-token-required' | translate }} | ||
48 | + </mat-error> | ||
49 | + <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsId').hasError('pattern')"> | ||
50 | + {{ 'device.access-token-invalid' | translate }} | ||
51 | + </mat-error> | ||
52 | + </mat-form-field> | ||
53 | + <mat-form-field *ngIf="deviceCredentialsFormGroup.get('credentialsType').value === deviceCredentialsType.X509_CERTIFICATE" | ||
54 | + class="mat-block"> | ||
55 | + <mat-label translate>device.rsa-key</mat-label> | ||
56 | + <textarea matInput formControlName="credentialsValue" cols="15" rows="5" required></textarea> | ||
57 | + <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsValue').hasError('required')"> | ||
58 | + {{ 'device.rsa-key-required' | translate }} | ||
59 | + </mat-error> | ||
60 | + </mat-form-field> | ||
61 | + </fieldset> | ||
62 | + </div> | ||
63 | + <div mat-dialog-actions fxLayout="row"> | ||
64 | + <span fxFlex></span> | ||
65 | + <button *ngIf="!isReadOnly" mat-button mat-raised-button color="primary" | ||
66 | + type="submit" | ||
67 | + [disabled]="(isLoading$ | async) || deviceCredentialsForm.invalid | ||
68 | + || !deviceCredentialsForm.dirty"> | ||
69 | + {{ 'action.save' | translate }} | ||
70 | + </button> | ||
71 | + <button mat-button color="primary" | ||
72 | + style="margin-right: 20px;" | ||
73 | + type="button" | ||
74 | + [disabled]="(isLoading$ | async)" | ||
75 | + (click)="cancel()" cdkFocusInitial> | ||
76 | + {{ (isReadOnly ? 'action.close' : 'action.cancel') | translate }} | ||
77 | + </button> | ||
78 | + </div> | ||
79 | +</form> |
1 | +/// | ||
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import {Component, OnInit, SkipSelf, Inject} from '@angular/core'; | ||
18 | +import {ErrorStateMatcher, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material'; | ||
19 | +import { PageComponent } from '@shared/components/page.component'; | ||
20 | +import { Store } from '@ngrx/store'; | ||
21 | +import { AppState } from '@core/core.state'; | ||
22 | +import {FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Validators} from '@angular/forms'; | ||
23 | +import { ActionNotificationShow } from '@core/notification/notification.actions'; | ||
24 | +import { TranslateService } from '@ngx-translate/core'; | ||
25 | +import { AuthService } from '@core/auth/auth.service'; | ||
26 | +import {DeviceService} from '@core/http/device.service'; | ||
27 | +import {DeviceCredentials, DeviceCredentialsType, credentialTypeNames} from '@shared/models/device.models'; | ||
28 | + | ||
29 | +export interface DeviceCredentialsDialogData { | ||
30 | + isReadOnly: boolean; | ||
31 | + deviceId: string; | ||
32 | +} | ||
33 | + | ||
34 | +@Component({ | ||
35 | + selector: 'tb-device-credentials-dialog', | ||
36 | + templateUrl: './device-credentials-dialog.component.html', | ||
37 | + providers: [{provide: ErrorStateMatcher, useExisting: DeviceCredentialsDialogComponent}], | ||
38 | + styleUrls: [] | ||
39 | +}) | ||
40 | +export class DeviceCredentialsDialogComponent extends PageComponent implements OnInit, ErrorStateMatcher { | ||
41 | + | ||
42 | + deviceCredentialsFormGroup: FormGroup; | ||
43 | + | ||
44 | + isReadOnly: boolean; | ||
45 | + | ||
46 | + deviceCredentials: DeviceCredentials; | ||
47 | + | ||
48 | + submitted = false; | ||
49 | + | ||
50 | + deviceCredentialsType = DeviceCredentialsType; | ||
51 | + | ||
52 | + credentialsTypes = Object.keys(DeviceCredentialsType); | ||
53 | + | ||
54 | + credentialTypeNamesMap = credentialTypeNames; | ||
55 | + | ||
56 | + constructor(protected store: Store<AppState>, | ||
57 | + @Inject(MAT_DIALOG_DATA) public data: DeviceCredentialsDialogData, | ||
58 | + private deviceService: DeviceService, | ||
59 | + @SkipSelf() private errorStateMatcher: ErrorStateMatcher, | ||
60 | + public dialogRef: MatDialogRef<DeviceCredentialsDialogComponent, DeviceCredentials>, | ||
61 | + public fb: FormBuilder) { | ||
62 | + super(store); | ||
63 | + | ||
64 | + this.isReadOnly = data.isReadOnly; | ||
65 | + } | ||
66 | + | ||
67 | + ngOnInit(): void { | ||
68 | + this.deviceCredentialsFormGroup = this.fb.group({ | ||
69 | + credentialsType: [DeviceCredentialsType.ACCESS_TOKEN], | ||
70 | + credentialsId: [''], | ||
71 | + credentialsValue: [''] | ||
72 | + }); | ||
73 | + if (this.isReadOnly) { | ||
74 | + this.deviceCredentialsFormGroup.disable({emitEvent: false}); | ||
75 | + } else { | ||
76 | + this.registerDisableOnLoadFormControl(this.deviceCredentialsFormGroup.get('credentialsType')); | ||
77 | + } | ||
78 | + this.loadDeviceCredentials(); | ||
79 | + } | ||
80 | + | ||
81 | + isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { | ||
82 | + const originalErrorState = this.errorStateMatcher.isErrorState(control, form); | ||
83 | + const customErrorState = !!(control && control.invalid && this.submitted); | ||
84 | + return originalErrorState || customErrorState; | ||
85 | + } | ||
86 | + | ||
87 | + loadDeviceCredentials() { | ||
88 | + this.deviceService.getDeviceCredentials(this.data.deviceId).subscribe( | ||
89 | + (deviceCredentials) => { | ||
90 | + this.deviceCredentials = deviceCredentials; | ||
91 | + this.deviceCredentialsFormGroup.patchValue({ | ||
92 | + credentialsType: deviceCredentials.credentialsType, | ||
93 | + credentialsId: deviceCredentials.credentialsId, | ||
94 | + credentialsValue: deviceCredentials.credentialsValue | ||
95 | + }); | ||
96 | + this.updateValidators(); | ||
97 | + } | ||
98 | + ); | ||
99 | + } | ||
100 | + | ||
101 | + credentialsTypeChanged(): void { | ||
102 | + this.deviceCredentialsFormGroup.patchValue( | ||
103 | + {credentialsId: null, credentialsValue: null}, {emitEvent: true}); | ||
104 | + this.updateValidators(); | ||
105 | + } | ||
106 | + | ||
107 | + updateValidators(): void { | ||
108 | + const crendetialsType = this.deviceCredentialsFormGroup.get('credentialsType').value as DeviceCredentialsType; | ||
109 | + switch (crendetialsType) { | ||
110 | + case DeviceCredentialsType.ACCESS_TOKEN: | ||
111 | + this.deviceCredentialsFormGroup.get('credentialsId').setValidators([Validators.max(20), Validators.pattern(/^.{1,20}$/)]); | ||
112 | + this.deviceCredentialsFormGroup.get('credentialsId').updateValueAndValidity(); | ||
113 | + this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([]); | ||
114 | + this.deviceCredentialsFormGroup.get('credentialsValue').updateValueAndValidity(); | ||
115 | + break; | ||
116 | + case DeviceCredentialsType.X509_CERTIFICATE: | ||
117 | + this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([Validators.required]); | ||
118 | + this.deviceCredentialsFormGroup.get('credentialsValue').updateValueAndValidity(); | ||
119 | + this.deviceCredentialsFormGroup.get('credentialsId').setValidators([]); | ||
120 | + this.deviceCredentialsFormGroup.get('credentialsId').updateValueAndValidity(); | ||
121 | + break; | ||
122 | + } | ||
123 | + } | ||
124 | + | ||
125 | + cancel(): void { | ||
126 | + this.dialogRef.close(null); | ||
127 | + } | ||
128 | + | ||
129 | + save(): void { | ||
130 | + this.submitted = true; | ||
131 | + this.deviceCredentials = {...this.deviceCredentials, ...this.deviceCredentialsFormGroup.value}; | ||
132 | + this.deviceService.saveDeviceCredentials(this.deviceCredentials).subscribe( | ||
133 | + (deviceCredentials) => { | ||
134 | + this.dialogRef.close(deviceCredentials); | ||
135 | + } | ||
136 | + ); | ||
137 | + } | ||
138 | +} |
@@ -26,6 +26,10 @@ import { Authority } from '@shared/models/authority.enum'; | @@ -26,6 +26,10 @@ import { Authority } from '@shared/models/authority.enum'; | ||
26 | import {DeviceInfo} from '@shared/models/device.models'; | 26 | import {DeviceInfo} from '@shared/models/device.models'; |
27 | import {EntityType} from '@shared/models/entity-type.models'; | 27 | import {EntityType} from '@shared/models/entity-type.models'; |
28 | import {NULL_UUID} from '@shared/models/id/has-uuid'; | 28 | import {NULL_UUID} from '@shared/models/id/has-uuid'; |
29 | +import {ActionNotificationShow} from '@core/notification/notification.actions'; | ||
30 | +import {TranslateService} from '@ngx-translate/core'; | ||
31 | +import {DeviceService} from '@core/http/device.service'; | ||
32 | +import {ClipboardService} from 'ngx-clipboard'; | ||
29 | 33 | ||
30 | @Component({ | 34 | @Component({ |
31 | selector: 'tb-device', | 35 | selector: 'tb-device', |
@@ -39,6 +43,9 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> { | @@ -39,6 +43,9 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> { | ||
39 | deviceScope: 'tenant' | 'customer' | 'customer_user'; | 43 | deviceScope: 'tenant' | 'customer' | 'customer_user'; |
40 | 44 | ||
41 | constructor(protected store: Store<AppState>, | 45 | constructor(protected store: Store<AppState>, |
46 | + protected translate: TranslateService, | ||
47 | + private deviceService: DeviceService, | ||
48 | + private clipboardService: ClipboardService, | ||
42 | public fb: FormBuilder) { | 49 | public fb: FormBuilder) { |
43 | super(store); | 50 | super(store); |
44 | } | 51 | } |
@@ -85,4 +92,35 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> { | @@ -85,4 +92,35 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> { | ||
85 | this.entityForm.patchValue({additionalInfo: {description: entity.additionalInfo ? entity.additionalInfo.description : ''}}); | 92 | this.entityForm.patchValue({additionalInfo: {description: entity.additionalInfo ? entity.additionalInfo.description : ''}}); |
86 | } | 93 | } |
87 | 94 | ||
95 | + | ||
96 | + onDeviceIdCopied($event) { | ||
97 | + this.store.dispatch(new ActionNotificationShow( | ||
98 | + { | ||
99 | + message: this.translate.instant('device.idCopiedMessage'), | ||
100 | + type: 'success', | ||
101 | + duration: 750, | ||
102 | + verticalPosition: 'bottom', | ||
103 | + horizontalPosition: 'right' | ||
104 | + })); | ||
105 | + } | ||
106 | + | ||
107 | + copyAccessToken($event) { | ||
108 | + if (this.entity.id) { | ||
109 | + this.deviceService.getDeviceCredentials(this.entity.id.id, true).subscribe( | ||
110 | + (deviceCredentials) => { | ||
111 | + const credentialsId = deviceCredentials.credentialsId; | ||
112 | + if (this.clipboardService.copyFromContent(credentialsId)) { | ||
113 | + this.store.dispatch(new ActionNotificationShow( | ||
114 | + { | ||
115 | + message: this.translate.instant('device.accessTokenCopiedMessage'), | ||
116 | + type: 'success', | ||
117 | + duration: 750, | ||
118 | + verticalPosition: 'bottom', | ||
119 | + horizontalPosition: 'right' | ||
120 | + })); | ||
121 | + } | ||
122 | + } | ||
123 | + ); | ||
124 | + } | ||
125 | + } | ||
88 | } | 126 | } |
@@ -20,15 +20,18 @@ import { SharedModule } from '@shared/shared.module'; | @@ -20,15 +20,18 @@ import { SharedModule } from '@shared/shared.module'; | ||
20 | import {DeviceComponent} from '@modules/home/pages/device/device.component'; | 20 | import {DeviceComponent} from '@modules/home/pages/device/device.component'; |
21 | import {DeviceRoutingModule} from './device-routing.module'; | 21 | import {DeviceRoutingModule} from './device-routing.module'; |
22 | import {DeviceTableHeaderComponent} from '@modules/home/pages/device/device-table-header.component'; | 22 | import {DeviceTableHeaderComponent} from '@modules/home/pages/device/device-table-header.component'; |
23 | +import {DeviceCredentialsDialogComponent} from '@modules/home/pages/device/device-credentials-dialog.component'; | ||
23 | 24 | ||
24 | @NgModule({ | 25 | @NgModule({ |
25 | entryComponents: [ | 26 | entryComponents: [ |
26 | DeviceComponent, | 27 | DeviceComponent, |
27 | - DeviceTableHeaderComponent | 28 | + DeviceTableHeaderComponent, |
29 | + DeviceCredentialsDialogComponent | ||
28 | ], | 30 | ], |
29 | declarations: [ | 31 | declarations: [ |
30 | DeviceComponent, | 32 | DeviceComponent, |
31 | - DeviceTableHeaderComponent | 33 | + DeviceTableHeaderComponent, |
34 | + DeviceCredentialsDialogComponent | ||
32 | ], | 35 | ], |
33 | imports: [ | 36 | imports: [ |
34 | CommonModule, | 37 | CommonModule, |
@@ -24,7 +24,8 @@ import { | @@ -24,7 +24,8 @@ import { | ||
24 | checkBoxCell, | 24 | checkBoxCell, |
25 | DateEntityTableColumn, | 25 | DateEntityTableColumn, |
26 | EntityTableColumn, | 26 | EntityTableColumn, |
27 | - EntityTableConfig | 27 | + EntityTableConfig, |
28 | + HeaderActionDescriptor | ||
28 | } from '@shared/components/entity/entities-table-config.models'; | 29 | } from '@shared/components/entity/entities-table-config.models'; |
29 | import { TenantService } from '@core/http/tenant.service'; | 30 | import { TenantService } from '@core/http/tenant.service'; |
30 | import { TranslateService } from '@ngx-translate/core'; | 31 | import { TranslateService } from '@ngx-translate/core'; |
@@ -37,7 +38,7 @@ import { | @@ -37,7 +38,7 @@ import { | ||
37 | import { TenantComponent } from '@modules/home/pages/tenant/tenant.component'; | 38 | import { TenantComponent } from '@modules/home/pages/tenant/tenant.component'; |
38 | import { EntityAction } from '@shared/components/entity/entity-component.models'; | 39 | import { EntityAction } from '@shared/components/entity/entity-component.models'; |
39 | import { User } from '@shared/models/user.model'; | 40 | import { User } from '@shared/models/user.model'; |
40 | -import {Device, DeviceInfo} from '@app/shared/models/device.models'; | 41 | +import {Device, DeviceCredentials, DeviceInfo} from '@app/shared/models/device.models'; |
41 | import {DeviceComponent} from '@modules/home/pages/device/device.component'; | 42 | import {DeviceComponent} from '@modules/home/pages/device/device.component'; |
42 | import {Observable, of} from 'rxjs'; | 43 | import {Observable, of} from 'rxjs'; |
43 | import {select, Store} from '@ngrx/store'; | 44 | import {select, Store} from '@ngrx/store'; |
@@ -51,6 +52,12 @@ import {Customer} from '@app/shared/models/customer.model'; | @@ -51,6 +52,12 @@ import {Customer} from '@app/shared/models/customer.model'; | ||
51 | import {NULL_UUID} from '@shared/models/id/has-uuid'; | 52 | import {NULL_UUID} from '@shared/models/id/has-uuid'; |
52 | import {BroadcastService} from '@core/services/broadcast.service'; | 53 | import {BroadcastService} from '@core/services/broadcast.service'; |
53 | import {DeviceTableHeaderComponent} from '@modules/home/pages/device/device-table-header.component'; | 54 | import {DeviceTableHeaderComponent} from '@modules/home/pages/device/device-table-header.component'; |
55 | +import {MatDialog} from '@angular/material'; | ||
56 | +import { | ||
57 | + DeviceCredentialsDialogComponent, | ||
58 | + DeviceCredentialsDialogData | ||
59 | +} from '@modules/home/pages/device/device-credentials-dialog.component'; | ||
60 | +import {DialogService} from '@core/services/dialog.service'; | ||
54 | 61 | ||
55 | @Injectable() | 62 | @Injectable() |
56 | export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<DeviceInfo>> { | 63 | export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<DeviceInfo>> { |
@@ -63,9 +70,11 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev | @@ -63,9 +70,11 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev | ||
63 | private broadcast: BroadcastService, | 70 | private broadcast: BroadcastService, |
64 | private deviceService: DeviceService, | 71 | private deviceService: DeviceService, |
65 | private customerService: CustomerService, | 72 | private customerService: CustomerService, |
73 | + private dialogService: DialogService, | ||
66 | private translate: TranslateService, | 74 | private translate: TranslateService, |
67 | private datePipe: DatePipe, | 75 | private datePipe: DatePipe, |
68 | - private router: Router) { | 76 | + private router: Router, |
77 | + private dialog: MatDialog) { | ||
69 | 78 | ||
70 | this.config.entityType = EntityType.CUSTOMER; | 79 | this.config.entityType = EntityType.CUSTOMER; |
71 | this.config.entityComponent = DeviceComponent; | 80 | this.config.entityComponent = DeviceComponent; |
@@ -122,6 +131,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev | @@ -122,6 +131,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev | ||
122 | this.config.columns = this.configureColumns(this.config.componentsData.deviceScope); | 131 | this.config.columns = this.configureColumns(this.config.componentsData.deviceScope); |
123 | this.configureEntityFunctions(this.config.componentsData.deviceScope); | 132 | this.configureEntityFunctions(this.config.componentsData.deviceScope); |
124 | this.config.cellActionDescriptors = this.configureCellActions(this.config.componentsData.deviceScope); | 133 | this.config.cellActionDescriptors = this.configureCellActions(this.config.componentsData.deviceScope); |
134 | + this.config.addActionDescriptors = this.configureAddActions(this.config.componentsData.deviceScope); | ||
125 | return this.config; | 135 | return this.config; |
126 | }) | 136 | }) |
127 | ); | 137 | ); |
@@ -154,16 +164,18 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev | @@ -154,16 +164,18 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev | ||
154 | 164 | ||
155 | configureEntityFunctions(deviceScope: string): void { | 165 | configureEntityFunctions(deviceScope: string): void { |
156 | if (deviceScope === 'tenant') { | 166 | if (deviceScope === 'tenant') { |
157 | - this.config.entitiesFetchFunction = pageLink => this.deviceService.getTenantDeviceInfos(pageLink, this.config.componentsData.deviceType); | 167 | + this.config.entitiesFetchFunction = pageLink => |
168 | + this.deviceService.getTenantDeviceInfos(pageLink, this.config.componentsData.deviceType); | ||
158 | this.config.deleteEntity = id => this.deviceService.deleteDevice(id.id); | 169 | this.config.deleteEntity = id => this.deviceService.deleteDevice(id.id); |
159 | } else { | 170 | } else { |
160 | - this.config.entitiesFetchFunction = pageLink => this.deviceService.getCustomerDeviceInfos(this.customerId, pageLink, this.config.componentsData.deviceType); | 171 | + this.config.entitiesFetchFunction = pageLink => |
172 | + this.deviceService.getCustomerDeviceInfos(this.customerId, pageLink, this.config.componentsData.deviceType); | ||
161 | this.config.deleteEntity = id => this.deviceService.unassignDeviceFromCustomer(id.id); | 173 | this.config.deleteEntity = id => this.deviceService.unassignDeviceFromCustomer(id.id); |
162 | } | 174 | } |
163 | } | 175 | } |
164 | 176 | ||
165 | - configureCellActions(deviceScope: string): Array<CellActionDescriptor<Device | DeviceInfo>> { | ||
166 | - const actions: Array<CellActionDescriptor<Device | DeviceInfo>> = []; | 177 | + configureCellActions(deviceScope: string): Array<CellActionDescriptor<DeviceInfo>> { |
178 | + const actions: Array<CellActionDescriptor<Device>> = []; | ||
167 | if (deviceScope === 'tenant') { | 179 | if (deviceScope === 'tenant') { |
168 | actions.push( | 180 | actions.push( |
169 | { | 181 | { |
@@ -177,18 +189,122 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev | @@ -177,18 +189,122 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev | ||
177 | return actions; | 189 | return actions; |
178 | } | 190 | } |
179 | 191 | ||
192 | + configureAddActions(deviceScope: string): Array<HeaderActionDescriptor> { | ||
193 | + const actions: Array<HeaderActionDescriptor> = []; | ||
194 | + actions.push( | ||
195 | + { | ||
196 | + name: this.translate.instant('device.add-device-text'), | ||
197 | + icon: 'insert_drive_file', | ||
198 | + isEnabled: () => true, | ||
199 | + onAction: ($event) => this.config.table.addEntity($event) | ||
200 | + }, | ||
201 | + { | ||
202 | + name: this.translate.instant('device.import'), | ||
203 | + icon: 'file_upload', | ||
204 | + isEnabled: () => true, | ||
205 | + onAction: ($event) => this.importDevices($event) | ||
206 | + } | ||
207 | + ); | ||
208 | + return actions; | ||
209 | + } | ||
210 | + | ||
211 | + importDevices($event: Event) { | ||
212 | + if ($event) { | ||
213 | + $event.stopPropagation(); | ||
214 | + } | ||
215 | + // TODO: | ||
216 | + } | ||
217 | + | ||
180 | makePublic($event: Event, device: Device) { | 218 | makePublic($event: Event, device: Device) { |
181 | if ($event) { | 219 | if ($event) { |
182 | $event.stopPropagation(); | 220 | $event.stopPropagation(); |
183 | } | 221 | } |
222 | + this.dialogService.confirm( | ||
223 | + this.translate.instant('device.make-public-device-title', {deviceName: device.name}), | ||
224 | + this.translate.instant('device.make-public-device-text'), | ||
225 | + this.translate.instant('action.no'), | ||
226 | + this.translate.instant('action.yes'), | ||
227 | + true | ||
228 | + ).subscribe((res) => { | ||
229 | + if (res) { | ||
230 | + this.deviceService.makeDevicePublic(device.id.id).subscribe( | ||
231 | + () => { | ||
232 | + this.config.table.updateData(); | ||
233 | + } | ||
234 | + ); | ||
235 | + } | ||
236 | + } | ||
237 | + ); | ||
238 | + } | ||
239 | + | ||
240 | + assignToCustomer($event: Event, device: Device) { | ||
241 | + if ($event) { | ||
242 | + $event.stopPropagation(); | ||
243 | + } | ||
184 | // TODO: | 244 | // TODO: |
185 | } | 245 | } |
186 | 246 | ||
187 | - onDeviceAction(action: EntityAction<Device | DeviceInfo>): boolean { | 247 | + unassignFromCustomer($event: Event, device: DeviceInfo) { |
248 | + if ($event) { | ||
249 | + $event.stopPropagation(); | ||
250 | + } | ||
251 | + const isPublic = device.customerIsPublic; | ||
252 | + let title; | ||
253 | + let content; | ||
254 | + if (isPublic) { | ||
255 | + title = this.translate.instant('device.make-private-device-title', {deviceName: device.name}); | ||
256 | + content = this.translate.instant('device.make-private-device-text'); | ||
257 | + } else { | ||
258 | + title = this.translate.instant('device.unassign-device-title', {deviceName: device.name}); | ||
259 | + content = this.translate.instant('device.unassign-device-text'); | ||
260 | + } | ||
261 | + this.dialogService.confirm( | ||
262 | + this.translate.instant(title), | ||
263 | + this.translate.instant(content), | ||
264 | + this.translate.instant('action.no'), | ||
265 | + this.translate.instant('action.yes'), | ||
266 | + true | ||
267 | + ).subscribe((res) => { | ||
268 | + if (res) { | ||
269 | + this.deviceService.unassignDeviceFromCustomer(device.id.id).subscribe( | ||
270 | + () => { | ||
271 | + this.config.table.updateData(); | ||
272 | + } | ||
273 | + ); | ||
274 | + } | ||
275 | + } | ||
276 | + ); | ||
277 | + } | ||
278 | + | ||
279 | + manageCredentials($event: Event, device: Device) { | ||
280 | + if ($event) { | ||
281 | + $event.stopPropagation(); | ||
282 | + } | ||
283 | + this.dialog.open<DeviceCredentialsDialogComponent, DeviceCredentialsDialogData, | ||
284 | + DeviceCredentials>(DeviceCredentialsDialogComponent, { | ||
285 | + disableClose: true, | ||
286 | + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], | ||
287 | + data: { | ||
288 | + deviceId: device.id.id, | ||
289 | + isReadOnly: this.config.componentsData.deviceScope === 'customer_user' | ||
290 | + } | ||
291 | + }); | ||
292 | + } | ||
293 | + | ||
294 | + onDeviceAction(action: EntityAction<DeviceInfo>): boolean { | ||
188 | switch (action.action) { | 295 | switch (action.action) { |
189 | case 'makePublic': | 296 | case 'makePublic': |
190 | this.makePublic(action.event, action.entity); | 297 | this.makePublic(action.event, action.entity); |
191 | return true; | 298 | return true; |
299 | + case 'assignToCustomer': | ||
300 | + this.assignToCustomer(action.event, action.entity); | ||
301 | + return true; | ||
302 | + case 'unassignFromCustomer': | ||
303 | + this.unassignFromCustomer(action.event, action.entity); | ||
304 | + return true; | ||
305 | + case 'manageCredentials': | ||
306 | + this.manageCredentials(action.event, action.entity); | ||
307 | + return true; | ||
192 | } | 308 | } |
193 | return false; | 309 | return false; |
194 | } | 310 | } |
@@ -14,26 +14,24 @@ | @@ -14,26 +14,24 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 16 | ||
17 | -import { Component, OnInit } from '@angular/core'; | ||
18 | -import { UserService } from '@core/http/user.service'; | ||
19 | -import { User } from '@shared/models/user.model'; | ||
20 | -import { Authority } from '@shared/models/authority.enum'; | ||
21 | -import { PageComponent } from '@shared/components/page.component'; | ||
22 | -import { select, Store } from '@ngrx/store'; | ||
23 | -import { AppState } from '@core/core.state'; | ||
24 | -import { getCurrentAuthUser, selectAuthUser } from '@core/auth/auth.selectors'; | ||
25 | -import { mergeMap, take } from 'rxjs/operators'; | ||
26 | -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; | ||
27 | -import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard'; | ||
28 | -import { ActionAuthUpdateUserDetails } from '@core/auth/auth.actions'; | ||
29 | -import { environment as env } from '@env/environment'; | ||
30 | -import { TranslateService } from '@ngx-translate/core'; | ||
31 | -import { ActionSettingsChangeLanguage } from '@core/settings/settings.actions'; | ||
32 | -import { ChangePasswordDialogComponent } from '@modules/home/pages/profile/change-password-dialog.component'; | ||
33 | -import { MatDialog } from '@angular/material'; | ||
34 | -import { DialogService } from '@core/services/dialog.service'; | ||
35 | -import { AuthService } from '@core/auth/auth.service'; | ||
36 | -import { ActivatedRoute } from '@angular/router'; | 17 | +import {Component, OnInit} from '@angular/core'; |
18 | +import {UserService} from '@core/http/user.service'; | ||
19 | +import {User} from '@shared/models/user.model'; | ||
20 | +import {Authority} from '@shared/models/authority.enum'; | ||
21 | +import {PageComponent} from '@shared/components/page.component'; | ||
22 | +import {Store} from '@ngrx/store'; | ||
23 | +import {AppState} from '@core/core.state'; | ||
24 | +import {FormBuilder, FormGroup, Validators} from '@angular/forms'; | ||
25 | +import {HasConfirmForm} from '@core/guards/confirm-on-exit.guard'; | ||
26 | +import {ActionAuthUpdateUserDetails} from '@core/auth/auth.actions'; | ||
27 | +import {environment as env} from '@env/environment'; | ||
28 | +import {TranslateService} from '@ngx-translate/core'; | ||
29 | +import {ActionSettingsChangeLanguage} from '@core/settings/settings.actions'; | ||
30 | +import {ChangePasswordDialogComponent} from '@modules/home/pages/profile/change-password-dialog.component'; | ||
31 | +import {MatDialog} from '@angular/material'; | ||
32 | +import {DialogService} from '@core/services/dialog.service'; | ||
33 | +import {AuthService} from '@core/auth/auth.service'; | ||
34 | +import {ActivatedRoute} from '@angular/router'; | ||
37 | 35 | ||
38 | @Component({ | 36 | @Component({ |
39 | selector: 'tb-profile', | 37 | selector: 'tb-profile', |
@@ -122,6 +122,7 @@ export class EntityTableConfig<T extends BaseData<HasId>, P extends PageLink = P | @@ -122,6 +122,7 @@ export class EntityTableConfig<T extends BaseData<HasId>, P extends PageLink = P | ||
122 | cellActionDescriptors: Array<CellActionDescriptor<T>> = []; | 122 | cellActionDescriptors: Array<CellActionDescriptor<T>> = []; |
123 | groupActionDescriptors: Array<GroupActionDescriptor<T>> = []; | 123 | groupActionDescriptors: Array<GroupActionDescriptor<T>> = []; |
124 | headerActionDescriptors: Array<HeaderActionDescriptor> = []; | 124 | headerActionDescriptors: Array<HeaderActionDescriptor> = []; |
125 | + addActionDescriptors: Array<HeaderActionDescriptor> = []; | ||
125 | headerComponent: Type<EntityTableHeaderComponent<T>>; | 126 | headerComponent: Type<EntityTableHeaderComponent<T>>; |
126 | addEntity: CreateEntityOperation<T> = null; | 127 | addEntity: CreateEntityOperation<T> = null; |
127 | detailsReadonly: EntityBooleanFunction<T> = () => false; | 128 | detailsReadonly: EntityBooleanFunction<T> = () => false; |
@@ -42,11 +42,32 @@ | @@ -42,11 +42,32 @@ | ||
42 | asButton historyOnly></tb-timewindow> | 42 | asButton historyOnly></tb-timewindow> |
43 | <tb-anchor #entityTableHeader></tb-anchor> | 43 | <tb-anchor #entityTableHeader></tb-anchor> |
44 | <span fxFlex *ngIf="!this.entitiesTableConfig.headerComponent"></span> | 44 | <span fxFlex *ngIf="!this.entitiesTableConfig.headerComponent"></span> |
45 | - <button mat-button mat-icon-button [disabled]="isLoading$ | async" [fxShow]="addEnabled()" (click)="addEntity($event)" | ||
46 | - matTooltip="{{ translations.add | translate }}" | ||
47 | - matTooltipPosition="above"> | ||
48 | - <mat-icon>add</mat-icon> | ||
49 | - </button> | 45 | + <div [fxShow]="addEnabled()"> |
46 | + <button mat-button mat-icon-button [disabled]="isLoading$ | async" | ||
47 | + *ngIf="!this.entitiesTableConfig.addActionDescriptors.length; else addActions" | ||
48 | + (click)="addEntity($event)" | ||
49 | + matTooltip="{{ translations.add | translate }}" | ||
50 | + matTooltipPosition="above"> | ||
51 | + <mat-icon>add</mat-icon> | ||
52 | + </button> | ||
53 | + <ng-template #addActions> | ||
54 | + <button mat-button mat-icon-button [disabled]="isLoading$ | async" | ||
55 | + matTooltip="{{ translations.add | translate }}" | ||
56 | + matTooltipPosition="above" | ||
57 | + [matMenuTriggerFor]="addActionsMenu"> | ||
58 | + <mat-icon>add</mat-icon> | ||
59 | + </button> | ||
60 | + <mat-menu #addActionsMenu="matMenu" xPosition="before"> | ||
61 | + <button mat-menu-item *ngFor="let actionDescriptor of this.entitiesTableConfig.addActionDescriptors" | ||
62 | + [disabled]="isLoading$ | async" | ||
63 | + [fxShow]="actionDescriptor.isEnabled()" | ||
64 | + (click)="actionDescriptor.onAction($event)"> | ||
65 | + <mat-icon>{{actionDescriptor.icon}}</mat-icon> | ||
66 | + <span>{{ actionDescriptor.name }}</span> | ||
67 | + </button> | ||
68 | + </mat-menu> | ||
69 | + </ng-template> | ||
70 | + </div> | ||
50 | <button mat-button mat-icon-button [disabled]="isLoading$ | async" | 71 | <button mat-button mat-icon-button [disabled]="isLoading$ | async" |
51 | [fxShow]="actionDescriptor.isEnabled()" *ngFor="let actionDescriptor of headerActionDescriptors" | 72 | [fxShow]="actionDescriptor.isEnabled()" *ngFor="let actionDescriptor of headerActionDescriptors" |
52 | matTooltip="{{ actionDescriptor.name }}" | 73 | matTooltip="{{ actionDescriptor.name }}" |
1 | +<!-- | ||
2 | + | ||
3 | + Copyright © 2016-2019 The Thingsboard Authors | ||
4 | + | ||
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + you may not use this file except in compliance with the License. | ||
7 | + You may obtain a copy of the License at | ||
8 | + | ||
9 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + | ||
11 | + Unless required by applicable law or agreed to in writing, software | ||
12 | + distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + See the License for the specific language governing permissions and | ||
15 | + limitations under the License. | ||
16 | + | ||
17 | +--> | ||
18 | +<mat-form-field [formGroup]="selectEntityFormGroup" class="mat-block"> | ||
19 | + <input matInput type="text" placeholder="{{ entityText | translate }}" | ||
20 | + #entityInput | ||
21 | + formControlName="entity" | ||
22 | + [required]="required" | ||
23 | + [matAutocomplete]="entityAutocomplete"> | ||
24 | + <button *ngIf="selectEntityFormGroup.get('entity').value && !disabled" | ||
25 | + type="button" | ||
26 | + matSuffix mat-button mat-icon-button aria-label="Clear" | ||
27 | + (click)="clear()"> | ||
28 | + <mat-icon class="material-icons">close</mat-icon> | ||
29 | + </button> | ||
30 | + <mat-autocomplete #entityAutocomplete="matAutocomplete" [displayWith]="displayEntityFn"> | ||
31 | + <mat-option *ngFor="let entity of filteredEntities | async" [value]="entity"> | ||
32 | + <span [innerHTML]="entity.name | highlight:searchText"></span> | ||
33 | + </mat-option> | ||
34 | + <mat-option *ngIf="!(filteredEntities | async)?.length" [value]="null"> | ||
35 | + <span> | ||
36 | + {{ translate.get(noEntitiesMatchingText, {entity: searchText}) | async }} | ||
37 | + </span> | ||
38 | + </mat-option> | ||
39 | + </mat-autocomplete> | ||
40 | + <mat-error *ngIf="selectEntityFormGroup.get('entity').hasError('required')"> | ||
41 | + {{ entityRequiredText | translate }} | ||
42 | + </mat-error> | ||
43 | +</mat-form-field> |
1 | +/// | ||
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import {AfterViewInit, Component, ElementRef, forwardRef, Input, OnInit, ViewChild} from '@angular/core'; | ||
18 | +import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR} from '@angular/forms'; | ||
19 | +import {Observable} from 'rxjs'; | ||
20 | +import {map, mergeMap, startWith, tap} from 'rxjs/operators'; | ||
21 | +import {Store} from '@ngrx/store'; | ||
22 | +import {AppState} from '@app/core/core.state'; | ||
23 | +import {TranslateService} from '@ngx-translate/core'; | ||
24 | +import {AliasEntityType, EntityType} from '@shared/models/entity-type.models'; | ||
25 | +import {BaseData} from '@shared/models/base-data'; | ||
26 | +import {EntityId} from '@shared/models/id/entity-id'; | ||
27 | +import {EntityService} from '@core/http/entity.service'; | ||
28 | + | ||
29 | +@Component({ | ||
30 | + selector: 'tb-entity-autocomplete', | ||
31 | + templateUrl: './entity-autocomplete.component.html', | ||
32 | + styleUrls: [], | ||
33 | + providers: [{ | ||
34 | + provide: NG_VALUE_ACCESSOR, | ||
35 | + useExisting: forwardRef(() => EntityAutocompleteComponent), | ||
36 | + multi: true | ||
37 | + }] | ||
38 | +}) | ||
39 | +export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit, AfterViewInit { | ||
40 | + | ||
41 | + selectEntityFormGroup: FormGroup; | ||
42 | + | ||
43 | + modelValue: string | null; | ||
44 | + | ||
45 | + entityTypeValue: EntityType | AliasEntityType; | ||
46 | + | ||
47 | + entitySubtypeValue: string; | ||
48 | + | ||
49 | + @Input() | ||
50 | + set entityType(entityType: EntityType) { | ||
51 | + if (this.entityTypeValue !== entityType) { | ||
52 | + this.entityTypeValue = entityType; | ||
53 | + this.load(); | ||
54 | + } | ||
55 | + } | ||
56 | + | ||
57 | + @Input() | ||
58 | + set entitySubtype(entitySubtype: string) { | ||
59 | + if (this.entitySubtypeValue !== entitySubtype) { | ||
60 | + this.entitySubtypeValue = entitySubtype; | ||
61 | + const currentEntity = this.getCurrentEntity(); | ||
62 | + if (currentEntity) { | ||
63 | + if ((currentEntity as any).type !== this.entitySubtypeValue) { | ||
64 | + this.reset(); | ||
65 | + } | ||
66 | + } | ||
67 | + } | ||
68 | + } | ||
69 | + | ||
70 | + @Input() | ||
71 | + excludeEntityIds: Array<string>; | ||
72 | + | ||
73 | + @Input() | ||
74 | + required: boolean; | ||
75 | + | ||
76 | + @Input() | ||
77 | + disabled: boolean; | ||
78 | + | ||
79 | + @ViewChild('entityInput', {static: true}) entityInput: ElementRef; | ||
80 | + | ||
81 | + entityText: string; | ||
82 | + noEntitiesMatchingText: string; | ||
83 | + entityRequiredText: string; | ||
84 | + | ||
85 | + filteredEntities: Observable<Array<BaseData<EntityId>>>; | ||
86 | + | ||
87 | + private searchText = ''; | ||
88 | + | ||
89 | + private propagateChange = (v: any) => { }; | ||
90 | + | ||
91 | + constructor(private store: Store<AppState>, | ||
92 | + public translate: TranslateService, | ||
93 | + private entityService: EntityService, | ||
94 | + private fb: FormBuilder) { | ||
95 | + this.selectEntityFormGroup = this.fb.group({ | ||
96 | + entity: [null] | ||
97 | + }); | ||
98 | + } | ||
99 | + | ||
100 | + registerOnChange(fn: any): void { | ||
101 | + this.propagateChange = fn; | ||
102 | + } | ||
103 | + | ||
104 | + registerOnTouched(fn: any): void { | ||
105 | + } | ||
106 | + | ||
107 | + ngOnInit() { | ||
108 | + this.filteredEntities = this.selectEntityFormGroup.get('dashboard').valueChanges | ||
109 | + .pipe( | ||
110 | + tap(value => { | ||
111 | + let modelValue; | ||
112 | + if (typeof value === 'string' || !value) { | ||
113 | + modelValue = null; | ||
114 | + } else { | ||
115 | + modelValue = value.id.id; | ||
116 | + } | ||
117 | + this.updateView(modelValue); | ||
118 | + }), | ||
119 | + startWith<string | BaseData<EntityId>>(''), | ||
120 | + map(value => value ? (typeof value === 'string' ? value : value.name) : ''), | ||
121 | + mergeMap(name => this.fetchEntities(name) ) | ||
122 | + ); | ||
123 | + } | ||
124 | + | ||
125 | + ngAfterViewInit(): void {} | ||
126 | + | ||
127 | + load(): void { | ||
128 | + if (this.entityTypeValue) { | ||
129 | + switch (this.entityTypeValue) { | ||
130 | + case EntityType.ASSET: | ||
131 | + this.entityText = 'asset.asset'; | ||
132 | + this.noEntitiesMatchingText = 'asset.no-assets-matching'; | ||
133 | + this.entityRequiredText = 'asset.asset-required'; | ||
134 | + break; | ||
135 | + case EntityType.DEVICE: | ||
136 | + this.entityText = 'device.device'; | ||
137 | + this.noEntitiesMatchingText = 'device.no-devices-matching'; | ||
138 | + this.entityRequiredText = 'device.device-required'; | ||
139 | + break; | ||
140 | + case EntityType.ENTITY_VIEW: | ||
141 | + this.entityText = 'entity-view.entity-view'; | ||
142 | + this.noEntitiesMatchingText = 'entity-view.no-entity-views-matching'; | ||
143 | + this.entityRequiredText = 'entity-view.entity-view-required'; | ||
144 | + break; | ||
145 | + case EntityType.RULE_CHAIN: | ||
146 | + this.entityText = 'rulechain.rulechain'; | ||
147 | + this.noEntitiesMatchingText = 'rulechain.no-rulechains-matching'; | ||
148 | + this.entityRequiredText = 'rulechain.rulechain-required'; | ||
149 | + break; | ||
150 | + case EntityType.TENANT: | ||
151 | + this.entityText = 'tenant.tenant'; | ||
152 | + this.noEntitiesMatchingText = 'tenant.no-tenants-matching'; | ||
153 | + this.entityRequiredText = 'tenant.tenant-required'; | ||
154 | + break; | ||
155 | + case EntityType.CUSTOMER: | ||
156 | + this.entityText = 'customer.customer'; | ||
157 | + this.noEntitiesMatchingText = 'customer.no-customers-matching'; | ||
158 | + this.entityRequiredText = 'customer.customer-required'; | ||
159 | + break; | ||
160 | + case EntityType.USER: | ||
161 | + this.entityText = 'user.user'; | ||
162 | + this.noEntitiesMatchingText = 'user.no-users-matching'; | ||
163 | + this.entityRequiredText = 'user.user-required'; | ||
164 | + break; | ||
165 | + case EntityType.DASHBOARD: | ||
166 | + this.entityText = 'dashboard.dashboard'; | ||
167 | + this.noEntitiesMatchingText = 'dashboard.no-dashboards-matching'; | ||
168 | + this.entityRequiredText = 'dashboard.dashboard-required'; | ||
169 | + break; | ||
170 | + case EntityType.ALARM: | ||
171 | + this.entityText = 'alarm.alarm'; | ||
172 | + this.noEntitiesMatchingText = 'alarm.no-alarms-matching'; | ||
173 | + this.entityRequiredText = 'alarm.alarm-required'; | ||
174 | + break; | ||
175 | + case AliasEntityType.CURRENT_CUSTOMER: | ||
176 | + this.entityText = 'customer.default-customer'; | ||
177 | + this.noEntitiesMatchingText = 'customer.no-customers-matching'; | ||
178 | + this.entityRequiredText = 'customer.default-customer-required'; | ||
179 | + break; | ||
180 | + } | ||
181 | + } | ||
182 | + const currentEntity = this.getCurrentEntity(); | ||
183 | + if (currentEntity) { | ||
184 | + const currentEntityType = currentEntity.id.entityType; | ||
185 | + if (this.entityTypeValue && currentEntityType !== this.entityTypeValue) { | ||
186 | + this.reset(); | ||
187 | + } | ||
188 | + } | ||
189 | + } | ||
190 | + | ||
191 | + getCurrentEntity(): BaseData<EntityId> | null { | ||
192 | + const currentEntity = this.selectEntityFormGroup.get('entity').value; | ||
193 | + if (currentEntity && typeof currentEntity !== 'string') { | ||
194 | + return currentEntity as BaseData<EntityId>; | ||
195 | + } else { | ||
196 | + return null; | ||
197 | + } | ||
198 | + } | ||
199 | + | ||
200 | + setDisabledState(isDisabled: boolean): void { | ||
201 | + this.disabled = isDisabled; | ||
202 | + } | ||
203 | + | ||
204 | + writeValue(value: string | null): void { | ||
205 | + this.searchText = ''; | ||
206 | + if (value != null) { | ||
207 | + let targetEntityType = this.entityTypeValue; | ||
208 | + if (targetEntityType === AliasEntityType.CURRENT_CUSTOMER) { | ||
209 | + targetEntityType = EntityType.CUSTOMER; | ||
210 | + } | ||
211 | + this.entityService.getEntity(targetEntityType, value).subscribe( | ||
212 | + (entity) => { | ||
213 | + this.modelValue = entity.id.id; | ||
214 | + this.selectEntityFormGroup.get('entity').patchValue(entity, {emitEvent: true}); | ||
215 | + } | ||
216 | + ); | ||
217 | + } else { | ||
218 | + this.modelValue = null; | ||
219 | + this.selectEntityFormGroup.get('entity').patchValue(null, {emitEvent: true}); | ||
220 | + } | ||
221 | + } | ||
222 | + | ||
223 | + reset() { | ||
224 | + this.selectEntityFormGroup.get('entity').patchValue(null, {emitEvent: true}); | ||
225 | + } | ||
226 | + | ||
227 | + updateView(value: string | null) { | ||
228 | + if (this.modelValue !== value) { | ||
229 | + this.modelValue = value; | ||
230 | + this.propagateChange(this.modelValue); | ||
231 | + } | ||
232 | + } | ||
233 | + | ||
234 | + displayEntityFn(entity?: BaseData<EntityId>): string | undefined { | ||
235 | + return entity ? entity.name : undefined; | ||
236 | + } | ||
237 | + | ||
238 | + fetchEntities(searchText?: string): Observable<Array<BaseData<EntityId>>> { | ||
239 | + this.searchText = searchText; | ||
240 | + let targetEntityType = this.entityTypeValue; | ||
241 | + if (targetEntityType === AliasEntityType.CURRENT_CUSTOMER) { | ||
242 | + targetEntityType = EntityType.CUSTOMER; | ||
243 | + } | ||
244 | + return this.entityService.getEntitiesByNameFilter(targetEntityType, searchText, | ||
245 | + 50, this.entitySubtypeValue, false, true).pipe( | ||
246 | + map((data) => { | ||
247 | + if (data) { | ||
248 | + if (this.excludeEntityIds && this.excludeEntityIds.length) { | ||
249 | + const entities: Array<BaseData<EntityId>> = []; | ||
250 | + data.forEach((entity) => { | ||
251 | + if (this.excludeEntityIds.indexOf(entity.id.id) === -1) { | ||
252 | + entities.push(entity); | ||
253 | + } | ||
254 | + }); | ||
255 | + return entities; | ||
256 | + } else { | ||
257 | + return data; | ||
258 | + } | ||
259 | + } else { | ||
260 | + return []; | ||
261 | + } | ||
262 | + } | ||
263 | + )); | ||
264 | + } | ||
265 | + | ||
266 | + clear() { | ||
267 | + this.selectEntityFormGroup.get('entity').patchValue(null, {emitEvent: true}); | ||
268 | + setTimeout(() => { | ||
269 | + this.entityInput.nativeElement.blur(); | ||
270 | + this.entityInput.nativeElement.focus(); | ||
271 | + }, 0); | ||
272 | + } | ||
273 | + | ||
274 | +} |
ui-ngx/src/app/shared/models/alarm.models.ts
0 → 100644
1 | +/// | ||
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import {BaseData} from '@shared/models/base-data'; | ||
18 | +import {AssetId} from '@shared/models/id/asset-id'; | ||
19 | +import {TenantId} from '@shared/models/id/tenant-id'; | ||
20 | +import {CustomerId} from '@shared/models/id/customer-id'; | ||
21 | +import {AlarmId} from '@shared/models/id/alarm-id'; | ||
22 | +import {EntityId} from '@shared/models/id/entity-id'; | ||
23 | + | ||
24 | +export enum AlarmSeverity { | ||
25 | + CRITICAL = 'CRITICAL', | ||
26 | + MAJOR = 'MAJOR', | ||
27 | + MINOR = 'MINOR', | ||
28 | + WARNING = 'WARNING', | ||
29 | + INDETERMINATE = 'INDETERMINATE' | ||
30 | +} | ||
31 | + | ||
32 | +export enum AlarmStatus { | ||
33 | + ACTIVE_UNACK = 'ACTIVE_UNACK', | ||
34 | + ACTIVE_ACK = 'ACTIVE_ACK', | ||
35 | + CLEARED_UNACK = 'CLEARED_UNACK', | ||
36 | + CLEARED_ACK = 'CLEARED_ACK' | ||
37 | +} | ||
38 | + | ||
39 | +export interface Alarm extends BaseData<AlarmId> { | ||
40 | + tenantId: TenantId; | ||
41 | + type: string; | ||
42 | + originator: EntityId; | ||
43 | + severity: AlarmSeverity; | ||
44 | + status: AlarmStatus; | ||
45 | + startTs: number; | ||
46 | + endTs: number; | ||
47 | + ackTs: number; | ||
48 | + clearTs: number; | ||
49 | + propagate: boolean; | ||
50 | + details?: any; | ||
51 | +} |
ui-ngx/src/app/shared/models/asset.models.ts
0 → 100644
1 | +/// | ||
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import {BaseData} from '@shared/models/base-data'; | ||
18 | +import {AssetId} from './id/asset-id'; | ||
19 | +import {TenantId} from '@shared/models/id/tenant-id'; | ||
20 | +import {CustomerId} from '@shared/models/id/customer-id'; | ||
21 | +import {DeviceCredentialsId} from '@shared/models/id/device-credentials-id'; | ||
22 | + | ||
23 | +export interface Asset extends BaseData<AssetId> { | ||
24 | + tenantId: TenantId; | ||
25 | + customerId: CustomerId; | ||
26 | + name: string; | ||
27 | + type: string; | ||
28 | + additionalInfo?: any; | ||
29 | +} | ||
30 | + | ||
31 | +export interface AssetInfo extends Asset { | ||
32 | + customerTitle: string; | ||
33 | + customerIsPublic: boolean; | ||
34 | +} |
@@ -18,6 +18,7 @@ import {BaseData} from '@shared/models/base-data'; | @@ -18,6 +18,7 @@ import {BaseData} from '@shared/models/base-data'; | ||
18 | import {DeviceId} from './id/device-id'; | 18 | import {DeviceId} from './id/device-id'; |
19 | import {TenantId} from '@shared/models/id/tenant-id'; | 19 | import {TenantId} from '@shared/models/id/tenant-id'; |
20 | import {CustomerId} from '@shared/models/id/customer-id'; | 20 | import {CustomerId} from '@shared/models/id/customer-id'; |
21 | +import {DeviceCredentialsId} from '@shared/models/id/device-credentials-id'; | ||
21 | 22 | ||
22 | export interface Device extends BaseData<DeviceId> { | 23 | export interface Device extends BaseData<DeviceId> { |
23 | tenantId: TenantId; | 24 | tenantId: TenantId; |
@@ -32,3 +33,22 @@ export interface DeviceInfo extends Device { | @@ -32,3 +33,22 @@ export interface DeviceInfo extends Device { | ||
32 | customerTitle: string; | 33 | customerTitle: string; |
33 | customerIsPublic: boolean; | 34 | customerIsPublic: boolean; |
34 | } | 35 | } |
36 | + | ||
37 | +export enum DeviceCredentialsType { | ||
38 | + ACCESS_TOKEN = 'ACCESS_TOKEN', | ||
39 | + X509_CERTIFICATE = 'X509_CERTIFICATE' | ||
40 | +} | ||
41 | + | ||
42 | +export const credentialTypeNames = new Map<DeviceCredentialsType, string>( | ||
43 | + [ | ||
44 | + [DeviceCredentialsType.ACCESS_TOKEN, 'Access token'], | ||
45 | + [DeviceCredentialsType.X509_CERTIFICATE, 'X.509 Certificate'], | ||
46 | + ] | ||
47 | +); | ||
48 | + | ||
49 | +export interface DeviceCredentials extends BaseData<DeviceCredentialsId> { | ||
50 | + deviceId: DeviceId; | ||
51 | + credentialsType: DeviceCredentialsType; | ||
52 | + credentialsId: string; | ||
53 | + credentialsValue: string; | ||
54 | +} |
@@ -31,6 +31,10 @@ export enum EntityType { | @@ -31,6 +31,10 @@ export enum EntityType { | ||
31 | WIDGET_TYPE = 'WIDGET_TYPE' | 31 | WIDGET_TYPE = 'WIDGET_TYPE' |
32 | } | 32 | } |
33 | 33 | ||
34 | +export enum AliasEntityType { | ||
35 | + CURRENT_CUSTOMER = 'CURRENT_CUSTOMER' | ||
36 | +} | ||
37 | + | ||
34 | export interface EntityTypeTranslation { | 38 | export interface EntityTypeTranslation { |
35 | type: string; | 39 | type: string; |
36 | typePlural: string; | 40 | typePlural: string; |
1 | +/// | ||
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import {BaseData} from '@shared/models/base-data'; | ||
18 | +import {AssetId} from './id/asset-id'; | ||
19 | +import {TenantId} from '@shared/models/id/tenant-id'; | ||
20 | +import {CustomerId} from '@shared/models/id/customer-id'; | ||
21 | +import {EntityViewId} from '@shared/models/id/entity-view-id'; | ||
22 | +import {EntityId} from '@shared/models/id/entity-id'; | ||
23 | + | ||
24 | +export interface AttributesEntityView { | ||
25 | + cs: Array<string>; | ||
26 | + ss: Array<string>; | ||
27 | + sh: Array<string>; | ||
28 | +} | ||
29 | + | ||
30 | +export interface TelemetryEntityView { | ||
31 | + timeseries: Array<string>; | ||
32 | + attributes: AttributesEntityView; | ||
33 | +} | ||
34 | + | ||
35 | +export interface EntityView extends BaseData<EntityViewId> { | ||
36 | + tenantId: TenantId; | ||
37 | + customerId: CustomerId; | ||
38 | + entityId: EntityId; | ||
39 | + name: string; | ||
40 | + type: string; | ||
41 | + keys: TelemetryEntityView; | ||
42 | + startTimeMs: number; | ||
43 | + endTimeMs: number; | ||
44 | + additionalInfo?: any; | ||
45 | +} | ||
46 | + | ||
47 | +export interface EntityViewInfo extends EntityView { | ||
48 | + customerTitle: string; | ||
49 | + customerIsPublic: boolean; | ||
50 | +} |
ui-ngx/src/app/shared/models/id/alarm-id.ts
0 → 100644
1 | +/// | ||
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import { EntityId } from './entity-id'; | ||
18 | +import { EntityType } from '@shared/models/entity-type.models'; | ||
19 | + | ||
20 | +export class AlarmId implements EntityId { | ||
21 | + entityType = EntityType.ALARM; | ||
22 | + id: string; | ||
23 | + constructor(id: string) { | ||
24 | + this.id = id; | ||
25 | + } | ||
26 | +} |
ui-ngx/src/app/shared/models/id/asset-id.ts
0 → 100644
1 | +/// | ||
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import { EntityId } from './entity-id'; | ||
18 | +import { EntityType } from '@shared/models/entity-type.models'; | ||
19 | + | ||
20 | +export class AssetId implements EntityId { | ||
21 | + entityType = EntityType.ASSET; | ||
22 | + id: string; | ||
23 | + constructor(id: string) { | ||
24 | + this.id = id; | ||
25 | + } | ||
26 | +} |
1 | +/// | ||
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import { HasUUID } from '@shared/models/id/has-uuid'; | ||
18 | + | ||
19 | +export class DeviceCredentialsId implements HasUUID { | ||
20 | + id: string; | ||
21 | + constructor(id: string) { | ||
22 | + this.id = id; | ||
23 | + } | ||
24 | +} |
1 | +/// | ||
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import { EntityId } from './entity-id'; | ||
18 | +import { EntityType } from '@shared/models/entity-type.models'; | ||
19 | + | ||
20 | +export class EntityViewId implements EntityId { | ||
21 | + entityType = EntityType.ENTITY_VIEW; | ||
22 | + id: string; | ||
23 | + constructor(id: string) { | ||
24 | + this.id = id; | ||
25 | + } | ||
26 | +} |
1 | +/// | ||
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import { EntityId } from './entity-id'; | ||
18 | +import { EntityType } from '@shared/models/entity-type.models'; | ||
19 | + | ||
20 | +export class RuleChainId implements EntityId { | ||
21 | + entityType = EntityType.RULE_CHAIN; | ||
22 | + id: string; | ||
23 | + constructor(id: string) { | ||
24 | + this.id = id; | ||
25 | + } | ||
26 | +} |
1 | +/// | ||
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import { EntityId } from './entity-id'; | ||
18 | +import { EntityType } from '@shared/models/entity-type.models'; | ||
19 | + | ||
20 | +export class RuleNodeId implements EntityId { | ||
21 | + entityType = EntityType.RULE_NODE; | ||
22 | + id: string; | ||
23 | + constructor(id: string) { | ||
24 | + this.id = id; | ||
25 | + } | ||
26 | +} |
1 | +/// | ||
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import { EntityId } from './entity-id'; | ||
18 | +import { EntityType } from '@shared/models/entity-type.models'; | ||
19 | + | ||
20 | +export class WidgetTypeId implements EntityId { | ||
21 | + entityType = EntityType.WIDGET_TYPE; | ||
22 | + id: string; | ||
23 | + constructor(id: string) { | ||
24 | + this.id = id; | ||
25 | + } | ||
26 | +} |
1 | +/// | ||
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import { EntityId } from './entity-id'; | ||
18 | +import { EntityType } from '@shared/models/entity-type.models'; | ||
19 | + | ||
20 | +export class WidgetsBundleId implements EntityId { | ||
21 | + entityType = EntityType.WIDGETS_BUNDLE; | ||
22 | + id: string; | ||
23 | + constructor(id: string) { | ||
24 | + this.id = id; | ||
25 | + } | ||
26 | +} |
1 | +/// | ||
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import {BaseData} from '@shared/models/base-data'; | ||
18 | +import {AssetId} from '@shared/models/id/asset-id'; | ||
19 | +import {TenantId} from '@shared/models/id/tenant-id'; | ||
20 | +import {CustomerId} from '@shared/models/id/customer-id'; | ||
21 | +import {RuleChainId} from '@shared/models/id/rule-chain-id'; | ||
22 | +import {RuleNodeId} from '@shared/models/id/rule-node-id'; | ||
23 | + | ||
24 | +export interface RuleChainConfiguration { | ||
25 | + todo: Array<any>; | ||
26 | + // TODO: | ||
27 | +} | ||
28 | + | ||
29 | +export interface RuleChain extends BaseData<RuleChainId> { | ||
30 | + tenantId: TenantId; | ||
31 | + name: string; | ||
32 | + firstRuleNodeId: RuleNodeId; | ||
33 | + root: boolean; | ||
34 | + debugMode: boolean; | ||
35 | + configuration: RuleChainConfiguration; | ||
36 | + additionalInfo?: any; | ||
37 | +} |
1 | +/// | ||
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import {BaseData} from '@shared/models/base-data'; | ||
18 | +import {AssetId} from '@shared/models/id/asset-id'; | ||
19 | +import {TenantId} from '@shared/models/id/tenant-id'; | ||
20 | +import {CustomerId} from '@shared/models/id/customer-id'; | ||
21 | +import {RuleChainId} from '@shared/models/id/rule-chain-id'; | ||
22 | +import {RuleNodeId} from '@shared/models/id/rule-node-id'; | ||
23 | + | ||
24 | +export interface RuleNodeConfiguration { | ||
25 | + todo: Array<any>; | ||
26 | + // TODO: | ||
27 | +} | ||
28 | + | ||
29 | +export interface RuleNode extends BaseData<RuleNodeId> { | ||
30 | + ruleChainId: RuleChainId; | ||
31 | + type: string; | ||
32 | + name: string; | ||
33 | + debugMode: boolean; | ||
34 | + configuration: RuleNodeConfiguration; | ||
35 | + additionalInfo?: any; | ||
36 | +} |
1 | +/// | ||
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import {BaseData} from '@shared/models/base-data'; | ||
18 | +import {TenantId} from '@shared/models/id/tenant-id'; | ||
19 | +import {WidgetsBundleId} from '@shared/models/id/widgets-bundle-id'; | ||
20 | +import {WidgetTypeId} from '@shared/models/id/widget-type-id'; | ||
21 | + | ||
22 | +export interface WidgetTypeDescriptor { | ||
23 | + todo: Array<any>; | ||
24 | + // TODO: | ||
25 | +} | ||
26 | + | ||
27 | +export interface WidgetType extends BaseData<WidgetTypeId> { | ||
28 | + tenantId: TenantId; | ||
29 | + bundleAlias: string; | ||
30 | + alias: string; | ||
31 | + name: string; | ||
32 | + descriptor: WidgetTypeDescriptor; | ||
33 | +} |
1 | +/// | ||
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import {BaseData} from '@shared/models/base-data'; | ||
18 | +import {TenantId} from '@shared/models/id/tenant-id'; | ||
19 | +import {WidgetsBundleId} from '@shared/models/id/widgets-bundle-id'; | ||
20 | + | ||
21 | +export interface WidgetsBundle extends BaseData<WidgetsBundleId> { | ||
22 | + tenantId: TenantId; | ||
23 | + alias: string; | ||
24 | + title: string; | ||
25 | + image: string; | ||
26 | +} |
@@ -80,6 +80,7 @@ import { HighlightPipe } from '@shared/pipe/highlight.pipe'; | @@ -80,6 +80,7 @@ import { HighlightPipe } from '@shared/pipe/highlight.pipe'; | ||
80 | import {DashboardAutocompleteComponent} from '@shared/components/dashboard-autocomplete.component'; | 80 | import {DashboardAutocompleteComponent} from '@shared/components/dashboard-autocomplete.component'; |
81 | import {EntitySubTypeAutocompleteComponent} from '@shared/components/entity/entity-subtype-autocomplete.component'; | 81 | import {EntitySubTypeAutocompleteComponent} from '@shared/components/entity/entity-subtype-autocomplete.component'; |
82 | import {EntitySubTypeSelectComponent} from './components/entity/entity-subtype-select.component'; | 82 | import {EntitySubTypeSelectComponent} from './components/entity/entity-subtype-select.component'; |
83 | +import {EntityAutocompleteComponent} from './components/entity/entity-autocomplete.component'; | ||
83 | 84 | ||
84 | @NgModule({ | 85 | @NgModule({ |
85 | providers: [ | 86 | providers: [ |
@@ -122,6 +123,7 @@ import {EntitySubTypeSelectComponent} from './components/entity/entity-subtype-s | @@ -122,6 +123,7 @@ import {EntitySubTypeSelectComponent} from './components/entity/entity-subtype-s | ||
122 | DashboardAutocompleteComponent, | 123 | DashboardAutocompleteComponent, |
123 | EntitySubTypeAutocompleteComponent, | 124 | EntitySubTypeAutocompleteComponent, |
124 | EntitySubTypeSelectComponent, | 125 | EntitySubTypeSelectComponent, |
126 | + EntityAutocompleteComponent, | ||
125 | NospacePipe, | 127 | NospacePipe, |
126 | MillisecondsToTimeStringPipe, | 128 | MillisecondsToTimeStringPipe, |
127 | EnumToArrayPipe, | 129 | EnumToArrayPipe, |
@@ -189,6 +191,7 @@ import {EntitySubTypeSelectComponent} from './components/entity/entity-subtype-s | @@ -189,6 +191,7 @@ import {EntitySubTypeSelectComponent} from './components/entity/entity-subtype-s | ||
189 | DashboardAutocompleteComponent, | 191 | DashboardAutocompleteComponent, |
190 | EntitySubTypeAutocompleteComponent, | 192 | EntitySubTypeAutocompleteComponent, |
191 | EntitySubTypeSelectComponent, | 193 | EntitySubTypeSelectComponent, |
194 | + EntityAutocompleteComponent, | ||
192 | // ValueInputComponent, | 195 | // ValueInputComponent, |
193 | MatButtonModule, | 196 | MatButtonModule, |
194 | MatCheckboxModule, | 197 | MatCheckboxModule, |