diff --git a/README.md b/README.md index 952bf2cc..585a61d6 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ magic-api 是一个基于Java的接口快速开发框架,编写接口将通过 org.ssssssss magic-api-spring-boot-starter - 1.4.2 + 1.4.3 ``` ## 修改application.properties diff --git a/magic-api-spring-boot-starter/pom.xml b/magic-api-spring-boot-starter/pom.xml index 8ba610b9..fc9ba7ba 100644 --- a/magic-api-spring-boot-starter/pom.xml +++ b/magic-api-spring-boot-starter/pom.xml @@ -6,7 +6,7 @@ org.ssssssss magic-api-parent - 1.4.2 + 1.4.3 magic-api-spring-boot-starter jar diff --git a/magic-api/pom.xml b/magic-api/pom.xml index 89de7fda..c4c20240 100644 --- a/magic-api/pom.xml +++ b/magic-api/pom.xml @@ -6,7 +6,7 @@ org.ssssssss magic-api-parent - 1.4.2 + 1.4.3 magic-api jar diff --git a/magic-api/src/main/java/org/ssssssss/magicapi/adapter/DialectAdapter.java b/magic-api/src/main/java/org/ssssssss/magicapi/adapter/DialectAdapter.java index f226ef43..bed03be5 100644 --- a/magic-api/src/main/java/org/ssssssss/magicapi/adapter/DialectAdapter.java +++ b/magic-api/src/main/java/org/ssssssss/magicapi/adapter/DialectAdapter.java @@ -28,7 +28,7 @@ public DialectAdapter() { } public void add(Dialect dialect) { - this.dialectList.add(dialect); + this.dialectList.add(0, dialect); } public Dialect getDialectFromConnection(Connection connection) { diff --git a/magic-api/src/main/java/org/ssssssss/magicapi/controller/MagicAPIController.java b/magic-api/src/main/java/org/ssssssss/magicapi/controller/MagicAPIController.java index e001b137..33e529c3 100644 --- a/magic-api/src/main/java/org/ssssssss/magicapi/controller/MagicAPIController.java +++ b/magic-api/src/main/java/org/ssssssss/magicapi/controller/MagicAPIController.java @@ -1,5 +1,6 @@ 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; @@ -8,6 +9,7 @@ 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; @@ -33,7 +35,9 @@ public MagicAPIController(MagicConfiguration configuration) { @ResponseBody @Valid(readonly = false) public JsonBean delete(HttpServletRequest request, String id) { - isTrue(allowVisit(request, Authorization.DELETE, getApiInfo(id)), PERMISSION_INVALID); + 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)); } @@ -102,6 +106,7 @@ public JsonBean apiMove(HttpServletRequest request, String id, String g // 新的分组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)); } @@ -113,10 +118,36 @@ public JsonBean apiMove(HttpServletRequest request, String id, String g @Valid(readonly = false) public JsonBean 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)); } - private ApiInfo getApiInfo(String id){ + /** + * 锁定接口 + */ + @RequestMapping("/lock") + @ResponseBody + @Valid(readonly = false) + public JsonBean 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 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; diff --git a/magic-api/src/main/java/org/ssssssss/magicapi/controller/MagicDebugHandler.java b/magic-api/src/main/java/org/ssssssss/magicapi/controller/MagicDebugHandler.java index 71b3e75d..e74a04c8 100644 --- a/magic-api/src/main/java/org/ssssssss/magicapi/controller/MagicDebugHandler.java +++ b/magic-api/src/main/java/org/ssssssss/magicapi/controller/MagicDebugHandler.java @@ -1,8 +1,8 @@ package org.ssssssss.magicapi.controller; +import org.apache.commons.lang3.StringUtils; import org.ssssssss.magicapi.config.Message; import org.ssssssss.magicapi.config.MessageType; -import org.ssssssss.magicapi.config.WebSocketSessionManager; import org.ssssssss.magicapi.model.MagicConsoleSession; import org.ssssssss.script.MagicScriptDebugContext; @@ -40,10 +40,13 @@ public boolean setBreakPoint(MagicConsoleSession session, String breakpoints) { * 当本机没有该Session时,通知其他机器处理 */ @Message(MessageType.RESUME_BREAKPOINT) - public boolean resumeBreakpoint(MagicConsoleSession session, String stepInto) { + public boolean resumeBreakpoint(MagicConsoleSession session, String stepInto, String breakpoints) { MagicScriptDebugContext context = session.getMagicScriptDebugContext(); if (context != null) { context.setStepInto("1".equals(stepInto)); + if(StringUtils.isNotBlank(breakpoints)){ + context.setBreakpoints(Stream.of(breakpoints.split("\\|")).map(Integer::valueOf).collect(Collectors.toList())); + } try { context.singal(); } catch (InterruptedException ignored) { diff --git a/magic-api/src/main/java/org/ssssssss/magicapi/controller/MagicFunctionController.java b/magic-api/src/main/java/org/ssssssss/magicapi/controller/MagicFunctionController.java index 93498bd6..70c4ee8a 100644 --- a/magic-api/src/main/java/org/ssssssss/magicapi/controller/MagicFunctionController.java +++ b/magic-api/src/main/java/org/ssssssss/magicapi/controller/MagicFunctionController.java @@ -1,5 +1,6 @@ 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; @@ -7,6 +8,7 @@ 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; @@ -63,6 +65,7 @@ public JsonBean move(HttpServletRequest request, String id, String grou 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)); } @@ -72,6 +75,10 @@ public JsonBean move(HttpServletRequest request, String id, String grou @Valid(readonly = false) public JsonBean 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)); } @@ -79,10 +86,34 @@ public JsonBean save(HttpServletRequest request, @RequestBody FunctionIn @ResponseBody @Valid(readonly = false) public JsonBean delete(HttpServletRequest request, String id) { - isTrue(allowVisit(request, Authorization.DELETE, getFunctionInfo(id)), PERMISSION_INVALID); + 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 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 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); diff --git a/magic-api/src/main/java/org/ssssssss/magicapi/controller/MagicGroupController.java b/magic-api/src/main/java/org/ssssssss/magicapi/controller/MagicGroupController.java index fb7637c0..babaca32 100644 --- a/magic-api/src/main/java/org/ssssssss/magicapi/controller/MagicGroupController.java +++ b/magic-api/src/main/java/org/ssssssss/magicapi/controller/MagicGroupController.java @@ -70,4 +70,22 @@ public JsonBean createGroup(HttpServletRequest request, @RequestBody Gro isTrue(allowVisit(request, Authorization.SAVE, group), PERMISSION_INVALID); return new JsonBean<>(magicAPIService.createGroup(group)); } + + /** + * 复制分组 + */ + @RequestMapping("/group/copy") + @ResponseBody + @Valid(readonly = false) + public JsonBean copyGroup(HttpServletRequest request, String src, String target) { + Group group = magicAPIService.getGroup(src); + notNull(group, GROUP_NOT_FOUND); + if(!"0".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)); + } } diff --git a/magic-api/src/main/java/org/ssssssss/magicapi/controller/RequestHandler.java b/magic-api/src/main/java/org/ssssssss/magicapi/controller/RequestHandler.java index d4f3ff91..79c77f87 100644 --- a/magic-api/src/main/java/org/ssssssss/magicapi/controller/RequestHandler.java +++ b/magic-api/src/main/java/org/ssssssss/magicapi/controller/RequestHandler.java @@ -340,7 +340,7 @@ private MagicScriptContext createMagicScriptContext(RequestEntity requestEntity, debugContext.setCallback(variables -> { List> varList = (List>) variables.get("variables"); varList.stream().filter(it -> it.containsKey("value")).forEach(variable -> { - variable.put("value", JsonUtils.toJsonString(variable.get("value"))); + variable.put("value", JsonUtils.toJsonStringWithoutLog(variable.get("value"))); }); WebSocketSessionManager.sendBySessionId(sessionId, BREAKPOINT, variables); }); diff --git a/magic-api/src/main/java/org/ssssssss/magicapi/interceptor/Authorization.java b/magic-api/src/main/java/org/ssssssss/magicapi/interceptor/Authorization.java index bbc94445..dd050205 100644 --- a/magic-api/src/main/java/org/ssssssss/magicapi/interceptor/Authorization.java +++ b/magic-api/src/main/java/org/ssssssss/magicapi/interceptor/Authorization.java @@ -28,5 +28,13 @@ public enum Authorization { /** * 执行推送动作 */ - PUSH + PUSH, + /** + * 锁定动作 + */ + LOCK, + /** + * 解锁动作 + */ + UNLOCK } \ No newline at end of file diff --git a/magic-api/src/main/java/org/ssssssss/magicapi/model/ApiInfo.java b/magic-api/src/main/java/org/ssssssss/magicapi/model/ApiInfo.java index ddbeb85d..783cf1e2 100644 --- a/magic-api/src/main/java/org/ssssssss/magicapi/model/ApiInfo.java +++ b/magic-api/src/main/java/org/ssssssss/magicapi/model/ApiInfo.java @@ -264,6 +264,7 @@ public ApiInfo simple(){ target.setGroupId(this.getGroupId()); target.setPath(this.getPath()); target.setMethod(this.getMethod()); + target.setLock(this.getLock()); return target; } @@ -311,6 +312,8 @@ public ApiInfo copy() { info.setPaths(this.paths); info.setRequestBodyDefinition(this.requestBodyDefinition); info.setResponseBodyDefinition(this.responseBodyDefinition); + info.setLock(this.lock); + info.setProperties(this.properties); return info; } } diff --git a/magic-api/src/main/java/org/ssssssss/magicapi/model/Constants.java b/magic-api/src/main/java/org/ssssssss/magicapi/model/Constants.java index b71cb2ea..e2adb541 100644 --- a/magic-api/src/main/java/org/ssssssss/magicapi/model/Constants.java +++ b/magic-api/src/main/java/org/ssssssss/magicapi/model/Constants.java @@ -107,6 +107,10 @@ public class Constants { public static final String UPLOAD_MODE_FULL = "full"; + public static final String LOCK = "1"; + + public static final String UNLOCK = "0"; + /** * 执行成功的code值 */ diff --git a/magic-api/src/main/java/org/ssssssss/magicapi/model/FunctionInfo.java b/magic-api/src/main/java/org/ssssssss/magicapi/model/FunctionInfo.java index ee27ab81..8ffd01d5 100644 --- a/magic-api/src/main/java/org/ssssssss/magicapi/model/FunctionInfo.java +++ b/magic-api/src/main/java/org/ssssssss/magicapi/model/FunctionInfo.java @@ -87,4 +87,20 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(id, path, script, name, groupId, parameters, description, returnType); } + + public FunctionInfo copy(){ + FunctionInfo info = new FunctionInfo(); + info.setId(this.id); + info.setName(this.name); + info.setGroupId(this.groupId); + info.setScript(this.script); + info.setDescription(this.description); + info.setParameters(this.parameters); + info.setPath(this.path); + info.setMappingPath(this.mappingPath); + info.setReturnType(this.returnType); + info.setProperties(this.properties); + info.setLock(this.lock); + return info; + } } diff --git a/magic-api/src/main/java/org/ssssssss/magicapi/model/JsonCodeConstants.java b/magic-api/src/main/java/org/ssssssss/magicapi/model/JsonCodeConstants.java index 45bad39d..f151fae1 100644 --- a/magic-api/src/main/java/org/ssssssss/magicapi/model/JsonCodeConstants.java +++ b/magic-api/src/main/java/org/ssssssss/magicapi/model/JsonCodeConstants.java @@ -19,6 +19,8 @@ public interface JsonCodeConstants { JsonCode NAME_CONFLICT = new JsonCode(0, "移动后名称会重复,请修改名称后在试。"); + JsonCode RESOURCE_LOCKED = new JsonCode(0, "当前资源已被锁定,请解锁后在操作。"); + JsonCode REQUEST_PATH_CONFLICT = new JsonCode(0, "该路径已被映射,请换一个请求方法或路径"); JsonCode FUNCTION_PATH_CONFLICT = new JsonCode(0, "该路径已被映射,请换一个请求方法或路径"); diff --git a/magic-api/src/main/java/org/ssssssss/magicapi/model/MagicEntity.java b/magic-api/src/main/java/org/ssssssss/magicapi/model/MagicEntity.java index 5006c96a..f3133da2 100644 --- a/magic-api/src/main/java/org/ssssssss/magicapi/model/MagicEntity.java +++ b/magic-api/src/main/java/org/ssssssss/magicapi/model/MagicEntity.java @@ -14,6 +14,8 @@ public class MagicEntity extends Attributes implements Cloneable { protected Long updateTime; + protected String lock; + public String getId() { return id; } @@ -62,6 +64,14 @@ public void setName(String name) { this.name = name; } + public String getLock() { + return lock; + } + + public void setLock(String lock) { + this.lock = lock; + } + public MagicEntity clone() { try { return (MagicEntity) super.clone(); diff --git a/magic-api/src/main/java/org/ssssssss/magicapi/modules/table/NamedTable.java b/magic-api/src/main/java/org/ssssssss/magicapi/modules/table/NamedTable.java index 81469f31..4c231bc1 100644 --- a/magic-api/src/main/java/org/ssssssss/magicapi/modules/table/NamedTable.java +++ b/magic-api/src/main/java/org/ssssssss/magicapi/modules/table/NamedTable.java @@ -48,10 +48,10 @@ public NamedTable(String tableName, SQLModule sqlModule, Function 2){ - this.logicDeleteValue = deleteValue.substring(1,deleteValue.length() - 1); - }else{ + if (deleteValue != null) { + if ((deleteValue.startsWith("'") || deleteValue.startsWith("\"")) && deleteValue.length() > 2) { + this.logicDeleteValue = deleteValue.substring(1, deleteValue.length() - 1); + } else { try { this.logicDeleteValue = Integer.parseInt(deleteValue); } catch (NumberFormatException e) { @@ -62,13 +62,13 @@ public NamedTable(String tableName, SQLModule sqlModule, Function columns){ - if(columns != null){ + public NamedTable excludes(List columns) { + if (columns != null) { excludeColumns.addAll(columns); } return this; @@ -169,7 +169,7 @@ public NamedTable groupBy(@Comment("要分组的列") String... columns) { } private Collection> filterNotBlanks() { - if(this.withBlank){ + if (this.withBlank) { return this.columns.entrySet() .stream() .filter(it -> !excludeColumns.contains(it.getKey())) @@ -212,7 +212,7 @@ public Object insert(@Comment("各项列和值") Map data) { @Comment("执行delete语句") public int delete() { - if(useLogic){ + if (useLogic) { Map dataMap = new HashMap<>(); dataMap.put(logicDeleteColumn, logicDeleteValue); return update(dataMap); @@ -315,7 +315,7 @@ private List buildWhere(StringBuilder builder) { where.ne(useLogic, logicDeleteColumn, logicDeleteValue); builder.append(where.getSql()); params.addAll(where.getParams()); - }else if(useLogic){ + } else if (useLogic) { where.ne(logicDeleteColumn, logicDeleteValue); builder.append(where.getSql()); params.addAll(where.getParams()); @@ -378,7 +378,7 @@ public int update(@Comment("各项列和值") Map data) { } @Comment("查询条数") - public int count(){ + public int count() { StringBuilder builder = new StringBuilder(); builder.append("select count(1) from ").append(tableName); List params = buildWhere(builder); @@ -386,7 +386,7 @@ public int count(){ } @Comment("判断是否存在") - public boolean exists(){ + public boolean exists() { return count() > 0; } diff --git a/magic-api/src/main/java/org/ssssssss/magicapi/provider/MagicAPIService.java b/magic-api/src/main/java/org/ssssssss/magicapi/provider/MagicAPIService.java index 65d7ab29..ceb2eb81 100644 --- a/magic-api/src/main/java/org/ssssssss/magicapi/provider/MagicAPIService.java +++ b/magic-api/src/main/java/org/ssssssss/magicapi/provider/MagicAPIService.java @@ -47,6 +47,14 @@ public interface MagicAPIService extends MagicModule { */ String saveApi(ApiInfo apiInfo); + boolean lockApi(String id); + + boolean unlockApi(String id); + + boolean lockFunction(String id); + + boolean unlockFunction(String id); + /** * 获取接口详情 * @@ -201,4 +209,6 @@ public interface MagicAPIService extends MagicModule { * 处理刷新通知 */ boolean processNotify(MagicNotify magicNotify); + + String copyGroup(String src, String target); } diff --git a/magic-api/src/main/java/org/ssssssss/magicapi/provider/StoreServiceProvider.java b/magic-api/src/main/java/org/ssssssss/magicapi/provider/StoreServiceProvider.java index 9ce5fb7a..e39ba979 100644 --- a/magic-api/src/main/java/org/ssssssss/magicapi/provider/StoreServiceProvider.java +++ b/magic-api/src/main/java/org/ssssssss/magicapi/provider/StoreServiceProvider.java @@ -4,6 +4,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.ssssssss.magicapi.adapter.Resource; +import org.ssssssss.magicapi.model.Constants; import org.ssssssss.magicapi.model.MagicEntity; import org.ssssssss.magicapi.utils.JsonUtils; @@ -84,6 +85,24 @@ public boolean delete(String id) { return false; } + public boolean lock(String id){ + T info = get(id); + if(info != null){ + info.setLock(Constants.LOCK); + return update(info); + } + return false; + } + + public boolean unlock(String id){ + T info = get(id); + if(info != null){ + info.setLock(Constants.UNLOCK); + return update(info); + } + return false; + } + /** * 查询所有(提供给页面,无需带script) */ diff --git a/magic-api/src/main/java/org/ssssssss/magicapi/provider/impl/DefaultMagicAPIService.java b/magic-api/src/main/java/org/ssssssss/magicapi/provider/impl/DefaultMagicAPIService.java index 2fa72da0..f8841490 100644 --- a/magic-api/src/main/java/org/ssssssss/magicapi/provider/impl/DefaultMagicAPIService.java +++ b/magic-api/src/main/java/org/ssssssss/magicapi/provider/impl/DefaultMagicAPIService.java @@ -123,7 +123,7 @@ public DefaultMagicAPIService(MappingHandlerMapping mappingHandlerMapping, MagicScriptContext.set(newContext); try { Object value = ScriptManager.executeScript(info.getScript(), newContext); - if(value instanceof ExitValue){ + if (value instanceof ExitValue) { throw new MagicExitException((ExitValue) value); } return value; @@ -228,6 +228,33 @@ public String saveApi(ApiInfo info) { return info.getId(); } + @Override + public boolean lockApi(String id) { + return lockWithNotify(apiServiceProvider.lock(id), id, NOTIFY_ACTION_API); + } + + @Override + public boolean unlockApi(String id) { + return lockWithNotify(apiServiceProvider.unlock(id), id, NOTIFY_ACTION_API); + } + + @Override + public boolean lockFunction(String id) { + return lockWithNotify(functionServiceProvider.lock(id), id, NOTIFY_ACTION_FUNCTION); + } + + @Override + public boolean unlockFunction(String id) { + return lockWithNotify(functionServiceProvider.unlock(id), id, NOTIFY_ACTION_FUNCTION); + } + + private boolean lockWithNotify(boolean success, String id, int type) { + if (success) { + magicNotifyService.sendNotify(new MagicNotify(instanceId, id, NOTIFY_ACTION_UPDATE, type)); + } + return success; + } + @Override public ApiInfo getApiInfo(String id) { return apiServiceProvider.get(id); @@ -288,7 +315,7 @@ public String saveFunction(FunctionInfo functionInfo) { isTrue(!functionServiceProvider.existsWithoutId(functionInfo), FUNCTION_ALREADY_EXISTS.format(functionInfo.getPath())); FunctionInfo oldInfo = functionServiceProvider.get(functionInfo.getId()); isTrue(functionServiceProvider.update(functionInfo), FUNCTION_SAVE_FAILURE); - if(!oldInfo.getScript().equals(functionInfo.getScript())){ + if (!oldInfo.getScript().equals(functionInfo.getScript())) { backupService.backup(functionInfo); } } @@ -555,7 +582,7 @@ public void upload(InputStream inputStream, String mode) throws IOException { // 检查上级分组是否存在 isTrue("0".equals(group.getParentId()) || groupServiceProvider.getGroupResource(group.getParentId()).exists(), GROUP_NOT_FOUND); } - if(checked) { + if (checked) { // 检测分组是否有冲突 groups.forEach(group -> { Resource resource; @@ -569,7 +596,7 @@ public void upload(InputStream inputStream, String mode) throws IOException { isTrue(src == null || src.getId().equals(group.getId()), GROUP_CONFLICT); } }); - }else{ + } else { Resource resource = workspace.getDirectory(PATH_API); resource.delete(); resource.mkdir(); @@ -710,7 +737,7 @@ public boolean processNotify(MagicNotify magicNotify) { case NOTIFY_ACTION_ALL: return processAllNotify(); } - switch (action){ + switch (action) { case NOTIFY_WS_C_S: return processWebSocketMessageReceived(magicNotify.getSessionId(), magicNotify.getContent()); case NOTIFY_WS_S_C: @@ -719,6 +746,32 @@ public boolean processNotify(MagicNotify magicNotify) { return false; } + @Override + public String copyGroup(String srcId, String target) { + Group src = getGroup(srcId); + src.setId(null); + src.setParentId(target); + src.setName(src.getName() + "(复制)"); + src.setPath(src.getPath() + "_copy"); + String newId = createGroup(src); + if (GROUP_TYPE_API.equals(src.getType())) { + apiServiceProvider.listWithScript() + .stream().filter(it -> srcId.equals(it.getGroupId())) + .map(ApiInfo::copy) + .peek(it -> it.setGroupId(newId)) + .peek(it -> it.setId(null)) + .forEach(this::saveApi); + } else { + functionServiceProvider.listWithScript() + .stream().filter(it -> srcId.equals(it.getGroupId())) + .map(FunctionInfo::copy) + .peek(it -> it.setGroupId(newId)) + .peek(it -> it.setId(null)) + .forEach(this::saveFunction); + } + return newId; + } + @Override public String getModuleName() { return "magic"; @@ -867,7 +920,7 @@ private void readPaths(Set groups, Set apiPaths, Set func for (Resource file : root.files(".ms")) { if (isApi) { ApiInfo info = apiServiceProvider.deserialize(file.read()); - if (checked){ + if (checked) { checkApiConflict(info); } apiInfos.add(info); @@ -875,7 +928,7 @@ private void readPaths(Set groups, Set apiPaths, Set func isTrue(apiPaths.add(apiPath), UPLOAD_PATH_CONFLICT.format(apiPath)); } else { FunctionInfo info = functionServiceProvider.deserialize(file.read()); - if (checked){ + if (checked) { checkFunctionConflict(info); } functionInfos.add(info); diff --git a/magic-api/src/main/java/org/ssssssss/magicapi/swagger/SwaggerEntity.java b/magic-api/src/main/java/org/ssssssss/magicapi/swagger/SwaggerEntity.java index 6f25919c..c6f5f895 100644 --- a/magic-api/src/main/java/org/ssssssss/magicapi/swagger/SwaggerEntity.java +++ b/magic-api/src/main/java/org/ssssssss/magicapi/swagger/SwaggerEntity.java @@ -221,7 +221,7 @@ public static class Path { private String description; - private String operationId = UUID.randomUUID().toString().replace("-", ""); + private String operationId; private List produces = new ArrayList<>(); @@ -247,7 +247,7 @@ public String getOperationId() { return operationId; } - public void setOperationId(String operationId) { + public Path(String operationId) { this.operationId = operationId; } diff --git a/magic-api/src/main/java/org/ssssssss/magicapi/swagger/SwaggerProvider.java b/magic-api/src/main/java/org/ssssssss/magicapi/swagger/SwaggerProvider.java index a0a69522..70612e87 100644 --- a/magic-api/src/main/java/org/ssssssss/magicapi/swagger/SwaggerProvider.java +++ b/magic-api/src/main/java/org/ssssssss/magicapi/swagger/SwaggerProvider.java @@ -71,7 +71,7 @@ public SwaggerEntity swaggerJson() { for (ApiInfo info : infos) { String groupName = groupServiceProvider.getFullName(info.getGroupId()).replace("/", "-"); String requestPath = "/" + mappingHandlerMapping.getRequestPath(info.getGroupId(), info.getPath()); - SwaggerEntity.Path path = new SwaggerEntity.Path(); + SwaggerEntity.Path path = new SwaggerEntity.Path(info.getId()); path.addTag(groupName); boolean hasBody = false; try { diff --git a/magic-api/src/main/java/org/ssssssss/magicapi/utils/JsonUtils.java b/magic-api/src/main/java/org/ssssssss/magicapi/utils/JsonUtils.java index 984c69b6..82d19376 100644 --- a/magic-api/src/main/java/org/ssssssss/magicapi/utils/JsonUtils.java +++ b/magic-api/src/main/java/org/ssssssss/magicapi/utils/JsonUtils.java @@ -31,6 +31,14 @@ public static String toJsonString(Object target) { } } + public static String toJsonStringWithoutLog(Object target) { + try { + return mapper.writeValueAsString(target); + } catch (Exception e) { + return target == null ? null : target.toString(); + } + } + public static T readValue(String json, TypeReference typeReference) { try { return mapper.readValue(json, typeReference); diff --git a/magic-editor/pom.xml b/magic-editor/pom.xml index ed904a66..6f5d53ab 100644 --- a/magic-editor/pom.xml +++ b/magic-editor/pom.xml @@ -6,7 +6,7 @@ org.ssssssss magic-api-parent - 1.4.2 + 1.4.3 magic-editor jar diff --git a/magic-editor/src/console/package.json b/magic-editor/src/console/package.json index a50845e7..2fef9d32 100644 --- a/magic-editor/src/console/package.json +++ b/magic-editor/src/console/package.json @@ -1,6 +1,6 @@ { "name": "magic-editor", - "version": "1.4.2", + "version": "1.4.3", "private": false, "description": "magic-editor for magic-api", "main": "dist/magic-editor.umd.min.js", diff --git a/magic-editor/src/console/src/assets/iconfont/iconfont.css b/magic-editor/src/console/src/assets/iconfont/iconfont.css index 7f4d36d1..0840d5fe 100644 --- a/magic-editor/src/console/src/assets/iconfont/iconfont.css +++ b/magic-editor/src/console/src/assets/iconfont/iconfont.css @@ -11,6 +11,14 @@ -moz-osx-font-smoothing: grayscale; } +.ma-icon-lock:before { + content: "\e64f"; +} + +.ma-icon-unlock:before { + content: "\e783"; +} + .ma-icon-collapse:before { content: "\e609"; } diff --git a/magic-editor/src/console/src/assets/iconfont/iconfont.ttf b/magic-editor/src/console/src/assets/iconfont/iconfont.ttf index 2003f576..5c9726b4 100644 Binary files a/magic-editor/src/console/src/assets/iconfont/iconfont.ttf and b/magic-editor/src/console/src/assets/iconfont/iconfont.ttf differ diff --git a/magic-editor/src/console/src/components/editor/magic-script-editor.vue b/magic-editor/src/console/src/components/editor/magic-script-editor.vue index 2d9ca42e..f2a0c225 100644 --- a/magic-editor/src/console/src/components/editor/magic-script-editor.vue +++ b/magic-editor/src/console/src/components/editor/magic-script-editor.vue @@ -18,7 +18,8 @@ > - {{item.name}}* + {{item.name}} + * @@ -548,7 +549,7 @@ export default { requestConfig.headers['Content-Type'] = 'multipart/form-data'; let formData = new FormData() Object.keys(params).forEach(key => { - let value = requestConfig.data[key]; + let value = params[key]; if(value instanceof FileList){ value.forEach(file => formData.append(key, file, file.name)) }else{ @@ -620,7 +621,12 @@ export default { target.ext.debugDecorations && this.editor.deltaDecorations(target.ext.debugDecorations, []) target.ext.debuging = false target.ext.variables = [] - bus.$emit('message', 'resume_breakpoint', step === true ? '1' : '0') + bus.$emit('message', 'resume_breakpoint', (step === true ? '1' : '0')+ ',' + this.editor + .getModel() + .getAllDecorations() + .filter(it => it.options.linesDecorationsClassName === 'breakpoints') + .map(it => it.range.startLineNumber) + .join('|')) } }, doStepInto() { @@ -958,12 +964,14 @@ ul li.draggableTargetItem { background: var(--hover-background); } -ul li i { +ul li i:not(.ma-icon-lock) { color: var(--icon-color); margin-left: 5px; font-size: 0.5em; } - +.ma-icon-lock{ + margin-left: 5px; +} .ma-editor-container > div { flex: 1; } diff --git a/magic-editor/src/console/src/components/layout/magic-group.vue b/magic-editor/src/console/src/components/layout/magic-group.vue index f2a16d98..8160a7bf 100644 --- a/magic-editor/src/console/src/components/layout/magic-group.vue +++ b/magic-editor/src/console/src/components/layout/magic-group.vue @@ -90,7 +90,8 @@ import MagicInput from '@/components/common/magic-input.vue' import MagicSelect from '@/components/common/magic-select.vue' import request from "@/api/request" import { requestGroup } from '@/scripts/utils.js' -import bus from "@/scripts/bus"; +import bus from "@/scripts/bus" +import contants from "@/scripts/contants.js" export default { name: 'MagicGroup', @@ -128,6 +129,8 @@ export default { mounted() { let map = {} request.send('/options').success(data => { + data = data || [] + data = data.concat(contants.OPTIONS) this.defaultOptions = data&&data.map(e => { let item = {text: e[0], value: e[0], description: e[1], defaultValue: e[2]} this.optionsMap[item.value] = item; diff --git a/magic-editor/src/console/src/components/layout/magic-option.vue b/magic-editor/src/console/src/components/layout/magic-option.vue index e8afb0d3..de8979fc 100644 --- a/magic-editor/src/console/src/components/layout/magic-option.vue +++ b/magic-editor/src/console/src/components/layout/magic-option.vue @@ -35,6 +35,7 @@ import request from '@/api/request.js' import MagicInput from '@/components/common/magic-input.vue' import MagicSelect from '@/components/common/magic-select.vue' +import contants from "@/scripts/contants.js" export default { name: 'MagicOption', @@ -55,6 +56,8 @@ export default { mounted() { let map = {} request.send('/options').success(data => { + data = data || [] + data = data.concat(contants.OPTIONS) this.defaultOptions = data&&data.map(e => { let item = {text: e[0], value: e[0], description: e[1], defaultValue: e[2]} this.optionsMap[item.value] = item; diff --git a/magic-editor/src/console/src/components/magic-editor.vue b/magic-editor/src/console/src/components/magic-editor.vue index 2595bc02..3a84fee9 100644 --- a/magic-editor/src/console/src/components/magic-editor.vue +++ b/magic-editor/src/console/src/components/magic-editor.vue @@ -105,7 +105,7 @@ export default { if (contants.BASE_URL.startsWith('http')) { // http开头 link = contants.BASE_URL } else if (contants.BASE_URL.startsWith('/')) { // / 开头的 - link = link + contants.BASE_URL + link = `${location.protocol}/${location.host}${contants.BASE_URL}` } else { link = link + '/' + contants.BASE_URL } @@ -114,6 +114,9 @@ export default { }) bus.$on('ws_open', () => bus.$emit('message', 'login', contants.HEADER_MAGIC_TOKEN_VALUE)) contants.DEFAULT_EXPAND = this.config.defaultExpand !== false + contants.JDBC_DRIVERS = this.config.jdbcDrivers || [] + contants.DATASOURCE_TYPES = this.config.datasourceTypes || [] + contants.OPTIONS = this.config.options || [] this.config.version = contants.MAGIC_API_VERSION_TEXT this.config.title = this.config.title || 'magic-api' this.config.themes = this.config.themes || {} diff --git a/magic-editor/src/console/src/components/resources/magic-api-list.vue b/magic-editor/src/console/src/components/resources/magic-api-list.vue index 5eb60842..074a52c8 100644 --- a/magic-editor/src/console/src/components/resources/magic-api-list.vue +++ b/magic-editor/src/console/src/components/resources/magic-api-list.vue @@ -64,6 +64,7 @@ ({{ item.path }}) + @@ -82,6 +83,15 @@ + + + + @@ -91,6 +101,7 @@ import MagicTree from '../common/magic-tree.vue' import request from '@/api/request.js' import MagicDialog from '@/components/common/modal/magic-dialog.vue' import MagicInput from '@/components/common/magic-input.vue' +import MagicGroupChoose from '@/components/resources/magic-group-choose.vue' import { replaceURL, download as downloadFile, requestGroup, goToAnchor, deepClone } from '@/scripts/utils.js' import contants from '@/scripts/contants.js' import Key from '@/scripts/hotkey.js' @@ -103,7 +114,8 @@ export default { components: { MagicTree, MagicDialog, - MagicInput + MagicInput, + MagicGroupChoose }, data() { return { @@ -116,6 +128,8 @@ export default { tree: [], // 数据排序规则,true:升序,false:降序 treeSort: true, + groupChooseVisible: false, + srcId: '', // 新建分组对象 createGroupObj: { visible: false, @@ -320,6 +334,7 @@ export default { parameters: null, paths: null, option: null, + lock: '0', requestBody: null, headers: null, responseBody: null, @@ -356,6 +371,15 @@ export default { this.openCreateGroupModal(item) } }, + { + label: '复制分组', + icon: 'ma-icon-copy', + onClick: () => { + this.srcId = item.id + this.groupChooseVisible = true + this.$refs.groupChoose.initData() + } + }, { label: '删除分组', icon: 'ma-icon-delete', @@ -449,9 +473,27 @@ export default { this.copyPathToClipboard(item, true) } }, + { + label: `${item.lock === '1' ? '解锁' : '锁定'}`, + icon: `ma-icon-${item.lock === '1' ? 'unlock' : 'lock'}`, + onClick: () => { + let action = item.lock === '1' ? '解锁接口' : '锁定接口'; + request.send(item.lock === '1' ? 'unlock' : 'lock', {id: item.id}).success(data => { + if (data) { + bus.$emit('status', `${action}「${item.name}(${item.path})」`) + bus.$emit('report', `api_${item.lock === '1' ? 'unlock' : 'lock'}`) + item['lock'] = item.lock === '1' ? '0' : '1'; + this.changeForceUpdate() + } else { + this.$magicAlert({content: `${action}失败`}) + } + }) + } + }, { label: '刷新接口', icon: 'ma-icon-refresh', + divided: true, onClick: () => { this.initData() } @@ -473,6 +515,15 @@ export default { }) return false }, + copyGroup(){ + let target = this.$refs.groupChoose.getSelected() + if(target && this.srcId){ + this.groupChooseVisible = false + request.send('group/copy', { src: this.srcId, target }).success(() => { + this.initData(); + }) + } + }, // 删除接口 deleteApiInfo(item) { bus.$emit('status', `准备删除接口「${item.name}(${item.path})」`) diff --git a/magic-editor/src/console/src/components/resources/magic-datasource-list.vue b/magic-editor/src/console/src/components/resources/magic-datasource-list.vue index b82406d2..16e9c149 100644 --- a/magic-editor/src/console/src/components/resources/magic-datasource-list.vue +++ b/magic-editor/src/console/src/components/resources/magic-datasource-list.vue @@ -83,6 +83,7 @@ + + + diff --git a/magic-editor/src/console/src/components/resources/magic-resource.css b/magic-editor/src/console/src/components/resources/magic-resource.css index 391cf68e..f0499954 100644 --- a/magic-editor/src/console/src/components/resources/magic-resource.css +++ b/magic-editor/src/console/src/components/resources/magic-resource.css @@ -41,10 +41,21 @@ .ma-tree-wrapper span { color: var(--toolbox-list-span-color); + display: inline-block; + height: 22px; + line-height: 22px; +} + +.ma-tree-wrapper .ma-icon-lock { + color: var(--toolbox-list-label-color); + margin-left: 5px; } .ma-tree-wrapper label { color: var(--toolbox-list-label-color); + display: inline-block; + height: 22px; + line-height: 22px; } .ma-tree-wrapper .ma-tree-toolbar-search { diff --git a/magic-editor/src/console/src/scripts/contants.js b/magic-editor/src/console/src/scripts/contants.js index 7cd5f075..878880cd 100644 --- a/magic-editor/src/console/src/scripts/contants.js +++ b/magic-editor/src/console/src/scripts/contants.js @@ -22,6 +22,9 @@ const contants = { RESPONSE_CODE_SCRIPT_ERROR: -1000, RESPONSE_NO_PERMISSION: -10, DEFAULT_EXPAND: true, + JDBC_DRIVERS: [], + DATASOURCE_TYPES: [], + OPTIONS: [], config: {} } diff --git a/pom.xml b/pom.xml index b4e1ef61..96068906 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 org.ssssssss magic-api-parent - 1.4.2 + 1.4.3 pom magic-api-parent auto generate http api @@ -30,7 +30,7 @@ 2.4.5 - 1.5.2 + 1.5.3 1.21 2.7 1.6