diff --git a/pom.xml b/pom.xml index 4a5895f..a4d41a9 100644 --- a/pom.xml +++ b/pom.xml @@ -376,6 +376,11 @@ 0.10.2 + + io.swagger.parser.v3 + swagger-parser + 2.1.25 + diff --git a/src/main/java/com/guwan/backend/controller/DemoController.java b/src/main/java/com/guwan/backend/controller/DemoController.java index 24a82fb..d5b054b 100644 --- a/src/main/java/com/guwan/backend/controller/DemoController.java +++ b/src/main/java/com/guwan/backend/controller/DemoController.java @@ -6,19 +6,23 @@ import com.guwan.backend.elasticsearch.document.ProductDocument; import com.guwan.backend.elasticsearch.mapper.ProductEsMapper; import com.guwan.backend.mongodb.CategoryService; import com.guwan.backend.mongodb.FileStorageService; +import com.guwan.backend.service.SwaggerAutoFillService; import com.guwan.backend.util.MinioUtil; import com.mongodb.client.gridfs.model.GridFSFile; import io.minio.MinioClient; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.hadoop.thirdparty.protobuf.Api; import org.elasticsearch.search.SearchHit; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import org.springframework.web.client.RestTemplate; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.List; @Slf4j @@ -42,6 +46,10 @@ public class DemoController { private final FileStorageService fileStorageService; private final CategoryService categoryService; + + private final RestTemplate restTemplate; + + private final SwaggerAutoFillService swaggerAutoFillService; @PostMapping("/uploadFile") public Result uploadFile(String bucketName, MultipartFile file){ return Result.success(minioUtil.getUrl(minioUtil.getFileUrl @@ -126,5 +134,17 @@ public class DemoController { System.out.println("categoryService.getAllCategories() = " + categoryService.getAllCategories()); } + @GetMapping("/testSwagger") + public void testSwagger(){ + Object forObject = restTemplate.getForObject("http://localhost:8084/v3/api-docs", Object.class); + System.out.println("forObject = " + forObject); + } + + + @GetMapping("/parseSwagger") + public void parseSwagger(){ + swaggerAutoFillService.parseSwagger(); + } + } diff --git a/src/main/java/com/guwan/backend/pojo/dto/Api.java b/src/main/java/com/guwan/backend/pojo/dto/Api.java new file mode 100644 index 0000000..fd75db3 --- /dev/null +++ b/src/main/java/com/guwan/backend/pojo/dto/Api.java @@ -0,0 +1,46 @@ +/* +package com.guwan.backend.pojo.dto; + +import com.baomidou.mybatisplus.annotation.*; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@TableName("apis") +public class Api { + @TableId(type = IdType.AUTO) + private Long id; + + private String name; + private String method; + private String url; + private Long projectId; + + @TableField(typeHandler = JacksonTypeHandler.class) + private List headers; + + @TableField(typeHandler = JacksonTypeHandler.class) + private List queryParams; + + private String bodyType; + + @TableField(typeHandler = JacksonTypeHandler.class) + private List formParams; + + @TableField(columnDefinition = "text") + private String requestSchema; + + @TableField(columnDefinition = "text") + private String responseSchema; + + @TableField(typeHandler = JacksonTypeHandler.class) + private TestCaseGenerationRules generationRules; + + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; +}*/ diff --git a/src/main/java/com/guwan/backend/service/SwaggerAutoFillService.java b/src/main/java/com/guwan/backend/service/SwaggerAutoFillService.java new file mode 100644 index 0000000..26a6593 --- /dev/null +++ b/src/main/java/com/guwan/backend/service/SwaggerAutoFillService.java @@ -0,0 +1,82 @@ +package com.guwan.backend.service; + + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.oas.models.responses.ApiResponses; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.parser.OpenAPIV3Parser; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.util.HashMap; +import java.util.Map; + +@Service +public class SwaggerAutoFillService { + + private static final String SWAGGER_URL = "http://127.0.0.1:8084/v3/api-docs"; + private final RestTemplate restTemplate = new RestTemplate(); + + /** + * 解析 Swagger,自动提取接口字段 + */ + public void parseSwagger() { + OpenAPI openAPI = new OpenAPIV3Parser().read(SWAGGER_URL); + + // 遍历所有 API 接口 + openAPI.getPaths().forEach((path, pathItem) -> { + System.out.println("解析接口: " + path); + + // 处理不同类型的 HTTP 方法 + for (PathItem.HttpMethod method : pathItem.readOperationsMap().keySet()) { + Operation operation = pathItem.readOperationsMap().get(method); + ApiResponses responses = operation.getResponses(); + + // 解析返回字段 + responses.forEach((code, response) -> extractResponseFields(response, path)); + } + }); + } + + + private void extractResponseFields(ApiResponse response, String path) { + if (response.getContent() != null) { + for (String contentType : response.getContent().keySet()) { + Schema schema = response.getContent().get(contentType).getSchema(); + + if (schema != null) { + System.out.println("接口 " + path + " (" + contentType + ") 返回字段: "); + parseSchema(schema, ""); + } + } + } + } + + /** + * 提取 API 返回字段 + */ + /** + * 递归解析 Schema,支持复杂对象和嵌套结构 + */ + private void parseSchema(Schema schema, String parentKey) { + if (schema.getProperties() != null) { + for (Map.Entry entry : schema.getProperties().entrySet()) { + String fieldName = parentKey.isEmpty() ? entry.getKey() : parentKey + "." + entry.getKey(); + System.out.println(" - " + fieldName + " : " + entry.getValue().getType()); + + // 递归解析子对象 + parseSchema(entry.getValue(), fieldName); + } + } else if (schema.getType() != null && schema.getType().equals("array")) { + // 处理数组类型 + Schema itemsSchema = schema.getItems(); + System.out.println(" - " + parentKey + "[] : " + (itemsSchema != null ? itemsSchema.getType() : "Unknown")); + if (itemsSchema != null) { + parseSchema(itemsSchema, parentKey + "[]"); + } + } + } +} diff --git a/src/main/java/com/guwan/backend/service/SwaggerImportService.java b/src/main/java/com/guwan/backend/service/SwaggerImportService.java new file mode 100644 index 0000000..82c5824 --- /dev/null +++ b/src/main/java/com/guwan/backend/service/SwaggerImportService.java @@ -0,0 +1,179 @@ +/* +package com.guwan.backend.service; + +import org.apache.hadoop.thirdparty.protobuf.Api; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@Service +@Slf4j +public class SwaggerImportService { + + @Autowired + private ApiRepository apiRepository; + + public List importFromSwagger(String swaggerJson) { + List apis = new ArrayList<>(); + try { + ObjectMapper mapper = new ObjectMapper(); + JsonNode root = mapper.readTree(swaggerJson); + + // 获取所有路径 + JsonNode paths = root.get("paths"); + if (paths != null) { + paths.fields().forEachRemaining(path -> { + String url = path.getKey(); + JsonNode methods = path.getValue(); + + methods.fields().forEachRemaining(method -> { + Api api = parseApiFromSwagger(url, method.getKey(), method.getValue()); + apis.add(api); + }); + }); + } + } catch (Exception e) { + log.error("解析Swagger文档失败", e); + throw new RuntimeException("解析Swagger文档失败", e); + } + return apis; + } + + private Api parseApiFromSwagger(String url, String method, JsonNode operation) { + Api api = new Api(); + api.setUrl(url); + api.setMethod(HttpMethod.valueOf(method.toUpperCase())); + api.setName(operation.get("summary").asText()); + + // 解析请求参数 + List parameters = new ArrayList<>(); + JsonNode params = operation.get("parameters"); + if (params != null) { + params.forEach(param -> { + RequestParameter parameter = new RequestParameter(); + parameter.setName(param.get("name").asText()); + parameter.setDescription(param.get("description").asText()); + parameter.setRequired(param.get("required").asBoolean()); + parameter.setType(parseParameterType(param.get("schema"))); + + String in = param.get("in").asText(); + if ("query".equals(in)) { + if (api.getQueryParams() == null) { + api.setQueryParams(new ArrayList<>()); + } + api.getQueryParams().add(parameter); + } else if ("header".equals(in)) { + RequestHeader header = new RequestHeader(); + header.setKey(parameter.getName()); + header.setDescription(parameter.getDescription()); + header.setRequired(parameter.isRequired()); + if (api.getHeaders() == null) { + api.setHeaders(new ArrayList<>()); + } + api.getHeaders().add(header); + } + }); + } + + // 解析请求体 + JsonNode requestBody = operation.get("requestBody"); + if (requestBody != null) { + JsonNode content = requestBody.get("content"); + if (content.has("application/json")) { + api.setBodyType(BodyType.JSON); + api.setRequestSchema(content.get("application/json") + .get("schema").toString()); + } else if (content.has("application/x-www-form-urlencoded")) { + api.setBodyType(BodyType.FORM); + JsonNode formSchema = content.get("application/x-www-form-urlencoded") + .get("schema"); + api.setFormParams(parseFormParameters(formSchema)); + } + } + + // 解析响应 + JsonNode responses = operation.get("responses"); + if (responses != null && responses.has("200")) { + JsonNode okResponse = responses.get("200"); + if (okResponse.has("content") && + okResponse.get("content").has("application/json")) { + api.setResponseSchema(okResponse.get("content") + .get("application/json") + .get("schema").toString()); + } + } + + // 设置自动生成测试用例的规则 + api.setGenerationRules(createDefaultGenerationRules()); + + return api; + } + + private ParamType parseParameterType(JsonNode schema) { + String type = schema.get("type").asText(); + switch (type) { + case "integer": + case "number": + return ParamType.NUMBER; + case "boolean": + return ParamType.BOOLEAN; + case "array": + return ParamType.ARRAY; + case "object": + return ParamType.OBJECT; + default: + return ParamType.STRING; + } + } + + private List parseFormParameters(JsonNode schema) { + List params = new ArrayList<>(); + JsonNode properties = schema.get("properties"); + JsonNode required = schema.get("required"); + + properties.fields().forEachRemaining(field -> { + RequestParameter param = new RequestParameter(); + param.setName(field.getKey()); + JsonNode property = field.getValue(); + param.setType(parseParameterType(property)); + param.setDescription(property.get("description").asText()); + param.setRequired(required != null && required.toString() + .contains(field.getKey())); + + if (property.has("format")) { + param.setFormat(property.get("format").asText()); + } + if (property.has("pattern")) { + param.setPattern(property.get("pattern").asText()); + } + if (property.has("minimum")) { + param.setMinimum(property.get("minimum").asText()); + } + if (property.has("maximum")) { + param.setMaximum(property.get("maximum").asText()); + } + if (property.has("enum")) { + List enumValues = new ArrayList<>(); + property.get("enum").forEach(v -> enumValues.add(v.asText())); + param.setEnumValues(enumValues); + } + + params.add(param); + }); + + return params; + } + + private TestCaseGenerationRules createDefaultGenerationRules() { + TestCaseGenerationRules rules = new TestCaseGenerationRules(); + rules.setEnabled(true); + rules.setStrategies(Arrays.asList( + GenerationStrategy.REQUIRED_ONLY, + GenerationStrategy.ALL_PARAMS, + GenerationStrategy.NULL_EMPTY_VALUES + )); + return rules; + } +}*/ diff --git a/src/main/java/com/guwan/backend/service/TestDataAutoFillService.java b/src/main/java/com/guwan/backend/service/TestDataAutoFillService.java new file mode 100644 index 0000000..4a88c51 --- /dev/null +++ b/src/main/java/com/guwan/backend/service/TestDataAutoFillService.java @@ -0,0 +1,34 @@ +package com.guwan.backend.service; + +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.util.Map; + +@Service +public class TestDataAutoFillService { + private final RestTemplate restTemplate = new RestTemplate(); + + /** + * 自动获取前置 API 数据 + */ + public Map getPreConditionData(String apiUrl) { + return restTemplate.getForObject(apiUrl, Map.class); + } + + /** + * 自动填充接口的入参 + */ + public Map autoFillTestCase(String apiUrl, Map inputParams) { + Map preData = getPreConditionData(apiUrl); + + // 遍历 API 参数,自动填充依赖字段 + for (String key : inputParams.keySet()) { + if (preData.containsKey(key)) { + inputParams.put(key, preData.get(key)); + } + } + + return inputParams; + } +} diff --git a/src/main/java/com/guwan/backend/swagger/SwaggerAPIParser.java b/src/main/java/com/guwan/backend/swagger/SwaggerAPIParser.java new file mode 100644 index 0000000..1178f40 --- /dev/null +++ b/src/main/java/com/guwan/backend/swagger/SwaggerAPIParser.java @@ -0,0 +1,51 @@ +package com.guwan.backend.swagger; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.parser.OpenAPIV3Parser; + +import java.util.Map; + +public class SwaggerAPIParser { + + private static final String SWAGGER_URL = "http://127.0.0.1:8084/v3/api-docs"; + + public static void parseAPI() { + OpenAPI openAPI = new OpenAPIV3Parser().read(SWAGGER_URL); + + + System.out.println("openAPI = " + openAPI); + + /* openAPI.getPaths().forEach((path, pathItem) -> { + for (PathItem.HttpMethod method : pathItem.readOperationsMap().keySet()) { + Operation operation = pathItem.readOperationsMap().get(method); + + System.out.println("接口:" + method + " " + path); + + // 解析 Query & Path 参数 + Map queryParams = SwaggerQueryParamParser.extractQueryParameters(operation.getParameters()); + queryParams.forEach((key, value) -> System.out.println(" - 参数:" + key + " 类型:" + value)); + + // 解析请求体 + if (operation.getRequestBody() != null) { + Schema bodySchema = SwaggerRequestBodyParser.extractRequestBodySchema(operation.getRequestBody(), openAPI); + System.out.println(" - 请求体:" + bodySchema.getProperties()); + } + + // 解析响应体 + Schema responseSchema = SwaggerResponseParser.extractResponseSchema(operation.getResponses(), openAPI); + if (responseSchema != null) { + System.out.println(" - 响应数据:" + responseSchema.getProperties()); + } + + System.out.println("================================="); + } + });*/ + } + + public static void main(String[] args) { + parseAPI(); + } +} diff --git a/src/main/java/com/guwan/backend/swagger/SwaggerQueryParamParser.java b/src/main/java/com/guwan/backend/swagger/SwaggerQueryParamParser.java new file mode 100644 index 0000000..4c32d06 --- /dev/null +++ b/src/main/java/com/guwan/backend/swagger/SwaggerQueryParamParser.java @@ -0,0 +1,25 @@ +package com.guwan.backend.swagger; + +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.OpenAPI; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SwaggerQueryParamParser { + + public static Map extractQueryParameters(List parameters) { + Map paramMap = new HashMap<>(); + if (parameters != null) { + for (Parameter param : parameters) { + String name = param.getName(); + Schema schema = param.getSchema(); + String type = (schema != null) ? schema.getType() : "unknown"; + paramMap.put(name, type); + } + } + return paramMap; + } +} diff --git a/src/main/java/com/guwan/backend/swagger/SwaggerRequestBodyParser.java b/src/main/java/com/guwan/backend/swagger/SwaggerRequestBodyParser.java new file mode 100644 index 0000000..a487c3c --- /dev/null +++ b/src/main/java/com/guwan/backend/swagger/SwaggerRequestBodyParser.java @@ -0,0 +1,31 @@ +package com.guwan.backend.swagger; + +import io.swagger.v3.oas.models.media.Content; +import io.swagger.v3.oas.models.media.MediaType; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.RequestBody; +import io.swagger.v3.oas.models.OpenAPI; + +import java.util.Map; + +public class SwaggerRequestBodyParser { + + public static Schema extractRequestBodySchema(RequestBody requestBody, OpenAPI openAPI) { + if (requestBody != null) { + Content content = requestBody.getContent(); + for (Map.Entry entry : content.entrySet()) { + Schema schema = entry.getValue().getSchema(); + return resolveSchema(schema, openAPI); + } + } + return null; + } + + private static Schema resolveSchema(Schema schema, OpenAPI openAPI) { + if (schema != null && schema.get$ref() != null) { + String refName = schema.get$ref().replace("#/components/schemas/", ""); + return openAPI.getComponents().getSchemas().get(refName); + } + return schema; + } +} diff --git a/src/main/java/com/guwan/backend/swagger/SwaggerResponseParser.java b/src/main/java/com/guwan/backend/swagger/SwaggerResponseParser.java new file mode 100644 index 0000000..a6a7e22 --- /dev/null +++ b/src/main/java/com/guwan/backend/swagger/SwaggerResponseParser.java @@ -0,0 +1,32 @@ +package com.guwan.backend.swagger; + +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.oas.models.responses.ApiResponses; +import io.swagger.v3.oas.models.OpenAPI; + +import java.util.Map; + +public class SwaggerResponseParser { + + public static Schema extractResponseSchema(ApiResponses responses, OpenAPI openAPI) { + if (responses != null && responses.containsKey("200")) { + ApiResponse response = responses.get("200"); + if (response.getContent() != null) { + for (Map.Entry entry : response.getContent().entrySet()) { + Schema schema = entry.getValue().getSchema(); + return resolveSchema(schema, openAPI); + } + } + } + return null; + } + + private static Schema resolveSchema(Schema schema, OpenAPI openAPI) { + if (schema != null && schema.get$ref() != null) { + String refName = schema.get$ref().replace("#/components/schemas/", ""); + return openAPI.getComponents().getSchemas().get(refName); + } + return schema; + } +}