Commit 7a34e2a3f53f3e6bba87427e379ed66132bc7360
Merge branch 'master' of github.com:thingsboard/thingsboard
Showing
63 changed files
with
427 additions
and
306 deletions
... | ... | @@ -16,6 +16,7 @@ |
16 | 16 | package org.thingsboard.server.controller; |
17 | 17 | |
18 | 18 | import lombok.extern.slf4j.Slf4j; |
19 | +import org.springframework.beans.factory.annotation.Autowired; | |
19 | 20 | import org.springframework.http.HttpStatus; |
20 | 21 | import org.springframework.security.access.prepost.PreAuthorize; |
21 | 22 | import org.springframework.web.bind.annotation.*; |
... | ... | @@ -23,6 +24,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; |
23 | 24 | import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; |
24 | 25 | import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; |
25 | 26 | import org.thingsboard.server.common.data.oauth2.SchemeType; |
27 | +import org.thingsboard.server.dao.oauth2.OAuth2Configuration; | |
26 | 28 | import org.thingsboard.server.queue.util.TbCoreComponent; |
27 | 29 | import org.thingsboard.server.service.security.permission.Operation; |
28 | 30 | import org.thingsboard.server.service.security.permission.Resource; |
... | ... | @@ -36,6 +38,10 @@ import java.util.List; |
36 | 38 | @RequestMapping("/api") |
37 | 39 | @Slf4j |
38 | 40 | public class OAuth2Controller extends BaseController { |
41 | + | |
42 | + @Autowired | |
43 | + private OAuth2Configuration oAuth2Configuration; | |
44 | + | |
39 | 45 | @RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST) |
40 | 46 | @ResponseBody |
41 | 47 | public List<OAuth2ClientInfo> getOAuth2Clients(HttpServletRequest request) throws ThingsboardException { |
... | ... | @@ -70,4 +76,16 @@ public class OAuth2Controller extends BaseController { |
70 | 76 | throw handleException(e); |
71 | 77 | } |
72 | 78 | } |
79 | + | |
80 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") | |
81 | + @RequestMapping(value = "/oauth2/loginProcessingUrl", method = RequestMethod.GET) | |
82 | + @ResponseBody | |
83 | + public String getLoginProcessingUrl() throws ThingsboardException { | |
84 | + try { | |
85 | + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ); | |
86 | + return "\"" + oAuth2Configuration.getLoginProcessingUrl() + "\""; | |
87 | + } catch (Exception e) { | |
88 | + throw handleException(e); | |
89 | + } | |
90 | + } | |
73 | 91 | } | ... | ... |
... | ... | @@ -27,6 +27,6 @@ import java.util.Set; |
27 | 27 | @NoArgsConstructor |
28 | 28 | @AllArgsConstructor |
29 | 29 | public class OAuth2ClientsDomainParams { |
30 | - private Set<DomainInfo> domainInfos; | |
31 | - private Set<ClientRegistrationDto> clientRegistrations; | |
32 | -} | |
\ No newline at end of file | ||
30 | + private List<DomainInfo> domainInfos; | |
31 | + private List<ClientRegistrationDto> clientRegistrations; | |
32 | +} | ... | ... |
... | ... | @@ -16,6 +16,8 @@ |
16 | 16 | package org.thingsboard.server.common.data.oauth2; |
17 | 17 | |
18 | 18 | import lombok.*; |
19 | + | |
20 | +import java.util.List; | |
19 | 21 | import java.util.Set; |
20 | 22 | |
21 | 23 | @EqualsAndHashCode |
... | ... | @@ -26,5 +28,5 @@ import java.util.Set; |
26 | 28 | @AllArgsConstructor |
27 | 29 | public class OAuth2ClientsParams { |
28 | 30 | private boolean enabled; |
29 | - private Set<OAuth2ClientsDomainParams> domainsParams; | |
30 | -} | |
\ No newline at end of file | ||
31 | + private List<OAuth2ClientsDomainParams> domainsParams; | |
32 | +} | ... | ... |
... | ... | @@ -32,19 +32,19 @@ public class OAuth2Utils { |
32 | 32 | } |
33 | 33 | |
34 | 34 | public static OAuth2ClientsParams toOAuth2Params(List<ExtendedOAuth2ClientRegistrationInfo> extendedOAuth2ClientRegistrationInfos) { |
35 | - Map<OAuth2ClientRegistrationInfoId, Set<DomainInfo>> domainsByInfoId = new HashMap<>(); | |
36 | - Map<OAuth2ClientRegistrationInfoId, OAuth2ClientRegistrationInfo> infoById = new HashMap<>(); | |
35 | + Map<OAuth2ClientRegistrationInfoId, List<DomainInfo>> domainsByInfoId = new LinkedHashMap<>(); | |
36 | + Map<OAuth2ClientRegistrationInfoId, OAuth2ClientRegistrationInfo> infoById = new LinkedHashMap<>(); | |
37 | 37 | for (ExtendedOAuth2ClientRegistrationInfo extendedClientRegistrationInfo : extendedOAuth2ClientRegistrationInfos) { |
38 | 38 | String domainName = extendedClientRegistrationInfo.getDomainName(); |
39 | 39 | SchemeType domainScheme = extendedClientRegistrationInfo.getDomainScheme(); |
40 | - domainsByInfoId.computeIfAbsent(extendedClientRegistrationInfo.getId(), key -> new HashSet<>()) | |
40 | + domainsByInfoId.computeIfAbsent(extendedClientRegistrationInfo.getId(), key -> new ArrayList<>()) | |
41 | 41 | .add(new DomainInfo(domainScheme, domainName)); |
42 | 42 | infoById.put(extendedClientRegistrationInfo.getId(), extendedClientRegistrationInfo); |
43 | 43 | } |
44 | - Map<Set<DomainInfo>, OAuth2ClientsDomainParams> domainParamsMap = new HashMap<>(); | |
44 | + Map<List<DomainInfo>, OAuth2ClientsDomainParams> domainParamsMap = new LinkedHashMap<>(); | |
45 | 45 | domainsByInfoId.forEach((clientRegistrationInfoId, domainInfos) -> { |
46 | 46 | domainParamsMap.computeIfAbsent(domainInfos, |
47 | - key -> new OAuth2ClientsDomainParams(key, new HashSet<>()) | |
47 | + key -> new OAuth2ClientsDomainParams(key, new ArrayList<>()) | |
48 | 48 | ) |
49 | 49 | .getClientRegistrations() |
50 | 50 | .add(toClientRegistrationDto(infoById.get(clientRegistrationInfoId))); |
... | ... | @@ -52,7 +52,7 @@ public class OAuth2Utils { |
52 | 52 | boolean enabled = extendedOAuth2ClientRegistrationInfos.stream() |
53 | 53 | .map(OAuth2ClientRegistrationInfo::isEnabled) |
54 | 54 | .findFirst().orElse(false); |
55 | - return new OAuth2ClientsParams(enabled, new HashSet<>(domainParamsMap.values())); | |
55 | + return new OAuth2ClientsParams(enabled, new ArrayList<>(domainParamsMap.values())); | |
56 | 56 | } |
57 | 57 | |
58 | 58 | public static ClientRegistrationDto toClientRegistrationDto(OAuth2ClientRegistrationInfo oAuth2ClientRegistrationInfo) { | ... | ... |
... | ... | @@ -15,6 +15,7 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.dao.service; |
17 | 17 | |
18 | +import com.google.common.collect.Lists; | |
18 | 19 | import com.google.common.collect.Sets; |
19 | 20 | import org.junit.After; |
20 | 21 | import org.junit.Assert; |
... | ... | @@ -29,7 +30,7 @@ import java.util.*; |
29 | 30 | import java.util.stream.Collectors; |
30 | 31 | |
31 | 32 | public class BaseOAuth2ServiceTest extends AbstractServiceTest { |
32 | - private static final OAuth2ClientsParams EMPTY_PARAMS = new OAuth2ClientsParams(false, new HashSet<>()); | |
33 | + private static final OAuth2ClientsParams EMPTY_PARAMS = new OAuth2ClientsParams(false, new ArrayList<>()); | |
33 | 34 | |
34 | 35 | @Autowired |
35 | 36 | protected OAuth2Service oAuth2Service; |
... | ... | @@ -48,14 +49,14 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { |
48 | 49 | |
49 | 50 | @Test(expected = DataValidationException.class) |
50 | 51 | public void testSaveHttpAndMixedDomainsTogether() { |
51 | - OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( | |
52 | + OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( | |
52 | 53 | OAuth2ClientsDomainParams.builder() |
53 | - .domainInfos(Sets.newHashSet( | |
54 | + .domainInfos(Lists.newArrayList( | |
54 | 55 | DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), |
55 | 56 | DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(), |
56 | 57 | DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() |
57 | 58 | )) |
58 | - .clientRegistrations(Sets.newHashSet( | |
59 | + .clientRegistrations(Lists.newArrayList( | |
59 | 60 | validClientRegistrationDto(), |
60 | 61 | validClientRegistrationDto(), |
61 | 62 | validClientRegistrationDto() |
... | ... | @@ -67,14 +68,14 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { |
67 | 68 | |
68 | 69 | @Test(expected = DataValidationException.class) |
69 | 70 | public void testSaveHttpsAndMixedDomainsTogether() { |
70 | - OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( | |
71 | + OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( | |
71 | 72 | OAuth2ClientsDomainParams.builder() |
72 | - .domainInfos(Sets.newHashSet( | |
73 | + .domainInfos(Lists.newArrayList( | |
73 | 74 | DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build(), |
74 | 75 | DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(), |
75 | 76 | DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() |
76 | 77 | )) |
77 | - .clientRegistrations(Sets.newHashSet( | |
78 | + .clientRegistrations(Lists.newArrayList( | |
78 | 79 | validClientRegistrationDto(), |
79 | 80 | validClientRegistrationDto(), |
80 | 81 | validClientRegistrationDto() |
... | ... | @@ -131,20 +132,20 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { |
131 | 132 | Assert.assertNotNull(foundClientsParams); |
132 | 133 | Assert.assertEquals(clientsParams, foundClientsParams); |
133 | 134 | |
134 | - OAuth2ClientsParams newClientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( | |
135 | + OAuth2ClientsParams newClientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( | |
135 | 136 | OAuth2ClientsDomainParams.builder() |
136 | - .domainInfos(Sets.newHashSet( | |
137 | + .domainInfos(Lists.newArrayList( | |
137 | 138 | DomainInfo.builder().name("another-domain").scheme(SchemeType.HTTPS).build() |
138 | 139 | )) |
139 | - .clientRegistrations(Sets.newHashSet( | |
140 | + .clientRegistrations(Lists.newArrayList( | |
140 | 141 | validClientRegistrationDto() |
141 | 142 | )) |
142 | 143 | .build(), |
143 | 144 | OAuth2ClientsDomainParams.builder() |
144 | - .domainInfos(Sets.newHashSet( | |
145 | + .domainInfos(Lists.newArrayList( | |
145 | 146 | DomainInfo.builder().name("test-domain").scheme(SchemeType.MIXED).build() |
146 | 147 | )) |
147 | - .clientRegistrations(Sets.newHashSet( | |
148 | + .clientRegistrations(Lists.newArrayList( | |
148 | 149 | validClientRegistrationDto() |
149 | 150 | )) |
150 | 151 | .build() |
... | ... | @@ -157,22 +158,22 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { |
157 | 158 | |
158 | 159 | @Test |
159 | 160 | public void testGetOAuth2Clients() { |
160 | - Set<ClientRegistrationDto> firstGroup = Sets.newHashSet( | |
161 | + List<ClientRegistrationDto> firstGroup = Lists.newArrayList( | |
161 | 162 | validClientRegistrationDto(), |
162 | 163 | validClientRegistrationDto(), |
163 | 164 | validClientRegistrationDto(), |
164 | 165 | validClientRegistrationDto() |
165 | 166 | ); |
166 | - Set<ClientRegistrationDto> secondGroup = Sets.newHashSet( | |
167 | + List<ClientRegistrationDto> secondGroup = Lists.newArrayList( | |
167 | 168 | validClientRegistrationDto(), |
168 | 169 | validClientRegistrationDto() |
169 | 170 | ); |
170 | - Set<ClientRegistrationDto> thirdGroup = Sets.newHashSet( | |
171 | + List<ClientRegistrationDto> thirdGroup = Lists.newArrayList( | |
171 | 172 | validClientRegistrationDto() |
172 | 173 | ); |
173 | - OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( | |
174 | + OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( | |
174 | 175 | OAuth2ClientsDomainParams.builder() |
175 | - .domainInfos(Sets.newHashSet( | |
176 | + .domainInfos(Lists.newArrayList( | |
176 | 177 | DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), |
177 | 178 | DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), |
178 | 179 | DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() |
... | ... | @@ -180,14 +181,14 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { |
180 | 181 | .clientRegistrations(firstGroup) |
181 | 182 | .build(), |
182 | 183 | OAuth2ClientsDomainParams.builder() |
183 | - .domainInfos(Sets.newHashSet( | |
184 | + .domainInfos(Lists.newArrayList( | |
184 | 185 | DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), |
185 | 186 | DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() |
186 | 187 | )) |
187 | 188 | .clientRegistrations(secondGroup) |
188 | 189 | .build(), |
189 | 190 | OAuth2ClientsDomainParams.builder() |
190 | - .domainInfos(Sets.newHashSet( | |
191 | + .domainInfos(Lists.newArrayList( | |
191 | 192 | DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), |
192 | 193 | DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() |
193 | 194 | )) |
... | ... | @@ -285,15 +286,15 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { |
285 | 286 | |
286 | 287 | @Test |
287 | 288 | public void testGetOAuth2ClientsForHttpAndHttps() { |
288 | - Set<ClientRegistrationDto> firstGroup = Sets.newHashSet( | |
289 | + List<ClientRegistrationDto> firstGroup = Lists.newArrayList( | |
289 | 290 | validClientRegistrationDto(), |
290 | 291 | validClientRegistrationDto(), |
291 | 292 | validClientRegistrationDto(), |
292 | 293 | validClientRegistrationDto() |
293 | 294 | ); |
294 | - OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( | |
295 | + OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( | |
295 | 296 | OAuth2ClientsDomainParams.builder() |
296 | - .domainInfos(Sets.newHashSet( | |
297 | + .domainInfos(Lists.newArrayList( | |
297 | 298 | DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), |
298 | 299 | DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), |
299 | 300 | DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build() |
... | ... | @@ -335,25 +336,25 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { |
335 | 336 | |
336 | 337 | @Test |
337 | 338 | public void testGetDisabledOAuth2Clients() { |
338 | - OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( | |
339 | + OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( | |
339 | 340 | OAuth2ClientsDomainParams.builder() |
340 | - .domainInfos(Sets.newHashSet( | |
341 | + .domainInfos(Lists.newArrayList( | |
341 | 342 | DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), |
342 | 343 | DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), |
343 | 344 | DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() |
344 | 345 | )) |
345 | - .clientRegistrations(Sets.newHashSet( | |
346 | + .clientRegistrations(Lists.newArrayList( | |
346 | 347 | validClientRegistrationDto(), |
347 | 348 | validClientRegistrationDto(), |
348 | 349 | validClientRegistrationDto() |
349 | 350 | )) |
350 | 351 | .build(), |
351 | 352 | OAuth2ClientsDomainParams.builder() |
352 | - .domainInfos(Sets.newHashSet( | |
353 | + .domainInfos(Lists.newArrayList( | |
353 | 354 | DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), |
354 | 355 | DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() |
355 | 356 | )) |
356 | - .clientRegistrations(Sets.newHashSet( | |
357 | + .clientRegistrations(Lists.newArrayList( | |
357 | 358 | validClientRegistrationDto(), |
358 | 359 | validClientRegistrationDto() |
359 | 360 | )) |
... | ... | @@ -374,35 +375,35 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { |
374 | 375 | |
375 | 376 | @Test |
376 | 377 | public void testFindAllClientRegistrationInfos() { |
377 | - OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( | |
378 | + OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( | |
378 | 379 | OAuth2ClientsDomainParams.builder() |
379 | - .domainInfos(Sets.newHashSet( | |
380 | + .domainInfos(Lists.newArrayList( | |
380 | 381 | DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), |
381 | 382 | DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), |
382 | 383 | DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() |
383 | 384 | )) |
384 | - .clientRegistrations(Sets.newHashSet( | |
385 | + .clientRegistrations(Lists.newArrayList( | |
385 | 386 | validClientRegistrationDto(), |
386 | 387 | validClientRegistrationDto(), |
387 | 388 | validClientRegistrationDto() |
388 | 389 | )) |
389 | 390 | .build(), |
390 | 391 | OAuth2ClientsDomainParams.builder() |
391 | - .domainInfos(Sets.newHashSet( | |
392 | + .domainInfos(Lists.newArrayList( | |
392 | 393 | DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), |
393 | 394 | DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() |
394 | 395 | )) |
395 | - .clientRegistrations(Sets.newHashSet( | |
396 | + .clientRegistrations(Lists.newArrayList( | |
396 | 397 | validClientRegistrationDto(), |
397 | 398 | validClientRegistrationDto() |
398 | 399 | )) |
399 | 400 | .build(), |
400 | 401 | OAuth2ClientsDomainParams.builder() |
401 | - .domainInfos(Sets.newHashSet( | |
402 | + .domainInfos(Lists.newArrayList( | |
402 | 403 | DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), |
403 | 404 | DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() |
404 | 405 | )) |
405 | - .clientRegistrations(Sets.newHashSet( | |
406 | + .clientRegistrations(Lists.newArrayList( | |
406 | 407 | validClientRegistrationDto() |
407 | 408 | )) |
408 | 409 | .build() |
... | ... | @@ -423,35 +424,35 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { |
423 | 424 | |
424 | 425 | @Test |
425 | 426 | public void testFindClientRegistrationById() { |
426 | - OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( | |
427 | + OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( | |
427 | 428 | OAuth2ClientsDomainParams.builder() |
428 | - .domainInfos(Sets.newHashSet( | |
429 | + .domainInfos(Lists.newArrayList( | |
429 | 430 | DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), |
430 | 431 | DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), |
431 | 432 | DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() |
432 | 433 | )) |
433 | - .clientRegistrations(Sets.newHashSet( | |
434 | + .clientRegistrations(Lists.newArrayList( | |
434 | 435 | validClientRegistrationDto(), |
435 | 436 | validClientRegistrationDto(), |
436 | 437 | validClientRegistrationDto() |
437 | 438 | )) |
438 | 439 | .build(), |
439 | 440 | OAuth2ClientsDomainParams.builder() |
440 | - .domainInfos(Sets.newHashSet( | |
441 | + .domainInfos(Lists.newArrayList( | |
441 | 442 | DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), |
442 | 443 | DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() |
443 | 444 | )) |
444 | - .clientRegistrations(Sets.newHashSet( | |
445 | + .clientRegistrations(Lists.newArrayList( | |
445 | 446 | validClientRegistrationDto(), |
446 | 447 | validClientRegistrationDto() |
447 | 448 | )) |
448 | 449 | .build(), |
449 | 450 | OAuth2ClientsDomainParams.builder() |
450 | - .domainInfos(Sets.newHashSet( | |
451 | + .domainInfos(Lists.newArrayList( | |
451 | 452 | DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), |
452 | 453 | DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() |
453 | 454 | )) |
454 | - .clientRegistrations(Sets.newHashSet( | |
455 | + .clientRegistrations(Lists.newArrayList( | |
455 | 456 | validClientRegistrationDto() |
456 | 457 | )) |
457 | 458 | .build() |
... | ... | @@ -466,14 +467,14 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { |
466 | 467 | } |
467 | 468 | |
468 | 469 | private OAuth2ClientsParams createDefaultClientsParams() { |
469 | - return new OAuth2ClientsParams(true, Sets.newHashSet( | |
470 | + return new OAuth2ClientsParams(true, Lists.newArrayList( | |
470 | 471 | OAuth2ClientsDomainParams.builder() |
471 | - .domainInfos(Sets.newHashSet( | |
472 | + .domainInfos(Lists.newArrayList( | |
472 | 473 | DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), |
473 | 474 | DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), |
474 | 475 | DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() |
475 | 476 | )) |
476 | - .clientRegistrations(Sets.newHashSet( | |
477 | + .clientRegistrations(Lists.newArrayList( | |
477 | 478 | validClientRegistrationDto(), |
478 | 479 | validClientRegistrationDto(), |
479 | 480 | validClientRegistrationDto(), |
... | ... | @@ -481,11 +482,11 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { |
481 | 482 | )) |
482 | 483 | .build(), |
483 | 484 | OAuth2ClientsDomainParams.builder() |
484 | - .domainInfos(Sets.newHashSet( | |
485 | + .domainInfos(Lists.newArrayList( | |
485 | 486 | DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), |
486 | 487 | DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() |
487 | 488 | )) |
488 | - .clientRegistrations(Sets.newHashSet( | |
489 | + .clientRegistrations(Lists.newArrayList( | |
489 | 490 | validClientRegistrationDto(), |
490 | 491 | validClientRegistrationDto() |
491 | 492 | )) | ... | ... |
... | ... | @@ -41,4 +41,8 @@ export class OAuth2Service { |
41 | 41 | return this.http.post<OAuth2ClientsParams>('/api/oauth2/config', OAuth2Setting, |
42 | 42 | defaultHttpOptionsFromConfig(config)); |
43 | 43 | } |
44 | + | |
45 | + public getLoginProcessingUrl(config?: RequestConfig): Observable<string> { | |
46 | + return this.http.get<string>(`/api/oauth2/loginProcessingUrl`, defaultHttpOptionsFromConfig(config)); | |
47 | + } | |
44 | 48 | } | ... | ... |
... | ... | @@ -191,6 +191,11 @@ export class MenuService { |
191 | 191 | name: 'admin.security-settings', |
192 | 192 | icon: 'security', |
193 | 193 | path: '/settings/security-settings' |
194 | + }, | |
195 | + { | |
196 | + name: 'admin.oauth2.oauth2', | |
197 | + icon: 'security', | |
198 | + path: '/settings/oauth2' | |
194 | 199 | } |
195 | 200 | ] |
196 | 201 | } | ... | ... |
... | ... | @@ -88,14 +88,20 @@ |
88 | 88 | </fieldset> |
89 | 89 | </div> |
90 | 90 | <div mat-dialog-actions fxLayout="row"> |
91 | - <div fxLayout="row" *ngIf="alarm$ | async; let alarm;"> | |
91 | + <button mat-button color="primary" | |
92 | + type="button" | |
93 | + [disabled]="(isLoading$ | async)" | |
94 | + (click)="close()" cdkFocusInitial> | |
95 | + {{ 'action.close' | translate }} | |
96 | + </button> | |
97 | + <span fxFlex></span> | |
98 | + <div fxLayout="row" *ngIf="alarm$ | async; let alarm;" fxLayoutGap="8px"> | |
92 | 99 | <button *ngIf="allowAcknowledgment && (alarm.status === alarmStatuses.ACTIVE_UNACK || |
93 | 100 | alarm.status === alarmStatuses.CLEARED_UNACK)" |
94 | 101 | mat-raised-button |
95 | 102 | color="primary" |
96 | 103 | type="button" |
97 | 104 | (click)="acknowledge()" |
98 | - style="margin-right: 20px;" | |
99 | 105 | [disabled]="(isLoading$ | async)"> |
100 | 106 | {{ 'alarm.acknowledge' | translate }} |
101 | 107 | </button> |
... | ... | @@ -109,12 +115,5 @@ |
109 | 115 | {{ 'alarm.clear' | translate }} |
110 | 116 | </button> |
111 | 117 | </div> |
112 | - <span fxFlex></span> | |
113 | - <button mat-button color="primary" | |
114 | - type="button" | |
115 | - [disabled]="(isLoading$ | async)" | |
116 | - (click)="close()" cdkFocusInitial> | |
117 | - {{ 'action.close' | translate }} | |
118 | - </button> | |
119 | 118 | </div> |
120 | 119 | </form> | ... | ... |
... | ... | @@ -59,16 +59,16 @@ |
59 | 59 | </fieldset> |
60 | 60 | </div> |
61 | 61 | <div mat-dialog-actions fxLayoutAlign="end center"> |
62 | - <button mat-raised-button color="primary" | |
63 | - type="submit" | |
64 | - [disabled]="(isLoading$ | async) || entityAliasFormGroup.invalid || !entityAliasFormGroup.dirty"> | |
65 | - {{ (isAdd ? 'action.add' : 'action.save') | translate }} | |
66 | - </button> | |
67 | 62 | <button mat-button color="primary" |
68 | 63 | type="button" |
69 | 64 | [disabled]="(isLoading$ | async)" |
70 | 65 | (click)="cancel()" cdkFocusInitial> |
71 | 66 | {{ 'action.cancel' | translate }} |
72 | 67 | </button> |
68 | + <button mat-raised-button color="primary" | |
69 | + type="submit" | |
70 | + [disabled]="(isLoading$ | async) || entityAliasFormGroup.invalid || !entityAliasFormGroup.dirty"> | |
71 | + {{ (isAdd ? 'action.add' : 'action.save') | translate }} | |
72 | + </button> | |
73 | 73 | </div> |
74 | 74 | </form> | ... | ... |
... | ... | @@ -95,11 +95,6 @@ |
95 | 95 | {{ 'alias.add' | translate }} |
96 | 96 | </button> |
97 | 97 | <span fxFlex></span> |
98 | - <button mat-raised-button color="primary" | |
99 | - type="submit" | |
100 | - [disabled]="(isLoading$ | async) || entityAliasesFormGroup.invalid || !entityAliasesFormGroup.dirty"> | |
101 | - {{ 'action.save' | translate }} | |
102 | - </button> | |
103 | 98 | <button mat-button color="primary" |
104 | 99 | type="button" |
105 | 100 | [disabled]="(isLoading$ | async)" |
... | ... | @@ -107,5 +102,10 @@ |
107 | 102 | cdkFocusInitial> |
108 | 103 | {{ 'action.cancel' | translate }} |
109 | 104 | </button> |
105 | + <button mat-raised-button color="primary" | |
106 | + type="submit" | |
107 | + [disabled]="(isLoading$ | async) || entityAliasesFormGroup.invalid || !entityAliasesFormGroup.dirty"> | |
108 | + {{ 'action.save' | translate }} | |
109 | + </button> | |
110 | 110 | </div> |
111 | 111 | </form> | ... | ... |
... | ... | @@ -44,16 +44,16 @@ |
44 | 44 | </fieldset> |
45 | 45 | </div> |
46 | 46 | <div mat-dialog-actions fxLayoutAlign="end center"> |
47 | - <button mat-raised-button color="primary" | |
48 | - type="submit" | |
49 | - [disabled]="(isLoading$ | async) || attributeFormGroup.invalid || !attributeFormGroup.dirty"> | |
50 | - {{ 'action.add' | translate }} | |
51 | - </button> | |
52 | 47 | <button mat-button color="primary" |
53 | 48 | type="button" |
54 | 49 | [disabled]="(isLoading$ | async)" |
55 | 50 | (click)="cancel()" cdkFocusInitial> |
56 | 51 | {{ 'action.cancel' | translate }} |
57 | 52 | </button> |
53 | + <button mat-raised-button color="primary" | |
54 | + type="submit" | |
55 | + [disabled]="(isLoading$ | async) || attributeFormGroup.invalid || !attributeFormGroup.dirty"> | |
56 | + {{ 'action.add' | translate }} | |
57 | + </button> | |
58 | 58 | </div> |
59 | 59 | </form> | ... | ... |
... | ... | @@ -55,21 +55,22 @@ |
55 | 55 | </mat-radio-group> |
56 | 56 | </fieldset> |
57 | 57 | </div> |
58 | - <div mat-dialog-actions fxLayoutAlign="end center"> | |
58 | + <div mat-dialog-actions fxLayout="row"> | |
59 | 59 | <mat-checkbox formControlName="openDashboard" |
60 | - style="margin-bottom: 0; padding-right: 20px;"> | |
60 | + style="margin-bottom: 0;"> | |
61 | 61 | {{ 'dashboard.open-dashboard' | translate }} |
62 | 62 | </mat-checkbox> |
63 | - <button mat-raised-button color="primary" | |
64 | - type="submit" | |
65 | - [disabled]="(isLoading$ | async) || addWidgetFormGroup.invalid || !addWidgetFormGroup.dirty"> | |
66 | - {{ 'action.add' | translate }} | |
67 | - </button> | |
63 | + <span fxFlex></span> | |
68 | 64 | <button mat-button color="primary" |
69 | 65 | type="button" |
70 | 66 | [disabled]="(isLoading$ | async)" |
71 | 67 | (click)="cancel()" cdkFocusInitial> |
72 | 68 | {{ 'action.cancel' | translate }} |
73 | 69 | </button> |
70 | + <button mat-raised-button color="primary" | |
71 | + type="submit" | |
72 | + [disabled]="(isLoading$ | async) || addWidgetFormGroup.invalid || !addWidgetFormGroup.dirty"> | |
73 | + {{ 'action.add' | translate }} | |
74 | + </button> | |
74 | 75 | </div> |
75 | 76 | </form> | ... | ... |
... | ... | @@ -40,11 +40,6 @@ |
40 | 40 | </fieldset> |
41 | 41 | </div> |
42 | 42 | <div mat-dialog-actions fxLayoutAlign="end center"> |
43 | - <button mat-raised-button color="primary" | |
44 | - type="submit" | |
45 | - [disabled]="(isLoading$ | async) || stateFormGroup.invalid"> | |
46 | - {{ 'action.select' | translate }} | |
47 | - </button> | |
48 | 43 | <button mat-button color="primary" |
49 | 44 | type="button" |
50 | 45 | [disabled]="(isLoading$ | async)" |
... | ... | @@ -52,5 +47,10 @@ |
52 | 47 | cdkFocusInitial> |
53 | 48 | {{ 'action.cancel' | translate }} |
54 | 49 | </button> |
50 | + <button mat-raised-button color="primary" | |
51 | + type="submit" | |
52 | + [disabled]="(isLoading$ | async) || stateFormGroup.invalid"> | |
53 | + {{ 'action.select' | translate }} | |
54 | + </button> | |
55 | 55 | </div> |
56 | 56 | </form> | ... | ... |
... | ... | @@ -46,12 +46,6 @@ |
46 | 46 | </fieldset> |
47 | 47 | </div> |
48 | 48 | <div mat-dialog-actions fxLayoutAlign="end center"> |
49 | - <button mat-raised-button color="primary" | |
50 | - *ngIf="!data.readonly" | |
51 | - type="submit" | |
52 | - [disabled]="(isLoading$ | async) || complexFilterFormGroup.invalid || !complexFilterFormGroup.dirty"> | |
53 | - {{ (isAdd ? 'action.add' : 'action.update') | translate }} | |
54 | - </button> | |
55 | 49 | <button mat-button color="primary" |
56 | 50 | type="button" |
57 | 51 | [disabled]="(isLoading$ | async)" |
... | ... | @@ -59,5 +53,11 @@ |
59 | 53 | cdkFocusInitial> |
60 | 54 | {{ (data.readonly ? 'action.close' : 'action.cancel') | translate }} |
61 | 55 | </button> |
56 | + <button mat-raised-button color="primary" | |
57 | + *ngIf="!data.readonly" | |
58 | + type="submit" | |
59 | + [disabled]="(isLoading$ | async) || complexFilterFormGroup.invalid || !complexFilterFormGroup.dirty"> | |
60 | + {{ (isAdd ? 'action.add' : 'action.update') | translate }} | |
61 | + </button> | |
62 | 62 | </div> |
63 | 63 | </form> | ... | ... |
... | ... | @@ -55,16 +55,16 @@ |
55 | 55 | </fieldset> |
56 | 56 | </div> |
57 | 57 | <div mat-dialog-actions fxLayoutAlign="end center"> |
58 | - <button mat-raised-button color="primary" | |
59 | - type="submit" | |
60 | - [disabled]="(isLoading$ | async) || filterFormGroup.invalid || !filterFormGroup.dirty"> | |
61 | - {{ (isAdd ? 'action.add' : 'action.update') | translate }} | |
62 | - </button> | |
63 | 58 | <button mat-button color="primary" |
64 | 59 | type="button" |
65 | 60 | [disabled]="(isLoading$ | async)" |
66 | 61 | (click)="cancel()" cdkFocusInitial> |
67 | 62 | {{ 'action.cancel' | translate }} |
68 | 63 | </button> |
64 | + <button mat-raised-button color="primary" | |
65 | + type="submit" | |
66 | + [disabled]="(isLoading$ | async) || filterFormGroup.invalid || !filterFormGroup.dirty"> | |
67 | + {{ (isAdd ? 'action.add' : 'action.update') | translate }} | |
68 | + </button> | |
69 | 69 | </div> |
70 | 70 | </form> | ... | ... |
... | ... | @@ -46,12 +46,6 @@ |
46 | 46 | </fieldset> |
47 | 47 | </div> |
48 | 48 | <div mat-dialog-actions fxLayoutAlign="end center"> |
49 | - <button mat-raised-button color="primary" | |
50 | - *ngIf="!data.readonly" | |
51 | - type="submit" | |
52 | - [disabled]="(isLoading$ | async) || filterUserInfoFormGroup.invalid || !filterUserInfoFormGroup.dirty"> | |
53 | - {{ 'action.update' | translate }} | |
54 | - </button> | |
55 | 49 | <button mat-button color="primary" |
56 | 50 | type="button" |
57 | 51 | [disabled]="(isLoading$ | async)" |
... | ... | @@ -59,5 +53,11 @@ |
59 | 53 | cdkFocusInitial> |
60 | 54 | {{ (data.readonly ? 'action.close' : 'action.cancel') | translate }} |
61 | 55 | </button> |
56 | + <button mat-raised-button color="primary" | |
57 | + *ngIf="!data.readonly" | |
58 | + type="submit" | |
59 | + [disabled]="(isLoading$ | async) || filterUserInfoFormGroup.invalid || !filterUserInfoFormGroup.dirty"> | |
60 | + {{ 'action.update' | translate }} | |
61 | + </button> | |
62 | 62 | </div> |
63 | 63 | </form> | ... | ... |
... | ... | @@ -88,11 +88,6 @@ |
88 | 88 | {{ 'filter.add' | translate }} |
89 | 89 | </button> |
90 | 90 | <span fxFlex></span> |
91 | - <button mat-raised-button color="primary" | |
92 | - type="submit" | |
93 | - [disabled]="(isLoading$ | async) || filtersFormGroup.invalid || !filtersFormGroup.dirty"> | |
94 | - {{ 'action.save' | translate }} | |
95 | - </button> | |
96 | 91 | <button mat-button color="primary" |
97 | 92 | type="button" |
98 | 93 | [disabled]="(isLoading$ | async)" |
... | ... | @@ -100,5 +95,10 @@ |
100 | 95 | cdkFocusInitial> |
101 | 96 | {{ 'action.cancel' | translate }} |
102 | 97 | </button> |
98 | + <button mat-raised-button color="primary" | |
99 | + type="submit" | |
100 | + [disabled]="(isLoading$ | async) || filtersFormGroup.invalid || !filtersFormGroup.dirty"> | |
101 | + {{ 'action.save' | translate }} | |
102 | + </button> | |
103 | 103 | </div> |
104 | 104 | </form> | ... | ... |
... | ... | @@ -79,12 +79,6 @@ |
79 | 79 | </fieldset> |
80 | 80 | </div> |
81 | 81 | <div mat-dialog-actions fxLayoutAlign="end center"> |
82 | - <button mat-raised-button color="primary" | |
83 | - type="submit" | |
84 | - *ngIf="!data.readonly" | |
85 | - [disabled]="(isLoading$ | async) || keyFilterFormGroup.invalid || !keyFilterFormGroup.dirty"> | |
86 | - {{ (data.isAdd ? 'action.add' : 'action.update') | translate }} | |
87 | - </button> | |
88 | 82 | <button mat-button color="primary" |
89 | 83 | type="button" |
90 | 84 | [disabled]="(isLoading$ | async)" |
... | ... | @@ -92,5 +86,11 @@ |
92 | 86 | cdkFocusInitial> |
93 | 87 | {{ (data.readonly ? 'action.close' : 'action.cancel') | translate }} |
94 | 88 | </button> |
89 | + <button mat-raised-button color="primary" | |
90 | + type="submit" | |
91 | + *ngIf="!data.readonly" | |
92 | + [disabled]="(isLoading$ | async) || keyFilterFormGroup.invalid || !keyFilterFormGroup.dirty"> | |
93 | + {{ (data.isAdd ? 'action.add' : 'action.update') | translate }} | |
94 | + </button> | |
95 | 95 | </div> |
96 | 96 | </form> | ... | ... |
... | ... | @@ -65,16 +65,16 @@ |
65 | 65 | </fieldset> |
66 | 66 | </div> |
67 | 67 | <div mat-dialog-actions fxLayoutAlign="end center"> |
68 | - <button mat-raised-button color="primary" | |
69 | - type="submit" | |
70 | - [disabled]="(isLoading$ | async) || userFilterFormGroup.invalid || !userFilterFormGroup.dirty"> | |
71 | - {{ 'action.update' | translate }} | |
72 | - </button> | |
73 | 68 | <button mat-button color="primary" |
74 | 69 | type="button" |
75 | 70 | [disabled]="(isLoading$ | async)" |
76 | 71 | (click)="cancel()" cdkFocusInitial> |
77 | 72 | {{ 'action.cancel' | translate }} |
78 | 73 | </button> |
74 | + <button mat-raised-button color="primary" | |
75 | + type="submit" | |
76 | + [disabled]="(isLoading$ | async) || userFilterFormGroup.invalid || !userFilterFormGroup.dirty"> | |
77 | + {{ 'action.update' | translate }} | |
78 | + </button> | |
79 | 79 | </div> |
80 | 80 | </form> | ... | ... |
... | ... | @@ -43,16 +43,16 @@ |
43 | 43 | </fieldset> |
44 | 44 | </div> |
45 | 45 | <div mat-dialog-actions fxLayoutAlign="end center"> |
46 | - <button mat-raised-button color="primary" | |
47 | - type="submit" | |
48 | - [disabled]="(isLoading$ | async) || importFormGroup.invalid || !importFormGroup.dirty"> | |
49 | - {{ 'action.import' | translate }} | |
50 | - </button> | |
51 | 46 | <button mat-button color="primary" |
52 | 47 | type="button" |
53 | 48 | [disabled]="(isLoading$ | async)" |
54 | 49 | (click)="cancel()" cdkFocusInitial> |
55 | 50 | {{ 'action.cancel' | translate }} |
56 | 51 | </button> |
52 | + <button mat-raised-button color="primary" | |
53 | + type="submit" | |
54 | + [disabled]="(isLoading$ | async) || importFormGroup.invalid || !importFormGroup.dirty"> | |
55 | + {{ 'action.import' | translate }} | |
56 | + </button> | |
57 | 57 | </div> |
58 | 58 | </form> | ... | ... |
... | ... | @@ -108,17 +108,17 @@ |
108 | 108 | </fieldset> |
109 | 109 | </div> |
110 | 110 | <div mat-dialog-actions fxLayoutAlign="end center"> |
111 | - <button mat-raised-button color="primary" | |
112 | - *ngIf="!readonly" | |
113 | - type="submit" | |
114 | - [disabled]="(isLoading$ | async) || conditionFormGroup.invalid || !conditionFormGroup.dirty"> | |
115 | - {{ 'action.save' | translate }} | |
116 | - </button> | |
117 | 111 | <button mat-button color="primary" |
118 | 112 | type="button" |
119 | 113 | [disabled]="(isLoading$ | async)" |
120 | 114 | (click)="cancel()" cdkFocusInitial> |
121 | 115 | {{ (readonly ? 'action.close' : 'action.cancel') | translate }} |
122 | 116 | </button> |
117 | + <button mat-raised-button color="primary" | |
118 | + *ngIf="!readonly" | |
119 | + type="submit" | |
120 | + [disabled]="(isLoading$ | async) || conditionFormGroup.invalid || !conditionFormGroup.dirty"> | |
121 | + {{ 'action.save' | translate }} | |
122 | + </button> | |
123 | 123 | </div> |
124 | 124 | </form> | ... | ... |
... | ... | @@ -37,17 +37,17 @@ |
37 | 37 | </fieldset> |
38 | 38 | </div> |
39 | 39 | <div mat-dialog-actions fxLayoutAlign="end center"> |
40 | - <button mat-raised-button color="primary" | |
41 | - *ngIf="!readonly" | |
42 | - type="submit" | |
43 | - [disabled]="(isLoading$ | async) || alarmScheduleFormGroup.invalid || !alarmScheduleFormGroup.dirty"> | |
44 | - {{ 'action.save' | translate }} | |
45 | - </button> | |
46 | 40 | <button mat-button color="primary" |
47 | 41 | type="button" |
48 | 42 | [disabled]="(isLoading$ | async)" |
49 | 43 | (click)="cancel()" cdkFocusInitial> |
50 | 44 | {{ (readonly ? 'action.close' : 'action.cancel') | translate }} |
51 | 45 | </button> |
46 | + <button mat-raised-button color="primary" | |
47 | + *ngIf="!readonly" | |
48 | + type="submit" | |
49 | + [disabled]="(isLoading$ | async) || alarmScheduleFormGroup.invalid || !alarmScheduleFormGroup.dirty"> | |
50 | + {{ 'action.save' | translate }} | |
51 | + </button> | |
52 | 52 | </div> |
53 | 53 | </form> | ... | ... |
... | ... | @@ -47,9 +47,9 @@ |
47 | 47 | <mat-icon>remove_circle_outline</mat-icon> |
48 | 48 | </button> |
49 | 49 | </div> |
50 | - <div *ngIf="!createAlarmRulesFormArray().controls.length"> | |
50 | + <div *ngIf="!createAlarmRulesFormArray().controls.length && !disabled"> | |
51 | 51 | <span translate fxLayoutAlign="center center" style="margin: 16px 0" |
52 | - class="tb-prompt">device-profile.no-create-alarm-rules</span> | |
52 | + class="tb-prompt required">device-profile.add-create-alarm-rule-prompt</span> | |
53 | 53 | </div> |
54 | 54 | <div fxLayout="row" *ngIf="!disabled"> |
55 | 55 | <button mat-stroked-button color="primary" | ... | ... |
... | ... | @@ -23,11 +23,11 @@ import { |
23 | 23 | FormControl, |
24 | 24 | FormGroup, |
25 | 25 | NG_VALIDATORS, |
26 | - NG_VALUE_ACCESSOR, | |
26 | + NG_VALUE_ACCESSOR, ValidationErrors, | |
27 | 27 | Validator, |
28 | 28 | Validators |
29 | 29 | } from '@angular/forms'; |
30 | -import { AlarmRule } from '@shared/models/device.models'; | |
30 | +import { AlarmRule, alarmRuleValidator } from '@shared/models/device.models'; | |
31 | 31 | import { MatDialog } from '@angular/material/dialog'; |
32 | 32 | import { Subscription } from 'rxjs'; |
33 | 33 | import { AlarmSeverity, alarmSeverityTranslations } from '../../../../../shared/models/alarm.models'; |
... | ... | @@ -141,10 +141,23 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit, |
141 | 141 | }; |
142 | 142 | const createAlarmRulesArray = this.createAlarmRulesFormGroup.get('createAlarmRules') as FormArray; |
143 | 143 | createAlarmRulesArray.push(this.fb.group({ |
144 | - severity: [null, Validators.required], | |
145 | - alarmRule: [createAlarmRule, Validators.required] | |
144 | + severity: [this.getFirstUnusedSeverity(), Validators.required], | |
145 | + alarmRule: [createAlarmRule, alarmRuleValidator] | |
146 | 146 | })); |
147 | 147 | this.createAlarmRulesFormGroup.updateValueAndValidity(); |
148 | + if (!this.createAlarmRulesFormGroup.valid) { | |
149 | + this.updateModel(); | |
150 | + } | |
151 | + } | |
152 | + | |
153 | + private getFirstUnusedSeverity(): AlarmSeverity { | |
154 | + for (const severityKey of Object.keys(AlarmSeverity)) { | |
155 | + const severity = AlarmSeverity[severityKey]; | |
156 | + if (this.usedSeverities.indexOf(severity) === -1) { | |
157 | + return severity; | |
158 | + } | |
159 | + } | |
160 | + return null; | |
148 | 161 | } |
149 | 162 | |
150 | 163 | public validate(c: FormControl) { | ... | ... |
... | ... | @@ -25,7 +25,7 @@ import { |
25 | 25 | Validator, |
26 | 26 | Validators |
27 | 27 | } from '@angular/forms'; |
28 | -import { AlarmRule, DeviceProfileAlarm } from '@shared/models/device.models'; | |
28 | +import { AlarmRule, DeviceProfileAlarm, deviceProfileAlarmValidator } from '@shared/models/device.models'; | |
29 | 29 | import { MatDialog } from '@angular/material/dialog'; |
30 | 30 | import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; |
31 | 31 | import { MatChipInputEvent } from '@angular/material/chips'; |
... | ... | @@ -92,7 +92,7 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit |
92 | 92 | clearRule: [null], |
93 | 93 | propagate: [null], |
94 | 94 | propagateRelationTypes: [null] |
95 | - }); | |
95 | + }, { validators: deviceProfileAlarmValidator }); | |
96 | 96 | this.alarmFormGroup.valueChanges.subscribe(() => { |
97 | 97 | this.updateModel(); |
98 | 98 | }); | ... | ... |
... | ... | @@ -30,7 +30,7 @@ import { |
30 | 30 | import { Store } from '@ngrx/store'; |
31 | 31 | import { AppState } from '@app/core/core.state'; |
32 | 32 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
33 | -import { DeviceProfileAlarm } from '@shared/models/device.models'; | |
33 | +import { DeviceProfileAlarm, deviceProfileAlarmValidator } from '@shared/models/device.models'; | |
34 | 34 | import { guid } from '@core/utils'; |
35 | 35 | import { Subscription } from 'rxjs'; |
36 | 36 | import { MatDialog } from '@angular/material/dialog'; |
... | ... | @@ -141,7 +141,7 @@ export class DeviceProfileAlarmsComponent implements ControlValueAccessor, OnIni |
141 | 141 | id: guid(), |
142 | 142 | alarmType: '', |
143 | 143 | createRules: { |
144 | - empty: { | |
144 | + CRITICAL: { | |
145 | 145 | condition: { |
146 | 146 | condition: [] |
147 | 147 | } |
... | ... | @@ -149,8 +149,11 @@ export class DeviceProfileAlarmsComponent implements ControlValueAccessor, OnIni |
149 | 149 | } |
150 | 150 | }; |
151 | 151 | const alarmsArray = this.deviceProfileAlarmsFormGroup.get('alarms') as FormArray; |
152 | - alarmsArray.push(this.fb.control(alarm, [Validators.required])); | |
152 | + alarmsArray.push(this.fb.control(alarm, [deviceProfileAlarmValidator])); | |
153 | 153 | this.deviceProfileAlarmsFormGroup.updateValueAndValidity(); |
154 | + if (!this.deviceProfileAlarmsFormGroup.valid) { | |
155 | + this.updateModel(); | |
156 | + } | |
154 | 157 | } |
155 | 158 | |
156 | 159 | public validate(c: FormControl) { | ... | ... |
... | ... | @@ -15,7 +15,7 @@ |
15 | 15 | limitations under the License. |
16 | 16 | |
17 | 17 | --> |
18 | -<form (ngSubmit)="save()" style="min-width: 1000px;"> | |
18 | +<form (ngSubmit)="save()" style="width: 1000px;"> | |
19 | 19 | <mat-toolbar color="primary"> |
20 | 20 | <h2>{{ (isAdd ? 'device-profile.add' : 'device-profile.edit' ) | translate }}</h2> |
21 | 21 | <span fxFlex></span> |
... | ... | @@ -37,11 +37,6 @@ |
37 | 37 | </tb-device-profile> |
38 | 38 | </div> |
39 | 39 | <div mat-dialog-actions fxLayoutAlign="end center"> |
40 | - <button mat-raised-button color="primary" | |
41 | - type="submit" | |
42 | - [disabled]="(isLoading$ | async) || deviceProfileComponent.entityForm?.invalid || !deviceProfileComponent.entityForm?.dirty"> | |
43 | - {{ (isAdd ? 'action.add' : 'action.save') | translate }} | |
44 | - </button> | |
45 | 40 | <button mat-button color="primary" |
46 | 41 | type="button" |
47 | 42 | cdkFocusInitial |
... | ... | @@ -49,5 +44,10 @@ |
49 | 44 | (click)="cancel()"> |
50 | 45 | {{ 'action.cancel' | translate }} |
51 | 46 | </button> |
47 | + <button mat-raised-button color="primary" | |
48 | + type="submit" | |
49 | + [disabled]="(isLoading$ | async) || deviceProfileComponent.entityForm?.invalid || !deviceProfileComponent.entityForm?.dirty"> | |
50 | + {{ (isAdd ? 'action.add' : 'action.save') | translate }} | |
51 | + </button> | |
52 | 52 | </div> |
53 | 53 | </form> | ... | ... |
... | ... | @@ -41,7 +41,7 @@ |
41 | 41 | </div> |
42 | 42 | <div class="mat-padding" fxLayout="column"> |
43 | 43 | <form [formGroup]="entityForm"> |
44 | - <fieldset [disabled]="(isLoading$ | async) || !isEdit"> | |
44 | + <fieldset [disabled]="(isLoading$ | async) || !isEdit" style="min-width: 0;"> | |
45 | 45 | <mat-form-field class="mat-block"> |
46 | 46 | <mat-label translate>device-profile.name</mat-label> |
47 | 47 | <input matInput formControlName="name" required/> | ... | ... |
... | ... | @@ -15,7 +15,7 @@ |
15 | 15 | limitations under the License. |
16 | 16 | |
17 | 17 | --> |
18 | -<form (ngSubmit)="save()" style="min-width: 600px;"> | |
18 | +<form (ngSubmit)="save()" style="width: 600px;"> | |
19 | 19 | <mat-toolbar color="primary"> |
20 | 20 | <h2>{{ (isAdd ? 'tenant-profile.add' : 'tenant-profile.edit' ) | translate }}</h2> |
21 | 21 | <span fxFlex></span> |
... | ... | @@ -37,11 +37,6 @@ |
37 | 37 | </tb-tenant-profile> |
38 | 38 | </div> |
39 | 39 | <div mat-dialog-actions fxLayoutAlign="end center"> |
40 | - <button mat-raised-button color="primary" | |
41 | - type="submit" | |
42 | - [disabled]="(isLoading$ | async) || tenantProfileComponent.entityForm?.invalid || !tenantProfileComponent.entityForm?.dirty"> | |
43 | - {{ (isAdd ? 'action.add' : 'action.save') | translate }} | |
44 | - </button> | |
45 | 40 | <button mat-button color="primary" |
46 | 41 | type="button" |
47 | 42 | cdkFocusInitial |
... | ... | @@ -49,5 +44,10 @@ |
49 | 44 | (click)="cancel()"> |
50 | 45 | {{ 'action.cancel' | translate }} |
51 | 46 | </button> |
47 | + <button mat-raised-button color="primary" | |
48 | + type="submit" | |
49 | + [disabled]="(isLoading$ | async) || tenantProfileComponent.entityForm?.invalid || !tenantProfileComponent.entityForm?.dirty"> | |
50 | + {{ (isAdd ? 'action.add' : 'action.save') | translate }} | |
51 | + </button> | |
52 | 52 | </div> |
53 | 53 | </form> | ... | ... |
... | ... | @@ -53,17 +53,16 @@ |
53 | 53 | </div> |
54 | 54 | <div mat-dialog-actions fxLayout="row"> |
55 | 55 | <span fxFlex></span> |
56 | - <button mat-button mat-raised-button color="primary" | |
57 | - type="submit" | |
58 | - [disabled]="(isLoading$ | async) || relationFormGroup.invalid || !(relationFormGroup.dirty || additionalInfo.dirty)"> | |
59 | - {{ (isAdd ? 'action.add' : 'action.save') | translate }} | |
60 | - </button> | |
61 | 56 | <button mat-button color="primary" |
62 | - style="margin-right: 20px;" | |
63 | 57 | type="button" |
64 | 58 | [disabled]="(isLoading$ | async)" |
65 | 59 | (click)="cancel()" cdkFocusInitial> |
66 | 60 | {{ 'action.cancel' | translate }} |
67 | 61 | </button> |
62 | + <button mat-button mat-raised-button color="primary" | |
63 | + type="submit" | |
64 | + [disabled]="(isLoading$ | async) || relationFormGroup.invalid || !(relationFormGroup.dirty || additionalInfo.dirty)"> | |
65 | + {{ (isAdd ? 'action.add' : 'action.save') | translate }} | |
66 | + </button> | |
68 | 67 | </div> |
69 | 68 | </form> | ... | ... |
... | ... | @@ -147,16 +147,16 @@ |
147 | 147 | </fieldset> |
148 | 148 | </div> |
149 | 149 | <div mat-dialog-actions fxLayoutAlign="end center"> |
150 | - <button mat-raised-button color="primary" | |
151 | - type="submit" | |
152 | - [disabled]="(isLoading$ | async) || widgetActionFormGroup.invalid || actionTypeFormGroup.invalid || (!widgetActionFormGroup.dirty && !actionTypeFormGroup.dirty)"> | |
153 | - {{ (isAdd ? 'action.add' : 'action.save') | translate }} | |
154 | - </button> | |
155 | 150 | <button mat-button color="primary" |
156 | 151 | type="button" |
157 | 152 | [disabled]="(isLoading$ | async)" |
158 | 153 | (click)="cancel()" cdkFocusInitial> |
159 | 154 | {{ 'action.cancel' | translate }} |
160 | 155 | </button> |
156 | + <button mat-raised-button color="primary" | |
157 | + type="submit" | |
158 | + [disabled]="(isLoading$ | async) || widgetActionFormGroup.invalid || actionTypeFormGroup.invalid || (!widgetActionFormGroup.dirty && !actionTypeFormGroup.dirty)"> | |
159 | + {{ (isAdd ? 'action.add' : 'action.save') | translate }} | |
160 | + </button> | |
161 | 161 | </div> |
162 | 162 | </form> | ... | ... |
... | ... | @@ -38,16 +38,16 @@ |
38 | 38 | </tb-data-key-config> |
39 | 39 | </div> |
40 | 40 | <div mat-dialog-actions fxLayoutAlign="end center"> |
41 | - <button mat-raised-button color="primary" | |
42 | - type="submit" | |
43 | - [disabled]="(isLoading$ | async) || dataKeyFormGroup.invalid || !dataKeyFormGroup.dirty"> | |
44 | - {{ 'action.save' | translate }} | |
45 | - </button> | |
46 | 41 | <button mat-button color="primary" |
47 | 42 | type="button" |
48 | 43 | [disabled]="(isLoading$ | async)" |
49 | 44 | (click)="cancel()" cdkFocusInitial> |
50 | 45 | {{ 'action.cancel' | translate }} |
51 | 46 | </button> |
47 | + <button mat-raised-button color="primary" | |
48 | + type="submit" | |
49 | + [disabled]="(isLoading$ | async) || dataKeyFormGroup.invalid || !dataKeyFormGroup.dirty"> | |
50 | + {{ 'action.save' | translate }} | |
51 | + </button> | |
52 | 52 | </div> |
53 | 53 | </form> | ... | ... |
... | ... | @@ -50,17 +50,16 @@ |
50 | 50 | </mat-chip-list> |
51 | 51 | </mat-form-field> |
52 | 52 | <div fxLayout="row" class="tb-panel-actions" fxLayoutAlign="end center"> |
53 | + <button type="button" | |
54 | + mat-button | |
55 | + (click)="cancel()"> | |
56 | + {{ 'action.cancel' | translate }} | |
57 | + </button> | |
53 | 58 | <button type="submit" |
54 | 59 | mat-raised-button |
55 | 60 | color="primary" |
56 | 61 | [disabled]="alarmFilterFormGroup.invalid || !alarmFilterFormGroup.dirty"> |
57 | 62 | {{ 'action.update' | translate }} |
58 | 63 | </button> |
59 | - <button type="button" | |
60 | - mat-button | |
61 | - (click)="cancel()" | |
62 | - style="margin-right: 20px;"> | |
63 | - {{ 'action.cancel' | translate }} | |
64 | - </button> | |
65 | 64 | </div> |
66 | 65 | </form> | ... | ... |
... | ... | @@ -28,7 +28,6 @@ |
28 | 28 | <div class="tb-panel-actions" fxLayout="row" *ngIf="!settings.autoConfirm"> |
29 | 29 | <span fxFlex></span> |
30 | 30 | <button mat-button mat-raised-button color="primary" |
31 | - style="margin-right: 20px;" | |
32 | 31 | type="button" (click)="apply()"> |
33 | 32 | {{ 'action.ok' | translate }} |
34 | 33 | </button> | ... | ... |
... | ... | @@ -352,7 +352,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni |
352 | 352 | } |
353 | 353 | dataKeys.push(dataKey); |
354 | 354 | |
355 | - dataKey.title = this.utils.customTranslation(dataKey.label, dataKey.label); | |
355 | + dataKey.label = this.utils.customTranslation(dataKey.label, dataKey.label); | |
356 | + dataKey.title = dataKey.label; | |
356 | 357 | dataKey.def = 'def' + this.columns.length; |
357 | 358 | const keySettings: TableWidgetDataKeySettings = dataKey.settings; |
358 | 359 | if (dataKey.type === DataKeyType.entityField && |
... | ... | @@ -374,7 +375,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni |
374 | 375 | } |
375 | 376 | |
376 | 377 | if (this.settings.defaultSortOrder && this.settings.defaultSortOrder.length) { |
377 | - this.defaultSortOrder = this.settings.defaultSortOrder; | |
378 | + this.defaultSortOrder = this.utils.customTranslation(this.settings.defaultSortOrder, this.settings.defaultSortOrder); | |
378 | 379 | } |
379 | 380 | |
380 | 381 | this.pageLink.sortOrder = entityDataSortOrderFromString(this.defaultSortOrder, this.columns); | ... | ... |
... | ... | @@ -40,17 +40,17 @@ |
40 | 40 | </fieldset> |
41 | 41 | </div> |
42 | 42 | <div mat-dialog-actions fxLayoutAlign="end center"> |
43 | - <button mat-raised-button color="primary" | |
44 | - type="submit" | |
45 | - [disabled]="(isLoading$ | async) || addEntitiesToCustomerFormGroup.invalid | |
46 | - || !addEntitiesToCustomerFormGroup.dirty"> | |
47 | - {{ 'action.assign' | translate }} | |
48 | - </button> | |
49 | 43 | <button mat-button color="primary" |
50 | 44 | type="button" |
51 | 45 | [disabled]="(isLoading$ | async)" |
52 | 46 | (click)="cancel()" cdkFocusInitial> |
53 | 47 | {{ 'action.cancel' | translate }} |
54 | 48 | </button> |
49 | + <button mat-raised-button color="primary" | |
50 | + type="submit" | |
51 | + [disabled]="(isLoading$ | async) || addEntitiesToCustomerFormGroup.invalid | |
52 | + || !addEntitiesToCustomerFormGroup.dirty"> | |
53 | + {{ 'action.assign' | translate }} | |
54 | + </button> | |
55 | 55 | </div> |
56 | 56 | </form> | ... | ... |
... | ... | @@ -39,17 +39,17 @@ |
39 | 39 | </fieldset> |
40 | 40 | </div> |
41 | 41 | <div mat-dialog-actions fxLayout="row" fxLayoutAlign="end center"> |
42 | - <button mat-raised-button color="primary" | |
43 | - type="submit" | |
44 | - [disabled]="(isLoading$ | async) || assignToCustomerFormGroup.invalid | |
45 | - || !assignToCustomerFormGroup.dirty"> | |
46 | - {{ 'action.assign' | translate }} | |
47 | - </button> | |
48 | 42 | <button mat-button color="primary" |
49 | 43 | type="button" |
50 | 44 | [disabled]="(isLoading$ | async)" |
51 | 45 | (click)="cancel()" cdkFocusInitial> |
52 | 46 | {{ 'action.cancel' | translate }} |
53 | 47 | </button> |
48 | + <button mat-raised-button color="primary" | |
49 | + type="submit" | |
50 | + [disabled]="(isLoading$ | async) || assignToCustomerFormGroup.invalid | |
51 | + || !assignToCustomerFormGroup.dirty"> | |
52 | + {{ 'action.assign' | translate }} | |
53 | + </button> | |
54 | 54 | </div> |
55 | 55 | </form> | ... | ... |
... | ... | @@ -14,8 +14,8 @@ |
14 | 14 | /// limitations under the License. |
15 | 15 | /// |
16 | 16 | |
17 | -import { NgModule } from '@angular/core'; | |
18 | -import { RouterModule, Routes } from '@angular/router'; | |
17 | +import { Injectable, NgModule } from '@angular/core'; | |
18 | +import { Resolve, RouterModule, Routes } from '@angular/router'; | |
19 | 19 | |
20 | 20 | import { MailServerComponent } from '@modules/home/pages/admin/mail-server.component'; |
21 | 21 | import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; |
... | ... | @@ -23,6 +23,25 @@ import { Authority } from '@shared/models/authority.enum'; |
23 | 23 | import { GeneralSettingsComponent } from '@modules/home/pages/admin/general-settings.component'; |
24 | 24 | import { SecuritySettingsComponent } from '@modules/home/pages/admin/security-settings.component'; |
25 | 25 | import { OAuth2SettingsComponent } from '@home/pages/admin/oauth2-settings.component'; |
26 | +import { User } from '@shared/models/user.model'; | |
27 | +import { Store } from '@ngrx/store'; | |
28 | +import { AppState } from '@core/core.state'; | |
29 | +import { UserService } from '@core/http/user.service'; | |
30 | +import { Observable } from 'rxjs'; | |
31 | +import { getCurrentAuthUser } from '@core/auth/auth.selectors'; | |
32 | +import { OAuth2Service } from '@core/http/oauth2.service'; | |
33 | +import { UserProfileResolver } from '@home/pages/profile/profile-routing.module'; | |
34 | + | |
35 | +@Injectable() | |
36 | +export class OAuth2LoginProcessingUrlResolver implements Resolve<string> { | |
37 | + | |
38 | + constructor(private oauth2Service: OAuth2Service) { | |
39 | + } | |
40 | + | |
41 | + resolve(): Observable<string> { | |
42 | + return this.oauth2Service.getLoginProcessingUrl(); | |
43 | + } | |
44 | +} | |
26 | 45 | |
27 | 46 | const routes: Routes = [ |
28 | 47 | { |
... | ... | @@ -90,6 +109,9 @@ const routes: Routes = [ |
90 | 109 | label: 'admin.oauth2.oauth2', |
91 | 110 | icon: 'security' |
92 | 111 | } |
112 | + }, | |
113 | + resolve: { | |
114 | + loginProcessingUrl: OAuth2LoginProcessingUrlResolver | |
93 | 115 | } |
94 | 116 | } |
95 | 117 | ] |
... | ... | @@ -98,6 +120,9 @@ const routes: Routes = [ |
98 | 120 | |
99 | 121 | @NgModule({ |
100 | 122 | imports: [RouterModule.forChild(routes)], |
101 | - exports: [RouterModule] | |
123 | + exports: [RouterModule], | |
124 | + providers: [ | |
125 | + OAuth2LoginProcessingUrlResolver | |
126 | + ] | |
102 | 127 | }) |
103 | 128 | export class AdminRoutingModule { } | ... | ... |
... | ... | @@ -43,6 +43,7 @@ import { DialogService } from '@core/services/dialog.service'; |
43 | 43 | import { TranslateService } from '@ngx-translate/core'; |
44 | 44 | import { isDefined, isDefinedAndNotNull } from '@core/utils'; |
45 | 45 | import { OAuth2Service } from '@core/http/oauth2.service'; |
46 | +import { ActivatedRoute } from '@angular/router'; | |
46 | 47 | |
47 | 48 | @Component({ |
48 | 49 | selector: 'tb-oauth2-settings', |
... | ... | @@ -87,7 +88,10 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha |
87 | 88 | |
88 | 89 | templateProvider = ['Custom']; |
89 | 90 | |
91 | + private loginProcessingUrl: string = this.route.snapshot.data.loginProcessingUrl; | |
92 | + | |
90 | 93 | constructor(protected store: Store<AppState>, |
94 | + private route: ActivatedRoute, | |
91 | 95 | private oauth2Service: OAuth2Service, |
92 | 96 | private fb: FormBuilder, |
93 | 97 | private dialogService: DialogService, |
... | ... | @@ -130,7 +134,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha |
130 | 134 | return this.oauth2SettingsForm.get('domainsParams') as FormArray; |
131 | 135 | } |
132 | 136 | |
133 | - private formBasicGroup(mapperConfigBasic?: MapperConfigBasic): FormGroup { | |
137 | + private formBasicGroup(type: MapperConfigType, mapperConfigBasic?: MapperConfigBasic): FormGroup { | |
134 | 138 | let tenantNamePattern; |
135 | 139 | if (mapperConfigBasic?.tenantNamePattern) { |
136 | 140 | tenantNamePattern = mapperConfigBasic.tenantNamePattern; |
... | ... | @@ -138,16 +142,20 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha |
138 | 142 | tenantNamePattern = {value: null, disabled: true}; |
139 | 143 | } |
140 | 144 | const basicGroup = this.fb.group({ |
141 | - emailAttributeKey: [mapperConfigBasic?.emailAttributeKey ? mapperConfigBasic.emailAttributeKey : 'email', Validators.required], | |
142 | 145 | firstNameAttributeKey: [mapperConfigBasic?.firstNameAttributeKey ? mapperConfigBasic.firstNameAttributeKey : ''], |
143 | 146 | lastNameAttributeKey: [mapperConfigBasic?.lastNameAttributeKey ? mapperConfigBasic.lastNameAttributeKey : ''], |
144 | 147 | tenantNameStrategy: [mapperConfigBasic?.tenantNameStrategy ? mapperConfigBasic.tenantNameStrategy : TenantNameStrategy.DOMAIN], |
145 | 148 | tenantNamePattern: [tenantNamePattern, Validators.required], |
146 | 149 | customerNamePattern: [mapperConfigBasic?.customerNamePattern ? mapperConfigBasic.customerNamePattern : null], |
147 | 150 | defaultDashboardName: [mapperConfigBasic?.defaultDashboardName ? mapperConfigBasic.defaultDashboardName : null], |
148 | - alwaysFullScreen: [mapperConfigBasic?.alwaysFullScreen ? mapperConfigBasic.alwaysFullScreen : false] | |
151 | + alwaysFullScreen: [isDefinedAndNotNull(mapperConfigBasic?.alwaysFullScreen) ? mapperConfigBasic.alwaysFullScreen : false] | |
149 | 152 | }); |
150 | 153 | |
154 | + if (MapperConfigType.GITHUB !== type) { | |
155 | + basicGroup.addControl('emailAttributeKey', | |
156 | + this.fb.control( mapperConfigBasic?.emailAttributeKey ? mapperConfigBasic.emailAttributeKey : 'email', Validators.required)); | |
157 | + } | |
158 | + | |
151 | 159 | this.subscriptions.push(basicGroup.get('tenantNameStrategy').valueChanges.subscribe((domain) => { |
152 | 160 | if (domain === 'CUSTOM') { |
153 | 161 | basicGroup.get('tenantNamePattern').enable(); |
... | ... | @@ -279,9 +287,12 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha |
279 | 287 | clientRegistration?.userNameAttributeName ? clientRegistration.userNameAttributeName : 'email', Validators.required], |
280 | 288 | mapperConfig: this.fb.group({ |
281 | 289 | allowUserCreation: [ |
282 | - clientRegistration?.mapperConfig?.allowUserCreation ? clientRegistration.mapperConfig.allowUserCreation : true | |
290 | + isDefinedAndNotNull(clientRegistration?.mapperConfig?.allowUserCreation) ? | |
291 | + clientRegistration.mapperConfig.allowUserCreation : true | |
292 | + ], | |
293 | + activateUser: [ | |
294 | + isDefinedAndNotNull(clientRegistration?.mapperConfig?.activateUser) ? clientRegistration.mapperConfig.activateUser : false | |
283 | 295 | ], |
284 | - activateUser: [clientRegistration?.mapperConfig?.activateUser ? clientRegistration.mapperConfig.activateUser : false], | |
285 | 296 | type: [ |
286 | 297 | clientRegistration?.mapperConfig?.type ? clientRegistration.mapperConfig.type : MapperConfigType.BASIC, Validators.required |
287 | 298 | ] |
... | ... | @@ -308,7 +319,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha |
308 | 319 | return clientRegistrationFormGroup; |
309 | 320 | } |
310 | 321 | |
311 | - private validateScope (control: AbstractControl): ValidationErrors | null { | |
322 | + private validateScope(control: AbstractControl): ValidationErrors | null { | |
312 | 323 | const scope: string[] = control.value; |
313 | 324 | if (!scope || !scope.length) { |
314 | 325 | return { |
... | ... | @@ -347,7 +358,11 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha |
347 | 358 | mapperConfig.addControl('custom', this.formCustomGroup(predefinedValue?.custom)); |
348 | 359 | } else { |
349 | 360 | mapperConfig.removeControl('custom'); |
350 | - mapperConfig.addControl('basic', this.formBasicGroup(predefinedValue?.basic)); | |
361 | + if (mapperConfig.get('basic')) { | |
362 | + mapperConfig.setControl('basic', this.formBasicGroup(type, predefinedValue?.basic)); | |
363 | + } else { | |
364 | + mapperConfig.addControl('basic', this.formBasicGroup(type, predefinedValue?.basic)); | |
365 | + } | |
351 | 366 | } |
352 | 367 | } |
353 | 368 | |
... | ... | @@ -490,7 +505,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha |
490 | 505 | } else { |
491 | 506 | protocol = domainInfo.scheme === DomainSchema.MIXED ? DomainSchema.HTTPS.toLowerCase() : domainInfo.scheme.toLowerCase(); |
492 | 507 | } |
493 | - return `${protocol}://${domainInfo.name}/login/oauth2/code/`; | |
508 | + return `${protocol}://${domainInfo.name}${this.loginProcessingUrl}`; | |
494 | 509 | } |
495 | 510 | return ''; |
496 | 511 | } | ... | ... |
... | ... | @@ -41,11 +41,6 @@ |
41 | 41 | </fieldset> |
42 | 42 | </div> |
43 | 43 | <div mat-dialog-actions fxLayoutAlign="end center"> |
44 | - <button mat-raised-button color="primary" | |
45 | - type="submit" | |
46 | - [disabled]="(isLoading$ | async) || widgetFormGroup.invalid"> | |
47 | - {{ 'action.add' | translate }} | |
48 | - </button> | |
49 | 44 | <button mat-button color="primary" |
50 | 45 | type="button" |
51 | 46 | [disabled]="(isLoading$ | async)" |
... | ... | @@ -53,5 +48,10 @@ |
53 | 48 | cdkFocusInitial> |
54 | 49 | {{ 'action.cancel' | translate }} |
55 | 50 | </button> |
51 | + <button mat-raised-button color="primary" | |
52 | + type="submit" | |
53 | + [disabled]="(isLoading$ | async) || widgetFormGroup.invalid"> | |
54 | + {{ 'action.add' | translate }} | |
55 | + </button> | |
56 | 56 | </div> |
57 | 57 | </form> | ... | ... |
... | ... | @@ -152,17 +152,17 @@ |
152 | 152 | </fieldset> |
153 | 153 | </div> |
154 | 154 | <div mat-dialog-actions fxLayoutAlign="end center"> |
155 | - <button mat-raised-button color="primary" | |
156 | - type="submit" | |
157 | - [disabled]="(isLoading$ | async) || settingsFormGroup.invalid || gridSettingsFormGroup.invalid | |
158 | - || (!settingsFormGroup.dirty && !gridSettingsFormGroup.dirty)"> | |
159 | - {{ 'action.save' | translate }} | |
160 | - </button> | |
161 | 155 | <button mat-button color="primary" |
162 | 156 | type="button" |
163 | 157 | [disabled]="(isLoading$ | async)" |
164 | 158 | (click)="cancel()" cdkFocusInitial> |
165 | 159 | {{ 'action.cancel' | translate }} |
166 | 160 | </button> |
161 | + <button mat-raised-button color="primary" | |
162 | + type="submit" | |
163 | + [disabled]="(isLoading$ | async) || settingsFormGroup.invalid || gridSettingsFormGroup.invalid | |
164 | + || (!settingsFormGroup.dirty && !gridSettingsFormGroup.dirty)"> | |
165 | + {{ 'action.save' | translate }} | |
166 | + </button> | |
167 | 167 | </div> |
168 | 168 | </form> | ... | ... |
... | ... | @@ -54,11 +54,6 @@ |
54 | 54 | </fieldset> |
55 | 55 | </div> |
56 | 56 | <div mat-dialog-actions fxLayoutAlign="end center"> |
57 | - <button mat-raised-button color="primary" | |
58 | - type="submit" | |
59 | - [disabled]="(isLoading$ | async) || layoutsFormGroup.invalid || !layoutsFormGroup.dirty"> | |
60 | - {{ 'action.save' | translate }} | |
61 | - </button> | |
62 | 57 | <button mat-button |
63 | 58 | color="primary" |
64 | 59 | type="button" |
... | ... | @@ -66,5 +61,10 @@ |
66 | 61 | (click)="cancel()" cdkFocusInitial> |
67 | 62 | {{ 'action.cancel' | translate }} |
68 | 63 | </button> |
64 | + <button mat-raised-button color="primary" | |
65 | + type="submit" | |
66 | + [disabled]="(isLoading$ | async) || layoutsFormGroup.invalid || !layoutsFormGroup.dirty"> | |
67 | + {{ 'action.save' | translate }} | |
68 | + </button> | |
69 | 69 | </div> |
70 | 70 | </form> | ... | ... |
... | ... | @@ -41,18 +41,17 @@ |
41 | 41 | </div> |
42 | 42 | <div mat-dialog-actions fxLayout="row"> |
43 | 43 | <span fxFlex></span> |
44 | - <button mat-button mat-raised-button color="primary" | |
45 | - type="submit" | |
46 | - [disabled]="(isLoading$ | async) || dashboardCustomersFormGroup.invalid | |
47 | - || !dashboardCustomersFormGroup.dirty"> | |
48 | - {{ actionName | translate }} | |
49 | - </button> | |
50 | 44 | <button mat-button color="primary" |
51 | - style="margin-right: 20px;" | |
52 | 45 | type="button" |
53 | 46 | [disabled]="(isLoading$ | async)" |
54 | 47 | (click)="cancel()" cdkFocusInitial> |
55 | 48 | {{ 'action.cancel' | translate }} |
56 | 49 | </button> |
50 | + <button mat-button mat-raised-button color="primary" | |
51 | + type="submit" | |
52 | + [disabled]="(isLoading$ | async) || dashboardCustomersFormGroup.invalid | |
53 | + || !dashboardCustomersFormGroup.dirty"> | |
54 | + {{ actionName | translate }} | |
55 | + </button> | |
57 | 56 | </div> |
58 | 57 | </form> | ... | ... |
... | ... | @@ -52,16 +52,16 @@ |
52 | 52 | </fieldset> |
53 | 53 | </div> |
54 | 54 | <div mat-dialog-actions fxLayoutAlign="end center"> |
55 | - <button mat-raised-button color="primary" | |
56 | - type="submit" | |
57 | - [disabled]="(isLoading$ | async) || stateFormGroup.invalid || !stateFormGroup.dirty"> | |
58 | - {{ (isAdd ? 'action.add' : 'action.save') | translate }} | |
59 | - </button> | |
60 | 55 | <button mat-button color="primary" |
61 | 56 | type="button" |
62 | 57 | [disabled]="(isLoading$ | async)" |
63 | 58 | (click)="cancel()" cdkFocusInitial> |
64 | 59 | {{ 'action.cancel' | translate }} |
65 | 60 | </button> |
61 | + <button mat-raised-button color="primary" | |
62 | + type="submit" | |
63 | + [disabled]="(isLoading$ | async) || stateFormGroup.invalid || !stateFormGroup.dirty"> | |
64 | + {{ (isAdd ? 'action.add' : 'action.save') | translate }} | |
65 | + </button> | |
66 | 66 | </div> |
67 | 67 | </form> | ... | ... |
... | ... | @@ -136,16 +136,16 @@ |
136 | 136 | </fieldset> |
137 | 137 | </div> |
138 | 138 | <div mat-dialog-actions fxLayoutAlign="end center"> |
139 | - <button mat-raised-button color="primary" | |
140 | - type="submit" | |
141 | - [disabled]="(isLoading$ | async) || statesFormGroup.invalid || !statesFormGroup.dirty"> | |
142 | - {{ 'action.save' | translate }} | |
143 | - </button> | |
144 | 139 | <button mat-button color="primary" |
145 | 140 | type="button" |
146 | 141 | [disabled]="(isLoading$ | async)" |
147 | 142 | (click)="cancel()" cdkFocusInitial> |
148 | 143 | {{ 'action.cancel' | translate }} |
149 | 144 | </button> |
145 | + <button mat-raised-button color="primary" | |
146 | + type="submit" | |
147 | + [disabled]="(isLoading$ | async) || statesFormGroup.invalid || !statesFormGroup.dirty"> | |
148 | + {{ 'action.save' | translate }} | |
149 | + </button> | |
150 | 150 | </div> |
151 | 151 | </form> | ... | ... |
... | ... | @@ -70,7 +70,7 @@ |
70 | 70 | </div> |
71 | 71 | </div> |
72 | 72 | </mat-tab> |
73 | -<mat-tab *ngIf="entity" | |
73 | +<mat-tab *ngIf="entity && !isEdit" | |
74 | 74 | label="{{ 'audit-log.audit-logs' | translate }}" #auditLogsTab="matTab"> |
75 | 75 | <tb-audit-log-table detailsMode="true" [active]="auditLogsTab.isActive" [auditLogMode]="auditLogModes.ENTITY" [entityId]="entity.id"></tb-audit-log-table> |
76 | 76 | </mat-tab> | ... | ... |
... | ... | @@ -36,17 +36,17 @@ |
36 | 36 | </fieldset> |
37 | 37 | </div> |
38 | 38 | <div mat-dialog-actions fxLayoutAlign="end center"> |
39 | - <button *ngIf="!isReadOnly" mat-raised-button color="primary" | |
40 | - type="submit" | |
41 | - [disabled]="(isLoading$ | async) || deviceCredentialsFormGroup.invalid | |
42 | - || !deviceCredentialsFormGroup.dirty"> | |
43 | - {{ 'action.save' | translate }} | |
44 | - </button> | |
45 | 39 | <button mat-button color="primary" |
46 | 40 | type="button" |
47 | 41 | [disabled]="(isLoading$ | async)" |
48 | 42 | (click)="cancel()" cdkFocusInitial> |
49 | 43 | {{ (isReadOnly ? 'action.close' : 'action.cancel') | translate }} |
50 | 44 | </button> |
45 | + <button *ngIf="!isReadOnly" mat-raised-button color="primary" | |
46 | + type="submit" | |
47 | + [disabled]="(isLoading$ | async) || deviceCredentialsFormGroup.invalid | |
48 | + || !deviceCredentialsFormGroup.dirty"> | |
49 | + {{ 'action.save' | translate }} | |
50 | + </button> | |
51 | 51 | </div> |
52 | 52 | </form> | ... | ... |
... | ... | @@ -46,16 +46,16 @@ |
46 | 46 | </mat-form-field> |
47 | 47 | </div> |
48 | 48 | <div mat-dialog-actions fxLayout="row" fxLayoutAlign="end center"> |
49 | - <button mat-raised-button color="primary" | |
50 | - type="submit" | |
51 | - [disabled]="(isLoading$ | async) || changePassword.invalid"> | |
52 | - {{ 'profile.change-password' | translate }} | |
53 | - </button> | |
54 | 49 | <button mat-button color="primary" |
55 | 50 | type="button" |
56 | 51 | [disabled]="(isLoading$ | async)" |
57 | 52 | [mat-dialog-close]="false" cdkFocusInitial> |
58 | 53 | {{ 'action.cancel' | translate }} |
59 | 54 | </button> |
55 | + <button mat-raised-button color="primary" | |
56 | + type="submit" | |
57 | + [disabled]="(isLoading$ | async) || changePassword.invalid"> | |
58 | + {{ 'profile.change-password' | translate }} | |
59 | + </button> | |
60 | 60 | </div> |
61 | 61 | </form> | ... | ... |
... | ... | @@ -40,16 +40,16 @@ |
40 | 40 | </fieldset> |
41 | 41 | </div> |
42 | 42 | <div mat-dialog-actions fxLayoutAlign="end center"> |
43 | - <button mat-raised-button color="primary" | |
44 | - type="submit" | |
45 | - [disabled]="(isLoading$ | async) || tbRuleNode.ruleNodeFormGroup.invalid || !tbRuleNode.ruleNodeFormGroup.dirty"> | |
46 | - {{ 'action.add' | translate }} | |
47 | - </button> | |
48 | 43 | <button mat-button color="primary" |
49 | 44 | type="button" |
50 | 45 | [disabled]="(isLoading$ | async)" |
51 | 46 | (click)="cancel()" cdkFocusInitial> |
52 | 47 | {{ 'action.cancel' | translate }} |
53 | 48 | </button> |
49 | + <button mat-raised-button color="primary" | |
50 | + type="submit" | |
51 | + [disabled]="(isLoading$ | async) || tbRuleNode.ruleNodeFormGroup.invalid || !tbRuleNode.ruleNodeFormGroup.dirty"> | |
52 | + {{ 'action.add' | translate }} | |
53 | + </button> | |
54 | 54 | </div> |
55 | 55 | </form> | ... | ... |
... | ... | @@ -39,16 +39,16 @@ |
39 | 39 | </fieldset> |
40 | 40 | </div> |
41 | 41 | <div mat-dialog-actions fxLayoutAlign="end center"> |
42 | - <button mat-raised-button color="primary" | |
43 | - type="submit" | |
44 | - [disabled]="(isLoading$ | async) || ruleNodeLinkFormGroup.invalid || !ruleNodeLinkFormGroup.dirty"> | |
45 | - {{ 'action.add' | translate }} | |
46 | - </button> | |
47 | 42 | <button mat-button color="primary" |
48 | 43 | type="button" |
49 | 44 | [disabled]="(isLoading$ | async)" |
50 | 45 | (click)="cancel()" cdkFocusInitial> |
51 | 46 | {{ 'action.cancel' | translate }} |
52 | 47 | </button> |
48 | + <button mat-raised-button color="primary" | |
49 | + type="submit" | |
50 | + [disabled]="(isLoading$ | async) || ruleNodeLinkFormGroup.invalid || !ruleNodeLinkFormGroup.dirty"> | |
51 | + {{ 'action.add' | translate }} | |
52 | + </button> | |
53 | 53 | </div> |
54 | 54 | </form> | ... | ... |
... | ... | @@ -41,11 +41,6 @@ |
41 | 41 | </mat-form-field> |
42 | 42 | </div> |
43 | 43 | <div mat-dialog-actions fxLayoutAlign="end center"> |
44 | - <button mat-raised-button color="primary" | |
45 | - type="submit" | |
46 | - [disabled]="(isLoading$ | async) || detailsForm.invalid || !detailsForm.dirty"> | |
47 | - {{ 'action.add' | translate }} | |
48 | - </button> | |
49 | 44 | <button mat-button color="primary" |
50 | 45 | type="button" |
51 | 46 | cdkFocusInitial |
... | ... | @@ -53,5 +48,10 @@ |
53 | 48 | (click)="cancel()"> |
54 | 49 | {{ 'action.cancel' | translate }} |
55 | 50 | </button> |
51 | + <button mat-raised-button color="primary" | |
52 | + type="submit" | |
53 | + [disabled]="(isLoading$ | async) || detailsForm.invalid || !detailsForm.dirty"> | |
54 | + {{ 'action.add' | translate }} | |
55 | + </button> | |
56 | 56 | </div> |
57 | 57 | </form> | ... | ... |
... | ... | @@ -46,17 +46,17 @@ |
46 | 46 | </fieldset> |
47 | 47 | </div> |
48 | 48 | <div mat-dialog-actions fxLayoutAlign="end center"> |
49 | - <button mat-raised-button color="primary" | |
50 | - type="submit" | |
51 | - [disabled]="(isLoading$ | async) || saveWidgetTypeAsFormGroup.invalid | |
52 | - || !saveWidgetTypeAsFormGroup.dirty"> | |
53 | - {{ 'action.saveAs' | translate }} | |
54 | - </button> | |
55 | 49 | <button mat-button color="primary" |
56 | 50 | type="button" |
57 | 51 | [disabled]="(isLoading$ | async)" |
58 | 52 | (click)="cancel()" cdkFocusInitial> |
59 | 53 | {{ 'action.cancel' | translate }} |
60 | 54 | </button> |
55 | + <button mat-raised-button color="primary" | |
56 | + type="submit" | |
57 | + [disabled]="(isLoading$ | async) || saveWidgetTypeAsFormGroup.invalid | |
58 | + || !saveWidgetTypeAsFormGroup.dirty"> | |
59 | + {{ 'action.saveAs' | translate }} | |
60 | + </button> | |
61 | 61 | </div> |
62 | 62 | </form> | ... | ... |
... | ... | @@ -41,17 +41,16 @@ |
41 | 41 | </div> |
42 | 42 | <div mat-dialog-actions fxLayout="row" fxLayoutAlign="end center"> |
43 | 43 | <span fxFlex></span> |
44 | - <button mat-button mat-raised-button color="primary" | |
45 | - type="submit" | |
46 | - [disabled]="(isLoading$ | async) || jsonFormGroup.invalid || !jsonFormGroup.dirty"> | |
47 | - {{ 'action.save' | translate }} | |
48 | - </button> | |
49 | 44 | <button mat-button color="primary" |
50 | - style="margin-right: 20px;" | |
51 | 45 | type="button" |
52 | 46 | [disabled]="(isLoading$ | async)" |
53 | 47 | (click)="cancel()" cdkFocusInitial> |
54 | 48 | {{ 'action.cancel' | translate }} |
55 | 49 | </button> |
50 | + <button mat-button mat-raised-button color="primary" | |
51 | + type="submit" | |
52 | + [disabled]="(isLoading$ | async) || jsonFormGroup.invalid || !jsonFormGroup.dirty"> | |
53 | + {{ 'action.save' | translate }} | |
54 | + </button> | |
56 | 55 | </div> |
57 | 56 | </form> | ... | ... |
... | ... | @@ -109,18 +109,17 @@ |
109 | 109 | {{ 'rulenode.test' | translate }} |
110 | 110 | </button> |
111 | 111 | <span fxFlex></span> |
112 | - <button mat-button mat-raised-button color="primary" | |
113 | - type="submit" | |
114 | - [disabled]="(isLoading$ | async) || nodeScriptTestFormGroup.get('script').invalid || !nodeScriptTestFormGroup.get('script').dirty"> | |
115 | - {{ 'action.save' | translate }} | |
116 | - </button> | |
117 | 112 | <button mat-button color="primary" |
118 | - style="margin-right: 20px;" | |
119 | 113 | type="button" |
120 | 114 | cdkFocusInitial |
121 | 115 | [disabled]="(isLoading$ | async)" |
122 | 116 | (click)="cancel()"> |
123 | 117 | {{ 'action.cancel' | translate }} |
124 | 118 | </button> |
119 | + <button mat-button mat-raised-button color="primary" | |
120 | + type="submit" | |
121 | + [disabled]="(isLoading$ | async) || nodeScriptTestFormGroup.get('script').invalid || !nodeScriptTestFormGroup.get('script').dirty"> | |
122 | + {{ 'action.save' | translate }} | |
123 | + </button> | |
125 | 124 | </div> |
126 | 125 | </form> | ... | ... |
... | ... | @@ -140,19 +140,18 @@ |
140 | 140 | </tb-timeinterval> |
141 | 141 | </div> |
142 | 142 | <div fxLayout="row" class="tb-panel-actions" fxLayoutAlign="end center"> |
143 | + <button type="button" | |
144 | + mat-button | |
145 | + [disabled]="(isLoading$ | async)" | |
146 | + (click)="cancel()"> | |
147 | + {{ 'action.cancel' | translate }} | |
148 | + </button> | |
143 | 149 | <button type="submit" |
144 | 150 | mat-raised-button |
145 | 151 | color="primary" |
146 | 152 | [disabled]="(isLoading$ | async) || timewindowForm.invalid || !timewindowForm.dirty"> |
147 | 153 | {{ 'action.update' | translate }} |
148 | 154 | </button> |
149 | - <button type="button" | |
150 | - mat-button | |
151 | - [disabled]="(isLoading$ | async)" | |
152 | - (click)="cancel()" | |
153 | - style="margin-right: 20px;"> | |
154 | - {{ 'action.cancel' | translate }} | |
155 | - </button> | |
156 | 155 | </div> |
157 | 156 | </div> |
158 | 157 | </fieldset> | ... | ... |
... | ... | @@ -26,7 +26,7 @@ import { EntityInfoData } from '@shared/models/entity.models'; |
26 | 26 | import { KeyFilter } from '@shared/models/query/query.models'; |
27 | 27 | import { TimeUnit } from '@shared/models/time/time.models'; |
28 | 28 | import * as _moment from 'moment-timezone'; |
29 | -import { AbstractControl, FormGroup } from '@angular/forms'; | |
29 | +import { AbstractControl, FormGroup, ValidationErrors } from '@angular/forms'; | |
30 | 30 | |
31 | 31 | export enum DeviceProfileType { |
32 | 32 | DEFAULT = 'DEFAULT' |
... | ... | @@ -87,7 +87,7 @@ export const deviceProvisionTypeTranslationMap = new Map<DeviceProvisionType, st |
87 | 87 | [DeviceProvisionType.ALLOW_CREATE_NEW_DEVICES, 'device-profile.provision-strategy-created-new'], |
88 | 88 | [DeviceProvisionType.CHECK_PRE_PROVISIONED_DEVICES, 'device-profile.provision-strategy-check-pre-provisioned'] |
89 | 89 | ] |
90 | -) | |
90 | +); | |
91 | 91 | |
92 | 92 | export const deviceTransportTypeHintMap = new Map<DeviceTransportType, string>( |
93 | 93 | [ |
... | ... | @@ -303,6 +303,18 @@ export interface AlarmRule { |
303 | 303 | schedule?: AlarmSchedule; |
304 | 304 | } |
305 | 305 | |
306 | +export function alarmRuleValidator(control: AbstractControl): ValidationErrors | null { | |
307 | + const alarmRule: AlarmRule = control.value; | |
308 | + return alarmRuleValid(alarmRule) ? null : {alarmRule: true}; | |
309 | +} | |
310 | + | |
311 | +function alarmRuleValid(alarmRule: AlarmRule): boolean { | |
312 | + if (!alarmRule || !alarmRule.condition || !alarmRule.condition.condition || !alarmRule.condition.condition.length) { | |
313 | + return false; | |
314 | + } | |
315 | + return true; | |
316 | +} | |
317 | + | |
306 | 318 | export interface DeviceProfileAlarm { |
307 | 319 | id: string; |
308 | 320 | alarmType: string; |
... | ... | @@ -312,6 +324,34 @@ export interface DeviceProfileAlarm { |
312 | 324 | propagateRelationTypes?: Array<string>; |
313 | 325 | } |
314 | 326 | |
327 | +export function deviceProfileAlarmValidator(control: AbstractControl): ValidationErrors | null { | |
328 | + const deviceProfileAlarm: DeviceProfileAlarm = control.value; | |
329 | + if (deviceProfileAlarm && deviceProfileAlarm.id && deviceProfileAlarm.alarmType && | |
330 | + deviceProfileAlarm.createRules) { | |
331 | + const severities = Object.keys(deviceProfileAlarm.createRules); | |
332 | + if (severities.length) { | |
333 | + let alarmRulesValid = true; | |
334 | + for (const severity of severities) { | |
335 | + const alarmRule = deviceProfileAlarm.createRules[severity]; | |
336 | + if (!alarmRuleValid(alarmRule)) { | |
337 | + alarmRulesValid = false; | |
338 | + break; | |
339 | + } | |
340 | + } | |
341 | + if (alarmRulesValid) { | |
342 | + if (deviceProfileAlarm.clearRule && !alarmRuleValid(deviceProfileAlarm.clearRule)) { | |
343 | + alarmRulesValid = false; | |
344 | + } | |
345 | + } | |
346 | + if (alarmRulesValid) { | |
347 | + return null; | |
348 | + } | |
349 | + } | |
350 | + } | |
351 | + return {deviceProfileAlarm: true}; | |
352 | +} | |
353 | + | |
354 | + | |
315 | 355 | export interface DeviceProfileData { |
316 | 356 | configuration: DeviceProfileConfiguration; |
317 | 357 | transportConfiguration: DeviceProfileTransportConfiguration; | ... | ... |
... | ... | @@ -908,6 +908,7 @@ |
908 | 908 | "create-alarm-pattern": "Create <b>{{alarmType}}</b> alarm", |
909 | 909 | "create-alarm-rules": "Create alarm rules", |
910 | 910 | "no-create-alarm-rules": "No create conditions configured", |
911 | + "add-create-alarm-rule-prompt": "Please add create alarm rule", | |
911 | 912 | "clear-alarm-rule": "Clear alarm rule", |
912 | 913 | "no-clear-alarm-rule": "No clear condition configured", |
913 | 914 | "add-create-alarm-rule": "Add create condition", | ... | ... |