From f4b277125ce57f3bfd2bec8a48e125950f2824bb Mon Sep 17 00:00:00 2001
From: AgAngle <1323481023@qq.com>
Date: Mon, 15 Jul 2024 18:04:52 +0800
Subject: [PATCH] =?UTF-8?q?feat(=E6=8E=A5=E5=8F=A3=E6=B5=8B=E8=AF=95):=20j?=
=?UTF-8?q?son-schema=E8=87=AA=E5=8A=A8=E7=94=9F=E6=88=90=E6=B5=8B?=
=?UTF-8?q?=E8=AF=95=E6=95=B0=E6=8D=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
--story=1015366 --user=陈建星 【接口测试】json-schema组件 https://www.tapd.cn/55049933/s/1547748
---
backend/services/api-test/pom.xml | 6 +
.../definition/ApiDefinitionController.java | 9 +-
.../api/dto/schema/JsonSchemaItem.java | 2 +-
.../api/parser/api/Swagger3Parser.java | 7 +-
.../definition/ApiDefinitionService.java | 8 +
.../api/utils/JsonSchemaBuilder.java | 168 ++++++++++++++----
.../ApiDefinitionControllerTests.java | 35 +++-
.../project/constants/PropertyConstant.java | 7 +
.../src/api/modules/api-test/management.ts | 6 +
.../src/api/requrls/api-test/management.ts | 3 +-
.../components/pure/ms-json-schema/index.vue | 6 +-
.../components/requestComposition/body.vue | 4 +-
.../requestComposition/response/edit.vue | 4 +-
pom.xml | 2 +-
14 files changed, 215 insertions(+), 52 deletions(-)
diff --git a/backend/services/api-test/pom.xml b/backend/services/api-test/pom.xml
index cb462d66f4..c7f7208769 100644
--- a/backend/services/api-test/pom.xml
+++ b/backend/services/api-test/pom.xml
@@ -94,6 +94,12 @@
+
+
+ com.github.krraghavan
+ xeger
+ ${xeger.version}
+
diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiDefinitionController.java b/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiDefinitionController.java
index 7cd1e3efa0..0c4f8bc06b 100644
--- a/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiDefinitionController.java
+++ b/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiDefinitionController.java
@@ -278,13 +278,20 @@ public class ApiDefinitionController {
return apiFileResourceService.transfer(request, SessionUtils.getUserId(), apiDefinitionDir);
}
- @PostMapping("/preview")
+ @PostMapping("/json-schema/preview")
@Operation(summary = "接口测试-接口管理-接口-json-schema-预览")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ)
public String preview(@RequestBody JsonSchemaItem jsonSchemaItem) {
return apiDefinitionService.preview(jsonSchemaItem);
}
+ @PostMapping("/json-schema/auto-generate")
+ @Operation(summary = "接口测试-接口管理-接口-json-schema-自动生成测试数据")
+ @RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ)
+ public String jsonSchemaAutoGenerate(@RequestBody JsonSchemaItem jsonSchemaItem) {
+ return apiDefinitionService.jsonSchemaAutoGenerate(jsonSchemaItem);
+ }
+
@PostMapping("/debug")
@Operation(summary = "接口调试")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_EXECUTE)
diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/schema/JsonSchemaItem.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/schema/JsonSchemaItem.java
index e102493be5..bbbdd304ef 100644
--- a/backend/services/api-test/src/main/java/io/metersphere/api/dto/schema/JsonSchemaItem.java
+++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/schema/JsonSchemaItem.java
@@ -106,7 +106,7 @@ public class JsonSchemaItem {
/**
* 参数值的枚举
*/
- private List extends Object> enumValues;
+ private List enumValues;
/**
* 是否启用
*/
diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/api/Swagger3Parser.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/api/Swagger3Parser.java
index 3b4abe3164..e035b308a5 100644
--- a/backend/services/api-test/src/main/java/io/metersphere/api/parser/api/Swagger3Parser.java
+++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/api/Swagger3Parser.java
@@ -310,7 +310,7 @@ public class Swagger3Parser extends ApiImportAbstractParser
} else {
String jsonString = JSON.toJSONString(jsonSchemaItem);
if (StringUtils.isNotBlank(jsonString)) {
- jsonBody.setJsonValue(JsonSchemaBuilder.jsonSchemaToJson(jsonString));
+ jsonBody.setJsonValue(JsonSchemaBuilder.jsonSchemaToJson(jsonString, true));
}
}
return jsonBody;
@@ -663,7 +663,10 @@ public class Swagger3Parser extends ApiImportAbstractParser
jsonSchemaItem.setFormat(StringUtils.isNotBlank(integerSchema.getFormat()) ? integerSchema.getFormat() : StringUtils.EMPTY);
jsonSchemaItem.setMaximum(integerSchema.getMaximum());
jsonSchemaItem.setMinimum(integerSchema.getMinimum());
- jsonSchemaItem.setEnumValues(integerSchema.getEnum());
+ List enumValues = integerSchema.getEnum();
+ if (CollectionUtils.isNotEmpty(enumValues)) {
+ jsonSchemaItem.setEnumValues(enumValues.stream().map(item -> item.toString()).toList());
+ }
return jsonSchemaItem;
}
diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionService.java
index 52df63e36a..b0982e77b0 100644
--- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionService.java
+++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionService.java
@@ -1195,6 +1195,14 @@ public class ApiDefinitionService extends MoveNodeService {
return JsonSchemaBuilder.preview(JSON.toJSONString(jsonSchemaItem));
}
+ public String jsonSchemaAutoGenerate(JsonSchemaItem jsonSchemaItem) {
+ if (BooleanUtils.isFalse(jsonSchemaItem.getEnable())) {
+ return "{}";
+ }
+ filterDisableItem(jsonSchemaItem);
+ return JsonSchemaBuilder.jsonSchemaAutoGenerate(JSON.toJSONString(jsonSchemaItem));
+ }
+
private void filterDisableItem(JsonSchemaItem jsonSchemaItem) {
if (isObjectItem(jsonSchemaItem)) {
Map properties = jsonSchemaItem.getProperties();
diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/utils/JsonSchemaBuilder.java b/backend/services/api-test/src/main/java/io/metersphere/api/utils/JsonSchemaBuilder.java
index 05d0dbcbc4..2221ce9769 100644
--- a/backend/services/api-test/src/main/java/io/metersphere/api/utils/JsonSchemaBuilder.java
+++ b/backend/services/api-test/src/main/java/io/metersphere/api/utils/JsonSchemaBuilder.java
@@ -2,26 +2,26 @@ package io.metersphere.api.utils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.*;
-import io.metersphere.jmeter.mock.Mock;
import io.metersphere.project.constants.PropertyConstant;
+import nl.flotsam.xeger.Xeger;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.text.RandomStringGenerator;
import org.jetbrains.annotations.NotNull;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import java.util.Random;
public class JsonSchemaBuilder {
- public static String jsonSchemaToJson(String jsonSchemaString) {
+ public static String jsonSchemaToJson(String jsonSchemaString, boolean isPreview) {
// 解析 JSON Schema 字符串为 JsonNode
JsonNode jsonSchemaNode = ApiDataUtils.readTree(jsonSchemaString);
Map processMap = new HashMap<>();
// 生成符合 JSON Schema 的 JSON
- JsonNode jsonNode = generateJson(jsonSchemaNode, processMap);
+ JsonNode jsonNode = generateJson(jsonSchemaNode, processMap, isPreview);
String jsonString = ApiDataUtils.writerWithDefaultPrettyPrinter(jsonNode);
if (MapUtils.isNotEmpty(processMap)) {
for (String str : processMap.keySet()) {
@@ -31,13 +31,13 @@ public class JsonSchemaBuilder {
return jsonString;
}
- private static JsonNode generateJson(JsonNode jsonSchemaNode, Map processMap) {
+ private static JsonNode generateJson(JsonNode jsonSchemaNode, Map processMap, boolean isPreview) {
ObjectNode jsonNode = ApiDataUtils.createObjectNode();
if (jsonSchemaNode instanceof NullNode) {
return NullNode.getInstance();
}
- String type = jsonSchemaNode.get(PropertyConstant.TYPE) == null ? StringUtils.EMPTY : jsonSchemaNode.get(PropertyConstant.TYPE).asText();
+ String type = getPropertyTextValue(jsonSchemaNode, PropertyConstant.TYPE);
if (StringUtils.equals(type, PropertyConstant.OBJECT)) {
JsonNode propertiesNode = jsonSchemaNode.get(PropertyConstant.PROPERTIES);
// 遍历 properties
@@ -46,7 +46,8 @@ public class JsonSchemaBuilder {
String propertyName = entry.getKey();
JsonNode propertyNode = entry.getValue();
// 根据属性类型生成对应的值
- JsonNode valueNode = generateValue(entry.getKey(), propertyNode, processMap);
+ JsonNode valueNode = isPreview ? generateValueForPreview(entry.getKey(), propertyNode, processMap)
+ : generateValue(entry.getKey(), propertyNode, processMap);
// 将属性和值添加到 JSON 对象节点
jsonNode.set(propertyName, valueNode);
});
@@ -55,23 +56,27 @@ public class JsonSchemaBuilder {
JsonNode items = jsonSchemaNode.get(PropertyConstant.ITEMS);
if (items != null) {
ArrayNode arrayNode = new ArrayNode(JsonNodeFactory.instance);
- items.forEach(item -> arrayNode.add(generateValue(null, item, processMap)));
+ items.forEach(item -> {
+ JsonNode valueNode = isPreview ? generateValueForPreview(null, item, processMap)
+ : generateValue(null, item, processMap);
+ arrayNode.add(valueNode);
+ });
return arrayNode;
}
}
return jsonNode;
}
- private static JsonNode generateValue(String propertyName, JsonNode propertyNode, Map processMap) {
+ private static JsonNode generateValueForPreview(String propertyName, JsonNode propertyNode, Map processMap) {
// 获取属性类型
if (propertyNode instanceof NullNode) {
return NullNode.getInstance();
}
- String type = propertyNode.get(PropertyConstant.TYPE) == null ? StringUtils.EMPTY : propertyNode.get(PropertyConstant.TYPE).asText();
- String value = propertyNode.get(PropertyConstant.EXAMPLE) == null ? StringUtils.EMPTY : propertyNode.get(PropertyConstant.EXAMPLE).asText();
+ String type = getPropertyTextValue(propertyNode, PropertyConstant.TYPE);
+ String value = getPropertyTextValue(propertyNode, PropertyConstant.EXAMPLE);
return switch (type) {
case PropertyConstant.STRING ->
- new TextNode(!StringUtils.equals(value, PropertyConstant.NULL) ? value : "string");
+ new TextNode(StringUtils.isBlank(value) ? "string" : value);
case PropertyConstant.INTEGER -> {
if (isVariable(value)) {
yield getJsonNodes(propertyName, processMap, value);
@@ -97,8 +102,112 @@ public class JsonSchemaBuilder {
yield BooleanNode.valueOf(propertyNode.get(PropertyConstant.EXAMPLE).asBoolean());
}
}
+ case PropertyConstant.OBJECT -> generateJson(propertyNode, processMap, true);
+ case PropertyConstant.ARRAY -> {
+ ArrayNode arrayNode = new ArrayNode(JsonNodeFactory.instance);
+ JsonNode items = propertyNode.get(PropertyConstant.ITEMS);
+ if (items != null) {
+ items.forEach(item -> arrayNode.add(generateValueForPreview(null, item, processMap)));
+ }
+ yield arrayNode;
+ }
+ default -> NullNode.getInstance();
+ };
- case PropertyConstant.OBJECT -> generateJson(propertyNode, processMap);
+ }
+
+ private static JsonNode generateValue(String propertyName, JsonNode propertyNode, Map processMap) {
+ // 获取属性类型
+ if (propertyNode instanceof NullNode) {
+ return NullNode.getInstance();
+ }
+ String type = getPropertyTextValue(propertyNode, PropertyConstant.TYPE);
+ String value = getPropertyTextValue(propertyNode, PropertyConstant.EXAMPLE);
+ return switch (type) {
+ case PropertyConstant.STRING -> {
+ if (StringUtils.isBlank(value)) {
+ JsonNode enumValues = propertyNode.get(PropertyConstant.ENUM_VALUES);
+ JsonNode defaultValue = propertyNode.get(PropertyConstant.DEFAULT_VALUE);
+ JsonNode pattern = propertyNode.get(PropertyConstant.PATTERN);
+ JsonNode maxLength = propertyNode.get(PropertyConstant.MAX_LENGTH);
+ JsonNode minLength = propertyNode.get(PropertyConstant.MIN_LENGTH);
+ int max = isTextNotBlank(maxLength) ? maxLength.asInt() : 20;
+ int min = isTextNotBlank(minLength) ? minLength.asInt() : 1;
+ if (enumValues != null && enumValues instanceof ArrayNode) {
+ value = enumValues.get(new Random().nextInt(enumValues.size())).asText();
+ } else if (isTextNotBlank(defaultValue)) {
+ value = defaultValue.asText();
+ } else if (isTextNotBlank(pattern)) {
+ Xeger generator = new Xeger(pattern.asText());
+ value = generator.generate();
+ } else {
+ value = RandomStringGenerator.builder().withinRange('0', 'z').build().generate(new Random().nextInt(max - min + 1) + min);
+ }
+ }
+ yield new TextNode(value);
+ }
+ case PropertyConstant.INTEGER -> {
+ if (StringUtils.isBlank(value)) {
+ JsonNode enumValues = propertyNode.get(PropertyConstant.ENUM_VALUES);
+ JsonNode defaultValue = propertyNode.get(PropertyConstant.DEFAULT_VALUE);
+ if (enumValues != null && enumValues instanceof ArrayNode) {
+ value = enumValues.get(new Random().nextInt(enumValues.size())).asText();
+ } else if (isTextNotBlank(defaultValue)) {
+ value = defaultValue.asText();
+ } else {
+ JsonNode maximum = propertyNode.get(PropertyConstant.MAXIMUM);
+ JsonNode minimum = propertyNode.get(PropertyConstant.MINIMUM);
+ int max = isTextNotBlank(maximum) ? maximum.asInt() : Integer.MAX_VALUE;
+ int min = isTextNotBlank(minimum) ? minimum.asInt() : Integer.MIN_VALUE;
+ // 这里减去负数可能超过整型最大值,使用 Long 类型
+ value = new Random().nextLong(Long.valueOf(max) - Long.valueOf(min)) + min + StringUtils.EMPTY;
+ }
+ } else {
+ if (isVariable(value)) {
+ yield getJsonNodes(propertyName, processMap, value);
+ }
+ }
+ try {
+ yield new IntNode(Integer.valueOf(value));
+ } catch (Exception e) {
+ yield new IntNode(propertyNode.get(PropertyConstant.EXAMPLE).asInt());
+ }
+ }
+ case PropertyConstant.NUMBER -> {
+ if (StringUtils.isBlank(value)) {
+ JsonNode enumValues = propertyNode.get(PropertyConstant.ENUM_VALUES);
+ JsonNode defaultValue = propertyNode.get(PropertyConstant.DEFAULT_VALUE);
+ if (enumValues != null && enumValues instanceof ArrayNode) {
+ value = enumValues.get(new Random().nextInt(enumValues.size())).asText();
+ } else if (isTextNotBlank(defaultValue)) {
+ value = defaultValue.asText();
+ } else {
+ JsonNode maximum = propertyNode.get(PropertyConstant.MAXIMUM);
+ JsonNode minimum = propertyNode.get(PropertyConstant.MINIMUM);
+ BigDecimal max = isTextNotBlank(maximum) ? new BigDecimal(maximum.asText()) : new BigDecimal(String.valueOf(Float.MAX_VALUE));
+ BigDecimal min = isTextNotBlank(minimum) ? new BigDecimal(minimum.asText()) : new BigDecimal(String.valueOf(Float.MIN_VALUE));
+ BigDecimal randomBigDecimal = min.add(new BigDecimal(String.valueOf(Math.random())).multiply(max.subtract(min)));
+ yield new DecimalNode(randomBigDecimal.setScale(2, BigDecimal.ROUND_HALF_UP));
+ }
+ } else {
+ if (isVariable(value)) {
+ yield getJsonNodes(propertyName, processMap, value);
+ }
+ }
+ try {
+ yield new DecimalNode(new BigDecimal(value));
+ } catch (Exception e) {
+ yield new DecimalNode(propertyNode.get(PropertyConstant.EXAMPLE).decimalValue());
+ }
+ }
+ case PropertyConstant.BOOLEAN -> {
+ if (isVariable(value)) {
+ yield getJsonNodes(propertyName, processMap, value);
+ } else {
+ yield BooleanNode.valueOf(propertyNode.get(PropertyConstant.EXAMPLE).asBoolean());
+ }
+ }
+ case PropertyConstant.OBJECT -> generateJson(propertyNode, processMap, true);
case PropertyConstant.ARRAY -> {
ArrayNode arrayNode = new ArrayNode(JsonNodeFactory.instance);
JsonNode items = propertyNode.get(PropertyConstant.ITEMS);
@@ -112,6 +221,14 @@ public class JsonSchemaBuilder {
}
+ private static boolean isTextNotBlank(JsonNode jsonNode) {
+ return jsonNode != null && !(jsonNode instanceof NullNode) && StringUtils.isNotBlank(jsonNode.asText());
+ }
+
+ private static String getPropertyTextValue(JsonNode propertyNode, String key) {
+ return propertyNode.get(key) == null ? StringUtils.EMPTY : propertyNode.get(key).asText();
+ }
+
private static boolean isVariable(String value) {
return !StringUtils.equals(value, PropertyConstant.NULL) && (value.startsWith("@") || value.startsWith("${"));
}
@@ -125,25 +242,10 @@ public class JsonSchemaBuilder {
}
public static String preview(String jsonSchema) {
- String jsonString = jsonSchemaToJson(jsonSchema);
- //需要匹配到mock函数 然后换成mock数据
- if (StringUtils.isNotBlank(jsonString)) {
- String pattern = "@[a-zA-Z\\\\(|,'-\\\\d ]*[a-zA-Z)-9),\\\\\"]";
- Pattern regex = Pattern.compile(pattern);
- Matcher matcher = regex.matcher(jsonString);
- while (matcher.find()) {
- //取出group的最后一个字符 主要是防止 @string|number 和 @string 这种情况
- String group = matcher.group();
- String lastChar = null;
- if (group.endsWith(",") || group.endsWith("\"")) {
- lastChar = group.substring(group.length() - 1);
- group = group.substring(0, group.length() - 1);
- }
- jsonString = jsonString.replace(matcher.group(),
- StringUtils.join(Mock.calculate(group), lastChar));
- }
- }
- return jsonString;
+ return jsonSchemaToJson(jsonSchema, true);
+ }
+ public static String jsonSchemaAutoGenerate(String jsonString) {
+ return jsonSchemaToJson(jsonString, false);
}
}
diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDefinitionControllerTests.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDefinitionControllerTests.java
index 6f0fe1a27c..4907f813c0 100644
--- a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDefinitionControllerTests.java
+++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDefinitionControllerTests.java
@@ -117,6 +117,8 @@ public class ApiDefinitionControllerTests extends BaseTest {
private static final String ALL_API = "api_definition_module.api.all";
private static final String UNPLANNED_API = "api_unplanned_request";
+ private static final String JSON_SCHEMA_PREVIEW = "json-schema/preview";
+ private static final String JSON_SCHEMA_AUTO_GENERATE = "json-schema/auto-generate";
private static final String EXPORT = "/export/";
private static ApiDefinition apiDefinition;
@@ -1718,12 +1720,12 @@ public class ApiDefinitionControllerTests extends BaseTest {
{"example":null,"id":null,"title":null,"type":"object","description":null,"items":null,"properties":{"id":{"example":10,"id":null,"title":null,"type":"integer","description":"","items":null,"properties":null,"additionalProperties":null,"required":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"schema":null,"format":"int64","enumString":null,"enumInteger":null,"enumNumber":null,"extensions":null},"name":{"example":"@string","id":null,"title":null,"type":"string","description":"","items":null,"properties":null,"additionalProperties":null,"required":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"schema":null,"format":"","enumString":null,"enumInteger":null,"enumNumber":null,"extensions":null},"category":{"example":null,"id":null,"title":null,"type":"object","description":null,"items":null,"properties":{"id":{"example":"@integer","id":null,"title":null,"type":"integer","description":"","items":null,"properties":null,"additionalProperties":null,"required":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"schema":null,"format":"int64","enumString":null,"enumInteger":null,"enumNumber":null,"extensions":null},"name":{"example":"Dogs","id":null,"title":null,"type":"string","description":"","items":null,"properties":null,"additionalProperties":null,"required":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"schema":null,"format":"","enumString":null,"enumInteger":null,"enumNumber":null,"extensions":null}},"additionalProperties":null,"required":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"schema":null,"format":null,"enumString":null,"enumInteger":null,"enumNumber":null,"extensions":null},"photoUrls":{"example":null,"id":null,"title":null,"type":"array","description":null,"items":[{"example":null,"id":null,"title":null,"type":"string","description":"","items":null,"properties":null,"additionalProperties":null,"required":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"schema":null,"format":"","enumString":null,"enumInteger":null,"enumNumber":null,"extensions":null}],"properties":null,"additionalProperties":null,"required":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"schema":null,"format":null,"enumString":null,"enumInteger":null,"enumNumber":null,"extensions":null},"tags":{"example":null,"id":null,"title":null,"type":"array","description":null,"items":[{"example":null,"id":null,"title":null,"type":"object","description":null,"items":null,"properties":{"id":{"example":null,"id":null,"title":null,"type":"integer","description":"","items":null,"properties":null,"additionalProperties":null,"required":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"schema":null,"format":"int64","enumString":null,"enumInteger":null,"enumNumber":null,"extensions":null},"name":{"example":null,"id":null,"title":null,"type":"string","description":"","items":null,"properties":null,"additionalProperties":null,"required":null,"pattern":null,"maxLength":null,"minLength":null,"mainimum":null,"maximum":null,"schema":null,"format":"","enumString":null,"enumInteger":null,"enumNumber":null,"extensions":null}},"additionalProperties":null,"required":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"schema":null,"format":null,"enumString":null,"enumInteger":null,"enumNumber":null,"extensions":null}],"properties":null,"additionalProperties":null,"required":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"schema":null,"format":null,"enumString":null,"enumInteger":null,"enumNumber":null,"extensions":null},"status":{"example":"available","id":null,"title":null,"type":"string","description":"pet status in the store","items":null,"properties":null,"additionalProperties":null,"required":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"schema":null,"format":"","enumString":["available","pending","sold"],"enumInteger":null,"enumNumber":null,"extensions":null},"testnumber":{"example":1.23139183198000000283719387,"id":null,"title":null,"type":"number","description":"pet status in the store","items":null,"properties":null,"additionalProperties":null,"required":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"schema":null,"format":"","enumString":["available","pending","sold"],"enumInteger":null,"enumNumber":null,"extensions":null},"testnumber11":{"example":"@number","id":null,"title":null,"type":"number","description":"pet status in the store","items":null,"properties":null,"additionalProperties":null,"required":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"schema":null,"format":"","enumString":["available","pending","sold"],"enumInteger":null,"enumNumber":null,"extensions":null},"testfalse":{"example":"@boolean","id":null,"title":null,"type":"boolean","description":"pet status in the store","items":null,"properties":null,"additionalProperties":null,"required":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"schema":null,"format":"","enumString":["available","pending","sold"],"enumInteger":null,"enumNumber":null,"extensions":null},"testfalse":{"example":false,"id":null,"title":null,"type":"boolean","description":"pet status in the store","items":null,"properties":null,"additionalProperties":null,"required":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"schema":null,"format":"","enumString":["available","pending","sold"],"enumInteger":null,"enumNumber":null,"extensions":null},"testnull":{"example":null,"id":null,"title":null,"type":"null","description":"pet status in the store","items":null,"properties":null,"additionalProperties":null,"required":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"schema":null,"format":"","enumInteger":null,"enumNumber":null,"extensions":null},"testass": null},"additionalProperties":null,"required":["name","photoUrls"],"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"schema":null,"format":null,"enumString":null,"enumInteger":null,"enumNumber":null,"extensions":null}
""";
//正常数据;
- requestPost("preview", JSON.parseObject(jsonString, JsonSchemaItem.class)).andExpect(status().isOk());
+ requestPostWithOk(JSON_SCHEMA_PREVIEW, JSON.parseObject(jsonString, JsonSchemaItem.class));
//正常array数据
String jsonArray = """
{"example":null,"id":null,"title":null,"type":"array","description":null,"items":{"example":null,"id":null,"title":null,"type":"object","description":null,"items":null,"properties":{"id":{"example":"@integer","id":null,"title":null,"type":"integer","description":"","items":null,"properties":null,"additionalProperties":null,"required":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"schema":null,"format":"int64","enumString":null,"enumInteger":null,"enumNumber":null,"extensions":null},"name":{"example":null,"id":null,"title":null,"type":"string","description":"","items":null,"properties":null,"additionalProperties":null,"required":null,"pattern":null,"maxLength":null,"minLength":null,"mainimum":null,"maximum":null,"schema":null,"format":"","enumString":null,"enumInteger":null,"enumNumber":null,"extensions":null}},"additionalProperties":null,"required":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"schema":null,"format":null,"enumString":null,"enumInteger":null,"enumNumber":null,"extensions":null},"properties":null,"additionalProperties":null,"required":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"schema":null,"format":null,"enumString":null,"enumInteger":null,"enumNumber":null,"extensions":null}
""";
- requestPost("preview", JSON.parseObject(jsonArray, JsonSchemaItem.class)).andExpect(status().isOk());
+ requestPostWithOk(JSON_SCHEMA_PREVIEW, JSON.parseObject(jsonArray, JsonSchemaItem.class));
// 校验转换是否正确
String schema = """
@@ -1739,7 +1741,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
"null" : null
}
""";
- MvcResult mvcResult = requestPostWithOkAndReturn("preview", JSON.parseObject(schema, JsonSchemaItem.class));
+ MvcResult mvcResult = requestPostWithOkAndReturn(JSON_SCHEMA_PREVIEW, JSON.parseObject(schema, JsonSchemaItem.class));
String resultData = getResultData(mvcResult, String.class);
Assertions.assertEquals(JSON.parseObject(jsonResult), JSON.parseObject(resultData));
@@ -1760,7 +1762,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
null
]
""";
- mvcResult = requestPostWithOkAndReturn("preview", JSON.parseObject(schema, JsonSchemaItem.class));
+ mvcResult = requestPostWithOkAndReturn(JSON_SCHEMA_PREVIEW, JSON.parseObject(schema, JsonSchemaItem.class));
resultData = getResultData(mvcResult, String.class);
Assertions.assertEquals(JSON.parseObject(jsonResult), JSON.parseObject(resultData));
@@ -1768,18 +1770,39 @@ public class ApiDefinitionControllerTests extends BaseTest {
schema = """
{"type":"object","enable":false}
""";
- mvcResult = requestPostWithOkAndReturn("preview", JSON.parseObject(schema, JsonSchemaItem.class));
+ mvcResult = requestPostWithOkAndReturn(JSON_SCHEMA_PREVIEW, JSON.parseObject(schema, JsonSchemaItem.class));
Assertions.assertEquals(getResultData(mvcResult, String.class), "{}");
// 校验禁用
schema = """
{"type":"object","enable":true,"properties":{"array":{"type":"array","enable":false,"items":[{"type":"string","example":"1","enable":false},{"type":"number","example":"2","enable":false}]},"string":{"type":"string","example":"stringValue","enable":false},"int":{"type":"integer","example":"1","enable":false},"num":{"type":"number","example":"1.00","enable":false},"boolean":{"type":"boolean","example":"booleanValue","enable":false},"null":{"type":"null","enable":false}}}
""";
- mvcResult = requestPostWithOkAndReturn("preview", JSON.parseObject(schema, JsonSchemaItem.class));
+ mvcResult = requestPostWithOkAndReturn(JSON_SCHEMA_PREVIEW, JSON.parseObject(schema, JsonSchemaItem.class));
resultData = getResultData(mvcResult, String.class);
Assertions.assertEquals(JSON.parseObject("{}"), JSON.parseObject(resultData));
}
+ @Test
+ @Order(104)
+ public void testJsonSchemaAutoGenerate() throws Exception {
+ String jsonString = """
+ {"id":null,"title":null,"example":null,"type":"object","description":null,"items":null,"properties":{"array":{"id":null,"title":null,"example":null,"type":"array","description":null,"items":[{"id":null,"title":null,"example":"","type":"string","description":"","items":null,"properties":null,"additionalProperties":null,"required":null,"defaultValue":"","pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"maxItems":null,"minItems":null,"format":null,"enumValues":null,"enable":true},{"id":null,"title":null,"example":"","type":"number","description":"","items":null,"properties":null,"additionalProperties":null,"required":null,"defaultValue":"","pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"maxItems":null,"minItems":null,"format":null,"enumValues":null,"enable":true}],"properties":null,"additionalProperties":null,"required":null,"defaultValue":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"maxItems":null,"minItems":null,"format":null,"enumValues":null,"enable":true},"string":{"id":null,"title":null,"example":"","type":"string","description":"","items":null,"properties":null,"additionalProperties":null,"required":null,"defaultValue":"","pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"maxItems":null,"minItems":null,"format":null,"enumValues":null,"enable":true},"int":{"id":null,"title":null,"example":"","type":"integer","description":"","items":null,"properties":null,"additionalProperties":null,"required":null,"defaultValue":"","pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"maxItems":null,"minItems":null,"format":null,"enumValues":null,"enable":true},"num":{"id":null,"title":null,"example":"","type":"number","description":"","items":null,"properties":null,"additionalProperties":null,"required":null,"defaultValue":"","pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"maxItems":null,"minItems":null,"format":null,"enumValues":null,"enable":true},"boolean":{"id":null,"title":null,"example":"","type":"boolean","description":"","items":null,"properties":null,"additionalProperties":null,"required":null,"defaultValue":"","pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"maxItems":null,"minItems":null,"format":null,"enumValues":null,"enable":true},"null":{"id":null,"title":null,"example":null,"type":"null","description":null,"items":null,"properties":null,"additionalProperties":null,"required":null,"defaultValue":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"maxItems":null,"minItems":null,"format":null,"enumValues":null,"enable":true}},"additionalProperties":null,"required":null,"defaultValue":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"maxItems":null,"minItems":null,"format":null,"enumValues":null,"enable":true}
+ """;
+ // 默认生成
+ requestPost(JSON_SCHEMA_AUTO_GENERATE, JSON.parseObject(jsonString, JsonSchemaItem.class));
+ // 带枚举值
+ jsonString = """
+ {"type":"object","enable":true,"properties":{"array":{"type":"array","enable":true,"items":[{"type":"string","example":"","description":"","additionalProperties":null,"defaultValue":"","pattern":null,"maxLength":4,"minLength":0,"minimum":null,"maximum":null,"maxItems":null,"minItems":null,"format":null,"enable":true,"regex":"^[A-Z]"},{"type":"number","example":"","description":"","additionalProperties":null,"defaultValue":"","pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"maxItems":null,"minItems":null,"format":null,"enable":true}]},"string":{"type":"string","example":"","description":"","additionalProperties":null,"defaultValue":"","pattern":null,"maxLength":5,"minLength":1,"minimum":null,"maximum":null,"maxItems":null,"minItems":null,"format":null,"enumValues":["11111","22222"],"enable":true},"int":{"type":"integer","example":"","description":"","additionalProperties":null,"defaultValue":"","pattern":null,"maxLength":null,"minLength":null,"minimum":0,"maximum":4,"maxItems":null,"minItems":null,"format":null,"enumValues":["111","2333","444"],"enable":true},"num":{"type":"number","example":"","description":"","additionalProperties":null,"defaultValue":"","pattern":null,"maxLength":null,"minLength":null,"minimum":0,"maximum":5,"maxItems":null,"minItems":null,"format":null,"enumValues":["111","222","3333"],"enable":true},"boolean":{"type":"boolean","example":"","description":"","additionalProperties":null,"defaultValue":"","pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"maxItems":null,"minItems":null,"format":null,"enable":true},"null":{"type":"null","enable":true}}}
+ """;
+ requestPostWithOk(JSON_SCHEMA_AUTO_GENERATE, JSON.parseObject(jsonString, JsonSchemaItem.class));
+
+ // 默认值和正则
+ jsonString = """
+ {"id":null,"title":null,"example":null,"type":"object","description":null,"items":null,"properties":{"array":{"id":null,"title":null,"example":null,"type":"array","description":null,"items":[{"id":null,"title":null,"example":"","type":"string","description":"","items":null,"properties":null,"additionalProperties":null,"required":null,"defaultValue":"默认值","pattern":null,"maxLength":4,"minLength":0,"minimum":null,"maximum":null,"maxItems":null,"minItems":null,"format":null,"enumValues":null,"enable":true},{"id":null,"title":null,"example":"","type":"number","description":"","items":null,"properties":null,"additionalProperties":null,"required":null,"defaultValue":"","pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"maxItems":null,"minItems":null,"format":null,"enumValues":null,"enable":true}],"properties":null,"additionalProperties":null,"required":null,"defaultValue":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"maxItems":null,"minItems":null,"format":null,"enumValues":null,"enable":true},"string":{"id":null,"title":null,"example":"","type":"string","description":"","items":null,"properties":null,"additionalProperties":null,"required":null,"defaultValue":"","pattern":"[A-Z0-9_]+","maxLength":5,"minLength":1,"minimum":null,"maximum":null,"maxItems":null,"minItems":null,"format":null,"enumValues":null,"enable":true},"int":{"id":null,"title":null,"example":"","type":"integer","description":"","items":null,"properties":null,"additionalProperties":null,"required":null,"defaultValue":3,"pattern":null,"maxLength":null,"minLength":null,"minimum":0,"maximum":4,"maxItems":null,"minItems":null,"format":null,"enumValues":null,"enable":true},"num":{"id":null,"title":null,"example":"","type":"number","description":"","items":null,"properties":null,"additionalProperties":null,"required":null,"defaultValue":2,"pattern":null,"maxLength":null,"minLength":null,"minimum":0,"maximum":5,"maxItems":null,"minItems":null,"format":null,"enumValues":null,"enable":true},"boolean":{"id":null,"title":null,"example":"","type":"boolean","description":"","items":null,"properties":null,"additionalProperties":null,"required":null,"defaultValue":"true","pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"maxItems":null,"minItems":null,"format":null,"enumValues":null,"enable":true},"null":{"id":null,"title":null,"example":null,"type":"null","description":null,"items":null,"properties":null,"additionalProperties":null,"required":null,"defaultValue":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"maxItems":null,"minItems":null,"format":null,"enumValues":null,"enable":true}},"additionalProperties":null,"required":null,"defaultValue":null,"pattern":null,"maxLength":null,"minLength":null,"minimum":null,"maximum":null,"maxItems":null,"minItems":null,"format":null,"enumValues":null,"enable":true}
+ """;
+ requestPostWithOk(JSON_SCHEMA_AUTO_GENERATE, JSON.parseObject(jsonString, JsonSchemaItem.class));
+ }
+
@Test
@Order(20)
public void testGetRef() throws Exception {
diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/constants/PropertyConstant.java b/backend/services/project-management/src/main/java/io/metersphere/project/constants/PropertyConstant.java
index 4050012ef6..048d88aa60 100644
--- a/backend/services/project-management/src/main/java/io/metersphere/project/constants/PropertyConstant.java
+++ b/backend/services/project-management/src/main/java/io/metersphere/project/constants/PropertyConstant.java
@@ -22,5 +22,12 @@ public class PropertyConstant {
public final static String MOCK = "mock";
public final static String BODY_TYPE = "bodyType";
public final static String PARAM_TYPE = "paramType";
+ public final static String ENUM_VALUES = "enumValues";
+ public final static String DEFAULT_VALUE = "defaultValue";
+ public final static String PATTERN = "pattern";
+ public final static String MAX_LENGTH = "maxLength";
+ public final static String MIN_LENGTH = "minLength";
+ public final static String MINIMUM = "minimum";
+ public final static String MAXIMUM = "maximum";
}
diff --git a/frontend/src/api/modules/api-test/management.ts b/frontend/src/api/modules/api-test/management.ts
index c2af6bde7e..1b87d6b20c 100644
--- a/frontend/src/api/modules/api-test/management.ts
+++ b/frontend/src/api/modules/api-test/management.ts
@@ -55,6 +55,7 @@ import {
GetTrashModuleCountUrl,
GetTrashModuleTreeUrl,
ImportDefinitionUrl,
+ JsonSchemaAutoGenerateUrl,
MockDetailUrl,
MoveModuleUrl,
OperationHistoryUrl,
@@ -308,6 +309,11 @@ export function convertJsonSchemaToJson(data: JsonSchema) {
return MSR.post({ url: ConvertJsonSchemaToJsonUrl, data });
}
+// json-schema 生成测试数据
+export function jsonSchemaAutoGenerate(data: JsonSchema) {
+ return MSR.post({ url: JsonSchemaAutoGenerateUrl, data });
+}
+
/**
* Mock
*/
diff --git a/frontend/src/api/requrls/api-test/management.ts b/frontend/src/api/requrls/api-test/management.ts
index 70ce2a0795..999d40eedb 100644
--- a/frontend/src/api/requrls/api-test/management.ts
+++ b/frontend/src/api/requrls/api-test/management.ts
@@ -36,7 +36,8 @@ export const OperationHistoryUrl = '/api/definition/operation-history'; // 接
export const SaveOperationHistoryUrl = '/api/definition/operation-history/save'; // 接口定义-另存变更历史为指定版本
export const RecoverOperationHistoryUrl = '/api/definition/operation-history/recover'; // 接口定义-变更历史恢复
export const DefinitionReferenceUrl = '/api/definition/get-reference'; // 获取接口引用关系
-export const ConvertJsonSchemaToJsonUrl = '/api/definition/preview'; // 将json-schema转换为 json 数据
+export const ConvertJsonSchemaToJsonUrl = '/api/definition/json-schema/preview'; // 将json-schema转换为 json 数据
+export const JsonSchemaAutoGenerateUrl = '/api/definition/json-schema/auto-generate'; // 将json-schema转换为 json 数据
/**
* Mock
diff --git a/frontend/src/components/pure/ms-json-schema/index.vue b/frontend/src/components/pure/ms-json-schema/index.vue
index e9d9f20472..7ffabca299 100644
--- a/frontend/src/components/pure/ms-json-schema/index.vue
+++ b/frontend/src/components/pure/ms-json-schema/index.vue
@@ -427,7 +427,7 @@
@@ -821,8 +821,8 @@
},
{
title: t('ms.json.schema.regex'),
- dataIndex: 'regex',
- slotName: 'regex',
+ dataIndex: 'pattern',
+ slotName: 'pattern',
inputType: 'input',
size: 'medium',
addLineDisabled: true,
diff --git a/frontend/src/views/api-test/components/requestComposition/body.vue b/frontend/src/views/api-test/components/requestComposition/body.vue
index 01612c5633..a490a94628 100644
--- a/frontend/src/views/api-test/components/requestComposition/body.vue
+++ b/frontend/src/views/api-test/components/requestComposition/body.vue
@@ -185,7 +185,7 @@
import batchAddKeyVal from '@/views/api-test/components/batchAddKeyVal.vue';
import paramTable, { type ParamTableColumn } from '@/views/api-test/components/paramTable.vue';
- import { convertJsonSchemaToJson } from '@/api/modules/api-test/management';
+ import { jsonSchemaAutoGenerate } from '@/api/modules/api-test/management';
import { requestBodyTypeMap } from '@/config/apiTest';
import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
@@ -387,7 +387,7 @@
const schema = parseTableDataToJsonSchema(innerParams.value.jsonBody.jsonSchemaTableData?.[0]);
if (schema) {
// 再将 json schema 转换为 json 格式
- const res = await convertJsonSchemaToJson(schema);
+ const res = await jsonSchemaAutoGenerate(schema);
innerParams.value.jsonBody.jsonValue = res;
emit('change');
} else {
diff --git a/frontend/src/views/api-test/components/requestComposition/response/edit.vue b/frontend/src/views/api-test/components/requestComposition/response/edit.vue
index f92febabba..f0703b2623 100644
--- a/frontend/src/views/api-test/components/requestComposition/response/edit.vue
+++ b/frontend/src/views/api-test/components/requestComposition/response/edit.vue
@@ -254,7 +254,7 @@
import paramTable, { ParamTableColumn } from '@/views/api-test/components/paramTable.vue';
import popConfirm from '@/views/api-test/components/popConfirm.vue';
- import { convertJsonSchemaToJson } from '@/api/modules/api-test/management';
+ import { jsonSchemaAutoGenerate } from '@/api/modules/api-test/management';
import { responseHeaderOption } from '@/config/apiTest';
import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
@@ -431,7 +431,7 @@
}
if (schema) {
// 再将 json schema 转换为 json 格式
- const res = await convertJsonSchemaToJson(schema);
+ const res = await jsonSchemaAutoGenerate(schema);
activeResponse.value.body.jsonBody.jsonValue = res;
} else {
Message.warning(t('apiTestManagement.pleaseInputJsonSchema'));
diff --git a/pom.xml b/pom.xml
index 683ffc2247..44c794ca86 100644
--- a/pom.xml
+++ b/pom.xml
@@ -85,7 +85,7 @@
3.3
3.x
3.0
-
+ 1.0.0-RELEASE