Merge branch 'develop/3.4' into refactoring_tests_alarm_customer
refactoring: alarm resolving the conflict
This commit is contained in:
commit
eceec2af0c
@ -19,6 +19,8 @@ server:
|
|||||||
address: "${HTTP_BIND_ADDRESS:0.0.0.0}"
|
address: "${HTTP_BIND_ADDRESS:0.0.0.0}"
|
||||||
# Server bind port
|
# Server bind port
|
||||||
port: "${HTTP_BIND_PORT:8080}"
|
port: "${HTTP_BIND_PORT:8080}"
|
||||||
|
# Server forward headers strategy
|
||||||
|
forward_headers_strategy: "${HTTP_FORWARD_HEADERS_STRATEGY:NONE}"
|
||||||
# Server SSL configuration
|
# Server SSL configuration
|
||||||
ssl:
|
ssl:
|
||||||
# Enable/disable SSL support
|
# Enable/disable SSL support
|
||||||
|
|||||||
@ -32,8 +32,6 @@ import org.thingsboard.server.common.msg.TbMsg;
|
|||||||
import org.thingsboard.server.dao.audit.AuditLogService;
|
import org.thingsboard.server.dao.audit.AuditLogService;
|
||||||
import org.thingsboard.server.dao.model.ModelConstants;
|
import org.thingsboard.server.dao.model.ModelConstants;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.io.StringWriter;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
@ -49,40 +47,47 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
|
|||||||
@SpyBean
|
@SpyBean
|
||||||
protected AuditLogService auditLogService;
|
protected AuditLogService auditLogService;
|
||||||
|
|
||||||
protected void testNotifyEntityOne(HasName entity, EntityId entityId, EntityId originatorId,
|
protected void testNotifyEntityAllOneTime(HasName entity, EntityId entityId, EntityId originatorId,
|
||||||
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
|
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
|
||||||
ActionType actionType, Object... additionalInfo) {
|
ActionType actionType, Object... additionalInfo) {
|
||||||
testSendNotificationMsgToEdgeServiceOne(entityId, tenantId, actionType);
|
testSendNotificationMsgToEdgeServiceOneTime(entityId, tenantId, actionType);
|
||||||
testLogEntityActionOne(entity, originatorId, tenantId, customerId, userId, userName, actionType, additionalInfo);
|
testLogEntityActionOneTime(entity, originatorId, tenantId, customerId, userId, userName, actionType, additionalInfo);
|
||||||
testPushMsgToRuleEngineOne(originatorId, tenantId);
|
testPushMsgToRuleEngineOneTime(originatorId, tenantId);
|
||||||
Mockito.reset(tbClusterService, auditLogService);
|
Mockito.reset(tbClusterService, auditLogService);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void testNotifyEntityDeleteOneMsgToEdgeServiceNever(HasName entity, EntityId entityId, EntityId originatorId,
|
protected void testNotifyEntityDeleteOneTimeMsgToEdgeServiceNever(HasName entity, EntityId entityId, EntityId originatorId,
|
||||||
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
|
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
|
||||||
ActionType actionType, Object... additionalInfo) {
|
ActionType actionType, Object... additionalInfo) {
|
||||||
testNotificationMsgToEdgeServiceNever(entityId);
|
testNotificationMsgToEdgeServiceNever(entityId);
|
||||||
testLogEntityActionOne(entity, originatorId, tenantId, customerId, userId, userName, actionType, additionalInfo);
|
testLogEntityActionOneTime(entity, originatorId, tenantId, customerId, userId, userName, actionType, additionalInfo);
|
||||||
testPushMsgToRuleEngineOne(entityId, tenantId);
|
testPushMsgToRuleEngineOneTime(entityId, tenantId);
|
||||||
testBroadcastEntityStateChangeEventOne(entityId, tenantId);
|
testBroadcastEntityStateChangeEventOneTime(entityId, tenantId);
|
||||||
|
Mockito.reset(tbClusterService, auditLogService);
|
||||||
}
|
}
|
||||||
|
protected void testNotifyEntityNeverMsgToEdgeServiceOneTime(HasName entity, EntityId entityId, TenantId tenantId, ActionType actionType) {
|
||||||
protected void testNotifyEntityOneMsgToEdgeServiceNever(HasName entity, EntityId entityId, EntityId originatorId,
|
testSendNotificationMsgToEdgeServiceOneTime(entityId, tenantId, actionType);
|
||||||
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
|
testLogEntityActionNever(entityId, entity);
|
||||||
ActionType actionType, Object... additionalInfo) {
|
testPushMsgToRuleEngineNever(entityId);
|
||||||
testNotificationMsgToEdgeServiceNever(entityId);
|
|
||||||
testLogEntityActionOne(entity, originatorId, tenantId, customerId, userId, userName, actionType, additionalInfo);
|
|
||||||
testPushMsgToRuleEngineOne(originatorId, tenantId);
|
|
||||||
Mockito.reset(tbClusterService, auditLogService);
|
Mockito.reset(tbClusterService, auditLogService);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void testNotifyEntityBroadcastEntityStateChangeEventOneMsgToEdgeServiceNever(HasName entity, EntityId entityId, EntityId originatorId,
|
protected void testNotifyEntityOneTimeMsgToEdgeServiceNever(HasName entity, EntityId entityId, EntityId originatorId,
|
||||||
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
|
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
|
||||||
ActionType actionType, Object... additionalInfo) {
|
ActionType actionType, Object... additionalInfo) {
|
||||||
testNotificationMsgToEdgeServiceNever(entityId);
|
testNotificationMsgToEdgeServiceNever(entityId);
|
||||||
testLogEntityActionOne(entity, originatorId, tenantId, customerId, userId, userName, actionType, additionalInfo);
|
testLogEntityActionOneTime(entity, originatorId, tenantId, customerId, userId, userName, actionType, additionalInfo);
|
||||||
testPushMsgToRuleEngineOne(originatorId, tenantId);
|
testPushMsgToRuleEngineOneTime(originatorId, tenantId);
|
||||||
testBroadcastEntityStateChangeEventOne(entityId, tenantId);
|
Mockito.reset(tbClusterService, auditLogService);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void testNotifyEntityBroadcastEntityStateChangeEventOneTimeMsgToEdgeServiceNever(HasName entity, EntityId entityId, EntityId originatorId,
|
||||||
|
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
|
||||||
|
ActionType actionType, Object... additionalInfo) {
|
||||||
|
testNotificationMsgToEdgeServiceNever(entityId);
|
||||||
|
testLogEntityActionOneTime(entity, originatorId, tenantId, customerId, userId, userName, actionType, additionalInfo);
|
||||||
|
testPushMsgToRuleEngineOneTime(originatorId, tenantId);
|
||||||
|
testBroadcastEntityStateChangeEventOneTime(entityId, tenantId);
|
||||||
Mockito.reset(tbClusterService, auditLogService);
|
Mockito.reset(tbClusterService, auditLogService);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,8 +112,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
|
|||||||
Mockito.argThat(argument ->
|
Mockito.argThat(argument ->
|
||||||
argument.getMessage().equals(exp.getMessage())));
|
argument.getMessage().equals(exp.getMessage())));
|
||||||
}
|
}
|
||||||
Mockito.verify(tbClusterService, never()).pushMsgToRuleEngine(Mockito.any(), Mockito.any(entity_NULL_UUID.getClass()),
|
testPushMsgToRuleEngineNever(entity_NULL_UUID);
|
||||||
Mockito.any(), Mockito.any());
|
|
||||||
Mockito.reset(tbClusterService, auditLogService);
|
Mockito.reset(tbClusterService, auditLogService);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,26 +120,27 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
|
|||||||
testNotificationMsgToEdgeServiceNever(entityId);
|
testNotificationMsgToEdgeServiceNever(entityId);
|
||||||
testLogEntityActionNever(entityId, entity);
|
testLogEntityActionNever(entityId, entity);
|
||||||
testPushMsgToRuleEngineNever(entityId);
|
testPushMsgToRuleEngineNever(entityId);
|
||||||
|
Mockito.reset(tbClusterService, auditLogService);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void testNotificationMsgToEdgeServiceNever(EntityId entityId) {
|
private void testNotificationMsgToEdgeServiceNever(EntityId entityId) {
|
||||||
Mockito.verify(tbClusterService, never()).sendNotificationMsgToEdgeService(Mockito.any(),
|
Mockito.verify(tbClusterService, never()).sendNotificationMsgToEdgeService(Mockito.any(),
|
||||||
Mockito.any(), Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any(), Mockito.any());
|
Mockito.any(), Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any(), Mockito.any());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void testLogEntityActionNever(EntityId entityId, HasName entity) {
|
private void testLogEntityActionNever(EntityId entityId, HasName entity) {
|
||||||
Mockito.verify(auditLogService, never()).logEntityAction(Mockito.any(), Mockito.any(),
|
Mockito.verify(auditLogService, never()).logEntityAction(Mockito.any(), Mockito.any(),
|
||||||
Mockito.any(), Mockito.any(), Mockito.any(entityId.getClass()), Mockito.any(entity.getClass()),
|
Mockito.any(), Mockito.any(), Mockito.any(entityId.getClass()), Mockito.any(entity.getClass()),
|
||||||
Mockito.any(), Mockito.any());
|
Mockito.any(), Mockito.any());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void testPushMsgToRuleEngineNever(EntityId entityId) {
|
private void testPushMsgToRuleEngineNever(EntityId entityId) {
|
||||||
Mockito.verify(tbClusterService, never()).pushMsgToRuleEngine(Mockito.any(),
|
Mockito.verify(tbClusterService, never()).pushMsgToRuleEngine(Mockito.any(),
|
||||||
Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any());
|
Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testLogEntityActionOne(HasName entity, EntityId originatorId, TenantId tenantId, CustomerId customerId,
|
private void testLogEntityActionOneTime(HasName entity, EntityId originatorId, TenantId tenantId, CustomerId customerId,
|
||||||
UserId userId, String userName, ActionType actionType, Object... additionalInfo) {
|
UserId userId, String userName, ActionType actionType, Object... additionalInfo) {
|
||||||
if (additionalInfo.length == 0) {
|
if (additionalInfo.length == 0) {
|
||||||
Mockito.verify(auditLogService, times(1)).logEntityAction(Mockito.eq(tenantId), Mockito.eq(customerId),
|
Mockito.verify(auditLogService, times(1)).logEntityAction(Mockito.eq(tenantId), Mockito.eq(customerId),
|
||||||
Mockito.eq(userId), Mockito.eq(userName), Mockito.eq(originatorId),
|
Mockito.eq(userId), Mockito.eq(userName), Mockito.eq(originatorId),
|
||||||
@ -148,18 +153,18 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testPushMsgToRuleEngineOne(EntityId originatorId, TenantId tenantId) {
|
private void testPushMsgToRuleEngineOneTime(EntityId originatorId, TenantId tenantId) {
|
||||||
Mockito.verify(tbClusterService, times(1)).pushMsgToRuleEngine(Mockito.eq(tenantId),
|
Mockito.verify(tbClusterService, times(1)).pushMsgToRuleEngine(Mockito.eq(tenantId),
|
||||||
Mockito.eq(originatorId), Mockito.any(TbMsg.class), Mockito.isNull());
|
Mockito.eq(originatorId), Mockito.any(TbMsg.class), Mockito.isNull());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testSendNotificationMsgToEdgeServiceOne(EntityId entityId, TenantId tenantId, ActionType actionType) {
|
private void testSendNotificationMsgToEdgeServiceOneTime(EntityId entityId, TenantId tenantId, ActionType actionType) {
|
||||||
Mockito.verify(tbClusterService, times(1)).sendNotificationMsgToEdgeService(Mockito.eq(tenantId),
|
Mockito.verify(tbClusterService, times(1)).sendNotificationMsgToEdgeService(Mockito.eq(tenantId),
|
||||||
Mockito.isNull(), Mockito.eq(entityId), Mockito.isNull(), Mockito.isNull(),
|
Mockito.isNull(), Mockito.eq(entityId), Mockito.isNull(), Mockito.isNull(),
|
||||||
Mockito.eq(edgeTypeByActionType(actionType)));
|
Mockito.eq(edgeTypeByActionType(actionType)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testBroadcastEntityStateChangeEventOne(EntityId entityId, TenantId tenantId) {
|
private void testBroadcastEntityStateChangeEventOneTime(EntityId entityId, TenantId tenantId) {
|
||||||
Mockito.verify(tbClusterService, times(1)).broadcastEntityStateChangeEvent(Mockito.eq(tenantId),
|
Mockito.verify(tbClusterService, times(1)).broadcastEntityStateChangeEvent(Mockito.eq(tenantId),
|
||||||
Mockito.any(entityId.getClass()), Mockito.any(ComponentLifecycleEvent.class));
|
Mockito.any(entityId.getClass()), Mockito.any(ComponentLifecycleEvent.class));
|
||||||
}
|
}
|
||||||
@ -174,10 +179,4 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getFailureStack(Exception e) {
|
|
||||||
StringWriter sw = new StringWriter();
|
|
||||||
e.printStackTrace(new PrintWriter(sw));
|
|
||||||
return sw.toString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -107,6 +107,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
|
|||||||
protected ObjectMapper mapper = new ObjectMapper();
|
protected ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
protected static final String TEST_TENANT_NAME = "TEST TENANT";
|
protected static final String TEST_TENANT_NAME = "TEST TENANT";
|
||||||
|
protected static final String TEST_DIFFERENT_TENANT_NAME = "TEST DIFFERENT TENANT";
|
||||||
|
|
||||||
protected static final String SYS_ADMIN_EMAIL = "sysadmin@thingsboard.org";
|
protected static final String SYS_ADMIN_EMAIL = "sysadmin@thingsboard.org";
|
||||||
private static final String SYS_ADMIN_PASSWORD = "sysadmin";
|
private static final String SYS_ADMIN_PASSWORD = "sysadmin";
|
||||||
@ -114,9 +115,15 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
|
|||||||
protected static final String TENANT_ADMIN_EMAIL = "testtenant@thingsboard.org";
|
protected static final String TENANT_ADMIN_EMAIL = "testtenant@thingsboard.org";
|
||||||
protected static final String TENANT_ADMIN_PASSWORD = "tenant";
|
protected static final String TENANT_ADMIN_PASSWORD = "tenant";
|
||||||
|
|
||||||
|
protected static final String DIFFERENT_TENANT_ADMIN_EMAIL = "testdifftenant@thingsboard.org";
|
||||||
|
private static final String DIFFERENT_TENANT_ADMIN_PASSWORD = "difftenant";
|
||||||
|
|
||||||
protected static final String CUSTOMER_USER_EMAIL = "testcustomer@thingsboard.org";
|
protected static final String CUSTOMER_USER_EMAIL = "testcustomer@thingsboard.org";
|
||||||
private static final String CUSTOMER_USER_PASSWORD = "customer";
|
private static final String CUSTOMER_USER_PASSWORD = "customer";
|
||||||
|
|
||||||
|
protected static final String DIFFERENT_CUSTOMER_USER_EMAIL = "testdifferentcustomer@thingsboard.org";
|
||||||
|
private static final String DIFFERENT_CUSTOMER_USER_PASSWORD = "diffcustomer";
|
||||||
|
|
||||||
/** See {@link org.springframework.test.web.servlet.DefaultMvcResult#getAsyncResult(long)}
|
/** See {@link org.springframework.test.web.servlet.DefaultMvcResult#getAsyncResult(long)}
|
||||||
* and {@link org.springframework.mock.web.MockAsyncContext#getTimeout()}
|
* and {@link org.springframework.mock.web.MockAsyncContext#getTimeout()}
|
||||||
*/
|
*/
|
||||||
@ -134,6 +141,8 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
|
|||||||
protected UserId tenantAdminUserId;
|
protected UserId tenantAdminUserId;
|
||||||
protected CustomerId tenantAdminCustomerId;
|
protected CustomerId tenantAdminCustomerId;
|
||||||
protected CustomerId customerId;
|
protected CustomerId customerId;
|
||||||
|
protected TenantId differentTenantId;
|
||||||
|
protected CustomerId differentCustomerId;
|
||||||
protected UserId customerUserId;
|
protected UserId customerUserId;
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
@ -272,36 +281,52 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
|
|||||||
private Customer savedDifferentCustomer;
|
private Customer savedDifferentCustomer;
|
||||||
|
|
||||||
protected void loginDifferentTenant() throws Exception {
|
protected void loginDifferentTenant() throws Exception {
|
||||||
loginSysAdmin();
|
|
||||||
if (savedDifferentTenant != null) {
|
if (savedDifferentTenant != null) {
|
||||||
deleteDifferentTenant();
|
login(savedDifferentTenant.getEmail(), TENANT_ADMIN_PASSWORD);
|
||||||
|
} else {
|
||||||
|
loginSysAdmin();
|
||||||
|
|
||||||
|
Tenant tenant = new Tenant();
|
||||||
|
tenant.setTitle(TEST_DIFFERENT_TENANT_NAME);
|
||||||
|
savedDifferentTenant = doPost("/api/tenant", tenant, Tenant.class);
|
||||||
|
differentTenantId = savedDifferentTenant.getId();
|
||||||
|
Assert.assertNotNull(savedDifferentTenant);
|
||||||
|
User differentTenantAdmin = new User();
|
||||||
|
differentTenantAdmin.setAuthority(Authority.TENANT_ADMIN);
|
||||||
|
differentTenantAdmin.setTenantId(savedDifferentTenant.getId());
|
||||||
|
differentTenantAdmin.setEmail(DIFFERENT_TENANT_ADMIN_EMAIL);
|
||||||
|
|
||||||
|
createUserAndLogin(differentTenantAdmin, DIFFERENT_TENANT_ADMIN_PASSWORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
Tenant tenant = new Tenant();
|
|
||||||
tenant.setTitle("Different tenant");
|
|
||||||
savedDifferentTenant = doPost("/api/tenant", tenant, Tenant.class);
|
|
||||||
Assert.assertNotNull(savedDifferentTenant);
|
|
||||||
User differentTenantAdmin = new User();
|
|
||||||
differentTenantAdmin.setAuthority(Authority.TENANT_ADMIN);
|
|
||||||
differentTenantAdmin.setTenantId(savedDifferentTenant.getId());
|
|
||||||
differentTenantAdmin.setEmail("different_tenant@thingsboard.org");
|
|
||||||
|
|
||||||
createUserAndLogin(differentTenantAdmin, "testPassword");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void loginDifferentCustomer() throws Exception {
|
protected void loginDifferentCustomer() throws Exception {
|
||||||
|
if (savedDifferentCustomer != null) {
|
||||||
|
login(savedDifferentCustomer.getEmail(), CUSTOMER_USER_PASSWORD);
|
||||||
|
} else {
|
||||||
|
createDifferentCustomer();
|
||||||
|
|
||||||
|
loginTenantAdmin();
|
||||||
|
User differentCustomerUser = new User();
|
||||||
|
differentCustomerUser.setAuthority(Authority.CUSTOMER_USER);
|
||||||
|
differentCustomerUser.setTenantId(tenantId);
|
||||||
|
differentCustomerUser.setCustomerId(savedDifferentCustomer.getId());
|
||||||
|
differentCustomerUser.setEmail(DIFFERENT_CUSTOMER_USER_EMAIL);
|
||||||
|
|
||||||
|
createUserAndLogin(differentCustomerUser, DIFFERENT_CUSTOMER_USER_PASSWORD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void createDifferentCustomer() throws Exception {
|
||||||
loginTenantAdmin();
|
loginTenantAdmin();
|
||||||
|
|
||||||
Customer customer = new Customer();
|
Customer customer = new Customer();
|
||||||
customer.setTitle("Different customer");
|
customer.setTitle("Different customer");
|
||||||
savedDifferentCustomer = doPost("/api/customer", customer, Customer.class);
|
savedDifferentCustomer = doPost("/api/customer", customer, Customer.class);
|
||||||
Assert.assertNotNull(savedDifferentCustomer);
|
Assert.assertNotNull(savedDifferentCustomer);
|
||||||
User differentCustomerUser = new User();
|
differentCustomerId = savedDifferentCustomer.getId();
|
||||||
differentCustomerUser.setAuthority(Authority.CUSTOMER_USER);
|
|
||||||
differentCustomerUser.setTenantId(tenantId);
|
|
||||||
differentCustomerUser.setCustomerId(savedDifferentCustomer.getId());
|
|
||||||
differentCustomerUser.setEmail("different_customer@thingsboard.org");
|
|
||||||
|
|
||||||
createUserAndLogin(differentCustomerUser, "testPassword");
|
logout();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void deleteDifferentTenant() throws Exception {
|
protected void deleteDifferentTenant() throws Exception {
|
||||||
|
|||||||
@ -15,17 +15,26 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.controller;
|
package org.thingsboard.server.controller;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
|
import org.thingsboard.common.util.JacksonUtil;
|
||||||
import org.thingsboard.server.common.data.Device;
|
import org.thingsboard.server.common.data.Device;
|
||||||
|
import org.thingsboard.server.common.data.EntityType;
|
||||||
import org.thingsboard.server.common.data.alarm.Alarm;
|
import org.thingsboard.server.common.data.alarm.Alarm;
|
||||||
|
import org.thingsboard.server.common.data.alarm.AlarmInfo;
|
||||||
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
||||||
import org.thingsboard.server.common.data.alarm.AlarmStatus;
|
import org.thingsboard.server.common.data.alarm.AlarmStatus;
|
||||||
import org.thingsboard.server.common.data.audit.ActionType;
|
import org.thingsboard.server.common.data.audit.ActionType;
|
||||||
|
import org.thingsboard.server.common.data.page.PageData;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
@ -65,9 +74,8 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
|
|||||||
|
|
||||||
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
||||||
|
|
||||||
testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(),
|
testNotifyEntityAllOneTime(alarm, alarm.getId(), alarm.getOriginator(),
|
||||||
tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ADDED);
|
tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ADDED);
|
||||||
logout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -78,15 +86,13 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
|
|||||||
|
|
||||||
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
||||||
|
|
||||||
testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(),
|
testNotifyEntityAllOneTime(alarm, alarm.getId(), alarm.getOriginator(),
|
||||||
tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ADDED);
|
tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ADDED);
|
||||||
logout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateAlarmViaCustomer() throws Exception {
|
public void testUpdateAlarmViaCustomer() throws Exception {
|
||||||
loginCustomerUser();
|
loginCustomerUser();
|
||||||
|
|
||||||
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
||||||
|
|
||||||
Mockito.reset(tbClusterService, auditLogService);
|
Mockito.reset(tbClusterService, auditLogService);
|
||||||
@ -96,15 +102,13 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
|
|||||||
Assert.assertNotNull(updatedAlarm);
|
Assert.assertNotNull(updatedAlarm);
|
||||||
Assert.assertEquals(AlarmSeverity.MAJOR, updatedAlarm.getSeverity());
|
Assert.assertEquals(AlarmSeverity.MAJOR, updatedAlarm.getSeverity());
|
||||||
|
|
||||||
testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(),
|
testNotifyEntityAllOneTime(updatedAlarm, updatedAlarm.getId(), updatedAlarm.getOriginator(),
|
||||||
tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.UPDATED);
|
tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.UPDATED);
|
||||||
logout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateAlarmViaTenant() throws Exception {
|
public void testUpdateAlarmViaTenant() throws Exception {
|
||||||
loginTenantAdmin();
|
loginTenantAdmin();
|
||||||
|
|
||||||
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
||||||
|
|
||||||
Mockito.reset(tbClusterService, auditLogService);
|
Mockito.reset(tbClusterService, auditLogService);
|
||||||
@ -114,15 +118,13 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
|
|||||||
Assert.assertNotNull(updatedAlarm);
|
Assert.assertNotNull(updatedAlarm);
|
||||||
Assert.assertEquals(AlarmSeverity.MAJOR, updatedAlarm.getSeverity());
|
Assert.assertEquals(AlarmSeverity.MAJOR, updatedAlarm.getSeverity());
|
||||||
|
|
||||||
testNotifyEntityOne(updatedAlarm, updatedAlarm.getId(), updatedAlarm.getOriginator(),
|
testNotifyEntityAllOneTime(updatedAlarm, updatedAlarm.getId(), updatedAlarm.getOriginator(),
|
||||||
tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.UPDATED);
|
tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.UPDATED);
|
||||||
logout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateAlarmViaDifferentTenant() throws Exception {
|
public void testUpdateAlarmViaDifferentTenant() throws Exception {
|
||||||
loginTenantAdmin();
|
loginTenantAdmin();
|
||||||
|
|
||||||
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
||||||
|
|
||||||
alarm.setSeverity(AlarmSeverity.MAJOR);
|
alarm.setSeverity(AlarmSeverity.MAJOR);
|
||||||
@ -133,13 +135,11 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
|
|||||||
doPost("/api/alarm", alarm).andExpect(status().isForbidden());
|
doPost("/api/alarm", alarm).andExpect(status().isForbidden());
|
||||||
|
|
||||||
testNotifyEntityNever(alarm.getId(), alarm);
|
testNotifyEntityNever(alarm.getId(), alarm);
|
||||||
logout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateAlarmViaDifferentCustomer() throws Exception {
|
public void testUpdateAlarmViaDifferentCustomer() throws Exception {
|
||||||
loginCustomerUser();
|
loginCustomerUser();
|
||||||
|
|
||||||
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
||||||
|
|
||||||
loginDifferentCustomer();
|
loginDifferentCustomer();
|
||||||
@ -150,43 +150,37 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
|
|||||||
doPost("/api/alarm", alarm).andExpect(status().isForbidden());
|
doPost("/api/alarm", alarm).andExpect(status().isForbidden());
|
||||||
|
|
||||||
testNotifyEntityNever(alarm.getId(), alarm);
|
testNotifyEntityNever(alarm.getId(), alarm);
|
||||||
logout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteAlarmViaCustomer() throws Exception {
|
public void testDeleteAlarmViaCustomer() throws Exception {
|
||||||
loginCustomerUser();
|
loginCustomerUser();
|
||||||
|
|
||||||
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
||||||
|
|
||||||
Mockito.reset(tbClusterService, auditLogService);
|
Mockito.reset(tbClusterService, auditLogService);
|
||||||
|
|
||||||
doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isOk());
|
doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isOk());
|
||||||
|
|
||||||
testNotifyEntityOneMsgToEdgeServiceNever(alarm, alarm.getId(), alarm.getOriginator(),
|
testNotifyEntityOneTimeMsgToEdgeServiceNever(alarm, alarm.getId(), alarm.getOriginator(),
|
||||||
tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.DELETED);
|
tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.DELETED);
|
||||||
logout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteAlarmViaTenant() throws Exception {
|
public void testDeleteAlarmViaTenant() throws Exception {
|
||||||
loginTenantAdmin();
|
loginTenantAdmin();
|
||||||
|
|
||||||
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
||||||
|
|
||||||
Mockito.reset(tbClusterService, auditLogService);
|
Mockito.reset(tbClusterService, auditLogService);
|
||||||
|
|
||||||
doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isOk());
|
doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isOk());
|
||||||
|
|
||||||
testNotifyEntityOneMsgToEdgeServiceNever(alarm, alarm.getId(), alarm.getOriginator(),
|
testNotifyEntityOneTimeMsgToEdgeServiceNever(alarm, alarm.getId(), alarm.getOriginator(),
|
||||||
tenantId, tenantAdminCustomerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.DELETED);
|
tenantId, tenantAdminCustomerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.DELETED);
|
||||||
logout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteAlarmViaDifferentTenant() throws Exception {
|
public void testDeleteAlarmViaDifferentTenant() throws Exception {
|
||||||
loginTenantAdmin();
|
loginTenantAdmin();
|
||||||
|
|
||||||
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
||||||
|
|
||||||
loginDifferentTenant();
|
loginDifferentTenant();
|
||||||
@ -196,14 +190,11 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
|
|||||||
doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isForbidden());
|
doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isForbidden());
|
||||||
|
|
||||||
testNotifyEntityNever(alarm.getId(), alarm);
|
testNotifyEntityNever(alarm.getId(), alarm);
|
||||||
|
|
||||||
logout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteAlarmViaAnotherCustomer() throws Exception {
|
public void testDeleteAlarmViaDifferentCustomer() throws Exception {
|
||||||
loginCustomerUser();
|
loginCustomerUser();
|
||||||
|
|
||||||
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
||||||
|
|
||||||
loginDifferentCustomer();
|
loginDifferentCustomer();
|
||||||
@ -213,14 +204,11 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
|
|||||||
doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isForbidden());
|
doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isForbidden());
|
||||||
|
|
||||||
testNotifyEntityNever(alarm.getId(), alarm);
|
testNotifyEntityNever(alarm.getId(), alarm);
|
||||||
|
|
||||||
logout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testClearAlarmViaCustomer() throws Exception {
|
public void testClearAlarmViaCustomer() throws Exception {
|
||||||
loginCustomerUser();
|
loginCustomerUser();
|
||||||
|
|
||||||
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
||||||
|
|
||||||
Mockito.reset(tbClusterService, auditLogService);
|
Mockito.reset(tbClusterService, auditLogService);
|
||||||
@ -231,15 +219,13 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
|
|||||||
Assert.assertNotNull(foundAlarm);
|
Assert.assertNotNull(foundAlarm);
|
||||||
Assert.assertEquals(AlarmStatus.CLEARED_UNACK, foundAlarm.getStatus());
|
Assert.assertEquals(AlarmStatus.CLEARED_UNACK, foundAlarm.getStatus());
|
||||||
|
|
||||||
testNotifyEntityOne(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(),
|
testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(),
|
||||||
tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ALARM_CLEAR);
|
tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ALARM_CLEAR);
|
||||||
logout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testClearAlarmViaTenant() throws Exception {
|
public void testClearAlarmViaTenant() throws Exception {
|
||||||
loginTenantAdmin();
|
loginTenantAdmin();
|
||||||
|
|
||||||
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
||||||
|
|
||||||
Mockito.reset(tbClusterService, auditLogService);
|
Mockito.reset(tbClusterService, auditLogService);
|
||||||
@ -249,15 +235,13 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
|
|||||||
Assert.assertNotNull(foundAlarm);
|
Assert.assertNotNull(foundAlarm);
|
||||||
Assert.assertEquals(AlarmStatus.CLEARED_UNACK, foundAlarm.getStatus());
|
Assert.assertEquals(AlarmStatus.CLEARED_UNACK, foundAlarm.getStatus());
|
||||||
|
|
||||||
testNotifyEntityOne(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(),
|
testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(),
|
||||||
tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_CLEAR);
|
tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_CLEAR);
|
||||||
logout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAcknowledgeAlarmViaCustomer() throws Exception {
|
public void testAcknowledgeAlarmViaCustomer() throws Exception {
|
||||||
loginCustomerUser();
|
loginCustomerUser();
|
||||||
|
|
||||||
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
||||||
|
|
||||||
Mockito.reset(tbClusterService, auditLogService);
|
Mockito.reset(tbClusterService, auditLogService);
|
||||||
@ -268,15 +252,13 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
|
|||||||
Assert.assertNotNull(foundAlarm);
|
Assert.assertNotNull(foundAlarm);
|
||||||
Assert.assertEquals(AlarmStatus.ACTIVE_ACK, foundAlarm.getStatus());
|
Assert.assertEquals(AlarmStatus.ACTIVE_ACK, foundAlarm.getStatus());
|
||||||
|
|
||||||
testNotifyEntityOne(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(),
|
testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(),
|
||||||
tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ALARM_ACK);
|
tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ALARM_ACK);
|
||||||
logout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testClearAlarmViaDifferentCustomer() throws Exception {
|
public void testClearAlarmViaDifferentCustomer() throws Exception {
|
||||||
loginCustomerUser();
|
loginCustomerUser();
|
||||||
|
|
||||||
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
||||||
|
|
||||||
loginDifferentCustomer();
|
loginDifferentCustomer();
|
||||||
@ -286,13 +268,11 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
|
|||||||
doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isForbidden());
|
doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isForbidden());
|
||||||
|
|
||||||
testNotifyEntityNever(alarm.getId(), alarm);
|
testNotifyEntityNever(alarm.getId(), alarm);
|
||||||
logout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testClearAlarmViaDifferentTenant() throws Exception {
|
public void testClearAlarmViaDifferentTenant() throws Exception {
|
||||||
loginTenantAdmin();
|
loginTenantAdmin();
|
||||||
|
|
||||||
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
||||||
|
|
||||||
loginDifferentTenant();
|
loginDifferentTenant();
|
||||||
@ -302,13 +282,11 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
|
|||||||
doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isForbidden());
|
doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isForbidden());
|
||||||
|
|
||||||
testNotifyEntityNever(alarm.getId(), alarm);
|
testNotifyEntityNever(alarm.getId(), alarm);
|
||||||
logout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAcknowledgeAlarmViaDifferentCustomer() throws Exception {
|
public void testAcknowledgeAlarmViaDifferentCustomer() throws Exception {
|
||||||
loginCustomerUser();
|
loginCustomerUser();
|
||||||
|
|
||||||
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
||||||
|
|
||||||
loginDifferentCustomer();
|
loginDifferentCustomer();
|
||||||
@ -318,13 +296,11 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
|
|||||||
doPost("/api/alarm/" + alarm.getId() + "/ack").andExpect(status().isForbidden());
|
doPost("/api/alarm/" + alarm.getId() + "/ack").andExpect(status().isForbidden());
|
||||||
|
|
||||||
testNotifyEntityNever(alarm.getId(), alarm);
|
testNotifyEntityNever(alarm.getId(), alarm);
|
||||||
logout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAcknowledgeAlarmViaDifferentTenant() throws Exception {
|
public void testAcknowledgeAlarmViaDifferentTenant() throws Exception {
|
||||||
loginTenantAdmin();
|
loginTenantAdmin();
|
||||||
|
|
||||||
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
Alarm alarm = createAlarm(TEST_ALARM_TYPE);
|
||||||
|
|
||||||
loginDifferentTenant();
|
loginDifferentTenant();
|
||||||
@ -332,9 +308,99 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
|
|||||||
Mockito.reset(tbClusterService, auditLogService);
|
Mockito.reset(tbClusterService, auditLogService);
|
||||||
|
|
||||||
doPost("/api/alarm/" + alarm.getId() + "/ack").andExpect(status().isForbidden());
|
doPost("/api/alarm/" + alarm.getId() + "/ack").andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindAlarmsViaCustomerUser() throws Exception {
|
||||||
|
loginCustomerUser();
|
||||||
|
|
||||||
|
List<Alarm> createdAlarms = new LinkedList<>();
|
||||||
|
|
||||||
|
final int size = 10;
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
createdAlarms.add(
|
||||||
|
createAlarm(TEST_ALARM_TYPE + i)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = doGetTyped(
|
||||||
|
"/api/alarm/" + EntityType.DEVICE + "/"
|
||||||
|
+ customerDevice.getUuidId() + "?page=0&pageSize=" + size,
|
||||||
|
new TypeReference<PageData<AlarmInfo>>() {}
|
||||||
|
);
|
||||||
|
var foundAlarmInfos = response.getData();
|
||||||
|
Assert.assertNotNull("Found pageData is null", foundAlarmInfos);
|
||||||
|
Assert.assertNotEquals(
|
||||||
|
"Expected alarms are not found!",
|
||||||
|
0, foundAlarmInfos.size()
|
||||||
|
);
|
||||||
|
|
||||||
|
boolean allMatch = createdAlarms.stream()
|
||||||
|
.allMatch(alarm -> foundAlarmInfos.stream()
|
||||||
|
.map(Alarm::getType)
|
||||||
|
.anyMatch(type -> alarm.getType().equals(type))
|
||||||
|
);
|
||||||
|
Assert.assertTrue("Created alarm doesn't match any found!", allMatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindAlarmsViaDifferentCustomerUser() throws Exception {
|
||||||
|
loginCustomerUser();
|
||||||
|
|
||||||
|
final int size = 10;
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
createAlarm(TEST_ALARM_TYPE + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
loginDifferentCustomer();
|
||||||
|
doGet("/api/alarm/" + EntityType.DEVICE + "/"
|
||||||
|
+ customerDevice.getUuidId() + "?page=0&pageSize=" + size)
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindAlarmsViaPublicCustomer() throws Exception {
|
||||||
|
loginTenantAdmin();
|
||||||
|
|
||||||
|
Device device = new Device();
|
||||||
|
device.setName("Test Public Device");
|
||||||
|
device.setLabel("Label");
|
||||||
|
device.setCustomerId(customerId);
|
||||||
|
device = doPost("/api/device", device, Device.class);
|
||||||
|
device = doPost("/api/customer/public/device/" + device.getUuidId(), Device.class);
|
||||||
|
|
||||||
|
String publicId = device.getCustomerId().toString();
|
||||||
|
|
||||||
|
Alarm alarm = Alarm.builder()
|
||||||
|
.originator(device.getId())
|
||||||
|
.status(AlarmStatus.ACTIVE_UNACK)
|
||||||
|
.severity(AlarmSeverity.CRITICAL)
|
||||||
|
.type("Test")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Mockito.reset(tbClusterService, auditLogService);
|
||||||
|
|
||||||
|
alarm = doPost("/api/alarm", alarm, Alarm.class);
|
||||||
|
Assert.assertNotNull("Saved alarm is null!", alarm);
|
||||||
|
|
||||||
|
testNotifyEntityNeverMsgToEdgeServiceOneTime(alarm, alarm.getId(), tenantId, ActionType.ADDED);
|
||||||
|
|
||||||
testNotifyEntityNever(alarm.getId(), alarm);
|
|
||||||
logout();
|
logout();
|
||||||
|
|
||||||
|
JsonNode publicLoginRequest = JacksonUtil.toJsonNode("{\"publicId\": \"" + publicId + "\"}");
|
||||||
|
JsonNode tokens = doPost("/api/auth/login/public", publicLoginRequest, JsonNode.class);
|
||||||
|
this.token = tokens.get("token").asText();
|
||||||
|
|
||||||
|
PageData<AlarmInfo> pageData = doGetTyped(
|
||||||
|
"/api/alarm/DEVICE/" + device.getUuidId() + "?page=0&pageSize=1", new TypeReference<PageData<AlarmInfo>>() {}
|
||||||
|
);
|
||||||
|
|
||||||
|
Assert.assertNotNull("Found pageData is null", pageData);
|
||||||
|
Assert.assertNotEquals("Expected alarms are not found!", 0, pageData.getTotalElements());
|
||||||
|
|
||||||
|
AlarmInfo alarmInfo = pageData.getData().get(0);
|
||||||
|
boolean equals = alarm.getId().equals(alarmInfo.getId()) && alarm.getType().equals(alarmInfo.getType());
|
||||||
|
Assert.assertTrue("Created alarm doesn't match the found one!", equals);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Alarm createAlarm(String type) throws Exception {
|
private Alarm createAlarm(String type) throws Exception {
|
||||||
@ -346,8 +412,10 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
|
|||||||
.severity(AlarmSeverity.CRITICAL)
|
.severity(AlarmSeverity.CRITICAL)
|
||||||
.type(type)
|
.type(type)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
alarm = doPost("/api/alarm", alarm, Alarm.class);
|
alarm = doPost("/api/alarm", alarm, Alarm.class);
|
||||||
Assert.assertNotNull(alarm);
|
Assert.assertNotNull(alarm);
|
||||||
|
|
||||||
return alarm;
|
return alarm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -94,7 +94,7 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
|
|||||||
|
|
||||||
Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
|
Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
|
||||||
|
|
||||||
testNotifyEntityOneMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), savedCustomer.getId(),
|
testNotifyEntityOneTimeMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), savedCustomer.getId(),
|
||||||
savedCustomer.getTenantId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
|
savedCustomer.getTenantId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
|
||||||
ActionType.ADDED);
|
ActionType.ADDED);
|
||||||
|
|
||||||
@ -103,14 +103,11 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
|
|||||||
Assert.assertTrue(savedCustomer.getCreatedTime() > 0);
|
Assert.assertTrue(savedCustomer.getCreatedTime() > 0);
|
||||||
Assert.assertEquals(customer.getTitle(), savedCustomer.getTitle());
|
Assert.assertEquals(customer.getTitle(), savedCustomer.getTitle());
|
||||||
|
|
||||||
|
|
||||||
savedCustomer.setTitle("My new customer");
|
savedCustomer.setTitle("My new customer");
|
||||||
|
|
||||||
Mockito.reset(tbClusterService, auditLogService);
|
|
||||||
|
|
||||||
doPost("/api/customer", savedCustomer, Customer.class);
|
doPost("/api/customer", savedCustomer, Customer.class);
|
||||||
|
|
||||||
testNotifyEntityOne(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), savedCustomer.getTenantId(),
|
testNotifyEntityAllOneTime(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), savedCustomer.getTenantId(),
|
||||||
savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
|
savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
|
||||||
ActionType.UPDATED);
|
ActionType.UPDATED);
|
||||||
|
|
||||||
@ -119,10 +116,6 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
|
|||||||
|
|
||||||
doDelete("/api/customer/" + savedCustomer.getId().getId().toString())
|
doDelete("/api/customer/" + savedCustomer.getId().getId().toString())
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
testNotifyEntityBroadcastEntityStateChangeEventOneMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(),
|
|
||||||
savedCustomer.getId(), savedCustomer.getTenantId(), savedCustomer.getId(), tenantAdmin.getId(),
|
|
||||||
tenantAdmin.getEmail(), ActionType.DELETED, savedCustomer.getId().getId().toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -199,14 +192,12 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
|
|||||||
deleteDifferentTenant();
|
deleteDifferentTenant();
|
||||||
login(tenantAdmin.getName(), "testPassword1");
|
login(tenantAdmin.getName(), "testPassword1");
|
||||||
|
|
||||||
Mockito.reset(tbClusterService, auditLogService);
|
|
||||||
|
|
||||||
doDelete("/api/customer/" + savedCustomer.getId().getId().toString())
|
doDelete("/api/customer/" + savedCustomer.getId().getId().toString())
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
testNotifyEntityDeleteOneMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), savedCustomer.getId(),
|
testNotifyEntityBroadcastEntityStateChangeEventOneTimeMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(),
|
||||||
savedCustomer.getTenantId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
|
savedCustomer.getId(), savedCustomer.getTenantId(), savedCustomer.getId(), tenantAdmin.getId(),
|
||||||
ActionType.DELETED, savedCustomer.getId().getId().toString());
|
tenantAdmin.getEmail(), ActionType.DELETED, savedCustomer.getId().getId().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -223,10 +214,6 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
|
|||||||
|
|
||||||
doDelete("/api/customer/" + savedCustomer.getId().getId().toString())
|
doDelete("/api/customer/" + savedCustomer.getId().getId().toString())
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
testNotifyEntityBroadcastEntityStateChangeEventOneMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(),
|
|
||||||
savedCustomer.getId(), savedCustomer.getTenantId(), savedCustomer.getId(), tenantAdmin.getId(),
|
|
||||||
tenantAdmin.getEmail(), ActionType.DELETED, savedCustomer.getId().getId().toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -240,7 +227,7 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
|
|||||||
doDelete("/api/customer/" + savedCustomer.getId().getId().toString())
|
doDelete("/api/customer/" + savedCustomer.getId().getId().toString())
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
testNotifyEntityBroadcastEntityStateChangeEventOneMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(),
|
testNotifyEntityBroadcastEntityStateChangeEventOneTimeMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(),
|
||||||
savedCustomer.getId(), savedCustomer.getTenantId(), savedCustomer.getId(), tenantAdmin.getId(),
|
savedCustomer.getId(), savedCustomer.getTenantId(), savedCustomer.getId(), tenantAdmin.getId(),
|
||||||
tenantAdmin.getEmail(), ActionType.DELETED, savedCustomer.getId().getId().toString());
|
tenantAdmin.getEmail(), ActionType.DELETED, savedCustomer.getId().getId().toString());
|
||||||
|
|
||||||
|
|||||||
@ -12,20 +12,19 @@
|
|||||||
"start-prod": "NODE_ENV=production nodemon server.js"
|
"start-prod": "NODE_ENV=production nodemon server.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@azure/service-bus": "^1.1.9",
|
"@azure/service-bus": "^7.5.1",
|
||||||
"@google-cloud/pubsub": "^2.5.0",
|
"@google-cloud/pubsub": "^3.0.1",
|
||||||
"amqplib": "^0.6.0",
|
"amqplib": "^0.10.0",
|
||||||
"aws-sdk": "^2.741.0",
|
"aws-sdk": "^2.1152.0",
|
||||||
"azure-sb": "^0.11.1",
|
"config": "^3.3.7",
|
||||||
"config": "^3.3.1",
|
"express": "^4.18.1",
|
||||||
"express": "^4.17.1",
|
"js-yaml": "^4.1.0",
|
||||||
"js-yaml": "^3.14.0",
|
"kafkajs": "^2.0.2",
|
||||||
"kafkajs": "^1.15.0",
|
"long": "^5.2.0",
|
||||||
"long": "^4.0.0",
|
|
||||||
"uuid-parse": "^1.1.0",
|
"uuid-parse": "^1.1.0",
|
||||||
"uuid-random": "^1.3.2",
|
"uuid-random": "^1.3.2",
|
||||||
"winston": "^3.3.3",
|
"winston": "^3.7.2",
|
||||||
"winston-daily-rotate-file": "^4.5.0"
|
"winston-daily-rotate-file": "^4.7.1"
|
||||||
},
|
},
|
||||||
"nyc": {
|
"nyc": {
|
||||||
"exclude": [
|
"exclude": [
|
||||||
@ -36,13 +35,19 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"fs-extra": "^10.0.0",
|
"fs-extra": "^10.1.0",
|
||||||
"nodemon": "^2.0.12",
|
"nodemon": "^2.0.16",
|
||||||
"pkg": "^5.3.1"
|
"pkg": "^5.7.0"
|
||||||
},
|
},
|
||||||
"pkg": {
|
"pkg": {
|
||||||
"assets": [
|
"assets": [
|
||||||
"node_modules/config/**/*.*"
|
"node_modules/config/**/*.*"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"ansi-regex": "^5.0.1",
|
||||||
|
"color-string": "^1.5.5",
|
||||||
|
"minimist": "^1.2.6",
|
||||||
|
"node-fetch": "^2.6.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -71,8 +71,8 @@
|
|||||||
<goal>install-node-and-yarn</goal>
|
<goal>install-node-and-yarn</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
<configuration>
|
||||||
<nodeVersion>v12.16.1</nodeVersion>
|
<nodeVersion>v16.13.1</nodeVersion>
|
||||||
<yarnVersion>v1.22.4</yarnVersion>
|
<yarnVersion>v1.22.17</yarnVersion>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
<execution>
|
<execution>
|
||||||
|
|||||||
@ -18,8 +18,7 @@
|
|||||||
const config = require('config'),
|
const config = require('config'),
|
||||||
JsInvokeMessageProcessor = require('../api/jsInvokeMessageProcessor'),
|
JsInvokeMessageProcessor = require('../api/jsInvokeMessageProcessor'),
|
||||||
logger = require('../config/logger')._logger('serviceBusTemplate');
|
logger = require('../config/logger')._logger('serviceBusTemplate');
|
||||||
const {ServiceBusClient, ReceiveMode} = require("@azure/service-bus");
|
const {ServiceBusClient, ServiceBusAdministrationClient} = require("@azure/service-bus");
|
||||||
const azure = require('azure-sb');
|
|
||||||
|
|
||||||
const requestTopic = config.get('request_topic');
|
const requestTopic = config.get('request_topic');
|
||||||
const namespaceName = config.get('service_bus.namespace_name');
|
const namespaceName = config.get('service_bus.namespace_name');
|
||||||
@ -28,7 +27,6 @@ const sasKey = config.get('service_bus.sas_key');
|
|||||||
const queueProperties = config.get('service_bus.queue_properties');
|
const queueProperties = config.get('service_bus.queue_properties');
|
||||||
|
|
||||||
let sbClient;
|
let sbClient;
|
||||||
let receiverClient;
|
|
||||||
let receiver;
|
let receiver;
|
||||||
let serviceBusService;
|
let serviceBusService;
|
||||||
|
|
||||||
@ -61,11 +59,10 @@ function ServiceBusProducer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function CustomSender(topic) {
|
function CustomSender(topic) {
|
||||||
this.queueClient = sbClient.createQueueClient(topic);
|
this.sender = sbClient.createSender(topic);
|
||||||
this.sender = this.queueClient.createSender();
|
|
||||||
|
|
||||||
this.send = async (message) => {
|
this.send = async (message) => {
|
||||||
return this.sender.send(message);
|
return this.sender.sendMessages(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,8 +71,8 @@ function CustomSender(topic) {
|
|||||||
logger.info('Starting ThingsBoard JavaScript Executor Microservice...');
|
logger.info('Starting ThingsBoard JavaScript Executor Microservice...');
|
||||||
|
|
||||||
const connectionString = `Endpoint=sb://${namespaceName}.servicebus.windows.net/;SharedAccessKeyName=${sasKeyName};SharedAccessKey=${sasKey}`;
|
const connectionString = `Endpoint=sb://${namespaceName}.servicebus.windows.net/;SharedAccessKeyName=${sasKeyName};SharedAccessKey=${sasKey}`;
|
||||||
sbClient = ServiceBusClient.createFromConnectionString(connectionString);
|
sbClient = new ServiceBusClient(connectionString)
|
||||||
serviceBusService = azure.createServiceBusService(connectionString);
|
serviceBusService = new ServiceBusAdministrationClient(connectionString);
|
||||||
|
|
||||||
parseQueueProperties();
|
parseQueueProperties();
|
||||||
|
|
||||||
@ -84,9 +81,9 @@ function CustomSender(topic) {
|
|||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
data.forEach(queue => {
|
for (const queue of data) {
|
||||||
queues.push(queue.QueueName);
|
queues.push(queue.name);
|
||||||
});
|
}
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -97,8 +94,7 @@ function CustomSender(topic) {
|
|||||||
queues.push(requestTopic);
|
queues.push(requestTopic);
|
||||||
}
|
}
|
||||||
|
|
||||||
receiverClient = sbClient.createQueueClient(requestTopic);
|
receiver = sbClient.createReceiver(requestTopic, {receiveMode: 'peekLock'});
|
||||||
receiver = receiverClient.createReceiver(ReceiveMode.peekLock);
|
|
||||||
|
|
||||||
const messageProcessor = new JsInvokeMessageProcessor(new ServiceBusProducer());
|
const messageProcessor = new JsInvokeMessageProcessor(new ServiceBusProducer());
|
||||||
|
|
||||||
@ -111,18 +107,18 @@ function CustomSender(topic) {
|
|||||||
const errorHandler = (error) => {
|
const errorHandler = (error) => {
|
||||||
logger.error('Failed to receive message from queue.', error);
|
logger.error('Failed to receive message from queue.', error);
|
||||||
};
|
};
|
||||||
receiver.registerMessageHandler(messageHandler, errorHandler);
|
receiver.subscribe({processMessage: messageHandler, processError: errorHandler})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Failed to start ThingsBoard JavaScript Executor Microservice: %s', e.message);
|
logger.error('Failed to start ThingsBoard JavaScript Executor Microservice: %s', e.message);
|
||||||
logger.error(e.stack);
|
logger.error(e.stack);
|
||||||
exit(-1);
|
await exit(-1);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
async function createQueueIfNotExist(topic) {
|
async function createQueueIfNotExist(topic) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
serviceBusService.createQueueIfNotExists(topic, queueOptions, (err) => {
|
serviceBusService.createQueue(topic, queueOptions, (err) => {
|
||||||
if (err) {
|
if (err && err.code !== "MessageEntityAlreadyExistsError") {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
resolve();
|
resolve();
|
||||||
@ -139,10 +135,10 @@ function parseQueueProperties() {
|
|||||||
properties[p.substring(0, delimiterPosition)] = p.substring(delimiterPosition + 1);
|
properties[p.substring(0, delimiterPosition)] = p.substring(delimiterPosition + 1);
|
||||||
});
|
});
|
||||||
queueOptions = {
|
queueOptions = {
|
||||||
DuplicateDetection: 'false',
|
requiresDuplicateDetection: false,
|
||||||
MaxSizeInMegabytes: properties['maxSizeInMb'],
|
maxSizeInMegabytes: properties['maxSizeInMb'],
|
||||||
DefaultMessageTimeToLive: `PT${properties['messageTimeToLiveInSec']}S`,
|
defaultMessageTimeToLive: `PT${properties['messageTimeToLiveInSec']}S`,
|
||||||
LockDuration: `PT${properties['lockDurationInSec']}S`
|
lockDuration: `PT${properties['lockDurationInSec']}S`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,24 +157,11 @@ async function exit(status) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (receiverClient) {
|
|
||||||
try {
|
|
||||||
await receiverClient.close();
|
|
||||||
} catch (e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
senderMap.forEach((k, v) => {
|
senderMap.forEach((k, v) => {
|
||||||
try {
|
try {
|
||||||
v.sender.close();
|
v.sender.close();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
v.queueClient.close();
|
|
||||||
} catch (e) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -13,14 +13,14 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"config": "^3.3.1",
|
"config": "^3.3.7",
|
||||||
"connect-history-api-fallback": "^1.6.0",
|
"connect-history-api-fallback": "^1.6.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.18.1",
|
||||||
"http": "0.0.0",
|
"http": "0.0.0",
|
||||||
"http-proxy": "^1.18.1",
|
"http-proxy": "^1.18.1",
|
||||||
"js-yaml": "^3.14.0",
|
"js-yaml": "^4.1.0",
|
||||||
"winston": "^3.3.3",
|
"winston": "^3.7.2",
|
||||||
"winston-daily-rotate-file": "^4.5.0"
|
"winston-daily-rotate-file": "^4.7.1"
|
||||||
},
|
},
|
||||||
"nyc": {
|
"nyc": {
|
||||||
"exclude": [
|
"exclude": [
|
||||||
@ -31,13 +31,18 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"fs-extra": "^10.0.0",
|
"fs-extra": "^10.1.0",
|
||||||
"nodemon": "^2.0.12",
|
"nodemon": "^2.0.16",
|
||||||
"pkg": "^5.3.1"
|
"pkg": "^5.7.0"
|
||||||
},
|
},
|
||||||
"pkg": {
|
"pkg": {
|
||||||
"assets": [
|
"assets": [
|
||||||
"node_modules/config/**/*.*"
|
"node_modules/config/**/*.*"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"color-string": "^1.5.5",
|
||||||
|
"follow-redirects": "^1.14.8",
|
||||||
|
"minimist": "^1.2.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -80,8 +80,8 @@
|
|||||||
<goal>install-node-and-yarn</goal>
|
<goal>install-node-and-yarn</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
<configuration>
|
||||||
<nodeVersion>v12.16.1</nodeVersion>
|
<nodeVersion>v16.13.1</nodeVersion>
|
||||||
<yarnVersion>v1.22.4</yarnVersion>
|
<yarnVersion>v1.22.17</yarnVersion>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
<execution>
|
<execution>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -62,6 +62,7 @@
|
|||||||
"leaflet-providers": "^1.13.0",
|
"leaflet-providers": "^1.13.0",
|
||||||
"leaflet.gridlayer.googlemutant": "^0.13.4",
|
"leaflet.gridlayer.googlemutant": "^0.13.4",
|
||||||
"leaflet.markercluster": "^1.5.3",
|
"leaflet.markercluster": "^1.5.3",
|
||||||
|
"libphonenumber-js": "^1.10.4",
|
||||||
"messageformat": "^2.3.0",
|
"messageformat": "^2.3.0",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"moment-timezone": "^0.5.34",
|
"moment-timezone": "^0.5.34",
|
||||||
|
|||||||
@ -30,17 +30,11 @@
|
|||||||
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
|
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
|
||||||
<div mat-dialog-content tb-toast toastTarget="sendTestSmsDialogContent">
|
<div mat-dialog-content tb-toast toastTarget="sendTestSmsDialogContent">
|
||||||
<fieldset [disabled]="(isLoading$ | async)">
|
<fieldset [disabled]="(isLoading$ | async)">
|
||||||
<mat-form-field class="mat-block">
|
<tb-phone-input required
|
||||||
<mat-label translate>admin.number-to</mat-label>
|
formControlName="numberTo"
|
||||||
<input type="tel" required [pattern]="phoneNumberPattern" matInput formControlName="numberTo">
|
[enableFlagsSelect]="false"
|
||||||
<mat-error *ngIf="sendTestSmsFormGroup.get('numberTo').hasError('required')">
|
[label]="'admin.number-to'">
|
||||||
{{ 'admin.number-to-required' | translate }}
|
</tb-phone-input>
|
||||||
</mat-error>
|
|
||||||
<mat-error *ngIf="sendTestSmsFormGroup.get('numberTo').hasError('pattern')">
|
|
||||||
{{ 'admin.phone-number-pattern' | translate }}
|
|
||||||
</mat-error>
|
|
||||||
<mat-hint innerHTML="{{ 'admin.phone-number-hint' | translate }}"></mat-hint>
|
|
||||||
</mat-form-field>
|
|
||||||
<mat-form-field class="mat-block">
|
<mat-form-field class="mat-block">
|
||||||
<mat-label translate>admin.sms-message</mat-label>
|
<mat-label translate>admin.sms-message</mat-label>
|
||||||
<textarea required matInput rows="3" [maxLength]="1600" formControlName="message"></textarea>
|
<textarea required matInput rows="3" [maxLength]="1600" formControlName="message"></textarea>
|
||||||
|
|||||||
@ -36,21 +36,12 @@
|
|||||||
<ng-template matStepLabel>{{ 'security.2fa.dialog.sms-step-label' | translate }}</ng-template>
|
<ng-template matStepLabel>{{ 'security.2fa.dialog.sms-step-label' | translate }}</ng-template>
|
||||||
<form [formGroup]="smsConfigForm" (ngSubmit)="nextStep()">
|
<form [formGroup]="smsConfigForm" (ngSubmit)="nextStep()">
|
||||||
<p class="mat-body step-description input" translate>security.2fa.dialog.sms-step-description</p>
|
<p class="mat-body step-description input" translate>security.2fa.dialog.sms-step-description</p>
|
||||||
<div fxLayout="row" fxLayoutAlign="space-between center" fxLayoutGap="8px">
|
<div fxLayout="row" fxLayoutAlign="space-between center" fxLayoutGap="15px">
|
||||||
<mat-form-field fxFlex class="mat-block input-container" floatLabel="always" hideRequiredMarker>
|
<tb-phone-input fxFlex
|
||||||
<mat-label></mat-label>
|
formControlName="phone"
|
||||||
<input type="tel" required
|
[floatLabel]="'never'"
|
||||||
[pattern]="phoneNumberPattern"
|
[placeholder]="'security.2fa.dialog.sms-step-label'">
|
||||||
matInput formControlName="phone"
|
</tb-phone-input>
|
||||||
placeholder="{{ 'security.2fa.dialog.sms-step-label' | translate }}">
|
|
||||||
<mat-error *ngIf="smsConfigForm.get('phone').hasError('required')">
|
|
||||||
{{ 'admin.number-to-required' | translate }}
|
|
||||||
</mat-error>
|
|
||||||
<mat-error *ngIf="smsConfigForm.get('phone').hasError('pattern')">
|
|
||||||
{{ 'admin.phone-number-pattern' | translate }}
|
|
||||||
</mat-error>
|
|
||||||
<mat-hint innerHTML="{{ 'admin.phone-number-hint' | translate }}"></mat-hint>
|
|
||||||
</mat-form-field>
|
|
||||||
<button mat-raised-button
|
<button mat-raised-button
|
||||||
type="submit"
|
type="submit"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|||||||
@ -55,13 +55,11 @@
|
|||||||
<mat-label translate>contact.address2</mat-label>
|
<mat-label translate>contact.address2</mat-label>
|
||||||
<input matInput formControlName="address2">
|
<input matInput formControlName="address2">
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field class="mat-block">
|
<tb-phone-input [required]="false"
|
||||||
<mat-label translate>contact.phone</mat-label>
|
[label]="'contact.phone'"
|
||||||
<input matInput formControlName="phone">
|
[enableFlagsSelect]="false"
|
||||||
<mat-error *ngIf="parentForm.get('phone').hasError('maxlength')">
|
formControlName="phone">
|
||||||
{{ 'contact.phone-max-length' | translate }}
|
</tb-phone-input>
|
||||||
</mat-error>
|
|
||||||
</mat-form-field>
|
|
||||||
<mat-form-field class="mat-block">
|
<mat-form-field class="mat-block">
|
||||||
<mat-label translate>contact.email</mat-label>
|
<mat-label translate>contact.email</mat-label>
|
||||||
<input matInput formControlName="email">
|
<input matInput formControlName="email">
|
||||||
|
|||||||
50
ui-ngx/src/app/shared/components/phone-input.component.html
Normal file
50
ui-ngx/src/app/shared/components/phone-input.component.html
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright © 2016-2022 The Thingsboard Authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
<form [formGroup]="phoneFormGroup">
|
||||||
|
<div class="phone-input-container">
|
||||||
|
<div class="flags-select-container" *ngIf="enableFlagsSelect">
|
||||||
|
<span class="flag-container">{{ flagIcon }}</span>
|
||||||
|
<mat-select class="country-select" formControlName="country">
|
||||||
|
<mat-option *ngFor="let country of allCountries" [value]="country.iso2">
|
||||||
|
<span style="font-size: 20px;">{{country.flag}}</span>
|
||||||
|
<span>{{' ' + country.name + ' +' + country.dialCode }}</span>
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</div>
|
||||||
|
<mat-form-field class="phone-input" [appearance]="appearance" [floatLabel]="floatLabel">
|
||||||
|
<mat-label>{{ label | translate }}</mat-label>
|
||||||
|
<input
|
||||||
|
formControlName="phoneNumber"
|
||||||
|
type="tel"
|
||||||
|
matInput
|
||||||
|
placeholder="{{ placeholder | translate }}"
|
||||||
|
[pattern]="phoneNumberPattern"
|
||||||
|
(focus)="focus()"
|
||||||
|
autocomplete="off"
|
||||||
|
[required]="required">
|
||||||
|
<mat-hint innerHTML="{{ 'phone-input.phone-input-hint' | translate: {phoneNumber: phonePlaceholder} }}"></mat-hint>
|
||||||
|
<mat-error *ngIf="phoneFormGroup.get('phoneNumber').hasError('required')">
|
||||||
|
{{ 'phone-input.phone-input-required' | translate }}
|
||||||
|
</mat-error>
|
||||||
|
<mat-error *ngIf="phoneFormGroup.get('phoneNumber').hasError('invalidPhoneNumber')">
|
||||||
|
{{ 'phone-input.phone-input-validation' | translate }}
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
55
ui-ngx/src/app/shared/components/phone-input.component.scss
Normal file
55
ui-ngx/src/app/shared/components/phone-input.component.scss
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2022 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
:host ::ng-deep {
|
||||||
|
|
||||||
|
.phone-input-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.phone-input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.flags-select-container {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
width: 50px;
|
||||||
|
height: 100%;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flag-container {
|
||||||
|
position: absolute;
|
||||||
|
font-size: 20px;
|
||||||
|
top: 50%;
|
||||||
|
left: 0;
|
||||||
|
transform: translate(0, -50%);
|
||||||
|
}
|
||||||
|
.country-select {
|
||||||
|
width: 45px;
|
||||||
|
height: 30px;
|
||||||
|
|
||||||
|
.mat-select-trigger {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-select-value {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
222
ui-ngx/src/app/shared/components/phone-input.component.ts
Normal file
222
ui-ngx/src/app/shared/components/phone-input.component.ts
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
///
|
||||||
|
/// Copyright © 2016-2022 The Thingsboard Authors
|
||||||
|
///
|
||||||
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
/// you may not use this file except in compliance with the License.
|
||||||
|
/// You may obtain a copy of the License at
|
||||||
|
///
|
||||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
///
|
||||||
|
/// Unless required by applicable law or agreed to in writing, software
|
||||||
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
/// See the License for the specific language governing permissions and
|
||||||
|
/// limitations under the License.
|
||||||
|
///
|
||||||
|
|
||||||
|
import { Component, forwardRef, Input, OnInit } from '@angular/core';
|
||||||
|
import {
|
||||||
|
ControlValueAccessor,
|
||||||
|
FormBuilder,
|
||||||
|
FormControl,
|
||||||
|
FormGroup,
|
||||||
|
NG_VALIDATORS,
|
||||||
|
NG_VALUE_ACCESSOR,
|
||||||
|
ValidationErrors,
|
||||||
|
Validator,
|
||||||
|
ValidatorFn,
|
||||||
|
Validators
|
||||||
|
} from '@angular/forms';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { Country, CountryData } from '@shared/models/country.models';
|
||||||
|
import examples from 'libphonenumber-js/examples.mobile.json';
|
||||||
|
import { CountryCode, getExampleNumber, parsePhoneNumberFromString } from 'libphonenumber-js';
|
||||||
|
import { phoneNumberPattern } from '@shared/models/settings.models';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { FloatLabelType, MatFormFieldAppearance } from '@angular/material/form-field/form-field';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'tb-phone-input',
|
||||||
|
templateUrl: './phone-input.component.html',
|
||||||
|
styleUrls: ['./phone-input.component.scss'],
|
||||||
|
providers: [
|
||||||
|
CountryData,
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => PhoneInputComponent),
|
||||||
|
multi: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: NG_VALIDATORS,
|
||||||
|
useExisting: forwardRef(() => PhoneInputComponent),
|
||||||
|
multi: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class PhoneInputComponent implements OnInit, ControlValueAccessor, Validator {
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
disabled: boolean;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
defaultCountry: CountryCode = 'US';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
enableFlagsSelect = true;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
required = true;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
floatLabel: FloatLabelType = 'auto';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
appearance: MatFormFieldAppearance = 'legacy';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
placeholder;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
label = 'phone-input.phone-input-label';
|
||||||
|
|
||||||
|
allCountries: Array<Country> = this.countryCodeData.allCountries;
|
||||||
|
phonePlaceholder: string;
|
||||||
|
flagIcon: string;
|
||||||
|
phoneFormGroup: FormGroup;
|
||||||
|
phoneNumberPattern = phoneNumberPattern;
|
||||||
|
|
||||||
|
private baseCode = 127397;
|
||||||
|
private countryCallingCode: string;
|
||||||
|
private modelValue: string;
|
||||||
|
private valueChange$: Subscription = null;
|
||||||
|
private propagateChange = (v: any) => { };
|
||||||
|
|
||||||
|
constructor(private translate: TranslateService,
|
||||||
|
private fb: FormBuilder,
|
||||||
|
private countryCodeData: CountryData) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
const validators: ValidatorFn[] = [Validators.pattern(phoneNumberPattern), this.validatePhoneNumber()];
|
||||||
|
if (this.required) {
|
||||||
|
validators.push(Validators.required);
|
||||||
|
}
|
||||||
|
this.phoneFormGroup = this.fb.group({
|
||||||
|
country: [this.defaultCountry, []],
|
||||||
|
phoneNumber: [null, validators]
|
||||||
|
});
|
||||||
|
|
||||||
|
this.valueChange$ = this.phoneFormGroup.get('phoneNumber').valueChanges.subscribe(value => {
|
||||||
|
this.updateModel();
|
||||||
|
if (value) {
|
||||||
|
const parsedPhoneNumber = parsePhoneNumberFromString(value);
|
||||||
|
const country = this.phoneFormGroup.get('country').value;
|
||||||
|
if (parsedPhoneNumber?.country && parsedPhoneNumber?.country !== country) {
|
||||||
|
this.phoneFormGroup.get('country').patchValue(parsedPhoneNumber.country, {emitEvent: true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.phoneFormGroup.get('country').valueChanges.subscribe(value => {
|
||||||
|
if (value) {
|
||||||
|
const code = this.countryCallingCode;
|
||||||
|
this.getFlagAndPhoneNumberData(value);
|
||||||
|
let phoneNumber = this.phoneFormGroup.get('phoneNumber').value;
|
||||||
|
if (phoneNumber) {
|
||||||
|
if (code !== this.countryCallingCode && phoneNumber.includes(code)) {
|
||||||
|
phoneNumber = phoneNumber.replace(code, this.countryCallingCode);
|
||||||
|
this.phoneFormGroup.get('phoneNumber').patchValue(phoneNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (this.valueChange$) {
|
||||||
|
this.valueChange$.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
focus() {
|
||||||
|
const phoneNumber = this.phoneFormGroup.get('phoneNumber');
|
||||||
|
this.phoneFormGroup.markAsPristine();
|
||||||
|
this.phoneFormGroup.markAsUntouched();
|
||||||
|
if (!phoneNumber.value) {
|
||||||
|
phoneNumber.patchValue(this.countryCallingCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getFlagAndPhoneNumberData(country) {
|
||||||
|
if (this.enableFlagsSelect) {
|
||||||
|
this.flagIcon = this.getFlagIcon(country);
|
||||||
|
}
|
||||||
|
this.getPhoneNumberData(country);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getPhoneNumberData(country): void {
|
||||||
|
const phoneData = getExampleNumber(country, examples);
|
||||||
|
this.phonePlaceholder = phoneData.number;
|
||||||
|
this.countryCallingCode = '+' + phoneData.countryCallingCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getFlagIcon(countryCode) {
|
||||||
|
return String.fromCodePoint(...countryCode.split('').map(country => this.baseCode + country.charCodeAt(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
validatePhoneNumber(): ValidatorFn {
|
||||||
|
return (c: FormControl) => {
|
||||||
|
const phoneNumber = c.value;
|
||||||
|
if (phoneNumber) {
|
||||||
|
const parsedPhoneNumber = parsePhoneNumberFromString(phoneNumber);
|
||||||
|
if (!parsedPhoneNumber?.isValid() || !parsedPhoneNumber?.isPossible()) {
|
||||||
|
return {
|
||||||
|
invalidPhoneNumber: {
|
||||||
|
valid: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
validate(): ValidationErrors | null {
|
||||||
|
return this.phoneFormGroup.get('phoneNumber').valid ? null : {
|
||||||
|
phoneFormGroup: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: any): void {
|
||||||
|
this.propagateChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(fn: any): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabledState(isDisabled: boolean): void {
|
||||||
|
this.disabled = isDisabled;
|
||||||
|
if (isDisabled) {
|
||||||
|
this.phoneFormGroup.disable({emitEvent: false});
|
||||||
|
} else {
|
||||||
|
this.phoneFormGroup.enable({emitEvent: false});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeValue(phoneNumber): void {
|
||||||
|
this.modelValue = phoneNumber;
|
||||||
|
const country = phoneNumber ? parsePhoneNumberFromString(phoneNumber)?.country : this.defaultCountry;
|
||||||
|
this.getFlagAndPhoneNumberData(country);
|
||||||
|
this.phoneFormGroup.patchValue({phoneNumber, country}, {emitEvent: !phoneNumber});
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateModel() {
|
||||||
|
const phoneNumber = this.phoneFormGroup.get('phoneNumber');
|
||||||
|
if (phoneNumber.valid && phoneNumber.value) {
|
||||||
|
this.modelValue = phoneNumber.value;
|
||||||
|
this.propagateChange(this.modelValue);
|
||||||
|
} else {
|
||||||
|
this.propagateChange(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
520
ui-ngx/src/app/shared/models/country.models.ts
Normal file
520
ui-ngx/src/app/shared/models/country.models.ts
Normal file
@ -0,0 +1,520 @@
|
|||||||
|
///
|
||||||
|
/// Copyright © 2016-2022 The Thingsboard Authors
|
||||||
|
///
|
||||||
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
/// you may not use this file except in compliance with the License.
|
||||||
|
/// You may obtain a copy of the License at
|
||||||
|
///
|
||||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
///
|
||||||
|
/// Unless required by applicable law or agreed to in writing, software
|
||||||
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
/// See the License for the specific language governing permissions and
|
||||||
|
/// limitations under the License.
|
||||||
|
///
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
export interface Country {
|
||||||
|
name: string;
|
||||||
|
iso2: string;
|
||||||
|
dialCode: string;
|
||||||
|
areaCodes?: string[];
|
||||||
|
flag: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum CountryISO {
|
||||||
|
Afghanistan = 'AF',
|
||||||
|
Albania = 'AL',
|
||||||
|
Algeria = 'DZ',
|
||||||
|
AmericanSamoa = 'AS',
|
||||||
|
Andorra = 'AD',
|
||||||
|
Angola = 'AO',
|
||||||
|
Anguilla = 'AI',
|
||||||
|
AntiguaAndBarbuda = 'AG',
|
||||||
|
Argentina = 'AR',
|
||||||
|
Armenia = 'AM',
|
||||||
|
Aruba = 'AW',
|
||||||
|
Australia = 'AU',
|
||||||
|
Austria = 'AT',
|
||||||
|
Azerbaijan = 'AZ',
|
||||||
|
Bahamas = 'BS',
|
||||||
|
Bahrain = 'BH',
|
||||||
|
Bangladesh = 'BD',
|
||||||
|
Barbados = 'BB',
|
||||||
|
Belarus = 'BY',
|
||||||
|
Belgium = 'BE',
|
||||||
|
Belize = 'BZ',
|
||||||
|
Benin = 'BJ',
|
||||||
|
Bermuda = 'BM',
|
||||||
|
Bhutan = 'BT',
|
||||||
|
Bolivia = 'BO',
|
||||||
|
BosniaAndHerzegovina = 'BA',
|
||||||
|
Botswana = 'BW',
|
||||||
|
Brazil = 'BR',
|
||||||
|
BritishIndianOceanTerritory = 'IO',
|
||||||
|
BritishVirginIslands = 'VG',
|
||||||
|
Brunei = 'BN',
|
||||||
|
Bulgaria = 'BG',
|
||||||
|
BurkinaFaso = 'BF',
|
||||||
|
Burundi = 'BI',
|
||||||
|
Cambodia = 'KH',
|
||||||
|
Cameroon = 'CM',
|
||||||
|
Canada = 'CA',
|
||||||
|
CapeVerde = 'CV',
|
||||||
|
CaribbeanNetherlands = 'BQ',
|
||||||
|
CaymanIslands = 'KY',
|
||||||
|
CentralAfricanRepublic = 'CF',
|
||||||
|
Chad = 'TD',
|
||||||
|
Chile = 'CL',
|
||||||
|
China = 'CN',
|
||||||
|
ChristmasIsland = 'CX',
|
||||||
|
Cocos = 'CC',
|
||||||
|
Colombia = 'CC',
|
||||||
|
Comoros = 'KM',
|
||||||
|
CongoDRCJamhuriYaKidemokrasiaYaKongo = 'CD',
|
||||||
|
CongoRepublicCongoBrazzaville = 'CG',
|
||||||
|
CookIslands = 'CK',
|
||||||
|
CostaRica = 'CR',
|
||||||
|
CôteDIvoire = 'CI',
|
||||||
|
Croatia = 'HR',
|
||||||
|
Cuba = 'CU',
|
||||||
|
Curaçao = 'CW',
|
||||||
|
Cyprus = 'CY',
|
||||||
|
CzechRepublic = 'CZ',
|
||||||
|
Denmark = 'DK',
|
||||||
|
Djibouti = 'DJ',
|
||||||
|
Dominica = 'DM',
|
||||||
|
DominicanRepublic = 'DO',
|
||||||
|
Ecuador = 'EC',
|
||||||
|
Egypt = 'EG',
|
||||||
|
ElSalvador = 'SV',
|
||||||
|
EquatorialGuinea = 'GQ',
|
||||||
|
Eritrea = 'ER',
|
||||||
|
Estonia = 'EE',
|
||||||
|
Ethiopia = 'ET',
|
||||||
|
FalklandIslands = 'FK',
|
||||||
|
FaroeIslands = 'FO',
|
||||||
|
Fiji = 'FJ',
|
||||||
|
Finland = 'FI',
|
||||||
|
France = 'FR',
|
||||||
|
FrenchGuiana = 'GF',
|
||||||
|
FrenchPolynesia = 'PF',
|
||||||
|
Gabon = 'GA',
|
||||||
|
Gambia = 'GM',
|
||||||
|
Georgia = 'GE',
|
||||||
|
Germany = 'DE',
|
||||||
|
Ghana = 'GH',
|
||||||
|
Gibraltar = 'GI',
|
||||||
|
Greece = 'GR',
|
||||||
|
Greenland = 'GL',
|
||||||
|
Grenada = 'GD',
|
||||||
|
Guadeloupe = 'GP',
|
||||||
|
Guam = 'GU',
|
||||||
|
Guatemala = 'GT',
|
||||||
|
Guernsey = 'GG',
|
||||||
|
Guinea = 'GN',
|
||||||
|
GuineaBissau = 'GW',
|
||||||
|
Guyana = 'GY',
|
||||||
|
Haiti = 'HT',
|
||||||
|
Honduras = 'HN',
|
||||||
|
HongKong = 'HK',
|
||||||
|
Hungary = 'HU',
|
||||||
|
Iceland = 'IS',
|
||||||
|
India = 'IN',
|
||||||
|
Indonesia = 'ID',
|
||||||
|
Iran = 'IR',
|
||||||
|
Iraq = 'IQ',
|
||||||
|
Ireland = 'IE',
|
||||||
|
IsleOfMan = 'IM',
|
||||||
|
Israel = 'IL',
|
||||||
|
Italy = 'IT',
|
||||||
|
Jamaica = 'JM',
|
||||||
|
Japan = 'JP',
|
||||||
|
Jersey = 'JE',
|
||||||
|
Jordan = 'JO',
|
||||||
|
Kazakhstan = 'KZ',
|
||||||
|
Kenya = 'KE',
|
||||||
|
Kiribati = 'KI',
|
||||||
|
Kosovo = 'XK',
|
||||||
|
Kuwait = 'KW',
|
||||||
|
Kyrgyzstan = 'KG',
|
||||||
|
Laos = 'LA',
|
||||||
|
Latvia = 'LV',
|
||||||
|
Lebanon = 'LB',
|
||||||
|
Lesotho = 'LS',
|
||||||
|
Liberia = 'LR',
|
||||||
|
Libya = 'LY',
|
||||||
|
Liechtenstein = 'LI',
|
||||||
|
Lithuania = 'LT',
|
||||||
|
Luxembourg = 'LU',
|
||||||
|
Macau = 'MO',
|
||||||
|
Macedonia = 'MK',
|
||||||
|
Madagascar = 'MG',
|
||||||
|
Malawi = 'MW',
|
||||||
|
Malaysia = 'MY',
|
||||||
|
Maldives = 'MV',
|
||||||
|
Mali = 'ML',
|
||||||
|
Malta = 'MT',
|
||||||
|
MarshallIslands = 'MH',
|
||||||
|
Martinique = 'MQ',
|
||||||
|
Mauritania = 'MR',
|
||||||
|
Mauritius = 'MU',
|
||||||
|
Mayotte = 'YT',
|
||||||
|
Mexico = 'MX',
|
||||||
|
Micronesia = 'FM',
|
||||||
|
Moldova = 'MD',
|
||||||
|
Monaco = 'MC',
|
||||||
|
Mongolia = 'MN',
|
||||||
|
Montenegro = 'ME',
|
||||||
|
Montserrat = 'MS',
|
||||||
|
Morocco = 'MA',
|
||||||
|
Mozambique = 'MZ',
|
||||||
|
Myanmar = 'MM',
|
||||||
|
Namibia = 'NA',
|
||||||
|
Nauru = 'NR',
|
||||||
|
Nepal = 'NP',
|
||||||
|
Netherlands = 'NL',
|
||||||
|
NewCaledonia = 'NC',
|
||||||
|
NewZealand = 'NZ',
|
||||||
|
Nicaragua = 'NI',
|
||||||
|
Niger = 'NE',
|
||||||
|
Nigeria = 'NG',
|
||||||
|
Niue = 'NU',
|
||||||
|
NorfolkIsland = 'NF',
|
||||||
|
NorthKorea = 'KP',
|
||||||
|
NorthernMarianaIslands = 'MP',
|
||||||
|
Norway = 'NO',
|
||||||
|
Oman = 'OM',
|
||||||
|
Pakistan = 'PK',
|
||||||
|
Palau = 'PW',
|
||||||
|
Palestine = 'PS',
|
||||||
|
Panama = 'PA',
|
||||||
|
PapuaNewGuinea = 'PG',
|
||||||
|
Paraguay = 'PY',
|
||||||
|
Peru = 'PE',
|
||||||
|
Philippines = 'PH',
|
||||||
|
Poland = 'PL',
|
||||||
|
Portugal = 'PT',
|
||||||
|
PuertoRico = 'PR',
|
||||||
|
Qatar = 'QA',
|
||||||
|
Réunion = 'RE',
|
||||||
|
Romania = 'RO',
|
||||||
|
Russia = 'RU',
|
||||||
|
Rwanda = 'RW',
|
||||||
|
SaintBarthélemy = 'BL',
|
||||||
|
SaintHelena = 'SH',
|
||||||
|
SaintKittsAndNevis = 'KN',
|
||||||
|
SaintLucia = 'LC',
|
||||||
|
SaintMartin = 'MF',
|
||||||
|
SaintPierreAndMiquelon = 'PM',
|
||||||
|
SaintVincentAndTheGrenadines = 'VC',
|
||||||
|
Samoa = 'WS',
|
||||||
|
SanMarino = 'SM',
|
||||||
|
SãoToméAndPríncipe = 'ST',
|
||||||
|
SaudiArabia = 'SA',
|
||||||
|
Senegal = 'SN',
|
||||||
|
Serbia = 'RS',
|
||||||
|
Seychelles = 'SC',
|
||||||
|
SierraLeone = 'SL',
|
||||||
|
Singapore = 'SG',
|
||||||
|
SintMaarten = 'SX',
|
||||||
|
Slovakia = 'SK',
|
||||||
|
Slovenia = 'SI',
|
||||||
|
SolomonIslands = 'SB',
|
||||||
|
Somalia = 'SO',
|
||||||
|
SouthAfrica = 'ZA',
|
||||||
|
SouthKorea = 'KR',
|
||||||
|
SouthSudan = 'SS',
|
||||||
|
Spain = 'ES',
|
||||||
|
SriLanka = 'LK',
|
||||||
|
Sudan = 'SD',
|
||||||
|
Suriname = 'SR',
|
||||||
|
SvalbardAndJanMayen = 'SJ',
|
||||||
|
Swaziland = 'SZ',
|
||||||
|
Sweden = 'SE',
|
||||||
|
Switzerland = 'CH',
|
||||||
|
Syria = 'SY',
|
||||||
|
Taiwan = 'TW',
|
||||||
|
Tajikistan = 'TJ',
|
||||||
|
Tanzania = 'TZ',
|
||||||
|
Thailand = 'TH',
|
||||||
|
TimorLeste = 'TL',
|
||||||
|
Togo = 'TG',
|
||||||
|
Tokelau = 'TK',
|
||||||
|
Tonga = 'TO',
|
||||||
|
TrinidadAndTobago = 'TT',
|
||||||
|
Tunisia = 'TN',
|
||||||
|
Turkey = 'TR',
|
||||||
|
Turkmenistan = 'TM',
|
||||||
|
TurksAndCaicosIslands = 'TC',
|
||||||
|
Tuvalu = 'TV',
|
||||||
|
USVirginIslands = 'VI',
|
||||||
|
Uganda = 'UG',
|
||||||
|
Ukraine = 'UA',
|
||||||
|
UnitedArabEmirates = 'AE',
|
||||||
|
UnitedKingdom = 'GB',
|
||||||
|
UnitedStates = 'US',
|
||||||
|
Uruguay = 'UY',
|
||||||
|
Uzbekistan = 'UZ',
|
||||||
|
Vanuatu = 'VU',
|
||||||
|
VaticanCity = 'VA',
|
||||||
|
Venezuela = 'VE',
|
||||||
|
Vietnam = 'VN',
|
||||||
|
WallisAndFutuna = 'WF',
|
||||||
|
WesternSahara = 'EH',
|
||||||
|
Yemen = 'YE',
|
||||||
|
Zambia = 'ZM',
|
||||||
|
Zimbabwe = 'ZW',
|
||||||
|
ÅlandIslands = 'AX',
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CountryData {
|
||||||
|
public allCountries: Array<Country> = [
|
||||||
|
{name: 'Afghanistan', iso2: CountryISO.Afghanistan, dialCode: '93', flag: '🇦🇫'},
|
||||||
|
{name: 'Albania', iso2: CountryISO.Albania, dialCode: '355', flag: '🇦🇱'},
|
||||||
|
{name: 'Algeria', iso2: CountryISO.Algeria, dialCode: '213', flag: '🇩🇿'},
|
||||||
|
{name: 'American Samoa', iso2: CountryISO.AmericanSamoa, dialCode: '1', flag: '🇦🇸'},
|
||||||
|
{name: 'Andorra', iso2: CountryISO.Andorra, dialCode: '376', flag: '🇦🇩'},
|
||||||
|
{name: 'Angola', iso2: CountryISO.Angola, dialCode: '244', flag: '🇦🇴'},
|
||||||
|
{name: 'Anguilla', iso2: CountryISO.Anguilla, dialCode: '1', flag: '🇦🇮'},
|
||||||
|
{name: 'Antigua and Barbuda', iso2: CountryISO.AntiguaAndBarbuda, dialCode: '1', flag: '🇦🇬'},
|
||||||
|
{name: 'Argentina', iso2: CountryISO.Argentina, dialCode: '54', flag: '🇦🇷'},
|
||||||
|
{name: 'Armenia', iso2: CountryISO.Armenia, dialCode: '374', flag: '🇦🇲'},
|
||||||
|
{name: 'Aruba', iso2: CountryISO.Aruba, dialCode: '297', flag: '🇦🇼'},
|
||||||
|
{name: 'Australia', iso2: CountryISO.Australia, dialCode: '61', flag: '🇦🇺'},
|
||||||
|
{name: 'Austria', iso2: CountryISO.Austria, dialCode: '43', flag: '🇦🇹'},
|
||||||
|
{name: 'Azerbaijan', iso2: CountryISO.Azerbaijan, dialCode: '994', flag: '🇦🇿'},
|
||||||
|
{name: 'Bahamas', iso2: CountryISO.Bahamas, dialCode: '1', flag: '🇧🇸'},
|
||||||
|
{name: 'Bahrain', iso2: CountryISO.Bahrain, dialCode: '973', flag: '🇧🇭'},
|
||||||
|
{name: 'Bangladesh', iso2: CountryISO.Bangladesh, dialCode: '880', flag: '🇧🇩'},
|
||||||
|
{name: 'Barbados', iso2: CountryISO.Barbados, dialCode: '1', flag: '🇧🇧'},
|
||||||
|
{name: 'Belarus', iso2: CountryISO.Belarus, dialCode: '375', flag: '🇧🇾'},
|
||||||
|
{name: 'Belgium', iso2: CountryISO.Belgium, dialCode: '32', flag: '🇧🇪'},
|
||||||
|
{name: 'Belize', iso2: CountryISO.Belize, dialCode: '501', flag: '🇧🇿'},
|
||||||
|
{name: 'Benin', iso2: CountryISO.Benin, dialCode: '229', flag: '🇧🇯'},
|
||||||
|
{name: 'Bermuda', iso2: CountryISO.Bermuda, dialCode: '1', flag: '🇧🇲'},
|
||||||
|
{name: 'Bhutan', iso2: CountryISO.Bhutan, dialCode: '975', flag: '🇧🇹'},
|
||||||
|
{name: 'Bolivia', iso2: CountryISO.Bolivia, dialCode: '591', flag: '🇧🇴'},
|
||||||
|
{name: 'Bosnia and Herzegovina', iso2: CountryISO.BosniaAndHerzegovina, dialCode: '387', flag: '🇧🇦'},
|
||||||
|
{name: 'Botswana', iso2: CountryISO.Botswana, dialCode: '267', flag: '🇧🇼'},
|
||||||
|
{name: 'Brazil', iso2: CountryISO.Brazil, dialCode: '55', flag: '🇧🇷'},
|
||||||
|
{name: 'British Indian Ocean Territory', iso2: CountryISO.BritishIndianOceanTerritory, dialCode: '246', flag: '🇮🇴'},
|
||||||
|
{name: 'British Virgin Islands', iso2: CountryISO.BritishVirginIslands, dialCode: '1', flag: '🇻🇬'},
|
||||||
|
{name: 'Brunei', iso2: CountryISO.Brunei, dialCode: '673', flag: '🇧🇳'},
|
||||||
|
{name: 'Bulgaria', iso2: CountryISO.Bulgaria, dialCode: '359', flag: '🇧🇬'},
|
||||||
|
{name: 'Burkina Faso', iso2: CountryISO.BurkinaFaso, dialCode: '226', flag: '🇧🇫'},
|
||||||
|
{name: 'Burundi', iso2: CountryISO.Burundi, dialCode: '257', flag: '🇧🇮'},
|
||||||
|
{name: 'Cambodia', iso2: CountryISO.Cambodia, dialCode: '855', flag: '🇰🇭'},
|
||||||
|
{name: 'Cameroon', iso2: CountryISO.Cameroon, dialCode: '237', flag: '🇨🇲'},
|
||||||
|
{name: 'Canada', iso2: CountryISO.Canada, dialCode: '1', flag: '🇨🇦'},
|
||||||
|
{name: 'Cape Verde', iso2: CountryISO.CapeVerde, dialCode: '238', flag: '🇨🇻'},
|
||||||
|
{name: 'Caribbean Netherlands', iso2: CountryISO.CaribbeanNetherlands, dialCode: '599', flag: '🇧🇶'},
|
||||||
|
{name: 'Cayman Islands', iso2: CountryISO.CaymanIslands, dialCode: '1', flag: '🇰🇾'},
|
||||||
|
{name: 'Central African Republic', iso2: CountryISO.CentralAfricanRepublic, dialCode: '236', flag: '🇨🇫'},
|
||||||
|
{name: 'Chad', iso2: CountryISO.Chad, dialCode: '235', flag: '🇹🇩'},
|
||||||
|
{name: 'Chile', iso2: CountryISO.Chile, dialCode: '56', flag: '🇨🇱'},
|
||||||
|
{name: 'China', iso2: CountryISO.China, dialCode: '86', flag: '🇨🇳'},
|
||||||
|
{name: 'Christmas Island', iso2: CountryISO.ChristmasIsland, dialCode: '61', flag: '🇨🇽'},
|
||||||
|
{name: 'Cocos Islands', iso2: CountryISO.Cocos, dialCode: '61', flag: '🇨🇨'},
|
||||||
|
{name: 'Colombia', iso2: CountryISO.Colombia, dialCode: '57', flag: '🇨🇨'},
|
||||||
|
{name: 'Comoros', iso2: CountryISO.Comoros, dialCode: '269', flag: '🇰🇲'},
|
||||||
|
{name: 'Congo-Kinshasa', iso2: CountryISO.CongoDRCJamhuriYaKidemokrasiaYaKongo, dialCode: '243', flag: '🇨🇩'},
|
||||||
|
{name: 'Congo-Brazzaville', iso2: CountryISO.CongoRepublicCongoBrazzaville, dialCode: '242', flag: '🇨🇬'},
|
||||||
|
{name: 'Cook Islands', iso2: CountryISO.CookIslands, dialCode: '682', flag: '🇨🇰'},
|
||||||
|
{name: 'Costa Rica', iso2: CountryISO.CostaRica, dialCode: '506', flag: '🇨🇷'},
|
||||||
|
{name: 'Côte d’Ivoire', iso2: CountryISO.CôteDIvoire, dialCode: '225', flag: '🇨🇮'},
|
||||||
|
{name: 'Croatia', iso2: CountryISO.Croatia, dialCode: '385', flag: '🇭🇷'},
|
||||||
|
{name: 'Cuba', iso2: CountryISO.Cuba, dialCode: '53', flag: '🇨🇺'},
|
||||||
|
{name: 'Curaçao', iso2: CountryISO.Curaçao, dialCode: '599', flag: '🇨🇼'},
|
||||||
|
{name: 'Cyprus', iso2: CountryISO.Cyprus, dialCode: '357', flag: '🇨🇾'},
|
||||||
|
{name: 'Czech Republic', iso2: CountryISO.CzechRepublic, dialCode: '420', flag: '🇨🇿'},
|
||||||
|
{name: 'Denmark', iso2: CountryISO.Denmark, dialCode: '45', flag: '🇩🇰'},
|
||||||
|
{name: 'Djibouti', iso2: CountryISO.Djibouti, dialCode: '253', flag: '🇩🇯'},
|
||||||
|
{name: 'Dominica', iso2: CountryISO.Dominica, dialCode: '1767', flag: '🇩🇲'},
|
||||||
|
{name: 'Dominican Republic', iso2: CountryISO.DominicanRepublic, dialCode: '1', flag: '🇩🇴'},
|
||||||
|
{name: 'Ecuador', iso2: CountryISO.Ecuador, dialCode: '593', flag: '🇪🇨'},
|
||||||
|
{name: 'Egypt', iso2: CountryISO.Egypt, dialCode: '20', flag: '🇪🇬'},
|
||||||
|
{name: 'El Salvador', iso2: CountryISO.ElSalvador, dialCode: '503', flag: '🇸🇻'},
|
||||||
|
{name: 'Equatorial Guinea', iso2: CountryISO.EquatorialGuinea, dialCode: '240', flag: '🇬🇶'},
|
||||||
|
{name: 'Eritrea', iso2: CountryISO.Eritrea, dialCode: '291', flag: '🇪🇷'},
|
||||||
|
{name: 'Estonia', iso2: CountryISO.Estonia, dialCode: '372', flag: '🇪🇪'},
|
||||||
|
{name: 'Ethiopia', iso2: CountryISO.Ethiopia, dialCode: '251', flag: '🇪🇹'},
|
||||||
|
{name: 'Falkland Islands', iso2: CountryISO.FalklandIslands, dialCode: '500', flag: '🇫🇰'},
|
||||||
|
{name: 'Faroe Islands', iso2: CountryISO.FaroeIslands, dialCode: '298', flag: '🇫🇴'},
|
||||||
|
{name: 'Fiji', iso2: CountryISO.Fiji, dialCode: '679', flag: '🇫🇯'},
|
||||||
|
{name: 'Finland', iso2: CountryISO.Finland, dialCode: '358', flag: '🇫🇮'},
|
||||||
|
{name: 'France', iso2: CountryISO.France, dialCode: '33', flag: '🇫🇷'},
|
||||||
|
{name: 'French Guiana', iso2: CountryISO.FrenchGuiana, dialCode: '594', flag: '🇬🇫'},
|
||||||
|
{name: 'French Polynesia', iso2: CountryISO.FrenchPolynesia, dialCode: '689', flag: '🇵🇫'},
|
||||||
|
{name: 'Gabon', iso2: CountryISO.Gabon, dialCode: '241', flag: '🇬🇦'},
|
||||||
|
{name: 'Gambia', iso2: CountryISO.Gambia, dialCode: '220', flag: '🇬🇲'},
|
||||||
|
{name: 'Georgia', iso2: CountryISO.Georgia, dialCode: '995', flag: '🇬🇪'},
|
||||||
|
{name: 'Germany', iso2: CountryISO.Germany, dialCode: '49', flag: '🇩🇪'},
|
||||||
|
{name: 'Ghana', iso2: CountryISO.Ghana, dialCode: '233', flag: '🇬🇭'},
|
||||||
|
{name: 'Gibraltar', iso2: CountryISO.Gibraltar, dialCode: '350', flag: '🇬🇮'},
|
||||||
|
{name: 'Greece', iso2: CountryISO.Greece, dialCode: '30', flag: '🇬🇷'},
|
||||||
|
{name: 'Greenland', iso2: CountryISO.Greenland, dialCode: '299', flag: '🇬🇱'},
|
||||||
|
{name: 'Grenada', iso2: CountryISO.Grenada, dialCode: '1473', flag: '🇬🇩'},
|
||||||
|
{name: 'Guadeloupe', iso2: CountryISO.Guadeloupe, dialCode: '590', flag: '🇬🇵'},
|
||||||
|
{name: 'Guam', iso2: CountryISO.Guam, dialCode: '1', flag: '🇬🇺'},
|
||||||
|
{name: 'Guatemala', iso2: CountryISO.Guatemala, dialCode: '502', flag: '🇬🇹'},
|
||||||
|
{name: 'Guernsey', iso2: CountryISO.Guernsey, dialCode: '44', flag: '🇬🇬'},
|
||||||
|
{name: 'Guinea', iso2: CountryISO.Guinea, dialCode: '224', flag: '🇬🇳'},
|
||||||
|
{name: 'Guinea-Bissau', iso2: CountryISO.GuineaBissau, dialCode: '245', flag: '🇬🇼'},
|
||||||
|
{name: 'Guyana', iso2: CountryISO.Guyana, dialCode: '592', flag: '🇬🇾'},
|
||||||
|
{name: 'Haiti', iso2: CountryISO.Haiti, dialCode: '509', flag: '🇭🇹'},
|
||||||
|
{name: 'Honduras', iso2: CountryISO.Honduras, dialCode: '504', flag: '🇭🇳'},
|
||||||
|
{name: 'Hong Kong', iso2: CountryISO.HongKong, dialCode: '852', flag: '🇭🇰'},
|
||||||
|
{name: 'Hungary', iso2: CountryISO.Hungary, dialCode: '36', flag: '🇭🇺'},
|
||||||
|
{name: 'Iceland', iso2: CountryISO.Iceland, dialCode: '354', flag: '🇮🇸'},
|
||||||
|
{name: 'India', iso2: CountryISO.India, dialCode: '91', flag: '🇮🇳'},
|
||||||
|
{name: 'Indonesia', iso2: CountryISO.Indonesia, dialCode: '62', flag: '🇮🇩'},
|
||||||
|
{name: 'Iran', iso2: CountryISO.Iran, dialCode: '98', flag: '🇮🇷'},
|
||||||
|
{name: 'Iraq', iso2: CountryISO.Iraq, dialCode: '964', flag: '🇮🇶'},
|
||||||
|
{name: 'Ireland', iso2: CountryISO.Ireland, dialCode: '353', flag: '🇮🇪'},
|
||||||
|
{name: 'Isle of Man', iso2: CountryISO.IsleOfMan, dialCode: '44', flag: '🇮🇲'},
|
||||||
|
{name: 'Israel', iso2: CountryISO.Israel, dialCode: '972', flag: '🇮🇱'},
|
||||||
|
{name: 'Italy', iso2: CountryISO.Italy, dialCode: '39', flag: '🇮🇹'},
|
||||||
|
{name: 'Jamaica', iso2: CountryISO.Jamaica, dialCode: '1', flag: '🇯🇲'},
|
||||||
|
{name: 'Japan', iso2: CountryISO.Japan, dialCode: '81', flag: '🇯🇵'},
|
||||||
|
{name: 'Jersey', iso2: CountryISO.Jersey, dialCode: '44', flag: '🇯🇪'},
|
||||||
|
{name: 'Jordan', iso2: CountryISO.Jordan, dialCode: '962', flag: '🇯🇴'},
|
||||||
|
{name: 'Kazakhstan', iso2: CountryISO.Kazakhstan, dialCode: '7', flag: '🇰🇿'},
|
||||||
|
{name: 'Kenya', iso2: CountryISO.Kenya, dialCode: '254', flag: '🇰🇪'},
|
||||||
|
{name: 'Kiribati', iso2: CountryISO.Kiribati, dialCode: '686', flag: '🇰🇮'},
|
||||||
|
{name: 'Kosovo', iso2: CountryISO.Kosovo, dialCode: '383', flag: '🇽🇰'},
|
||||||
|
{name: 'Kuwait', iso2: CountryISO.Kuwait, dialCode: '965', flag: '🇰🇼'},
|
||||||
|
{name: 'Kyrgyzstan', iso2: CountryISO.Kyrgyzstan, dialCode: '996', flag: '🇰🇬'},
|
||||||
|
{name: 'Laos', iso2: CountryISO.Laos, dialCode: '856', flag: '🇱🇦'},
|
||||||
|
{name: 'Latvia', iso2: CountryISO.Latvia, dialCode: '371', flag: '🇱🇻'},
|
||||||
|
{name: 'Lebanon', iso2: CountryISO.Lebanon, dialCode: '961', flag: '🇱🇧'},
|
||||||
|
{name: 'Lesotho', iso2: CountryISO.Lesotho, dialCode: '266', flag: '🇱🇸'},
|
||||||
|
{name: 'Liberia', iso2: CountryISO.Liberia, dialCode: '231', flag: '🇱🇷'},
|
||||||
|
{name: 'Libya', iso2: CountryISO.Libya, dialCode: '218', flag: '🇱🇾'},
|
||||||
|
{name: 'Liechtenstein', iso2: CountryISO.Liechtenstein, dialCode: '423', flag: '🇱🇮'},
|
||||||
|
{name: 'Lithuania', iso2: CountryISO.Lithuania, dialCode: '370', flag: '🇱🇹'},
|
||||||
|
{name: 'Luxembourg', iso2: CountryISO.Luxembourg, dialCode: '352', flag: '🇱🇺'},
|
||||||
|
{name: 'Macau', iso2: CountryISO.Macau, dialCode: '853', flag: '🇲🇴'},
|
||||||
|
{name: 'Macedonia', iso2: CountryISO.Macedonia, dialCode: '389', flag: '🇲🇰'},
|
||||||
|
{name: 'Madagascar', iso2: CountryISO.Madagascar, dialCode: '261', flag: '🇲🇬'},
|
||||||
|
{name: 'Malawi', iso2: CountryISO.Malawi, dialCode: '265', flag: '🇲🇼'},
|
||||||
|
{name: 'Malaysia', iso2: CountryISO.Malaysia, dialCode: '60', flag: '🇲🇾'},
|
||||||
|
{name: 'Maldives', iso2: CountryISO.Maldives, dialCode: '960', flag: '🇲🇻'},
|
||||||
|
{name: 'Mali', iso2: CountryISO.Mali, dialCode: '223', flag: '🇲🇱'},
|
||||||
|
{name: 'Malta', iso2: CountryISO.Malta, dialCode: '356', flag: '🇲🇹'},
|
||||||
|
{name: 'Marshall Islands', iso2: CountryISO.MarshallIslands, dialCode: '692', flag: '🇲🇭'},
|
||||||
|
{name: 'Martinique', iso2: CountryISO.Martinique, dialCode: '596', flag: '🇲🇶'},
|
||||||
|
{name: 'Mauritania', iso2: CountryISO.Mauritania, dialCode: '222', flag: '🇲🇷'},
|
||||||
|
{name: 'Mauritius', iso2: CountryISO.Mauritius, dialCode: '230', flag: '🇲🇺'},
|
||||||
|
{name: 'Mayotte', iso2: CountryISO.Mayotte, dialCode: '262', flag: '🇾🇹'},
|
||||||
|
{name: 'Mexico', iso2: CountryISO.Mexico, dialCode: '52', flag: '🇲🇽'},
|
||||||
|
{name: 'Micronesia', iso2: CountryISO.Micronesia, dialCode: '691', flag: '🇫🇲'},
|
||||||
|
{name: 'Moldova', iso2: CountryISO.Moldova, dialCode: '373', flag: '🇲🇩'},
|
||||||
|
{name: 'Monaco', iso2: CountryISO.Monaco, dialCode: '377', flag: '🇲🇨'},
|
||||||
|
{name: 'Mongolia', iso2: CountryISO.Mongolia, dialCode: '976', flag: '🇲🇳'},
|
||||||
|
{name: 'Montenegro', iso2: CountryISO.Montenegro, dialCode: '382', flag: '🇲🇪'},
|
||||||
|
{name: 'Montserrat', iso2: CountryISO.Montserrat, dialCode: '1', flag: '🇲🇸'},
|
||||||
|
{name: 'Morocco', iso2: CountryISO.Morocco, dialCode: '212', flag: '🇲🇦'},
|
||||||
|
{name: 'Mozambique', iso2: CountryISO.Mozambique, dialCode: '258', flag: '🇲🇿'},
|
||||||
|
{name: 'Myanmar', iso2: CountryISO.Myanmar, dialCode: '95', flag: '🇲🇲'},
|
||||||
|
{name: 'Namibia', iso2: CountryISO.Namibia, dialCode: '264', flag: '🇳🇦'},
|
||||||
|
{name: 'Nauru', iso2: CountryISO.Nauru, dialCode: '674', flag: '🇳🇷'},
|
||||||
|
{name: 'Nepal', iso2: CountryISO.Nepal, dialCode: '977', flag: '🇳🇵'},
|
||||||
|
{name: 'Netherlands', iso2: CountryISO.Netherlands, dialCode: '31', flag: '🇳🇱'},
|
||||||
|
{name: 'New Caledonia', iso2: CountryISO.NewCaledonia, dialCode: '687', flag: '🇳🇨'},
|
||||||
|
{name: 'New Zealand', iso2: CountryISO.NewZealand, dialCode: '64', flag: '🇳🇿'},
|
||||||
|
{name: 'Nicaragua', iso2: CountryISO.Nicaragua, dialCode: '505', flag: '🇳🇮'},
|
||||||
|
{name: 'Niger', iso2: CountryISO.Niger, dialCode: '227', flag: '🇳🇪'},
|
||||||
|
{name: 'Nigeria', iso2: CountryISO.Nigeria, dialCode: '234', flag: '🇳🇬'},
|
||||||
|
{name: 'Niue', iso2: CountryISO.Niue, dialCode: '683', flag: '🇳🇺'},
|
||||||
|
{name: 'Norfolk Island', iso2: CountryISO.NorfolkIsland, dialCode: '672', flag: '🇳🇫'},
|
||||||
|
{name: 'North Korea', iso2: CountryISO.NorthKorea, dialCode: '850', flag: '🇰🇵'},
|
||||||
|
{name: 'Northern Mariana Islands', iso2: CountryISO.NorthernMarianaIslands, dialCode: '1670', flag: '🇲🇵'},
|
||||||
|
{name: 'Norway', iso2: CountryISO.Norway, dialCode: '47', flag: '🇳🇴'},
|
||||||
|
{name: 'Oman', iso2: CountryISO.Oman, dialCode: '968', flag: '🇴🇲'},
|
||||||
|
{name: 'Pakistan', iso2: CountryISO.Pakistan, dialCode: '92', flag: '🇵🇰'},
|
||||||
|
{name: 'Palau', iso2: CountryISO.Palau, dialCode: '680', flag: '🇵🇼'},
|
||||||
|
{name: 'Palestine', iso2: CountryISO.Palestine, dialCode: '970', flag: '🇵🇸'},
|
||||||
|
{name: 'Panama', iso2: CountryISO.Panama, dialCode: '507', flag: '🇵🇦'},
|
||||||
|
{name: 'Papua New Guinea', iso2: CountryISO.PapuaNewGuinea, dialCode: '675', flag: '🇵🇬'},
|
||||||
|
{name: 'Paraguay', iso2: CountryISO.Paraguay, dialCode: '595', flag: '🇵🇾'},
|
||||||
|
{name: 'Peru', iso2: CountryISO.Peru, dialCode: '51', flag: '🇵🇪'},
|
||||||
|
{name: 'Philippines', iso2: CountryISO.Philippines, dialCode: '63', flag: '🇵🇭'},
|
||||||
|
{name: 'Poland', iso2: CountryISO.Poland, dialCode: '48', flag: '🇵🇱'},
|
||||||
|
{name: 'Portugal', iso2: CountryISO.Portugal, dialCode: '351', flag: '🇵🇹'},
|
||||||
|
{name: 'Puerto Rico', iso2: CountryISO.PuertoRico, dialCode: '1', flag: '🇵🇷'},
|
||||||
|
{name: 'Qatar', iso2: CountryISO.Qatar, dialCode: '974', flag: '🇶🇦'},
|
||||||
|
{name: 'Réunion', iso2: CountryISO.Réunion, dialCode: '262', flag: '🇷🇪'},
|
||||||
|
{name: 'Romania', iso2: CountryISO.Romania, dialCode: '40', flag: '🇷🇴'},
|
||||||
|
{name: 'Russia', iso2: CountryISO.Russia, dialCode: '7', flag: '🇷🇺'},
|
||||||
|
{name: 'Rwanda', iso2: CountryISO.Rwanda, dialCode: '250', flag: '🇷🇼'},
|
||||||
|
{name: 'Saint Barthélemy', iso2: CountryISO.SaintBarthélemy, dialCode: '590', flag: '🇧🇱'},
|
||||||
|
{name: 'Saint Helena', iso2: CountryISO.SaintHelena, dialCode: '290', flag: '🇸🇭'},
|
||||||
|
{name: 'Saint Kitts and Nevis', iso2: CountryISO.SaintKittsAndNevis, dialCode: '1869', flag: '🇰🇳'},
|
||||||
|
{name: 'Saint Lucia', iso2: CountryISO.SaintLucia, dialCode: '1', flag: '🇱🇨'},
|
||||||
|
{name: 'Saint Martin', iso2: CountryISO.SaintMartin, dialCode: '590', flag: '🇲🇫'},
|
||||||
|
{name: 'Saint Pierre and Miquelon', iso2: CountryISO.SaintPierreAndMiquelon, dialCode: '508', flag: '🇵🇲'},
|
||||||
|
{name: 'Saint Vincent and the Grenadines', iso2: CountryISO.SaintVincentAndTheGrenadines, dialCode: '1', flag: '🇻🇨'},
|
||||||
|
{name: 'Samoa', iso2: CountryISO.Samoa, dialCode: '685', flag: '🇼🇸'},
|
||||||
|
{name: 'San Marino', iso2: CountryISO.SanMarino, dialCode: '378', flag: '🇸🇲'},
|
||||||
|
{name: 'São Tomé and Príncipe', iso2: CountryISO.SãoToméAndPríncipe, dialCode: '239', flag: '🇸🇹'},
|
||||||
|
{name: 'Saudi Arabia', iso2: CountryISO.SaudiArabia, dialCode: '966', flag: '🇸🇦'},
|
||||||
|
{name: 'Senegal', iso2: CountryISO.Senegal, dialCode: '221', flag: '🇸🇳'},
|
||||||
|
{name: 'Serbia', iso2: CountryISO.Serbia, dialCode: '381', flag: '🇷🇸'},
|
||||||
|
{name: 'Seychelles', iso2: CountryISO.Seychelles, dialCode: '248', flag: '🇸🇨'},
|
||||||
|
{name: 'Sierra Leone', iso2: CountryISO.SierraLeone, dialCode: '232', flag: '🇸🇱'},
|
||||||
|
{name: 'Singapore', iso2: CountryISO.Singapore, dialCode: '65', flag: '🇸🇬'},
|
||||||
|
{name: 'Sint Maarten', iso2: CountryISO.SintMaarten, dialCode: '1', flag: '🇸🇽'},
|
||||||
|
{name: 'Slovakia', iso2: CountryISO.Slovakia, dialCode: '421', flag: '🇸🇰'},
|
||||||
|
{name: 'Slovenia', iso2: CountryISO.Slovenia, dialCode: '386', flag: '🇸🇮'},
|
||||||
|
{name: 'Solomon Islands', iso2: CountryISO.SolomonIslands, dialCode: '677', flag: '🇸🇧'},
|
||||||
|
{name: 'Somalia', iso2: CountryISO.Somalia, dialCode: '252', flag: '🇸🇴'},
|
||||||
|
{name: 'South Africa', iso2: CountryISO.SouthAfrica, dialCode: '27', flag: '🇿🇦'},
|
||||||
|
{name: 'South Korea', iso2: CountryISO.SouthKorea, dialCode: '82', flag: '🇰🇷'},
|
||||||
|
{name: 'South Sudan', iso2: CountryISO.SouthSudan, dialCode: '211', flag: '🇸🇸'},
|
||||||
|
{name: 'Spain', iso2: CountryISO.Spain, dialCode: '34', flag: '🇪🇸'},
|
||||||
|
{name: 'Sri Lanka', iso2: CountryISO.SriLanka, dialCode: '94', flag: '🇱🇰'},
|
||||||
|
{name: 'Sudan', iso2: CountryISO.Sudan, dialCode: '249', flag: '🇸🇩'},
|
||||||
|
{name: 'Suriname: ', iso2: CountryISO.Suriname, dialCode: '597', flag: '🇸🇷'},
|
||||||
|
{name: 'Svalbard and Jan Mayen', iso2: CountryISO.SvalbardAndJanMayen, dialCode: '47', flag: '🇸🇯'},
|
||||||
|
{name: 'Swaziland', iso2: CountryISO.Swaziland, dialCode: '268', flag: '🇸🇿'},
|
||||||
|
{name: 'Sweden', iso2: CountryISO.Sweden, dialCode: '46', flag: '🇸🇪'},
|
||||||
|
{name: 'Switzerland', iso2: CountryISO.Switzerland, dialCode: '41', flag: '🇨🇭'},
|
||||||
|
{name: 'Syria', iso2: CountryISO.Syria, dialCode: '963', flag: '🇸🇾'},
|
||||||
|
{name: 'Taiwan', iso2: CountryISO.Taiwan, dialCode: '886', flag: '🇹🇼'},
|
||||||
|
{name: 'Tajikistan', iso2: CountryISO.Tajikistan, dialCode: '992', flag: '🇹🇯'},
|
||||||
|
{name: 'Tanzania', iso2: CountryISO.Tanzania, dialCode: '255', flag: '🇹🇿'},
|
||||||
|
{name: 'Thailand', iso2: CountryISO.Thailand, dialCode: '66', flag: '🇹🇭'},
|
||||||
|
{name: 'Timor-Leste', iso2: CountryISO.TimorLeste, dialCode: '670', flag: '🇹🇱'},
|
||||||
|
{name: 'Togo', iso2: CountryISO.Togo, dialCode: '228', flag: '🇹🇬'},
|
||||||
|
{name: 'Tokelau', iso2: CountryISO.Tokelau, dialCode: '690', flag: '🇹🇰'},
|
||||||
|
{name: 'Tonga', iso2: CountryISO.Tonga, dialCode: '676', flag: '🇹🇴'},
|
||||||
|
{name: 'Trinidad and Tobago', iso2: CountryISO.TrinidadAndTobago, dialCode: '1', flag: '🇹🇹'},
|
||||||
|
{name: 'Tunisia', iso2: CountryISO.Tunisia, dialCode: '216', flag: '🇹🇳'},
|
||||||
|
{name: 'Turkey', iso2: CountryISO.Turkey, dialCode: '90', flag: '🇹🇷'},
|
||||||
|
{name: 'Turkmenistan', iso2: CountryISO.Turkmenistan, dialCode: '993', flag: '🇹🇲'},
|
||||||
|
{name: 'Turks and Caicos Islands', iso2: CountryISO.TurksAndCaicosIslands, dialCode: '1649', flag: '🇹🇨'},
|
||||||
|
{name: 'Tuvalu', iso2: CountryISO.Tuvalu, dialCode: '688', flag: '🇹🇻'},
|
||||||
|
{name: 'U.S. Virgin Islands', iso2: CountryISO.USVirginIslands, dialCode: '1', flag: '🇻🇮'},
|
||||||
|
{name: 'Uganda', iso2: CountryISO.Uganda, dialCode: '256', flag: '🇺🇬'},
|
||||||
|
{name: 'Ukraine', iso2: CountryISO.Ukraine, dialCode: '380', flag: '🇺🇦'},
|
||||||
|
{name: 'United Arab Emirates', iso2: CountryISO.UnitedArabEmirates, dialCode: '971', flag: '🇦🇪'},
|
||||||
|
{name: 'United Kingdom', iso2: CountryISO.UnitedKingdom, dialCode: '44', flag: '🇬🇧'},
|
||||||
|
{name: 'United States', iso2: CountryISO.UnitedStates, dialCode: '1', flag: '🇺🇸'},
|
||||||
|
{name: 'Uruguay', iso2: CountryISO.Uruguay, dialCode: '598', flag: '🇺🇾'},
|
||||||
|
{name: 'Uzbekistan', iso2: CountryISO.Uzbekistan, dialCode: '998', flag: '🇺🇿'},
|
||||||
|
{name: 'Vanuatu', iso2: CountryISO.Vanuatu, dialCode: '678', flag: '🇻🇺'},
|
||||||
|
{name: 'Vatican City', iso2: CountryISO.VaticanCity, dialCode: '39', flag: '🇻🇦'},
|
||||||
|
{name: 'Venezuela', iso2: CountryISO.Venezuela, dialCode: '58', flag: '🇻🇪'},
|
||||||
|
{name: 'Vietnam', iso2: CountryISO.Vietnam, dialCode: '84', flag: '🇻🇳'},
|
||||||
|
{name: 'Wallis and Futuna', iso2: CountryISO.WallisAndFutuna, dialCode: '681', flag: '🇼🇫'},
|
||||||
|
{name: 'Western Sahara', iso2: CountryISO.WesternSahara, dialCode: '212', flag: '🇪🇭'},
|
||||||
|
{name: 'Yemen', iso2: CountryISO.Yemen, dialCode: '967', flag: '🇾🇪'},
|
||||||
|
{name: 'Zambia', iso2: CountryISO.Zambia, dialCode: '260', flag: '🇿🇲'},
|
||||||
|
{name: 'Zimbabwe', iso2: CountryISO.Zimbabwe, dialCode: '263', flag: '🇿🇼'},
|
||||||
|
{name: 'Åland Islands', iso2: CountryISO.ÅlandIslands, dialCode: '358', flag: '🇦🇽'}
|
||||||
|
];
|
||||||
|
}
|
||||||
@ -163,6 +163,7 @@ import { HtmlComponent } from '@shared/components/html.component';
|
|||||||
import { SafePipe } from '@shared/pipe/safe.pipe';
|
import { SafePipe } from '@shared/pipe/safe.pipe';
|
||||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||||
import { MultipleImageInputComponent } from '@shared/components/multiple-image-input.component';
|
import { MultipleImageInputComponent } from '@shared/components/multiple-image-input.component';
|
||||||
|
import { PhoneInputComponent } from '@shared/components/phone-input.component';
|
||||||
|
|
||||||
export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) {
|
export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) {
|
||||||
return markedOptionsService;
|
return markedOptionsService;
|
||||||
@ -284,7 +285,8 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
|
|||||||
WidgetsBundleSearchComponent,
|
WidgetsBundleSearchComponent,
|
||||||
CopyButtonComponent,
|
CopyButtonComponent,
|
||||||
TogglePasswordComponent,
|
TogglePasswordComponent,
|
||||||
ProtobufContentComponent
|
ProtobufContentComponent,
|
||||||
|
PhoneInputComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@ -484,7 +486,8 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
|
|||||||
WidgetsBundleSearchComponent,
|
WidgetsBundleSearchComponent,
|
||||||
CopyButtonComponent,
|
CopyButtonComponent,
|
||||||
TogglePasswordComponent,
|
TogglePasswordComponent,
|
||||||
ProtobufContentComponent
|
ProtobufContentComponent,
|
||||||
|
PhoneInputComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class SharedModule { }
|
export class SharedModule { }
|
||||||
|
|||||||
@ -4449,6 +4449,13 @@
|
|||||||
"material-icons": "Material icons",
|
"material-icons": "Material icons",
|
||||||
"show-all": "Show all icons"
|
"show-all": "Show all icons"
|
||||||
},
|
},
|
||||||
|
"phone-input": {
|
||||||
|
"phone-input-label": "Phone number",
|
||||||
|
"phone-input-required": "Phone number is required",
|
||||||
|
"phone-input-validation": "Phone number is invalid or not possible",
|
||||||
|
"phone-input-pattern": "Invalid phone number. Should be in E.164 format, ex. {{phoneNumber}}",
|
||||||
|
"phone-input-hint": "Phone Number in E.164 format, ex. {{phoneNumber}}"
|
||||||
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"widget-action": {
|
"widget-action": {
|
||||||
"action-cell-button": "Action cell button",
|
"action-cell-button": "Action cell button",
|
||||||
|
|||||||
@ -6027,6 +6027,11 @@ less@4.1.1:
|
|||||||
needle "^2.5.2"
|
needle "^2.5.2"
|
||||||
source-map "~0.6.0"
|
source-map "~0.6.0"
|
||||||
|
|
||||||
|
libphonenumber-js@^1.10.4:
|
||||||
|
version "1.10.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.4.tgz#90397f0ed620262570a32244c9fbc389cc417ce4"
|
||||||
|
integrity sha512-9QWxEk4GW5RDnFzt8UtyRENfFpAN8u7Sbf9wf32tcXY9tdtnz1dKHIBwW2Wnfx8ypXJb9zUnTpK9aQJ/B8AlnA==
|
||||||
|
|
||||||
license-webpack-plugin@2.3.20:
|
license-webpack-plugin@2.3.20:
|
||||||
version "2.3.20"
|
version "2.3.20"
|
||||||
resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-2.3.20.tgz#f51fb674ca31519dbedbe1c7aabc036e5a7f2858"
|
resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-2.3.20.tgz#f51fb674ca31519dbedbe1c7aabc036e5a7f2858"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user