Merge branch '2.x' into dev
# Conflicts: # README.md # magic-api-spring-boot-starter/pom.xml # magic-api/pom.xml # magic-api/src/main/java/org/ssssssss/magicapi/controller/MagicGroupController.java # magic-api/src/main/java/org/ssssssss/magicapi/model/JsonCodeConstants.java # magic-api/src/main/java/org/ssssssss/magicapi/provider/impl/MagicDatabaseBackupService.java # magic-editor/pom.xml # magic-editor/src/console/package.json # magic-editor/src/console/src/scripts/parsing/ast.js # magic-editor/src/console/src/scripts/parsing/parser.js # pom.xml
This commit is contained in:
commit
92864e9fd8
@ -52,7 +52,7 @@ magic-api 是一个基于Java的接口快速开发框架,编写接口将通过
|
||||
<dependency>
|
||||
<groupId>org.ssssssss</groupId>
|
||||
<artifactId>magic-api-spring-boot-starter</artifactId>
|
||||
<version>1.7.5</version>
|
||||
<version>2.0.0-beta.1</version>
|
||||
</dependency>
|
||||
```
|
||||
## 修改application.properties
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
> 0.7.x版本之后仅需要一张表两个字段,建表语句如下:
|
||||
```sql
|
||||
CREATE TABLE `magic_api_file` (
|
||||
CREATE TABLE `magic_api_file_v2` (
|
||||
`file_path` varchar(512) NOT NULL,
|
||||
`file_content` mediumtext,
|
||||
PRIMARY KEY (`file_path`)
|
||||
@ -8,14 +8,14 @@ CREATE TABLE `magic_api_file` (
|
||||
```
|
||||
### 备份表建表语句
|
||||
```sql
|
||||
CREATE TABLE `magic_api_backup` (
|
||||
CREATE TABLE `magic_backup_record_v2` (
|
||||
`id` varchar(32) NOT NULL COMMENT '原对象ID',
|
||||
`create_date` bigint(13) NOT NULL COMMENT '备份时间',
|
||||
`tag` varchar(32) DEFAULT NULL COMMENT '标签',
|
||||
`type` varchar(32) DEFAULT NULL COMMENT '类型',
|
||||
`name` varchar(64) DEFAULT NULL COMMENT '原名称',
|
||||
`content` mediumtext COMMENT '备份内容',
|
||||
`content` blob COMMENT '备份内容',
|
||||
`create_by` varchar(64) DEFAULT NULL COMMENT '操作人',
|
||||
PRIMARY KEY (`id`,`create_date`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||
```
|
||||
```
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
SET NAMES utf8mb4;
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
ALTER TABLE `magic_api_info` ADD COLUMN `api_group_prefix` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '分组前缀' AFTER `api_group_name`;
|
||||
ALTER TABLE `magic_api_info` ADD COLUMN `api_output` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '输出结果' AFTER `api_group_prefix`;
|
||||
-- ----------------------------
|
||||
-- Table structure for magic_api_info_his
|
||||
-- ----------------------------
|
||||
CREATE TABLE `magic_api_info_his` (
|
||||
`id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'api_id',
|
||||
`api_method` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '请求方法',
|
||||
`api_path` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '请求路径',
|
||||
`api_script` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '接口脚本',
|
||||
`api_parameter` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '接口参数',
|
||||
`api_option` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '接口选项',
|
||||
`api_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '接口名称',
|
||||
`api_group_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '接口分组',
|
||||
`api_group_prefix` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '分组前缀',
|
||||
`api_output` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '输出结果',
|
||||
`api_create_time` bigint(20) NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`api_update_time` bigint(20) NULL DEFAULT NULL COMMENT '修改时间'
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'MagicAPI接口历史记录' ROW_FORMAT = Dynamic;
|
||||
@ -1,29 +0,0 @@
|
||||
-- 创建分组表
|
||||
CREATE TABLE `magic_group` (
|
||||
`id` varchar(32) NOT NULL,
|
||||
`group_name` varchar(64) NULL COMMENT '组名',
|
||||
`group_type` varchar(1) NULL COMMENT '组类型,1:接口分组,2:函数分组',
|
||||
`group_path` varchar(64) NULL COMMENT '分组路径',
|
||||
`parent_id` varchar(32) NULL COMMENT '父级ID',
|
||||
`deleted` char(1) NULL DEFAULT 0 COMMENT '是否被删除,1:是,0:否',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'MagicAPI分组信息表' ROW_FORMAT = Dynamic;
|
||||
-- 插入分组数据
|
||||
insert into magic_group select md5(uuid()),api_group_name,'1',api_group_prefix,'0','0' from magic_api_info group by api_group_name,api_group_prefix;
|
||||
-- 修改字段
|
||||
ALTER TABLE `magic_api_info` ADD COLUMN `api_group_id` varchar(32) NULL COMMENT '分组ID' AFTER `api_name`;
|
||||
ALTER TABLE `magic_api_info` CHANGE COLUMN `api_output` `api_response_body` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '输出结果' AFTER `api_group_id`;
|
||||
ALTER TABLE `magic_api_info` ADD COLUMN `api_description` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '接口描述' AFTER `api_response_body`;
|
||||
ALTER TABLE `magic_api_info` ADD COLUMN `api_request_body` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '请求体' AFTER `api_group_id`, ADD COLUMN `api_request_header` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '请求Header' AFTER `api_request_body`;
|
||||
ALTER TABLE `magic_api_info_his` ADD COLUMN `api_group_id` varchar(32) NULL COMMENT '分组ID' AFTER `api_name`;
|
||||
ALTER TABLE `magic_api_info_his` CHANGE COLUMN `api_output` `api_response_body` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '输出结果' AFTER `api_group_id`;
|
||||
ALTER TABLE `magic_api_info_his` ADD COLUMN `api_description` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '接口描述' AFTER `api_response_body`;
|
||||
ALTER TABLE `magic_api_info_his` ADD COLUMN `api_request_body` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '请求体' AFTER `api_group_id`, ADD COLUMN `api_request_header` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '请求Header' AFTER `api_request_body`;
|
||||
|
||||
-- 赋值api_group_id字段
|
||||
UPDATE magic_api_info mai JOIN magic_group mg ON mg.group_name = mai.api_group_name AND mg.group_path = mai.api_group_prefix SET mai.api_group_id = mg.id;
|
||||
-- 对关联不上的,归根节点
|
||||
UPDATE magic_api_info SET api_group_id = '0' where api_group_id IS NULL;
|
||||
-- 删除字段
|
||||
ALTER TABLE `magic_api_info` DROP COLUMN `api_group_name`,DROP COLUMN `api_group_prefix`;
|
||||
ALTER TABLE `magic_api_info_his` DROP COLUMN `api_group_name`,DROP COLUMN `api_group_prefix`;
|
||||
@ -1,34 +0,0 @@
|
||||
CREATE TABLE `magic_function`
|
||||
(
|
||||
`id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键',
|
||||
`function_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '函数名称',
|
||||
`function_path` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '函数路径',
|
||||
`function_parameter` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '参数列表',
|
||||
`function_return_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '返回值类型',
|
||||
`function_script` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '脚本',
|
||||
`function_group_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '所属分组',
|
||||
`function_description` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '函数描述',
|
||||
`function_create_time` bigint(20) NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`function_update_time` bigint(20) NULL DEFAULT NULL COMMENT '修改时间',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB
|
||||
CHARACTER SET = utf8mb4
|
||||
COLLATE = utf8mb4_general_ci COMMENT = 'MagicAPI 函数表'
|
||||
ROW_FORMAT = Dynamic;
|
||||
|
||||
CREATE TABLE `magic_function_his`
|
||||
(
|
||||
`id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'function_id',
|
||||
`function_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '函数名称',
|
||||
`function_path` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '函数路径',
|
||||
`function_parameter` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '参数列表',
|
||||
`function_return_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '返回值类型',
|
||||
`function_script` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '脚本',
|
||||
`function_group_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '所属分组',
|
||||
`function_description` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '函数描述',
|
||||
`function_create_time` bigint(20) NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`function_update_time` bigint(20) NULL DEFAULT NULL COMMENT '修改时间'
|
||||
) ENGINE = InnoDB
|
||||
CHARACTER SET = utf8mb4
|
||||
COLLATE = utf8mb4_general_ci COMMENT = 'MagicAPI 函数历史记录'
|
||||
ROW_FORMAT = Dynamic;
|
||||
@ -1,49 +0,0 @@
|
||||
import 'org.ssssssss.magicapi.adapter.Resource' as root;
|
||||
import 'org.ssssssss.magicapi.provider.GroupServiceProvider';
|
||||
import 'org.ssssssss.magicapi.provider.ApiServiceProvider';
|
||||
import 'org.ssssssss.magicapi.provider.FunctionServiceProvider';
|
||||
import 'org.ssssssss.magicapi.utils.IoUtils' as IoUtils
|
||||
import 'org.ssssssss.magicapi.utils.JsonUtils' as JsonUtils
|
||||
import 'java.io.File' as File;
|
||||
var ds = db.camel(); //如果之前保存在其他库,这里可以修改为db.xxx.camel();
|
||||
var apiSql = """ select * from magic_api_info """;
|
||||
var groupSql = """ select * from magic_group where deleted = '0' """;
|
||||
var functionSql = """ select * from magic_function """;
|
||||
// 替换key,去除前缀,将首字母小写。
|
||||
var replaceKey = (it,src) => it.replaceKey(src,'').replaceKey(it => it.substring(0,1).toLowerCase() + it.substring(1));
|
||||
// list转tree
|
||||
var toTree = (list,parentId)=>list.filter(it => it.parentId == parentId).each(it => it.children = toTree(list,it.id))
|
||||
// 查询分组列表
|
||||
var groupList = ds.select(groupSql).map(it => replaceKey(it,"group"));
|
||||
// 将接口分组转为tree
|
||||
var apiTree = toTree(groupList.filter(it => it.type == '1'),'0');
|
||||
// 将函数分组转为tree
|
||||
var functionTree = toTree(groupList.filter(it => it.type == '2'),'0');
|
||||
// 记录分组所在路径
|
||||
var groups = {};
|
||||
// 处理分组
|
||||
var processGroup = (parent,list)=>{
|
||||
if(!parent.exists()){
|
||||
parent.mkdir();
|
||||
}
|
||||
list.each(it => {
|
||||
var resource = parent.getResource(it.name);
|
||||
resource.mkdir();
|
||||
groups[it.id] = resource;
|
||||
// 防止序列化children
|
||||
var children = it.remove('children');
|
||||
resource.getResource('group.json').write(JsonUtils.toJsonString(it))
|
||||
if(children){
|
||||
processGroup(resource,children);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 处理接口分组
|
||||
processGroup(root.getResource("api"),apiTree);
|
||||
// 处理函数分组
|
||||
processGroup(root.getResource("function"),functionTree);
|
||||
// 处理接口
|
||||
ds.select(apiSql).map(it => replaceKey(it,'api')).each(it => groups[it.groupId].getResource(it.name + '.ms').write(ApiServiceProvider.serialize(it)));
|
||||
// 处理函数
|
||||
ds.select(functionSql).map(it => replaceKey(it,'function')).each(it => groups[it.groupId].getResource(it.name + '.ms').write(FunctionServiceProvider.serialize(it)));
|
||||
return 'ok';
|
||||
27
magic-api-plugins/magic-api-plugin-cluster/pom.xml
Normal file
27
magic-api-plugins/magic-api-plugin-cluster/pom.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.ssssssss</groupId>
|
||||
<artifactId>magic-api-plugins</artifactId>
|
||||
<version>2.0.0-beta.1</version>
|
||||
</parent>
|
||||
<artifactId>magic-api-plugin-cluster</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>magic-api-plugin-cluster</name>
|
||||
<description>magic-api-plugin-cluster</description>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.ssssssss</groupId>
|
||||
<artifactId>magic-api-plugin-redis</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -0,0 +1,28 @@
|
||||
package org.ssssssss.magicapi.cluster;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 集群配置
|
||||
*
|
||||
* @author mxd
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "magic-api.cluster")
|
||||
public class ClusterConfig {
|
||||
|
||||
/**
|
||||
* redis 通道
|
||||
*/
|
||||
private String channel = "magic-api:notify:channel";
|
||||
|
||||
public String getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public void setChannel(String channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
}
|
||||
@ -1,66 +1,50 @@
|
||||
package org.ssssssss.magicapi.spring.boot.starter;
|
||||
package org.ssssssss.magicapi.cluster;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.redis.listener.ChannelTopic;
|
||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||
import org.ssssssss.magicapi.adapter.Resource;
|
||||
import org.ssssssss.magicapi.adapter.resource.RedisResource;
|
||||
import org.ssssssss.magicapi.model.MagicNotify;
|
||||
import org.ssssssss.magicapi.modules.RedisModule;
|
||||
import org.ssssssss.magicapi.provider.MagicAPIService;
|
||||
import org.ssssssss.magicapi.provider.MagicNotifyService;
|
||||
import org.ssssssss.magicapi.core.config.MagicAPIProperties;
|
||||
import org.ssssssss.magicapi.core.config.MagicPluginConfiguration;
|
||||
import org.ssssssss.magicapi.core.model.MagicNotify;
|
||||
import org.ssssssss.magicapi.core.model.Plugin;
|
||||
import org.ssssssss.magicapi.core.service.MagicAPIService;
|
||||
import org.ssssssss.magicapi.core.service.MagicNotifyService;
|
||||
import org.ssssssss.magicapi.utils.JsonUtils;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* redis配置
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
@ConditionalOnClass(RedisConnectionFactory.class)
|
||||
@Configuration
|
||||
@AutoConfigureBefore(MagicAPIAutoConfiguration.class)
|
||||
public class MagicRedisAutoConfiguration {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(MagicRedisAutoConfiguration.class);
|
||||
@EnableConfigurationProperties(ClusterConfig.class)
|
||||
@Configuration
|
||||
public class MagicClusterConfiguration implements MagicPluginConfiguration {
|
||||
|
||||
private final ClusterConfig config;
|
||||
|
||||
private final MagicAPIProperties properties;
|
||||
|
||||
private final StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
public MagicRedisAutoConfiguration(MagicAPIProperties properties, ObjectProvider<StringRedisTemplate> stringRedisTemplateProvider) {
|
||||
private final Logger logger = LoggerFactory.getLogger(MagicClusterConfiguration.class);
|
||||
|
||||
public MagicClusterConfiguration(MagicAPIProperties properties, ClusterConfig config, ObjectProvider<StringRedisTemplate> stringRedisTemplateProvider) {
|
||||
this.properties = properties;
|
||||
this.config = config;
|
||||
this.stringRedisTemplate = stringRedisTemplateProvider.getIfAvailable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入redis模块
|
||||
*/
|
||||
@Bean
|
||||
public RedisModule redisFunctions(RedisConnectionFactory connectionFactory) {
|
||||
return new RedisModule(connectionFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用Redis存储
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(prefix = "magic-api", name = "resource.type", havingValue = "redis")
|
||||
public Resource magicRedisResource(RedisConnectionFactory connectionFactory) {
|
||||
ResourceConfig resource = properties.getResource();
|
||||
return new RedisResource(new StringRedisTemplate(connectionFactory), resource.getPrefix(), resource.isReadonly());
|
||||
@Override
|
||||
public Plugin plugin() {
|
||||
return new Plugin("Cluster");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -68,18 +52,24 @@ public class MagicRedisAutoConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(prefix = "magic-api", name = "cluster-config.enable", havingValue = "true")
|
||||
public MagicNotifyService magicNotifyService() {
|
||||
return magicNotify -> stringRedisTemplate.convertAndSend(properties.getClusterConfig().getChannel(), Objects.requireNonNull(JsonUtils.toJsonString(magicNotify)));
|
||||
return magicNotify -> stringRedisTemplate.convertAndSend(config.getChannel(), Objects.requireNonNull(JsonUtils.toJsonString(magicNotify)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息处理服务
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public MagicSynchronizationService magicSynchronizationService(MagicNotifyService magicNotifyService) {
|
||||
return new MagicSynchronizationService(magicNotifyService, properties.getInstanceId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 集群通知监听
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "magic-api", name = "cluster-config.enable", havingValue = "true")
|
||||
public RedisMessageListenerContainer magicRedisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory, MagicAPIService magicAPIService) {
|
||||
ClusterConfig config = properties.getClusterConfig();
|
||||
logger.info("开启集群通知监听, Redis channel: {}", config.getChannel());
|
||||
RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
|
||||
redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
|
||||
@ -0,0 +1,54 @@
|
||||
package org.ssssssss.magicapi.cluster;
|
||||
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.ssssssss.magicapi.core.event.*;
|
||||
import org.ssssssss.magicapi.core.config.Constants;
|
||||
import org.ssssssss.magicapi.core.model.MagicNotify;
|
||||
import org.ssssssss.magicapi.core.service.MagicNotifyService;
|
||||
|
||||
public class MagicSynchronizationService {
|
||||
|
||||
private final MagicNotifyService magicNotifyService;
|
||||
|
||||
/**
|
||||
* 当前实例ID
|
||||
*/
|
||||
private final String instanceId;
|
||||
|
||||
public MagicSynchronizationService(MagicNotifyService magicNotifyService, String instanceId) {
|
||||
this.magicNotifyService = magicNotifyService;
|
||||
this.instanceId = instanceId;
|
||||
}
|
||||
|
||||
@EventListener(condition = "#event.source != T(org.ssssssss.magicapi.core.config.Constants).EVENT_SOURCE_NOTIFY")
|
||||
public void onFolderEvent(GroupEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case CREATE:
|
||||
case SAVE:
|
||||
case MOVE:
|
||||
case DELETE:
|
||||
magicNotifyService.sendNotify(new MagicNotify(instanceId, event.getGroup().getId(), event.getAction(), event.getType()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@EventListener(condition = "#event.source != T(org.ssssssss.magicapi.core.config.Constants).EVENT_SOURCE_NOTIFY")
|
||||
public void onFileEvent(FileEvent event) {
|
||||
if (Constants.EVENT_SOURCE_NOTIFY.equals(event.getSource())) {
|
||||
return;
|
||||
}
|
||||
switch (event.getAction()) {
|
||||
case CREATE:
|
||||
case SAVE:
|
||||
case MOVE:
|
||||
case DELETE:
|
||||
magicNotifyService.sendNotify(new MagicNotify(instanceId, event.getEntity().getId(), event.getAction(), Constants.EVENT_TYPE_FILE));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@EventListener(condition = "#event.action == T(org.ssssssss.magicapi.core.event.EventAction).CLEAR && #event.source != T(org.ssssssss.magicapi.core.config.Constants).EVENT_SOURCE_NOTIFY")
|
||||
public void onClearEvent(MagicEvent event){
|
||||
magicNotifyService.sendNotify(new MagicNotify(instanceId, null, event.getAction(), null));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.ssssssss.magicapi.cluster.MagicClusterConfiguration
|
||||
21
magic-api-plugins/magic-api-plugin-elasticsearch/pom.xml
Normal file
21
magic-api-plugins/magic-api-plugin-elasticsearch/pom.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.ssssssss</groupId>
|
||||
<artifactId>magic-api-plugins</artifactId>
|
||||
<version>2.0.0-beta.1</version>
|
||||
</parent>
|
||||
<artifactId>magic-api-plugin-elasticsearch</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>magic-api-plugin-elasticsearch</name>
|
||||
<description>magic-api-plugin-elasticsearch</description>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -0,0 +1,49 @@
|
||||
package org.ssssssss.magicapi.elasticsearch;
|
||||
|
||||
import org.elasticsearch.client.RestClient;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
public class ElasticSearchConnection extends ElasticSearchRest {
|
||||
|
||||
public ElasticSearchConnection(RestClient restClient, String endpoint) {
|
||||
super(restClient);
|
||||
super.endpoint(endpoint);
|
||||
}
|
||||
|
||||
public ElasticSearchConnection parameter(String key, String value) {
|
||||
if (value != null) {
|
||||
parameters.put(key, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ElasticSearchConnection parameters(Map<String, String> params) {
|
||||
if (params != null) {
|
||||
parameters.putAll(params);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Object put(Object data) throws IOException {
|
||||
return processResponse(json(data).doPut());
|
||||
}
|
||||
|
||||
public Object delete() throws IOException {
|
||||
return processResponse(doDelete());
|
||||
}
|
||||
|
||||
public Object delete(Object data) throws IOException {
|
||||
return processResponse(json(data).doDelete());
|
||||
}
|
||||
|
||||
public Object post(Object data) throws IOException {
|
||||
return processResponse(json(data).doPost());
|
||||
}
|
||||
|
||||
public Object get() throws IOException {
|
||||
return processResponse(doGet());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
package org.ssssssss.magicapi.elasticsearch;
|
||||
|
||||
import org.elasticsearch.client.RestClient;
|
||||
import org.ssssssss.magicapi.utils.JsonUtils;
|
||||
import org.ssssssss.script.annotation.Comment;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ElasticSearchIndex {
|
||||
|
||||
private final RestClient restClient;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final String type;
|
||||
|
||||
|
||||
public ElasticSearchIndex(RestClient restClient, String name, String type) {
|
||||
this.restClient = restClient;
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Comment("根据`_id`保存,当存在时更新,不存在时插入")
|
||||
public Object save(@Comment(value = "_id", name = "_id")String _id, @Comment(value = "保存对象", name = "data")Object data) throws IOException {
|
||||
return connect("/%s/%s/%s", this.name, this.type, _id).post(data);
|
||||
}
|
||||
|
||||
@Comment("不指定`_id`插入")
|
||||
public Object insert(@Comment(value = "插入对象", name = "data")Object data) throws IOException {
|
||||
return connect("/%s/%s", this.name, this.type).post(data);
|
||||
}
|
||||
|
||||
@Comment("指定`_id`插入,当`_id`存在时不会更新")
|
||||
public Object insert(@Comment(value = "_id", name = "_id")String _id, @Comment(value = "插入对象", name = "data")Object data) throws IOException {
|
||||
return connect("/%s/%s/%s/_create", this.name, this.type, _id).post(data);
|
||||
}
|
||||
|
||||
@Comment("根据`id`删除")
|
||||
public Object delete(@Comment(value = "id", name = "id")String id) throws IOException {
|
||||
return connect("/%s/%s/%s", this.name, this.type, id).delete();
|
||||
}
|
||||
|
||||
@Comment("批量保存,当包含`id`时,则使用该列值匹配保存")
|
||||
public Object bulkSave(@Comment(value = "保存内容", name = "list") List<Map<String, Object>> list) throws IOException {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
list.forEach(item -> {
|
||||
Object id = item.get("id");
|
||||
if(id != null){
|
||||
builder.append(String.format("{ \"index\":{ \"_id\": \"%s\" } }\r\n", id));
|
||||
} else {
|
||||
builder.append("{ \"index\":{} }\r\n");
|
||||
}
|
||||
builder.append(JsonUtils.toJsonStringWithoutPretty(item));
|
||||
builder.append("\r\n");
|
||||
});
|
||||
return connect("/%s/%s/_bulk", this.name, this.type).post(builder.toString());
|
||||
}
|
||||
|
||||
@Comment("根据`_id`修改")
|
||||
public Object update(@Comment(value = "_id", name = "_id")String _id, @Comment(value = "修改项", name = "data")Object data) throws IOException {
|
||||
return connect("/%s/%s/%s", this.name, this.type, _id).post(Collections.singletonMap("doc", data));
|
||||
}
|
||||
|
||||
@Comment("搜索")
|
||||
public Object search(@Comment(value = "搜索`DSL`语句", name = "dsl")Map<String, Object> dsl) throws IOException {
|
||||
return connect("/%s/_search", this.name).post(dsl);
|
||||
}
|
||||
|
||||
private ElasticSearchConnection connect(String format, Object... args) {
|
||||
return new ElasticSearchConnection(this.restClient, String.format(format, args));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package org.ssssssss.magicapi.elasticsearch;
|
||||
|
||||
import org.elasticsearch.client.RestClient;
|
||||
import org.ssssssss.magicapi.core.annotation.MagicModule;
|
||||
import org.ssssssss.script.annotation.Comment;
|
||||
|
||||
@MagicModule("elasticsearch")
|
||||
public class ElasticSearchModule {
|
||||
|
||||
private static final String DOC = "_doc";
|
||||
|
||||
private final RestClient restClient;
|
||||
|
||||
public ElasticSearchModule(RestClient restClient) {
|
||||
this.restClient = restClient;
|
||||
}
|
||||
|
||||
@Comment(value = "ElasticSearch REST API")
|
||||
public ElasticSearchConnection rest(String url){
|
||||
return new ElasticSearchConnection(this.restClient, url);
|
||||
}
|
||||
|
||||
public ElasticSearchIndex index(String indexName){
|
||||
return new ElasticSearchIndex(this.restClient, indexName, DOC);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,94 @@
|
||||
package org.ssssssss.magicapi.elasticsearch;
|
||||
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.nio.entity.NStringEntity;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.elasticsearch.client.Request;
|
||||
import org.elasticsearch.client.Response;
|
||||
import org.elasticsearch.client.RestClient;
|
||||
import org.ssssssss.magicapi.utils.JsonUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ElasticSearchRest {
|
||||
|
||||
private final RestClient restClient;
|
||||
|
||||
private String method;
|
||||
|
||||
private String endpoint = "/";
|
||||
|
||||
private HttpEntity entity;
|
||||
|
||||
protected final Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
public ElasticSearchRest(RestClient restClient) {
|
||||
this.restClient = restClient;
|
||||
}
|
||||
|
||||
ElasticSearchRest endpoint(String endpoint){
|
||||
this.endpoint = endpoint;
|
||||
return this;
|
||||
}
|
||||
|
||||
Response doGet() throws IOException {
|
||||
this.method = "GET";
|
||||
return execute();
|
||||
}
|
||||
|
||||
Response doPost() throws IOException {
|
||||
this.method = "POST";
|
||||
return execute();
|
||||
}
|
||||
|
||||
Response doDelete() throws IOException {
|
||||
this.method = "DELETE";
|
||||
return execute();
|
||||
}
|
||||
|
||||
Response doPut() throws IOException {
|
||||
this.method = "PUT";
|
||||
return execute();
|
||||
}
|
||||
|
||||
ElasticSearchRest json(Object data){
|
||||
if(data == null){
|
||||
return this;
|
||||
}
|
||||
String json = null;
|
||||
if(data instanceof CharSequence){
|
||||
json = data.toString();
|
||||
} else {
|
||||
json = JsonUtils.toJsonString(data);
|
||||
}
|
||||
if(json != null){
|
||||
this.entity = new NStringEntity(json, ContentType.APPLICATION_JSON);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private Response execute() throws IOException {
|
||||
Request request = new Request(method, this.endpoint);
|
||||
request.addParameters(parameters);
|
||||
request.setEntity(entity);
|
||||
return this.restClient.performRequest(request);
|
||||
}
|
||||
|
||||
Object processResponse(Response response) throws IOException {
|
||||
int code = response.getStatusLine().getStatusCode();
|
||||
if (code >= 200 && code < 300) { // 2xx
|
||||
HttpEntity entity = response.getEntity();
|
||||
String resp = EntityUtils.toString(entity, StandardCharsets.UTF_8);
|
||||
ContentType contentType = ContentType.get(entity);
|
||||
if (Objects.equals(ContentType.APPLICATION_JSON.getMimeType(), contentType.getMimeType())) {
|
||||
return JsonUtils.readValue(resp, Object.class);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package org.ssssssss.magicapi.elasticsearch;
|
||||
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.ssssssss.magicapi.core.config.MagicPluginConfiguration;
|
||||
import org.ssssssss.magicapi.core.model.Plugin;
|
||||
|
||||
@Configuration
|
||||
public class MagicElasticSearchConfiguration implements MagicPluginConfiguration {
|
||||
|
||||
@Override
|
||||
public Plugin plugin() {
|
||||
return new Plugin("ElasticSearch");
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(RestHighLevelClient.class)
|
||||
public ElasticSearchModule elasticSearchModule(RestHighLevelClient restHighLevelClient){
|
||||
return new ElasticSearchModule(restHighLevelClient.getLowLevelClient());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.ssssssss.magicapi.elasticsearch.MagicElasticSearchConfiguration
|
||||
21
magic-api-plugins/magic-api-plugin-mongo/pom.xml
Normal file
21
magic-api-plugins/magic-api-plugin-mongo/pom.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.ssssssss</groupId>
|
||||
<artifactId>magic-api-plugins</artifactId>
|
||||
<version>2.0.0-beta.1</version>
|
||||
</parent>
|
||||
<artifactId>magic-api-plugin-mongo</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>magic-api-plugin-mongo</name>
|
||||
<description>magic-api-plugin-mongo</description>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-mongodb</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -1,31 +1,28 @@
|
||||
package org.ssssssss.magicapi.spring.boot.starter;
|
||||
package org.ssssssss.magicapi.mongo;
|
||||
|
||||
import com.mongodb.client.FindIterable;
|
||||
import com.mongodb.client.MongoCollection;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
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;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.ssssssss.magicapi.modules.MongoCollectionExtension;
|
||||
import org.ssssssss.magicapi.modules.MongoFindIterableExtension;
|
||||
import org.ssssssss.magicapi.modules.MongoModule;
|
||||
import org.ssssssss.magicapi.core.config.MagicPluginConfiguration;
|
||||
import org.ssssssss.magicapi.core.model.Plugin;
|
||||
import org.ssssssss.script.reflection.JavaReflection;
|
||||
|
||||
/**
|
||||
* mongo配置
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnBean(MongoTemplate.class)
|
||||
@AutoConfigureBefore(MagicAPIAutoConfiguration.class)
|
||||
public class MagicMongoAutoConfiguration {
|
||||
public class MagicMongoConfiguration implements MagicPluginConfiguration {
|
||||
|
||||
@Override
|
||||
public Plugin plugin() {
|
||||
return new Plugin("Mongo");
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入mongo模块
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public MongoModule mongoFunctions(MongoTemplate mongoTemplate) {
|
||||
JavaReflection.registerMethodExtension(MongoCollection.class, new MongoCollectionExtension());
|
||||
JavaReflection.registerMethodExtension(FindIterable.class, new MongoFindIterableExtension());
|
||||
@ -1,4 +1,4 @@
|
||||
package org.ssssssss.magicapi.modules;
|
||||
package org.ssssssss.magicapi.mongo;
|
||||
|
||||
import com.mongodb.client.FindIterable;
|
||||
import com.mongodb.client.MongoCollection;
|
||||
@ -1,4 +1,4 @@
|
||||
package org.ssssssss.magicapi.modules;
|
||||
package org.ssssssss.magicapi.mongo;
|
||||
|
||||
import com.mongodb.client.FindIterable;
|
||||
import com.mongodb.client.MongoCursor;
|
||||
@ -1,4 +1,4 @@
|
||||
package org.ssssssss.magicapi.modules;
|
||||
package org.ssssssss.magicapi.mongo;
|
||||
|
||||
import com.mongodb.client.MongoCollection;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
@ -8,15 +8,16 @@ import org.bson.conversions.Bson;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.ssssssss.magicapi.config.MagicModule;
|
||||
import org.ssssssss.magicapi.model.Constants;
|
||||
import org.ssssssss.magicapi.core.config.Constants;
|
||||
import org.ssssssss.magicapi.core.annotation.MagicModule;
|
||||
import org.ssssssss.script.convert.ClassImplicitConvert;
|
||||
import org.ssssssss.script.functions.DynamicAttribute;
|
||||
import org.ssssssss.script.reflection.JavaInvoker;
|
||||
import org.ssssssss.script.reflection.JavaReflection;
|
||||
import org.ssssssss.script.runtime.Variables;
|
||||
|
||||
import java.beans.Transient;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@ -24,7 +25,8 @@ import java.util.Map;
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
public class MongoModule extends HashMap<String, MongoModule.MongoDataBaseGetter> implements MagicModule, ClassImplicitConvert {
|
||||
@MagicModule("mongo")
|
||||
public class MongoModule implements ClassImplicitConvert, DynamicAttribute<MongoModule.MongoDataBaseGetter, MongoModule.MongoDataBaseGetter> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MongoModule.class);
|
||||
|
||||
@ -54,23 +56,19 @@ public class MongoModule extends HashMap<String, MongoModule.MongoDataBaseGetter
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoDataBaseGetter get(Object databaseName) {
|
||||
@Transient
|
||||
public MongoDataBaseGetter getDynamicAttribute(String databaseName) {
|
||||
try {
|
||||
if (databaseName == null) {
|
||||
return null;
|
||||
}
|
||||
MongoDatabase database = (MongoDatabase) invoker.invoke0(factory, null, new Object[]{databaseName.toString()});
|
||||
MongoDatabase database = (MongoDatabase) invoker.invoke0(factory, null, new Object[]{databaseName});
|
||||
return new MongoDataBaseGetter(database);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModuleName() {
|
||||
return "mongo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean support(Class<?> from, Class<?> to) {
|
||||
return Map.class.isAssignableFrom(from) && (Bson.class.isAssignableFrom(to));
|
||||
@ -81,7 +79,7 @@ public class MongoModule extends HashMap<String, MongoModule.MongoDataBaseGetter
|
||||
return new Document((Map<String, Object>) source);
|
||||
}
|
||||
|
||||
public static class MongoDataBaseGetter extends HashMap<String, MongoCollection<Document>> {
|
||||
public static class MongoDataBaseGetter implements DynamicAttribute<MongoCollection<Document>, MongoCollection<Document>> {
|
||||
|
||||
MongoDatabase database;
|
||||
|
||||
@ -90,8 +88,9 @@ public class MongoModule extends HashMap<String, MongoModule.MongoDataBaseGetter
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoCollection<Document> get(Object key) {
|
||||
return database.getCollection(key.toString());
|
||||
@Transient
|
||||
public MongoCollection<Document> getDynamicAttribute(String key) {
|
||||
return database.getCollection(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.ssssssss.magicapi.mongo.MagicMongoConfiguration
|
||||
21
magic-api-plugins/magic-api-plugin-redis/pom.xml
Normal file
21
magic-api-plugins/magic-api-plugin-redis/pom.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.ssssssss</groupId>
|
||||
<artifactId>magic-api-plugins</artifactId>
|
||||
<version>2.0.0-beta.1</version>
|
||||
</parent>
|
||||
<artifactId>magic-api-plugin-redis</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>magic-api-plugin-redis</name>
|
||||
<description>magic-api-plugin-redis</description>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -0,0 +1,46 @@
|
||||
package org.ssssssss.magicapi.redis;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.ssssssss.magicapi.core.config.MagicAPIProperties;
|
||||
import org.ssssssss.magicapi.core.config.MagicPluginConfiguration;
|
||||
import org.ssssssss.magicapi.core.config.Resource;
|
||||
import org.ssssssss.magicapi.core.model.Plugin;
|
||||
|
||||
@Configuration
|
||||
public class MagicRedisConfiguration implements MagicPluginConfiguration {
|
||||
|
||||
private final MagicAPIProperties properties;
|
||||
|
||||
public MagicRedisConfiguration(MagicAPIProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用Redis存储
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(prefix = "magic-api", name = "resource.type", havingValue = "redis")
|
||||
public org.ssssssss.magicapi.core.resource.Resource magicRedisResource(RedisConnectionFactory connectionFactory) {
|
||||
Resource resource = properties.getResource();
|
||||
return new RedisResource(new StringRedisTemplate(connectionFactory), resource.getPrefix(), resource.isReadonly());
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入redis模块
|
||||
*/
|
||||
@Bean
|
||||
public RedisModule redisFunctions(RedisConnectionFactory connectionFactory) {
|
||||
return new RedisModule(connectionFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plugin plugin() {
|
||||
return new Plugin("Redis");
|
||||
}
|
||||
}
|
||||
@ -1,20 +1,20 @@
|
||||
package org.ssssssss.magicapi.modules;
|
||||
package org.ssssssss.magicapi.redis;
|
||||
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisCallback;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.ssssssss.magicapi.config.MagicModule;
|
||||
import org.ssssssss.magicapi.core.annotation.MagicModule;
|
||||
import org.ssssssss.script.functions.DynamicMethod;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* redis模块
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
public class RedisModule implements MagicModule, DynamicMethod {
|
||||
@MagicModule("redis")
|
||||
public class RedisModule implements DynamicMethod {
|
||||
|
||||
private final StringRedisTemplate redisTemplate;
|
||||
|
||||
@ -22,11 +22,6 @@ public class RedisModule implements MagicModule, DynamicMethod {
|
||||
this.redisTemplate = new StringRedisTemplate(connectionFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModuleName() {
|
||||
return "redis";
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化
|
||||
*/
|
||||
@ -40,13 +35,13 @@ public class RedisModule implements MagicModule, DynamicMethod {
|
||||
/**
|
||||
* 反序列化
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object deserialize(Object value) {
|
||||
if (value != null) {
|
||||
if (value instanceof byte[]) {
|
||||
return this.redisTemplate.getStringSerializer().deserialize((byte[]) value);
|
||||
}
|
||||
if (value instanceof List) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object> valueList = (List<Object>) value;
|
||||
List<Object> resultList = new ArrayList<>(valueList.size());
|
||||
for (Object val : valueList) {
|
||||
@ -54,6 +49,12 @@ public class RedisModule implements MagicModule, DynamicMethod {
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
if(value instanceof Map){
|
||||
Map<Object, Object> map = (Map<Object, Object>) value;
|
||||
LinkedHashMap<Object, Object> newMap = new LinkedHashMap<>(map.size());
|
||||
map.forEach((key, val) -> newMap.put(deserialize(key), deserialize(val)));
|
||||
return newMap;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package org.ssssssss.magicapi.adapter.resource;
|
||||
package org.ssssssss.magicapi.redis;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -6,7 +6,8 @@ import org.springframework.data.redis.core.Cursor;
|
||||
import org.springframework.data.redis.core.RedisCallback;
|
||||
import org.springframework.data.redis.core.ScanOptions;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.ssssssss.magicapi.adapter.Resource;
|
||||
import org.ssssssss.magicapi.core.resource.KeyValueResource;
|
||||
import org.ssssssss.magicapi.core.resource.Resource;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
@ -0,0 +1 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.ssssssss.magicapi.redis.MagicRedisConfiguration
|
||||
31
magic-api-plugins/magic-api-plugin-swagger/pom.xml
Normal file
31
magic-api-plugins/magic-api-plugin-swagger/pom.xml
Normal file
@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.ssssssss</groupId>
|
||||
<artifactId>magic-api-plugins</artifactId>
|
||||
<version>2.0.0-beta.1</version>
|
||||
</parent>
|
||||
<artifactId>magic-api-plugin-swagger</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>magic-api-plugin-swagger</name>
|
||||
<description>magic-api-plugin-swagger</description>
|
||||
<properties>
|
||||
<swagger.version>2.9.2</swagger.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
<version>${swagger.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -1,4 +1,4 @@
|
||||
package org.ssssssss.magicapi.spring.boot.starter;
|
||||
package org.ssssssss.magicapi.swagger;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
@ -11,10 +11,13 @@ import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
import org.ssssssss.magicapi.config.MappingHandlerMapping;
|
||||
import org.ssssssss.magicapi.provider.GroupServiceProvider;
|
||||
import org.ssssssss.magicapi.swagger.SwaggerEntity;
|
||||
import org.ssssssss.magicapi.swagger.SwaggerProvider;
|
||||
import org.ssssssss.magicapi.core.config.MagicAPIProperties;
|
||||
import org.ssssssss.magicapi.core.config.MagicPluginConfiguration;
|
||||
import org.ssssssss.magicapi.core.model.Plugin;
|
||||
import org.ssssssss.magicapi.core.service.MagicResourceService;
|
||||
import org.ssssssss.magicapi.core.service.impl.RequestMagicDynamicRegistry;
|
||||
import org.ssssssss.magicapi.swagger.entity.SwaggerEntity;
|
||||
import org.ssssssss.magicapi.swagger.entity.SwaggerProvider;
|
||||
import org.ssssssss.magicapi.utils.Mapping;
|
||||
import springfox.documentation.swagger.web.SwaggerResource;
|
||||
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
|
||||
@ -24,44 +27,39 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Swagger配置类
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
@Configuration
|
||||
@AutoConfigureAfter({MagicAPIAutoConfiguration.class})
|
||||
@EnableConfigurationProperties(MagicAPIProperties.class)
|
||||
@EnableConfigurationProperties(SwaggerConfig.class)
|
||||
@ConditionalOnClass(name = "springfox.documentation.swagger.web.SwaggerResourcesProvider")
|
||||
public class MagicSwaggerConfiguration {
|
||||
public class MagicSwaggerConfiguration implements MagicPluginConfiguration {
|
||||
|
||||
private final MagicAPIProperties properties;
|
||||
private final SwaggerConfig swaggerConfig;
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
@Autowired
|
||||
@Lazy
|
||||
private RequestMappingHandlerMapping requestMappingHandlerMapping;
|
||||
|
||||
public MagicSwaggerConfiguration(MagicAPIProperties properties, ApplicationContext applicationContext) {
|
||||
public MagicSwaggerConfiguration(MagicAPIProperties properties, SwaggerConfig swaggerConfig, ApplicationContext applicationContext) {
|
||||
this.properties = properties;
|
||||
this.swaggerConfig = swaggerConfig;
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plugin plugin() {
|
||||
return new Plugin("Swagger");
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public SwaggerResourcesProvider magicSwaggerResourcesProvider(MappingHandlerMapping handlerMapping, GroupServiceProvider groupServiceProvider, ServletContext servletContext) throws NoSuchMethodException {
|
||||
SwaggerConfig config = properties.getSwaggerConfig();
|
||||
public SwaggerResourcesProvider magicSwaggerResourcesProvider(RequestMagicDynamicRegistry requestMagicDynamicRegistry, MagicResourceService magicResourceService, ServletContext servletContext) throws NoSuchMethodException {
|
||||
Mapping mapping = Mapping.create(requestMappingHandlerMapping);
|
||||
RequestMappingInfo requestMappingInfo = mapping.paths(config.getLocation()).build();
|
||||
|
||||
// 构建文档信息
|
||||
SwaggerProvider swaggerProvider = new SwaggerProvider();
|
||||
swaggerProvider.setGroupServiceProvider(groupServiceProvider);
|
||||
swaggerProvider.setMappingHandlerMapping(handlerMapping);
|
||||
swaggerProvider.setPersistenceResponseBody(properties.isPersistenceResponseBody());
|
||||
RequestMappingInfo requestMappingInfo = mapping.paths(swaggerConfig.getLocation()).build();
|
||||
SwaggerEntity.License license = new SwaggerEntity.License("MIT", "https://gitee.com/ssssssss-team/magic-api/blob/master/LICENSE");
|
||||
swaggerProvider.setInfo(new SwaggerEntity.Info(config.getDescription(), config.getVersion(), config.getTitle(), license, config.getConcat()));
|
||||
swaggerProvider.setBasePath(servletContext.getContextPath());
|
||||
SwaggerEntity.Info info = new SwaggerEntity.Info(swaggerConfig.getDescription(), swaggerConfig.getVersion(), swaggerConfig.getTitle(), license, swaggerConfig.getConcat());
|
||||
// 构建文档信息
|
||||
SwaggerProvider swaggerProvider = new SwaggerProvider(requestMagicDynamicRegistry, magicResourceService, servletContext.getContextPath(), info, properties.isPersistenceResponseBody());
|
||||
|
||||
|
||||
// 注册swagger.json
|
||||
@ -70,7 +68,7 @@ public class MagicSwaggerConfiguration {
|
||||
return () -> {
|
||||
List<SwaggerResource> resources = new ArrayList<>();
|
||||
// 追加Magic Swagger信息
|
||||
resources.add(swaggerResource(config.getName(), config.getLocation()));
|
||||
resources.add(swaggerResource(swaggerConfig.getName(), swaggerConfig.getLocation()));
|
||||
Map<String, SwaggerResourcesProvider> beans = applicationContext.getBeansOfType(SwaggerResourcesProvider.class);
|
||||
// 获取已定义的文档信息
|
||||
for (Map.Entry<String, SwaggerResourcesProvider> entry : beans.entrySet()) {
|
||||
@ -1,13 +1,16 @@
|
||||
package org.ssssssss.magicapi.spring.boot.starter;
|
||||
package org.ssssssss.magicapi.swagger;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||
import org.ssssssss.magicapi.swagger.SwaggerEntity;
|
||||
import org.ssssssss.magicapi.swagger.entity.SwaggerEntity;
|
||||
|
||||
/**
|
||||
* Swagger 配置
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "magic-api.swagger")
|
||||
public class SwaggerConfig {
|
||||
|
||||
/**
|
||||
@ -1,4 +1,4 @@
|
||||
package org.ssssssss.magicapi.swagger;
|
||||
package org.ssssssss.magicapi.swagger.entity;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@ -17,17 +17,17 @@ public class SwaggerEntity {
|
||||
|
||||
private Info info;
|
||||
|
||||
private Set<Tag> tags = new TreeSet<>(Comparator.comparing(Tag::getName));
|
||||
private final Set<Tag> tags = new TreeSet<>(Comparator.comparing(Tag::getName));
|
||||
|
||||
private Map<String, Object> definitions = new HashMap<>();
|
||||
private final Map<String, Object> definitions = new HashMap<>();
|
||||
|
||||
private Map<String, Map<String, Path>> paths = new HashMap<>();
|
||||
private final Map<String, Map<String, Path>> paths = new HashMap<>();
|
||||
|
||||
private static Map<String, Object> doProcessSchema(Object target) {
|
||||
Map<String, Object> result = new HashMap<>(3);
|
||||
result.put("type", getType(target));
|
||||
if (target instanceof List) {
|
||||
List targetList = (List) target;
|
||||
List<?> targetList = (List<?>) target;
|
||||
if (targetList.size() > 0) {
|
||||
result.put("items", doProcessSchema(targetList.get(0)));
|
||||
} else {
|
||||
@ -244,7 +244,7 @@ public class SwaggerEntity {
|
||||
|
||||
private String description;
|
||||
|
||||
private String operationId;
|
||||
private final String operationId;
|
||||
|
||||
private List<String> produces = new ArrayList<>();
|
||||
|
||||
@ -343,7 +343,6 @@ public class SwaggerEntity {
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static class Parameter {
|
||||
|
||||
private String name;
|
||||
@ -367,9 +366,6 @@ public class SwaggerEntity {
|
||||
this.description = description;
|
||||
this.required = required;
|
||||
if ("body".equalsIgnoreCase(in)) {
|
||||
Map<String, Object> schema = new HashMap<>();
|
||||
schema.put("type", type);
|
||||
schema.put("example", example);
|
||||
this.schema = "";
|
||||
} else {
|
||||
this.example = example;
|
||||
@ -1,21 +1,23 @@
|
||||
package org.ssssssss.magicapi.swagger;
|
||||
package org.ssssssss.magicapi.swagger.entity;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.ssssssss.magicapi.config.MappingHandlerMapping;
|
||||
import org.ssssssss.magicapi.model.ApiInfo;
|
||||
import org.ssssssss.magicapi.model.BaseDefinition;
|
||||
import org.ssssssss.magicapi.model.DataType;
|
||||
import org.ssssssss.magicapi.model.Path;
|
||||
import org.ssssssss.magicapi.provider.GroupServiceProvider;
|
||||
import org.ssssssss.magicapi.core.config.MagicConfiguration;
|
||||
import org.ssssssss.magicapi.core.model.ApiInfo;
|
||||
import org.ssssssss.magicapi.core.model.BaseDefinition;
|
||||
import org.ssssssss.magicapi.core.model.DataType;
|
||||
import org.ssssssss.magicapi.core.model.Path;
|
||||
import org.ssssssss.magicapi.core.service.MagicResourceService;
|
||||
import org.ssssssss.magicapi.core.service.impl.RequestMagicDynamicRegistry;
|
||||
import org.ssssssss.magicapi.utils.JsonUtils;
|
||||
import org.ssssssss.magicapi.utils.PathUtils;
|
||||
import org.ssssssss.script.parsing.ast.literal.BooleanLiteral;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static org.ssssssss.magicapi.model.Constants.*;
|
||||
import static org.ssssssss.magicapi.core.config.Constants.*;
|
||||
|
||||
/**
|
||||
* 生成swagger用的json
|
||||
@ -32,56 +34,46 @@ public class SwaggerProvider {
|
||||
* body空对象
|
||||
*/
|
||||
private static final String BODY_EMPTY = "{}";
|
||||
|
||||
private final Map<String, Object> DEFINITION_MAP = new ConcurrentHashMap<>();
|
||||
private MappingHandlerMapping mappingHandlerMapping;
|
||||
|
||||
private final RequestMagicDynamicRegistry requestMagicDynamicRegistry;
|
||||
|
||||
private final MagicResourceService magicResourceService;
|
||||
/**
|
||||
* 基础路径
|
||||
*/
|
||||
private String basePath;
|
||||
private GroupServiceProvider groupServiceProvider;
|
||||
private SwaggerEntity.Info info;
|
||||
private boolean persistenceResponseBody;
|
||||
private final String basePath;
|
||||
private final SwaggerEntity.Info info;
|
||||
private final boolean persistenceResponseBody;
|
||||
|
||||
public void setMappingHandlerMapping(MappingHandlerMapping mappingHandlerMapping) {
|
||||
this.mappingHandlerMapping = mappingHandlerMapping;
|
||||
}
|
||||
|
||||
public void setGroupServiceProvider(GroupServiceProvider groupServiceProvider) {
|
||||
this.groupServiceProvider = groupServiceProvider;
|
||||
}
|
||||
|
||||
public void setInfo(SwaggerEntity.Info info) {
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public void setBasePath(String basePath) {
|
||||
public SwaggerProvider(RequestMagicDynamicRegistry requestMagicDynamicRegistry, MagicResourceService magicResourceService, String basePath, SwaggerEntity.Info info, boolean persistenceResponseBody) {
|
||||
this.requestMagicDynamicRegistry = requestMagicDynamicRegistry;
|
||||
this.magicResourceService = magicResourceService;
|
||||
this.basePath = basePath;
|
||||
}
|
||||
|
||||
public void setPersistenceResponseBody(boolean persistenceResponseBody) {
|
||||
this.info = info;
|
||||
this.persistenceResponseBody = persistenceResponseBody;
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
public SwaggerEntity swaggerJson() {
|
||||
this.DEFINITION_MAP.clear();
|
||||
List<ApiInfo> infos = mappingHandlerMapping.getApiInfos();
|
||||
List<ApiInfo> infos = requestMagicDynamicRegistry.mappings();
|
||||
SwaggerEntity swaggerEntity = new SwaggerEntity();
|
||||
swaggerEntity.setInfo(info);
|
||||
swaggerEntity.setBasePath(this.basePath);
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
for (ApiInfo info : infos) {
|
||||
String groupName = groupServiceProvider.getFullName(info.getGroupId()).replace("/", "-");
|
||||
String requestPath = "/" + mappingHandlerMapping.getRequestPath(info.getGroupId(), info.getPath());
|
||||
String groupName = magicResourceService.getGroupName(info.getGroupId()).replace("/", "-");
|
||||
String requestPath = PathUtils.replaceSlash("/" + magicResourceService.getGroupPath(info.getGroupId()) + "/" + info.getPath());
|
||||
SwaggerEntity.Path path = new SwaggerEntity.Path(info.getId());
|
||||
path.addTag(groupName);
|
||||
boolean hasBody = false;
|
||||
try {
|
||||
List<Map<String, Object>> parameters = parseParameters(mapper, info);
|
||||
List<Map<String, Object>> parameters = parseParameters(info);
|
||||
hasBody = parameters.stream().anyMatch(it -> VAR_NAME_REQUEST_BODY.equals(it.get("in")));
|
||||
BaseDefinition baseDefinition = info.getRequestBodyDefinition();
|
||||
if (hasBody && baseDefinition != null) {
|
||||
doProcessDefinition(baseDefinition, info, "root_", "request", 0);
|
||||
doProcessDefinition(baseDefinition, info, groupName, "root_", "request", 0);
|
||||
}
|
||||
parameters.forEach(path::addParameter);
|
||||
if (this.persistenceResponseBody) {
|
||||
@ -90,10 +82,10 @@ public class SwaggerProvider {
|
||||
Map responseMap = parseResponse(info);
|
||||
if (!responseMap.isEmpty()) {
|
||||
path.setResponses(responseMap);
|
||||
doProcessDefinition(baseDefinition, info, "root_" + baseDefinition.getName(), "response", 0);
|
||||
doProcessDefinition(baseDefinition, info, groupName, "root_" + baseDefinition.getName(), "response", 0);
|
||||
}
|
||||
} else {
|
||||
path.addResponse("200", mapper.readValue(Objects.toString(info.getResponseBody(), BODY_EMPTY), Object.class));
|
||||
path.addResponse("200", JsonUtils.readValue(Objects.toString(info.getResponseBody(), BODY_EMPTY), Object.class));
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,26 +113,23 @@ public class SwaggerProvider {
|
||||
return swaggerEntity;
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> parseParameters(ObjectMapper mapper, ApiInfo info) {
|
||||
private List<Map<String, Object>> parseParameters(ApiInfo info) {
|
||||
List<Map<String, Object>> parameters = new ArrayList<>();
|
||||
info.getParameters().forEach(it -> parameters.add(SwaggerEntity.createParameter(it.isRequired(), it.getName(), VAR_NAME_QUERY, it.getDataType().getJavascriptType(), it.getDescription(), it.getValue())));
|
||||
info.getHeaders().forEach(it -> parameters.add(SwaggerEntity.createParameter(it.isRequired(), it.getName(), VAR_NAME_HEADER, it.getDataType().getJavascriptType(), it.getDescription(), it.getValue())));
|
||||
List<Path> paths = new ArrayList<>(info.getPaths());
|
||||
MappingHandlerMapping.findGroups(info.getGroupId())
|
||||
MagicConfiguration.getMagicResourceService().getGroupsByFileId(info.getId())
|
||||
.stream()
|
||||
.flatMap(it -> it.getPaths().stream())
|
||||
.forEach(it -> {
|
||||
if (!paths.contains(it)) {
|
||||
paths.add(it);
|
||||
}
|
||||
});
|
||||
.filter(it -> !paths.contains(it))
|
||||
.forEach(paths::add);
|
||||
paths.forEach(it -> parameters.add(SwaggerEntity.createParameter(it.isRequired(), it.getName(), VAR_NAME_PATH_VARIABLE, it.getDataType().getJavascriptType(), it.getDescription(), it.getValue())));
|
||||
try {
|
||||
BaseDefinition baseDefinition = info.getRequestBodyDefinition();
|
||||
if (baseDefinition != null && !CollectionUtils.isEmpty(baseDefinition.getChildren())) {
|
||||
Map<String, Object> parameter = SwaggerEntity.createParameter(baseDefinition.isRequired(), StringUtils.isNotBlank(baseDefinition.getName()) ? baseDefinition.getName() : VAR_NAME_REQUEST_BODY, VAR_NAME_REQUEST_BODY, baseDefinition.getDataType().getJavascriptType(), baseDefinition.getDescription(), baseDefinition);
|
||||
Map<String, Object> schema = new HashMap<>(2);
|
||||
String groupName = groupServiceProvider.getFullName(info.getGroupId()).replace("/", "-");
|
||||
String groupName = magicResourceService.getGroupName(info.getGroupId()).replace("/", "-");
|
||||
String voName = groupName + "«" + info.getPath().replaceFirst("/", "").replaceAll("/", "_") + "«request«";
|
||||
if (DataType.Array == baseDefinition.getDataType()) {
|
||||
voName += "root_" + (StringUtils.isNotBlank(baseDefinition.getName()) ? baseDefinition.getName() + "_" : "_") + "»»»";
|
||||
@ -158,7 +147,7 @@ public class SwaggerProvider {
|
||||
parameter.put("schema", schema);
|
||||
parameters.add(parameter);
|
||||
} else {
|
||||
Object object = mapper.readValue(info.getRequestBody(), Object.class);
|
||||
Object object = JsonUtils.readValue(info.getRequestBody(), Object.class);
|
||||
boolean isListOrMap = (object instanceof List || object instanceof Map);
|
||||
if (isListOrMap && BooleanLiteral.isTrue(object)) {
|
||||
parameters.add(SwaggerEntity.createParameter(false, VAR_NAME_REQUEST_BODY, VAR_NAME_REQUEST_BODY, object instanceof List ? VAR_NAME_REQUEST_BODY_VALUE_TYPE_ARRAY : VAR_NAME_REQUEST_BODY_VALUE_TYPE_OBJECT, null, object));
|
||||
@ -175,7 +164,7 @@ public class SwaggerProvider {
|
||||
|
||||
BaseDefinition baseDefinition = info.getResponseBodyDefinition();
|
||||
if (!CollectionUtils.isEmpty(baseDefinition.getChildren())) {
|
||||
String groupName = groupServiceProvider.getFullName(info.getGroupId()).replace("/", "-");
|
||||
String groupName = magicResourceService.getGroupName(info.getGroupId()).replace("/", "-");
|
||||
String voName = groupName + "«" + info.getPath().replaceFirst("/", "").replaceAll("/", "_") + "«response«";
|
||||
voName += "root_" + baseDefinition.getName() + "»»»";
|
||||
|
||||
@ -192,25 +181,24 @@ public class SwaggerProvider {
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, Object> doProcessDefinition(BaseDefinition target, ApiInfo info, String parentName, String definitionType, int level) {
|
||||
private Map<String, Object> doProcessDefinition(BaseDefinition target, ApiInfo info, String groupName, String parentName, String definitionType, int level) {
|
||||
Map<String, Object> result = new HashMap<>(4);
|
||||
result.put("description", target.getDescription());
|
||||
if (DataType.Array == target.getDataType()) {
|
||||
if (!CollectionUtils.isEmpty(target.getChildren())) {
|
||||
result.put("items", doProcessDefinition(target.getChildren().get(0), info, parentName + target.getName() + "_", definitionType, level + 1));
|
||||
result.put("items", doProcessDefinition(target.getChildren().get(0), info, groupName, parentName + target.getName() + "_", definitionType, level + 1));
|
||||
} else {
|
||||
result.put("items", Collections.emptyList());
|
||||
}
|
||||
result.put("type", target.getDataType().getJavascriptType());
|
||||
} else if (DataType.Object == target.getDataType()) {
|
||||
String groupName = groupServiceProvider.getFullName(info.getGroupId()).replace("/", "-");
|
||||
String voName = groupName + "«" + info.getPath().replaceFirst("/", "").replaceAll("/", "_") + (StringUtils.equals("response", definitionType) ? "«response«" : "«request«") + parentName + target.getName() + "»»»";
|
||||
|
||||
Map<String, Object> definition = new HashMap<>(4);
|
||||
Map<String, Map<String, Object>> properties = new HashMap<>(target.getChildren().size());
|
||||
Set<String> requiredSet = new HashSet<>(target.getChildren().size());
|
||||
for (BaseDefinition obj : target.getChildren()) {
|
||||
properties.put(obj.getName(), doProcessDefinition(obj, info, parentName + target.getName() + "_", definitionType, level + 1));
|
||||
properties.put(obj.getName(), doProcessDefinition(obj, info, groupName, parentName + target.getName() + "_", definitionType, level + 1));
|
||||
if (obj.isRequired()) {
|
||||
requiredSet.add(obj.getName());
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.ssssssss.magicapi.swagger.MagicSwaggerConfiguration
|
||||
91
magic-api-plugins/magic-api-plugin-task/pom.xml
Normal file
91
magic-api-plugins/magic-api-plugin-task/pom.xml
Normal file
@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.ssssssss</groupId>
|
||||
<artifactId>magic-api-plugins</artifactId>
|
||||
<version>2.0.0-beta.1</version>
|
||||
</parent>
|
||||
<artifactId>magic-api-plugin-task</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>magic-api-plugin-task</name>
|
||||
<description>magic-api-plugin-task</description>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- npm install && npm run build -->
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>1.6.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>exec-npm-install</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<executable>npm</executable>
|
||||
<arguments>
|
||||
<argument>install</argument>
|
||||
</arguments>
|
||||
<workingDirectory>${basedir}/src/console</workingDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>exec-npm-run-build</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<executable>npm</executable>
|
||||
<arguments>
|
||||
<argument>run</argument>
|
||||
<argument>build</argument>
|
||||
</arguments>
|
||||
<workingDirectory>${basedir}/src/console</workingDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-resource</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${basedir}/target/classes/magic-editor/plugins</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${basedir}/src/console/dist</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "magic-test",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "vite build"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"vue": "^3.2.26",
|
||||
"@vitejs/plugin-vue": "^2.0.1",
|
||||
"vite-plugin-svg-icons": "^1.1.0",
|
||||
"vite": "^2.7.10"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div class="magic-task-info">
|
||||
<form>
|
||||
<label>{{ $i('message.enable') }}</label>
|
||||
<magic-checkbox v-model:value="info.enabled" />
|
||||
<label>cron</label>
|
||||
<magic-input v-model:value="info.cron" :placeholder="$i('task.form.placeholder.cron')" width="250px"/>
|
||||
<label>{{ $i('task.form.name') }}</label>
|
||||
<magic-input v-model:value="info.name" :placeholder="$i('task.form.placeholder.name')" width="250px"/>
|
||||
<label>{{ $i('task.form.path') }}</label>
|
||||
<magic-input v-model:value="info.path" :placeholder="$i('task.form.placeholder.path')" width="auto" style="flex:1"/>
|
||||
</form>
|
||||
<div style="flex:1;padding-top:5px;">
|
||||
<magic-textarea v-model:value="info.description" :placeholder="$i('task.form.placeholder.description')"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { inject } from 'vue'
|
||||
const $i = inject('i18n.format')
|
||||
const info = inject('info')
|
||||
</script>
|
||||
<style scoped>
|
||||
.magic-task-info{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
padding: 5px;
|
||||
}
|
||||
.magic-task-info form{
|
||||
display: flex;
|
||||
}
|
||||
.magic-task-info form label{
|
||||
display: inline-block;
|
||||
width: 75px;
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
font-weight: 400;
|
||||
text-align: right;
|
||||
padding: 0 5px;
|
||||
}
|
||||
.magic-task-info form :deep(.magic-checkbox){
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
.magic-task-info form :deep(.magic-textarea){
|
||||
margin: 5px;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,16 @@
|
||||
export default {
|
||||
task: {
|
||||
title: 'Task Info',
|
||||
name: 'Task',
|
||||
form: {
|
||||
name: 'Task Name',
|
||||
path: 'Task Path',
|
||||
placeholder: {
|
||||
cron: 'Please Enter Cron Expression',
|
||||
name: 'Please Enter Task Name',
|
||||
path: 'Please Enter Task Path',
|
||||
description: 'Please Enter Task Description'
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
export default {
|
||||
task: {
|
||||
title: '定时任务信息',
|
||||
name: '定时任务',
|
||||
form: {
|
||||
name: '任务名称',
|
||||
path: '任务路径',
|
||||
placeholder: {
|
||||
cron: '请输入Cron表达式',
|
||||
name: '请输入任务名称',
|
||||
path: '请输入任务路径',
|
||||
description: '请输入任务描述'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
<svg class="icon" style="width: 1em;height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M512.78747336 189.40294037A372.25009177 372.25009177 0 1 1 512.73122556 933.8468761a372.25009177 372.25009177 0 0 1 0-744.50018353z m20.02433313 179.43151904h-39.93616901a6.69352725 6.69352725 0 0 0-6.69352725 6.69352725V604.2328627c0 2.19367667 1.01246621 4.1623613 2.75615881 5.39982038l137.41416897 100.23415877a6.63727863 6.63727863 0 0 0 9.28094102-1.4624511l23.79295651-32.39891977a6.5247822 6.5247822 0 0 0-1.51869891-9.22469321L539.44908511 581.17113175V375.52798666a6.69352725 6.69352725 0 0 0-6.63727862-6.69352726zM711.28710712 90.125a24.80542356 24.80542356 0 0 1-1e-8 49.61084629H314.23159262a24.80542356 24.80542356 0 0 1 0-49.61084629h397.1117623z" /></svg>
|
||||
|
After Width: | Height: | Size: 863 B |
@ -0,0 +1,35 @@
|
||||
import MagicTask from './service/magic-task.js'
|
||||
import localZhCN from './i18n/zh-cn.js'
|
||||
import localEn from './i18n/en.js'
|
||||
import MagicTaskInfo from './components/magic-task-info.vue'
|
||||
import 'vite-plugin-svg-icons/register'
|
||||
export default (opt) => {
|
||||
const i18n = opt.i18n
|
||||
// 添加i18n 国际化信息
|
||||
i18n.add('zh-cn', localZhCN)
|
||||
i18n.add('en', localEn)
|
||||
return {
|
||||
// 左侧资源
|
||||
resource: [{
|
||||
// 资源类型,和后端存储结构一致
|
||||
type: 'task',
|
||||
// 展示图标
|
||||
icon: '#magic-task-task', // #开头表示图标在插件中
|
||||
// 展示名称
|
||||
title: 'task.name',
|
||||
// 运行服务
|
||||
service: MagicTask(opt.bus, opt.constants, i18n.format, opt.Message, opt.request),
|
||||
}],
|
||||
// 底部工具条
|
||||
toolbars: [{
|
||||
// 当打开的资源类型为 task 时显示
|
||||
type: 'task',
|
||||
// 工具条展示的标题
|
||||
title: 'task.title',
|
||||
// 展示图标
|
||||
icon: 'parameter',
|
||||
// 对应的组件
|
||||
component: MagicTaskInfo,
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
export default function (bus, constants, $i, Message, request) {
|
||||
return {
|
||||
// svg text
|
||||
getIcon: item => ['TASK', '#9012FE'],
|
||||
// 任务名称
|
||||
name: $i('task.name'),
|
||||
language: 'magicscript',
|
||||
// 执行测试的逻辑
|
||||
doTest: (opened) => {
|
||||
opened.running = true
|
||||
const info = opened.item
|
||||
const requestConfig = {
|
||||
baseURL: constants.SERVER_URL,
|
||||
url: '/task/execute',
|
||||
method: 'POST',
|
||||
responseType: 'json',
|
||||
headers: {},
|
||||
withCredentials: true
|
||||
}
|
||||
bus.$emit(Message.SWITCH_TOOLBAR, 'log')
|
||||
requestConfig.headers[constants.HEADER_REQUEST_CLIENT_ID] = constants.CLIENT_ID
|
||||
requestConfig.headers[constants.HEADER_REQUEST_SCRIPT_ID] = opened.item.id
|
||||
requestConfig.headers[constants.HEADER_MAGIC_TOKEN] = constants.HEADER_MAGIC_TOKEN_VALUE
|
||||
// 设置断点
|
||||
requestConfig.headers[constants.HEADER_REQUEST_BREAKPOINTS] = (opened.decorations || []).filter(it => it.options.linesDecorationsClassName === 'breakpoints').map(it => it.range.startLineNumber).join(',')
|
||||
const fullName = opened.path()
|
||||
bus.status(`开始测试定时任务「${fullName}」`)
|
||||
request.sendPost('/task/execute', { id: info.id }, requestConfig).success(res => {
|
||||
opened.running = false
|
||||
}).end(() => {
|
||||
bus.status(`定时任务「${fullName}」测试完毕`)
|
||||
opened.running = false
|
||||
})
|
||||
},
|
||||
// 是否允许执行测试
|
||||
runnable: true,
|
||||
// 是否需要填写路径
|
||||
requirePath: true,
|
||||
// 合并
|
||||
merge: item => item
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import viteSvgIcons from 'vite-plugin-svg-icons'
|
||||
import path from 'path'
|
||||
import pkg from './package.json'
|
||||
|
||||
export default {
|
||||
base: './',
|
||||
build: {
|
||||
minify: false,
|
||||
cssCodeSplit: true, // 将组件的 style 打包到 js 文件中
|
||||
outDir: 'dist',
|
||||
lib: {
|
||||
target: 'esnext',
|
||||
formats: ['iife'],
|
||||
entry: path.resolve(__dirname, 'src/index.js'),
|
||||
name: 'MagicTask',
|
||||
fileName: (format) => `magic-task.${pkg.version}.${format}.js`
|
||||
},
|
||||
rollupOptions: {
|
||||
// 确保外部化处理那些你不想打包进库的依赖
|
||||
external: ['vue'],
|
||||
output: {
|
||||
// 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
|
||||
globals: {
|
||||
vue: 'Vue'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
vue(),
|
||||
viteSvgIcons({
|
||||
iconDirs: [path.resolve(process.cwd(), 'src/icons')],
|
||||
symbolId: 'magic-task-[name]'
|
||||
}),
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
package org.ssssssss.magicapi.task.model;
|
||||
|
||||
import org.ssssssss.magicapi.core.model.MagicEntity;
|
||||
import org.ssssssss.magicapi.core.model.PathMagicEntity;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class TaskInfo extends PathMagicEntity {
|
||||
|
||||
/**
|
||||
* cron 表达式
|
||||
*/
|
||||
private String cron;
|
||||
|
||||
/**
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled;
|
||||
|
||||
|
||||
public String getCron() {
|
||||
return cron;
|
||||
}
|
||||
|
||||
public void setCron(String cron) {
|
||||
this.cron = cron;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public TaskInfo copy() {
|
||||
TaskInfo info = new TaskInfo();
|
||||
super.copyTo(info);
|
||||
info.setCron(this.cron);
|
||||
info.setEnabled(this.enabled);
|
||||
return info;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MagicEntity simple() {
|
||||
TaskInfo info = new TaskInfo();
|
||||
super.simple(info);
|
||||
return info;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
if (!super.equals(o)) return false;
|
||||
TaskInfo taskInfo = (TaskInfo) o;
|
||||
return Objects.equals(id, taskInfo.id) &&
|
||||
Objects.equals(path, taskInfo.path) &&
|
||||
Objects.equals(script, taskInfo.script) &&
|
||||
Objects.equals(name, taskInfo.name) &&
|
||||
Objects.equals(cron, taskInfo.cron) &&
|
||||
Objects.equals(enabled, taskInfo.enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, path, script, name, groupId, cron, enabled);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package org.ssssssss.magicapi.task.service;
|
||||
|
||||
import org.ssssssss.magicapi.core.service.AbstractPathMagicResourceStorage;
|
||||
import org.ssssssss.magicapi.task.model.TaskInfo;
|
||||
|
||||
public class TaskInfoMagicResourceStorage extends AbstractPathMagicResourceStorage<TaskInfo> {
|
||||
|
||||
@Override
|
||||
public String folder() {
|
||||
return "task";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<TaskInfo> magicClass() {
|
||||
return TaskInfo.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(TaskInfo entity) {
|
||||
notBlank(entity.getCron(), CRON_ID_REQUIRED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String buildMappingKey(TaskInfo info) {
|
||||
return buildMappingKey(info, magicResourceService.getGroupPath(info.getGroupId()));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,90 @@
|
||||
package org.ssssssss.magicapi.task.service;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.config.CronTask;
|
||||
import org.ssssssss.magicapi.core.config.MagicConfiguration;
|
||||
import org.ssssssss.magicapi.core.event.FileEvent;
|
||||
import org.ssssssss.magicapi.core.event.GroupEvent;
|
||||
import org.ssssssss.magicapi.core.service.AbstractMagicDynamicRegistry;
|
||||
import org.ssssssss.magicapi.core.service.MagicResourceStorage;
|
||||
import org.ssssssss.magicapi.task.model.TaskInfo;
|
||||
import org.ssssssss.magicapi.utils.ScriptManager;
|
||||
import org.ssssssss.script.MagicScriptContext;
|
||||
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
||||
public class TaskMagicDynamicRegistry extends AbstractMagicDynamicRegistry<TaskInfo> {
|
||||
|
||||
private final TaskScheduler taskScheduler;
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(TaskMagicDynamicRegistry.class);
|
||||
|
||||
public TaskMagicDynamicRegistry(MagicResourceStorage<TaskInfo> magicResourceStorage, TaskScheduler taskScheduler) {
|
||||
super(magicResourceStorage);
|
||||
this.taskScheduler = taskScheduler;
|
||||
}
|
||||
|
||||
@EventListener(condition = "#event.type == 'task'")
|
||||
public void onFileEvent(FileEvent event) {
|
||||
processEvent(event);
|
||||
}
|
||||
|
||||
@EventListener(condition = "#event.type == 'task'")
|
||||
public void onGroupEvent(GroupEvent event) {
|
||||
processEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean register(TaskInfo entity) {
|
||||
unregister(entity);
|
||||
return super.register(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean register(MappingNode<TaskInfo> mappingNode) {
|
||||
TaskInfo info = mappingNode.getEntity();
|
||||
if (taskScheduler != null) {
|
||||
CronTask cronTask = new CronTask(() -> {
|
||||
TaskInfo entity = mappingNode.getEntity();
|
||||
String scriptName = MagicConfiguration.getMagicResourceService().getScriptName(entity);
|
||||
if (entity.isEnabled()) {
|
||||
try {
|
||||
logger.info("定时任务:[{}]开始执行", scriptName);
|
||||
MagicScriptContext magicScriptContext = new MagicScriptContext();
|
||||
magicScriptContext.setScriptName(scriptName);
|
||||
ScriptManager.executeScript(entity.getScript(), magicScriptContext);
|
||||
} catch (Exception e) {
|
||||
logger.error("定时任务执行出错", e);
|
||||
} finally {
|
||||
logger.info("定时任务:[{}]执行完毕", scriptName);
|
||||
}
|
||||
}
|
||||
}, info.getCron());
|
||||
mappingNode.setMappingData(taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger()));
|
||||
logger.debug("注册定时任务:[{},{}]", MagicConfiguration.getMagicResourceService().getScriptName(info), info.getCron());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unregister(MappingNode<TaskInfo> mappingNode) {
|
||||
if (taskScheduler == null) {
|
||||
return;
|
||||
}
|
||||
TaskInfo info = mappingNode.getEntity();
|
||||
logger.debug("取消注册定时任务:[{}, {}, {}]", info.getName(), info.getPath(), info.getCron());
|
||||
ScheduledFuture<?> scheduledFuture = (ScheduledFuture<?>) mappingNode.getMappingData();
|
||||
if (scheduledFuture != null) {
|
||||
try {
|
||||
scheduledFuture.cancel(true);
|
||||
} catch (Exception e) {
|
||||
String scriptName = MagicConfiguration.getMagicResourceService().getScriptName(info);
|
||||
logger.warn("定时任务:[{}]取消失败", scriptName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
package org.ssssssss.magicapi.task.starter;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.ssssssss.magicapi.core.config.MagicPluginConfiguration;
|
||||
import org.ssssssss.magicapi.core.model.Plugin;
|
||||
import org.ssssssss.magicapi.core.web.MagicControllerRegister;
|
||||
import org.ssssssss.magicapi.task.service.TaskInfoMagicResourceStorage;
|
||||
import org.ssssssss.magicapi.task.service.TaskMagicDynamicRegistry;
|
||||
import org.ssssssss.magicapi.task.web.MagicTaskController;
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(MagicTaskConfig.class)
|
||||
public class MagicAPITaskConfiguration implements MagicPluginConfiguration {
|
||||
|
||||
private final MagicTaskConfig config;
|
||||
|
||||
public MagicAPITaskConfiguration(MagicTaskConfig config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TaskInfoMagicResourceStorage taskInfoMagicResourceStorage() {
|
||||
return new TaskInfoMagicResourceStorage();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TaskMagicDynamicRegistry taskMagicDynamicRegistry(TaskInfoMagicResourceStorage taskInfoMagicResourceStorage) {
|
||||
MagicTaskConfig.Shutdown shutdown = config.getShutdown();
|
||||
ThreadPoolTaskScheduler poolTaskScheduler = null;
|
||||
if(config.isEnable()){
|
||||
poolTaskScheduler = new ThreadPoolTaskScheduler();
|
||||
poolTaskScheduler.setPoolSize(config.getPool().getSize());
|
||||
poolTaskScheduler.setWaitForTasksToCompleteOnShutdown(shutdown.isAwaitTermination());
|
||||
if(shutdown.getAwaitTerminationPeriod() != null){
|
||||
poolTaskScheduler.setAwaitTerminationSeconds((int) shutdown.getAwaitTerminationPeriod().getSeconds());
|
||||
}
|
||||
poolTaskScheduler.setThreadNamePrefix(config.getThreadNamePrefix());
|
||||
poolTaskScheduler.initialize();
|
||||
}
|
||||
return new TaskMagicDynamicRegistry(taskInfoMagicResourceStorage, poolTaskScheduler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plugin plugin() {
|
||||
return new Plugin("定时任务", "MagicTask", "magic-task.1.0.0.iife.js");
|
||||
}
|
||||
|
||||
@Override
|
||||
public MagicControllerRegister controllerRegister() {
|
||||
return (mapping, configuration) -> mapping.registerController(new MagicTaskController(configuration));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
package org.ssssssss.magicapi.task.starter;
|
||||
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@ConfigurationProperties("magic-api.task")
|
||||
public class MagicTaskConfig {
|
||||
|
||||
/**
|
||||
* 是否启用定时任务
|
||||
*/
|
||||
private boolean enable = true;
|
||||
|
||||
/**
|
||||
* 线程池相关配置
|
||||
*/
|
||||
private final Pool pool = new Pool();
|
||||
|
||||
/**
|
||||
* 关闭时相关配置
|
||||
*/
|
||||
private final Shutdown shutdown = new Shutdown();
|
||||
|
||||
/**
|
||||
* 线程池前缀
|
||||
*/
|
||||
private String threadNamePrefix = "magic-task-";
|
||||
|
||||
public Pool getPool() {
|
||||
return this.pool;
|
||||
}
|
||||
|
||||
public Shutdown getShutdown() {
|
||||
return this.shutdown;
|
||||
}
|
||||
|
||||
public String getThreadNamePrefix() {
|
||||
return this.threadNamePrefix;
|
||||
}
|
||||
|
||||
public void setThreadNamePrefix(String threadNamePrefix) {
|
||||
this.threadNamePrefix = threadNamePrefix;
|
||||
}
|
||||
|
||||
public boolean isEnable() {
|
||||
return enable;
|
||||
}
|
||||
|
||||
public void setEnable(boolean enable) {
|
||||
this.enable = enable;
|
||||
}
|
||||
|
||||
public static class Pool {
|
||||
|
||||
/**
|
||||
* 线程池大小
|
||||
*/
|
||||
private int size = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
public int getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
public void setSize(int size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Shutdown {
|
||||
|
||||
/**
|
||||
* 关闭时是否等待任务执行完毕,默认为false
|
||||
*/
|
||||
private boolean awaitTermination;
|
||||
|
||||
/**
|
||||
* 关闭时最多等待任务执行完毕的时间
|
||||
*/
|
||||
private Duration awaitTerminationPeriod;
|
||||
|
||||
public boolean isAwaitTermination() {
|
||||
return this.awaitTermination;
|
||||
}
|
||||
|
||||
public void setAwaitTermination(boolean awaitTermination) {
|
||||
this.awaitTermination = awaitTermination;
|
||||
}
|
||||
|
||||
public Duration getAwaitTerminationPeriod() {
|
||||
return this.awaitTerminationPeriod;
|
||||
}
|
||||
|
||||
public void setAwaitTerminationPeriod(Duration awaitTerminationPeriod) {
|
||||
this.awaitTerminationPeriod = awaitTerminationPeriod;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
package org.ssssssss.magicapi.task.web;
|
||||
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.ssssssss.magicapi.core.config.MagicConfiguration;
|
||||
import org.ssssssss.magicapi.core.config.WebSocketSessionManager;
|
||||
import org.ssssssss.magicapi.core.logging.MagicLoggerContext;
|
||||
import org.ssssssss.magicapi.core.model.DebugRequest;
|
||||
import org.ssssssss.magicapi.core.model.JsonBean;
|
||||
import org.ssssssss.magicapi.core.model.MagicEntity;
|
||||
import org.ssssssss.magicapi.core.web.MagicController;
|
||||
import org.ssssssss.magicapi.core.web.MagicExceptionHandler;
|
||||
import org.ssssssss.magicapi.utils.ScriptManager;
|
||||
import org.ssssssss.script.MagicScriptDebugContext;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
public class MagicTaskController extends MagicController implements MagicExceptionHandler {
|
||||
|
||||
public MagicTaskController(MagicConfiguration configuration) {
|
||||
super(configuration);
|
||||
}
|
||||
|
||||
@PostMapping("/task/execute")
|
||||
@ResponseBody
|
||||
public JsonBean<Object> execute(String id, HttpServletRequest request){
|
||||
MagicEntity entity = MagicConfiguration.getMagicResourceService().file(id);
|
||||
notNull(entity, FILE_NOT_FOUND);
|
||||
String script = entity.getScript();
|
||||
DebugRequest debugRequest = DebugRequest.create(request);
|
||||
MagicLoggerContext.SESSION.set(debugRequest.getRequestedClientId());
|
||||
String sessionAndScriptId = debugRequest.getRequestedClientId() + debugRequest.getRequestedScriptId();
|
||||
try {
|
||||
MagicScriptDebugContext magicScriptContext = debugRequest.createMagicScriptContext(configuration.getDebugTimeout());
|
||||
WebSocketSessionManager.addMagicScriptContext(sessionAndScriptId, magicScriptContext);
|
||||
magicScriptContext.setScriptName(MagicConfiguration.getMagicResourceService().getScriptName(entity));
|
||||
return new JsonBean<>(ScriptManager.executeScript(script, magicScriptContext));
|
||||
} finally {
|
||||
WebSocketSessionManager.removeMagicScriptContext(sessionAndScriptId);
|
||||
MagicLoggerContext.SESSION.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.ssssssss.magicapi.task.starter.MagicAPITaskConfiguration
|
||||
41
magic-api-plugins/pom.xml
Normal file
41
magic-api-plugins/pom.xml
Normal file
@ -0,0 +1,41 @@
|
||||
<?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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.ssssssss</groupId>
|
||||
<artifactId>magic-api-parent</artifactId>
|
||||
<version>2.0.0-beta.1</version>
|
||||
</parent>
|
||||
<artifactId>magic-api-plugins</artifactId>
|
||||
<version>2.0.0-beta.1</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>magic-api-plugins</name>
|
||||
<description>auto generate http api</description>
|
||||
<modules>
|
||||
<module>magic-api-plugin-task</module>
|
||||
<module>magic-api-plugin-swagger</module>
|
||||
<module>magic-api-plugin-redis</module>
|
||||
<module>magic-api-plugin-mongo</module>
|
||||
<module>magic-api-plugin-elasticsearch</module>
|
||||
<module>magic-api-plugin-cluster</module>
|
||||
</modules>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ssssssss</groupId>
|
||||
<artifactId>magic-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ssssssss</groupId>
|
||||
<artifactId>magic-script</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.ssssssss</groupId>
|
||||
<artifactId>magic-api-parent</artifactId>
|
||||
<version>1.7.5</version>
|
||||
<version>2.0.0-beta.1</version>
|
||||
</parent>
|
||||
<artifactId>magic-api-spring-boot-starter</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
@ -18,16 +18,6 @@
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-mongodb</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
@ -55,11 +45,6 @@
|
||||
<artifactId>fastjson</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
|
||||
@ -7,6 +7,7 @@ import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.ssssssss.magicapi.core.config.MagicAPIProperties;
|
||||
import org.ssssssss.magicapi.utils.PathUtils;
|
||||
|
||||
import java.net.InetAddress;
|
||||
@ -25,7 +26,7 @@ import java.util.Objects;
|
||||
@Order
|
||||
public class ApplicationUriPrinter implements CommandLineRunner {
|
||||
|
||||
@Value("${server.port:9999}")
|
||||
@Value("${server.port:8080}")
|
||||
private int port;
|
||||
|
||||
@Value("${server.servlet.context-path:}")
|
||||
|
||||
@ -1,51 +0,0 @@
|
||||
package org.ssssssss.magicapi.spring.boot.starter;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 集群配置
|
||||
*
|
||||
* @author mxd
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class ClusterConfig {
|
||||
|
||||
/**
|
||||
* 是否启用,默认不启用
|
||||
*/
|
||||
private boolean enable = false;
|
||||
|
||||
/**
|
||||
* 实例ID,集群环境下,要保证每台机器不同。默认启动后随机生成uuid
|
||||
*/
|
||||
private String instanceId = UUID.randomUUID().toString();
|
||||
|
||||
/**
|
||||
* redis 通道
|
||||
*/
|
||||
private String channel = "magic-api:notify:channel";
|
||||
|
||||
public String getInstanceId() {
|
||||
return instanceId;
|
||||
}
|
||||
|
||||
public void setInstanceId(String instanceId) {
|
||||
this.instanceId = instanceId;
|
||||
}
|
||||
|
||||
public boolean isEnable() {
|
||||
return enable;
|
||||
}
|
||||
|
||||
public void setEnable(boolean enable) {
|
||||
this.enable = enable;
|
||||
}
|
||||
|
||||
public String getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public void setChannel(String channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
@ -17,46 +17,52 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.multipart.MultipartResolver;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocket;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistration;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
|
||||
import org.ssssssss.magicapi.adapter.ColumnMapperAdapter;
|
||||
import org.ssssssss.magicapi.adapter.DialectAdapter;
|
||||
import org.ssssssss.magicapi.adapter.Resource;
|
||||
import org.ssssssss.magicapi.adapter.ResourceAdapter;
|
||||
import org.ssssssss.magicapi.adapter.resource.DatabaseResource;
|
||||
import org.ssssssss.magicapi.cache.DefaultSqlCache;
|
||||
import org.ssssssss.magicapi.cache.SqlCache;
|
||||
import org.ssssssss.magicapi.config.*;
|
||||
import org.ssssssss.magicapi.controller.*;
|
||||
import org.ssssssss.magicapi.dialect.Dialect;
|
||||
import org.ssssssss.magicapi.exception.MagicAPIException;
|
||||
import org.ssssssss.magicapi.interceptor.*;
|
||||
import org.ssssssss.magicapi.logging.LoggerManager;
|
||||
import org.ssssssss.magicapi.model.Constants;
|
||||
import org.ssssssss.magicapi.model.DataType;
|
||||
import org.ssssssss.magicapi.model.Options;
|
||||
import org.ssssssss.magicapi.modules.*;
|
||||
import org.ssssssss.magicapi.provider.*;
|
||||
import org.ssssssss.magicapi.provider.impl.*;
|
||||
import org.ssssssss.magicapi.utils.ClassScanner;
|
||||
import org.ssssssss.magicapi.backup.service.MagicBackupService;
|
||||
import org.ssssssss.magicapi.backup.service.MagicDatabaseBackupService;
|
||||
import org.ssssssss.magicapi.backup.web.MagicBackupController;
|
||||
import org.ssssssss.magicapi.core.annotation.MagicModule;
|
||||
import org.ssssssss.magicapi.core.config.*;
|
||||
import org.ssssssss.magicapi.core.exception.MagicAPIException;
|
||||
import org.ssssssss.magicapi.core.handler.MagicCoordinationHandler;
|
||||
import org.ssssssss.magicapi.core.handler.MagicDebugHandler;
|
||||
import org.ssssssss.magicapi.core.handler.MagicWebSocketDispatcher;
|
||||
import org.ssssssss.magicapi.core.handler.MagicWorkbenchHandler;
|
||||
import org.ssssssss.magicapi.core.interceptor.*;
|
||||
import org.ssssssss.magicapi.core.logging.LoggerManager;
|
||||
import org.ssssssss.magicapi.core.model.DataType;
|
||||
import org.ssssssss.magicapi.core.model.MagicEntity;
|
||||
import org.ssssssss.magicapi.core.model.Plugin;
|
||||
import org.ssssssss.magicapi.core.resource.DatabaseResource;
|
||||
import org.ssssssss.magicapi.core.resource.ResourceAdapter;
|
||||
import org.ssssssss.magicapi.core.service.*;
|
||||
import org.ssssssss.magicapi.core.service.impl.DefaultMagicAPIService;
|
||||
import org.ssssssss.magicapi.core.service.impl.DefaultMagicResourceService;
|
||||
import org.ssssssss.magicapi.core.service.impl.RequestMagicDynamicRegistry;
|
||||
import org.ssssssss.magicapi.core.web.MagicResourceController;
|
||||
import org.ssssssss.magicapi.core.web.MagicWorkbenchController;
|
||||
import org.ssssssss.magicapi.core.web.RequestHandler;
|
||||
import org.ssssssss.magicapi.datasource.model.MagicDynamicDataSource;
|
||||
import org.ssssssss.magicapi.datasource.service.DataSourceEncryptProvider;
|
||||
import org.ssssssss.magicapi.datasource.web.MagicDataSourceController;
|
||||
import org.ssssssss.magicapi.function.service.FunctionMagicDynamicRegistry;
|
||||
import org.ssssssss.magicapi.jsr223.LanguageProvider;
|
||||
import org.ssssssss.magicapi.modules.servlet.RequestModule;
|
||||
import org.ssssssss.magicapi.modules.servlet.ResponseModule;
|
||||
import org.ssssssss.magicapi.modules.spring.EnvModule;
|
||||
import org.ssssssss.magicapi.utils.Mapping;
|
||||
import org.ssssssss.magicapi.utils.PathUtils;
|
||||
import org.ssssssss.script.MagicResourceLoader;
|
||||
import org.ssssssss.script.MagicScript;
|
||||
import org.ssssssss.script.MagicScriptEngine;
|
||||
@ -66,16 +72,12 @@ import org.ssssssss.script.functions.ExtensionMethod;
|
||||
import org.ssssssss.script.parsing.ast.statement.AsyncCall;
|
||||
import org.ssssssss.script.reflection.JavaReflection;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.sql.DataSource;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* magic-api自动配置类
|
||||
@ -85,8 +87,9 @@ import java.util.function.BiFunction;
|
||||
@Configuration
|
||||
@ConditionalOnClass({RequestMappingHandlerMapping.class})
|
||||
@EnableConfigurationProperties(MagicAPIProperties.class)
|
||||
@Import({MagicRedisAutoConfiguration.class, MagicMongoAutoConfiguration.class, MagicSwaggerConfiguration.class, MagicJsonAutoConfiguration.class, ApplicationUriPrinter.class})
|
||||
@Import({MagicJsonAutoConfiguration.class, ApplicationUriPrinter.class, MagicModuleConfiguration.class, MagicDynamicRegistryConfiguration.class})
|
||||
@EnableWebSocket
|
||||
@AutoConfigureAfter(MagicPluginConfiguration.class)
|
||||
public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketConfigurer {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MagicAPIAutoConfiguration.class);
|
||||
@ -96,15 +99,6 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
|
||||
*/
|
||||
private final ObjectProvider<List<RequestInterceptor>> requestInterceptorsProvider;
|
||||
|
||||
/**
|
||||
* SQL拦截器
|
||||
*/
|
||||
private final ObjectProvider<List<SQLInterceptor>> sqlInterceptorsProvider;
|
||||
|
||||
/**
|
||||
* 单表API拦截器
|
||||
*/
|
||||
private final ObjectProvider<List<NamedTableInterceptor>> namedTableInterceptorsProvider;
|
||||
|
||||
/**
|
||||
* 自定义的类型扩展
|
||||
@ -116,16 +110,6 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
|
||||
*/
|
||||
private final ObjectProvider<List<HttpMessageConverter<?>>> httpMessageConvertersProvider;
|
||||
|
||||
/**
|
||||
* 自定义的方言
|
||||
*/
|
||||
private final ObjectProvider<List<Dialect>> dialectsProvider;
|
||||
|
||||
/**
|
||||
* 自定义的列名转换
|
||||
*/
|
||||
private final ObjectProvider<List<ColumnMapperProvider>> columnMapperProvidersProvider;
|
||||
|
||||
|
||||
private final ObjectProvider<AuthorizationInterceptor> authorizationInterceptorProvider;
|
||||
|
||||
@ -134,11 +118,15 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
|
||||
*/
|
||||
private final ObjectProvider<List<MagicFunction>> magicFunctionsProvider;
|
||||
|
||||
private final ObjectProvider<List<MagicPluginConfiguration>> magicPluginsProvider;
|
||||
|
||||
private final ObjectProvider<MagicNotifyService> magicNotifyServiceProvider;
|
||||
|
||||
private final ObjectProvider<DataSourceEncryptProvider> dataSourceEncryptProvider;
|
||||
private final ObjectProvider<List<MagicDynamicRegistry<? extends MagicEntity>>> magicDynamicRegistriesProvider;
|
||||
|
||||
private final Environment environment;
|
||||
private final ObjectProvider<List<MagicResourceStorage<? extends MagicEntity>>> magicResourceStoragesProvider;
|
||||
|
||||
private final ObjectProvider<DataSourceEncryptProvider> dataSourceEncryptProvider;
|
||||
|
||||
private final MagicCorsFilter magicCorsFilter = new MagicCorsFilter();
|
||||
|
||||
@ -156,94 +144,40 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
|
||||
@Lazy
|
||||
private RequestMappingHandlerMapping requestMappingHandlerMapping;
|
||||
|
||||
@Autowired(required = false)
|
||||
private MultipartResolver multipartResolver;
|
||||
|
||||
private String allClassTxt;
|
||||
private DefaultAuthorizationInterceptor defaultAuthorizationInterceptor;
|
||||
|
||||
public MagicAPIAutoConfiguration(MagicAPIProperties properties,
|
||||
ObjectProvider<List<Dialect>> dialectsProvider,
|
||||
ObjectProvider<List<RequestInterceptor>> requestInterceptorsProvider,
|
||||
ObjectProvider<List<SQLInterceptor>> sqlInterceptorsProvider,
|
||||
ObjectProvider<List<ExtensionMethod>> extensionMethodsProvider,
|
||||
ObjectProvider<List<HttpMessageConverter<?>>> httpMessageConvertersProvider,
|
||||
ObjectProvider<List<ColumnMapperProvider>> columnMapperProvidersProvider,
|
||||
ObjectProvider<List<MagicFunction>> magicFunctionsProvider,
|
||||
ObjectProvider<List<MagicPluginConfiguration>> magicPluginsProvider,
|
||||
ObjectProvider<MagicNotifyService> magicNotifyServiceProvider,
|
||||
ObjectProvider<AuthorizationInterceptor> authorizationInterceptorProvider,
|
||||
ObjectProvider<List<NamedTableInterceptor>> namedTableInterceptorsProvider,
|
||||
ObjectProvider<DataSourceEncryptProvider> dataSourceEncryptProvider,
|
||||
Environment environment,
|
||||
ObjectProvider<List<MagicDynamicRegistry<? extends MagicEntity>>> magicDynamicRegistriesProvider,
|
||||
ObjectProvider<List<MagicResourceStorage<? extends MagicEntity>>> magicResourceStoragesProvider,
|
||||
ApplicationContext applicationContext
|
||||
) {
|
||||
this.properties = properties;
|
||||
this.dialectsProvider = dialectsProvider;
|
||||
this.requestInterceptorsProvider = requestInterceptorsProvider;
|
||||
this.sqlInterceptorsProvider = sqlInterceptorsProvider;
|
||||
this.extensionMethodsProvider = extensionMethodsProvider;
|
||||
this.httpMessageConvertersProvider = httpMessageConvertersProvider;
|
||||
this.columnMapperProvidersProvider = columnMapperProvidersProvider;
|
||||
this.magicFunctionsProvider = magicFunctionsProvider;
|
||||
this.magicPluginsProvider = magicPluginsProvider;
|
||||
this.magicNotifyServiceProvider = magicNotifyServiceProvider;
|
||||
this.authorizationInterceptorProvider = authorizationInterceptorProvider;
|
||||
this.namedTableInterceptorsProvider = namedTableInterceptorsProvider;
|
||||
this.dataSourceEncryptProvider = dataSourceEncryptProvider;
|
||||
this.environment = environment;
|
||||
this.magicDynamicRegistriesProvider = magicDynamicRegistriesProvider;
|
||||
this.magicResourceStoragesProvider = magicResourceStoragesProvider;
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
private String redirectIndex(HttpServletRequest request) {
|
||||
if (request.getRequestURI().endsWith("/")) {
|
||||
return "redirect:./index.html";
|
||||
}
|
||||
return "redirect:" + properties.getWeb() + "/index.html";
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
private MagicAPIProperties readConfig() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
private String readClass() {
|
||||
if (allClassTxt == null) {
|
||||
try {
|
||||
allClassTxt = ClassScanner.compress(ClassScanner.scan());
|
||||
} catch (Throwable t) {
|
||||
logger.warn("扫描Class失败", t);
|
||||
allClassTxt = "";
|
||||
}
|
||||
}
|
||||
return allClassTxt;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(HttpModule.class)
|
||||
public HttpModule magicHttpModule() {
|
||||
return new HttpModule(createRestTemplate());
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入动态数据源
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(MagicDynamicDataSource.class)
|
||||
public MagicDynamicDataSource magicDynamicDataSource(@Autowired(required = false) DataSource dataSource) {
|
||||
MagicDynamicDataSource dynamicDataSource = new MagicDynamicDataSource();
|
||||
if (dataSource != null) {
|
||||
dynamicDataSource.put(dataSource);
|
||||
} else {
|
||||
logger.warn("当前数据源未配置");
|
||||
}
|
||||
return dynamicDataSource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(Resource.class)
|
||||
@ConditionalOnMissingBean(org.ssssssss.magicapi.core.resource.Resource.class)
|
||||
@ConditionalOnProperty(prefix = "magic-api", name = "resource.type", havingValue = "database")
|
||||
public Resource magicDatabaseResource(MagicDynamicDataSource magicDynamicDataSource) {
|
||||
ResourceConfig resourceConfig = properties.getResource();
|
||||
public org.ssssssss.magicapi.core.resource.Resource magicDatabaseResource(MagicDynamicDataSource magicDynamicDataSource) {
|
||||
Resource resourceConfig = properties.getResource();
|
||||
if (magicDynamicDataSource.isEmpty()) {
|
||||
throw new MagicAPIException("当前未配置数据源,如已配置,请引入 spring-boot-starter-jdbc 后在试!");
|
||||
}
|
||||
@ -252,18 +186,18 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(Resource.class)
|
||||
@ConditionalOnMissingBean(org.ssssssss.magicapi.core.resource.Resource.class)
|
||||
@ConditionalOnProperty(prefix = "magic-api", name = "resource.type", havingValue = "file", matchIfMissing = true)
|
||||
public Resource magicResource() throws IOException {
|
||||
ResourceConfig resourceConfig = properties.getResource();
|
||||
public org.ssssssss.magicapi.core.resource.Resource magicResource() throws IOException {
|
||||
Resource resourceConfig = properties.getResource();
|
||||
return ResourceAdapter.getResource(resourceConfig.getLocation(), resourceConfig.isReadonly());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(MagicBackupService.class)
|
||||
@ConditionalOnProperty(prefix = "magic-api", name = "backup-config.resource-type", havingValue = "database")
|
||||
@ConditionalOnProperty(prefix = "magic-api", name = "backup.enable", havingValue = "true")
|
||||
public MagicBackupService magicDatabaseBackupService(MagicDynamicDataSource magicDynamicDataSource) {
|
||||
BackupConfig backupConfig = properties.getBackupConfig();
|
||||
Backup backupConfig = properties.getBackup();
|
||||
MagicDynamicDataSource.DataSourceNode dataSourceNode = magicDynamicDataSource.getDataSource(backupConfig.getDatasource());
|
||||
return new MagicDatabaseBackupService(new JdbcTemplate(dataSourceNode.getDataSource()), backupConfig.getTableName());
|
||||
}
|
||||
@ -277,16 +211,6 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
|
||||
LoggerManager.createMagicAppender();
|
||||
// 配置静态资源路径
|
||||
registry.addResourceHandler(web + "/**").addResourceLocations("classpath:/magic-editor/");
|
||||
try {
|
||||
Mapping mapping = Mapping.create(requestMappingHandlerMapping);
|
||||
// 默认首页设置
|
||||
mapping.register(mapping.paths(web).build(), this, MagicAPIAutoConfiguration.class.getDeclaredMethod("redirectIndex", HttpServletRequest.class))
|
||||
// 读取配置
|
||||
.register(mapping.paths(web + "/config.json").build(), this, MagicAPIAutoConfiguration.class.getDeclaredMethod("readConfig"))
|
||||
// 读取配置
|
||||
.register(mapping.paths(web + "/classes.txt").produces("text/plain").build(), this, MagicAPIAutoConfiguration.class.getDeclaredMethod("readClass"));
|
||||
} catch (NoSuchMethodException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,148 +234,33 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(PageProvider.class)
|
||||
public PageProvider pageProvider() {
|
||||
PageConfig pageConfig = properties.getPageConfig();
|
||||
logger.info("未找到分页实现,采用默认分页实现,分页配置:(页码={},页大小={},默认首页={},默认页大小={})", pageConfig.getPage(), pageConfig.getSize(), pageConfig.getDefaultPage(), pageConfig.getDefaultSize());
|
||||
return new DefaultPageProvider(pageConfig.getPage(), pageConfig.getSize(), pageConfig.getDefaultPage(), pageConfig.getDefaultSize());
|
||||
@ConditionalOnMissingBean
|
||||
public MagicResourceService magicResourceService(org.ssssssss.magicapi.core.resource.Resource workspace) {
|
||||
return new DefaultMagicResourceService(workspace, magicResourceStoragesProvider.getObject(), applicationContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入结果构建方法
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ResultProvider.class)
|
||||
public ResultProvider resultProvider() {
|
||||
return new DefaultResultProvider(properties.getResponse());
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入SQL缓存实现
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(SqlCache.class)
|
||||
public SqlCache sqlCache() {
|
||||
CacheConfig cacheConfig = properties.getCacheConfig();
|
||||
logger.info("未找到SQL缓存实现,采用默认缓存实现(LRU+TTL),缓存配置:(容量={},TTL={})", cacheConfig.getCapacity(), cacheConfig.getTtl());
|
||||
return new DefaultSqlCache(cacheConfig.getCapacity(), cacheConfig.getTtl());
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入接口映射
|
||||
*/
|
||||
@Bean
|
||||
public MappingHandlerMapping mappingHandlerMapping() throws NoSuchMethodException {
|
||||
String prefix = StringUtils.isNotBlank(properties.getPrefix()) ? PathUtils.replaceSlash("/" + properties.getPrefix() + "/") : null;
|
||||
return new MappingHandlerMapping(prefix, properties.isAllowOverride());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(FunctionServiceProvider.class)
|
||||
public FunctionServiceProvider functionServiceProvider(GroupServiceProvider groupServiceProvider, Resource magicResource) {
|
||||
return new DefaultFunctionServiceProvider(groupServiceProvider, magicResource);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入分组存储service
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(GroupServiceProvider.class)
|
||||
public GroupServiceProvider groupServiceProvider(Resource magicResource) {
|
||||
return new DefaultGroupServiceProvider(magicResource);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入接口存储service
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ApiServiceProvider.class)
|
||||
public ApiServiceProvider apiServiceProvider(GroupServiceProvider groupServiceProvider, Resource magicResource) {
|
||||
return new DefaultApiServiceProvider(groupServiceProvider, magicResource);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(MagicNotifyService.class)
|
||||
public MagicNotifyService magicNotifyService() {
|
||||
logger.info("未配置集群通知服务,本实例不会推送通知,集群环境下可能会有问题,如需开启,请配置magic-api.cluster-config.enable=true,若开启后本提示还在,请检查 spring-boot-starter-data-redis 是否引入");
|
||||
logger.info("未配置集群通知服务,本实例不会推送通知,集群环境下可能会有问题,如需开启,请引用magic-api-plugin-cluster插件");
|
||||
return magicNotify -> {
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(MagicBackupService.class)
|
||||
@ConditionalOnProperty(prefix = "magic-api", name = "backup-config.resource-type", havingValue = "file", matchIfMissing = true)
|
||||
public MagicBackupService magicFileBackupService() {
|
||||
return new MagicFileBackupService(new File(properties.getBackupConfig().getLocation()));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MagicFunctionManager magicFunctionManager(GroupServiceProvider groupServiceProvider, FunctionServiceProvider functionServiceProvider) {
|
||||
return new MagicFunctionManager(groupServiceProvider, functionServiceProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入API调用Service
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public MagicAPIService magicAPIService(MappingHandlerMapping mappingHandlerMapping,
|
||||
ApiServiceProvider apiServiceProvider,
|
||||
FunctionServiceProvider functionServiceProvider,
|
||||
GroupServiceProvider groupServiceProvider,
|
||||
ResultProvider resultProvider,
|
||||
MagicDynamicDataSource magicDynamicDataSource,
|
||||
MagicFunctionManager magicFunctionManager,
|
||||
Resource workspace,
|
||||
MagicBackupService magicBackupService) {
|
||||
return new DefaultMagicAPIService(mappingHandlerMapping, apiServiceProvider, functionServiceProvider, groupServiceProvider, resultProvider, magicDynamicDataSource, magicFunctionManager, magicNotifyServiceProvider.getObject(), properties.getClusterConfig().getInstanceId(), workspace, magicBackupService, dataSourceEncryptProvider.getIfAvailable() , properties.isThrowException());
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入数据库查询模块
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnBean({MagicDynamicDataSource.class})
|
||||
public SQLModule magicSqlModule(MagicDynamicDataSource dynamicDataSource,
|
||||
ResultProvider resultProvider,
|
||||
PageProvider pageProvider,
|
||||
SqlCache sqlCache) {
|
||||
SQLModule sqlModule = new SQLModule(dynamicDataSource);
|
||||
if (!dynamicDataSource.isEmpty()) {
|
||||
sqlModule.setDataSourceNode(dynamicDataSource.getDataSource());
|
||||
}
|
||||
sqlModule.setResultProvider(resultProvider);
|
||||
sqlModule.setPageProvider(pageProvider);
|
||||
List<SQLInterceptor> sqlInterceptors = sqlInterceptorsProvider.getIfAvailable(ArrayList::new);
|
||||
if (properties.isShowSql()) {
|
||||
sqlInterceptors.add(new DefaultSqlInterceptor());
|
||||
}
|
||||
sqlModule.setSqlInterceptors(sqlInterceptors);
|
||||
sqlModule.setNamedTableInterceptors(namedTableInterceptorsProvider.getIfAvailable(Collections::emptyList));
|
||||
ColumnMapperAdapter columnMapperAdapter = new ColumnMapperAdapter();
|
||||
this.columnMapperProvidersProvider.getIfAvailable(Collections::emptyList).stream().filter(mapperProvider -> !"default".equals(mapperProvider.name())).forEach(columnMapperAdapter::add);
|
||||
columnMapperAdapter.setDefault(properties.getSqlColumnCase());
|
||||
sqlModule.setColumnMapperProvider(columnMapperAdapter);
|
||||
sqlModule.setColumnMapRowMapper(columnMapperAdapter.getDefaultColumnMapRowMapper());
|
||||
sqlModule.setRowMapColumnMapper(columnMapperAdapter.getDefaultRowMapColumnMapper());
|
||||
sqlModule.setSqlCache(sqlCache);
|
||||
DialectAdapter dialectAdapter = new DialectAdapter();
|
||||
dialectsProvider.getIfAvailable(Collections::emptyList).forEach(dialectAdapter::add);
|
||||
sqlModule.setDialectAdapter(dialectAdapter);
|
||||
sqlModule.setLogicDeleteColumn(properties.getCrudConfig().getLogicDeleteColumn());
|
||||
sqlModule.setLogicDeleteValue(properties.getCrudConfig().getLogicDeleteValue());
|
||||
return sqlModule;
|
||||
public MagicAPIService magicAPIService(ResultProvider resultProvider, MagicResourceService magicResourceService, RequestMagicDynamicRegistry requestMagicDynamicRegistry, FunctionMagicDynamicRegistry functionMagicDynamicRegistry) {
|
||||
return new DefaultMagicAPIService(resultProvider, properties.getInstanceId(), magicResourceService, requestMagicDynamicRegistry, functionMagicDynamicRegistry, properties.isThrowException(), applicationContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册模块、类型扩展
|
||||
*/
|
||||
private void setupMagicModules(MagicDynamicDataSource dynamicDataSource,
|
||||
SQLModule sqlModule,
|
||||
ResultProvider resultProvider,
|
||||
List<MagicModule> magicModules,
|
||||
List<ExtensionMethod> extensionMethods,
|
||||
List<LanguageProvider> languageProviders) {
|
||||
private void setupMagicModules(List<ExtensionMethod> extensionMethods, List<LanguageProvider> languageProviders) {
|
||||
// 设置脚本import时 class加载策略
|
||||
MagicResourceLoader.setClassLoader((className) -> {
|
||||
try {
|
||||
@ -480,27 +289,13 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
|
||||
}).orElse(null)
|
||||
);
|
||||
logger.info("注册模块:{} -> {}", "log", Logger.class);
|
||||
MagicResourceLoader.addModule("log", new DynamicModuleImport(Logger.class, context -> LoggerFactory.getLogger(Objects.toString(context.getScriptName(),"Unknown"))));
|
||||
MagicResourceLoader.addModule("log", new DynamicModuleImport(Logger.class, context -> LoggerFactory.getLogger(Objects.toString(context.getScriptName(), "Unknown"))));
|
||||
List<String> importModules = properties.getAutoImportModuleList();
|
||||
logger.info("注册模块:{} -> {}", "env", EnvModule.class);
|
||||
MagicResourceLoader.addModule("env", new EnvModule(environment));
|
||||
logger.info("注册模块:{} -> {}", "request", RequestModule.class);
|
||||
MagicResourceLoader.addModule("request", new RequestModule(multipartResolver));
|
||||
logger.info("注册模块:{} -> {}", "response", ResponseModule.class);
|
||||
MagicResourceLoader.addModule("response", new ResponseModule(resultProvider));
|
||||
logger.info("注册模块:{} -> {}", "assert", AssertModule.class);
|
||||
MagicResourceLoader.addModule("assert", new AssertModule());
|
||||
magicModules.forEach(module -> {
|
||||
logger.info("注册模块:{} -> {}", module.getModuleName(), module.getClass());
|
||||
MagicResourceLoader.addModule(module.getModuleName(), module);
|
||||
applicationContext.getBeansWithAnnotation(MagicModule.class).values().forEach(module -> {
|
||||
String moduleName = module.getClass().getAnnotation(MagicModule.class).value();
|
||||
logger.info("注册模块:{} -> {}", moduleName, module.getClass());
|
||||
MagicResourceLoader.addModule(moduleName, module);
|
||||
});
|
||||
MagicResourceLoader.addModule(sqlModule.getModuleName(), new DynamicModuleImport(SQLModule.class, context -> {
|
||||
String dataSourceKey = context.getString(Options.DEFAULT_DATA_SOURCE.getValue());
|
||||
if(StringUtils.isEmpty(dataSourceKey)) return sqlModule;
|
||||
SQLModule newSqlModule = sqlModule.cloneSQLModule();
|
||||
newSqlModule.setDataSourceNode(dynamicDataSource.getDataSource(dataSourceKey));
|
||||
return newSqlModule;
|
||||
}));
|
||||
MagicResourceLoader.getModuleNames().stream().filter(importModules::contains).forEach(moduleName -> {
|
||||
logger.info("自动导入模块:{}", moduleName);
|
||||
MagicScriptEngine.addDefaultImport(moduleName, MagicResourceLoader.loadModule(moduleName));
|
||||
@ -516,47 +311,34 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JSR223LanguageProvider jsr223LanguageProvider() {
|
||||
return new JSR223LanguageProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MagicConfiguration magicConfiguration(MagicDynamicDataSource dynamicDataSource,
|
||||
SQLModule sqlModule,
|
||||
List<MagicModule> magicModules,
|
||||
List<LanguageProvider> languageProviders,
|
||||
Resource magicResource,
|
||||
public MagicConfiguration magicConfiguration(List<LanguageProvider> languageProviders,
|
||||
org.ssssssss.magicapi.core.resource.Resource magicResource,
|
||||
ResultProvider resultProvider,
|
||||
MagicResourceService magicResourceService,
|
||||
MagicAPIService magicAPIService,
|
||||
ApiServiceProvider apiServiceProvider,
|
||||
GroupServiceProvider groupServiceProvider,
|
||||
MappingHandlerMapping mappingHandlerMapping,
|
||||
FunctionServiceProvider functionServiceProvider,
|
||||
MagicNotifyService magicNotifyService,
|
||||
MagicFunctionManager magicFunctionManager,
|
||||
MagicBackupService magicBackupService) throws NoSuchMethodException {
|
||||
RequestMagicDynamicRegistry requestMagicDynamicRegistry,
|
||||
@Autowired(required = false) MagicBackupService magicBackupService) throws NoSuchMethodException {
|
||||
logger.info("magic-api工作目录:{}", magicResource);
|
||||
AsyncCall.setThreadPoolExecutorSize(properties.getThreadPoolExecutorSize());
|
||||
DataType.DATE_PATTERNS = properties.getDatePattern();
|
||||
MagicScript.setCompileCache(properties.getCompileCacheSize());
|
||||
// 设置响应结果的code值
|
||||
ResponseCodeConfig responseCodeConfig = properties.getResponseCodeConfig();
|
||||
ResponseCode responseCodeConfig = properties.getResponseCode();
|
||||
Constants.RESPONSE_CODE_SUCCESS = responseCodeConfig.getSuccess();
|
||||
Constants.RESPONSE_CODE_INVALID = responseCodeConfig.getInvalid();
|
||||
Constants.RESPONSE_CODE_EXCEPTION = responseCodeConfig.getException();
|
||||
// 设置模块和扩展方法
|
||||
setupMagicModules(dynamicDataSource, sqlModule, resultProvider, magicModules, extensionMethodsProvider.getIfAvailable(Collections::emptyList), languageProviders);
|
||||
setupMagicModules(extensionMethodsProvider.getIfAvailable(Collections::emptyList), languageProviders);
|
||||
MagicConfiguration configuration = new MagicConfiguration();
|
||||
configuration.setMagicAPIService(magicAPIService);
|
||||
configuration.setMagicNotifyService(magicNotifyService);
|
||||
configuration.setInstanceId(properties.getClusterConfig().getInstanceId());
|
||||
configuration.setApiServiceProvider(apiServiceProvider);
|
||||
configuration.setGroupServiceProvider(groupServiceProvider);
|
||||
configuration.setMappingHandlerMapping(mappingHandlerMapping);
|
||||
configuration.setFunctionServiceProvider(functionServiceProvider);
|
||||
configuration.setInstanceId(properties.getInstanceId());
|
||||
configuration.setMagicResourceService(magicResourceService);
|
||||
configuration.setMagicDynamicRegistries(magicDynamicRegistriesProvider.getObject());
|
||||
configuration.setMagicBackupService(magicBackupService);
|
||||
SecurityConfig securityConfig = properties.getSecurityConfig();
|
||||
configuration.setDebugTimeout(properties.getDebugConfig().getTimeout());
|
||||
Security securityConfig = properties.getSecurityConfig();
|
||||
configuration.setDebugTimeout(properties.getDebug().getTimeout());
|
||||
configuration.setHttpMessageConverters(httpMessageConvertersProvider.getIfAvailable(Collections::emptyList));
|
||||
configuration.setResultProvider(resultProvider);
|
||||
configuration.setThrowException(properties.isThrowException());
|
||||
@ -568,32 +350,25 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
|
||||
// 向页面传递配置信息时不传递用户名密码,增强安全性
|
||||
securityConfig.setUsername(null);
|
||||
securityConfig.setPassword(null);
|
||||
|
||||
requestMagicDynamicRegistry.setHandler(new RequestHandler(configuration, requestMagicDynamicRegistry));
|
||||
List<MagicPluginConfiguration> pluginConfigurations = magicPluginsProvider.getIfAvailable(Collections::emptyList);
|
||||
List<Plugin> plugins = pluginConfigurations.stream().map(MagicPluginConfiguration::plugin).collect(Collectors.toList());
|
||||
// 构建UI请求处理器
|
||||
String base = properties.getWeb();
|
||||
mappingHandlerMapping.setRequestMappingHandlerMapping(requestMappingHandlerMapping);
|
||||
MagicDataSourceController dataSourceController = new MagicDataSourceController(configuration);
|
||||
MagicWorkbenchController magicWorkbenchController = new MagicWorkbenchController(configuration, properties.getSecretKey());
|
||||
Mapping mapping = Mapping.create(requestMappingHandlerMapping, base, properties.getPrefix());
|
||||
MagicWorkbenchController magicWorkbenchController = new MagicWorkbenchController(configuration, properties, plugins);
|
||||
if (base != null) {
|
||||
configuration.setEnableWeb(true);
|
||||
List<MagicController> controllers = new ArrayList<>(Arrays.asList(
|
||||
new MagicAPIController(configuration),
|
||||
dataSourceController,
|
||||
magicWorkbenchController,
|
||||
new MagicGroupController(configuration),
|
||||
new MagicFunctionController(configuration)
|
||||
));
|
||||
controllers.forEach(item -> mappingHandlerMapping.registerController(item, base));
|
||||
mapping.registerController(magicWorkbenchController)
|
||||
.registerController(new MagicResourceController(configuration))
|
||||
.registerController(new MagicDataSourceController(configuration))
|
||||
.registerController(new MagicBackupController(configuration));
|
||||
pluginConfigurations.forEach(it -> it.controllerRegister().register(mapping, configuration));
|
||||
}
|
||||
// 注册接收推送的接口
|
||||
if (StringUtils.isNotBlank(properties.getSecretKey())) {
|
||||
Mapping mapping = Mapping.create(requestMappingHandlerMapping);
|
||||
RequestMappingInfo requestMappingInfo = mapping.paths(properties.getPushPath()).build();
|
||||
Method method = MagicWorkbenchController.class.getDeclaredMethod("receivePush", MultipartFile.class, String.class, Long.class, String.class);
|
||||
mapping.register(requestMappingInfo, magicWorkbenchController, method);
|
||||
mapping.register(mapping.paths(properties.getPushPath()).methods(RequestMethod.POST).build(), magicWorkbenchController, MagicWorkbenchController.class.getDeclaredMethod("receivePush", MultipartFile.class, String.class, Long.class, String.class));
|
||||
}
|
||||
// 注册数据源
|
||||
magicAPIService.registerAllDataSource();
|
||||
// 设置拦截器信息
|
||||
this.requestInterceptorsProvider.getIfAvailable(Collections::emptyList).forEach(interceptor -> {
|
||||
logger.info("注册请求拦截器:{}", interceptor.getClass());
|
||||
@ -601,21 +376,14 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
|
||||
});
|
||||
// 打印banner
|
||||
if (this.properties.isBanner()) {
|
||||
configuration.printBanner();
|
||||
configuration.printBanner(plugins.stream().map(Plugin::getName).collect(Collectors.toList()));
|
||||
}
|
||||
if (magicBackupService == null) {
|
||||
logger.error("当前备份设置未配置,强烈建议配置备份设置,以免代码丢失。");
|
||||
}
|
||||
configuration.setMagicFunctionManager(magicFunctionManager);
|
||||
// 注册函数加载器
|
||||
magicFunctionManager.registerFunctionLoader();
|
||||
// 注册所有函数
|
||||
magicFunctionManager.registerAllFunction();
|
||||
mappingHandlerMapping.setHandler(new RequestHandler(configuration));
|
||||
mappingHandlerMapping.setMagicApiService(apiServiceProvider);
|
||||
mappingHandlerMapping.setGroupServiceProvider(groupServiceProvider);
|
||||
// 注册所有映射
|
||||
mappingHandlerMapping.registerAllMapping();
|
||||
// 备份清理
|
||||
if (properties.getBackupConfig().getMaxHistory() > 0) {
|
||||
long interval = properties.getBackupConfig().getMaxHistory() * 86400000L;
|
||||
if (properties.getBackup().isEnable() && properties.getBackup().getMaxHistory() > 0 && magicBackupService != null) {
|
||||
long interval = properties.getBackup().getMaxHistory() * 86400000L;
|
||||
// 1小时执行1次
|
||||
new ScheduledThreadPoolExecutor(1, r -> new Thread(r, "magic-api-clean-task")).scheduleAtFixedRate(() -> {
|
||||
try {
|
||||
@ -635,26 +403,11 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
|
||||
if (defaultAuthorizationInterceptor != null) {
|
||||
return defaultAuthorizationInterceptor;
|
||||
}
|
||||
SecurityConfig securityConfig = properties.getSecurityConfig();
|
||||
Security securityConfig = properties.getSecurityConfig();
|
||||
defaultAuthorizationInterceptor = new DefaultAuthorizationInterceptor(securityConfig.getUsername(), securityConfig.getPassword());
|
||||
return defaultAuthorizationInterceptor;
|
||||
}
|
||||
|
||||
private RestTemplate createRestTemplate() {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
restTemplate.getMessageConverters().add(new StringHttpMessageConverter(StandardCharsets.UTF_8) {
|
||||
{
|
||||
setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return restTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
|
||||
String web = properties.getWeb();
|
||||
@ -662,8 +415,9 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
|
||||
WebSocketSessionManager.setMagicNotifyService(magicNotifyService);
|
||||
if (web != null && !registerWebsocket) {
|
||||
registerWebsocket = true;
|
||||
MagicWebSocketDispatcher dispatcher = new MagicWebSocketDispatcher(properties.getClusterConfig().getInstanceId(), magicNotifyService, Arrays.asList(
|
||||
MagicWebSocketDispatcher dispatcher = new MagicWebSocketDispatcher(properties.getInstanceId(), magicNotifyService, Arrays.asList(
|
||||
new MagicDebugHandler(),
|
||||
new MagicCoordinationHandler(),
|
||||
new MagicWorkbenchHandler(authorizationInterceptorProvider.getIfAvailable(this::createAuthorizationInterceptor))
|
||||
));
|
||||
WebSocketHandlerRegistration registration = webSocketHandlerRegistry.addHandler(dispatcher, web + "/console");
|
||||
|
||||
@ -0,0 +1,74 @@
|
||||
package org.ssssssss.magicapi.spring.boot.starter;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
import org.ssssssss.magicapi.core.config.MagicAPIProperties;
|
||||
import org.ssssssss.magicapi.core.interceptor.DefaultResultProvider;
|
||||
import org.ssssssss.magicapi.core.interceptor.ResultProvider;
|
||||
import org.ssssssss.magicapi.core.service.impl.ApiInfoMagicResourceStorage;
|
||||
import org.ssssssss.magicapi.core.service.impl.RequestMagicDynamicRegistry;
|
||||
import org.ssssssss.magicapi.datasource.model.MagicDynamicDataSource;
|
||||
import org.ssssssss.magicapi.datasource.service.DataSourceInfoMagicResourceStorage;
|
||||
import org.ssssssss.magicapi.datasource.service.DataSourceMagicDynamicRegistry;
|
||||
import org.ssssssss.magicapi.function.service.FunctionInfoMagicResourceStorage;
|
||||
import org.ssssssss.magicapi.function.service.FunctionMagicDynamicRegistry;
|
||||
import org.ssssssss.magicapi.utils.Mapping;
|
||||
|
||||
@Configuration
|
||||
@AutoConfigureAfter(MagicModuleConfiguration.class)
|
||||
public class MagicDynamicRegistryConfiguration {
|
||||
|
||||
|
||||
private final MagicAPIProperties properties;
|
||||
|
||||
@Autowired
|
||||
@Lazy
|
||||
private RequestMappingHandlerMapping requestMappingHandlerMapping;
|
||||
|
||||
|
||||
public MagicDynamicRegistryConfiguration(MagicAPIProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ApiInfoMagicResourceStorage apiInfoMagicResourceStorage() {
|
||||
return new ApiInfoMagicResourceStorage();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public RequestMagicDynamicRegistry magicRequestMagicDynamicRegistry(ApiInfoMagicResourceStorage apiInfoMagicResourceStorage) throws NoSuchMethodException {
|
||||
return new RequestMagicDynamicRegistry(apiInfoMagicResourceStorage, Mapping.create(requestMappingHandlerMapping, properties.getWeb(), properties.getPrefix()), properties.isAllowOverride());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public FunctionInfoMagicResourceStorage functionInfoMagicResourceStorage() {
|
||||
return new FunctionInfoMagicResourceStorage();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public FunctionMagicDynamicRegistry functionMagicDynamicRegistry(FunctionInfoMagicResourceStorage functionInfoMagicResourceStorage) {
|
||||
return new FunctionMagicDynamicRegistry(functionInfoMagicResourceStorage);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public DataSourceInfoMagicResourceStorage dataSourceInfoMagicResourceStorage() {
|
||||
return new DataSourceInfoMagicResourceStorage();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public DataSourceMagicDynamicRegistry dataSourceMagicDynamicRegistry(DataSourceInfoMagicResourceStorage dataSourceInfoMagicResourceStorage, MagicDynamicDataSource magicDynamicDataSource) {
|
||||
return new DataSourceMagicDynamicRegistry(dataSourceInfoMagicResourceStorage, magicDynamicDataSource);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,226 @@
|
||||
package org.ssssssss.magicapi.spring.boot.starter;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.multipart.MultipartResolver;
|
||||
import org.ssssssss.magicapi.core.config.Cache;
|
||||
import org.ssssssss.magicapi.core.config.MagicAPIProperties;
|
||||
import org.ssssssss.magicapi.core.config.Page;
|
||||
import org.ssssssss.magicapi.core.interceptor.DefaultResultProvider;
|
||||
import org.ssssssss.magicapi.core.interceptor.ResultProvider;
|
||||
import org.ssssssss.magicapi.core.model.Options;
|
||||
import org.ssssssss.magicapi.datasource.model.MagicDynamicDataSource;
|
||||
import org.ssssssss.magicapi.jsr223.JSR223LanguageProvider;
|
||||
import org.ssssssss.magicapi.modules.db.ColumnMapperAdapter;
|
||||
import org.ssssssss.magicapi.modules.db.SQLModule;
|
||||
import org.ssssssss.magicapi.modules.db.cache.DefaultSqlCache;
|
||||
import org.ssssssss.magicapi.modules.db.cache.SqlCache;
|
||||
import org.ssssssss.magicapi.modules.db.dialect.Dialect;
|
||||
import org.ssssssss.magicapi.modules.db.dialect.DialectAdapter;
|
||||
import org.ssssssss.magicapi.modules.db.inteceptor.DefaultSqlInterceptor;
|
||||
import org.ssssssss.magicapi.modules.db.inteceptor.NamedTableInterceptor;
|
||||
import org.ssssssss.magicapi.modules.db.inteceptor.SQLInterceptor;
|
||||
import org.ssssssss.magicapi.modules.db.provider.ColumnMapperProvider;
|
||||
import org.ssssssss.magicapi.modules.db.provider.DefaultPageProvider;
|
||||
import org.ssssssss.magicapi.modules.db.provider.PageProvider;
|
||||
import org.ssssssss.magicapi.modules.http.HttpModule;
|
||||
import org.ssssssss.magicapi.modules.servlet.RequestModule;
|
||||
import org.ssssssss.magicapi.modules.servlet.ResponseModule;
|
||||
import org.ssssssss.magicapi.modules.spring.EnvModule;
|
||||
import org.ssssssss.script.MagicResourceLoader;
|
||||
import org.ssssssss.script.functions.DynamicModuleImport;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class MagicModuleConfiguration {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MagicModuleConfiguration.class);
|
||||
|
||||
private final MagicAPIProperties properties;
|
||||
|
||||
|
||||
/**
|
||||
* SQL拦截器
|
||||
*/
|
||||
private final ObjectProvider<List<SQLInterceptor>> sqlInterceptorsProvider;
|
||||
|
||||
/**
|
||||
* 单表API拦截器
|
||||
*/
|
||||
private final ObjectProvider<List<NamedTableInterceptor>> namedTableInterceptorsProvider;
|
||||
|
||||
/**
|
||||
* 自定义的方言
|
||||
*/
|
||||
private final ObjectProvider<List<Dialect>> dialectsProvider;
|
||||
|
||||
/**
|
||||
* 自定义的列名转换
|
||||
*/
|
||||
private final ObjectProvider<List<ColumnMapperProvider>> columnMapperProvidersProvider;
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
@Autowired(required = false)
|
||||
private MultipartResolver multipartResolver;
|
||||
|
||||
public MagicModuleConfiguration(MagicAPIProperties properties,
|
||||
ObjectProvider<List<SQLInterceptor>> sqlInterceptorsProvider,
|
||||
ObjectProvider<List<NamedTableInterceptor>> namedTableInterceptorsProvider,
|
||||
ObjectProvider<List<Dialect>> dialectsProvider,
|
||||
ObjectProvider<List<ColumnMapperProvider>> columnMapperProvidersProvider,
|
||||
Environment environment) {
|
||||
this.properties = properties;
|
||||
this.sqlInterceptorsProvider = sqlInterceptorsProvider;
|
||||
this.namedTableInterceptorsProvider = namedTableInterceptorsProvider;
|
||||
this.dialectsProvider = dialectsProvider;
|
||||
this.columnMapperProvidersProvider = columnMapperProvidersProvider;
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入动态数据源
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(MagicDynamicDataSource.class)
|
||||
public MagicDynamicDataSource magicDynamicDataSource(@Autowired(required = false) DataSource dataSource) {
|
||||
MagicDynamicDataSource dynamicDataSource = new MagicDynamicDataSource();
|
||||
if (dataSource != null) {
|
||||
dynamicDataSource.put(dataSource);
|
||||
} else {
|
||||
logger.warn("当前数据源未配置");
|
||||
}
|
||||
return dynamicDataSource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(PageProvider.class)
|
||||
public PageProvider pageProvider() {
|
||||
Page pageConfig = properties.getPage();
|
||||
logger.info("未找到分页实现,采用默认分页实现,分页配置:(页码={},页大小={},默认首页={},默认页大小={})", pageConfig.getPage(), pageConfig.getSize(), pageConfig.getDefaultPage(), pageConfig.getDefaultSize());
|
||||
return new DefaultPageProvider(pageConfig.getPage(), pageConfig.getSize(), pageConfig.getDefaultPage(), pageConfig.getDefaultSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入SQL缓存实现
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(SqlCache.class)
|
||||
public SqlCache sqlCache() {
|
||||
Cache cacheConfig = properties.getCache();
|
||||
logger.info("未找到SQL缓存实现,采用默认缓存实现(LRU+TTL),缓存配置:(容量={},TTL={})", cacheConfig.getCapacity(), cacheConfig.getTtl());
|
||||
return new DefaultSqlCache(cacheConfig.getCapacity(), cacheConfig.getTtl());
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入数据库查询模块
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnBean({MagicDynamicDataSource.class})
|
||||
public SQLModule magicSqlModule(MagicDynamicDataSource dynamicDataSource,
|
||||
ResultProvider resultProvider,
|
||||
PageProvider pageProvider,
|
||||
SqlCache sqlCache) {
|
||||
SQLModule sqlModule = new SQLModule(dynamicDataSource);
|
||||
if (!dynamicDataSource.isEmpty()) {
|
||||
sqlModule.setDataSourceNode(dynamicDataSource.getDataSource());
|
||||
}
|
||||
sqlModule.setResultProvider(resultProvider);
|
||||
sqlModule.setPageProvider(pageProvider);
|
||||
List<SQLInterceptor> sqlInterceptors = sqlInterceptorsProvider.getIfAvailable(ArrayList::new);
|
||||
if (properties.isShowSql()) {
|
||||
sqlInterceptors.add(new DefaultSqlInterceptor());
|
||||
}
|
||||
sqlModule.setSqlInterceptors(sqlInterceptors);
|
||||
sqlModule.setNamedTableInterceptors(namedTableInterceptorsProvider.getIfAvailable(Collections::emptyList));
|
||||
ColumnMapperAdapter columnMapperAdapter = new ColumnMapperAdapter();
|
||||
this.columnMapperProvidersProvider.getIfAvailable(Collections::emptyList).stream().filter(mapperProvider -> !"default".equals(mapperProvider.name())).forEach(columnMapperAdapter::add);
|
||||
columnMapperAdapter.setDefault(properties.getSqlColumnCase());
|
||||
sqlModule.setColumnMapperProvider(columnMapperAdapter);
|
||||
sqlModule.setColumnMapRowMapper(columnMapperAdapter.getDefaultColumnMapRowMapper());
|
||||
sqlModule.setRowMapColumnMapper(columnMapperAdapter.getDefaultRowMapColumnMapper());
|
||||
sqlModule.setSqlCache(sqlCache);
|
||||
DialectAdapter dialectAdapter = new DialectAdapter();
|
||||
dialectsProvider.getIfAvailable(Collections::emptyList).forEach(dialectAdapter::add);
|
||||
sqlModule.setDialectAdapter(dialectAdapter);
|
||||
sqlModule.setLogicDeleteColumn(properties.getCrud().getLogicDeleteColumn());
|
||||
sqlModule.setLogicDeleteValue(properties.getCrud().getLogicDeleteValue());
|
||||
MagicResourceLoader.addModule("db", new DynamicModuleImport(SQLModule.class, context -> {
|
||||
String dataSourceKey = context.getString(Options.DEFAULT_DATA_SOURCE.getValue());
|
||||
if (StringUtils.isEmpty(dataSourceKey)) return sqlModule;
|
||||
SQLModule newSqlModule = sqlModule.cloneSQLModule();
|
||||
newSqlModule.setDataSourceNode(dynamicDataSource.getDataSource(dataSourceKey));
|
||||
return newSqlModule;
|
||||
}));
|
||||
return sqlModule;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JSR223LanguageProvider jsr223LanguageProvider() {
|
||||
return new JSR223LanguageProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(HttpModule.class)
|
||||
public HttpModule magicHttpModule() {
|
||||
return new HttpModule(createRestTemplate());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public EnvModule magicEnvModule(){
|
||||
return new EnvModule(environment);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public RequestModule magicRequestModule(){
|
||||
return new RequestModule(multipartResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入结果构建方法
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ResultProvider.class)
|
||||
public ResultProvider resultProvider() {
|
||||
return new DefaultResultProvider(properties.getResponse());
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ResponseModule magicResponseModule(ResultProvider resultProvider){
|
||||
return new ResponseModule(resultProvider);
|
||||
}
|
||||
|
||||
private RestTemplate createRestTemplate() {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
restTemplate.getMessageConverters().add(new StringHttpMessageConverter(StandardCharsets.UTF_8) {
|
||||
{
|
||||
setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return restTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
{
|
||||
"groups": [
|
||||
{
|
||||
"sourceType": "org.ssssssss.magicapi.spring.boot.starter.MagicAPIProperties",
|
||||
"name": "magic-api",
|
||||
"type": "org.ssssssss.magicapi.spring.boot.starter.MagicAPIProperties"
|
||||
},
|
||||
{
|
||||
"sourceType": "org.ssssssss.magicapi.spring.boot.starter.MagicAPIProperties",
|
||||
"name": "page-config",
|
||||
"sourceMethod": "getPageConfig()",
|
||||
"type": "org.ssssssss.magicapi.spring.boot.starter.PageConfig"
|
||||
},
|
||||
{
|
||||
"sourceType": "org.ssssssss.magicapi.spring.boot.starter.MagicAPIProperties",
|
||||
"name": "cache-config",
|
||||
"sourceMethod": "getCacheConfig()",
|
||||
"type": "org.ssssssss.magicapi.spring.boot.starter.CacheConfig"
|
||||
},
|
||||
{
|
||||
"sourceType": "org.ssssssss.magicapi.spring.boot.starter.MagicAPIProperties",
|
||||
"name": "debug-config",
|
||||
"sourceMethod": "getDebugConfig()",
|
||||
"type": "org.ssssssss.magicapi.spring.boot.starter.DebugConfig"
|
||||
},
|
||||
{
|
||||
"sourceType": "org.ssssssss.magicapi.spring.boot.starter.MagicAPIProperties",
|
||||
"name": "crud-config",
|
||||
"sourceMethod": "getCrudConfig()",
|
||||
"type": "org.ssssssss.magicapi.spring.boot.starter.CrudConfig"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.ssssssss</groupId>
|
||||
<artifactId>magic-api-parent</artifactId>
|
||||
<version>1.7.5</version>
|
||||
<version>2.0.0-beta.1</version>
|
||||
</parent>
|
||||
<artifactId>magic-api</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
@ -26,20 +26,9 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-mongodb</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
@ -71,5 +60,10 @@
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package org.ssssssss.magicapi.model;
|
||||
package org.ssssssss.magicapi.backup.model;
|
||||
|
||||
/**
|
||||
* 备份记录
|
||||
@ -34,7 +34,7 @@ public class Backup {
|
||||
/**
|
||||
* 备份内容
|
||||
*/
|
||||
private String content;
|
||||
private byte[] content;
|
||||
|
||||
/**
|
||||
* 操作人,取用户名,空为系统记录
|
||||
@ -45,7 +45,7 @@ public class Backup {
|
||||
public Backup() {
|
||||
}
|
||||
|
||||
public Backup(String id, String type, String name, String content) {
|
||||
public Backup(String id, String type, String name, byte[] content) {
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
@ -84,11 +84,11 @@ public class Backup {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
public byte[] getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
public void setContent(byte[] content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
package org.ssssssss.magicapi.provider;
|
||||
package org.ssssssss.magicapi.backup.service;
|
||||
|
||||
import org.ssssssss.magicapi.model.*;
|
||||
import org.ssssssss.magicapi.utils.JsonUtils;
|
||||
import org.ssssssss.magicapi.backup.model.Backup;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -14,33 +14,6 @@ public interface MagicBackupService {
|
||||
|
||||
int FETCH_SIZE = 100;
|
||||
|
||||
/**
|
||||
* 备份接口
|
||||
*
|
||||
* @param apiInfo 接口信息
|
||||
*/
|
||||
default void backup(ApiInfo apiInfo) {
|
||||
doBackup(new Backup(apiInfo.getId(), Constants.PATH_API, apiInfo.getName(), JsonUtils.toJsonString(apiInfo)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 备份函数
|
||||
*
|
||||
* @param functionInfo 函数信息
|
||||
*/
|
||||
default void backup(FunctionInfo functionInfo) {
|
||||
doBackup(new Backup(functionInfo.getId(), Constants.PATH_FUNCTION, functionInfo.getName(), JsonUtils.toJsonString(functionInfo)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 备份数据源
|
||||
*
|
||||
* @param dataSourceInfo 数据源信息
|
||||
*/
|
||||
default void backup(DataSourceInfo dataSourceInfo) {
|
||||
doBackup(new Backup(dataSourceInfo.getId(), Constants.PATH_DATASOURCE, dataSourceInfo.get("name"), JsonUtils.toJsonString(dataSourceInfo)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行备份动作
|
||||
*
|
||||
@ -48,6 +21,8 @@ public interface MagicBackupService {
|
||||
*/
|
||||
void doBackup(Backup backup);
|
||||
|
||||
void doBackupAll(String name, String createBy) throws IOException;
|
||||
|
||||
/**
|
||||
* 根据时间戳查询最近的 FETCH_SIZE 条记录
|
||||
*
|
||||
@ -85,14 +60,6 @@ public interface MagicBackupService {
|
||||
*/
|
||||
long removeBackup(String id);
|
||||
|
||||
/**
|
||||
* 删除一组备份信息
|
||||
*
|
||||
* @param idList 对象ID集合
|
||||
* @return 返回删除的记录数
|
||||
*/
|
||||
long removeBackup(List<String> idList);
|
||||
|
||||
/**
|
||||
* 根据13位时间戳删除备份记录(清除小于该值的记录)
|
||||
*
|
||||
@ -0,0 +1,161 @@
|
||||
package org.ssssssss.magicapi.backup.service;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.jdbc.core.BeanPropertyRowMapper;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.ssssssss.magicapi.core.config.MagicConfiguration;
|
||||
import org.ssssssss.magicapi.core.event.FileEvent;
|
||||
import org.ssssssss.magicapi.core.event.GroupEvent;
|
||||
import org.ssssssss.magicapi.backup.model.Backup;
|
||||
import org.ssssssss.magicapi.core.model.Group;
|
||||
import org.ssssssss.magicapi.core.model.MagicEntity;
|
||||
import org.ssssssss.magicapi.utils.JsonUtils;
|
||||
import org.ssssssss.magicapi.utils.WebUtils;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据库备份实现
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
public class MagicDatabaseBackupService implements MagicBackupService {
|
||||
|
||||
private final static String DEFAULT_COLUMNS = "id,create_date,tag,type,name,create_by";
|
||||
|
||||
private final JdbcTemplate template;
|
||||
|
||||
private final String INSERT_SQL;
|
||||
|
||||
private final String FIND_BY_ID;
|
||||
|
||||
private final String FIND_BY_TAG;
|
||||
|
||||
private final String FIND_BY_TIMESTAMP;
|
||||
|
||||
private final String FIND_BY_ID_AND_TIMESTAMP;
|
||||
|
||||
private final String DELETE_BY_ID;
|
||||
|
||||
private final String DELETE_BY_TIMESTAMP;
|
||||
|
||||
private final BeanPropertyRowMapper<Backup> rowMapper = new BeanPropertyRowMapper<>(Backup.class);
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MagicDatabaseBackupService.class);
|
||||
|
||||
public MagicDatabaseBackupService(JdbcTemplate template, String tableName) {
|
||||
this.template = template;
|
||||
this.template.setMaxRows(FETCH_SIZE);
|
||||
this.INSERT_SQL = String.format("insert into %s(%s,content) values(?,?,?,?,?,?,?)", tableName, DEFAULT_COLUMNS);
|
||||
this.FIND_BY_ID = String.format("select %s from %s where id = ? order by create_date desc", DEFAULT_COLUMNS, tableName);
|
||||
this.DELETE_BY_ID = String.format("delete from %s where id = ?", tableName);
|
||||
this.FIND_BY_TAG = String.format("select %s from %s where tag = ? order by create_date desc", DEFAULT_COLUMNS, tableName);
|
||||
this.FIND_BY_TIMESTAMP = String.format("select %s from %s where create_date < ? order by create_date desc", DEFAULT_COLUMNS, tableName);
|
||||
this.DELETE_BY_TIMESTAMP = String.format("delete from %s where create_date < ?", tableName);
|
||||
this.FIND_BY_ID_AND_TIMESTAMP = String.format("select * from %s where id = ? and create_date = ?", tableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doBackupAll(String name, String createBy) throws IOException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
MagicConfiguration.getMagicResourceService().export(null, null, baos);
|
||||
Backup backup = new Backup();
|
||||
backup.setId("full");
|
||||
backup.setType("full");
|
||||
backup.setName(name);
|
||||
backup.setCreateBy(createBy);
|
||||
backup.setContent(baos.toByteArray());
|
||||
doBackup(backup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doBackup(Backup backup) {
|
||||
try {
|
||||
if (backup.getCreateDate() == 0) {
|
||||
backup.setCreateDate(System.currentTimeMillis());
|
||||
}
|
||||
if (backup.getCreateBy() == null) {
|
||||
backup.setCreateBy(WebUtils.currentUserName());
|
||||
}
|
||||
template.update(INSERT_SQL, backup.getId(), backup.getCreateDate(), backup.getTag(), backup.getType(), backup.getName(), backup.getCreateBy(), backup.getContent());
|
||||
} catch (Exception e) {
|
||||
logger.warn("备份失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Backup> backupList(long timestamp) {
|
||||
return template.query(FIND_BY_TIMESTAMP, rowMapper, timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Backup> backupById(String id) {
|
||||
return template.query(FIND_BY_ID, rowMapper, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Backup backupInfo(String id, long timestamp) {
|
||||
return template.queryForObject(FIND_BY_ID_AND_TIMESTAMP, rowMapper, id, timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Backup> backupByTag(String tag) {
|
||||
return template.query(FIND_BY_TAG, rowMapper, tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long removeBackup(String id) {
|
||||
return template.update(DELETE_BY_ID, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long removeBackupByTimestamp(long timestamp) {
|
||||
try {
|
||||
return template.update(DELETE_BY_TIMESTAMP, timestamp);
|
||||
} catch (Exception e) {
|
||||
logger.warn("删除备份失败", e);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@EventListener(condition = "#event.source != T(org.ssssssss.magicapi.core.config.Constants).EVENT_SOURCE_NOTIFY")
|
||||
public void onFileEvent(FileEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case SAVE:
|
||||
case CREATE:
|
||||
case MOVE:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
MagicEntity entity = event.getEntity();
|
||||
doBackup(entity.getId(), JsonUtils.toJsonBytes(entity), entity.getName(), event.getType());
|
||||
}
|
||||
|
||||
@EventListener(condition = "#event.source != T(org.ssssssss.magicapi.core.config.Constants).EVENT_SOURCE_NOTIFY")
|
||||
public void onFolderEvent(GroupEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case SAVE:
|
||||
case CREATE:
|
||||
case MOVE:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
Group group = event.getGroup();
|
||||
doBackup(group.getId(), JsonUtils.toJsonBytes(group), group.getName(), group.getType() + "-group");
|
||||
}
|
||||
|
||||
private void doBackup(String id, byte[] content, String name, String type) {
|
||||
Backup backup = new Backup();
|
||||
backup.setName(name);
|
||||
backup.setId(id);
|
||||
backup.setContent(content);
|
||||
backup.setType(type);
|
||||
doBackup(backup);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,84 @@
|
||||
package org.ssssssss.magicapi.backup.web;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.ssssssss.magicapi.backup.model.Backup;
|
||||
import org.ssssssss.magicapi.core.config.Constants;
|
||||
import org.ssssssss.magicapi.core.web.MagicController;
|
||||
import org.ssssssss.magicapi.core.web.MagicExceptionHandler;
|
||||
import org.ssssssss.magicapi.core.config.MagicConfiguration;
|
||||
import org.ssssssss.magicapi.core.model.*;
|
||||
import org.ssssssss.magicapi.backup.service.MagicBackupService;
|
||||
import org.ssssssss.magicapi.core.service.MagicDynamicRegistry;
|
||||
import org.ssssssss.magicapi.utils.JsonUtils;
|
||||
import org.ssssssss.magicapi.utils.WebUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class MagicBackupController extends MagicController implements MagicExceptionHandler {
|
||||
|
||||
private final MagicBackupService service;
|
||||
|
||||
public MagicBackupController(MagicConfiguration configuration) {
|
||||
super(configuration);
|
||||
this.service = configuration.getMagicBackupService();
|
||||
}
|
||||
|
||||
@GetMapping("/backups")
|
||||
@ResponseBody
|
||||
public JsonBean<List<Backup>> backups(Long timestamp) {
|
||||
if(service == null){
|
||||
return new JsonBean<>(Collections.emptyList());
|
||||
}
|
||||
return new JsonBean<>(service.backupList(timestamp == null ? System.currentTimeMillis() : timestamp));
|
||||
}
|
||||
|
||||
@GetMapping("/backup/rollback")
|
||||
@ResponseBody
|
||||
public JsonBean<Boolean> rollback(String id, Long timestamp) throws IOException {
|
||||
notNull(service, BACKUP_NOT_ENABLED);
|
||||
Backup backup = service.backupInfo(id, timestamp);
|
||||
if("full".equals(id)){
|
||||
service.doBackupAll("还原全量备份前,系统自动全量备份", WebUtils.currentUserName());
|
||||
configuration.getMagicAPIService().upload(new ByteArrayInputStream(backup.getContent()), Constants.UPLOAD_MODE_FULL);
|
||||
return new JsonBean<>(true);
|
||||
}
|
||||
if(backup.getType().endsWith("-group")){
|
||||
Group group = JsonUtils.readValue(backup.getContent(), Group.class);
|
||||
return new JsonBean<>(MagicConfiguration.getMagicResourceService().saveGroup(group));
|
||||
}
|
||||
MagicEntity entity = configuration.getMagicDynamicRegistries().stream()
|
||||
.map(MagicDynamicRegistry::getMagicResourceStorage)
|
||||
.filter(it -> it.folder().equals(backup.getType()))
|
||||
.map(it -> it.read(backup.getContent()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if(entity != null){
|
||||
return new JsonBean<>(MagicConfiguration.getMagicResourceService().saveFile(entity));
|
||||
}
|
||||
return new JsonBean<>(false);
|
||||
}
|
||||
|
||||
@GetMapping("/backup")
|
||||
@ResponseBody
|
||||
public JsonBean<String> backup(Long timestamp, String id) {
|
||||
notNull(service, BACKUP_NOT_ENABLED);
|
||||
notBlank(id, PARAMETER_INVALID);
|
||||
notNull(timestamp, PARAMETER_INVALID);
|
||||
Backup backup = service.backupInfo(id, timestamp);
|
||||
MagicEntity entity = JsonUtils.readValue(backup.getContent(), MagicEntity.class);
|
||||
return new JsonBean<>(entity == null ? null : entity.getScript());
|
||||
}
|
||||
|
||||
@PostMapping("/backup/full")
|
||||
@ResponseBody
|
||||
public JsonBean<Boolean> doBackup() throws IOException {
|
||||
notNull(service, BACKUP_NOT_ENABLED);
|
||||
service.doBackupAll("主动全量备份", WebUtils.currentUserName());
|
||||
return new JsonBean<>(true);
|
||||
}
|
||||
}
|
||||
@ -1,222 +0,0 @@
|
||||
package org.ssssssss.magicapi.config;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.ssssssss.magicapi.model.FunctionInfo;
|
||||
import org.ssssssss.magicapi.model.Group;
|
||||
import org.ssssssss.magicapi.model.Parameter;
|
||||
import org.ssssssss.magicapi.model.TreeNode;
|
||||
import org.ssssssss.magicapi.provider.FunctionServiceProvider;
|
||||
import org.ssssssss.magicapi.provider.GroupServiceProvider;
|
||||
import org.ssssssss.magicapi.script.ScriptManager;
|
||||
import org.ssssssss.magicapi.utils.PathUtils;
|
||||
import org.ssssssss.script.MagicResourceLoader;
|
||||
import org.ssssssss.script.MagicScriptContext;
|
||||
import org.ssssssss.script.exception.MagicExitException;
|
||||
import org.ssssssss.script.runtime.ExitValue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 函数映射管理
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
public class MagicFunctionManager {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MagicFunctionManager.class);
|
||||
private static final Map<String, FunctionInfo> MAPPINGS = new ConcurrentHashMap<>();
|
||||
private final GroupServiceProvider groupServiceProvider;
|
||||
private final FunctionServiceProvider functionServiceProvider;
|
||||
private TreeNode<Group> groups;
|
||||
|
||||
public MagicFunctionManager(GroupServiceProvider groupServiceProvider, FunctionServiceProvider functionServiceProvider) {
|
||||
this.groupServiceProvider = groupServiceProvider;
|
||||
this.functionServiceProvider = functionServiceProvider;
|
||||
}
|
||||
|
||||
public void registerFunctionLoader() {
|
||||
MagicResourceLoader.addFunctionLoader((context, path) -> {
|
||||
FunctionInfo info = MAPPINGS.get(path);
|
||||
if (info != null) {
|
||||
String scriptName = groupServiceProvider.getScriptName(info.getGroupId(), info.getName(), info.getPath());
|
||||
List<Parameter> parameters = info.getParameters();
|
||||
return (Function<Object[], Object>) objects -> {
|
||||
MagicScriptContext functionContext = new MagicScriptContext(context.getRootVariables());
|
||||
functionContext.setScriptName(scriptName);
|
||||
if (objects != null) {
|
||||
for (int i = 0, len = objects.length, size = parameters.size(); i < len && i < size; i++) {
|
||||
functionContext.set(parameters.get(i).getName(), objects[i]);
|
||||
}
|
||||
}
|
||||
Object value = ScriptManager.executeScript(info.getScript(), functionContext);
|
||||
if (value instanceof ExitValue) {
|
||||
throw new MagicExitException((ExitValue) value);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载所有分组
|
||||
*/
|
||||
public synchronized void loadGroup() {
|
||||
groups = groupServiceProvider.functionGroupTree();
|
||||
}
|
||||
|
||||
public void registerAllFunction() {
|
||||
loadGroup();
|
||||
functionServiceProvider.listWithScript().stream()
|
||||
.filter(it -> groupServiceProvider.getFullPath(it.getGroupId()) != null)
|
||||
.forEach(this::register);
|
||||
}
|
||||
|
||||
public boolean hasRegister(FunctionInfo info) {
|
||||
String path = PathUtils.replaceSlash(Objects.toString(groupServiceProvider.getFullPath(info.getGroupId()), "") + "/" + info.getPath());
|
||||
FunctionInfo functionInfo = MAPPINGS.get(path);
|
||||
return functionInfo != null && !Objects.equals(info.getId(), functionInfo.getId());
|
||||
}
|
||||
|
||||
public boolean hasRegister(Set<String> paths) {
|
||||
return paths.stream().anyMatch(MAPPINGS::containsKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 函数移动
|
||||
*/
|
||||
public boolean move(String id, String groupId) {
|
||||
FunctionInfo info = MAPPINGS.get(id);
|
||||
if (info == null) {
|
||||
return false;
|
||||
}
|
||||
String path = Objects.toString(groupServiceProvider.getFullPath(groupId), "");
|
||||
FunctionInfo functionInfo = MAPPINGS.get(PathUtils.replaceSlash(path + "/" + info.getPath()));
|
||||
if (functionInfo != null && !Objects.equals(functionInfo.getId(), id)) {
|
||||
return false;
|
||||
}
|
||||
unregister(id);
|
||||
info.setGroupId(groupId);
|
||||
register(info);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public void register(FunctionInfo functionInfo) {
|
||||
if (functionInfo == null) {
|
||||
return;
|
||||
}
|
||||
FunctionInfo oldFunctionInfo = MAPPINGS.get(functionInfo.getId());
|
||||
if (oldFunctionInfo != null) {
|
||||
// 完全一致时不用注册
|
||||
if (functionInfo.equals(oldFunctionInfo)) {
|
||||
return;
|
||||
}
|
||||
// 如果路径不一致,则需要取消注册
|
||||
if (!Objects.equals(functionInfo.getPath(), oldFunctionInfo.getPath())) {
|
||||
unregister(functionInfo.getId());
|
||||
}
|
||||
}
|
||||
String path = Objects.toString(groupServiceProvider.getFullPath(functionInfo.getGroupId()), "");
|
||||
MAPPINGS.put(functionInfo.getId(), functionInfo);
|
||||
path = PathUtils.replaceSlash(path + "/" + functionInfo.getPath());
|
||||
functionInfo.setMappingPath(path);
|
||||
MAPPINGS.put(path, functionInfo);
|
||||
logger.info("注册函数:[{}:{}]", functionInfo.getName(), path);
|
||||
}
|
||||
|
||||
public List<FunctionInfo> getFunctionInfos() {
|
||||
return MAPPINGS.values().stream().distinct().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public FunctionInfo getFunctionInfo(String path) {
|
||||
return MAPPINGS.get(path);
|
||||
}
|
||||
|
||||
private boolean hasConflict(TreeNode<Group> group, String newPath) {
|
||||
// 获取要移动的接口
|
||||
List<FunctionInfo> infos = MAPPINGS.values().stream()
|
||||
.filter(info -> Objects.equals(info.getGroupId(), group.getNode().getId()))
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
// 判断是否有冲突
|
||||
for (FunctionInfo info : infos) {
|
||||
if (MAPPINGS.containsKey(PathUtils.replaceSlash(newPath + "/" + info.getPath()))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (TreeNode<Group> child : group.getChildren()) {
|
||||
if (hasConflict(child, newPath + "/" + Objects.toString(child.getNode().getPath(), ""))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public TreeNode<Group> findGroupTree(String groupId) {
|
||||
return groups.findTreeNode(it -> it.getId().equals(groupId));
|
||||
}
|
||||
|
||||
public boolean checkGroup(Group group) {
|
||||
TreeNode<Group> oldTree = groups.findTreeNode((item) -> item.getId().equals(group.getId()));
|
||||
// 如果只改了名字,则不做任何操作
|
||||
if (Objects.equals(oldTree.getNode().getParentId(), group.getParentId()) &&
|
||||
Objects.equals(oldTree.getNode().getPath(), group.getPath())) {
|
||||
return true;
|
||||
}
|
||||
// 新的接口分组路径
|
||||
String newPath = Objects.toString(groupServiceProvider.getFullPath(group.getParentId()), "");
|
||||
// 检测冲突
|
||||
return !hasConflict(oldTree, newPath + "/" + Objects.toString(group.getPath(), ""));
|
||||
}
|
||||
|
||||
private void recurseUpdateGroup(TreeNode<Group> node, boolean updateGroupId) {
|
||||
MAPPINGS.values().stream()
|
||||
.filter(info -> Objects.equals(info.getGroupId(), node.getNode().getId()))
|
||||
.distinct()
|
||||
.collect(Collectors.toList())
|
||||
.forEach(info -> {
|
||||
unregister(info.getId());
|
||||
if (updateGroupId) {
|
||||
info.setGroupId(node.getNode().getId());
|
||||
}
|
||||
register(info);
|
||||
});
|
||||
for (TreeNode<Group> child : node.getChildren()) {
|
||||
recurseUpdateGroup(child, false);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean updateGroup(String groupId) {
|
||||
loadGroup(); // 重新加载分组
|
||||
TreeNode<Group> groupTreeNode = groups.findTreeNode((item) -> item.getId().equals(groupId));
|
||||
recurseUpdateGroup(groupTreeNode, true);
|
||||
return functionServiceProvider.reload(groupId);
|
||||
}
|
||||
|
||||
public void deleteGroup(List<String> groupIds) {
|
||||
MAPPINGS.values().stream()
|
||||
.filter(info -> groupIds.contains(info.getGroupId()))
|
||||
.distinct()
|
||||
.collect(Collectors.toList())
|
||||
.forEach(info -> unregister(info.getId()));
|
||||
// 刷新分组缓存
|
||||
loadGroup();
|
||||
}
|
||||
|
||||
public void unregister(String id) {
|
||||
FunctionInfo functionInfo = MAPPINGS.remove(id);
|
||||
if (functionInfo != null) {
|
||||
MAPPINGS.remove(functionInfo.getMappingPath());
|
||||
logger.info("取消注册函数:[{},{}]", functionInfo.getName(), functionInfo.getMappingPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
package org.ssssssss.magicapi.config;
|
||||
|
||||
import org.ssssssss.script.annotation.UnableCall;
|
||||
|
||||
/**
|
||||
* 模块,主要用于import指令,import时根据模块名获取当前类如:<code>import assert</code>;
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
public interface MagicModule {
|
||||
|
||||
/**
|
||||
* 获取模块名
|
||||
*
|
||||
* @return 返回模块名称
|
||||
*/
|
||||
@UnableCall
|
||||
String getModuleName();
|
||||
}
|
||||
@ -1,528 +0,0 @@
|
||||
package org.ssssssss.magicapi.config;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
import org.ssssssss.magicapi.controller.RequestHandler;
|
||||
import org.ssssssss.magicapi.model.ApiInfo;
|
||||
import org.ssssssss.magicapi.model.Constants;
|
||||
import org.ssssssss.magicapi.model.Group;
|
||||
import org.ssssssss.magicapi.model.TreeNode;
|
||||
import org.ssssssss.magicapi.provider.ApiServiceProvider;
|
||||
import org.ssssssss.magicapi.provider.GroupServiceProvider;
|
||||
import org.ssssssss.magicapi.utils.Mapping;
|
||||
import org.ssssssss.magicapi.utils.PathUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 请求映射
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
public class MappingHandlerMapping {
|
||||
|
||||
/**
|
||||
* 已缓存的映射信息
|
||||
*/
|
||||
private static final Map<String, MappingNode> MAPPINGS = new ConcurrentHashMap<>();
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MappingHandlerMapping.class);
|
||||
/**
|
||||
* 接口分组
|
||||
*/
|
||||
private static TreeNode<Group> groups;
|
||||
/**
|
||||
* 请求到达时处理的方法
|
||||
*/
|
||||
private final Method method = RequestHandler.class.getDeclaredMethod("invoke", HttpServletRequest.class, HttpServletResponse.class, Map.class, Map.class, Map.class);
|
||||
/**
|
||||
* 统一接口前缀
|
||||
*/
|
||||
private final String prefix;
|
||||
/**
|
||||
* 是否覆盖应用接口
|
||||
*/
|
||||
private final boolean allowOverride;
|
||||
/**
|
||||
* 缓存已映射的接口信息
|
||||
*/
|
||||
private final List<ApiInfo> apiInfos = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
private Mapping mappingHelper;
|
||||
/**
|
||||
* 请求处理器
|
||||
*/
|
||||
private Object handler;
|
||||
/**
|
||||
* 接口信息读取
|
||||
*/
|
||||
private ApiServiceProvider magicApiService;
|
||||
/**
|
||||
* 分组信息读取
|
||||
*/
|
||||
private GroupServiceProvider groupServiceProvider;
|
||||
|
||||
public MappingHandlerMapping(String prefix, boolean allowOverride) throws NoSuchMethodException {
|
||||
this.prefix = prefix;
|
||||
this.allowOverride = allowOverride;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据request获取对应的接口信息
|
||||
*/
|
||||
public static ApiInfo getMappingApiInfo(HttpServletRequest request) {
|
||||
NativeWebRequest webRequest = new ServletWebRequest(request);
|
||||
// 找到注册的路径
|
||||
String requestMapping = (String) webRequest.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
|
||||
// 根据请求方法和路径获取接口信息
|
||||
return getMappingApiInfo(buildMappingKey(request.getMethod(), requestMapping));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据绑定的key获取接口信息
|
||||
*/
|
||||
private static ApiInfo getMappingApiInfo(String key) {
|
||||
return MAPPINGS.get(key).getInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建缓存map的key
|
||||
*
|
||||
* @param requestMethod 请求方法
|
||||
* @param requestMapping 请求路径
|
||||
*/
|
||||
private static String buildMappingKey(String requestMethod, String requestMapping) {
|
||||
if (StringUtils.isNotBlank(requestMapping) && !requestMapping.startsWith("/")) {
|
||||
requestMapping = "/" + requestMapping;
|
||||
}
|
||||
return Objects.toString(requestMethod, "GET").toUpperCase() + ":" + requestMapping;
|
||||
}
|
||||
|
||||
public static Group findGroup(String groupId) {
|
||||
TreeNode<Group> node = groups.findTreeNode(it -> it.getId().equals(groupId));
|
||||
return node != null ? node.getNode() : null;
|
||||
}
|
||||
|
||||
public static List<Group> findGroups(String groupId) {
|
||||
List<Group> groups = new ArrayList<>();
|
||||
Group group;
|
||||
while (!Constants.ROOT_ID.equals(groupId) && (group = MappingHandlerMapping.findGroup(groupId)) != null) {
|
||||
groups.add(group);
|
||||
groupId = group.getParentId();
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
public TreeNode<Group> findGroupTree(String groupId) {
|
||||
return groups.findTreeNode(it -> it.getId().equals(groupId));
|
||||
}
|
||||
|
||||
public void setRequestMappingHandlerMapping(RequestMappingHandlerMapping requestMappingHandlerMapping) {
|
||||
this.mappingHelper = Mapping.create(requestMappingHandlerMapping);
|
||||
}
|
||||
|
||||
public void setHandler(Object handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
public void setMagicApiService(ApiServiceProvider magicApiService) {
|
||||
this.magicApiService = magicApiService;
|
||||
}
|
||||
|
||||
public void setGroupServiceProvider(GroupServiceProvider groupServiceProvider) {
|
||||
this.groupServiceProvider = groupServiceProvider;
|
||||
}
|
||||
|
||||
public List<ApiInfo> getApiInfos() {
|
||||
return apiInfos;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载所有分组
|
||||
*/
|
||||
public synchronized void loadGroup() {
|
||||
groups = groupServiceProvider.apiGroupTree();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册请求
|
||||
*/
|
||||
public void registerAllMapping() {
|
||||
try {
|
||||
loadGroup();
|
||||
List<ApiInfo> list = magicApiService.listWithScript();
|
||||
if (list != null) {
|
||||
list = list.stream().filter(it -> groupServiceProvider.getFullPath(it.getGroupId()) != null).collect(Collectors.toList());
|
||||
for (ApiInfo info : list) {
|
||||
try {
|
||||
// 当接口存在时,刷新缓存
|
||||
registerMapping(info, true);
|
||||
} catch (Exception e) {
|
||||
logger.error("接口:{}注册失败", info.getName(), e);
|
||||
}
|
||||
}
|
||||
List<String> resistedList = list.stream().map(ApiInfo::getId).collect(Collectors.toList());
|
||||
Iterator<ApiInfo> iterator = apiInfos.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
String oldId = iterator.next().getId();
|
||||
// 当接口不存在时,取消注册接口
|
||||
if (!resistedList.contains(oldId)) {
|
||||
unregisterMapping(oldId, false);
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.info("注册接口映射失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据请求方法和路径获取接口信息
|
||||
*
|
||||
* @param method 请求方法
|
||||
* @param requestMapping 请求路径
|
||||
*/
|
||||
public ApiInfo getApiInfo(String method, String requestMapping) {
|
||||
MappingNode mappingNode = MAPPINGS.get(buildMappingKey(method, concatPath("", requestMapping)));
|
||||
return mappingNode == null ? null : mappingNode.getInfo();
|
||||
}
|
||||
|
||||
private boolean hasConflict(TreeNode<Group> group, String newPath) {
|
||||
// 获取要移动的接口
|
||||
List<ApiInfo> infos = apiInfos.stream().filter(info -> Objects.equals(info.getGroupId(), group.getNode().getId())).collect(Collectors.toList());
|
||||
// 判断是否有冲突
|
||||
for (ApiInfo info : infos) {
|
||||
String path = concatPath(newPath, "/" + info.getPath());
|
||||
String mappingKey = buildMappingKey(info.getMethod(), path);
|
||||
MappingNode mappingNode = MAPPINGS.get(mappingKey);
|
||||
if (mappingNode != null) {
|
||||
if (mappingNode.getInfo().equals(info)) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (!allowOverride) {
|
||||
Map<RequestMappingInfo, HandlerMethod> handlerMethods = this.mappingHelper.getHandlerMethods();
|
||||
if (handlerMethods.get(getRequestMapping(info.getMethod(), path)) != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (TreeNode<Group> child : group.getChildren()) {
|
||||
if (hasConflict(child, newPath + "/" + Objects.toString(child.getNode().getPath(), ""))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否允许修改
|
||||
*/
|
||||
public boolean checkGroup(Group group) {
|
||||
TreeNode<Group> oldTree = groups.findTreeNode((item) -> item.getId().equals(group.getId()));
|
||||
// 如果没移动目录且没改路径,则只需要判断名字是否冲突
|
||||
boolean parentIdEquals = Objects.equals(oldTree.getNode().getParentId(), group.getParentId());
|
||||
boolean nameEquals = Objects.equals(oldTree.getNode().getName(), group.getName());
|
||||
if (parentIdEquals && Objects.equals(oldTree.getNode().getPath(), group.getPath())) {
|
||||
return nameEquals || !groupServiceProvider.exists(group);
|
||||
}
|
||||
// 检测名字是否冲突
|
||||
boolean requiredChecked = (!parentIdEquals || !nameEquals);
|
||||
if (requiredChecked && groupServiceProvider.exists(group)) {
|
||||
return false;
|
||||
}
|
||||
// 新的接口分组路径
|
||||
String newPath = groupServiceProvider.getFullPath(group.getParentId());
|
||||
// 检测冲突
|
||||
return !hasConflict(oldTree, newPath + "/" + Objects.toString(group.getPath(), ""));
|
||||
}
|
||||
|
||||
public boolean hasRegister(Set<String> paths) {
|
||||
return paths.stream().anyMatch(MAPPINGS::containsKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除分组
|
||||
*/
|
||||
public void deleteGroup(List<String> groupIds) {
|
||||
// 找到对应的所有接口
|
||||
List<ApiInfo> deleteInfos = apiInfos.stream().filter(info -> groupIds.contains(info.getGroupId())).collect(Collectors.toList());
|
||||
for (ApiInfo info : deleteInfos) {
|
||||
unregisterMapping(info.getId(), true);
|
||||
}
|
||||
// 全部删除
|
||||
apiInfos.removeAll(deleteInfos);
|
||||
// 刷新分组缓存
|
||||
loadGroup();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改分组
|
||||
*/
|
||||
public boolean updateGroup(String groupId) {
|
||||
loadGroup(); // 重新加载分组
|
||||
TreeNode<Group> groupTreeNode = groups.findTreeNode((item) -> item.getId().equals(groupId));
|
||||
recurseUpdateGroup(groupTreeNode, true);
|
||||
return magicApiService.reload(groupId);
|
||||
}
|
||||
|
||||
private void recurseUpdateGroup(TreeNode<Group> node, boolean updateGroupId) {
|
||||
apiInfos.stream().filter(info -> Objects.equals(info.getGroupId(), node.getNode().getId())).forEach(info -> {
|
||||
unregisterMapping(info.getId(), false);
|
||||
if (updateGroupId) {
|
||||
info.setGroupId(node.getNode().getId());
|
||||
}
|
||||
registerMapping(info, false);
|
||||
});
|
||||
for (TreeNode<Group> child : node.getChildren()) {
|
||||
recurseUpdateGroup(child, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否已注册
|
||||
*/
|
||||
public boolean hasRegisterMapping(ApiInfo info) {
|
||||
if (info.getId() != null) {
|
||||
MappingNode mappingNode = MAPPINGS.get(info.getId());
|
||||
ApiInfo oldInfo = mappingNode == null ? null : mappingNode.getInfo();
|
||||
if (oldInfo != null
|
||||
&& Objects.equals(oldInfo.getGroupId(), info.getGroupId())
|
||||
&& Objects.equals(oldInfo.getMethod(), info.getMethod())
|
||||
&& Objects.equals(oldInfo.getPath(), info.getPath())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
String mappingKey = getMappingKey(info);
|
||||
if (MAPPINGS.containsKey(mappingKey)) {
|
||||
return !MAPPINGS.get(mappingKey).getInfo().getId().equals(info.getId());
|
||||
}
|
||||
if (!allowOverride) {
|
||||
Map<RequestMappingInfo, HandlerMethod> handlerMethods = this.mappingHelper.getHandlerMethods();
|
||||
return handlerMethods.get(getRequestMapping(info)) != null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 接口移动
|
||||
*/
|
||||
public boolean move(String id, String groupId) {
|
||||
MappingNode mappingNode = MAPPINGS.get(id);
|
||||
if (mappingNode == null) {
|
||||
return false;
|
||||
}
|
||||
ApiInfo copy = mappingNode.getInfo().copy();
|
||||
copy.setGroupId(groupId);
|
||||
if (hasRegisterMapping(copy)) {
|
||||
return false;
|
||||
}
|
||||
unregisterMapping(id, true);
|
||||
registerMapping(copy, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册请求映射
|
||||
*/
|
||||
public void registerMapping(ApiInfo info, boolean delete) {
|
||||
if (info == null) {
|
||||
return;
|
||||
}
|
||||
// 先判断是否已注册,如果已注册,则先取消注册在进行注册。
|
||||
MappingNode mappingNode = MAPPINGS.get(info.getId());
|
||||
String newMappingKey = getMappingKey(info);
|
||||
if (mappingNode != null) {
|
||||
ApiInfo oldInfo = mappingNode.getInfo();
|
||||
String oldMappingKey = mappingNode.getMappingKey();
|
||||
// URL 路径一致时,刷新脚本内容即可
|
||||
if (Objects.equals(oldMappingKey, newMappingKey)) {
|
||||
if (!info.equals(oldInfo)) {
|
||||
mappingNode.setInfo(info);
|
||||
MAPPINGS.get(newMappingKey).setInfo(info);
|
||||
if (delete) {
|
||||
refreshCache(info);
|
||||
}
|
||||
logger.info("刷新接口:{},{}", info.getName(), newMappingKey);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// URL不一致时,需要取消注册旧接口,重新注册新接口
|
||||
logger.info("取消注册接口:{},{}", oldInfo.getName(), oldMappingKey);
|
||||
// 取消注册
|
||||
MAPPINGS.remove(oldMappingKey);
|
||||
mappingHelper.unregister(getRequestMapping(oldInfo));
|
||||
}
|
||||
mappingNode = new MappingNode(info);
|
||||
mappingNode.setMappingKey(newMappingKey);
|
||||
// 注册
|
||||
RequestMappingInfo requestMapping = getRequestMapping(info);
|
||||
mappingNode.setRequestMappingInfo(requestMapping);
|
||||
mappingNode.setInfo(info);
|
||||
// 如果与应用冲突
|
||||
if (!overrideApplicationMapping(requestMapping)) {
|
||||
logger.error("接口{},{}与应用冲突,无法注册", info.getName(), newMappingKey);
|
||||
return;
|
||||
}
|
||||
logger.info("注册接口:{},{}", info.getName(), newMappingKey);
|
||||
MAPPINGS.put(info.getId(), mappingNode);
|
||||
MAPPINGS.put(newMappingKey, mappingNode);
|
||||
registerMapping(requestMapping, handler, method);
|
||||
if (delete) {
|
||||
// 刷新缓存
|
||||
refreshCache(info);
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshCache(ApiInfo info) {
|
||||
apiInfos.removeIf(i -> i.getId().equalsIgnoreCase(info.getId()));
|
||||
apiInfos.add(info);
|
||||
}
|
||||
|
||||
private void registerMapping(RequestMappingInfo requestMapping, Object handler, Method method) {
|
||||
mappingHelper.register(requestMapping, handler, method);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消注册请求映射
|
||||
*/
|
||||
public void unregisterMapping(String id, boolean delete) {
|
||||
MappingNode mappingNode = MAPPINGS.remove(id);
|
||||
if (mappingNode != null) {
|
||||
ApiInfo info = mappingNode.getInfo();
|
||||
logger.info("取消注册接口:{}", info.getName());
|
||||
MAPPINGS.remove(mappingNode.getMappingKey());
|
||||
mappingHelper.unregister(mappingNode.getRequestMappingInfo());
|
||||
if (delete) {
|
||||
// 刷新缓存
|
||||
apiInfos.removeIf(i -> i.getId().equalsIgnoreCase(info.getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据接口信息获取绑定map的key
|
||||
*/
|
||||
private String getMappingKey(ApiInfo info) {
|
||||
return buildMappingKey(info.getMethod(), getRequestPath(info.getGroupId(), info.getPath()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理前缀
|
||||
*
|
||||
* @param groupId 分组ID
|
||||
* @param path 请求路径
|
||||
*/
|
||||
public String getRequestPath(String groupId, String path) {
|
||||
return concatPath(groupServiceProvider.getFullPath(groupId), path);
|
||||
|
||||
}
|
||||
|
||||
public void registerController(Object target, String base) {
|
||||
Method[] methods = target.getClass().getDeclaredMethods();
|
||||
for (Method method : methods) {
|
||||
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
|
||||
if (requestMapping != null) {
|
||||
String[] paths = Stream.of(requestMapping.value()).map(value -> base + value).toArray(String[]::new);
|
||||
mappingHelper.register(mappingHelper.paths(paths).build(), target, method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String concatPath(String groupPath, String path) {
|
||||
path = groupPath + "/" + path;
|
||||
if (prefix != null) {
|
||||
path = prefix + "/" + path;
|
||||
}
|
||||
path = PathUtils.replaceSlash(path);
|
||||
if (path.startsWith("/")) {
|
||||
return path.substring(1);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 覆盖应用接口
|
||||
*/
|
||||
private boolean overrideApplicationMapping(RequestMappingInfo requestMapping) {
|
||||
if (mappingHelper.getHandlerMethods().containsKey(requestMapping)) {
|
||||
if (!allowOverride) {
|
||||
// 不允许覆盖
|
||||
return false;
|
||||
}
|
||||
logger.warn("取消注册应用接口:{}", requestMapping);
|
||||
// 取消注册原接口
|
||||
mappingHelper.unregister(requestMapping);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据接口信息构建 RequestMappingInfo
|
||||
*/
|
||||
private RequestMappingInfo getRequestMapping(ApiInfo info) {
|
||||
return mappingHelper.paths(getRequestPath(info.getGroupId(), info.getPath())).methods(RequestMethod.valueOf(info.getMethod().toUpperCase())).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据接口信息构建 RequestMappingInfo
|
||||
*/
|
||||
private RequestMappingInfo getRequestMapping(String method, String path) {
|
||||
return mappingHelper.paths(path).methods(RequestMethod.valueOf(method.toUpperCase())).build();
|
||||
}
|
||||
|
||||
static class MappingNode {
|
||||
|
||||
private ApiInfo info;
|
||||
|
||||
private String mappingKey;
|
||||
|
||||
private RequestMappingInfo requestMappingInfo;
|
||||
|
||||
public MappingNode(ApiInfo info) {
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public ApiInfo getInfo() {
|
||||
return info;
|
||||
}
|
||||
|
||||
public void setInfo(ApiInfo info) {
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public String getMappingKey() {
|
||||
return mappingKey;
|
||||
}
|
||||
|
||||
public void setMappingKey(String mappingKey) {
|
||||
this.mappingKey = mappingKey;
|
||||
}
|
||||
|
||||
public RequestMappingInfo getRequestMappingInfo() {
|
||||
return requestMappingInfo;
|
||||
}
|
||||
|
||||
public void setRequestMappingInfo(RequestMappingInfo requestMappingInfo) {
|
||||
this.requestMappingInfo = requestMappingInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
package org.ssssssss.magicapi.config;
|
||||
|
||||
/**
|
||||
* WebSocket 消息类型
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
public enum MessageType {
|
||||
/* S -> C message */
|
||||
/* 日志消息 */
|
||||
LOG,
|
||||
/* 进入断点 */
|
||||
BREAKPOINT,
|
||||
/* 请求接口发生异常 */
|
||||
EXCEPTION,
|
||||
|
||||
/* C -> S message */
|
||||
/* 设置断点 */
|
||||
SET_BREAKPOINT,
|
||||
/* 恢复断点 */
|
||||
RESUME_BREAKPOINT,
|
||||
/* 设置 Session ID */
|
||||
SET_SESSION_ID,
|
||||
/* 登录 */
|
||||
LOGIN
|
||||
}
|
||||
@ -1,133 +0,0 @@
|
||||
package org.ssssssss.magicapi.config;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
import org.ssssssss.magicapi.model.Constants;
|
||||
import org.ssssssss.magicapi.model.MagicConsoleSession;
|
||||
import org.ssssssss.magicapi.model.MagicNotify;
|
||||
import org.ssssssss.magicapi.provider.MagicNotifyService;
|
||||
import org.ssssssss.magicapi.utils.JsonUtils;
|
||||
import org.ssssssss.script.MagicScriptDebugContext;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* WebSocket Session 管理
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
public class WebSocketSessionManager {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(WebSocketSessionManager.class);
|
||||
|
||||
private static final Map<String, MagicConsoleSession> SESSION = new ConcurrentHashMap<>();
|
||||
|
||||
private static MagicNotifyService magicNotifyService;
|
||||
|
||||
private static String instanceId;
|
||||
|
||||
public static void add(MagicConsoleSession session) {
|
||||
SESSION.put(session.getId(), session);
|
||||
}
|
||||
|
||||
public static void remove(MagicConsoleSession session) {
|
||||
if (session.getId() != null) {
|
||||
remove(session.getId());
|
||||
}
|
||||
}
|
||||
|
||||
public static void remove(String sessionId) {
|
||||
SESSION.remove(sessionId);
|
||||
}
|
||||
|
||||
public static void sendToAll(MessageType messageType, Object... values) {
|
||||
String content = buildMessage(messageType, values);
|
||||
sendToAll(content);
|
||||
}
|
||||
|
||||
private static void sendToAll(String content) {
|
||||
SESSION.values().stream().filter(MagicConsoleSession::writeable).forEach(session -> sendBySession(session, content));
|
||||
sendToOther(null, content);
|
||||
}
|
||||
|
||||
public static void sendBySessionId(String sessionId, MessageType messageType, Object... values) {
|
||||
MagicConsoleSession session = findSession(sessionId);
|
||||
String content = buildMessage(messageType, values);
|
||||
if (session != null && session.writeable()) {
|
||||
sendBySession(session, content);
|
||||
} else {
|
||||
sendToOther(sessionId, content);
|
||||
}
|
||||
}
|
||||
|
||||
private static void sendToOther(String sessionId, String content) {
|
||||
if (magicNotifyService != null) {
|
||||
// 通知其他机器去发送消息
|
||||
magicNotifyService.sendNotify(new MagicNotify(instanceId, Constants.NOTIFY_WS_S_C, sessionId, content));
|
||||
}
|
||||
}
|
||||
|
||||
private static String buildMessage(MessageType messageType, Object... values) {
|
||||
StringBuilder builder = new StringBuilder(messageType.name().toLowerCase());
|
||||
if (values != null) {
|
||||
for (int i = 0, len = values.length; i < len; i++) {
|
||||
builder.append(",");
|
||||
Object value = values[i];
|
||||
if (i + 1 < len || value instanceof CharSequence || value instanceof Number) {
|
||||
builder.append(value);
|
||||
} else {
|
||||
builder.append(JsonUtils.toJsonString(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static void sendBySessionId(String sessionId, String content) {
|
||||
if (sessionId == null) {
|
||||
sendToAll(content);
|
||||
} else {
|
||||
MagicConsoleSession session = findSession(sessionId);
|
||||
if (session != null) {
|
||||
sendBySession(session, content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendBySession(MagicConsoleSession session, String content) {
|
||||
try {
|
||||
session.getWebSocketSession().sendMessage(new TextMessage(content));
|
||||
} catch (IOException e) {
|
||||
logger.error("发送WebSocket消息失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static MagicConsoleSession findSession(String sessionId) {
|
||||
return SESSION.values().stream()
|
||||
.filter(it -> Objects.equals(sessionId, it.getSessionId()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public static void setMagicNotifyService(MagicNotifyService magicNotifyService) {
|
||||
WebSocketSessionManager.magicNotifyService = magicNotifyService;
|
||||
}
|
||||
|
||||
public static void setInstanceId(String instanceId) {
|
||||
WebSocketSessionManager.instanceId = instanceId;
|
||||
}
|
||||
|
||||
public static void createSession(String sessionId, MagicScriptDebugContext debugContext) {
|
||||
MagicConsoleSession consoleSession = findSession(sessionId);
|
||||
if (consoleSession == null) {
|
||||
consoleSession = new MagicConsoleSession(sessionId, debugContext);
|
||||
SESSION.put(sessionId, consoleSession);
|
||||
} else {
|
||||
consoleSession.setMagicScriptDebugContext(debugContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,154 +0,0 @@
|
||||
package org.ssssssss.magicapi.controller;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.ssssssss.magicapi.config.MagicConfiguration;
|
||||
import org.ssssssss.magicapi.config.Valid;
|
||||
import org.ssssssss.magicapi.interceptor.Authorization;
|
||||
import org.ssssssss.magicapi.model.ApiInfo;
|
||||
import org.ssssssss.magicapi.model.Backup;
|
||||
import org.ssssssss.magicapi.model.Constants;
|
||||
import org.ssssssss.magicapi.model.JsonBean;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 接口相关操作
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
public class MagicAPIController extends MagicController implements MagicExceptionHandler {
|
||||
|
||||
public MagicAPIController(MagicConfiguration configuration) {
|
||||
super(configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除接口
|
||||
*
|
||||
* @param id 接口ID
|
||||
*/
|
||||
@RequestMapping("/delete")
|
||||
@ResponseBody
|
||||
@Valid(readonly = false)
|
||||
public JsonBean<Boolean> delete(HttpServletRequest request, String id) {
|
||||
ApiInfo apiInfo = getApiInfo(id);
|
||||
isTrue(allowVisit(request, Authorization.DELETE, apiInfo), PERMISSION_INVALID);
|
||||
isTrue(!Constants.LOCK.equals(apiInfo.getLock()), RESOURCE_LOCKED);
|
||||
return new JsonBean<>(magicAPIService.deleteApi(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有接口
|
||||
*/
|
||||
@RequestMapping("/list")
|
||||
@ResponseBody
|
||||
public JsonBean<List<ApiInfo>> list(HttpServletRequest request) {
|
||||
return new JsonBean<>(magicAPIService.apiList()
|
||||
.stream()
|
||||
.filter(it -> allowVisit(request, Authorization.VIEW, it))
|
||||
.map(ApiInfo::simple)
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询接口详情
|
||||
*
|
||||
* @param id 接口ID
|
||||
*/
|
||||
@RequestMapping("/get")
|
||||
@ResponseBody
|
||||
public JsonBean<ApiInfo> get(HttpServletRequest request, String id) {
|
||||
isTrue(allowVisit(request, Authorization.VIEW, getApiInfo(id)), PERMISSION_INVALID);
|
||||
return new JsonBean<>(magicAPIService.getApiInfo(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询历史记录
|
||||
*
|
||||
* @param id 接口ID
|
||||
*/
|
||||
@RequestMapping("/backups")
|
||||
@ResponseBody
|
||||
public JsonBean<List<Backup>> backupList(HttpServletRequest request, String id) {
|
||||
isTrue(allowVisit(request, Authorization.VIEW, getApiInfo(id)), PERMISSION_INVALID);
|
||||
return new JsonBean<>(magicBackupService.backupById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取历史记录
|
||||
*
|
||||
* @param id 接口ID
|
||||
* @param timestamp 时间点
|
||||
*/
|
||||
@RequestMapping("/backup/get")
|
||||
@ResponseBody
|
||||
public JsonBean<Backup> backups(HttpServletRequest request, String id, Long timestamp) {
|
||||
isTrue(allowVisit(request, Authorization.VIEW, getApiInfo(id)), PERMISSION_INVALID);
|
||||
return new JsonBean<>(magicBackupService.backupInfo(id, timestamp));
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动接口
|
||||
*/
|
||||
@RequestMapping("/api/move")
|
||||
@ResponseBody
|
||||
@Valid(readonly = false)
|
||||
public JsonBean<Boolean> apiMove(HttpServletRequest request, String id, String groupId) {
|
||||
ApiInfo apiInfo = getApiInfo(id).copy();
|
||||
// 新的分组ID
|
||||
apiInfo.setGroupId(groupId);
|
||||
isTrue(allowVisit(request, Authorization.SAVE, apiInfo), PERMISSION_INVALID);
|
||||
isTrue(!Constants.LOCK.equals(apiInfo.getLock()), RESOURCE_LOCKED);
|
||||
return new JsonBean<>(magicAPIService.moveApi(id, groupId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存接口
|
||||
*/
|
||||
@RequestMapping("/save")
|
||||
@ResponseBody
|
||||
@Valid(readonly = false)
|
||||
public JsonBean<String> save(HttpServletRequest request, @RequestBody ApiInfo info) {
|
||||
isTrue(allowVisit(request, Authorization.SAVE, info), PERMISSION_INVALID);
|
||||
if (StringUtils.isNotBlank(info.getId())) {
|
||||
ApiInfo oldInfo = getApiInfo(info.getId());
|
||||
isTrue(!Constants.LOCK.equals(oldInfo.getLock()), RESOURCE_LOCKED);
|
||||
}
|
||||
return new JsonBean<>(magicAPIService.saveApi(info));
|
||||
}
|
||||
|
||||
/**
|
||||
* 锁定接口
|
||||
*/
|
||||
@RequestMapping("/lock")
|
||||
@ResponseBody
|
||||
@Valid(readonly = false)
|
||||
public JsonBean<Boolean> lock(HttpServletRequest request, String id) {
|
||||
isTrue(allowVisit(request, Authorization.LOCK, getApiInfo(id)), PERMISSION_INVALID);
|
||||
return new JsonBean<>(magicAPIService.lockApi(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解锁接口
|
||||
*/
|
||||
@RequestMapping("/unlock")
|
||||
@ResponseBody
|
||||
@Valid(readonly = false)
|
||||
public JsonBean<Boolean> unlock(HttpServletRequest request, String id) {
|
||||
isTrue(allowVisit(request, Authorization.UNLOCK, getApiInfo(id)), PERMISSION_INVALID);
|
||||
return new JsonBean<>(magicAPIService.unlockApi(id));
|
||||
}
|
||||
|
||||
private ApiInfo getApiInfo(String id) {
|
||||
ApiInfo apiInfo = magicAPIService.getApiInfo(id);
|
||||
notNull(apiInfo, API_NOT_FOUND);
|
||||
return apiInfo;
|
||||
}
|
||||
}
|
||||
@ -1,86 +0,0 @@
|
||||
package org.ssssssss.magicapi.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.ssssssss.magicapi.config.MagicConfiguration;
|
||||
import org.ssssssss.magicapi.config.Valid;
|
||||
import org.ssssssss.magicapi.interceptor.Authorization;
|
||||
import org.ssssssss.magicapi.model.DataSourceInfo;
|
||||
import org.ssssssss.magicapi.model.JsonBean;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 数据源相关操作
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
public class MagicDataSourceController extends MagicController implements MagicExceptionHandler {
|
||||
|
||||
public MagicDataSourceController(MagicConfiguration configuration) {
|
||||
super(configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询数据源列表
|
||||
*/
|
||||
@RequestMapping("/datasource/list")
|
||||
@ResponseBody
|
||||
public JsonBean<List<DataSourceInfo>> list(HttpServletRequest request) {
|
||||
return new JsonBean<>(magicAPIService.datasourceList()
|
||||
.stream()
|
||||
.filter(it -> allowVisit(request, Authorization.VIEW, it))
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
}
|
||||
|
||||
@RequestMapping("/datasource/test")
|
||||
@ResponseBody
|
||||
public JsonBean<String> test(@RequestBody DataSourceInfo properties) {
|
||||
return new JsonBean<>(magicAPIService.testDataSource(properties));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据源
|
||||
*
|
||||
* @param properties 数据源配置信息
|
||||
*/
|
||||
@RequestMapping("/datasource/save")
|
||||
@Valid(readonly = false)
|
||||
@ResponseBody
|
||||
public JsonBean<String> save(HttpServletRequest request, @RequestBody DataSourceInfo properties) {
|
||||
isTrue(allowVisit(request, Authorization.SAVE, properties), PERMISSION_INVALID);
|
||||
return new JsonBean<>(magicAPIService.saveDataSource(properties));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据源
|
||||
*
|
||||
* @param id 数据源ID
|
||||
*/
|
||||
@RequestMapping("/datasource/delete")
|
||||
@Valid(readonly = false)
|
||||
@ResponseBody
|
||||
public JsonBean<Boolean> delete(HttpServletRequest request, String id) {
|
||||
DataSourceInfo dataSource = getDataSourceInfo(id);
|
||||
isTrue(allowVisit(request, Authorization.DELETE, dataSource), PERMISSION_INVALID);
|
||||
return new JsonBean<>(magicAPIService.deleteDataSource(id));
|
||||
}
|
||||
|
||||
@RequestMapping("/datasource/detail")
|
||||
@ResponseBody
|
||||
public JsonBean<DataSourceInfo> detail(HttpServletRequest request, String id) {
|
||||
DataSourceInfo dataSource = getDataSourceInfo(id);
|
||||
isTrue(allowVisit(request, Authorization.VIEW, dataSource), PERMISSION_INVALID);
|
||||
return new JsonBean<>(dataSource);
|
||||
}
|
||||
|
||||
private DataSourceInfo getDataSourceInfo(String id) {
|
||||
DataSourceInfo dataSource = magicAPIService.getDataSource(id);
|
||||
notNull(dataSource, DATASOURCE_NOT_FOUND);
|
||||
return dataSource;
|
||||
}
|
||||
}
|
||||
@ -1,127 +0,0 @@
|
||||
package org.ssssssss.magicapi.controller;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.ssssssss.magicapi.config.MagicConfiguration;
|
||||
import org.ssssssss.magicapi.config.Valid;
|
||||
import org.ssssssss.magicapi.interceptor.Authorization;
|
||||
import org.ssssssss.magicapi.model.Backup;
|
||||
import org.ssssssss.magicapi.model.Constants;
|
||||
import org.ssssssss.magicapi.model.FunctionInfo;
|
||||
import org.ssssssss.magicapi.model.JsonBean;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 函数相关操作
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
public class MagicFunctionController extends MagicController implements MagicExceptionHandler {
|
||||
|
||||
|
||||
public MagicFunctionController(MagicConfiguration configuration) {
|
||||
super(configuration);
|
||||
}
|
||||
|
||||
@RequestMapping("/function/list")
|
||||
@ResponseBody
|
||||
public JsonBean<List<FunctionInfo>> list(HttpServletRequest request) {
|
||||
return new JsonBean<>(magicAPIService.functionList()
|
||||
.stream()
|
||||
.filter(it -> allowVisit(request, Authorization.VIEW, it))
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
}
|
||||
|
||||
@RequestMapping("/function/get")
|
||||
@ResponseBody
|
||||
public JsonBean<FunctionInfo> get(HttpServletRequest request, String id) {
|
||||
isTrue(allowVisit(request, Authorization.VIEW, getFunctionInfo(id)), PERMISSION_INVALID);
|
||||
return new JsonBean<>(magicAPIService.getFunctionInfo(id));
|
||||
}
|
||||
|
||||
@RequestMapping("/function/backup/get")
|
||||
@ResponseBody
|
||||
public JsonBean<Backup> backups(HttpServletRequest request, String id, Long timestamp) {
|
||||
isTrue(allowVisit(request, Authorization.VIEW, getFunctionInfo(id)), PERMISSION_INVALID);
|
||||
return new JsonBean<>(magicBackupService.backupInfo(id, timestamp));
|
||||
}
|
||||
|
||||
@RequestMapping("/function/backups")
|
||||
@ResponseBody
|
||||
public JsonBean<List<Backup>> backupList(HttpServletRequest request, String id) {
|
||||
isTrue(allowVisit(request, Authorization.VIEW, getFunctionInfo(id)), PERMISSION_INVALID);
|
||||
return new JsonBean<>(magicBackupService.backupById(id)
|
||||
.stream()
|
||||
.sorted(Comparator.comparing(Backup::getCreateDate).reversed())
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@RequestMapping("/function/move")
|
||||
@ResponseBody
|
||||
@Valid(readonly = false)
|
||||
public JsonBean<Boolean> move(HttpServletRequest request, String id, String groupId) {
|
||||
FunctionInfo functionInfo = getFunctionInfo(id);
|
||||
functionInfo.setGroupId(groupId);
|
||||
isTrue(allowVisit(request, Authorization.SAVE, functionInfo), PERMISSION_INVALID);
|
||||
isTrue(!Constants.LOCK.equals(functionInfo.getLock()), RESOURCE_LOCKED);
|
||||
return new JsonBean<>(magicAPIService.moveFunction(id, groupId));
|
||||
}
|
||||
|
||||
|
||||
@RequestMapping("/function/save")
|
||||
@ResponseBody
|
||||
@Valid(readonly = false)
|
||||
public JsonBean<String> save(HttpServletRequest request, @RequestBody FunctionInfo functionInfo) {
|
||||
isTrue(allowVisit(request, Authorization.SAVE, functionInfo), PERMISSION_INVALID);
|
||||
if (StringUtils.isNotBlank(functionInfo.getId())) {
|
||||
FunctionInfo oldInfo = getFunctionInfo(functionInfo.getId());
|
||||
isTrue(!Constants.LOCK.equals(oldInfo.getLock()), RESOURCE_LOCKED);
|
||||
}
|
||||
return new JsonBean<>(magicAPIService.saveFunction(functionInfo));
|
||||
}
|
||||
|
||||
@RequestMapping("/function/delete")
|
||||
@ResponseBody
|
||||
@Valid(readonly = false)
|
||||
public JsonBean<Boolean> delete(HttpServletRequest request, String id) {
|
||||
FunctionInfo info = getFunctionInfo(id);
|
||||
isTrue(allowVisit(request, Authorization.DELETE, info), PERMISSION_INVALID);
|
||||
isTrue(!Constants.LOCK.equals(info.getLock()), RESOURCE_LOCKED);
|
||||
return new JsonBean<>(magicAPIService.deleteFunction(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 锁定函数
|
||||
*/
|
||||
@RequestMapping("/function/lock")
|
||||
@ResponseBody
|
||||
@Valid(readonly = false)
|
||||
public JsonBean<Boolean> lock(HttpServletRequest request, String id) {
|
||||
isTrue(allowVisit(request, Authorization.LOCK, getFunctionInfo(id)), PERMISSION_INVALID);
|
||||
return new JsonBean<>(magicAPIService.lockFunction(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解锁函数
|
||||
*/
|
||||
@RequestMapping("/function/unlock")
|
||||
@ResponseBody
|
||||
@Valid(readonly = false)
|
||||
public JsonBean<Boolean> unlock(HttpServletRequest request, String id) {
|
||||
isTrue(allowVisit(request, Authorization.UNLOCK, getFunctionInfo(id)), PERMISSION_INVALID);
|
||||
return new JsonBean<>(magicAPIService.unlockFunction(id));
|
||||
}
|
||||
|
||||
public FunctionInfo getFunctionInfo(String id) {
|
||||
FunctionInfo functionInfo = magicAPIService.getFunctionInfo(id);
|
||||
notNull(functionInfo, FUNCTION_NOT_FOUND);
|
||||
return functionInfo;
|
||||
}
|
||||
}
|
||||
@ -1,102 +0,0 @@
|
||||
package org.ssssssss.magicapi.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.ssssssss.magicapi.adapter.Resource;
|
||||
import org.ssssssss.magicapi.adapter.resource.FileResource;
|
||||
import org.ssssssss.magicapi.config.MagicConfiguration;
|
||||
import org.ssssssss.magicapi.config.Valid;
|
||||
import org.ssssssss.magicapi.interceptor.Authorization;
|
||||
import org.ssssssss.magicapi.model.Constants;
|
||||
import org.ssssssss.magicapi.model.Group;
|
||||
import org.ssssssss.magicapi.model.JsonBean;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 分组相关操作
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
public class MagicGroupController extends MagicController implements MagicExceptionHandler {
|
||||
|
||||
public MagicGroupController(MagicConfiguration configuration) {
|
||||
super(configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除分组
|
||||
*/
|
||||
@RequestMapping("/group/delete")
|
||||
@ResponseBody
|
||||
@Valid(readonly = false)
|
||||
public JsonBean<Boolean> deleteGroup(HttpServletRequest request, String groupId) {
|
||||
Group group = magicAPIService.getGroup(groupId);
|
||||
notNull(group, GROUP_NOT_FOUND);
|
||||
isTrue(allowVisit(request, Authorization.DELETE, group), PERMISSION_INVALID);
|
||||
return new JsonBean<>(magicAPIService.deleteGroup(groupId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改分组
|
||||
*/
|
||||
@RequestMapping("/group/update")
|
||||
@ResponseBody
|
||||
@Valid(readonly = false)
|
||||
public synchronized JsonBean<Boolean> groupUpdate(HttpServletRequest request, @RequestBody Group group) {
|
||||
isTrue(allowVisit(request, Authorization.SAVE, group), PERMISSION_INVALID);
|
||||
if (magicAPIService.updateGroup(group)) {
|
||||
return new JsonBean<>(true);
|
||||
}
|
||||
return new JsonBean<>(GROUP_CONFLICT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有分组
|
||||
*/
|
||||
@RequestMapping("/group/list")
|
||||
@ResponseBody
|
||||
public JsonBean<List<Group>> groupList(HttpServletRequest request, String type) {
|
||||
return new JsonBean<>(magicAPIService.groupList(type)
|
||||
.stream()
|
||||
.filter(it -> allowVisit(request, Authorization.VIEW, it))
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建分组
|
||||
*/
|
||||
@RequestMapping("/group/create")
|
||||
@ResponseBody
|
||||
@Valid(readonly = false)
|
||||
public JsonBean<String> createGroup(HttpServletRequest request, @RequestBody Group group) {
|
||||
isTrue(allowVisit(request, Authorization.SAVE, group), PERMISSION_INVALID);
|
||||
Resource resource = configuration.getWorkspace();
|
||||
if(resource instanceof FileResource){
|
||||
isTrue(resource.exists(), FILE_PATH_NOT_EXISTS);
|
||||
}
|
||||
return new JsonBean<>(magicAPIService.createGroup(group));
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制分组
|
||||
*/
|
||||
@RequestMapping("/group/copy")
|
||||
@ResponseBody
|
||||
@Valid(readonly = false)
|
||||
public JsonBean<String> copyGroup(HttpServletRequest request, String src, String target) {
|
||||
Group group = magicAPIService.getGroup(src);
|
||||
notNull(group, GROUP_NOT_FOUND);
|
||||
if (!Constants.ROOT_ID.equals(target)) {
|
||||
Group targetGroup = magicAPIService.getGroup(target);
|
||||
notNull(targetGroup, GROUP_NOT_FOUND);
|
||||
isTrue(allowVisit(request, Authorization.SAVE, targetGroup), PERMISSION_INVALID);
|
||||
}
|
||||
isTrue(allowVisit(request, Authorization.VIEW, group), PERMISSION_INVALID);
|
||||
return new JsonBean<>(magicAPIService.copyGroup(src, target));
|
||||
}
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
package org.ssssssss.magicapi.controller;
|
||||
|
||||
import org.ssssssss.magicapi.config.Message;
|
||||
import org.ssssssss.magicapi.config.MessageType;
|
||||
import org.ssssssss.magicapi.config.WebSocketSessionManager;
|
||||
import org.ssssssss.magicapi.exception.MagicLoginException;
|
||||
import org.ssssssss.magicapi.interceptor.AuthorizationInterceptor;
|
||||
import org.ssssssss.magicapi.model.MagicConsoleSession;
|
||||
|
||||
/**
|
||||
* UI上其它操作处理
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
public class MagicWorkbenchHandler {
|
||||
|
||||
private final AuthorizationInterceptor authorizationInterceptor;
|
||||
|
||||
public MagicWorkbenchHandler(AuthorizationInterceptor authorizationInterceptor) {
|
||||
this.authorizationInterceptor = authorizationInterceptor;
|
||||
}
|
||||
|
||||
@Message(MessageType.LOGIN)
|
||||
public void onLogin(MagicConsoleSession session, String token) {
|
||||
try {
|
||||
if (!authorizationInterceptor.requireLogin() || authorizationInterceptor.getUserByToken(token) != null) {
|
||||
WebSocketSessionManager.add(session);
|
||||
}
|
||||
} catch (MagicLoginException ignored) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package org.ssssssss.magicapi.core.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 模块,主要用于import指令,import时根据模块名获取当前类如:<code>import assert</code>;
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface MagicModule {
|
||||
|
||||
/**
|
||||
* 模块名
|
||||
*/
|
||||
String value();
|
||||
}
|
||||
@ -1,4 +1,6 @@
|
||||
package org.ssssssss.magicapi.config;
|
||||
package org.ssssssss.magicapi.core.annotation;
|
||||
|
||||
import org.ssssssss.magicapi.core.config.MessageType;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
package org.ssssssss.magicapi.config;
|
||||
package org.ssssssss.magicapi.core.annotation;
|
||||
|
||||
import org.ssssssss.magicapi.interceptor.Authorization;
|
||||
import org.ssssssss.magicapi.core.interceptor.Authorization;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@ -1,22 +1,17 @@
|
||||
package org.ssssssss.magicapi.spring.boot.starter;
|
||||
package org.ssssssss.magicapi.core.config;
|
||||
|
||||
/**
|
||||
* 备份配置
|
||||
*
|
||||
* @author mxd
|
||||
* @since 1.3.5
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class BackupConfig {
|
||||
public class Backup {
|
||||
|
||||
/**
|
||||
* 存储类型,可选 file, database
|
||||
* 是否启用备份配置,默认不启用
|
||||
*/
|
||||
private String resourceType = "file";
|
||||
|
||||
/**
|
||||
* 存储位置,选择存储为文件时专用
|
||||
*/
|
||||
private String location = "/data/magic-api/backup";
|
||||
private boolean enable = false;
|
||||
|
||||
/**
|
||||
* 保留天数,<=0 为不限制
|
||||
@ -33,22 +28,6 @@ public class BackupConfig {
|
||||
*/
|
||||
private String datasource;
|
||||
|
||||
public String getResourceType() {
|
||||
return resourceType;
|
||||
}
|
||||
|
||||
public void setResourceType(String resourceType) {
|
||||
this.resourceType = resourceType;
|
||||
}
|
||||
|
||||
public String getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public void setLocation(String location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public int getMaxHistory() {
|
||||
return maxHistory;
|
||||
}
|
||||
@ -72,4 +51,12 @@ public class BackupConfig {
|
||||
public void setDatasource(String datasource) {
|
||||
this.datasource = datasource;
|
||||
}
|
||||
|
||||
public boolean isEnable() {
|
||||
return enable;
|
||||
}
|
||||
|
||||
public void setEnable(boolean enable) {
|
||||
this.enable = enable;
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
package org.ssssssss.magicapi.spring.boot.starter;
|
||||
package org.ssssssss.magicapi.core.config;
|
||||
|
||||
/**
|
||||
* 缓存配置
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
public class CacheConfig {
|
||||
public class Cache {
|
||||
|
||||
/**
|
||||
* 是否启用缓存
|
||||
@ -1,10 +1,8 @@
|
||||
package org.ssssssss.magicapi.model;
|
||||
package org.ssssssss.magicapi.core.config;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* magic-api中使用的常量信息
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
public class Constants {
|
||||
|
||||
/**
|
||||
@ -13,46 +11,11 @@ public class Constants {
|
||||
public static final String CONST_STRING_TRUE = "true";
|
||||
|
||||
|
||||
/**
|
||||
* 分组类型: 接口
|
||||
*/
|
||||
public static final String GROUP_TYPE_API = "1";
|
||||
|
||||
/**
|
||||
* 分组类型: 函数
|
||||
*/
|
||||
public static final String GROUP_TYPE_FUNCTION = "2";
|
||||
|
||||
/**
|
||||
* 接口文件夹名
|
||||
*/
|
||||
public static final String PATH_API = "api";
|
||||
|
||||
/**
|
||||
* 函数文件夹名
|
||||
*/
|
||||
public static final String PATH_FUNCTION = "function";
|
||||
|
||||
/**
|
||||
* 数据源文件夹名
|
||||
*/
|
||||
public static final String PATH_DATASOURCE = "datasource";
|
||||
|
||||
/**
|
||||
* 备份文件夹名
|
||||
*/
|
||||
public static final String PATH_BACKUPS = "backups";
|
||||
|
||||
/**
|
||||
* 空值
|
||||
*/
|
||||
public static final String EMPTY = "";
|
||||
|
||||
/**
|
||||
* 根节点ID
|
||||
*/
|
||||
public static final String ROOT_ID = "0";
|
||||
|
||||
/**
|
||||
* 表达式验证
|
||||
*/
|
||||
@ -87,25 +50,31 @@ public class Constants {
|
||||
* 脚本中header的变量名
|
||||
*/
|
||||
public static final String VAR_NAME_HEADER = "header";
|
||||
|
||||
/**
|
||||
* 脚本中query的变量名
|
||||
*/
|
||||
public static final String VAR_NAME_QUERY = "query";
|
||||
/**
|
||||
|
||||
/**
|
||||
* 脚本中RequestBody的变量名
|
||||
*/
|
||||
public static final String VAR_NAME_REQUEST_BODY = "body";
|
||||
|
||||
/**
|
||||
* 脚本中RequestBody的变量值字段类型
|
||||
*/
|
||||
public static final String VAR_NAME_REQUEST_BODY_VALUE_TYPE_OBJECT = "object";
|
||||
|
||||
/**
|
||||
* 脚本中RequestBody的变量名字段类型
|
||||
*/
|
||||
public static final String VAR_NAME_REQUEST_BODY_VALUE_TYPE_ARRAY = "array";
|
||||
|
||||
public static final String HEADER_REQUEST_SESSION = "Magic-Request-Session";
|
||||
public static final String HEADER_REQUEST_SCRIPT_ID = "Magic-Request-Script-Id";
|
||||
|
||||
public static final String HEADER_REQUEST_CLIENT_ID = "Magic-Request-Client-Id";
|
||||
|
||||
public static final String HEADER_REQUEST_BREAKPOINTS = "Magic-Request-Breakpoints";
|
||||
|
||||
@ -115,72 +84,52 @@ public class Constants {
|
||||
|
||||
public static final String GROUP_METABASE = "group.json";
|
||||
|
||||
public static final String JSON_SUFFIX = ".json";
|
||||
|
||||
public static final String UPLOAD_MODE_FULL = "full";
|
||||
|
||||
public static final String LOCK = "1";
|
||||
|
||||
public static final String UNLOCK = "0";
|
||||
/**
|
||||
* 执行成功的message值
|
||||
*/
|
||||
public static final String RESPONSE_MESSAGE_SUCCESS = "success";
|
||||
/**
|
||||
* 通知新增
|
||||
*/
|
||||
public static final int NOTIFY_ACTION_ADD = 1;
|
||||
/**
|
||||
* 通知修改
|
||||
*/
|
||||
public static final int NOTIFY_ACTION_UPDATE = 2;
|
||||
/**
|
||||
* 通知删除
|
||||
*/
|
||||
public static final int NOTIFY_ACTION_DELETE = 3;
|
||||
/**
|
||||
* 通知更新全部
|
||||
*/
|
||||
public static final int NOTIFY_ACTION_ALL = 0;
|
||||
/**
|
||||
* 通知接口刷新
|
||||
*/
|
||||
public static final int NOTIFY_ACTION_API = 1;
|
||||
/**
|
||||
* 通知分组刷新
|
||||
*/
|
||||
public static final int NOTIFY_ACTION_GROUP = 2;
|
||||
/**
|
||||
* 通知函数刷新
|
||||
*/
|
||||
public static final int NOTIFY_ACTION_FUNCTION = 3;
|
||||
/**
|
||||
* 通知数据源刷新
|
||||
*/
|
||||
public static final int NOTIFY_ACTION_DATASOURCE = 4;
|
||||
/**
|
||||
* 通知 C -> S 的WebSocket消息
|
||||
*/
|
||||
public static final int NOTIFY_WS_C_S = 100;
|
||||
/**
|
||||
* 通知 S -> C 的WebSocket消息
|
||||
*/
|
||||
public static final int NOTIFY_WS_S_C = 200;
|
||||
/**
|
||||
* 空数组
|
||||
*/
|
||||
public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
|
||||
|
||||
public static final String ROOT_ID = "0";
|
||||
|
||||
public static final String EVENT_TYPE_FILE = "file";
|
||||
|
||||
public static final String EVENT_SOURCE_NOTIFY = "notify";
|
||||
|
||||
public static final String WEBSOCKET_ATTRIBUTE_FILE_ID = "fileId";
|
||||
|
||||
public static final String WEBSOCKET_ATTRIBUTE_USER_ID = "id";
|
||||
|
||||
public static final String WEBSOCKET_ATTRIBUTE_USER_NAME = "username";
|
||||
|
||||
public static final String WEBSOCKET_ATTRIBUTE_USER_IP = "ip";
|
||||
|
||||
public static final String WEBSOCKET_ATTRIBUTE_CLIENT_ID = "cid";
|
||||
|
||||
/**
|
||||
* 执行成功的code值
|
||||
*/
|
||||
public static int RESPONSE_CODE_SUCCESS = 1;
|
||||
|
||||
/**
|
||||
* 执行成功的message值
|
||||
*/
|
||||
public static final String RESPONSE_MESSAGE_SUCCESS = "success";
|
||||
|
||||
/**
|
||||
* 执行出现异常的code值
|
||||
*/
|
||||
public static int RESPONSE_CODE_EXCEPTION = -1;
|
||||
|
||||
/**
|
||||
* 参数验证未通过的code值
|
||||
*/
|
||||
public static int RESPONSE_CODE_INVALID = 0;
|
||||
|
||||
/**
|
||||
* 空数组
|
||||
*/
|
||||
public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
|
||||
|
||||
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package org.ssssssss.magicapi.spring.boot.starter;
|
||||
package org.ssssssss.magicapi.core.config;
|
||||
|
||||
/**
|
||||
* CRUD 配置
|
||||
@ -7,7 +7,7 @@ package org.ssssssss.magicapi.spring.boot.starter;
|
||||
* @date 2021-7-15 09:26:17
|
||||
* @since 1.3.4
|
||||
*/
|
||||
public class CrudConfig {
|
||||
public class Crud {
|
||||
/**
|
||||
* 逻辑删除列
|
||||
*/
|
||||
@ -1,11 +1,11 @@
|
||||
package org.ssssssss.magicapi.spring.boot.starter;
|
||||
package org.ssssssss.magicapi.core.config;
|
||||
|
||||
/**
|
||||
* Debug配置
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
public class DebugConfig {
|
||||
public class Debug {
|
||||
|
||||
/**
|
||||
* 断点超时时间
|
||||
@ -0,0 +1,100 @@
|
||||
package org.ssssssss.magicapi.core.config;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.ssssssss.magicapi.core.exception.InvalidArgumentException;
|
||||
import org.ssssssss.magicapi.core.model.JsonCode;
|
||||
|
||||
public interface JsonCodeConstants {
|
||||
|
||||
JsonCode SUCCESS = new JsonCode(1, Constants.RESPONSE_MESSAGE_SUCCESS);
|
||||
|
||||
JsonCode IS_READ_ONLY = new JsonCode(-2, "当前为只读模式,无法操作");
|
||||
|
||||
JsonCode PERMISSION_INVALID = new JsonCode(-10, "无权限操作.");
|
||||
|
||||
JsonCode GROUP_NOT_FOUND = new JsonCode(1001, "找不到分组信息");
|
||||
|
||||
JsonCode NOT_SUPPORTED_GROUP_TYPE = new JsonCode(1002, "不支持该分组类型");
|
||||
|
||||
JsonCode TARGET_IS_REQUIRED = new JsonCode(1003, "目标网址不能为空");
|
||||
|
||||
JsonCode SECRET_KEY_IS_REQUIRED = new JsonCode(1004, "secretKey不能为空");
|
||||
|
||||
JsonCode MOVE_NAME_CONFLICT = new JsonCode(1005, "移动后名称会重复,请修改名称后在试。");
|
||||
|
||||
JsonCode SRC_GROUP_CONFLICT = new JsonCode(1006, "源对象和分组不能一致");
|
||||
|
||||
JsonCode FILE_NOT_FOUND = new JsonCode(1007, "找不到对应文件或分组");
|
||||
|
||||
JsonCode RESOURCE_LOCKED = new JsonCode(1008, "当前资源已被锁定,请解锁后在操作。");
|
||||
|
||||
JsonCode PATH_CONFLICT = new JsonCode(1009, "该路径已被使用,请换一个路径在试");
|
||||
|
||||
JsonCode RESOURCE_PATH_CONFLICT = new JsonCode(1010, "资源中[%s]有冲突,请检查");
|
||||
|
||||
JsonCode MOVE_PATH_CONFLICT = new JsonCode(1011, "移动后路径会冲突,请换一个路径在试");
|
||||
|
||||
JsonCode REQUEST_METHOD_REQUIRED = new JsonCode(1012, "请求方法不能为空");
|
||||
|
||||
JsonCode REQUEST_PATH_REQUIRED = new JsonCode(1013, "请求路径不能为空");
|
||||
|
||||
JsonCode FUNCTION_PATH_REQUIRED = new JsonCode(1014, "函数路径不能为空");
|
||||
|
||||
JsonCode FILE_PATH_NOT_EXISTS = new JsonCode(1015, "配置的文件路径不存在,请检查");
|
||||
|
||||
JsonCode REQUEST_PATH_CONFLICT = new JsonCode(1016, "接口[%s(%s)]与应用冲突,无法注册");
|
||||
|
||||
JsonCode SCRIPT_REQUIRED = new JsonCode(1017, "脚本内容不能为空");
|
||||
|
||||
JsonCode NAME_REQUIRED = new JsonCode(1018, "名称不能为空");
|
||||
|
||||
JsonCode PATH_REQUIRED = new JsonCode(1019, "路径不能为空");
|
||||
|
||||
JsonCode DS_URL_REQUIRED = new JsonCode(1020, "jdbcURL不能为空");
|
||||
|
||||
JsonCode DS_KEY_REQUIRED = new JsonCode(1021, "key不能为空");
|
||||
|
||||
JsonCode DS_KEY_CONFLICT = new JsonCode(1022, "数据源key已被使用,请更换后在试");
|
||||
|
||||
JsonCode GROUP_ID_REQUIRED = new JsonCode(1023, "请选择分组");
|
||||
|
||||
JsonCode CRON_ID_REQUIRED = new JsonCode(1024, "cron表达式不能为空");
|
||||
|
||||
JsonCode NAME_INVALID = new JsonCode(1025, "名称不能包含特殊字符,只允许中文、数字、字母以及+_-.()的组合且不能.开头");
|
||||
|
||||
JsonCode DATASOURCE_KEY_INVALID = new JsonCode(1026, "数据源Key不能包含特殊字符,只允许中文、数字、字母以及_组合");
|
||||
|
||||
JsonCode FILE_SAVE_FAILURE = new JsonCode(1027, "保存失败,同一组下分组名称不能重复且不能包含特殊字符。");
|
||||
|
||||
JsonCode PARAMETER_INVALID = new JsonCode(1028, "参数验证失败");
|
||||
|
||||
JsonCode HEADER_INVALID = new JsonCode(1029, "header验证失败");
|
||||
|
||||
JsonCode PATH_VARIABLE_INVALID = new JsonCode(1030, "路径变量验证失败");
|
||||
|
||||
JsonCode BODY_INVALID = new JsonCode(1031, "body验证失败");
|
||||
|
||||
JsonCode FILE_IS_REQUIRED = new JsonCode(1032, "请上传文件");
|
||||
|
||||
JsonCode SIGN_IS_INVALID = new JsonCode(1033, "签名验证失败,请检查秘钥是否正确");
|
||||
|
||||
JsonCode BACKUP_NOT_ENABLED = new JsonCode(1034, "未启用备份,无法操作");
|
||||
|
||||
JsonCode API_NOT_FOUND = new JsonCode(1035, "找不到接口");
|
||||
|
||||
default void notNull(Object value, JsonCode jsonCode) {
|
||||
if (value == null) {
|
||||
throw new InvalidArgumentException(jsonCode);
|
||||
}
|
||||
}
|
||||
|
||||
default void isTrue(boolean value, JsonCode jsonCode) {
|
||||
if (!value) {
|
||||
throw new InvalidArgumentException(jsonCode);
|
||||
}
|
||||
}
|
||||
|
||||
default void notBlank(String value, JsonCode jsonCode) {
|
||||
isTrue(StringUtils.isNotBlank(value), jsonCode);
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,14 @@
|
||||
package org.ssssssss.magicapi.spring.boot.starter;
|
||||
package org.ssssssss.magicapi.core.config;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||
import org.ssssssss.magicapi.controller.RequestHandler;
|
||||
import org.ssssssss.magicapi.core.web.RequestHandler;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* magic-api配置信息
|
||||
@ -135,51 +136,34 @@ public class MagicAPIProperties {
|
||||
*/
|
||||
private boolean persistenceResponseBody = true;
|
||||
|
||||
@NestedConfigurationProperty
|
||||
private SecurityConfig securityConfig = new SecurityConfig();
|
||||
/**
|
||||
* 实例ID,集群环境下,要保证每台机器不同。默认启动后随机生成uuid
|
||||
*/
|
||||
private String instanceId = UUID.randomUUID().toString();
|
||||
|
||||
@NestedConfigurationProperty
|
||||
private PageConfig pageConfig = new PageConfig();
|
||||
private Security securityConfig = new Security();
|
||||
|
||||
@NestedConfigurationProperty
|
||||
private CacheConfig cacheConfig = new CacheConfig();
|
||||
private Page page = new Page();
|
||||
|
||||
@NestedConfigurationProperty
|
||||
private DebugConfig debugConfig = new DebugConfig();
|
||||
private Cache cache = new Cache();
|
||||
|
||||
@NestedConfigurationProperty
|
||||
private SwaggerConfig swaggerConfig = new SwaggerConfig();
|
||||
private Debug debug = new Debug();
|
||||
|
||||
@NestedConfigurationProperty
|
||||
private ResourceConfig resource = new ResourceConfig();
|
||||
private Resource resource = new Resource();
|
||||
|
||||
@NestedConfigurationProperty
|
||||
private ResponseCodeConfig responseCodeConfig = new ResponseCodeConfig();
|
||||
private ResponseCode responseCode = new ResponseCode();
|
||||
|
||||
@NestedConfigurationProperty
|
||||
private ClusterConfig clusterConfig = new ClusterConfig();
|
||||
private Crud crud = new Crud();
|
||||
|
||||
@NestedConfigurationProperty
|
||||
private CrudConfig crudConfig = new CrudConfig();
|
||||
|
||||
@NestedConfigurationProperty
|
||||
private BackupConfig backupConfig = new BackupConfig();
|
||||
|
||||
public CrudConfig getCrudConfig() {
|
||||
return crudConfig;
|
||||
}
|
||||
|
||||
public void setCrudConfig(CrudConfig crudConfig) {
|
||||
this.crudConfig = crudConfig;
|
||||
}
|
||||
|
||||
public String getEditorConfig() {
|
||||
return editorConfig;
|
||||
}
|
||||
|
||||
public void setEditorConfig(String editorConfig) {
|
||||
this.editorConfig = editorConfig;
|
||||
}
|
||||
private Backup backup = new Backup();
|
||||
|
||||
public String getWeb() {
|
||||
if (StringUtils.isBlank(web)) {
|
||||
@ -217,44 +201,20 @@ public class MagicAPIProperties {
|
||||
this.banner = banner;
|
||||
}
|
||||
|
||||
public PageConfig getPageConfig() {
|
||||
return pageConfig;
|
||||
|
||||
public List<String> getAutoImportModuleList() {
|
||||
return Arrays.asList(autoImportModule.replaceAll("\\s", "").split(","));
|
||||
}
|
||||
|
||||
public void setPageConfig(PageConfig pageConfig) {
|
||||
this.pageConfig = pageConfig;
|
||||
public List<String> getAutoImportPackageList() {
|
||||
if (autoImportPackage == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Arrays.asList(autoImportPackage.replaceAll("\\s", "").split(","));
|
||||
}
|
||||
|
||||
public boolean isThrowException() {
|
||||
return throwException;
|
||||
}
|
||||
|
||||
public void setThrowException(boolean throwException) {
|
||||
this.throwException = throwException;
|
||||
}
|
||||
|
||||
public CacheConfig getCacheConfig() {
|
||||
return cacheConfig;
|
||||
}
|
||||
|
||||
public void setCacheConfig(CacheConfig cacheConfig) {
|
||||
this.cacheConfig = cacheConfig;
|
||||
}
|
||||
|
||||
public DebugConfig getDebugConfig() {
|
||||
return debugConfig;
|
||||
}
|
||||
|
||||
public void setDebugConfig(DebugConfig debugConfig) {
|
||||
this.debugConfig = debugConfig;
|
||||
}
|
||||
|
||||
public SecurityConfig getSecurityConfig() {
|
||||
return securityConfig;
|
||||
}
|
||||
|
||||
public void setSecurityConfig(SecurityConfig securityConfig) {
|
||||
this.securityConfig = securityConfig;
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
@ -265,32 +225,20 @@ public class MagicAPIProperties {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public SwaggerConfig getSwaggerConfig() {
|
||||
return swaggerConfig;
|
||||
public boolean isThrowException() {
|
||||
return throwException;
|
||||
}
|
||||
|
||||
public void setSwaggerConfig(SwaggerConfig swaggerConfig) {
|
||||
this.swaggerConfig = swaggerConfig;
|
||||
public void setThrowException(boolean throwException) {
|
||||
this.throwException = throwException;
|
||||
}
|
||||
|
||||
public String getAutoImportModule() {
|
||||
return autoImportModule;
|
||||
}
|
||||
|
||||
public void setAutoImportModule(String autoImport) {
|
||||
this.autoImportModule = autoImport;
|
||||
}
|
||||
|
||||
public List<String> getAutoImportModuleList() {
|
||||
return Arrays.asList(autoImportModule.replaceAll("\\s", "").split(","));
|
||||
}
|
||||
|
||||
public boolean isAllowOverride() {
|
||||
return allowOverride;
|
||||
}
|
||||
|
||||
public void setAllowOverride(boolean allowOverride) {
|
||||
this.allowOverride = allowOverride;
|
||||
public void setAutoImportModule(String autoImportModule) {
|
||||
this.autoImportModule = autoImportModule;
|
||||
}
|
||||
|
||||
public String getAutoImportPackage() {
|
||||
@ -301,11 +249,12 @@ public class MagicAPIProperties {
|
||||
this.autoImportPackage = autoImportPackage;
|
||||
}
|
||||
|
||||
public List<String> getAutoImportPackageList() {
|
||||
if (autoImportPackage == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Arrays.asList(autoImportPackage.replaceAll("\\s", "").split(","));
|
||||
public boolean isAllowOverride() {
|
||||
return allowOverride;
|
||||
}
|
||||
|
||||
public void setAllowOverride(boolean allowOverride) {
|
||||
this.allowOverride = allowOverride;
|
||||
}
|
||||
|
||||
public int getThreadPoolExecutorSize() {
|
||||
@ -316,17 +265,12 @@ public class MagicAPIProperties {
|
||||
this.threadPoolExecutorSize = threadPoolExecutorSize;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
public String getEditorConfig() {
|
||||
return editorConfig;
|
||||
}
|
||||
|
||||
|
||||
public ResourceConfig getResource() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
public void setResource(ResourceConfig resource) {
|
||||
this.resource = resource;
|
||||
public void setEditorConfig(String editorConfig) {
|
||||
this.editorConfig = editorConfig;
|
||||
}
|
||||
|
||||
public boolean isSupportCrossDomain() {
|
||||
@ -345,22 +289,6 @@ public class MagicAPIProperties {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
public ResponseCodeConfig getResponseCodeConfig() {
|
||||
return responseCodeConfig;
|
||||
}
|
||||
|
||||
public void setResponseCodeConfig(ResponseCodeConfig responseCodeConfig) {
|
||||
this.responseCodeConfig = responseCodeConfig;
|
||||
}
|
||||
|
||||
public ClusterConfig getClusterConfig() {
|
||||
return clusterConfig;
|
||||
}
|
||||
|
||||
public void setClusterConfig(ClusterConfig clusterConfig) {
|
||||
this.clusterConfig = clusterConfig;
|
||||
}
|
||||
|
||||
public String getSecretKey() {
|
||||
return secretKey;
|
||||
}
|
||||
@ -385,14 +313,6 @@ public class MagicAPIProperties {
|
||||
this.showUrl = showUrl;
|
||||
}
|
||||
|
||||
public BackupConfig getBackupConfig() {
|
||||
return backupConfig;
|
||||
}
|
||||
|
||||
public void setBackupConfig(BackupConfig backupConfig) {
|
||||
this.backupConfig = backupConfig;
|
||||
}
|
||||
|
||||
public boolean isShowSql() {
|
||||
return showSql;
|
||||
}
|
||||
@ -424,4 +344,76 @@ public class MagicAPIProperties {
|
||||
public void setPersistenceResponseBody(boolean persistenceResponseBody) {
|
||||
this.persistenceResponseBody = persistenceResponseBody;
|
||||
}
|
||||
|
||||
public String getInstanceId() {
|
||||
return instanceId;
|
||||
}
|
||||
|
||||
public void setInstanceId(String instanceId) {
|
||||
this.instanceId = instanceId;
|
||||
}
|
||||
|
||||
public Security getSecurityConfig() {
|
||||
return securityConfig;
|
||||
}
|
||||
|
||||
public void setSecurityConfig(Security securityConfig) {
|
||||
this.securityConfig = securityConfig;
|
||||
}
|
||||
|
||||
public Page getPage() {
|
||||
return page;
|
||||
}
|
||||
|
||||
public void setPage(Page page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
public Cache getCache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
public void setCache(Cache cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
public Debug getDebug() {
|
||||
return debug;
|
||||
}
|
||||
|
||||
public void setDebug(Debug debug) {
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
public Resource getResource() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
public void setResource(Resource resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
public ResponseCode getResponseCode() {
|
||||
return responseCode;
|
||||
}
|
||||
|
||||
public void setResponseCode(ResponseCode responseCode) {
|
||||
this.responseCode = responseCode;
|
||||
}
|
||||
|
||||
public Crud getCrud() {
|
||||
return crud;
|
||||
}
|
||||
|
||||
public void setCrud(Crud crud) {
|
||||
this.crud = crud;
|
||||
}
|
||||
|
||||
public Backup getBackup() {
|
||||
return backup;
|
||||
}
|
||||
|
||||
public void setBackup(Backup backup) {
|
||||
this.backup = backup;
|
||||
}
|
||||
}
|
||||
@ -1,55 +1,38 @@
|
||||
package org.ssssssss.magicapi.config;
|
||||
package org.ssssssss.magicapi.core.config;
|
||||
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.ssssssss.magicapi.adapter.Resource;
|
||||
import org.ssssssss.magicapi.controller.RequestHandler;
|
||||
import org.ssssssss.magicapi.interceptor.AuthorizationInterceptor;
|
||||
import org.ssssssss.magicapi.interceptor.RequestInterceptor;
|
||||
import org.ssssssss.magicapi.provider.*;
|
||||
import org.ssssssss.magicapi.core.resource.Resource;
|
||||
import org.ssssssss.magicapi.core.web.RequestHandler;
|
||||
import org.ssssssss.magicapi.core.model.MagicEntity;
|
||||
import org.ssssssss.magicapi.core.service.MagicDynamicRegistry;
|
||||
import org.ssssssss.magicapi.core.service.MagicResourceService;
|
||||
import org.ssssssss.magicapi.core.interceptor.AuthorizationInterceptor;
|
||||
import org.ssssssss.magicapi.core.interceptor.RequestInterceptor;
|
||||
import org.ssssssss.magicapi.core.service.MagicAPIService;
|
||||
import org.ssssssss.magicapi.backup.service.MagicBackupService;
|
||||
import org.ssssssss.magicapi.core.service.MagicNotifyService;
|
||||
import org.ssssssss.magicapi.core.interceptor.ResultProvider;
|
||||
import org.ssssssss.magicapi.datasource.model.MagicDynamicDataSource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 配置信息
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
public class MagicConfiguration {
|
||||
|
||||
/**
|
||||
* 拦截器
|
||||
*/
|
||||
private final List<RequestInterceptor> requestInterceptors = new ArrayList<>();
|
||||
/**
|
||||
* 接口映射
|
||||
*/
|
||||
private MappingHandlerMapping mappingHandlerMapping;
|
||||
/**
|
||||
* 函数管理
|
||||
*/
|
||||
private MagicFunctionManager magicFunctionManager;
|
||||
|
||||
/**
|
||||
* 编辑器配置文件
|
||||
*/
|
||||
private String editorConfig;
|
||||
/**
|
||||
* 接口查询Service
|
||||
*/
|
||||
private ApiServiceProvider apiServiceProvider;
|
||||
|
||||
/**
|
||||
* 分组查询Service
|
||||
*/
|
||||
private GroupServiceProvider groupServiceProvider;
|
||||
|
||||
/**
|
||||
* 函数查询Service
|
||||
*/
|
||||
private FunctionServiceProvider functionServiceProvider;
|
||||
|
||||
private MagicAPIService magicAPIService;
|
||||
|
||||
private MagicDynamicDataSource magicDynamicDataSource;
|
||||
|
||||
/**
|
||||
* 请求出错时,是否抛出异常
|
||||
*/
|
||||
@ -72,6 +55,10 @@ public class MagicConfiguration {
|
||||
|
||||
private MagicBackupService magicBackupService;
|
||||
|
||||
private static MagicResourceService magicResourceService;
|
||||
|
||||
private List<MagicDynamicRegistry<? extends MagicEntity>> magicDynamicRegistries;
|
||||
|
||||
/**
|
||||
* debug 超时时间
|
||||
*/
|
||||
@ -83,14 +70,6 @@ public class MagicConfiguration {
|
||||
this.requestInterceptors.add(requestInterceptor);
|
||||
}
|
||||
|
||||
public MappingHandlerMapping getMappingHandlerMapping() {
|
||||
return mappingHandlerMapping;
|
||||
}
|
||||
|
||||
public void setMappingHandlerMapping(MappingHandlerMapping mappingHandlerMapping) {
|
||||
this.mappingHandlerMapping = mappingHandlerMapping;
|
||||
}
|
||||
|
||||
public AuthorizationInterceptor getAuthorizationInterceptor() {
|
||||
return authorizationInterceptor;
|
||||
}
|
||||
@ -103,22 +82,6 @@ public class MagicConfiguration {
|
||||
return requestInterceptors;
|
||||
}
|
||||
|
||||
public ApiServiceProvider getApiServiceProvider() {
|
||||
return apiServiceProvider;
|
||||
}
|
||||
|
||||
public void setApiServiceProvider(ApiServiceProvider apiServiceProvider) {
|
||||
this.apiServiceProvider = apiServiceProvider;
|
||||
}
|
||||
|
||||
public GroupServiceProvider getGroupServiceProvider() {
|
||||
return groupServiceProvider;
|
||||
}
|
||||
|
||||
public void setGroupServiceProvider(GroupServiceProvider groupServiceProvider) {
|
||||
this.groupServiceProvider = groupServiceProvider;
|
||||
}
|
||||
|
||||
public boolean isThrowException() {
|
||||
return throwException;
|
||||
}
|
||||
@ -159,22 +122,6 @@ public class MagicConfiguration {
|
||||
this.enableWeb = enableWeb;
|
||||
}
|
||||
|
||||
public FunctionServiceProvider getFunctionServiceProvider() {
|
||||
return functionServiceProvider;
|
||||
}
|
||||
|
||||
public void setFunctionServiceProvider(FunctionServiceProvider functionServiceProvider) {
|
||||
this.functionServiceProvider = functionServiceProvider;
|
||||
}
|
||||
|
||||
public MagicFunctionManager getMagicFunctionManager() {
|
||||
return magicFunctionManager;
|
||||
}
|
||||
|
||||
public void setMagicFunctionManager(MagicFunctionManager magicFunctionManager) {
|
||||
this.magicFunctionManager = magicFunctionManager;
|
||||
}
|
||||
|
||||
public String getEditorConfig() {
|
||||
return editorConfig;
|
||||
}
|
||||
@ -223,15 +170,43 @@ public class MagicConfiguration {
|
||||
this.magicBackupService = magicBackupService;
|
||||
}
|
||||
|
||||
public MagicDynamicDataSource getMagicDynamicDataSource() {
|
||||
return magicDynamicDataSource;
|
||||
}
|
||||
|
||||
public void setMagicDynamicDataSource(MagicDynamicDataSource magicDynamicDataSource) {
|
||||
this.magicDynamicDataSource = magicDynamicDataSource;
|
||||
}
|
||||
|
||||
public static MagicResourceService getMagicResourceService() {
|
||||
return MagicConfiguration.magicResourceService;
|
||||
}
|
||||
|
||||
public void setMagicResourceService(MagicResourceService magicResourceService) {
|
||||
MagicConfiguration.magicResourceService = magicResourceService;
|
||||
}
|
||||
|
||||
public List<MagicDynamicRegistry<? extends MagicEntity>> getMagicDynamicRegistries() {
|
||||
return magicDynamicRegistries;
|
||||
}
|
||||
|
||||
public void setMagicDynamicRegistries(List<MagicDynamicRegistry<? extends MagicEntity>> magicDynamicRegistries) {
|
||||
this.magicDynamicRegistries = magicDynamicRegistries;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印banner
|
||||
*/
|
||||
public void printBanner() {
|
||||
public void printBanner(List<String> plugins) {
|
||||
System.out.println(" __ __ _ _ ____ ___ ");
|
||||
System.out.println(" | \\/ | __ _ __ _ (_) ___ / \\ | _ \\|_ _|");
|
||||
System.out.println(" | |\\/| | / _` | / _` || | / __| / _ \\ | |_) || | ");
|
||||
System.out.println(" | | | || (_| || (_| || || (__ / ___ \\ | __/ | | ");
|
||||
System.out.println(" |_| |_| \\__,_| \\__, ||_| \\___|/_/ \\_\\|_| |___|");
|
||||
System.out.println(" |___/ " + RequestHandler.class.getPackage().getImplementationVersion());
|
||||
if(!plugins.isEmpty()){
|
||||
System.out.println("集成插件:");
|
||||
plugins.stream().peek(it -> System.out.print("- ")).forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,19 +1,13 @@
|
||||
package org.ssssssss.magicapi.config;
|
||||
package org.ssssssss.magicapi.core.config;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.ssssssss.magicapi.model.Constants;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 接口跨域处理
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
public class MagicCorsFilter implements Filter {
|
||||
|
||||
@Override
|
||||
@ -41,7 +35,7 @@ public class MagicCorsFilter implements Filter {
|
||||
@Override
|
||||
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletRequest request = (HttpServletRequest) req;
|
||||
if (StringUtils.isNotBlank(Constants.HEADER_REQUEST_SESSION)) {
|
||||
if (StringUtils.isNotBlank(Constants.HEADER_REQUEST_CLIENT_ID)) {
|
||||
process(request, (HttpServletResponse) resp);
|
||||
}
|
||||
chain.doFilter(req, resp);
|
||||
@ -1,4 +1,4 @@
|
||||
package org.ssssssss.magicapi.config;
|
||||
package org.ssssssss.magicapi.core.config;
|
||||
|
||||
/**
|
||||
* 函数,主要用于脚本中直接可使用的函数,如 now();
|
||||
@ -0,0 +1,17 @@
|
||||
package org.ssssssss.magicapi.core.config;
|
||||
|
||||
import org.ssssssss.magicapi.core.model.Plugin;
|
||||
import org.ssssssss.magicapi.core.web.MagicControllerRegister;
|
||||
|
||||
public interface MagicPluginConfiguration {
|
||||
|
||||
Plugin plugin();
|
||||
|
||||
|
||||
/**
|
||||
* 注册Controller
|
||||
*/
|
||||
default MagicControllerRegister controllerRegister(){
|
||||
return (mapping, configuration) -> { };
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package org.ssssssss.magicapi.core.config;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
public enum MessageType {
|
||||
/* S -> C message */
|
||||
/* 日志消息 */
|
||||
LOG,
|
||||
/* 多个日志消息 */
|
||||
LOGS,
|
||||
/* 进入断点 */
|
||||
BREAKPOINT,
|
||||
/* 请求接口发生异常 */
|
||||
EXCEPTION,
|
||||
/* 登录结果 */
|
||||
LOGIN_RESPONSE,
|
||||
/* 通知客户端,有用户上线 */
|
||||
USER_LOGIN,
|
||||
/* 通知客户端,有用户下线 */
|
||||
USER_LOGOUT,
|
||||
/* 通知客户端,当前机器在线人数 */
|
||||
ONLINE_USERS,
|
||||
/* 通知客户端,他人进入文件*/
|
||||
INTO_FILE_ID,
|
||||
/* PONG */
|
||||
PONG,
|
||||
|
||||
/* C -> S message */
|
||||
/* 设置断点 */
|
||||
SET_BREAKPOINT,
|
||||
/* 恢复断点 */
|
||||
RESUME_BREAKPOINT,
|
||||
/* 登录 */
|
||||
LOGIN,
|
||||
/* 设置当前所在文件 */
|
||||
SET_FILE_ID,
|
||||
/* ping */
|
||||
PING,
|
||||
|
||||
/* S <-> S -> C message*/
|
||||
/* 获取当前在线用户 */
|
||||
SEND_ONLINE
|
||||
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
package org.ssssssss.magicapi.spring.boot.starter;
|
||||
package org.ssssssss.magicapi.core.config;
|
||||
|
||||
/**
|
||||
* 分页配置
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
public class PageConfig {
|
||||
public class Page {
|
||||
|
||||
/**
|
||||
* 默认page表达式
|
||||
@ -1,11 +1,11 @@
|
||||
package org.ssssssss.magicapi.spring.boot.starter;
|
||||
package org.ssssssss.magicapi.core.config;
|
||||
|
||||
/**
|
||||
* 接口存储配置
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
public class ResourceConfig {
|
||||
public class Resource {
|
||||
|
||||
/**
|
||||
* 存储类型,默认是文件
|
||||
@ -1,4 +1,4 @@
|
||||
package org.ssssssss.magicapi.spring.boot.starter;
|
||||
package org.ssssssss.magicapi.core.config;
|
||||
|
||||
/**
|
||||
* json结果code配置
|
||||
@ -6,7 +6,7 @@ package org.ssssssss.magicapi.spring.boot.starter;
|
||||
* @author mxd
|
||||
* @since 1.1.2
|
||||
*/
|
||||
public class ResponseCodeConfig {
|
||||
public class ResponseCode {
|
||||
|
||||
/**
|
||||
* 执行成功的code值
|
||||
@ -1,4 +1,4 @@
|
||||
package org.ssssssss.magicapi.spring.boot.starter;
|
||||
package org.ssssssss.magicapi.core.config;
|
||||
|
||||
/**
|
||||
* 安全配置
|
||||
@ -6,7 +6,7 @@ package org.ssssssss.magicapi.spring.boot.starter;
|
||||
* @author mxd
|
||||
* @since 0.4.0
|
||||
*/
|
||||
public class SecurityConfig {
|
||||
public class Security {
|
||||
|
||||
/**
|
||||
* 登录用的用户名
|
||||
@ -0,0 +1,210 @@
|
||||
package org.ssssssss.magicapi.core.config;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
import org.ssssssss.magicapi.core.event.EventAction;
|
||||
import org.ssssssss.magicapi.core.context.MagicConsoleSession;
|
||||
import org.ssssssss.magicapi.core.model.MagicNotify;
|
||||
import org.ssssssss.magicapi.core.model.Pair;
|
||||
import org.ssssssss.magicapi.core.service.MagicNotifyService;
|
||||
import org.ssssssss.magicapi.utils.JsonUtils;
|
||||
import org.ssssssss.script.MagicScriptDebugContext;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class WebSocketSessionManager {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(WebSocketSessionManager.class);
|
||||
|
||||
private static final Map<String, MagicConsoleSession> SESSIONS = new ConcurrentHashMap<>();
|
||||
|
||||
private static MagicNotifyService magicNotifyService;
|
||||
|
||||
private static final Map<String, MagicScriptDebugContext> CONTEXTS = new ConcurrentHashMap<>();
|
||||
|
||||
private static String instanceId;
|
||||
|
||||
private static final List<Pair<String, String>> MESSAGE_CACHE = new ArrayList<>(200);
|
||||
|
||||
public static void add(MagicConsoleSession session) {
|
||||
SESSIONS.put(session.getClientId(), session);
|
||||
}
|
||||
|
||||
public static MagicConsoleSession getConsoleSession(String clientId) {
|
||||
return SESSIONS.get(clientId);
|
||||
}
|
||||
|
||||
static {
|
||||
// 1秒1次发送日志
|
||||
new ScheduledThreadPoolExecutor(1, r -> new Thread(r, "magic-api-send-log-task")).scheduleAtFixedRate(WebSocketSessionManager::flushLog, 1, 1, TimeUnit.SECONDS);
|
||||
// 60秒检测一次是否在线
|
||||
new ScheduledThreadPoolExecutor(1, r -> new Thread(r, "magic-api-websocket-clean-task")).scheduleAtFixedRate(WebSocketSessionManager::checkSession, 60, 60, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public static Collection<MagicConsoleSession> getSessions() {
|
||||
return SESSIONS.values();
|
||||
}
|
||||
|
||||
public static void remove(MagicConsoleSession session) {
|
||||
if (session.getClientId() != null) {
|
||||
remove(session.getClientId());
|
||||
}
|
||||
}
|
||||
|
||||
public static void remove(String sessionId) {
|
||||
SESSIONS.remove(sessionId);
|
||||
}
|
||||
|
||||
public static void sendToAll(MessageType messageType, Object... values) {
|
||||
String content = buildMessage(messageType, values);
|
||||
sendToAll(content);
|
||||
}
|
||||
|
||||
private static void sendToAll(String content) {
|
||||
getSessions().stream().filter(MagicConsoleSession::writeable).forEach(session -> sendBySession(session, content));
|
||||
sendToMachineByClientId(null, content);
|
||||
}
|
||||
|
||||
public static void sendLogs(String sessionId, String message) {
|
||||
synchronized (MESSAGE_CACHE) {
|
||||
MESSAGE_CACHE.add(Pair.of(sessionId, message));
|
||||
if (MESSAGE_CACHE.size() >= 100) {
|
||||
flushLog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void flushLog() {
|
||||
try {
|
||||
Map<String, List<String>> messages;
|
||||
synchronized (MESSAGE_CACHE) {
|
||||
messages = MESSAGE_CACHE.stream().collect(Collectors.groupingBy(Pair::getFirst, Collectors.mapping(Pair::getSecond, Collectors.toList())));
|
||||
MESSAGE_CACHE.clear();
|
||||
}
|
||||
messages.forEach((clientId, logs) -> sendByClientId(clientId, logs.size() > 1 ? MessageType.LOGS : MessageType.LOG, logs));
|
||||
} catch (Exception e) {
|
||||
logger.warn("发送日志失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendByClientId(String clientId, MessageType messageType, Object... values) {
|
||||
MagicConsoleSession session = findSession(clientId);
|
||||
String content = buildMessage(messageType, values);
|
||||
if (session != null && session.writeable()) {
|
||||
sendBySession(session, content);
|
||||
} else {
|
||||
sendToMachineByClientId(clientId, content);
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendToOther(String excludeClientId, MessageType messageType, Object... values) {
|
||||
String content = buildMessage(messageType, values);
|
||||
getSessions().stream()
|
||||
.filter(MagicConsoleSession::writeable)
|
||||
.filter(it -> !it.getClientId().equals(excludeClientId))
|
||||
.forEach(session -> sendBySession(session, content));
|
||||
sendToMachineByClientId(null, content);
|
||||
}
|
||||
|
||||
public static void sendToMachineByClientId(String clientId, String content) {
|
||||
if (magicNotifyService != null) {
|
||||
// 通知其他机器去发送消息
|
||||
magicNotifyService.sendNotify(new MagicNotify(instanceId, EventAction.WS_S_C, clientId, content));
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendToMachine(MessageType messageType, Object... args) {
|
||||
if (magicNotifyService != null) {
|
||||
// 通知其他机器去发送消息
|
||||
magicNotifyService.sendNotify(new MagicNotify(instanceId, EventAction.WS_S_S, null, buildMessage(messageType, args)));
|
||||
}
|
||||
}
|
||||
|
||||
public static String buildMessage(MessageType messageType, Object... values) {
|
||||
StringBuilder builder = new StringBuilder(messageType.name().toLowerCase());
|
||||
if (values != null) {
|
||||
for (int i = 0, len = values.length; i < len; i++) {
|
||||
builder.append(",");
|
||||
Object value = values[i];
|
||||
if (i + 1 < len || value instanceof CharSequence || value instanceof Number) {
|
||||
builder.append(value);
|
||||
} else {
|
||||
builder.append(JsonUtils.toJsonString(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static void sendByClientId(String clientId, String content) {
|
||||
if (clientId == null) {
|
||||
getSessions().stream().filter(MagicConsoleSession::writeable).forEach(session -> sendBySession(session, content));
|
||||
} else {
|
||||
MagicConsoleSession session = findSession(clientId);
|
||||
if (session != null) {
|
||||
sendBySession(session, content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendBySession(MagicConsoleSession session, String content) {
|
||||
try {
|
||||
if (session != null) {
|
||||
synchronized (session.getClientId()) {
|
||||
session.getWebSocketSession().sendMessage(new TextMessage(content));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("发送WebSocket消息失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static MagicConsoleSession findSession(String clientId) {
|
||||
return getSessions().stream()
|
||||
.filter(it -> Objects.equals(clientId, it.getClientId()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public static void setMagicNotifyService(MagicNotifyService magicNotifyService) {
|
||||
WebSocketSessionManager.magicNotifyService = magicNotifyService;
|
||||
}
|
||||
|
||||
public static void setInstanceId(String instanceId) {
|
||||
WebSocketSessionManager.instanceId = instanceId;
|
||||
}
|
||||
|
||||
public static void addMagicScriptContext(String sessionAndScriptId, MagicScriptDebugContext context) {
|
||||
CONTEXTS.put(sessionAndScriptId, context);
|
||||
}
|
||||
|
||||
public static MagicScriptDebugContext findMagicScriptContext(String sessionAndScriptId) {
|
||||
return CONTEXTS.get(sessionAndScriptId);
|
||||
}
|
||||
|
||||
public static void removeMagicScriptContext(String sessionAndScriptId) {
|
||||
CONTEXTS.remove(sessionAndScriptId);
|
||||
}
|
||||
|
||||
private static void checkSession() {
|
||||
try {
|
||||
long activateTime = System.currentTimeMillis() - 20 * 1000;
|
||||
SESSIONS.entrySet().stream()
|
||||
.filter(it -> it.getValue().getActivateTime() < activateTime)
|
||||
.collect(Collectors.toList())
|
||||
.forEach(entry -> {
|
||||
MagicConsoleSession session = entry.getValue();
|
||||
SESSIONS.remove(entry.getKey());
|
||||
session.close();
|
||||
sendToAll(MessageType.USER_LOGOUT, session.getAttributes());
|
||||
});
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package org.ssssssss.magicapi.context;
|
||||
package org.ssssssss.magicapi.core.context;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@ -0,0 +1,88 @@
|
||||
package org.ssssssss.magicapi.core.context;
|
||||
|
||||
import org.springframework.web.socket.CloseStatus;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import org.ssssssss.magicapi.core.config.Constants;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class MagicConsoleSession {
|
||||
|
||||
private static final Map<String, MagicConsoleSession> cached = new ConcurrentHashMap<>();
|
||||
|
||||
private String clientId;
|
||||
|
||||
private WebSocketSession webSocketSession;
|
||||
|
||||
private final Map<String, Object> attributes = new HashMap<>();
|
||||
|
||||
private long activateTime = System.currentTimeMillis();
|
||||
|
||||
public MagicConsoleSession(WebSocketSession webSocketSession) {
|
||||
this.webSocketSession = webSocketSession;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public WebSocketSession getWebSocketSession() {
|
||||
return webSocketSession;
|
||||
}
|
||||
|
||||
public boolean writeable() {
|
||||
return webSocketSession != null && webSocketSession.isOpen();
|
||||
}
|
||||
|
||||
public static MagicConsoleSession from(WebSocketSession session) {
|
||||
MagicConsoleSession magicConsoleSession = cached.get(session.getId());
|
||||
if (magicConsoleSession == null) {
|
||||
magicConsoleSession = new MagicConsoleSession(session);
|
||||
cached.put(session.getId(), magicConsoleSession);
|
||||
}
|
||||
return magicConsoleSession;
|
||||
}
|
||||
|
||||
public static void remove(WebSocketSession session) {
|
||||
cached.remove(session.getId());
|
||||
}
|
||||
|
||||
public Object getAttribute(String key){
|
||||
return attributes.get(key);
|
||||
}
|
||||
|
||||
public void setAttribute(String key, Object value){
|
||||
attributes.put(key, value);
|
||||
}
|
||||
|
||||
public Map<String, Object> getAttributes(){
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
setAttribute(Constants.WEBSOCKET_ATTRIBUTE_CLIENT_ID, clientId);
|
||||
}
|
||||
|
||||
public long getActivateTime() {
|
||||
return activateTime;
|
||||
}
|
||||
|
||||
public void setActivateTime(long activateTime) {
|
||||
this.activateTime = activateTime;
|
||||
}
|
||||
|
||||
public void close(){
|
||||
if(this.webSocketSession != null){
|
||||
remove(this.webSocketSession);
|
||||
try {
|
||||
this.webSocketSession.close(CloseStatus.SESSION_NOT_RELIABLE);
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
this.webSocketSession = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package org.ssssssss.magicapi.interceptor;
|
||||
package org.ssssssss.magicapi.core.context;
|
||||
|
||||
/**
|
||||
* magic 用户对象
|
||||
@ -1,6 +1,4 @@
|
||||
package org.ssssssss.magicapi.context;
|
||||
|
||||
import org.ssssssss.magicapi.model.RequestEntity;
|
||||
package org.ssssssss.magicapi.core.context;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
@ -0,0 +1,141 @@
|
||||
package org.ssssssss.magicapi.core.context;
|
||||
|
||||
import org.ssssssss.magicapi.core.model.ApiInfo;
|
||||
import org.ssssssss.magicapi.core.model.DebugRequest;
|
||||
import org.ssssssss.script.MagicScriptContext;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 请求信息
|
||||
*
|
||||
* @author mxd
|
||||
*/
|
||||
public class RequestEntity {
|
||||
|
||||
private final Long requestTime = System.currentTimeMillis();
|
||||
private final String requestId = UUID.randomUUID().toString().replace("-", "");
|
||||
private ApiInfo apiInfo;
|
||||
private HttpServletRequest request;
|
||||
private HttpServletResponse response;
|
||||
private boolean requestedFromTest;
|
||||
private Map<String, Object> parameters;
|
||||
private Map<String, Object> pathVariables;
|
||||
private MagicScriptContext magicScriptContext;
|
||||
private Object requestBody;
|
||||
private DebugRequest debugRequest;
|
||||
|
||||
private Map<String, Object> headers;
|
||||
|
||||
private RequestEntity() {
|
||||
|
||||
}
|
||||
|
||||
public static RequestEntity create() {
|
||||
return new RequestEntity();
|
||||
}
|
||||
|
||||
public ApiInfo getApiInfo() {
|
||||
return apiInfo;
|
||||
}
|
||||
|
||||
public RequestEntity info(ApiInfo apiInfo) {
|
||||
this.apiInfo = apiInfo;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpServletRequest getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
public RequestEntity request(HttpServletRequest request) {
|
||||
this.request = request;
|
||||
this.debugRequest = DebugRequest.create(request);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpServletResponse getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public RequestEntity response(HttpServletResponse response) {
|
||||
this.response = response;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isRequestedFromTest() {
|
||||
return requestedFromTest;
|
||||
}
|
||||
|
||||
public RequestEntity requestedFromTest(boolean requestedFromTest) {
|
||||
this.requestedFromTest = requestedFromTest;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isRequestedFromDebug() {
|
||||
return requestedFromTest && !this.debugRequest.getRequestedBreakpoints().isEmpty();
|
||||
}
|
||||
|
||||
public Map<String, Object> getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public RequestEntity parameters(Map<String, Object> parameters) {
|
||||
this.parameters = parameters;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, Object> getPathVariables() {
|
||||
return pathVariables;
|
||||
}
|
||||
|
||||
public RequestEntity pathVariables(Map<String, Object> pathVariables) {
|
||||
this.pathVariables = pathVariables;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Long getRequestTime() {
|
||||
return requestTime;
|
||||
}
|
||||
|
||||
public MagicScriptContext getMagicScriptContext() {
|
||||
return magicScriptContext;
|
||||
}
|
||||
|
||||
public RequestEntity setMagicScriptContext(MagicScriptContext magicScriptContext) {
|
||||
this.magicScriptContext = magicScriptContext;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, Object> getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
public RequestEntity setHeaders(Map<String, Object> headers) {
|
||||
this.headers = headers;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getRequestId() {
|
||||
return requestId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 RequestBody
|
||||
*/
|
||||
public Object getRequestBody() {
|
||||
return this.requestBody;
|
||||
}
|
||||
|
||||
public RequestEntity setRequestBody(Object requestBody) {
|
||||
this.requestBody = requestBody;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DebugRequest getDebugRequest() {
|
||||
return debugRequest;
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package org.ssssssss.magicapi.context;
|
||||
package org.ssssssss.magicapi.core.context;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.util.HashMap;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user