Compare commits
2 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
d96c9a54e2 | |
|
|
8691833c97 |
|
|
@ -0,0 +1,45 @@
|
|||
#!/bin/bash
|
||||
#set -x
|
||||
#******************************************************************************
|
||||
# @file : entrypoint.sh
|
||||
# @author : wangyubin
|
||||
# @date : 2018-08- 1 10:18:43
|
||||
#
|
||||
# @brief : entry point for manage service start order
|
||||
# history : init
|
||||
#******************************************************************************
|
||||
|
||||
: ${SLEEP_SECOND:=2}
|
||||
|
||||
wait_for() {
|
||||
echo Waiting for $1 to listen on $2...
|
||||
while ! nc -z $1 $2; do echo waiting...; sleep $SLEEP_SECOND; done
|
||||
}
|
||||
|
||||
declare DEPENDS
|
||||
declare CMD
|
||||
|
||||
while getopts "d:c:" arg
|
||||
do
|
||||
case $arg in
|
||||
d)
|
||||
DEPENDS=$OPTARG
|
||||
;;
|
||||
c)
|
||||
CMD=$OPTARG
|
||||
;;
|
||||
?)
|
||||
echo "unkonw argument"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
for var in ${DEPENDS//,/ }
|
||||
do
|
||||
host=${var%:*}
|
||||
port=${var#*:}
|
||||
wait_for $host $port
|
||||
done
|
||||
|
||||
eval $CMD
|
||||
|
|
@ -4,12 +4,14 @@ import io.micrometer.core.instrument.Clock;
|
|||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
|
||||
import lombok.Setter;
|
||||
import org.jetlinks.core.metadata.DataType;
|
||||
import org.jetlinks.core.metadata.types.StringType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
|
@ -23,18 +25,33 @@ public class MeterRegistryManager {
|
|||
|
||||
private Map<String, MeterRegistry> meterRegistryMap = new ConcurrentHashMap<>();
|
||||
|
||||
@Autowired
|
||||
private List<MeterRegistrySupplier> suppliers;
|
||||
private final List<MeterRegistrySupplier> suppliers;
|
||||
|
||||
private MeterRegistry createMeterRegistry(String metric, String... tagKeys) {
|
||||
return new CompositeMeterRegistry(Clock.SYSTEM,
|
||||
suppliers.stream()
|
||||
.map(supplier -> supplier.getMeterRegistry(metric, tagKeys))
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
public MeterRegistryManager(@Autowired(required = false) List<MeterRegistrySupplier> suppliers) {
|
||||
this.suppliers = suppliers == null ? new ArrayList<>() : suppliers;
|
||||
}
|
||||
|
||||
|
||||
private MeterRegistry createMeterRegistry(String metric, Map<String, DataType> tagDefine) {
|
||||
Map<String, DataType> tags = new HashMap<>(tagDefine);
|
||||
MeterRegistrySettings settings = tags::put;
|
||||
return new CompositeMeterRegistry(Clock.SYSTEM, suppliers
|
||||
.stream()
|
||||
.map(supplier -> supplier.getMeterRegistry(metric))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
public MeterRegistry getMeterRegister(String metric, String... tagKeys) {
|
||||
return meterRegistryMap.computeIfAbsent(metric, _metric -> createMeterRegistry(_metric, tagKeys));
|
||||
|
||||
return meterRegistryMap.computeIfAbsent(metric, _metric -> {
|
||||
if (tagKeys.length == 0) {
|
||||
return createMeterRegistry(metric, Collections.emptyMap());
|
||||
}
|
||||
return createMeterRegistry(metric, Arrays
|
||||
.stream(tagKeys)
|
||||
.collect(Collectors.toMap(Function.identity(), key -> StringType.GLOBAL)));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,58 +29,58 @@ import org.springframework.context.annotation.Configuration;
|
|||
@ConditionalOnBean(ProtocolSupports.class)
|
||||
public class DeviceClusterConfiguration {
|
||||
|
||||
@Bean
|
||||
public ClusterDeviceRegistry deviceRegistry(ProtocolSupports supports,
|
||||
ClusterManager manager,
|
||||
ConfigStorageManager storageManager,
|
||||
DeviceOperationBroker handler) {
|
||||
|
||||
return new ClusterDeviceRegistry(supports,
|
||||
storageManager,
|
||||
manager,
|
||||
handler,
|
||||
CaffeinatedGuava.build(Caffeine.newBuilder()));
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(ClusterDeviceRegistry.class)
|
||||
public BeanPostProcessor interceptorRegister(ClusterDeviceRegistry registry) {
|
||||
return new BeanPostProcessor() {
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof DeviceMessageSenderInterceptor) {
|
||||
registry.addInterceptor(((DeviceMessageSenderInterceptor) bean));
|
||||
}
|
||||
if (bean instanceof DeviceStateChecker) {
|
||||
registry.addStateChecker(((DeviceStateChecker) bean));
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean(initMethod = "init", destroyMethod = "shutdown")
|
||||
@ConditionalOnBean(RpcManager.class)
|
||||
public PersistenceDeviceSessionManager deviceSessionManager(RpcManager rpcManager) {
|
||||
|
||||
return new PersistenceDeviceSessionManager(rpcManager);
|
||||
}
|
||||
|
||||
@ConditionalOnBean(DecodedClientMessageHandler.class)
|
||||
@Bean
|
||||
public ClusterSendToDeviceMessageHandler defaultSendToDeviceMessageHandler(DeviceSessionManager sessionManager,
|
||||
DeviceRegistry registry,
|
||||
MessageHandler messageHandler,
|
||||
DecodedClientMessageHandler clientMessageHandler) {
|
||||
return new ClusterSendToDeviceMessageHandler(sessionManager, messageHandler, registry, clientMessageHandler);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RpcDeviceOperationBroker rpcDeviceOperationBroker(RpcManager rpcManager,
|
||||
DeviceSessionManager sessionManager) {
|
||||
return new RpcDeviceOperationBroker(rpcManager, sessionManager);
|
||||
}
|
||||
// @Bean
|
||||
// public ClusterDeviceRegistry deviceRegistry(ProtocolSupports supports,
|
||||
// ClusterManager manager,
|
||||
// ConfigStorageManager storageManager,
|
||||
// DeviceOperationBroker handler) {
|
||||
//
|
||||
// return new ClusterDeviceRegistry(supports,
|
||||
// storageManager,
|
||||
// manager,
|
||||
// handler,
|
||||
// CaffeinatedGuava.build(Caffeine.newBuilder()));
|
||||
// }
|
||||
//
|
||||
//
|
||||
// @Bean
|
||||
// @ConditionalOnBean(ClusterDeviceRegistry.class)
|
||||
// public BeanPostProcessor interceptorRegister(ClusterDeviceRegistry registry) {
|
||||
// return new BeanPostProcessor() {
|
||||
// @Override
|
||||
// public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
// if (bean instanceof DeviceMessageSenderInterceptor) {
|
||||
// registry.addInterceptor(((DeviceMessageSenderInterceptor) bean));
|
||||
// }
|
||||
// if (bean instanceof DeviceStateChecker) {
|
||||
// registry.addStateChecker(((DeviceStateChecker) bean));
|
||||
// }
|
||||
// return bean;
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// @Bean(initMethod = "init", destroyMethod = "shutdown")
|
||||
// @ConditionalOnBean(RpcManager.class)
|
||||
// public PersistenceDeviceSessionManager deviceSessionManager(RpcManager rpcManager) {
|
||||
//
|
||||
// return new PersistenceDeviceSessionManager(rpcManager);
|
||||
// }
|
||||
//
|
||||
// @ConditionalOnBean(DecodedClientMessageHandler.class)
|
||||
// @Bean
|
||||
// public ClusterSendToDeviceMessageHandler defaultSendToDeviceMessageHandler(DeviceSessionManager sessionManager,
|
||||
// DeviceRegistry registry,
|
||||
// MessageHandler messageHandler,
|
||||
// DecodedClientMessageHandler clientMessageHandler) {
|
||||
// return new ClusterSendToDeviceMessageHandler(sessionManager, messageHandler, registry, clientMessageHandler);
|
||||
// }
|
||||
//
|
||||
// @Bean
|
||||
// public RpcDeviceOperationBroker rpcDeviceOperationBroker(RpcManager rpcManager,
|
||||
// DeviceSessionManager sessionManager) {
|
||||
// return new RpcDeviceOperationBroker(rpcManager, sessionManager);
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import org.jetlinks.community.elastic.search.service.ElasticSearchService;
|
|||
import org.jetlinks.community.elastic.search.things.ElasticSearchColumnModeStrategy;
|
||||
import org.jetlinks.community.elastic.search.things.ElasticSearchRowModeStrategy;
|
||||
import org.jetlinks.community.things.data.ThingsDataRepositoryStrategy;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
|
@ -16,6 +17,7 @@ import org.springframework.context.annotation.Configuration;
|
|||
public class ElasticSearchThingDataConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(ElasticSearchService.class)
|
||||
public ElasticSearchColumnModeStrategy elasticSearchColumnModThingDataPolicy(
|
||||
ThingsRegistry registry,
|
||||
ElasticSearchService searchService,
|
||||
|
|
@ -26,6 +28,7 @@ public class ElasticSearchThingDataConfiguration {
|
|||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(ElasticSearchService.class)
|
||||
public ElasticSearchRowModeStrategy elasticSearchRowModThingDataPolicy(
|
||||
ThingsRegistry registry,
|
||||
ElasticSearchService searchService,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package org.jetlinks.community.elastic.search.index.strategies;
|
|||
import org.hswebframework.utils.time.DateFormatter;
|
||||
import org.jetlinks.community.elastic.search.index.ElasticSearchIndexProperties;
|
||||
import org.jetlinks.community.elastic.search.service.reactive.ReactiveElasticsearchClient;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
|
@ -15,6 +16,7 @@ import java.util.Date;
|
|||
* @since 1.0
|
||||
*/
|
||||
@Component
|
||||
@ConditionalOnBean(ReactiveElasticsearchClient.class)
|
||||
public class TimeByDayElasticSearchIndexStrategy extends TemplateElasticSearchIndexStrategy {
|
||||
|
||||
public TimeByDayElasticSearchIndexStrategy(ReactiveElasticsearchClient client, ElasticSearchIndexProperties properties) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package org.jetlinks.community.gateway.external.socket;
|
|||
import org.hswebframework.web.authorization.ReactiveAuthenticationManager;
|
||||
import org.hswebframework.web.authorization.token.UserTokenManager;
|
||||
import org.jetlinks.community.gateway.external.MessagingManager;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
|
@ -16,10 +17,10 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
|
||||
@Configuration
|
||||
//@ConditionalOnBean({
|
||||
// ReactiveAuthenticationManager.class,
|
||||
// UserTokenManager.class
|
||||
//})
|
||||
@ConditionalOnBean({
|
||||
ReactiveAuthenticationManager.class,
|
||||
UserTokenManager.class
|
||||
})
|
||||
public class WebSocketMessagingHandlerConfiguration {
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import org.jetlinks.core.event.Subscription;
|
|||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.stereotype.Component;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
|
@ -30,6 +31,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
@ConditionalOnBean(NetworkConfigManager.class)
|
||||
public class DefaultNetworkManager implements NetworkManager, BeanPostProcessor, CommandLineRunner {
|
||||
|
||||
private final NetworkConfigManager configManager;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
<module>script-component</module>
|
||||
<module>protocol-component</module>
|
||||
<module>relation-component</module>
|
||||
<module>test-component</module>
|
||||
</modules>
|
||||
|
||||
<artifactId>jetlinks-components</artifactId>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import org.jetlinks.community.elastic.search.index.ElasticSearchIndexManager;
|
|||
import org.jetlinks.community.rule.engine.event.handler.RuleEngineLoggerIndexProvider;
|
||||
import org.jetlinks.core.metadata.types.DateTimeType;
|
||||
import org.jetlinks.core.metadata.types.StringType;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
|
@ -16,6 +17,7 @@ import org.springframework.stereotype.Component;
|
|||
@Component
|
||||
@Order(1)
|
||||
@Slf4j
|
||||
@ConditionalOnBean(DefaultElasticSearchIndexMetadata.class)
|
||||
public class RuleEngineLogIndexInitialize {
|
||||
|
||||
public RuleEngineLogIndexInitialize(ElasticSearchIndexManager indexManager) {
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@ package org.jetlinks.community.rule.engine.event.handler;
|
|||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetlinks.community.elastic.search.service.ElasticSearchService;
|
||||
import org.jetlinks.community.elastic.search.service.reactive.ReactiveElasticsearchClient;
|
||||
import org.jetlinks.community.gateway.annotation.Subscribe;
|
||||
import org.jetlinks.community.rule.engine.entity.RuleEngineExecuteEventInfo;
|
||||
import org.jetlinks.core.event.TopicPayload;
|
||||
import org.jetlinks.rule.engine.defaults.LogEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
|
@ -14,6 +16,7 @@ import reactor.core.publisher.Mono;
|
|||
@Component
|
||||
@Slf4j
|
||||
@Order(3)
|
||||
@ConditionalOnBean(ElasticSearchService.class)
|
||||
public class RuleLogHandler {
|
||||
|
||||
@Autowired
|
||||
|
|
|
|||
|
|
@ -0,0 +1,130 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>jetlinks-components</artifactId>
|
||||
<groupId>org.jetlinks.community</groupId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<description>测试基础模块,引入后请设置scope为test</description>
|
||||
|
||||
<artifactId>test-component</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jetlinks</groupId>
|
||||
<artifactId>rule-engine-support</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jetlinks</groupId>
|
||||
<artifactId>jetlinks-supports</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aspects</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<!-- <scope>test</scope>-->
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-commons-crud</artifactId>
|
||||
<version>${hsweb.framework.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-starter</artifactId>
|
||||
<version>${hsweb.framework.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webflux</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.r2dbc</groupId>
|
||||
<artifactId>r2dbc-h2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.r2dbc</groupId>
|
||||
<artifactId>r2dbc-postgresql</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-junit-jupiter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers</artifactId>
|
||||
<version>1.15.3</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>1.15.3</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.lettuce</groupId>
|
||||
<artifactId>lettuce-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>uk.co.jemos.podam</groupId>
|
||||
<artifactId>podam</artifactId>
|
||||
<version>7.2.7.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package org.jetlinks.community.test.spring;
|
||||
|
||||
import org.hswebframework.web.crud.annotation.EnableEasyormRepository;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
@SpringBootApplication(
|
||||
scanBasePackages = {
|
||||
"org.jetlinks.community",
|
||||
},
|
||||
exclude = {
|
||||
DataSourceAutoConfiguration.class,
|
||||
KafkaAutoConfiguration.class,
|
||||
RabbitAutoConfiguration.class,
|
||||
ElasticsearchRestClientAutoConfiguration.class,
|
||||
ElasticsearchDataAutoConfiguration.class
|
||||
}
|
||||
)
|
||||
@EnableEasyormRepository("org.jetlinks.**.entity")
|
||||
public class TestJetLinksApplication {
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
package org.jetlinks.community.test.spring;
|
||||
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.hswebframework.web.authorization.ReactiveAuthenticationHolder;
|
||||
import org.hswebframework.web.authorization.ReactiveAuthenticationSupplier;
|
||||
import org.hswebframework.web.crud.configuration.EasyormConfiguration;
|
||||
import org.hswebframework.web.crud.configuration.R2dbcSqlExecutorConfiguration;
|
||||
import org.jetlinks.community.test.utils.ContainerUtils;
|
||||
import org.jetlinks.community.test.web.TestAuthentication;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.r2dbc.R2dbcTransactionManagerAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@ImportAutoConfiguration({
|
||||
EasyormConfiguration.class,
|
||||
R2dbcSqlExecutorConfiguration.class, R2dbcAutoConfiguration.class,
|
||||
R2dbcTransactionManagerAutoConfiguration.class, TransactionAutoConfiguration.class,
|
||||
WebClientAutoConfiguration.class, RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class
|
||||
})
|
||||
@EnableTransactionManagement(proxyTargetClass = true)
|
||||
@EnableAutoConfiguration
|
||||
@EnableAspectJAutoProxy(proxyTargetClass = true)
|
||||
@Testcontainers
|
||||
@ComponentScan("org.jetlinks.community")
|
||||
public class TestJetLinksController {
|
||||
|
||||
static {
|
||||
|
||||
System.setProperty("spring.r2dbc.url", "r2dbc:h2:mem:///./data/h2db/jetlinks");
|
||||
System.setProperty("spring.r2dbc.username", "sa");
|
||||
|
||||
}
|
||||
|
||||
@Container
|
||||
static GenericContainer<?> redis = ContainerUtils.newRedis();
|
||||
|
||||
@Autowired
|
||||
protected WebTestClient client;
|
||||
|
||||
@BeforeAll
|
||||
static void initAll() {
|
||||
System.setProperty("spring.redis.port", String.valueOf(redis.getMappedPort(6379)));
|
||||
System.setProperty("spring.redis.host", "127.0.0.1");
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void init() {
|
||||
ReactiveAuthenticationHolder.setSupplier(new ReactiveAuthenticationSupplier() {
|
||||
@Override
|
||||
public Mono<Authentication> get() {
|
||||
TestAuthentication authentication = new TestAuthentication("test");
|
||||
initAuth(authentication);
|
||||
return Mono.just(authentication);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Authentication> get(String userId) {
|
||||
TestAuthentication authentication = new TestAuthentication(userId);
|
||||
initAuth(authentication);
|
||||
return Mono.just(authentication);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void initAuth(TestAuthentication authentication) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package org.jetlinks.community.test.utils;
|
||||
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.containers.wait.strategy.Wait;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
|
||||
public class ContainerUtils {
|
||||
|
||||
public static GenericContainer<?> newRedis() {
|
||||
return new GenericContainer<>(DockerImageName.parse("redis:5"))
|
||||
.withEnv("TZ", "Asia/Shanghai")
|
||||
.withExposedPorts(6379)
|
||||
.waitingFor(Wait.forListeningPort());
|
||||
}
|
||||
|
||||
public static GenericContainer<?> newMysql() {
|
||||
return new GenericContainer<>(DockerImageName.parse("mysql:" + System.getProperty("test.mysql.version", "5.7")))
|
||||
.withEnv("TZ", "Asia/Shanghai")
|
||||
.withEnv("MYSQL_ROOT_PASSWORD", "password")
|
||||
.withEnv("MYSQL_DATABASE", "jetlinks")
|
||||
.withCommand("--character-set-server=utf8mb4")
|
||||
.withExposedPorts(3306)
|
||||
.waitingFor(Wait.forListeningPort());
|
||||
}
|
||||
|
||||
public static GenericContainer<?> newPostgresql() {
|
||||
return new GenericContainer<>(DockerImageName.parse("postgres:" + System.getProperty("test.postgres.version", "11")) + "-alpine")
|
||||
.withEnv("TZ", "Asia/Shanghai")
|
||||
.withEnv("POSTGRES_PASSWORD", "password")
|
||||
.withEnv("POSTGRES_DB", "jetlinks")
|
||||
.withCommand("postgres", "-c", "max_connections=500")
|
||||
.withExposedPorts(5432)
|
||||
.waitingFor(Wait.forListeningPort());
|
||||
}
|
||||
|
||||
public static GenericContainer<?> newElasticSearch() {
|
||||
return newElasticSearch(System.getProperty("test.elasticsearch.version", "7.9.0"));
|
||||
}
|
||||
|
||||
public static GenericContainer<?> newElasticSearch(String version) {
|
||||
return new GenericContainer<>(DockerImageName.parse("elasticsearch:" + version))
|
||||
.withEnv("TZ", "Asia/Shanghai")
|
||||
.withEnv("ES_JAVA_OPTS","-Djava.net.preferIPv4Stack=true -Xms1g -Xmx1g")
|
||||
// .withSharedMemorySize(DataSize.ofGigabytes(1).toBytes())
|
||||
.withEnv("transport.host", "0.0.0.0")
|
||||
.withEnv("discovery.type", "single-node")
|
||||
// .withEnv("bootstrap.memory_lock", "true")
|
||||
.withExposedPorts(9200)
|
||||
.waitingFor(Wait.forHttp("/").forStatusCode(200));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,297 @@
|
|||
package org.jetlinks.pro.test.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
import java.util.function.Function;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
import org.springframework.test.web.client.*;
|
||||
import reactor.core.Exceptions;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.http.ResponseCookie.ResponseCookieBuilder;
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.http.client.reactive.ClientHttpRequest;
|
||||
import org.springframework.http.client.reactive.ClientHttpResponse;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.mock.http.client.reactive.MockClientHttpRequest;
|
||||
import org.springframework.mock.http.client.reactive.MockClientHttpResponse;
|
||||
import org.springframework.test.web.client.match.MockRestRequestMatchers;
|
||||
import org.springframework.test.web.client.response.MockRestResponseCreators;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.reactive.function.client.WebClientResponseException;
|
||||
|
||||
/**
|
||||
* A mock REST service server that can be used to verify requests from {@link WebClient} objects, and
|
||||
* stub mock responses to provide to the client.
|
||||
* <p>
|
||||
* This acts as a drop-in replacement for {@link MockRestServiceServer} that provides support for reactive
|
||||
* {@link WebClient} objects. While supporting a reactive client API underneath, the majority of this implementation
|
||||
* works with the same components that {@link MockRestServiceServer} does.
|
||||
* <p>
|
||||
* This is relatively lightweight API to use, so can be initialized per test case if required. This does not open
|
||||
* any network sockets.
|
||||
* <p>
|
||||
* Instances of this server can be created by calling either
|
||||
* {@link #createServer()}, {@link #createServer(boolean)}, or {@link #createServer(RequestExpectationManager)}.
|
||||
* {@link WebClient} instances that will communicate with this server can then be created using the
|
||||
* {@link #createWebClient()} or {@link #createWebClientBuilder()} methods, respectively.
|
||||
*
|
||||
* @author Ashley Scopes
|
||||
* @see RequestMatcher
|
||||
* @see ResponseCreator
|
||||
* @see MockRestRequestMatchers
|
||||
* @see MockRestResponseCreators
|
||||
*/
|
||||
public final class MockReactiveRestServiceServer {
|
||||
private final RequestExpectationManager expectationManager;
|
||||
static DefaultDataBufferFactory bufferFactory=new DefaultDataBufferFactory();
|
||||
/**
|
||||
* Initialize the mock server with the desired expectation manager to use.
|
||||
*
|
||||
* @param expectationManager the expectation manager to use for requests.
|
||||
*/
|
||||
private MockReactiveRestServiceServer(@NonNull RequestExpectationManager expectationManager) {
|
||||
this.expectationManager = expectationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an expectation for a request to occur exactly once. This is an alias for calling
|
||||
* {@link #expect(ExpectedCount, RequestMatcher)} with {@link ExpectedCount#once()} as the first
|
||||
* parameter.
|
||||
*
|
||||
* @param requestMatcher the request matcher to use.
|
||||
* @return the response actions builder to use to define how to respond to such requests.
|
||||
*/
|
||||
@NonNull
|
||||
public ResponseActions expect(@NonNull RequestMatcher requestMatcher) {
|
||||
return expect(ExpectedCount.once(), requestMatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an expectation for a request to occur a given number of times.
|
||||
*
|
||||
* @param count the number of times the request should be made.
|
||||
* @param matcher the matcher for the request.
|
||||
* @return the response actions builder to use to define how to respond to such requests.
|
||||
*/
|
||||
@NonNull
|
||||
public ResponseActions expect(@NonNull ExpectedCount count, @NonNull RequestMatcher matcher) {
|
||||
return this.expectationManager.expectRequest(count, matcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the expected requests have been performed. This is equivalent to calling
|
||||
* {@link #verify(Duration)} with {@link Duration#ZERO}.
|
||||
*
|
||||
* @throws AssertionError if the requests have not been performed as expected.
|
||||
*/
|
||||
public void verify() {
|
||||
this.expectationManager.verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the expected results get performed within the given timeout.
|
||||
*
|
||||
* @param timeout the maximum time to wait before failing.
|
||||
* @throws AssertionError if the request times out before the expected requests occur.
|
||||
*/
|
||||
@NonNull
|
||||
public void verify(@NonNull Duration timeout) {
|
||||
this.expectationManager.verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all expectations from this mock server.
|
||||
*/
|
||||
public void reset() {
|
||||
this.expectationManager.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link WebClient} builder that is bound to this mock server.
|
||||
*
|
||||
* @return a {@code WebClient} builder that will pipe requests through this mock server instance.
|
||||
*/
|
||||
@NonNull
|
||||
public WebClient.Builder createWebClientBuilder() {
|
||||
return WebClient
|
||||
.builder()
|
||||
.clientConnector(new MockClientHttpConnector());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link WebClient} that is bound to this mock server.
|
||||
*
|
||||
* @return a {@code WebClient} with default settings that pipes requests through this mock server instance.
|
||||
*/
|
||||
@NonNull
|
||||
public WebClient createWebClient() {
|
||||
return createWebClientBuilder().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new mock reactive REST service server with the given expectation manager.
|
||||
*
|
||||
* @param manager the expectation manager to use.
|
||||
* @return the new mock reactive REST service server.
|
||||
*/
|
||||
@NonNull
|
||||
public static MockReactiveRestServiceServer createServer(@NonNull RequestExpectationManager manager) {
|
||||
return new MockReactiveRestServiceServer(manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new mock reactive REST service server.
|
||||
*
|
||||
* @param ignoreRequestOrder true to ignore the order of requests, or false to require requests to
|
||||
* be performed in the order that they are registered.
|
||||
* @return the new mock reactive REST service server.
|
||||
*/
|
||||
@NonNull
|
||||
public static MockReactiveRestServiceServer createServer(boolean ignoreRequestOrder) {
|
||||
RequestExpectationManager manager = ignoreRequestOrder
|
||||
? new UnorderedRequestExpectationManager()
|
||||
: new SimpleRequestExpectationManager();
|
||||
|
||||
return createServer(manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new mock reactive REST service server that requires requests to be performed in the order that they
|
||||
* are registered.
|
||||
*
|
||||
* @return the new mock reactive REST service server to use.
|
||||
*/
|
||||
@NonNull
|
||||
public static MockReactiveRestServiceServer createServer() {
|
||||
return createServer(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal mock connector for {@link WebClient} instances. This deals with converting reactive
|
||||
* {@link org.springframework.mock.http.client.reactive.MockClientHttpRequest} objects to non-reactive
|
||||
* {@link org.springframework.mock.http.client.MockClientHttpRequest} objects that the
|
||||
* {@link MockRestServiceServer} internals are compatible with. This also will handle converting non-reactive
|
||||
* {@link org.springframework.mock.http.client.MockClientHttpResponse} objects back to reactive
|
||||
* {@link org.springframework.mock.http.client.reactive.MockClientHttpResponse} objects for the response object
|
||||
* to use internally.
|
||||
*
|
||||
* @author Ashley Scopes
|
||||
*/
|
||||
private final class MockClientHttpConnector implements ClientHttpConnector {
|
||||
@NonNull
|
||||
public Mono<ClientHttpResponse> connect(
|
||||
@NonNull HttpMethod method, @NonNull URI uri,
|
||||
@NonNull Function<? super ClientHttpRequest, Mono<Void>> requestCallback) {
|
||||
|
||||
MockClientHttpRequest request = new MockClientHttpRequest(method, uri);
|
||||
|
||||
return Mono
|
||||
.defer(() -> requestCallback.apply(request))
|
||||
.thenReturn(request)
|
||||
.flatMap(this::createRequest)
|
||||
.map(this::performRequest)
|
||||
.map(this::convertResponse)
|
||||
.checkpoint("Request to " + method + " " + uri + " [MockReactiveRestServiceServer]")
|
||||
.onErrorMap(ex -> ex.getCause() instanceof AssertionError
|
||||
? Exceptions.bubble(ex.getCause())
|
||||
: ex);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Mono<org.springframework.http.client.ClientHttpRequest> createRequest(
|
||||
@NonNull MockClientHttpRequest reactiveRequest) {
|
||||
|
||||
org.springframework.mock.http.client.MockClientHttpRequest proceduralRequest =
|
||||
new org.springframework.mock.http.client.MockClientHttpRequest(
|
||||
reactiveRequest.getMethod(), reactiveRequest.getURI());
|
||||
|
||||
proceduralRequest.getHeaders().putAll(reactiveRequest.getHeaders());
|
||||
|
||||
return DataBufferUtils
|
||||
.join(reactiveRequest.getBody())
|
||||
.doOnNext(dataBuffer -> {
|
||||
byte[] arr = new byte[1024];
|
||||
int c;
|
||||
|
||||
try {
|
||||
InputStream input = dataBuffer.asInputStream();
|
||||
OutputStream output = proceduralRequest.getBody();
|
||||
|
||||
while ((c = input.read(arr)) != -1) {
|
||||
output.write(arr, 0, c);
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
})
|
||||
.thenReturn(proceduralRequest);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@SneakyThrows
|
||||
private org.springframework.http.client.ClientHttpResponse performRequest(
|
||||
@NonNull org.springframework.http.client.ClientHttpRequest proceduralRequest) {
|
||||
|
||||
return MockReactiveRestServiceServer
|
||||
.this
|
||||
.expectationManager
|
||||
.validateRequest(proceduralRequest);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
private ClientHttpResponse convertResponse(
|
||||
@NonNull org.springframework.http.client.ClientHttpResponse proceduralResponse) {
|
||||
|
||||
int status = -1;
|
||||
String statusMessage = "Could not read response status";
|
||||
HttpHeaders headers = null;
|
||||
|
||||
try {
|
||||
status = proceduralResponse.getRawStatusCode();
|
||||
statusMessage = proceduralResponse.getStatusText();
|
||||
headers = proceduralResponse.getHeaders();
|
||||
DataBuffer responseBody = bufferFactory
|
||||
.wrap(IOUtils.toByteArray(proceduralResponse.getBody()));
|
||||
|
||||
MockClientHttpResponse reactiveResponse = new MockClientHttpResponse(status);
|
||||
reactiveResponse.getHeaders().putAll(headers);
|
||||
MultiValueMap<String, ResponseCookie> cookies = reactiveResponse.getCookies();
|
||||
|
||||
headers.getOrEmpty(HttpHeaders.SET_COOKIE)
|
||||
.stream()
|
||||
.filter(cookie -> cookie.contains("="))
|
||||
.map(cookie -> ResponseCookie.fromClientResponse(
|
||||
cookie.substring(0, cookie.indexOf('=')),
|
||||
cookie.substring(cookie.indexOf('=') + 1)
|
||||
))
|
||||
.map(ResponseCookieBuilder::build)
|
||||
.forEach(cookie -> cookies.add(cookie.getName(), cookie));
|
||||
|
||||
reactiveResponse.setBody(Flux.just(responseBody));
|
||||
|
||||
return reactiveResponse;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new WebClientResponseException(status, statusMessage, headers, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
package org.jetlinks.community.test.web;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.hswebframework.web.authorization.Dimension;
|
||||
import org.hswebframework.web.authorization.Permission;
|
||||
import org.hswebframework.web.authorization.User;
|
||||
import org.hswebframework.web.authorization.simple.SimpleDimension;
|
||||
import org.hswebframework.web.authorization.simple.SimpleDimensionType;
|
||||
import org.hswebframework.web.authorization.simple.SimplePermission;
|
||||
import org.hswebframework.web.authorization.simple.SimpleUser;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Getter
|
||||
public class TestAuthentication implements Authentication {
|
||||
|
||||
private final User user;
|
||||
|
||||
private final List<Dimension> dimensions = new ArrayList<>();
|
||||
private final List<Permission> permissions = new ArrayList<>();
|
||||
|
||||
public TestAuthentication(String userId) {
|
||||
this.user = new SimpleUser(userId, userId, "test", "test", null);
|
||||
|
||||
}
|
||||
|
||||
public TestAuthentication addDimension(String type, String id) {
|
||||
return addDimension(type,id,null);
|
||||
}
|
||||
|
||||
public TestAuthentication addDimension(String type, String id,Map<String,Object> options) {
|
||||
dimensions.add(SimpleDimension.of(id, type, SimpleDimensionType.of(type), options));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public TestAuthentication addTenant(String tenantId,boolean admin) {
|
||||
Map<String,Object> options = new HashMap<>();
|
||||
options.put("tenantId",tenantId);
|
||||
options.put("admin",admin);
|
||||
dimensions.add(SimpleDimension.of(tenantId, tenantId, SimpleDimensionType.of("tenantMember"), options));
|
||||
return this;
|
||||
}
|
||||
|
||||
public TestAuthentication addPermission(String id, String... action) {
|
||||
permissions.add(SimplePermission.builder()
|
||||
.id(id)
|
||||
.name(id).actions(new HashSet<>(Arrays.asList(action)))
|
||||
.build());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Serializable> Optional<T> getAttribute(String name) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Serializable> getAttributes() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication merge(Authentication source) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication copy(BiPredicate<Permission, String> permissionFilter,
|
||||
Predicate<Dimension> dimension) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package org.jetlinks.community.test.utils;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@Testcontainers
|
||||
class ContainerUtilsTest {
|
||||
|
||||
@Container
|
||||
GenericContainer<?> redis = ContainerUtils.newRedis();
|
||||
|
||||
@Container
|
||||
GenericContainer<?> mysql = ContainerUtils.newMysql();
|
||||
|
||||
@Container
|
||||
GenericContainer<?> postgresql = ContainerUtils.newPostgresql();
|
||||
|
||||
@Container
|
||||
GenericContainer<?> elasticsearch = ContainerUtils.newElasticSearch();
|
||||
|
||||
|
||||
@Test
|
||||
void test(){
|
||||
|
||||
assertTrue(redis.isRunning());
|
||||
assertTrue(mysql.isRunning());
|
||||
assertTrue(postgresql.isRunning());
|
||||
assertTrue(elasticsearch.isRunning());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package org.jetlinks.community.timeseries;
|
||||
|
||||
import org.hswebframework.ezorm.core.param.QueryParam;
|
||||
import org.jetlinks.community.timeseries.query.AggregationData;
|
||||
import org.jetlinks.community.timeseries.query.AggregationQueryParam;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class NoneTimeSeriesManager implements TimeSeriesManager {
|
||||
public static final NoneTimeSeriesService NONE = new NoneTimeSeriesService();
|
||||
|
||||
@Override
|
||||
public TimeSeriesService getService(TimeSeriesMetric metric) {
|
||||
return NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeSeriesService getServices(TimeSeriesMetric... metric) {
|
||||
return NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeSeriesService getServices(String... metric) {
|
||||
return NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeSeriesService getService(String metric) {
|
||||
return NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> registerMetadata(TimeSeriesMetadata metadata) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
static class NoneTimeSeriesService implements TimeSeriesService {
|
||||
@Override
|
||||
public Flux<TimeSeriesData> query(QueryParam queryParam) {
|
||||
return Flux.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<TimeSeriesData> multiQuery(Collection<QueryParam> query) {
|
||||
return Flux.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Integer> count(QueryParam queryParam) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<AggregationData> aggregation(AggregationQueryParam queryParam) {
|
||||
return Flux.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> commit(Publisher<TimeSeriesData> data) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> commit(TimeSeriesData data) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> save(Publisher<TimeSeriesData> data) {
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package org.jetlinks.community.timeseries;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class TimeSeriesManagerConfiguration {
|
||||
|
||||
@ConditionalOnMissingBean(TimeSeriesManager.class)
|
||||
@Bean
|
||||
public NoneTimeSeriesManager timeSeriesManager() {
|
||||
return new NoneTimeSeriesManager();
|
||||
}
|
||||
}
|
||||
|
|
@ -76,6 +76,13 @@
|
|||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jetlinks.community</groupId>
|
||||
<artifactId>test-component</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package org.jetlinks.community.auth;
|
||||
|
||||
import org.hswebframework.web.authorization.simple.DefaultAuthorizationAutoConfiguration;
|
||||
import org.hswebframework.web.starter.jackson.CustomCodecsAutoConfiguration;
|
||||
import org.hswebframework.web.system.authorization.defaults.configuration.AuthorizationServiceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ImportAutoConfiguration({
|
||||
AuthorizationServiceAutoConfiguration.class,
|
||||
CodecsAutoConfiguration.class,
|
||||
JacksonAutoConfiguration.class,
|
||||
CustomCodecsAutoConfiguration.class,
|
||||
DefaultAuthorizationAutoConfiguration.class
|
||||
})
|
||||
public class AuthenticationTestConfiguration {
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
package org.jetlinks.community.auth.relation;
|
||||
|
||||
import org.hswebframework.ezorm.core.StaticMethodReferenceColumn;
|
||||
import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
|
||||
import org.hswebframework.ezorm.rdb.mapping.defaults.DefaultReactiveQuery;
|
||||
import org.jetlinks.community.auth.entity.ThirdPartyUserBindEntity;
|
||||
import org.jetlinks.community.auth.entity.UserDetail;
|
||||
import org.jetlinks.community.auth.service.UserDetailService;
|
||||
import org.jetlinks.community.relation.RelationConstants;
|
||||
import org.jetlinks.core.things.relation.PropertyOperation;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
class UserRelationObjectProviderTest {
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
UserDetailService service = Mockito.mock(UserDetailService.class);
|
||||
ReactiveRepository<ThirdPartyUserBindEntity, String> repository = Mockito.mock(ReactiveRepository.class);
|
||||
UserDetail detail = new UserDetail();
|
||||
detail.setName("Admin");
|
||||
detail.setUsername("admin");
|
||||
detail.setId("admin");
|
||||
detail.setEmail("admin@hsweb.me");
|
||||
{
|
||||
|
||||
Mockito.when(service.findUserDetail("admin"))
|
||||
.thenReturn(Mono.just(detail));
|
||||
|
||||
}
|
||||
ThirdPartyUserBindEntity entity = new ThirdPartyUserBindEntity();
|
||||
entity.setType("wx");
|
||||
entity.setThirdPartyUserId("third-admin");
|
||||
entity.setProvider("dingtalk");
|
||||
entity.setUserId("admin");
|
||||
{
|
||||
|
||||
|
||||
|
||||
DefaultReactiveQuery query = Mockito.mock(DefaultReactiveQuery.class);
|
||||
|
||||
Mockito.when(query.where(Mockito.any(StaticMethodReferenceColumn.class),Mockito.any()))
|
||||
.thenReturn(query);
|
||||
|
||||
Mockito.when(query.and(Mockito.any(StaticMethodReferenceColumn.class),Mockito.any()))
|
||||
.thenReturn(query);
|
||||
|
||||
Mockito.when(query.fetch())
|
||||
.thenReturn(Flux.just(
|
||||
entity
|
||||
));
|
||||
|
||||
Mockito.when(repository.createQuery())
|
||||
.thenReturn(query);
|
||||
}
|
||||
|
||||
|
||||
UserRelationObjectProvider provider = new UserRelationObjectProvider(service, repository);
|
||||
|
||||
PropertyOperation operation = provider.properties("admin");
|
||||
|
||||
operation.get(RelationConstants.UserProperty.name)
|
||||
.as(StepVerifier::create)
|
||||
.expectNext(detail.getName())
|
||||
.verifyComplete();
|
||||
|
||||
operation.get(RelationConstants.UserProperty.email)
|
||||
.as(StepVerifier::create)
|
||||
.expectNext(detail.getEmail())
|
||||
.verifyComplete();
|
||||
|
||||
operation.get("null")
|
||||
.as(StepVerifier::create)
|
||||
.expectComplete()
|
||||
.verify();
|
||||
|
||||
operation.get(RelationConstants.UserProperty.thirdParty(entity.getType(),entity.getProvider()))
|
||||
.as(StepVerifier::create)
|
||||
.expectNext(entity.getThirdPartyUserId())
|
||||
.verifyComplete();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
package org.jetlinks.community.auth.service;
|
||||
|
||||
import org.hswebframework.ezorm.core.StaticMethodReferenceColumn;
|
||||
import org.hswebframework.ezorm.core.param.QueryParam;
|
||||
import org.hswebframework.ezorm.rdb.mapping.ReactiveDelete;
|
||||
import org.hswebframework.ezorm.rdb.mapping.ReactiveQuery;
|
||||
import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
|
||||
import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult;
|
||||
import org.hswebframework.web.crud.events.EntityDeletedEvent;
|
||||
import org.hswebframework.web.system.authorization.api.entity.DimensionUserEntity;
|
||||
import org.hswebframework.web.system.authorization.defaults.service.DefaultDimensionUserService;
|
||||
import org.jetlinks.community.auth.entity.OrganizationEntity;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* 输入描述.
|
||||
*
|
||||
* @author zhangji
|
||||
* @version 1.11 2021/10/19
|
||||
*/
|
||||
public class OrganizationServiceTest {
|
||||
public static final String ORG_ID = "org-test";
|
||||
public static final String TENANT_ID_1 = "tenant-1";
|
||||
public static final String TENANT_ID_2 = "tenant-2";
|
||||
public static final String USER_ID_1 = "user-1";
|
||||
public static final String USER_ID_2 = "user-2";
|
||||
public static final String USER_ID_3 = "user-3";
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
// 模拟DefaultDimensionUserService
|
||||
ReactiveRepository<DimensionUserEntity, String> dimensionUserrepository = Mockito.mock(ReactiveRepository.class);
|
||||
ReactiveDelete reactiveDelete = Mockito.mock(ReactiveDelete.class);
|
||||
ReactiveQuery<DimensionUserEntity> dimensionUserQuery = Mockito.mock(ReactiveQuery.class);
|
||||
Mockito.when(dimensionUserrepository.save(Mockito.any(Publisher.class)))
|
||||
.thenReturn(Mono.just(SaveResult.of(3, 0)));
|
||||
Mockito.when(dimensionUserrepository.createDelete())
|
||||
.thenReturn(reactiveDelete);
|
||||
Mockito.when(reactiveDelete.where(Mockito.any(StaticMethodReferenceColumn.class), Mockito.any()))
|
||||
.thenReturn(reactiveDelete);
|
||||
Mockito.when(reactiveDelete.in(Mockito.any(StaticMethodReferenceColumn.class), Mockito.anyCollection()))
|
||||
.thenReturn(reactiveDelete);
|
||||
Mockito.when(reactiveDelete.and(Mockito.any(StaticMethodReferenceColumn.class), Mockito.any()))
|
||||
.thenReturn(reactiveDelete);
|
||||
Mockito.when(reactiveDelete.execute())
|
||||
.thenReturn(Mono.just(1));
|
||||
Mockito.when(reactiveDelete.onExecute(Mockito.any(BiFunction.class)))
|
||||
.thenReturn(reactiveDelete);
|
||||
Mockito.when(dimensionUserrepository.createQuery())
|
||||
.thenReturn(dimensionUserQuery);
|
||||
Mockito.when(dimensionUserQuery.select(Mockito.any(StaticMethodReferenceColumn.class)))
|
||||
.thenReturn(dimensionUserQuery);
|
||||
Mockito.when(dimensionUserQuery.setParam(Mockito.any(QueryParam.class)))
|
||||
.thenReturn(dimensionUserQuery);
|
||||
Mockito.when(dimensionUserQuery.fetch())
|
||||
.thenReturn(Flux.fromIterable(dimensionUserEntityList()));
|
||||
|
||||
DefaultDimensionUserService dimensionUserService = new DefaultDimensionUserService() {
|
||||
@Override
|
||||
public ReactiveRepository<DimensionUserEntity, String> getRepository() {
|
||||
return dimensionUserrepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<SaveResult> save(Publisher<DimensionUserEntity> entityPublisher) {
|
||||
return Flux.from(entityPublisher)
|
||||
.count()
|
||||
.map(Long::intValue)
|
||||
.map(i -> SaveResult.of(i, 0));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 模拟repostory
|
||||
ReactiveRepository<OrganizationEntity, String> repository = Mockito.mock(ReactiveRepository.class);
|
||||
Mockito.when(repository.findById(Mockito.anyString()))
|
||||
.thenReturn(Mono.just(organizationEntity()));
|
||||
|
||||
// 构建OrganizationService
|
||||
OrganizationService organizationService = new OrganizationService(dimensionUserService) {
|
||||
@Override
|
||||
public ReactiveRepository<OrganizationEntity, String> getRepository() {
|
||||
return repository;
|
||||
}
|
||||
};
|
||||
|
||||
// 绑定用户
|
||||
organizationService.bindUser(
|
||||
ORG_ID, Arrays.asList(USER_ID_1, USER_ID_2, USER_ID_3))
|
||||
.as(StepVerifier::create)
|
||||
.expectNext(3)
|
||||
.verifyComplete();
|
||||
|
||||
// 绑定用户-不指定租户ID
|
||||
organizationService.bindUser(ORG_ID, Arrays.asList(USER_ID_1, USER_ID_2, USER_ID_3))
|
||||
.as(StepVerifier::create)
|
||||
.expectNext(3)
|
||||
.verifyComplete();
|
||||
|
||||
// 解绑用户
|
||||
organizationService.unbindUser(ORG_ID, Arrays.asList(USER_ID_1))
|
||||
.as(StepVerifier::create)
|
||||
.expectNext(1)
|
||||
.verifyComplete();
|
||||
|
||||
// 解绑用户-不指定租户ID
|
||||
organizationService.unbindUser(ORG_ID, Arrays.asList(USER_ID_1))
|
||||
.as(StepVerifier::create)
|
||||
.expectNext(1)
|
||||
.verifyComplete();
|
||||
|
||||
EntityDeletedEvent<OrganizationEntity> event = new EntityDeletedEvent<>(
|
||||
Arrays.asList(organizationEntity()), OrganizationEntity.class
|
||||
);
|
||||
event.getAsync()
|
||||
.as(StepVerifier::create)
|
||||
.expectComplete();
|
||||
}
|
||||
|
||||
private OrganizationEntity organizationEntity() {
|
||||
OrganizationEntity organizationEntity = new OrganizationEntity();
|
||||
organizationEntity.setId(ORG_ID);
|
||||
organizationEntity.setName("测试机构");
|
||||
|
||||
return organizationEntity;
|
||||
}
|
||||
|
||||
private List<DimensionUserEntity> dimensionUserEntityList() {
|
||||
DimensionUserEntity dimensionUserEntity1 = new DimensionUserEntity();
|
||||
dimensionUserEntity1.setId("1");
|
||||
dimensionUserEntity1.setDimensionId(TENANT_ID_1);
|
||||
dimensionUserEntity1.setUserId("test");
|
||||
|
||||
DimensionUserEntity dimensionUserEntity2 = new DimensionUserEntity();
|
||||
dimensionUserEntity2.setId("2");
|
||||
dimensionUserEntity2.setDimensionId(TENANT_ID_2);
|
||||
dimensionUserEntity2.setUserId("test");
|
||||
|
||||
return Arrays.asList(dimensionUserEntity1, dimensionUserEntity2);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package org.jetlinks.community.auth.service.terms;
|
||||
|
||||
import org.hswebframework.ezorm.core.param.Term;
|
||||
import org.hswebframework.ezorm.rdb.executor.SqlRequest;
|
||||
import org.hswebframework.ezorm.rdb.metadata.RDBColumnMetadata;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class ThirdPartyUserTermBuilderTest {
|
||||
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
ThirdPartyUserTermBuilder builder = new ThirdPartyUserTermBuilder();
|
||||
|
||||
RDBColumnMetadata column = new RDBColumnMetadata();
|
||||
column.setName("id");
|
||||
Term term = new Term();
|
||||
term.setColumn("id");
|
||||
term.getOptions().add("wx");
|
||||
term.setValue("providerId");
|
||||
|
||||
SqlRequest request = builder
|
||||
.createFragments("id", column, term)
|
||||
.toRequest();
|
||||
System.out.printf(request.toNativeSql());
|
||||
assertEquals(
|
||||
"exists(select 1 from s_third_party_user_bind _bind where _bind.user_id = id and _bind.type = 'wx' and _bind.provider in ( 'providerId' ) )",
|
||||
request.toNativeSql());
|
||||
assertArrayEquals(request.getParameters(), new Object[]{"wx", "providerId"});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
package org.jetlinks.community.auth.web;
|
||||
|
||||
import org.jetlinks.community.auth.web.request.AuthorizationSettingDetail;
|
||||
import org.jetlinks.community.test.spring.TestJetLinksController;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
/**
|
||||
* 输入描述.
|
||||
*
|
||||
* @author zhangji
|
||||
* @version 1.11 2021/9/28
|
||||
*/
|
||||
@DisplayName("权限分配:AuthorizationSettingDetailController")
|
||||
@WebFluxTest(AuthorizationSettingDetailController.class)
|
||||
class AuthorizationSettingDetailControllerTest extends TestJetLinksController {
|
||||
|
||||
public static final String BASE_URI = "/autz-setting/detail";
|
||||
public static final String TARGET_TYPE = "user";
|
||||
public static final String TARGET = "test-operator";
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
Boolean result = client.post()
|
||||
.uri(BASE_URI + "/_save")
|
||||
.body(Flux.just(authSettingDetail()), AuthorizationSettingDetail.class)
|
||||
.exchange()
|
||||
.expectBody(Boolean.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
|
||||
assertEquals(true, result);
|
||||
|
||||
AuthorizationSettingDetail authSettingDetail = client.get()
|
||||
.uri(BASE_URI + String.format("/%s/%s", TARGET_TYPE, TARGET))
|
||||
.exchange()
|
||||
.expectBody(AuthorizationSettingDetail.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
|
||||
assertNotNull(authSettingDetail);
|
||||
assertEquals(TARGET_TYPE, authSettingDetail.getTargetType());
|
||||
assertEquals(TARGET, authSettingDetail.getTargetId());
|
||||
}
|
||||
|
||||
AuthorizationSettingDetail authSettingDetail() {
|
||||
AuthorizationSettingDetail authSettingDetail = new AuthorizationSettingDetail();
|
||||
authSettingDetail.setTargetType(TARGET_TYPE);
|
||||
authSettingDetail.setTargetId(TARGET);
|
||||
authSettingDetail.setMerge(true);
|
||||
authSettingDetail.setPriority(1);
|
||||
authSettingDetail.setPermissionList(Arrays.asList(
|
||||
AuthorizationSettingDetail.PermissionInfo.of("1", new HashSet<>(Arrays.asList("query"))),
|
||||
AuthorizationSettingDetail.PermissionInfo.of("2", new HashSet<>(Arrays.asList("query", "update")))
|
||||
));
|
||||
return authSettingDetail;
|
||||
}
|
||||
|
||||
|
||||
// @Override
|
||||
// protected void initAuth(TestAuthentication authentication) {
|
||||
// super.initAuth(authentication);
|
||||
// authentication.addDimension(TARGET_TYPE, TARGET);
|
||||
// }
|
||||
}
|
||||
|
|
@ -0,0 +1,266 @@
|
|||
package org.jetlinks.community.auth.web;
|
||||
|
||||
import org.hswebframework.web.system.authorization.api.entity.PermissionEntity;
|
||||
import org.hswebframework.web.system.authorization.api.service.reactive.ReactiveUserService;
|
||||
import org.hswebframework.web.system.authorization.defaults.service.DefaultPermissionService;
|
||||
import org.jetlinks.community.auth.entity.MenuButtonInfo;
|
||||
import org.jetlinks.community.auth.entity.MenuEntity;
|
||||
import org.jetlinks.community.auth.entity.MenuView;
|
||||
import org.jetlinks.community.auth.entity.PermissionInfo;
|
||||
import org.jetlinks.community.auth.service.request.MenuGrantRequest;
|
||||
import org.jetlinks.community.auth.web.request.AuthorizationSettingDetail;
|
||||
import org.jetlinks.community.test.spring.TestJetLinksController;
|
||||
import org.jetlinks.community.test.web.TestAuthentication;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
@WebFluxTest(value = MenuController.class, properties = {
|
||||
"menu.allow-all-menus-users=test"
|
||||
})
|
||||
class MenuControllerTest extends TestJetLinksController {
|
||||
|
||||
|
||||
@Autowired
|
||||
private DefaultPermissionService permissionService;
|
||||
|
||||
@Autowired
|
||||
private ReactiveUserService userService;
|
||||
|
||||
@Test
|
||||
void testCrud() {
|
||||
MenuEntity sys = new MenuEntity();
|
||||
sys.setName("系统设置");
|
||||
sys.setUrl("/xxx");
|
||||
sys.setId("sys-crud");
|
||||
MenuEntity menu = new MenuEntity();
|
||||
menu.setId("menu-crud");
|
||||
menu.setName("菜单管理");
|
||||
menu.setUrl("/xxx");
|
||||
menu.setParentId(sys.getId());
|
||||
menu.setButtons(Arrays.asList(
|
||||
MenuButtonInfo.of("create", "创建菜单", "menu", "add"),
|
||||
MenuButtonInfo.of("delete", "删除菜单", "menu", "remove")
|
||||
));
|
||||
|
||||
sys.setChildren(Collections.singletonList(menu));
|
||||
|
||||
client.patch()
|
||||
.uri("/menu")
|
||||
.bodyValue(sys)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client.delete()
|
||||
.uri("/menu/{id}", sys.getId())
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client.get()
|
||||
.uri("/menu/_query/no-paging")
|
||||
.exchange()
|
||||
.expectBodyList(MenuEntity.class)
|
||||
.hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGrant() {
|
||||
|
||||
MenuEntity sys = new MenuEntity();
|
||||
sys.setName("系统设置");
|
||||
sys.setUrl("/xxx");
|
||||
sys.setId("sys");
|
||||
|
||||
MenuEntity menu = new MenuEntity();
|
||||
menu.setId("menu");
|
||||
menu.setName("菜单管理");
|
||||
menu.setUrl("/xxx");
|
||||
menu.setParentId(sys.getId());
|
||||
menu.setButtons(Arrays.asList(
|
||||
MenuButtonInfo.of("create", "创建菜单", "menu", "add"),
|
||||
MenuButtonInfo.of("delete", "删除菜单", "menu", "remove")
|
||||
));
|
||||
|
||||
|
||||
sys.setChildren(Collections.singletonList(menu));
|
||||
|
||||
client.patch()
|
||||
.uri("/menu")
|
||||
.bodyValue(sys)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
MenuGrantRequest request = new MenuGrantRequest();
|
||||
request.setTargetType("user");
|
||||
request.setTargetId("test2");
|
||||
MenuView menuView = new MenuView();
|
||||
menuView.setId(menu.getId());
|
||||
menuView.setButtons(Arrays.asList(MenuView.ButtonView.of("create", "创建菜单", "", null)));
|
||||
menuView.setGranted(true);
|
||||
request.setMenus(Arrays.asList(menuView));
|
||||
|
||||
client
|
||||
.put()
|
||||
.uri("/menu/user/test2/_grant")
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
List<MenuView> tree = client
|
||||
.get()
|
||||
.uri("/menu/user/test2/_grant/tree")
|
||||
.exchange()
|
||||
.expectBodyList(MenuView.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
|
||||
assertNotNull(tree);
|
||||
assertFalse(tree.isEmpty());
|
||||
|
||||
assertEquals(tree.get(0).getId(), sys.getId());
|
||||
assertEquals(1, tree.get(0).getChildren().size());
|
||||
assertEquals(menu.getId(), tree.get(0).getChildren().get(0).getId());
|
||||
|
||||
assertEquals(2, tree.get(0).getChildren().get(0).getButtons().size());
|
||||
|
||||
assertTrue(tree
|
||||
.get(0)
|
||||
.getChildren()
|
||||
.get(0)
|
||||
.getButton("create")
|
||||
.map(MenuView.ButtonView::isGranted)
|
||||
.orElse(false));
|
||||
assertFalse(tree
|
||||
.get(0)
|
||||
.getChildren()
|
||||
.get(0)
|
||||
.getButton("delete")
|
||||
.map(MenuView.ButtonView::isGranted)
|
||||
.orElse(true));
|
||||
|
||||
client.delete()
|
||||
.uri("/menu/{id}", sys.getId())
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssetTypes() {
|
||||
//菜单
|
||||
MenuEntity sys = new MenuEntity();
|
||||
sys.setName("系统设置");
|
||||
sys.setUrl("/xxx");
|
||||
sys.setPermissions(Arrays.asList(PermissionInfo.of("test", new HashSet<>(Arrays.asList("save")))));
|
||||
sys.setId("asset-test");
|
||||
|
||||
client.patch()
|
||||
.uri("/menu")
|
||||
.bodyValue(sys)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
//权限
|
||||
PermissionEntity permissionEntity = new PermissionEntity();
|
||||
permissionEntity.setId("test");
|
||||
permissionEntity.setName("Test");
|
||||
permissionEntity.setActions(new ArrayList<>());
|
||||
|
||||
permissionService
|
||||
.save(permissionEntity)
|
||||
.then()
|
||||
.as(StepVerifier::create)
|
||||
.expectComplete()
|
||||
.verify();
|
||||
|
||||
//资产
|
||||
List<MenuController.AssetTypeView> view = client
|
||||
.post()
|
||||
.uri("/menu/asset-types")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue("[{\"id\":\"asset-test\"}]")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBodyList(MenuController.AssetTypeView.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
|
||||
assertNotNull(view);
|
||||
assertEquals(1, view.size());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testPermissions() {
|
||||
//菜单
|
||||
MenuEntity sys = new MenuEntity();
|
||||
sys.setName("系统设置");
|
||||
sys.setUrl("/xxx");
|
||||
sys.setPermissions(Arrays.asList(PermissionInfo.of("test", new HashSet<>(Arrays.asList("save")))));
|
||||
sys.setId("asset-test");
|
||||
|
||||
client.patch()
|
||||
.uri("/menu")
|
||||
.bodyValue(sys)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
//权限
|
||||
PermissionEntity permissionEntity = new PermissionEntity();
|
||||
permissionEntity.setId("test");
|
||||
permissionEntity.setName("Test");
|
||||
permissionEntity.setActions(new ArrayList<>());
|
||||
|
||||
permissionService
|
||||
.save(permissionEntity)
|
||||
.then()
|
||||
.as(StepVerifier::create)
|
||||
.expectComplete()
|
||||
.verify();
|
||||
|
||||
|
||||
List<AuthorizationSettingDetail.PermissionInfo> view = client
|
||||
.post()
|
||||
.uri("/menu/permissions")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue("[{\"id\":\"asset-test\"}]")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBodyList(AuthorizationSettingDetail.PermissionInfo.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
|
||||
assertNotNull(view);
|
||||
assertEquals(1, view.size());
|
||||
assertEquals("test", view.get(0).getId());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initAuth(TestAuthentication authentication) {
|
||||
super.initAuth(authentication);
|
||||
authentication.addPermission("menu", "add");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
package org.jetlinks.community.auth.web;
|
||||
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.jetlinks.community.auth.entity.OrganizationEntity;
|
||||
import org.jetlinks.community.test.spring.TestJetLinksController;
|
||||
import org.jetlinks.community.test.web.TestAuthentication;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
|
||||
/**
|
||||
* 机构管理.
|
||||
*
|
||||
* @author zhangji
|
||||
* @version 1.11 2021/9/28
|
||||
*/
|
||||
@DisplayName("机构管理:OrganizationController")
|
||||
@WebFluxTest(OrganizationController.class)
|
||||
class OrganizationControllerTest extends TestJetLinksController {
|
||||
public static final String BASE_URI = "/organization";
|
||||
|
||||
|
||||
@Override
|
||||
protected void initAuth(TestAuthentication authentication) {
|
||||
super.initAuth(authentication);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
// 创建
|
||||
for (OrganizationEntity organizationEntity : orgList()) {
|
||||
client.post()
|
||||
.uri(BASE_URI)
|
||||
.bodyValue(organizationEntity)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
}
|
||||
|
||||
// 查询全部-树结构
|
||||
List<OrganizationEntity> orgTreeList = client
|
||||
.get()
|
||||
.uri(BASE_URI + "/_all/tree")
|
||||
.exchange()
|
||||
.expectBodyList(OrganizationEntity.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
|
||||
assertFalse(CollectionUtils.isEmpty(orgTreeList));
|
||||
OrganizationEntity top = orgTreeList.get(0);
|
||||
assertEquals("1", top.getId());
|
||||
|
||||
List<OrganizationEntity> children = top.getChildren();
|
||||
assertFalse(CollectionUtils.isEmpty(children));
|
||||
assertEquals("2", children.get(0).getId());
|
||||
assertEquals("3", children.get(1).getId());
|
||||
assertFalse(CollectionUtils.isEmpty(children.get(0).getChildren()));
|
||||
assertEquals("4", children.get(0).getChildren().get(0).getId());
|
||||
|
||||
// 查询全部-列表
|
||||
// 根据sortIndex字段排序
|
||||
List<OrganizationEntity> orgList = client
|
||||
.get()
|
||||
.uri(BASE_URI + "/_all")
|
||||
.exchange()
|
||||
.expectBodyList(OrganizationEntity.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
|
||||
assertFalse(CollectionUtils.isEmpty(orgList));
|
||||
assertEquals("1", orgList.get(0).getId());
|
||||
assertEquals("2", orgList.get(1).getId());
|
||||
assertEquals("3", orgList.get(2).getId());
|
||||
assertEquals("4", orgList.get(3).getId());
|
||||
|
||||
// 查询机构列表(包含子机构)树结构
|
||||
List<OrganizationEntity> orgIncludeChildrenTreeList = client
|
||||
.get()
|
||||
.uri(BASE_URI + "/_query/_children/tree?where=code = 1001&orgerBy=id")
|
||||
.exchange()
|
||||
.expectBodyList(OrganizationEntity.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
|
||||
assertFalse(CollectionUtils.isEmpty(orgIncludeChildrenTreeList));
|
||||
assertEquals(2, orgIncludeChildrenTreeList.get(0).getChildren().size());
|
||||
|
||||
// 查询机构列表(包含子机构)
|
||||
List<OrganizationEntity> orgIncludeChildrenList = client
|
||||
.get()
|
||||
.uri(BASE_URI + "/_query/_children?where=code = 1001&orgerBy=id")
|
||||
.exchange()
|
||||
.expectBodyList(OrganizationEntity.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
|
||||
assertFalse(CollectionUtils.isEmpty(orgIncludeChildrenList));
|
||||
assertEquals(4, orgIncludeChildrenList.size());
|
||||
|
||||
// 绑定用户到机构
|
||||
client.post()
|
||||
.uri(BASE_URI + "/{id}/users/_bind", "1")
|
||||
.body(Flux.just("test").collectList(), new ParameterizedTypeReference<List<String>>() {
|
||||
})
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
// 从机构解绑用户
|
||||
client.post()
|
||||
.uri(BASE_URI + "/{id}/users/_unbind", "1")
|
||||
.body(Flux.just("test").collectList(), new ParameterizedTypeReference<List<String>>() {
|
||||
})
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client.delete()
|
||||
.uri(BASE_URI + "/{id}", "1")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
}
|
||||
|
||||
List<OrganizationEntity> orgList() {
|
||||
List<OrganizationEntity> orgList = new ArrayList<>();
|
||||
OrganizationEntity orgEntity = new OrganizationEntity();
|
||||
orgEntity.setId("1");
|
||||
orgEntity.setCode("1001");
|
||||
orgEntity.setName("总部");
|
||||
orgEntity.setType("默认");
|
||||
orgEntity.setDescribe("组织的最上级");
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
properties.put("province", "北京");
|
||||
properties.put("alias", "top");
|
||||
orgEntity.setProperties(properties);
|
||||
orgEntity.setParentId("");
|
||||
orgEntity.setPath("1001");
|
||||
orgEntity.setLevel(1);
|
||||
orgEntity.setSortIndex(1L);
|
||||
orgList.add(orgEntity);
|
||||
|
||||
OrganizationEntity orgChildrenEntity1 = new OrganizationEntity();
|
||||
orgChildrenEntity1.setId("2");
|
||||
orgChildrenEntity1.setCode("1002");
|
||||
orgChildrenEntity1.setName("重庆分部");
|
||||
orgChildrenEntity1.setType("默认");
|
||||
Map<String, Object> childrenProperties1 = new HashMap<>();
|
||||
childrenProperties1.put("province", "重庆");
|
||||
childrenProperties1.put("alias", "child-1");
|
||||
orgChildrenEntity1.setProperties(childrenProperties1);
|
||||
orgChildrenEntity1.setParentId("1");
|
||||
orgChildrenEntity1.setPath("1001-1002");
|
||||
orgChildrenEntity1.setLevel(2);
|
||||
orgChildrenEntity1.setSortIndex(2L);
|
||||
orgList.add(orgChildrenEntity1);
|
||||
|
||||
OrganizationEntity orgChildrenEntity2 = new OrganizationEntity();
|
||||
orgChildrenEntity2.setId("3");
|
||||
orgChildrenEntity2.setCode("1003");
|
||||
orgChildrenEntity2.setName("成都分部");
|
||||
orgChildrenEntity2.setType("默认");
|
||||
Map<String, Object> childrenProperties2 = new HashMap<>();
|
||||
childrenProperties2.put("province", "成都");
|
||||
childrenProperties2.put("alias", "child-2");
|
||||
orgChildrenEntity2.setProperties(childrenProperties2);
|
||||
orgChildrenEntity2.setParentId("1");
|
||||
orgChildrenEntity2.setPath("1001-1003");
|
||||
orgChildrenEntity2.setLevel(2);
|
||||
orgChildrenEntity2.setSortIndex(3L);
|
||||
orgList.add(orgChildrenEntity2);
|
||||
|
||||
OrganizationEntity orgChildrenEntity3 = new OrganizationEntity();
|
||||
orgChildrenEntity3.setId("4");
|
||||
orgChildrenEntity3.setCode("1004");
|
||||
orgChildrenEntity3.setName("沙坪坝办事点");
|
||||
orgChildrenEntity3.setType("默认");
|
||||
Map<String, Object> childrenProperties3 = new HashMap<>();
|
||||
childrenProperties3.put("province", "重庆");
|
||||
childrenProperties3.put("alias", "child-3");
|
||||
orgChildrenEntity3.setProperties(childrenProperties3);
|
||||
orgChildrenEntity3.setParentId("2");
|
||||
orgChildrenEntity3.setPath("1001-1002-1004");
|
||||
orgChildrenEntity3.setLevel(3);
|
||||
orgChildrenEntity3.setSortIndex(4L);
|
||||
orgList.add(orgChildrenEntity3);
|
||||
|
||||
return orgList;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
package org.jetlinks.community.auth.web;
|
||||
|
||||
import org.hswebframework.web.authorization.DefaultDimensionType;
|
||||
import org.hswebframework.web.authorization.ReactiveAuthenticationInitializeService;
|
||||
import org.hswebframework.web.system.authorization.api.entity.UserEntity;
|
||||
import org.hswebframework.web.system.authorization.api.service.reactive.ReactiveUserService;
|
||||
import org.jetlinks.community.auth.entity.RoleEntity;
|
||||
import org.jetlinks.community.test.spring.TestJetLinksController;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
|
||||
@WebFluxTest(RoleController.class)
|
||||
class RoleControllerTest extends TestJetLinksController {
|
||||
|
||||
@Autowired
|
||||
private ReactiveAuthenticationInitializeService initializeService;
|
||||
|
||||
@Autowired
|
||||
private ReactiveUserService userService;
|
||||
|
||||
@Test
|
||||
void testBind() {
|
||||
RoleEntity role = new RoleEntity();
|
||||
role.setId("test");
|
||||
role.setName("Test");
|
||||
|
||||
UserEntity user = new UserEntity();
|
||||
user.setName("Test");
|
||||
user.setUsername("test");
|
||||
user.setPassword("Test123456");
|
||||
|
||||
userService.saveUser(Mono.just(user))
|
||||
.block();
|
||||
|
||||
client
|
||||
.post()
|
||||
.uri("/role")
|
||||
.bodyValue(role)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client
|
||||
.post()
|
||||
.uri("/role/{id}/users/_bind", role.getId())
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(Collections.singletonList(user.getId()))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
initializeService
|
||||
.initUserAuthorization(user.getId())
|
||||
.as(StepVerifier::create)
|
||||
.expectNextMatches(auth -> auth.hasDimension(DefaultDimensionType.role, role.getId()))
|
||||
.verifyComplete();
|
||||
|
||||
client
|
||||
.post()
|
||||
.uri("/role/{id}/users/_unbind", role.getId())
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(Collections.singletonList(user.getId()))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
initializeService
|
||||
.initUserAuthorization(user.getId())
|
||||
.as(StepVerifier::create)
|
||||
.expectNextMatches(auth -> !auth.hasDimension(DefaultDimensionType.role, role.getId()))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package org.jetlinks.community.auth.web;
|
||||
|
||||
import org.jetlinks.community.auth.entity.ThirdPartyUserBindEntity;
|
||||
import org.jetlinks.community.auth.web.request.ThirdPartyBindUserInfo;
|
||||
import org.jetlinks.community.test.spring.TestJetLinksController;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
@WebFluxTest(ThirdPartyUserController.class)
|
||||
class ThirdPartyUserControllerTest extends TestJetLinksController {
|
||||
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
|
||||
String type = "wx-corp";
|
||||
String provider = "corp1";
|
||||
|
||||
client.patch()
|
||||
.uri("/user/third-party/{type}/{provider}", type, provider)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(ThirdPartyBindUserInfo.of(
|
||||
"test","test", "用户1", "u1"
|
||||
))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client.get()
|
||||
.uri("/user/third-party/{type}/{provider}", type, provider)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBody()
|
||||
.jsonPath("$[0].userId").isEqualTo("test")
|
||||
.jsonPath("$[0].providerName").isEqualTo("用户1")
|
||||
.jsonPath("$[0].thirdPartyUserId").isEqualTo("u1");
|
||||
|
||||
client.get()
|
||||
.uri("/user/third-party/me")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBody()
|
||||
.jsonPath("$[0].userId").isEqualTo("test")
|
||||
.jsonPath("$[0].providerName").isEqualTo("用户1")
|
||||
.jsonPath("$[0].thirdPartyUserId").isEqualTo("u1")
|
||||
;
|
||||
|
||||
client.delete()
|
||||
.uri("/user/third-party/me/{id}", ThirdPartyUserBindEntity.generateId(
|
||||
type, provider, "u1"
|
||||
))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client.get()
|
||||
.uri("/user/third-party/me")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBodyList(ThirdPartyUserBindEntity.class)
|
||||
.hasSize(0)
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
package org.jetlinks.community.auth.web;
|
||||
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.hswebframework.web.authorization.simple.SimpleUser;
|
||||
import org.hswebframework.web.system.authorization.api.entity.UserEntity;
|
||||
import org.jetlinks.community.auth.entity.UserDetail;
|
||||
import org.jetlinks.community.auth.service.request.SaveUserDetailRequest;
|
||||
import org.jetlinks.community.test.spring.TestJetLinksController;
|
||||
import org.jetlinks.community.test.web.TestAuthentication;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 用户信息接口.
|
||||
*
|
||||
* @author zhangji
|
||||
* @version 1.11 2021/9/29
|
||||
*/
|
||||
@DisplayName("用户信息接口:UserDetailController")
|
||||
@WebFluxTest({UserDetailController.class, WebFluxUserController.class})
|
||||
class UserDetailControllerTest extends TestJetLinksController {
|
||||
public static final String BASE_URI = "/user/detail";
|
||||
private String userId;
|
||||
|
||||
@Override
|
||||
protected void initAuth(TestAuthentication authentication) {
|
||||
super.initAuth(authentication);
|
||||
|
||||
// 初始化用户ID
|
||||
UserEntity userEntity = new UserEntity();
|
||||
userEntity.setUsername(authentication.getUser().getUsername());
|
||||
userEntity.generateId();
|
||||
userId = userEntity.getId();
|
||||
|
||||
// 修改当前用户的ID(和数据库保持一致)
|
||||
((SimpleUser) authentication.getUser()).setId(userEntity.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存用户
|
||||
*/
|
||||
@BeforeEach
|
||||
void addUser() {
|
||||
Map<String, String> userMap = new HashMap<>();
|
||||
userMap.put("name", "test");
|
||||
userMap.put("username", "test");
|
||||
userMap.put("password", "password123456");
|
||||
userMap.put("type", "test");
|
||||
|
||||
Boolean result = client.patch()
|
||||
.uri("/user")
|
||||
.bodyValue(userMap)
|
||||
.exchange()
|
||||
.expectBody(Boolean.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
// 保存当前用户详情
|
||||
SaveUserDetailRequest saveRequest = new SaveUserDetailRequest();
|
||||
saveRequest.setName("测试用户");
|
||||
saveRequest.setDescription("单元测试-保存");
|
||||
saveRequest.setEmail("test@email.com");
|
||||
saveRequest.setTelephone("023-88888888");
|
||||
|
||||
client.put()
|
||||
.uri(BASE_URI)
|
||||
.bodyValue(saveRequest)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
// 获取当前登录用户详情
|
||||
client.get()
|
||||
.uri(BASE_URI)
|
||||
.exchange()
|
||||
.expectBody()
|
||||
.jsonPath("$.name").isEqualTo(saveRequest.getName())
|
||||
.jsonPath("$.email").isEqualTo(saveRequest.getEmail())
|
||||
.jsonPath("$.telephone").isEqualTo(saveRequest.getTelephone())
|
||||
.jsonPath("$.description").isEqualTo(saveRequest.getDescription());
|
||||
|
||||
// 删除用户
|
||||
Boolean deleteResult = client.delete()
|
||||
.uri("/user/{id:.+}", userId)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBody(Boolean.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
Assertions.assertNotNull(deleteResult);
|
||||
Assertions.assertTrue(deleteResult);
|
||||
|
||||
// 获取当前登录用户详情(用户数据不存在,返回默认数据)
|
||||
UserDetail userDetail = client.get()
|
||||
.uri(BASE_URI)
|
||||
.exchange()
|
||||
.expectBody(UserDetail.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
Assertions.assertNotNull(userDetail);
|
||||
Authentication
|
||||
.currentReactive()
|
||||
.map(i -> i.getUser().getName().equals(userDetail.getName()))
|
||||
.as(StepVerifier::create)
|
||||
.expectNext(true)
|
||||
.verifyComplete();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
package org.jetlinks.community.auth.web;
|
||||
|
||||
import org.jetlinks.community.auth.entity.UserSettingEntity;
|
||||
import org.jetlinks.community.test.spring.TestJetLinksController;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
@WebFluxTest(UserSettingController.class)
|
||||
class UserSettingControllerTest extends TestJetLinksController {
|
||||
|
||||
|
||||
@Test
|
||||
void testCrud() {
|
||||
String type = "user-search";
|
||||
|
||||
UserSettingEntity newSetting = new UserSettingEntity();
|
||||
newSetting.setName("test");
|
||||
newSetting.setContent("test-content");
|
||||
client
|
||||
.post()
|
||||
.uri("/user/settings/{type}", type)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(newSetting)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
List<UserSettingEntity> entities = client
|
||||
.get()
|
||||
.uri("/user/settings/{type}", type)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBodyList(UserSettingEntity.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
|
||||
assertNotNull(entities);
|
||||
assertEquals(1, entities.size());
|
||||
|
||||
UserSettingEntity entity = client
|
||||
.get()
|
||||
.uri("/user/settings/{type}/{key}", type, entities.get(0).getKey())
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBody(UserSettingEntity.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
assertNotNull(entity);
|
||||
assertEquals(entity.getContent(), newSetting.getContent());
|
||||
|
||||
client
|
||||
.delete()
|
||||
.uri("/user/settings/{type}/{key}", type, entity.getKey())
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
|
||||
UserSettingEntity entity1 = client
|
||||
.get()
|
||||
.uri("/user/settings/{type}/{key}", type, entity.getKey())
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBody(UserSettingEntity.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
assertNull(entity1);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
package org.jetlinks.community.auth.web;
|
||||
|
||||
import org.hswebframework.web.api.crud.entity.PagerResult;
|
||||
import org.hswebframework.web.api.crud.entity.QueryParamEntity;
|
||||
import org.hswebframework.web.authorization.simple.SimpleUser;
|
||||
import org.hswebframework.web.system.authorization.api.entity.UserEntity;
|
||||
import org.jetlinks.community.test.spring.TestJetLinksController;
|
||||
import org.jetlinks.community.test.web.TestAuthentication;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 输入描述.
|
||||
*
|
||||
* @author zhangji
|
||||
* @version 1.11 2021/9/29
|
||||
*/
|
||||
@DisplayName("用户管理:WebFluxUserController")
|
||||
@WebFluxTest(WebFluxUserController.class)
|
||||
public class WebFluxUserControllerTest extends TestJetLinksController {
|
||||
public static final String BASE_URI = "/user";
|
||||
public String userId;
|
||||
public static final String TENANT_ID = "tenant-test";
|
||||
|
||||
@Override
|
||||
protected void initAuth(TestAuthentication authentication) {
|
||||
super.initAuth(authentication);
|
||||
|
||||
// 添加租户
|
||||
// authentication.addDimension(TenantDimensionType.tenant.getId(), TENANT_ID);
|
||||
|
||||
// 初始化用户ID
|
||||
UserEntity userEntity = new UserEntity();
|
||||
userEntity.setUsername(authentication.getUser().getUsername());
|
||||
userEntity.generateId();
|
||||
userId = userEntity.getId();
|
||||
|
||||
// 修改当前用户的ID(和数据库保持一致)
|
||||
((SimpleUser) authentication.getUser()).setId(userEntity.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存用户
|
||||
*/
|
||||
@BeforeEach
|
||||
void addUser() {
|
||||
Map<String, String> userMap = new HashMap<>();
|
||||
userMap.put("name", "test");
|
||||
userMap.put("username", "test");
|
||||
userMap.put("password", "password123456");
|
||||
userMap.put("type", "test");
|
||||
|
||||
|
||||
Boolean result = client.patch()
|
||||
.uri("/user")
|
||||
.bodyValue(userMap)
|
||||
.exchange()
|
||||
.expectBody(Boolean.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertTrue(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
*/
|
||||
@AfterEach
|
||||
void deleteUser() {
|
||||
Boolean deleteResult = client.delete()
|
||||
.uri("/user/{id:.+}", userId)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBody(Boolean.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
Assertions.assertNotNull(deleteResult);
|
||||
Assertions.assertTrue(deleteResult);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testUsernameValidate() {
|
||||
client.post()
|
||||
.uri("/user/username/_validate")
|
||||
.bodyValue("test")
|
||||
.exchange()
|
||||
.expectBody()
|
||||
.jsonPath("$.passed")
|
||||
.isEqualTo(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordValidate() {
|
||||
client.post()
|
||||
.uri("/user/password/_validate")
|
||||
.bodyValue("1111")
|
||||
.exchange()
|
||||
.expectBody()
|
||||
.jsonPath("$.passed")
|
||||
.isEqualTo(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordReset() {
|
||||
client.post()
|
||||
.uri("/user/" + userId + "/password/_reset")
|
||||
.bodyValue("pwd1qaz2wsx")
|
||||
.exchange()
|
||||
.expectBody(Integer.class)
|
||||
.isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
// 修改用户
|
||||
Map<String, String> userMap = new HashMap<>();
|
||||
userMap.put("id", userId);
|
||||
userMap.put("name", "test-update");
|
||||
|
||||
Boolean result = client.patch()
|
||||
.uri("/user")
|
||||
.bodyValue(userMap)
|
||||
.exchange()
|
||||
.expectBody(Boolean.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertTrue(result);
|
||||
|
||||
// 使用POST方式分页动态查询(不返回总数)
|
||||
List<UserEntity> userList = client.post()
|
||||
.uri(BASE_URI + "/_query/no-paging")
|
||||
.bodyValue(QueryParamEntity.of())
|
||||
.exchange()
|
||||
.expectBodyList(UserEntity.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
Assertions.assertFalse(CollectionUtils.isEmpty(userList));
|
||||
|
||||
// 使用GET方式分页动态查询(不返回总数)
|
||||
List<UserEntity> userList2 = client.get()
|
||||
.uri(BASE_URI + "/_query/no-paging")
|
||||
.exchange()
|
||||
.expectBodyList(UserEntity.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
Assertions.assertFalse(CollectionUtils.isEmpty(userList2));
|
||||
|
||||
// 使用POST方式分页动态查询
|
||||
PagerResult<UserEntity> userPager = client.post()
|
||||
.uri(BASE_URI + "/_query")
|
||||
.bodyValue(QueryParamEntity.of())
|
||||
.exchange()
|
||||
.expectBody(new ParameterizedTypeReference<PagerResult<UserEntity>>() {
|
||||
})
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
Assertions.assertNotNull(userPager);
|
||||
Assertions.assertFalse(CollectionUtils.isEmpty(userPager.getData()));
|
||||
|
||||
// 使用GET方式分页动态查询
|
||||
PagerResult<UserEntity> userPager2 = client.get()
|
||||
.uri(BASE_URI + "/_query")
|
||||
.exchange()
|
||||
.expectBody(new ParameterizedTypeReference<PagerResult<UserEntity>>() {
|
||||
})
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
Assertions.assertNotNull(userPager2);
|
||||
Assertions.assertFalse(CollectionUtils.isEmpty(userPager2.getData()));
|
||||
|
||||
// 使用POST方式查询总数
|
||||
Integer total = client.post()
|
||||
.uri(BASE_URI + "/_count")
|
||||
.bodyValue(QueryParamEntity.of())
|
||||
.exchange()
|
||||
.expectBody(Integer.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
Assertions.assertNotNull(total);
|
||||
Assertions.assertTrue(total > 0);
|
||||
|
||||
// 使用GET方式查询总数
|
||||
Integer total2 = client.get()
|
||||
.uri(BASE_URI + "/_count")
|
||||
.exchange()
|
||||
.expectBody(Integer.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
Assertions.assertNotNull(total2);
|
||||
Assertions.assertTrue(total2 > 0);
|
||||
|
||||
// 修改用户状态
|
||||
Boolean stateResult = client.put()
|
||||
.uri(BASE_URI + "/{id:.+}/{state}", userId, "0")
|
||||
.exchange()
|
||||
.expectBody(Boolean.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
Assertions.assertNotNull(stateResult);
|
||||
Assertions.assertTrue(stateResult);
|
||||
|
||||
// 根据ID查询
|
||||
UserEntity user = client.get()
|
||||
.uri(BASE_URI + "/{id:.+}", userId)
|
||||
.exchange()
|
||||
.expectBody(UserEntity.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
Assertions.assertNotNull(user);
|
||||
Assertions.assertEquals(userId, user.getId());
|
||||
}
|
||||
}
|
||||
|
|
@ -99,6 +99,13 @@
|
|||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jetlinks.community</groupId>
|
||||
<artifactId>test-component</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
package org.jetlinks.community.device;
|
||||
|
||||
import org.hswebframework.web.authorization.token.DefaultUserTokenManager;
|
||||
import org.hswebframework.web.authorization.token.UserTokenManager;
|
||||
import org.hswebframework.web.starter.jackson.CustomCodecsAutoConfiguration;
|
||||
import org.jetlinks.community.configure.cluster.ClusterConfiguration;
|
||||
import org.jetlinks.community.configure.device.DeviceClusterConfiguration;
|
||||
import org.jetlinks.community.elastic.search.configuration.ElasticSearchConfiguration;
|
||||
import org.jetlinks.core.ProtocolSupports;
|
||||
import org.jetlinks.core.device.DeviceRegistry;
|
||||
import org.jetlinks.core.device.StandaloneDeviceMessageBroker;
|
||||
import org.jetlinks.core.device.session.DeviceSessionManager;
|
||||
import org.jetlinks.core.server.MessageHandler;
|
||||
import org.jetlinks.supports.device.session.LocalDeviceSessionManager;
|
||||
import org.jetlinks.supports.official.JetLinksDeviceMetadataCodec;
|
||||
import org.jetlinks.supports.test.InMemoryDeviceRegistry;
|
||||
import org.jetlinks.supports.test.MockProtocolSupport;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ImportAutoConfiguration({
|
||||
CodecsAutoConfiguration.class,
|
||||
JacksonAutoConfiguration.class,
|
||||
CustomCodecsAutoConfiguration.class,
|
||||
ClusterConfiguration.class,
|
||||
DeviceClusterConfiguration.class
|
||||
})
|
||||
public class DeviceTestConfiguration {
|
||||
|
||||
@Bean
|
||||
public ProtocolSupports mockProtocolSupport(){
|
||||
return new MockProtocolSupport();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public UserTokenManager userTokenManager(){
|
||||
return new DefaultUserTokenManager();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DeviceRegistry deviceRegistry() {
|
||||
|
||||
return new InMemoryDeviceRegistry();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageHandler messageHandler() {
|
||||
|
||||
return new StandaloneDeviceMessageBroker();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DeviceSessionManager deviceSessionManager() {
|
||||
|
||||
return LocalDeviceSessionManager.create();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ElasticSearchConfiguration searchConfiguration() {
|
||||
|
||||
return new ElasticSearchConfiguration();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JetLinksDeviceMetadataCodec jetLinksDeviceMetadataCodec(){
|
||||
return new JetLinksDeviceMetadataCodec();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,695 @@
|
|||
package org.jetlinks.community.device.web;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.SneakyThrows;
|
||||
import org.jetlinks.community.test.spring.TestJetLinksController;
|
||||
import org.jetlinks.core.metadata.SimplePropertyMetadata;
|
||||
import org.jetlinks.core.metadata.types.FloatType;
|
||||
import org.jetlinks.community.PropertyMetadataConstants;
|
||||
import org.jetlinks.community.PropertyMetric;
|
||||
import org.jetlinks.community.device.entity.DeviceInstanceEntity;
|
||||
import org.jetlinks.community.device.entity.DeviceProductEntity;
|
||||
import org.jetlinks.community.device.entity.DeviceTagEntity;
|
||||
import org.jetlinks.community.device.service.LocalDeviceInstanceService;
|
||||
import org.jetlinks.community.device.service.LocalDeviceProductService;
|
||||
import org.jetlinks.community.device.service.data.DeviceDataService;
|
||||
import org.jetlinks.community.device.web.request.AggRequest;
|
||||
import org.jetlinks.community.relation.entity.RelationEntity;
|
||||
import org.jetlinks.community.relation.service.RelatedObjectInfo;
|
||||
import org.jetlinks.community.relation.service.RelationService;
|
||||
import org.jetlinks.community.relation.service.request.SaveRelationRequest;
|
||||
import org.jetlinks.community.timeseries.query.Aggregation;
|
||||
import org.jetlinks.supports.official.JetLinksDeviceMetadata;
|
||||
import org.jetlinks.supports.official.JetLinksDeviceMetadataCodec;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.StringUtils;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
@WebFluxTest(value = DeviceInstanceController.class, properties = {
|
||||
"spring.reactor.debug-agent.enabled=true"
|
||||
})
|
||||
class DeviceInstanceControllerTest extends TestJetLinksController {
|
||||
|
||||
@Autowired
|
||||
@SuppressWarnings("all")
|
||||
private LocalDeviceInstanceService deviceService;
|
||||
|
||||
@Autowired
|
||||
@SuppressWarnings("all")
|
||||
private LocalDeviceProductService productService;
|
||||
|
||||
private String deviceId;
|
||||
private String productId;
|
||||
|
||||
private String metadata;
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
DeviceProductEntity product = new DeviceProductEntity();
|
||||
product.setMetadata("{}");
|
||||
product.setTransportProtocol("MQTT");
|
||||
product.setMessageProtocol("test");
|
||||
product.setId(productId = "deviceinstancecontrollertest_product");
|
||||
product.setName("DeviceInstanceControllerTest");
|
||||
|
||||
JetLinksDeviceMetadata metadata = new JetLinksDeviceMetadata("Test", "Test");
|
||||
{
|
||||
SimplePropertyMetadata metric = SimplePropertyMetadata.of(
|
||||
"metric", "Metric", FloatType.GLOBAL
|
||||
);
|
||||
metric.setExpands(
|
||||
PropertyMetadataConstants.Metrics
|
||||
.metricsToExpands(Arrays.asList(
|
||||
PropertyMetric.of("max", "最大值", 100),
|
||||
PropertyMetric.of("min", "最小值", -100)
|
||||
))
|
||||
);
|
||||
metadata.addProperty(metric);
|
||||
}
|
||||
|
||||
product.setMetadata(this.metadata=JetLinksDeviceMetadataCodec.getInstance().doEncode(metadata));
|
||||
|
||||
productService
|
||||
.save(product)
|
||||
.then(productService.deploy(productId))
|
||||
.then()
|
||||
.as(StepVerifier::create)
|
||||
.expectComplete()
|
||||
.verify();
|
||||
|
||||
DeviceInstanceEntity device = new DeviceInstanceEntity();
|
||||
device.setId(deviceId = "deviceinstancecontrollertest_device");
|
||||
device.setName("DeviceInstanceControllerTest");
|
||||
device.setProductId(product.getId());
|
||||
device.setProductName(device.getName());
|
||||
|
||||
client
|
||||
.patch()
|
||||
.uri("/device/instance")
|
||||
.bodyValue(device)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client
|
||||
.put()
|
||||
.uri("/device/instance/batch/_deploy")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(Arrays.asList(deviceId))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void shutdown() {
|
||||
client
|
||||
.put()
|
||||
.uri("/device/instance/batch/_unDeploy")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(Arrays.asList(deviceId))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client
|
||||
.put()
|
||||
.uri("/device/instance/batch/_delete")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(Arrays.asList(deviceId))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
void testCommon() {
|
||||
// {
|
||||
// DeviceInstanceEntity device = new DeviceInstanceEntity();
|
||||
// device.setId(deviceId);
|
||||
// device.setName("DeviceInstanceControllerTest");
|
||||
// device.setProductId(productId);
|
||||
// device.setProductName(device.getName());
|
||||
// //重复创建
|
||||
// client
|
||||
// .post()
|
||||
// .uri("/device/instance")
|
||||
// .bodyValue(device)
|
||||
// .exchange()
|
||||
// .expectStatus()
|
||||
// .is4xxClientError();
|
||||
// }
|
||||
client
|
||||
.get()
|
||||
.uri("/device/instance/{id:.+}/detail", deviceId)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client
|
||||
.get()
|
||||
.uri("/device/instance/bind-providers")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
|
||||
client
|
||||
.get()
|
||||
.uri("/device/instance/{deviceId}/state", deviceId)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
client
|
||||
.get()
|
||||
.uri("/device/instance/state/_sync")
|
||||
.accept(MediaType.TEXT_EVENT_STREAM)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
|
||||
client
|
||||
.post()
|
||||
.uri("/device/instance/{deviceId}/deploy", deviceId)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
|
||||
client
|
||||
.post()
|
||||
.uri("/device/instance/{deviceId}/undeploy", deviceId)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client
|
||||
.post()
|
||||
.uri("/device/instance/{deviceId}/disconnect", deviceId)
|
||||
.exchange();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProperties() {
|
||||
|
||||
client
|
||||
.get()
|
||||
.uri("/device/instance/{deviceId:.+}/properties/_query?where=property is test", deviceId)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client
|
||||
.get()
|
||||
.uri("/device/instance/{deviceId:.+}/properties/_query", deviceId)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is4xxClientError();
|
||||
|
||||
client
|
||||
.get()
|
||||
.uri("/device/instance/{deviceId:.+}/properties/latest", deviceId)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client
|
||||
.get()
|
||||
.uri("/device/instance/{deviceId:.+}/properties", deviceId)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client
|
||||
.post()
|
||||
.uri("/device/instance/{deviceId:.+}/properties/_top/{numberOfTop}", deviceId, 1)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue("{}")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client
|
||||
.get()
|
||||
.uri("/device/instance/{deviceId:.+}/property/{property}/_query", deviceId, "test")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client
|
||||
.post()
|
||||
.uri("/device/instance/{deviceId:.+}/property/{property}/_query", deviceId, "test")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue("{}")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client
|
||||
.post()
|
||||
.uri("/device/instance/{deviceId:.+}/properties/_query", deviceId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue("{}")
|
||||
.exchange();
|
||||
|
||||
|
||||
client
|
||||
.get()
|
||||
.uri("/device/instance/{deviceId:.+}/property/{property:.+}", deviceId, "test")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
AggRequest request = new AggRequest();
|
||||
|
||||
request.setColumns(Arrays.asList(
|
||||
new DeviceDataService.DevicePropertyAggregation("test", "alias", Aggregation.AVG)
|
||||
));
|
||||
request.setQuery(DeviceDataService.AggregationRequest
|
||||
.builder()
|
||||
.build());
|
||||
|
||||
client
|
||||
.post()
|
||||
.uri("/device/instance/{deviceId:.+}/agg/_query", deviceId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange();
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEvent() {
|
||||
client
|
||||
.get()
|
||||
.uri("/device/instance/{deviceId:.+}/event/{eventId}", deviceId, "test")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
client
|
||||
.post()
|
||||
.uri("/device/instance/{deviceId:.+}/event/{eventId}", deviceId, "test")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue("{}")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLog() {
|
||||
client
|
||||
.get()
|
||||
.uri("/device/instance/{deviceId:.+}/logs", deviceId, "test")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
client
|
||||
.post()
|
||||
.uri("/device/instance/{deviceId:.+}/logs", deviceId, "test")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue("{}")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTag() {
|
||||
DeviceTagEntity tag = new DeviceTagEntity();
|
||||
tag.setKey("test");
|
||||
tag.setValue("value");
|
||||
|
||||
client
|
||||
.patch()
|
||||
.uri("/device/instance/{deviceId}/tag", deviceId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(tag)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
List<DeviceTagEntity> tags = client
|
||||
.get()
|
||||
.uri("/device/instance/{deviceId}/tags", deviceId)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBodyList(DeviceTagEntity.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
|
||||
assertNotNull(tags);
|
||||
assertFalse(tags.isEmpty());
|
||||
|
||||
client
|
||||
.delete()
|
||||
.uri("/device/instance/{deviceId}/tag/{tagId}", deviceId, tags.get(0).getId())
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
void testMetadata() {
|
||||
client
|
||||
.get()
|
||||
.uri("/device/instance/{id:.+}/config-metadata", deviceId)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client
|
||||
.get()
|
||||
.uri("/device/instance/{id:.+}/config-metadata/{metadataType}/{metadataId}/{typeId}",
|
||||
deviceId,
|
||||
"property",
|
||||
"temp",
|
||||
"test")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
|
||||
String metadata = client
|
||||
.post()
|
||||
.uri("/device/instance/{deviceId}/property-metadata/import?fileUrl=" + new ClassPathResource("property-metadata.csv")
|
||||
.getFile()
|
||||
.getAbsolutePath(), deviceId)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBody(String.class)
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
assertNotNull(metadata);
|
||||
client
|
||||
.put()
|
||||
.uri("/device/instance/{id:.+}/metadata", deviceId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(metadata)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
deviceService.findById(deviceId)
|
||||
.as(StepVerifier::create)
|
||||
.expectNextMatches(device -> Objects.equals(
|
||||
device.getDeriveMetadata(),
|
||||
metadata
|
||||
))
|
||||
.expectComplete()
|
||||
.verify();
|
||||
client
|
||||
.put()
|
||||
.uri("/device/instance/{id}/metadata/merge-product", deviceId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client
|
||||
.delete()
|
||||
.uri("/device/instance/{id}/metadata", deviceId)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
deviceService
|
||||
.findById(deviceId)
|
||||
.as(StepVerifier::create)
|
||||
.expectNextMatches(device -> StringUtils.isEmpty(device.getDeriveMetadata()))
|
||||
.expectComplete()
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConfiguration() {
|
||||
|
||||
deviceService.deploy(deviceId)
|
||||
.then()
|
||||
.as(StepVerifier::create)
|
||||
.expectComplete()
|
||||
.verify();
|
||||
client
|
||||
.post()
|
||||
.uri("/device/instance/{deviceId:.+}/configuration/_write", deviceId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue("{\"test\":\"123\"}")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client
|
||||
.post()
|
||||
.uri("/device/instance/{deviceId:.+}/configuration/_read", deviceId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue("[\"test\"]")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBody(Map.class)
|
||||
.isEqualTo(Collections.singletonMap("test", "123"));
|
||||
|
||||
client
|
||||
.put()
|
||||
.uri("/device/instance/{deviceId:.+}/configuration/_reset", deviceId)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client
|
||||
.put()
|
||||
.uri("/device/instance/{deviceId:.+}/shadow", deviceId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue("{\"test\":\"123\"}")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client
|
||||
.get()
|
||||
.uri("/device/instance/{deviceId:.+}/shadow", deviceId)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBody(String.class)
|
||||
.isEqualTo("{\"test\":\"123\"}");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCommand() {
|
||||
client
|
||||
.put()
|
||||
.uri("/device/instance/{deviceId:.+}/property", deviceId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(Collections.singletonMap("test", "value"))
|
||||
.exchange();
|
||||
|
||||
client
|
||||
.post()
|
||||
.uri("/device/instance/{deviceId:.+}/property/_read", deviceId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(Collections.singleton("test"))
|
||||
.exchange();
|
||||
|
||||
|
||||
client
|
||||
.post()
|
||||
.uri("/device/instance/{deviceId:.+}/function/test", deviceId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(Collections.singletonMap("test", "value"))
|
||||
.exchange();
|
||||
|
||||
client
|
||||
.post()
|
||||
.uri("/device/instance/{deviceId:.+}/message", deviceId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(Collections.singletonMap("properties", Collections.singletonList("test")))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("deviceId", "test");
|
||||
data.put("properties", Collections.singletonList("test"));
|
||||
|
||||
client
|
||||
.post()
|
||||
.uri("/device/instance/messages", deviceId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(data)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAutoChangeProductInfo() {
|
||||
client.post()
|
||||
.uri("/device/instance")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue("{\"id\":\"testAutoChangeProductInfo\",\"name\":\"Test\",\"productId\":\"" + productId + "\"}")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private RelationService relationService;
|
||||
|
||||
@Test
|
||||
void testRelation() {
|
||||
RelationEntity entity = new RelationEntity();
|
||||
entity.setRelation("manager");
|
||||
entity.setObjectType("device");
|
||||
entity.setObjectTypeName("设备");
|
||||
entity.setTargetType("user");
|
||||
entity.setTargetTypeName("用户");
|
||||
entity.setName("管理员");
|
||||
relationService
|
||||
.save(entity).block();
|
||||
|
||||
|
||||
client.get()
|
||||
.uri("/device/instance/{deviceId}/detail", deviceId)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBody()
|
||||
.jsonPath("$.relations[0].relation").isEqualTo("manager")
|
||||
.jsonPath("$.relations[0].related").isEmpty();
|
||||
|
||||
|
||||
client.patch()
|
||||
.uri("/device/instance/{deviceId}/relations", deviceId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(SaveRelationRequest.of(
|
||||
"user",
|
||||
"manager",
|
||||
Arrays.asList(RelatedObjectInfo.of("admin", "管理员")),
|
||||
null
|
||||
))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client.get()
|
||||
.uri("/device/instance/{deviceId}/detail", deviceId)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBody()
|
||||
.jsonPath("$.relations[0].relation").isEqualTo("manager")
|
||||
.jsonPath("$.relations[0].related[0].id").isEqualTo("admin")
|
||||
.jsonPath("$.relations[0].related[0].name").isEqualTo("管理员");
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMetric() {
|
||||
client.get()
|
||||
.uri("/device/instance/{deviceId}/metric/property/{property}", deviceId, "metric")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBody()
|
||||
.jsonPath("[0].id").isEqualTo("max")
|
||||
.jsonPath("[0].value").isEqualTo(100)
|
||||
.jsonPath("[1].id").isEqualTo("min")
|
||||
.jsonPath("[1].value").isEqualTo(-100);
|
||||
|
||||
|
||||
client.patch()
|
||||
.uri("/device/instance/{deviceId}/metric/property/{property}", deviceId, "metric")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(PropertyMetric.of("max", "最大值", 110))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client.get()
|
||||
.uri("/device/instance/{deviceId}/metric/property/{property}", deviceId, "metric")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBody()
|
||||
.jsonPath("[0].id").isEqualTo("max")
|
||||
.jsonPath("[0].value").isEqualTo(110)
|
||||
.jsonPath("[1].id").isEqualTo("min")
|
||||
.jsonPath("[1].value").isEqualTo(-100);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDetail(){
|
||||
client
|
||||
.get()
|
||||
.uri("/device/instance/{deviceId}/detail", deviceId)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBody()
|
||||
.jsonPath("$.metadata").isEqualTo(metadata);
|
||||
|
||||
String newMetadata="{\"properties\":[]}";
|
||||
|
||||
client
|
||||
.put()
|
||||
.uri("/device/product/{productId}", productId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue("{\"metadata\":"+ JSON.toJSONString(newMetadata)+"}")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
//仅保存 未发布
|
||||
client
|
||||
.get()
|
||||
.uri("/device/instance/{deviceId}/detail", deviceId)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBody()
|
||||
.jsonPath("$.metadata").isEqualTo(metadata);
|
||||
|
||||
client
|
||||
.post()
|
||||
.uri("/device/product/{productId}/deploy", productId)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful();
|
||||
|
||||
client
|
||||
.get()
|
||||
.uri("/device/instance/{deviceId}/detail", deviceId)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBody()
|
||||
.jsonPath("$.metadata").isEqualTo(newMetadata);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -11,5 +11,7 @@ COPY --from=builder application/snapshot-dependencies/ ./
|
|||
COPY --from=builder application/spring-boot-loader/ ./
|
||||
COPY --from=builder application/application/ ./
|
||||
COPY docker-entrypoint.sh ./
|
||||
COPY entrypoint.sh ./
|
||||
RUN chmod +x docker-entrypoint.sh
|
||||
RUN chmod +x entrypoint.sh
|
||||
ENTRYPOINT ["./docker-entrypoint.sh"]
|
||||
Loading…
Reference in New Issue