From ce733a0360f0e5acffe6678e6d687215e526551b Mon Sep 17 00:00:00 2001 From: patrick Date: Mon, 17 Mar 2025 11:42:37 +0800 Subject: [PATCH 1/5] add qa server --- README.md | 2 +- docs/1-qa-solution/qa-automation.png | Bin 0 -> 168416 bytes fluent-apps/pom.xml | 1 + fluent-apps/qaserver/.gitignore | 38 +++ fluent-apps/qaserver/pom.xml | 150 ++++++++++ .../main/java/io/fluentqa/QAWorkspaceApp.java | 17 ++ .../base/FluentProductConfigModule.java | 67 +++++ .../fluentqa/base/FluentUploadTCModule.java | 53 ++++ .../src/main/java/io/fluentqa/base/README.md | 8 + .../base/masterdata/model/MasterData.java | 76 +++++ .../base/masterdata/repo/MasterDataRepo.java | 15 + .../java/io/fluentqa/base/package-info.java | 1 + .../product/model/ProductModuleModel.java | 140 +++++++++ .../model/ProductModuleValidFlagVo.java | 47 +++ .../fluentqa/base/product/package-info.java | 1 + .../base/product/repo/ProductModuleRepo.java | 18 ++ .../product/service/ProductModuleService.java | 57 ++++ .../base/project/model/ProjectModel.java | 89 ++++++ .../base/project/repo/ProjectModelRepo.java | 18 ++ .../base/upload/model/UploadFileModel.java | 62 ++++ .../io/fluentqa/base/upload/package-info.java | 1 + .../upload/proxy/UploadFileDataProxy.java | 64 ++++ .../base/upload/proxy/UploadFileTypeEnum.java | 14 + .../io/fluentqa/qtm/FluentQAApiModule.java | 125 ++++++++ .../GenerateApiCaseByCaptureDataHandler.java | 24 ++ .../GenerateApiTestStepByApiTestRecord.java | 24 ++ .../handler/GenerateRawApiCaseHandler.java | 22 ++ .../qtm/api/model/ApiMonitorRecord.java | 131 +++++++++ .../qtm/api/model/ApiSpecChangeModel.java | 52 ++++ .../qtm/api/model/ApiSpecGitRepoModel.java | 47 +++ .../qtm/api/model/ApiSpecVersionModel.java | 102 +++++++ .../io/fluentqa/qtm/api/model/ApiStep.java | 156 ++++++++++ .../fluentqa/qtm/api/model/ApiTestRecord.java | 131 +++++++++ .../qtm/api/model/ApiTestScenario.java | 157 ++++++++++ .../qtm/api/model/RawApiTestCase.java | 96 ++++++ .../io/fluentqa/qtm/api/model/RemoteApi.java | 275 ++++++++++++++++++ .../qtm/api/model/RemoteApiStatus.java | 5 + .../fluentqa/qtm/api/model/RemoteApiType.java | 5 + .../io/fluentqa/qtm/api/package-info.java | 1 + .../qtm/api/repo/ApiMonitorRecordRepo.java | 14 + .../qtm/api/repo/ApiSpecChangeRepository.java | 11 + .../api/repo/ApiSpecGitRepoRepository.java | 10 + .../api/repo/ApiSpecVersionRepository.java | 10 + .../qtm/api/repo/ApiTestScenarioRepo.java | 10 + .../qtm/api/repo/ApiTestStepRepo.java | 10 + .../qtm/api/repo/RawApiTestCaseRepo.java | 10 + .../qtm/api/repo/RemoteServiceRepo.java | 23 ++ .../qtm/api/service/ApiTestCaseService.java | 107 +++++++ .../qtm/api/service/RemoteApiService.java | 142 +++++++++ .../java/io/fluentqa/qtm/pm/package-info.java | 1 + .../qtm/pm/requirement/FieldOption.java | 158 ++++++++++ .../qtm/pm/requirement/FieldOptionType.java | 13 + .../pm/requirement/RequirementFeature.java | 34 +++ .../qtm/pm/requirement/RequirementType.java | 15 + .../qtm/pm/requirement/TestRequirement.java | 192 ++++++++++++ .../io/fluentqa/qtm/tc/dto/TestCaseDTO.java | 28 ++ .../handlers/GenerateTestRecordHandler.java | 49 ++++ .../io/fluentqa/qtm/tc/model/TestCase.java | 238 +++++++++++++++ .../io/fluentqa/qtm/tc/model/TestResult.java | 206 +++++++++++++ .../io/fluentqa/qtm/tc/model/TestRun.java | 251 ++++++++++++++++ .../fluentqa/qtm/tc/model/TestScenario.java | 43 +++ .../java/io/fluentqa/qtm/tc/package-info.java | 1 + .../io/fluentqa/qtm/tc/repo/TestCaseRepo.java | 15 + .../fluentqa/qtm/tc/repo/TestResultRepo.java | 11 + .../io/fluentqa/qtm/tc/repo/TestRunRepo.java | 11 + .../qtm/tc/service/TestCaseService.java | 14 + .../tc/service/impl/MindMappingService.java | 41 +++ .../tc/service/impl/TestCaseServiceImpl.java | 94 ++++++ .../src/main/resources/application-dev.yaml | 101 +++++++ .../src/main/resources/application.yaml | 3 + .../src/main/resources/public/app.css | 24 ++ .../qaserver/src/main/resources/public/app.js | 71 +++++ .../src/main/resources/public/home.html | 12 + .../src/main/resources/tpl/operation.tpl | 17 ++ 74 files changed, 4281 insertions(+), 1 deletion(-) create mode 100644 docs/1-qa-solution/qa-automation.png create mode 100644 fluent-apps/qaserver/.gitignore create mode 100644 fluent-apps/qaserver/pom.xml create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/QAWorkspaceApp.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/base/FluentProductConfigModule.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/base/FluentUploadTCModule.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/base/README.md create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/base/masterdata/model/MasterData.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/base/masterdata/repo/MasterDataRepo.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/base/package-info.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/model/ProductModuleModel.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/model/ProductModuleValidFlagVo.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/package-info.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/repo/ProductModuleRepo.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/service/ProductModuleService.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/base/project/model/ProjectModel.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/base/project/repo/ProjectModelRepo.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/model/UploadFileModel.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/package-info.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/proxy/UploadFileDataProxy.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/proxy/UploadFileTypeEnum.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/FluentQAApiModule.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/handler/GenerateApiCaseByCaptureDataHandler.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/handler/GenerateApiTestStepByApiTestRecord.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/handler/GenerateRawApiCaseHandler.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiMonitorRecord.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiSpecChangeModel.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiSpecGitRepoModel.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiSpecVersionModel.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiStep.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiTestRecord.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiTestScenario.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RawApiTestCase.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RemoteApi.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RemoteApiStatus.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RemoteApiType.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/package-info.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiMonitorRecordRepo.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiSpecChangeRepository.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiSpecGitRepoRepository.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiSpecVersionRepository.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiTestScenarioRepo.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiTestStepRepo.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/RawApiTestCaseRepo.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/RemoteServiceRepo.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/service/ApiTestCaseService.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/service/RemoteApiService.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/package-info.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/FieldOption.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/FieldOptionType.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/RequirementFeature.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/RequirementType.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/TestRequirement.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/dto/TestCaseDTO.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/handlers/GenerateTestRecordHandler.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestCase.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestResult.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestRun.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestScenario.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/package-info.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/repo/TestCaseRepo.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/repo/TestResultRepo.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/repo/TestRunRepo.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/service/TestCaseService.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/service/impl/MindMappingService.java create mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/service/impl/TestCaseServiceImpl.java create mode 100644 fluent-apps/qaserver/src/main/resources/application-dev.yaml create mode 100644 fluent-apps/qaserver/src/main/resources/application.yaml create mode 100644 fluent-apps/qaserver/src/main/resources/public/app.css create mode 100644 fluent-apps/qaserver/src/main/resources/public/app.js create mode 100644 fluent-apps/qaserver/src/main/resources/public/home.html create mode 100644 fluent-apps/qaserver/src/main/resources/tpl/operation.tpl diff --git a/README.md b/README.md index 5ec69f6..dfbcc13 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ JAVA Libs or Applications. ## 1. Software QA: JAVA Revisited Overview **Automation Language Perspective**: -![img](qa-automation.png) +![img](docs/1-qa-solution/qa-automation.png) **Different Libs Perspective**: diff --git a/docs/1-qa-solution/qa-automation.png b/docs/1-qa-solution/qa-automation.png new file mode 100644 index 0000000000000000000000000000000000000000..feec58dc56c4932279bf52cc2758a187124b9baf GIT binary patch literal 168416 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�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 literal 0 HcmV?d00001 diff --git a/fluent-apps/pom.xml b/fluent-apps/pom.xml index 654ed41..f04511a 100644 --- a/fluent-apps/pom.xml +++ b/fluent-apps/pom.xml @@ -14,6 +14,7 @@ feeds pcinfo + qaserver diff --git a/fluent-apps/qaserver/.gitignore b/fluent-apps/qaserver/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/fluent-apps/qaserver/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/fluent-apps/qaserver/pom.xml b/fluent-apps/qaserver/pom.xml new file mode 100644 index 0000000..863f8ac --- /dev/null +++ b/fluent-apps/qaserver/pom.xml @@ -0,0 +1,150 @@ + + + 4.0.0 + + io.fluentqa + fluent-apps + 1.0-SNAPSHOT + + + qaserver + + + + + + xyz.erupt + erupt-upms + ${erupt.version} + + + + xyz.erupt + erupt-security + ${erupt.version} + + + xyz.erupt + erupt-job + ${erupt.version} + + + io.fluent + fluent-generator + ${fluent.version} + + + + + xyz.erupt + erupt-web + ${erupt.version} + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + org.springframework.boot + spring-boot-starter-undertow + 2.7.12 + + + org.postgresql + postgresql + ${postgresql.version} + + + javax.xml.bind + jaxb-api + 2.3.1 + + + com.github.xiaoymin + knife4j-openapi2-spring-boot-starter + 4.4.0 + + + + io.fluent + fluent-excel + ${fluent.version} + + + io.fluent + fluent-mindmap + ${fluent.version} + + + io.fluent + fluent-erupts-base + ${fluent.version} + + + + io.fluent + fluent-generator + ${fluent.version} + + + io.fluent + fluent-git + ${fluent.version} + + + + cn.hutool + hutool-all + + + io.fluent + fluent-quickdao + 1.0-SNAPSHOT + + + io.fluent + fluent-openapi + 1.0-SNAPSHOT + + + + + + + + + + + + + + + + + maven-compiler-plugin + org.apache.maven.plugins + 3.11.0 + + 17 + 17 + + + + org.springframework.boot + spring-boot-maven-plugin + 2.7.2 + + + + repackage + + + + + + + \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/QAWorkspaceApp.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/QAWorkspaceApp.java new file mode 100644 index 0000000..c6516b4 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/QAWorkspaceApp.java @@ -0,0 +1,17 @@ +package io.fluentqa; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.scheduling.annotation.EnableAsync; +import xyz.erupt.core.annotation.EruptScan; + +@SpringBootApplication +@EnableAsync +@EruptScan +@EntityScan +public class QAWorkspaceApp { + public static void main(String[] args) { + SpringApplication.run(QAWorkspaceApp.class); + } +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/FluentProductConfigModule.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/FluentProductConfigModule.java new file mode 100644 index 0000000..3dd958c --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/FluentProductConfigModule.java @@ -0,0 +1,67 @@ +package io.fluentqa.base; + +import io.fluentqa.base.masterdata.model.MasterData; +import io.fluentqa.base.product.model.ProductModuleModel; +import io.fluentqa.base.project.model.ProjectModel; +import io.fluentqa.base.upload.model.UploadFileModel; +import org.apache.commons.math3.stat.descriptive.summary.Product; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import xyz.erupt.core.annotation.EruptScan; +import xyz.erupt.core.constant.MenuTypeEnum; +import xyz.erupt.core.module.EruptModule; +import xyz.erupt.core.module.EruptModuleInvoke; +import xyz.erupt.core.module.MetaMenu; +import xyz.erupt.core.module.ModuleInfo; + +import java.util.ArrayList; +import java.util.List; + +@Configuration +@ComponentScan +@EntityScan +@EruptScan +@EnableConfigurationProperties +public class FluentProductConfigModule implements EruptModule { + + public FluentProductConfigModule() { + } + + @Override + public ModuleInfo info() { + return ModuleInfo.builder().name("fluent-product").build(); + } + + @Override + public void run() { + EruptModule.super.run(); + } + + @Override + public List initMenus() { + List menus = new ArrayList<>(); + menus.add(MetaMenu.createRootMenu("$fluent-master", "产品配置", "fa fa-product-hunt", 90)); + MetaMenu productMetaMenu = MetaMenu.createEruptClassMenu(ProductModuleModel.class, menus.get(0), 0, MenuTypeEnum.TABLE); + productMetaMenu.setIcon("fa fa-group"); + productMetaMenu.setName("产品元数据"); + productMetaMenu.setCode("$product-meta"); + menus.add(productMetaMenu); + MetaMenu masterDataMenu = MetaMenu.createEruptClassMenu(MasterData.class, menus.get(0), 1, MenuTypeEnum.TABLE); + masterDataMenu.setIcon("fa fa-times"); + masterDataMenu.setName("产品字典表配置"); + masterDataMenu.setCode("$master-data"); + menus.add(masterDataMenu); + MetaMenu projectMenu = MetaMenu.createEruptClassMenu(ProjectModel.class, menus.get(0), 2, MenuTypeEnum.TABLE); + projectMenu.setIcon("fa fa-linode"); + projectMenu.setName("项目配置"); + projectMenu.setCode("$project-meta"); + menus.add(projectMenu); + return menus; + } + + static { + EruptModuleInvoke.addEruptModule(FluentProductConfigModule.class); + } +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/FluentUploadTCModule.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/FluentUploadTCModule.java new file mode 100644 index 0000000..9e44381 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/FluentUploadTCModule.java @@ -0,0 +1,53 @@ +package io.fluentqa.base; + +import io.fluentqa.base.upload.model.UploadFileModel; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import xyz.erupt.core.annotation.EruptScan; +import xyz.erupt.core.constant.MenuTypeEnum; +import xyz.erupt.core.module.EruptModule; +import xyz.erupt.core.module.EruptModuleInvoke; +import xyz.erupt.core.module.MetaMenu; +import xyz.erupt.core.module.ModuleInfo; + +import java.util.ArrayList; +import java.util.List; + +@Configuration +@ComponentScan +@EntityScan +@EruptScan +@EnableConfigurationProperties +public class FluentUploadTCModule implements EruptModule { + + public FluentUploadTCModule() { + } + + @Override + public ModuleInfo info() { + return ModuleInfo.builder().name("fluent-tc-sync").build(); + } + + @Override + public void run() { + EruptModule.super.run(); + } + + @Override + public List initMenus() { + List menus = new ArrayList<>(); + menus.add(MetaMenu.createRootMenu("$tc-upload", "测试文件管理", "fa fa-file", 100)); + MetaMenu tfUploadSyncMenu = MetaMenu.createEruptClassMenu(UploadFileModel.class, menus.get(0), 0, MenuTypeEnum.TABLE); + tfUploadSyncMenu.setIcon("fa fa-folder-open"); + tfUploadSyncMenu.setName("测试文件同步"); + tfUploadSyncMenu.setCode("$tc-upload-sync"); + menus.add(tfUploadSyncMenu); + return menus; + } + + static { + EruptModuleInvoke.addEruptModule(FluentUploadTCModule.class); + } +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/README.md b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/README.md new file mode 100644 index 0000000..9fb066c --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/README.md @@ -0,0 +1,8 @@ +# README + +shared component: + +- master data: shared configurations +- upload data: upload component +- product: product module +- project: project module \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/masterdata/model/MasterData.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/masterdata/model/MasterData.java new file mode 100644 index 0000000..fab03e9 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/masterdata/model/MasterData.java @@ -0,0 +1,76 @@ +package io.fluentqa.base.masterdata.model; + +import io.fluentqa.base.handlers.SqlTagFetchHandler; +import io.fluentqa.base.model.ModelWithValidFlagVo; +import lombok.Data; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.InputType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; +import xyz.erupt.annotation.sub_field.sub_edit.TagsType; + +import javax.persistence.Entity; +import javax.persistence.Table; + + +@Erupt(name = "产品字典值配置", power = @Power(importable = true, export = true)) +@Table(name = "master_data") +@Entity +@Data +public class MasterData extends ModelWithValidFlagVo { + + @EruptField( + views = @View(title = "分类"), + edit = @Edit( + search = @Search(vague = true), + title = "获取可选种类", + type = EditType.TAGS, + desc = "动态获取可选种类", + tagsType = @TagsType( + fetchHandler = SqlTagFetchHandler.class, + fetchHandlerParams = "select distinct category from master_data where valid=true" + )) + ) + private String category; + + @EruptField( + views = @View( + title = "名称" + ), + edit = @Edit( + title = "名称", + type = EditType.INPUT, search = @Search, notNull = true, + inputType = @InputType + ) + ) + private String name; + + @EruptField( + views = @View( + title = "详细描述" + ), + edit = @Edit( + title = "详细描述", + type = EditType.INPUT, + inputType = @InputType + ) + ) + private String detail; + + @EruptField( + views = @View( + title = "代号" + ), + edit = @Edit( + title = "代号", + type = EditType.INPUT, search = @Search, notNull = true, + inputType = @InputType + ) + ) + private String code; + +} \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/masterdata/repo/MasterDataRepo.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/masterdata/repo/MasterDataRepo.java new file mode 100644 index 0000000..db4adae --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/masterdata/repo/MasterDataRepo.java @@ -0,0 +1,15 @@ +package io.fluentqa.base.masterdata.repo; + +import io.fluentqa.base.masterdata.model.MasterData; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface MasterDataRepo extends JpaRepository, JpaSpecificationExecutor { + + Optional findMasterDataByCode(String code); + +} \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/package-info.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/package-info.java new file mode 100644 index 0000000..3130485 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/package-info.java @@ -0,0 +1 @@ +package io.fluentqa.base; \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/model/ProductModuleModel.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/model/ProductModuleModel.java new file mode 100644 index 0000000..4a63a5e --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/model/ProductModuleModel.java @@ -0,0 +1,140 @@ +package io.fluentqa.base.product.model; + +import io.fluentqa.base.model.ModelWithValidFlagVo; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_erupt.Tree; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.ChoiceType; +import xyz.erupt.annotation.sub_field.sub_edit.InputType; +import xyz.erupt.annotation.sub_field.sub_edit.ReferenceTreeType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; +import xyz.erupt.toolkit.handler.SqlChoiceFetchHandler; + +import javax.persistence.*; +import java.util.UUID; + +@Erupt(name = "产品模块配置", + power = @Power(importable = true, export = true), + tree = @Tree(pid = "parent.id")) +@Entity +@Table(name = "products") +public class ProductModuleModel extends ModelWithValidFlagVo { + + @EruptField( + views = @View( + title = "名称" + ), + edit = @Edit( + title = "名称", + type = EditType.INPUT, search = @Search, + notNull = true, + inputType = @InputType + ) + ) + private String name; + + @EruptField( + views = @View( + title = "代号" + ), + edit = @Edit( + title = "代号", + type = EditType.INPUT, search = @Search, + notNull = true, + inputType = @InputType + ) + ) + private String code; + + @EruptField( + views = @View( + title = "详细描述" + ), + edit = @Edit( + title = "详细描述", + type = EditType.INPUT, search = @Search, notNull = true, + inputType = @InputType + ) + ) + private String details; + + @EruptField( + views = @View(title = "类型"), + edit = @Edit( + search = @Search, + title = "获取可选类型", + type = EditType.CHOICE, + desc = "动态获取可选类型", + choiceType = @ChoiceType( + fetchHandler = SqlChoiceFetchHandler.class, + fetchHandlerParams = "select id,name from master_data where category='PRODUCT'" + )) + ) + private String metaType; + + @ManyToOne + @EruptField( + edit = @Edit( + title = "上级树节点", + type = EditType.REFERENCE_TREE, + referenceTreeType = @ReferenceTreeType(pid = "parent.id") + ) + ) + private ProductModuleModel parent; + + + @Column(length = 36, nullable = true, updatable = false) + private String uuid = UUID.randomUUID().toString(); + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getDetails() { + return details; + } + + public void setDetails(String details) { + this.details = details; + } + + public String getMetaType() { + return metaType; + } + + public void setMetaType(String metaType) { + this.metaType = metaType; + } + + public ProductModuleModel getParent() { + return parent; + } + + public void setParent(ProductModuleModel parent) { + this.parent = parent; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } +} \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/model/ProductModuleValidFlagVo.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/model/ProductModuleValidFlagVo.java new file mode 100644 index 0000000..4aa8bee --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/model/ProductModuleValidFlagVo.java @@ -0,0 +1,47 @@ +package io.fluentqa.base.product.model; + +import io.fluentqa.base.model.ModelWithValidFlagVo; +import lombok.Getter; +import lombok.Setter; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.ReferenceTreeType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; + +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.MappedSuperclass; + +@MappedSuperclass +@Getter +@Setter +public class ProductModuleValidFlagVo extends ModelWithValidFlagVo { + @ManyToOne + @JoinColumn(name = "product_id") + @EruptField( + views = @View(title = "产品名称",column = "details"), + edit = @Edit( + search = @Search, + title = "产品选择", + type = EditType.REFERENCE_TREE, + desc = "动态获取产品", + referenceTreeType = @ReferenceTreeType(id = "id", label = "name", + pid = "parent.id")) + ) + private ProductModuleModel product; + + @ManyToOne + @JoinColumn(name = "module_id") + @EruptField( + views = @View(title = "模块名称",column = "details"), + edit = @Edit(title = "模块选择", search = @Search, type = EditType.REFERENCE_TREE, + referenceTreeType = @ReferenceTreeType(id = "id", label = "name", + dependField = "product", + dependColumn = "parent.id" + )) + ) + private ProductModuleModel module; + +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/package-info.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/package-info.java new file mode 100644 index 0000000..98160a8 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/package-info.java @@ -0,0 +1 @@ +package io.fluentqa.base.product; \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/repo/ProductModuleRepo.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/repo/ProductModuleRepo.java new file mode 100644 index 0000000..ac9d87f --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/repo/ProductModuleRepo.java @@ -0,0 +1,18 @@ +package io.fluentqa.base.product.repo; + + +import io.fluentqa.base.product.model.ProductModuleModel; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface ProductModuleRepo extends JpaRepository, JpaSpecificationExecutor { + + Optional findProductByNameAndValid(String name, boolean valid); + + Optional findProductByCodeAndValid(String codeName, boolean valid); + Optional findProductByParentIdAndNameAndValid(Long parentId, String name, boolean valid); +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/service/ProductModuleService.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/service/ProductModuleService.java new file mode 100644 index 0000000..72afcdf --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/service/ProductModuleService.java @@ -0,0 +1,57 @@ +package io.fluentqa.base.product.service; + +import io.fluent.builtin.PingYinUtils; +import io.fluentqa.base.product.repo.ProductModuleRepo; +import io.fluentqa.base.proxies.AuditDataEnhancerProxy; +import io.fluentqa.base.masterdata.repo.MasterDataRepo; +import io.fluentqa.base.product.model.ProductModuleModel; +import io.fluentqa.base.masterdata.model.MasterData; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.Optional; + +@Service +public class ProductModuleService { + @Resource + private ProductModuleRepo metaRepo; + + @Resource + private MasterDataRepo masterDataRepo; + + @Resource + AuditDataEnhancerProxy dataEnhancerProxy; + + public ProductModuleModel createModuleIfNotExist(Long productId, String moduleName, String updater) { + Optional meta = metaRepo.findProductByParentIdAndNameAndValid(productId, + moduleName, true); + if (meta.isPresent()) return meta.get(); + ProductModuleModel parent = new ProductModuleModel(); + parent.setId(productId); + ProductModuleModel module = new ProductModuleModel(); + module.setName(moduleName); + module.setDetails(moduleName); + module.setParent(parent); + module.setCode(PingYinUtils.convertToPinyinAbbreviation(moduleName)); + MasterData data = masterDataRepo.findMasterDataByCode("MODULE").get(); + module.setMetaType(data.getId().toString()); + dataEnhancerProxy.enhanceTimeAndUserAuditData(module,updater); + return metaRepo.save(module); + } + + public ProductModuleModel findApiServiceProduct() { + String API_SERVICE = "API"; + Optional meta = metaRepo.findProductByCodeAndValid(API_SERVICE, true); + if (meta.isPresent()) return meta.get(); + throw new RuntimeException("Please config API Service as a Product in Product Meta"); + } + + public ProductModuleModel createApiModuleIfNotExist(String moduleName,String updater) { + ProductModuleModel parent = findApiServiceProduct(); + return createModuleIfNotExist(parent.getId(), moduleName,updater); + } + + public ProductModuleModel findByName(String productName) { + return metaRepo.findProductByNameAndValid(productName, true).orElse(null); + } +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/project/model/ProjectModel.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/project/model/ProjectModel.java new file mode 100644 index 0000000..8109405 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/project/model/ProjectModel.java @@ -0,0 +1,89 @@ +package io.fluentqa.base.project.model; + + +import io.fluentqa.base.model.ModelWithValidFlagVo; +import io.fluentqa.base.product.model.ProductModuleModel; +import lombok.Data; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_erupt.Tree; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.InputType; +import xyz.erupt.annotation.sub_field.sub_edit.ReferenceTreeType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; + +import javax.persistence.*; +import java.util.UUID; + +@Erupt(name = "项目", + power = @Power(importable = true, export = true), + tree = @Tree(pid = "parent.id")) +@Entity +@Table(name = "projects") +@Data +public class ProjectModel extends ModelWithValidFlagVo { + + @EruptField( + views = @View( + title = "名称" + ), + edit = @Edit( + title = "名称", + type = EditType.INPUT, search = @Search, + notNull = true, + inputType = @InputType + ) + ) + private String name; + + @EruptField( + views = @View( + title = "代号" + ), + edit = @Edit( + title = "代号", + type = EditType.INPUT, search = @Search, + notNull = true, + inputType = @InputType + ) + ) + private String code; + + @EruptField( + views = @View( + title = "详细描述" + ), + edit = @Edit( + title = "详细描述", + type = EditType.INPUT, search = @Search, notNull = true, + inputType = @InputType + ) + ) + private String details; + + @ManyToOne + @EruptField( + edit = @Edit( + title = "产品列表", + type = EditType.REFERENCE_TREE, + referenceTreeType = @ReferenceTreeType(pid = "parent.id") + ) + ) + private ProductModuleModel productId; + + @ManyToOne + @EruptField( + edit = @Edit( + title = "上级树节点", + type = EditType.REFERENCE_TREE, + referenceTreeType = @ReferenceTreeType(pid = "parent.id") + ) + ) + private ProjectModel parent; + + @Column(length = 36, nullable = false, updatable = false) + private String uuid = UUID.randomUUID().toString(); +} \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/project/repo/ProjectModelRepo.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/project/repo/ProjectModelRepo.java new file mode 100644 index 0000000..5115fd8 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/project/repo/ProjectModelRepo.java @@ -0,0 +1,18 @@ +package io.fluentqa.base.project.repo; + + +import io.fluentqa.base.product.model.ProductModuleModel; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface ProjectModelRepo extends JpaRepository, JpaSpecificationExecutor { + + Optional findProductByNameAndValid(String name, boolean valid); + + Optional findProductByCodeAndValid(String codeName, boolean valid); + Optional findProductByParentIdAndNameAndValid(Long parentId, String name, boolean valid); +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/model/UploadFileModel.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/model/UploadFileModel.java new file mode 100644 index 0000000..484acb0 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/model/UploadFileModel.java @@ -0,0 +1,62 @@ +package io.fluentqa.base.upload.model; + +import io.fluentqa.base.upload.proxy.UploadFileDataProxy; +import io.fluentqa.base.product.model.ProductModuleModel; +import lombok.Data; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.PreDataProxy; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.AttachmentType; +import xyz.erupt.annotation.sub_field.sub_edit.ChoiceType; +import xyz.erupt.annotation.sub_field.sub_edit.InputType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; +import xyz.erupt.toolkit.handler.SqlChoiceFetchHandler; + +import javax.persistence.Entity; +import javax.persistence.Table; + +@Erupt(name = "测试相关文件上传同步", orderBy = "UploadFileModel.createTime desc") +@Table(name = "uploaded_files") +@Entity +@Data +@PreDataProxy(value = UploadFileDataProxy.class) +public class UploadFileModel extends ProductModuleModel { + + @EruptField( + views = @View(title = "用途"), + edit = @Edit( + search = @Search, + title = "获取可选类型", + type = EditType.CHOICE, + desc = "动态获取可选类型", + notNull = true, + choiceType = @ChoiceType( + fetchHandler = SqlChoiceFetchHandler.class, + fetchHandlerParams = "select distinct code,name from master_data where category='UPLOAD_FILE_USAGE' and valid=true" + )) + ) + private String usage; + + + @EruptField( + views = @View(title = "文件上传"), + edit = @Edit(title = "文件上传", type = EditType.ATTACHMENT, + attachmentType = @AttachmentType(size = 100000)) + ) + private String attachment; + + @EruptField( + views = @View( + title = "用途描述" + ), + edit = @Edit( + title = "用途描述", + type = EditType.TEXTAREA, notNull = true, + inputType = @InputType + ) + ) + private String comments; +} \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/package-info.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/package-info.java new file mode 100644 index 0000000..0998acf --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/package-info.java @@ -0,0 +1 @@ +package io.fluentqa.base.upload; \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/proxy/UploadFileDataProxy.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/proxy/UploadFileDataProxy.java new file mode 100644 index 0000000..ccd7116 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/proxy/UploadFileDataProxy.java @@ -0,0 +1,64 @@ +package io.fluentqa.base.upload.proxy; + + + +import cn.hutool.core.bean.BeanUtil; +import io.fluentqa.excel.ExcelReadWriter; +import io.fluentqa.base.product.model.ProductModuleModel; +import io.fluentqa.qtm.tc.dto.TestCaseDTO; +import io.fluentqa.qtm.tc.service.TestCaseService; +import io.fluentqa.qtm.tc.service.impl.MindMappingService; +import lombok.extern.slf4j.Slf4j; +import xyz.erupt.core.prop.EruptProp; +import xyz.erupt.core.util.EruptSpringUtil; +import xyz.erupt.jpa.model.MetaDataProxy; +import xyz.erupt.jpa.model.MetaModel; + +import java.util.List; + +@Slf4j +public class UploadFileDataProxy extends MetaDataProxy { + private final MindMappingService mindMappingService; + private final TestCaseService testCaseService; + private final EruptProp eruptProp; + private final ExcelReadWriter excelReadWriter; + + public UploadFileDataProxy() { + mindMappingService = EruptSpringUtil.getBean(MindMappingService.class); + testCaseService = EruptSpringUtil.getBean(TestCaseService.class); + eruptProp = EruptSpringUtil.getBean(EruptProp.class); + excelReadWriter = new ExcelReadWriter(); + } + + @Override + public void beforeAdd(MetaModel metaModel) { + //before add, add some check here + super.beforeAdd(metaModel); + } + + @Override + public void afterAdd(MetaModel metaModel) { + //after add, then doing business process + log.info("start handler uploaded file"); + String filePath = getUploaderFilePath(metaModel); + String uploadType = BeanUtil.getProperty(metaModel, "usage"); + + if(UploadFileTypeEnum.parseType(uploadType).equals(UploadFileTypeEnum.EXCEL_TC)){ + ProductModuleModel product = BeanUtil.getProperty(metaModel, "product"); + ProductModuleModel module = BeanUtil.getProperty(metaModel, "module"); + testCaseService.saveTestCases(getExcelTestCases(filePath),product,module,metaModel.getUpdateBy()); + } + if(UploadFileTypeEnum.parseType(uploadType).equals(UploadFileTypeEnum.FREEMIND)){ + mindMappingService.saveTestCases(filePath,metaModel); + } + } + + private List getExcelTestCases(String filePath){ + return excelReadWriter.readExcel(filePath, TestCaseDTO.class); + } + + private String getUploaderFilePath(MetaModel metaModel) { + return eruptProp.getUploadPath() + BeanUtil.getProperty(metaModel, "attachment"); + } + +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/proxy/UploadFileTypeEnum.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/proxy/UploadFileTypeEnum.java new file mode 100644 index 0000000..f9b14ca --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/proxy/UploadFileTypeEnum.java @@ -0,0 +1,14 @@ +package io.fluentqa.base.upload.proxy; + +public enum UploadFileTypeEnum { + EXCEL_TC,FREEMIND,PM,MINDMAP; + + public static UploadFileTypeEnum parseType(String uploadFileType) { + for (UploadFileTypeEnum uploadFileTypeEnum : UploadFileTypeEnum.values()) { + if (uploadFileTypeEnum.name().equals(uploadFileType)) { + return uploadFileTypeEnum; + } + } + throw new RuntimeException("UploadFileTypeEnum is not found for " + uploadFileType); + } +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/FluentQAApiModule.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/FluentQAApiModule.java new file mode 100644 index 0000000..58a13d4 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/FluentQAApiModule.java @@ -0,0 +1,125 @@ +package io.fluentqa.qtm; + + +import io.fluentqa.qtm.api.model.*; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import xyz.erupt.core.annotation.EruptScan; +import xyz.erupt.core.constant.MenuTypeEnum; +import xyz.erupt.core.module.EruptModule; +import xyz.erupt.core.module.EruptModuleInvoke; +import xyz.erupt.core.module.MetaMenu; +import xyz.erupt.core.module.ModuleInfo; + +import java.util.ArrayList; +import java.util.List; + + +@Configuration +@ComponentScan +@EntityScan +@EruptScan +@EnableConfigurationProperties +public class FluentQAApiModule implements EruptModule { + public FluentQAApiModule() { + } + + @Override + public ModuleInfo info() { + return ModuleInfo.builder().name("fluent-api").build(); + } + + @Override + public void run() { + EruptModule.super.run(); + } + + /** + * API管理: + *

+ * 1. API 仓库管理 + * 2. API 接口定义 + * 3. API 接口录制记录 + * 4. API 接口测试 + * + * @return + */ + @Override + public List initMenus() { + List menus = new ArrayList<>(); + menus.add(MetaMenu.createRootMenu("$APIMgr", "接口管理", "fa fa-exchange", 1)); + + MetaMenu menuForAdded = MetaMenu.createEruptClassMenu(RemoteApi.class, + menus.get(0), 1, MenuTypeEnum.TABLE); + menuForAdded.setIcon("fa fa-scissors"); + menuForAdded.setName("API清单"); + menuForAdded.setCode("$API-List"); + menus.add(menuForAdded); + + MetaMenu rawApiTestCaseMenu = MetaMenu.createEruptClassMenu(RawApiTestCase.class, + menus.get(0), 1, MenuTypeEnum.TABLE); + rawApiTestCaseMenu.setIcon("fa fa-scissors"); + rawApiTestCaseMenu.setName("API生成原始测试用例"); + rawApiTestCaseMenu.setCode("$API-TC-GEN"); + menus.add(rawApiTestCaseMenu); + + MetaMenu apiMonitorRecordMenu = MetaMenu.createEruptClassMenu(ApiMonitorRecord.class, + menus.get(0), 2, MenuTypeEnum.TABLE); + apiMonitorRecordMenu.setIcon("fa fa-repeat"); + apiMonitorRecordMenu.setName("API录制记录"); + apiMonitorRecordMenu.setCode("$API-Record"); + menus.add(apiMonitorRecordMenu); + + MetaMenu apiTestRecord = MetaMenu.createEruptClassMenu( + ApiTestRecord + .class, + menus.get(0), 3, MenuTypeEnum.TABLE); + apiTestRecord.setIcon("fa fa-thumbs-up"); + apiTestRecord.setName("API测试结果记录"); + apiTestRecord.setCode("$API-TestResult"); + menus.add(apiTestRecord); + + MetaMenu apiTestScenarioMenu = MetaMenu.createEruptClassMenu( + ApiTestScenario + .class, + menus.get(0), 4, MenuTypeEnum.TABLE); + apiTestScenarioMenu.setIcon("fa fa-folder"); + apiTestScenarioMenu.setName("API测试场景"); + apiTestScenarioMenu.setCode("$API-TestScenario"); + menus.add(apiTestScenarioMenu); + + MetaMenu apiStepMenu = MetaMenu.createEruptClassMenu( + ApiStep + .class, + menus.get(0), 5, MenuTypeEnum.TABLE); + apiStepMenu.setIcon("fa fa-folder"); + apiStepMenu.setName("API用例步骤"); + apiStepMenu.setCode("$API-Step"); + menus.add(apiStepMenu); +// MetaMenu apiDefMenu = MetaMenu.createSimpleMenu("$API-def", "接口定义", "fa fa-check-square-o", +// menus.get(0), 1, ""); +// menus.add(apiDefMenu); +// addNewMenu( +// menus,"$API-Spec-Git","API定义仓库", "fa fa-meetup", ApiSpecGitRepoModel.class, +// MenuTypeEnum.TABLE,1,0 +// ); +// addNewMenu( +// menus,"$API-Spec","API最新版本", "fa fa-gitlab", ApiSpecVersionModel.class, +// MenuTypeEnum.TABLE,1,1 +// ); + + return menus; + } + + + static { + EruptModuleInvoke.addEruptModule(FluentQAApiModule.class); + } + + @Override + public void initFun() { + EruptModule.super.initFun(); + } +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/handler/GenerateApiCaseByCaptureDataHandler.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/handler/GenerateApiCaseByCaptureDataHandler.java new file mode 100644 index 0000000..3ad4fc3 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/handler/GenerateApiCaseByCaptureDataHandler.java @@ -0,0 +1,24 @@ +package io.fluentqa.qtm.api.handler; + +import io.fluentqa.qtm.api.model.ApiMonitorRecord; +import io.fluentqa.qtm.api.service.ApiTestCaseService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import xyz.erupt.annotation.fun.OperationHandler; + +import javax.annotation.Resource; +import java.util.List; + +@Service +@Slf4j +public class GenerateApiCaseByCaptureDataHandler implements OperationHandler { + + @Resource + private ApiTestCaseService apiService; + @Override + public String exec(List data, Void unused, String[] param) { + log.info("start convert api capture data"); + apiService.convertApiMonitorRecordToTestCase(data); + return null; + } +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/handler/GenerateApiTestStepByApiTestRecord.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/handler/GenerateApiTestStepByApiTestRecord.java new file mode 100644 index 0000000..2ccb338 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/handler/GenerateApiTestStepByApiTestRecord.java @@ -0,0 +1,24 @@ +package io.fluentqa.qtm.api.handler; + +import io.fluentqa.qtm.api.model.ApiTestRecord; +import io.fluentqa.qtm.api.service.ApiTestCaseService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import xyz.erupt.annotation.fun.OperationHandler; + +import javax.annotation.Resource; +import java.util.List; + +@Service +@Slf4j +public class GenerateApiTestStepByApiTestRecord implements OperationHandler { + + @Resource + private ApiTestCaseService apiService; + @Override + public String exec(List data, Void unused, String[] param) { + log.info("start convert api capture data"); + apiService.convertApiTestResultToApiTestStep(data); + return null; + } +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/handler/GenerateRawApiCaseHandler.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/handler/GenerateRawApiCaseHandler.java new file mode 100644 index 0000000..c64a72e --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/handler/GenerateRawApiCaseHandler.java @@ -0,0 +1,22 @@ +package io.fluentqa.qtm.api.handler; + +import io.fluentqa.qtm.api.model.RemoteApi; +import io.fluentqa.qtm.api.service.ApiTestCaseService; +import org.springframework.stereotype.Service; +import xyz.erupt.annotation.fun.OperationHandler; + +import javax.annotation.Resource; +import java.util.List; + +@Service +public class GenerateRawApiCaseHandler implements OperationHandler { + + @Resource + private ApiTestCaseService apiService; + @Override + public String exec(List data, Void unused, String[] param) { + System.out.println("this is tests"); + apiService.convertToRawTestCase(data); + return null; + } +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiMonitorRecord.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiMonitorRecord.java new file mode 100644 index 0000000..ba9ced6 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiMonitorRecord.java @@ -0,0 +1,131 @@ +package io.fluentqa.qtm.api.model; + + +import io.fluentqa.qtm.api.handler.GenerateApiCaseByCaptureDataHandler; +import io.fluentqa.base.handlers.SqlTagFetchHandler; +import lombok.Data; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Layout; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_erupt.RowOperation; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.ViewType; +import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; +import xyz.erupt.annotation.sub_field.sub_edit.TagsType; +import xyz.erupt.jpa.model.MetaModel; + +import javax.persistence.Entity; +import javax.persistence.Table; + +@DynamicUpdate +@DynamicInsert +@Entity +@Table(name = "api_monitor_record") +@Erupt( + name = "接口访问记录", + layout = @Layout( + tableLeftFixed = 3, + pageSize = 30), + power = @Power(importable = true, export = true), + rowOperation = {@RowOperation( + title = "生成接口用例数据", + operationHandler = GenerateApiCaseByCaptureDataHandler.class)}, + orderBy = "ApiMonitorRecord.id desc" + +) +@Data +public class ApiMonitorRecord extends MetaModel { + + @EruptField( + views = @View(title = "app"), + edit = @Edit( + title = "app应用名", + type = EditType.TAGS, search = @Search(vague = true), notNull = true, + tagsType = @TagsType( + fetchHandler = SqlTagFetchHandler.class, + fetchHandlerParams = "select distinct app from api_monitor_record" + ) + )) + private String app; + @EruptField( + views = @View(title = "录制名称"), + edit = @Edit(title = "录制名称", notNull = true, search = @Search) + ) + private String recordName; + @EruptField( + views = @View(title = "请求地址"), + edit = @Edit(title = "请求地址", notNull = true, search = @Search) + ) + private String requestUrl; + + @EruptField( + views = @View(title = "服务"), + edit = @Edit( + title = "服务", + type = EditType.TAGS, search = @Search(vague = true), notNull = true, + tagsType = @TagsType( + fetchHandler = SqlTagFetchHandler.class, + fetchHandlerParams = "select distinct service from api_monitor_record" + ) + ) + ) + + private String service; + @EruptField( + views = @View(title = "接口名称"), + edit = @Edit(title = "接口名称", notNull = true, search = @Search) + ) + private String api; + + @EruptField( + views = @View(title = "服务URL"), + edit = @Edit(title = "服务URL", notNull = true, search = @Search) + ) + private String path; + + @EruptField( + views = @View(title = "请求头"), + edit = @Edit(title = "请求报文", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String requestHeaders; + + @EruptField( + views = @View(title = "HTTP方法"), + edit = @Edit(title = "HTTP方法", notNull = true, search = @Search) + ) + private String method; + + @EruptField( + views = @View(title = "请求报文", type = ViewType.CODE), + edit = @Edit(title = "请求报文", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String requestBody; + + + @EruptField( + views = @View(title = "response_headers"), + edit = @Edit(title = "responseHeaders", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String responseHeaders; + + @EruptField( + views = @View(title = "status_code"), + edit = @Edit(title = "status_code", notNull = true, search = @Search) + ) + private int statusCode; + + @EruptField( + views = @View(title = "返回报文", type = ViewType.CODE), + edit = @Edit(title = "返回报文", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String responseBody; + + + +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiSpecChangeModel.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiSpecChangeModel.java new file mode 100644 index 0000000..bbb757b --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiSpecChangeModel.java @@ -0,0 +1,52 @@ +package io.fluentqa.qtm.api.model; + +import lombok.Data; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Layout; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.jpa.model.BaseModel; + +import javax.persistence.Entity; +import javax.persistence.Table; +import java.time.LocalDateTime; + +@DynamicUpdate +@DynamicInsert +@Entity +@Table(name = "api_spec_change") +@Erupt( + layout = @Layout( + tableLeftFixed = 3, + pageSize = 30), + name = "api spec 变化记录", power = @Power(export = true) +) +@Data +public class ApiSpecChangeModel extends BaseModel { + @EruptField( + views = @View(title = "应用名-appName") + ) + private String name; + @EruptField( + views = @View(title = "GIT URL") + ) + private String gitUrl; + @EruptField( + views = @View(title = "GIT分支") + ) + private String branch; + + @EruptField( + views = @View(title = "创建时间") + ) + private LocalDateTime createdTime; + + @EruptField( + views = @View(title = "appVersion") + ) + private String appVersion; + +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiSpecGitRepoModel.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiSpecGitRepoModel.java new file mode 100644 index 0000000..36d69e8 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiSpecGitRepoModel.java @@ -0,0 +1,47 @@ +package io.fluentqa.qtm.api.model; + +import lombok.Data; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Layout; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.Search; +import xyz.erupt.jpa.model.MetaModel; + +import javax.persistence.Entity; +import javax.persistence.Table; + +@Entity +@Table(name = "apispec_git_repo") +@Data +@Erupt(name = "skel仓库设置", layout = @Layout( + tableLeftFixed = 3, + pageSize = 30), + power = @Power(importable = true, export = true)) +public class ApiSpecGitRepoModel extends MetaModel { + @EruptField( + views = @View(title = "应用名-appName"), + edit = @Edit(title = "应用名-App Name", notNull = true, search = @Search) + ) + private String name; + @EruptField( + views = @View(title = "gitUrl"), + edit = @Edit(title = "gitUrl", notNull = true) + ) + private String gitUrl; + + @EruptField( + views = @View(title = "gitlabId"), + edit = @Edit(title = "gitlabId", notNull = true) + ) + private Integer gitlabId; + + @EruptField( + views = @View(title = "webUrl"), + edit = @Edit(title = "webUrl", notNull = true) + ) + private String webUrl; + +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiSpecVersionModel.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiSpecVersionModel.java new file mode 100644 index 0000000..45d1154 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiSpecVersionModel.java @@ -0,0 +1,102 @@ +package io.fluentqa.qtm.api.model; + +import io.fluentqa.base.model.ModelWithValidFlagVo; +import lombok.Data; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.Where; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Layout; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.ViewType; +import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; +import xyz.erupt.annotation.sub_field.sub_edit.InputType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; + +import javax.persistence.Entity; +import javax.persistence.Table; + +@DynamicUpdate +@DynamicInsert +@Entity +@Table(name = "api_spec_version") +@Erupt( + name = "远程服务原始文件", + power = @Power(export = true), + layout = @Layout( + tableLeftFixed = 3, + pageSize = 30) +) +@Data +@SQLDelete(sql = "update api_spec_version set valid=false where id=?") +@Where(clause = "valid = true") +public class ApiSpecVersionModel extends ModelWithValidFlagVo { + @EruptField( + views = @View( + title = "名称" + ), + edit = @Edit( + title = "名称", + type = EditType.INPUT, search = @Search, notNull = true, + inputType = @InputType + ) + ) + private String name; + + + @EruptField( + views = @View( + title = "名称" + ), + edit = @Edit( + title = "名称", + type = EditType.INPUT, search = @Search, notNull = true, + inputType = @InputType + ) + ) + private String type="POSTMAN"; + + @EruptField( + views = @View( + title = "服务类型" + ), + edit = @Edit( + title = "服务类型", + type = EditType.INPUT, search = @Search, notNull = true, + inputType = @InputType + ) + ) + private String serviceType; //API or RPC + + @EruptField( + views = @View( + title = "版本" + ), + edit = @Edit( + title = "版本", + type = EditType.INPUT, search = @Search, notNull = true, + inputType = @InputType + ) + ) + private String appVersion; + + @EruptField( + views = @View(title = "GIT URL") + ) + private String gitUrl; + @EruptField( + views = @View(title = "GIT分支") + ) + private String branch; + + @EruptField( + views = @View(title = "接口定义", type = ViewType.CODE), + edit = @Edit(title = "接口定义", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String spec; +} \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiStep.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiStep.java new file mode 100644 index 0000000..7d72936 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiStep.java @@ -0,0 +1,156 @@ +package io.fluentqa.qtm.api.model; + +import io.fluentqa.base.handlers.SqlTagFetchHandler; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Layout; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; +import xyz.erupt.annotation.sub_field.sub_edit.TagsType; +import xyz.erupt.jpa.model.MetaModel; + +import javax.persistence.Entity; +import javax.persistence.Table; + +@DynamicUpdate +@DynamicInsert +@Entity +@Table(name = "api_steps") +@Erupt( + name = "接口测试用例", layout = @Layout( + tableLeftFixed = 3, + pageSize = 30), power = @Power(importable = true, export = true), + orderBy = "ApiTestStep.updateTime desc" +) +public class ApiStep extends MetaModel{ + + @EruptField( + views = @View(title = "场景"), + edit = @Edit(title = "场景", notNull = true, search = @Search) + ) + private String scenario; + + @EruptField( + views = @View(title = "用例名称"), + edit = @Edit(title = "用例名称", notNull = true, search = @Search) + ) + private String caseName; + + @EruptField( + views = @View(title = "服务"), + edit = @Edit( + search = @Search, + title = "获取可选服务", + type = EditType.TAGS, + desc = "获取可选服务", + tagsType = @TagsType( + fetchHandler = SqlTagFetchHandler.class, + fetchHandlerParams = "select distinct service_name from remote_services where type='API' and valid=true" + )) + ) + private String serviceName; + @EruptField( + views = @View(title = "服务方法"), + edit = @Edit(title = "服务方法", notNull = true, search = @Search) + ) + private String serviceMethod; + + @EruptField( + views = @View(title = "接口路径"), + edit = @Edit(title = "接口路径", notNull = true, search = @Search) + ) + private String path; + + @EruptField( +// views = @View(title = "测试请求", type = ViewType.CODE), + edit = @Edit(title = "测试请求", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String request; + @EruptField( +// views = @View(title = "接口请求结果"), + edit = @Edit(title = "接口请求结果", + type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String result; + + + @EruptField( +// views = @View(title = "预期结果"), + edit = @Edit(title = "预期结果", notNull = true, type = EditType.CODE_EDITOR, + codeEditType = @CodeEditorType(language = "json")) + ) + private String expect; + + + public String getScenario() { + return scenario; + } + + public void setScenario(String scenario) { + this.scenario = scenario; + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getServiceMethod() { + return serviceMethod; + } + + public void setServiceMethod(String serviceMethod) { + this.serviceMethod = serviceMethod; + } + + public String getCaseName() { + return caseName; + } + + public void setCaseName(String caseName) { + this.caseName = caseName; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getRequest() { + return request; + } + + public void setRequest(String request) { + this.request = request; + } + + public String getResult() { + return result; + } + + public void setResult(String result) { + this.result = result; + } + + + + public String getExpect() { + return expect; + } + + public void setExpect(String expect) { + this.expect = expect; + } +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiTestRecord.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiTestRecord.java new file mode 100644 index 0000000..d901d45 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiTestRecord.java @@ -0,0 +1,131 @@ +package io.fluentqa.qtm.api.model; + +import io.fluentqa.qtm.api.handler.GenerateApiTestStepByApiTestRecord; +import io.fluentqa.base.handlers.SqlTagFetchHandler; +import lombok.Data; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Layout; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_erupt.RowOperation; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; +import xyz.erupt.annotation.sub_field.sub_edit.TagsType; +import xyz.erupt.jpa.model.MetaModel; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 1. 原先的表结构设计几个问题: + * - 没有办法区分那次测试运行记录 + * - 查找不太方便 + * - name 和service name 重复,没有必要同时使用 + * - serviceName 从remote service里面取不过滤API,不够精确 + * 修改方式: + * - name 修改为测试用例运行名称 + * - serviceName 取tags 从remote service 的API 中取 + */ +@DynamicUpdate +@DynamicInsert +@Entity +@Table(name = "api_test_record") +@Erupt( + name = "接口测试结果", layout = @Layout( + tableLeftFixed = 3, + pageSize = 30), power = @Power(importable = true, export = true), + orderBy = "ApiTestRecord.id desc", + rowOperation = {@RowOperation( + title = "生成可用测试步骤", + operationHandler = GenerateApiTestStepByApiTestRecord.class)} +) +@Data +public class ApiTestRecord extends MetaModel { + @EruptField( + views = @View(title = "测试运行名称"), + edit = @Edit(title = "name", search = @Search) + ) + private String name; + + @EruptField( + views = @View(title = "场景"), + edit = @Edit(title = "场景", notNull = true, search = @Search) + ) + private String scenario; + + @EruptField( + views = @View(title = "用例名称"), + edit = @Edit(title = "用例名称", notNull = true, search = @Search) + ) + private String caseName; + + @EruptField( + views = @View(title = "服务"), + edit = @Edit( + search = @Search, + title = "获取可选服务", + type = EditType.TAGS, + desc = "获取可选服务", + tagsType = @TagsType( + fetchHandler = SqlTagFetchHandler.class, + fetchHandlerParams = "select distinct service_name from remote_services where type='API' and valid=true" + )) + ) + private String serviceName; + @EruptField( + views = @View(title = "服务方法"), + edit = @Edit(title = "服务方法", notNull = true, search = @Search) + ) + private String serviceMethod; + + @EruptField( + views = @View(title = "接口路径"), + edit = @Edit(title = "接口路径", notNull = true, search = @Search) + ) + private String path; + + @EruptField( +// views = @View(title = "测试请求", type = ViewType.CODE), + edit = @Edit(title = "测试请求", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String request; + @EruptField( +// views = @View(title = "接口请求结果"), + edit = @Edit(title = "接口请求结果", + type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String result; + + @EruptField( + views = @View(title = "状态码"), + edit = @Edit(title = "状态码", notNull = true, search = @Search) + ) + private String statusCode; + + @EruptField( + views = @View(title = "错误日志"), + edit = @Edit(title = "错误日志", + type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String errorLog; + + @EruptField( +// views = @View(title = "预期结果"), + edit = @Edit(title = "预期结果", notNull = true, type = EditType.CODE_EDITOR, + codeEditType = @CodeEditorType(language = "json")) + ) + private String expect; + + //TODO: 通过或者失败 + @EruptField( + views = @View(title = "用例执行结果"), + edit = @Edit(title = "用例执行结果", search = @Search) + ) + private boolean isSuccess; + +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiTestScenario.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiTestScenario.java new file mode 100644 index 0000000..4055930 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiTestScenario.java @@ -0,0 +1,157 @@ +package io.fluentqa.qtm.api.model; + +import io.fluentqa.base.handlers.SqlTagFetchHandler; +import io.fluentqa.base.model.ModelWithValidFlag; +import io.fluentqa.base.product.model.ProductModuleModel; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Layout; +import xyz.erupt.annotation.sub_erupt.LinkTree; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; +import xyz.erupt.annotation.sub_field.sub_edit.ReferenceTreeType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; +import xyz.erupt.annotation.sub_field.sub_edit.TagsType; + +import javax.persistence.*; +import java.util.Set; + +/** + * + */ +@Entity +@Erupt(name = "接口测试用例", + power = @Power(export = true), + orderBy = "ApiTestScenario.updateTime desc", + linkTree = @LinkTree(field = "module"), + layout = @Layout( + tableLeftFixed = 3, + pageSize = 30)) +@Table(name = "api_test_scenario") +public class ApiTestScenario extends ModelWithValidFlag { + + @ManyToOne + @JoinColumn(name = "product_id") + @EruptField( + views = @View(title = "所属模块", column = "details"), + edit = @Edit( + notNull = true, + search = @Search, + title = "产品模块选择", + type = EditType.REFERENCE_TREE, + desc = "动态获取产品", + referenceTreeType = @ReferenceTreeType(id = "id", label = "name", + pid = "parent.id")) + ) + private ProductModuleModel module; + + + @EruptField( + views = @View( + title = "测试场景" + ), + edit = @Edit( + title = "测试场景", + type = EditType.INPUT, search = @Search, notNull = true + ) + ) + private String testScenario; + + @EruptField( + views = @View( + title = "测试场景详细描述" + ), + edit = @Edit( + title = "测试场景详细描述", + type = EditType.TEXTAREA, search = @Search, notNull = true + ) + ) + private String details; + + @EruptField( + views = @View( + title = "优先级" + ), + edit = @Edit( + title = "优先级", + type = EditType.TAGS, + search = @Search, + tagsType = @TagsType( + fetchHandler = SqlTagFetchHandler.class, + fetchHandlerParams = "select distinct key,detail from master_data where category_code = 'PRIORITY' order by 1 " + ) + ) + ) + private String priority = "P2"; + + @EruptField( + views = @View(title = "场景参数"), + edit = @Edit(title = "场景参数", + type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String scenarioParameters; + + @JoinTable(name = "api_test_scenario_steps", + joinColumns = @JoinColumn(name = "api_test_scenario_id", referencedColumnName = "id"), + inverseJoinColumns = @JoinColumn(name = "api_test_step_id", referencedColumnName = "id")) + @ManyToMany(fetch = FetchType.EAGER) + @EruptField( + views = @View(title = "包含用例"), + edit = @Edit( + title = "包含用例", + type = EditType.TAB_TABLE_REFER + ) + ) + private Set testSteps; + public String getPriority() { + return priority; + } + + public void setPriority(String priority) { + this.priority = priority; + } + + + public ProductModuleModel getModule() { + return module; + } + + public void setModule(ProductModuleModel module) { + this.module = module; + } + + public String getTestScenario() { + return testScenario; + } + + public void setTestScenario(String testScenario) { + this.testScenario = testScenario; + } + + public String getDetails() { + return details; + } + + public void setDetails(String details) { + this.details = details; + } + + public String getScenarioParameters() { + return scenarioParameters; + } + + public void setScenarioParameters(String scenarioParameters) { + this.scenarioParameters = scenarioParameters; + } + + public Set getTestSteps() { + return testSteps; + } + + public void setTestSteps(Set testSteps) { + this.testSteps = testSteps; + } +} \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RawApiTestCase.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RawApiTestCase.java new file mode 100644 index 0000000..96441b0 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RawApiTestCase.java @@ -0,0 +1,96 @@ +package io.fluentqa.qtm.api.model; + +import lombok.Data; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Layout; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.ViewType; +import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; +import xyz.erupt.jpa.model.MetaModel; + +import javax.persistence.Entity; +import javax.persistence.Table; + + +@DynamicUpdate +@DynamicInsert +@Entity +@Table(name = "raw_api_cases") +@Erupt( + name = "接口测试用例生成", layout = @Layout( + tableLeftFixed = 3, + pageSize = 30), power = @Power(importable = true, export = true), + orderBy = "RawApiTestCase.createTime " +) +@Data +public class RawApiTestCase extends MetaModel{ + @EruptField( + views = @View(title = "测试场景"), + edit = @Edit(title = "测试场景", search = @Search) + ) + private String scenario; + + @EruptField( + views = @View(title = "服务名称"), + edit = @Edit(title = "服务名称", notNull = true, search = @Search) + ) + private String serviceName; + + @EruptField( + views = @View(title = "API接口"), + edit = @Edit(title = "API接口", notNull = true, search = @Search) + ) + private String serviceMethod; + + @EruptField( + views = @View(title = "用例名称"), + edit = @Edit(title = "用例名称", notNull = true, search = @Search) + ) + private String name; + + @EruptField( + views = @View(title = "请求路径"), + edit = @Edit(title = "请求路径", notNull = true, search = @Search) + ) + private String uri; + + @EruptField( + views = @View(title = "请求方法"), + edit = @Edit(title = "请求方法", notNull = true) + ) + private String method = "POST"; + + @EruptField( + views = @View(title = "输入", type = ViewType.CODE), + edit = @Edit(title = "输入", + type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String input; + + @EruptField( + views = @View(title = "期望结果", type = ViewType.CODE), + edit = @Edit(title = "期望结果", type = EditType.CODE_EDITOR, + codeEditType = @CodeEditorType(language = "json")) + ) + private String expected; + + @EruptField( + views = @View(title = "优先级"), + edit = @Edit(title = "优先级") + ) + private String priority = "P1"; + + @EruptField( + views = @View(title = "是否运行"), + edit = @Edit(title = "是否运行") + ) + private boolean isRun = true; + +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RemoteApi.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RemoteApi.java new file mode 100644 index 0000000..239ca7a --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RemoteApi.java @@ -0,0 +1,275 @@ +package io.fluentqa.qtm.api.model; + +import cn.hutool.core.lang.UUID; +import io.fluentqa.qtm.api.handler.GenerateRawApiCaseHandler; +import io.fluentqa.base.handlers.SqlTagFetchHandler; +import io.fluentqa.base.model.ModelWithValidFlag; +import lombok.Data; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.Where; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Layout; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_erupt.RowOperation; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.ViewType; +import xyz.erupt.annotation.sub_field.sub_edit.*; +import xyz.erupt.toolkit.handler.SqlChoiceFetchHandler; + +import javax.persistence.Entity; +import javax.persistence.Table; + +@DynamicUpdate +@DynamicInsert +@Entity +@Table(name = "remote_services") +@Erupt( + name = "远程服务清单", layout = @Layout( + tableLeftFixed = 3, + pageSize = 30), + power = @Power(export = true), + rowOperation = {@RowOperation( + title = "生成原始接口用例", + operationHandler = GenerateRawApiCaseHandler.class)} +) +@Data +@SQLDelete(sql = "update remote_services set valid=false where id=?") +@Where(clause = "valid = true") +public class RemoteApi extends ModelWithValidFlag { + @EruptField( + views = @View( + title = "名称" + ), + edit = @Edit( + title = "名称", + type = EditType.INPUT, search = @Search, notNull = true, + inputType = @InputType + ) + ) + private String name; + + @EruptField( + views = @View( + title = "产品" + ), + edit = @Edit( + title = "产品", + type = EditType.CHOICE, + desc = "获取产品", + choiceType = @ChoiceType( + fetchHandler = SqlChoiceFetchHandler.class, + fetchHandlerParams = "select id,name,details from products where valid =true and parent_id is NULL" + )) + ) + private Long productId; + + @EruptField( + views = @View(title = "模块名"), + edit = @Edit( + search = @Search, + title = "获取可选模块", + type = EditType.TAGS, + desc = "动态获取可选模块", + tagsType = @TagsType( + fetchHandler = SqlTagFetchHandler.class, + fetchHandlerParams = "select distinct module_name from remote_services where valid=true" + )) + ) + private String moduleName; + + @EruptField( + views = @View(title = "服务"), + edit = @Edit( + search = @Search, + title = "获取可选服务", + type = EditType.TAGS, + desc = "获取可选服务", + tagsType = @TagsType( + fetchHandler = SqlTagFetchHandler.class, + fetchHandlerParams = "select distinct service_name from remote_services where valid=true" + )) + ) + private String serviceName; + + @EruptField( + views = @View(title = "地址"), + edit = @Edit(title = "地址", notNull = true) + ) + private String endpoint; + + @EruptField( + views = @View(title = "方法"), + edit = @Edit(title = "方法", notNull = true, search = @Search) + ) + private String serviceMethod; + + @EruptField( + views = @View(title = "http请求方法"), + edit = @Edit(title = "http请求方法", notNull = true) + ) + private String httpMethod; + + @EruptField( + views = @View(title = "请求报文", type = ViewType.CODE), + edit = @Edit(title = "请求报文", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String body; + + @EruptField( + views = @View(title = "接口类型"), + edit = @Edit(title = "接口类型", search = @Search, + type = EditType.CHOICE, choiceType = @ChoiceType( + vl = {@VL(value = "API", label = "API"), @VL(value = "RPC", label = "RPC")} + )) + ) + private String type; + + @EruptField( + views = @View(title = "服务使用状态"), + edit = @Edit(title = "服务使用状态", + search = @Search, notNull = true, + type = EditType.CHOICE, choiceType = @ChoiceType( + vl = {@VL(value = "NEW", label = "新增"), + @VL(value = "IN_USE", label = "使用中"), + @VL(value = "UPDATE", label = "更新"), + @VL(value = "END_OF_LIFE", label = "已作废"),}))) + private String status; + + @EruptField( + views = @View( + title = "引入时版本" + ), + edit = @Edit( + title = "引入时版本", + type = EditType.INPUT, search = @Search, notNull = true, + inputType = @InputType + ) + ) + private String addedVersion; + + @EruptField( + views = @View( + title = "最新版本" + ), + edit = @Edit( + title = "最新版本", + type = EditType.INPUT, search = @Search, notNull = true, + inputType = @InputType + ) + ) + private String latestVersion; + + @EruptField( + views = @View(show = false, title = "uid") + ) + private String uId = UUID.fastUUID().toString(); + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Long getProductId() { + return productId; + } + + public void setProductId(Long productId) { + this.productId = productId; + } + + public String getModuleName() { + return moduleName; + } + + public void setModuleName(String moduleName) { + this.moduleName = moduleName; + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getEndpoint() { + return endpoint; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public String getServiceMethod() { + return serviceMethod; + } + + public void setServiceMethod(String serviceMethod) { + this.serviceMethod = serviceMethod; + } + + public String getHttpMethod() { + return httpMethod; + } + + public void setHttpMethod(String httpMethod) { + this.httpMethod = httpMethod; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getAddedVersion() { + return addedVersion; + } + + public void setAddedVersion(String addedVersion) { + this.addedVersion = addedVersion; + } + + public String getLatestVersion() { + return latestVersion; + } + + public void setLatestVersion(String latestVersion) { + this.latestVersion = latestVersion; + } + + public String getuId() { + return uId; + } + + public void setuId(String uId) { + this.uId = uId; + } +} \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RemoteApiStatus.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RemoteApiStatus.java new file mode 100644 index 0000000..7803e71 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RemoteApiStatus.java @@ -0,0 +1,5 @@ +package io.fluentqa.qtm.api.model; + +public enum RemoteApiStatus { + NEW,IN_USE,UPDATED,END_OF_LIFE +} \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RemoteApiType.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RemoteApiType.java new file mode 100644 index 0000000..77bcba5 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RemoteApiType.java @@ -0,0 +1,5 @@ +package io.fluentqa.qtm.api.model; + +public enum RemoteApiType { + API,RPC +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/package-info.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/package-info.java new file mode 100644 index 0000000..60e98c6 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/package-info.java @@ -0,0 +1 @@ +package io.fluentqa.qtm.api; \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiMonitorRecordRepo.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiMonitorRecordRepo.java new file mode 100644 index 0000000..fc3cf8a --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiMonitorRecordRepo.java @@ -0,0 +1,14 @@ +package io.fluentqa.qtm.api.repo; + +import io.fluentqa.qtm.api.model.ApiMonitorRecord; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface ApiMonitorRecordRepo extends JpaRepository, JpaSpecificationExecutor { + + List findApiMonitorRecordByPath(String path); +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiSpecChangeRepository.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiSpecChangeRepository.java new file mode 100644 index 0000000..f186062 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiSpecChangeRepository.java @@ -0,0 +1,11 @@ +package io.fluentqa.qtm.api.repo; + + +import io.fluentqa.qtm.api.model.ApiSpecChangeModel; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ApiSpecChangeRepository extends JpaRepository { + +} \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiSpecGitRepoRepository.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiSpecGitRepoRepository.java new file mode 100644 index 0000000..64af875 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiSpecGitRepoRepository.java @@ -0,0 +1,10 @@ +package io.fluentqa.qtm.api.repo; + +import io.fluentqa.qtm.api.model.ApiSpecGitRepoModel; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ApiSpecGitRepoRepository extends JpaRepository { + +} \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiSpecVersionRepository.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiSpecVersionRepository.java new file mode 100644 index 0000000..5f8c1bd --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiSpecVersionRepository.java @@ -0,0 +1,10 @@ +package io.fluentqa.qtm.api.repo; + +import io.fluentqa.qtm.api.model.ApiSpecVersionModel; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ApiSpecVersionRepository extends JpaRepository { + +} \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiTestScenarioRepo.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiTestScenarioRepo.java new file mode 100644 index 0000000..d0c358a --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiTestScenarioRepo.java @@ -0,0 +1,10 @@ +package io.fluentqa.qtm.api.repo; + +import io.fluentqa.qtm.api.model.ApiTestScenario; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.stereotype.Repository; + +@Repository +public interface ApiTestScenarioRepo extends JpaRepository, JpaSpecificationExecutor { +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiTestStepRepo.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiTestStepRepo.java new file mode 100644 index 0000000..82cac02 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiTestStepRepo.java @@ -0,0 +1,10 @@ +package io.fluentqa.qtm.api.repo; + +import io.fluentqa.qtm.api.model.ApiStep; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.stereotype.Repository; + +@Repository +public interface ApiTestStepRepo extends JpaRepository, JpaSpecificationExecutor { +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/RawApiTestCaseRepo.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/RawApiTestCaseRepo.java new file mode 100644 index 0000000..cd2e625 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/RawApiTestCaseRepo.java @@ -0,0 +1,10 @@ +package io.fluentqa.qtm.api.repo; + +import io.fluentqa.qtm.api.model.RawApiTestCase; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.stereotype.Repository; + +@Repository +public interface RawApiTestCaseRepo extends JpaRepository, JpaSpecificationExecutor { +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/RemoteServiceRepo.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/RemoteServiceRepo.java new file mode 100644 index 0000000..5213fd1 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/RemoteServiceRepo.java @@ -0,0 +1,23 @@ +package io.fluentqa.qtm.api.repo; + +import io.fluentqa.qtm.api.model.RemoteApi; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface RemoteServiceRepo extends JpaRepository, JpaSpecificationExecutor { + + Optional findRemoteApiByEndpointAndServiceNameAndServiceMethod( + String endpoint,String serviceName,String serviceMethod + ); + + Optional> findRemoteApiByModuleNameAndServiceNameAndLatestVersionNot( + String moduleName,String serviceName,String latestVersion + ); + + +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/service/ApiTestCaseService.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/service/ApiTestCaseService.java new file mode 100644 index 0000000..018a7e4 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/service/ApiTestCaseService.java @@ -0,0 +1,107 @@ +package io.fluentqa.qtm.api.service; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.StrUtil; +import io.fluent.builtin.CollectionsUtils; +import io.fluentqa.qtm.api.model.*; +import io.fluentqa.qtm.api.repo.ApiMonitorRecordRepo; +import io.fluentqa.qtm.api.repo.ApiTestStepRepo; +import io.fluentqa.qtm.api.repo.RawApiTestCaseRepo; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +@Service +public class ApiTestCaseService { + private final String DEFAULT_EXPECTED = "{ \"status_code\":200,\"values\":{}\n" + + "}"; + @Resource + RawApiTestCaseRepo rawApiTestCaseRepo; + + @Resource + ApiMonitorRecordRepo apiMonitorRecordRepo; + + @Resource + ApiTestStepRepo apiTestStepRepo; + + + /** + * @param services: HTTP API Services + * 1. Merge All HttpAPI Services if method,service and request-body/input is same + */ + public void convertToRawTestCase(List services) { + List cases = new ArrayList<>(); + services.forEach(service -> { + RawApiTestCase apiCase = new RawApiTestCase(); + BeanUtils.copyProperties(service, apiCase); + apiCase.setUri(service.getEndpoint()); + apiCase.setExpected(DEFAULT_EXPECTED); + //get body + String path = service.getEndpoint().replaceAll("https://\\{\\{base_url\\}\\}", ""); + List result = apiMonitorRecordRepo.findApiMonitorRecordByPath(path); + if (!result.isEmpty()) { + apiCase.setInput(result.get(0).getRequestBody()); + } else { + apiCase.setInput(service.getBody()); + } + cases.add(apiCase); + }); + rawApiTestCaseRepo.saveAll(cases); + } + + /** + * @param records Http traffic + * 1. Merge All HttpAPI Services if method,service and request-body/input is same + */ + public void convertApiMonitorRecordToTestCase(List records) { + List cases = new ArrayList<>(); + List result = CollectionsUtils.filterToReduceRedundant( + records, new Function() { + @Override + public String apply(ApiMonitorRecord apiMonitorRecord) { + return StrUtil.join( + "-", apiMonitorRecord.getApi(), apiMonitorRecord.getApp(), + apiMonitorRecord.getService(), apiMonitorRecord.getPath(), + apiMonitorRecord.getMethod(), apiMonitorRecord.getRequestBody() + ); + } + } + ); + result.forEach(service -> { + RawApiTestCase apiCase = new RawApiTestCase(); + BeanUtils.copyProperties(service, apiCase); + apiCase.setUri(service.getPath()); + apiCase.setExpected(DEFAULT_EXPECTED); + apiCase.setServiceMethod(service.getMethod()); + apiCase.setServiceName(service.getService()); + apiCase.setName(service.getService()); + apiCase.setServiceMethod(service.getApi()); + //get body + apiCase.setInput(service.getRequestBody()); + apiCase.setScenario(service.getRecordName()); + cases.add(apiCase); + }); + rawApiTestCaseRepo.saveAll(cases); + } + + /** + * Only Passed Test Scenario could be converted into test steps + * + * @param data + */ + public void convertApiTestResultToApiTestStep(List data) { + List apiTestSteps = new ArrayList<>(); + data.forEach((apiTestRecord) -> { + if (apiTestRecord.isSuccess()) { + apiTestSteps.add(BeanUtil.copyProperties(apiTestRecord, ApiStep.class)); + } else { + throw new RuntimeException("some cases are failed, can't convert to api test step"); + } + }); + apiTestStepRepo.saveAll(apiTestSteps); + } +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/service/RemoteApiService.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/service/RemoteApiService.java new file mode 100644 index 0000000..91fcd73 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/service/RemoteApiService.java @@ -0,0 +1,142 @@ +package io.fluentqa.qtm.api.service; + +import cn.hutool.core.bean.BeanUtil; +import io.fluent.postman.PostmanParser; +import io.fluent.postman.model.PostmanCollection; +import io.fluent.postman.model.PostmanItem; +import io.fluentqa.qtm.api.model.ApiSpecVersionModel; +import io.fluentqa.qtm.api.model.RemoteApi; +import io.fluentqa.qtm.api.model.RemoteApiStatus; +import io.fluentqa.qtm.api.repo.RemoteServiceRepo; +import io.fluentqa.base.product.model.ProductModuleModel; +import io.fluentqa.base.product.service.ProductModuleService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.util.List; +import java.util.Optional; + +@Service +@Slf4j +public class RemoteApiService { + + @Autowired + private ProductModuleService productMetaService; + + @Autowired + private RemoteServiceRepo remoteServiceRepo; + + + /** + * 1. 解析postman 文件 + * 2. 确认接口是 + * - 新增:NEW: 当前记录无记录,则更新 + * - 更新:UPDATED: 当前记录中有记录,但是接口定义发生变化,比如请求内容 + * - 使用中,IN_USE: + * - 移除:END_OF_LIFE: 接口不在最新清单中 + * 3. + * + * @param apiSpec + */ + @Transactional + public void apiSpecToApiList(ApiSpecVersionModel apiSpec, String updater) { + ProductModuleModel productMeta = productMetaService.createApiModuleIfNotExist(apiSpec.getName(), updater); + if (apiSpec.getSpec().isEmpty()) return; + PostmanCollection collection = PostmanParser.create().toPostmanCollection(apiSpec.getSpec()); + for (PostmanItem postmanItem : collection.getItem()) { + for (PostmanItem item : postmanItem.getItem()) { + RemoteApi rs = toRemoteApi(apiSpec, productMeta, postmanItem, item); + createOrUpdateRemoteApi(rs, apiSpec); + } + updateStatusToEndOfLife(apiSpec, postmanItem); + } + } + + @Transactional + public void apiSpecsToApiList(List apiSpecs, String updater) { + for (ApiSpecVersionModel apiSpec : apiSpecs) { + try { + this.apiSpecToApiList(apiSpec, updater); + } catch (Exception e) { + log.error("%s-api-failed,error=%s".formatted( + apiSpec.getName(), e.getMessage() + )); + } + } + } + + private RemoteApi toRemoteApi(ApiSpecVersionModel apiSpec, ProductModuleModel productMeta, + PostmanItem postmanItem, PostmanItem item) { + RemoteApi rs = new RemoteApi(); + rs.setName(item.getName()); + rs.setServiceName(postmanItem.getName()); + rs.setServiceMethod(item.getName()); + rs.setBody(item.getRequest().getBody().get("raw").toString()); + rs.setHttpMethod(item.getRequest().getMethod()); + rs.setEndpoint(item.getRequest().getUrl().getRaw()); + rs.setType(apiSpec.getServiceType()); + rs.setModuleName(apiSpec.getName()); + rs.setProductId(productMeta.getParent().getId()); + return rs; + } + + /** + * create or update 基本可以使用同一种方式处理 + * 1. 输入实体 + * 2. 字段检验规则 + * 3. 判断重复确认条件 + * 4. 更新字段处理,保存记录 + * TODO: try to integrate with Feishu + * + * @param newApi + */ + public void createOrUpdateRemoteApi(RemoteApi newApi, ApiSpecVersionModel apiSpec) { + Optional api = remoteServiceRepo.findRemoteApiByEndpointAndServiceNameAndServiceMethod( + newApi.getEndpoint(), + newApi.getServiceName(), + newApi.getServiceMethod()); + if (api.isEmpty()) { + newApi.setStatus(RemoteApiStatus.NEW.toString()); + newApi.setAddedVersion(apiSpec.getAppVersion()); + newApi.setLatestVersion(apiSpec.getAppVersion()); + remoteServiceRepo.save(newApi); + } else { + RemoteApi existApi = api.get(); + BeanUtil.copyProperties(newApi, existApi, "id"); + //TODO: 如何确认接口变更-暂时不确认 + if (existApi.getBody().equalsIgnoreCase(newApi.getBody())) { + existApi.setStatus(RemoteApiStatus.IN_USE.toString()); + existApi.setLatestVersion(apiSpec.getAppVersion()); + } else { + existApi.setStatus(RemoteApiStatus.UPDATED.toString()); + existApi.setLatestVersion(apiSpec.getAppVersion()); + } + remoteServiceRepo.save(existApi); + } + } + + public void updateStatusToEndOfLife(ApiSpecVersionModel apiSpec, PostmanItem postmanItem) { + Optional> apiListOptional = remoteServiceRepo.findRemoteApiByModuleNameAndServiceNameAndLatestVersionNot( + apiSpec.getName(), + postmanItem.getName(), + apiSpec.getAppVersion() + ); + if (apiListOptional.isEmpty()) { + log.info("没有需要删除的数据"); + } else { + List apiList = apiListOptional.get(); + for (RemoteApi api : apiList) { + api.setStatus(RemoteApiStatus.END_OF_LIFE.toString()); + remoteServiceRepo.save(api); + log.info("有需要删除的数据"); + } + + } + } + + +} + + diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/package-info.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/package-info.java new file mode 100644 index 0000000..65ca3a8 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/package-info.java @@ -0,0 +1 @@ +package io.fluentqa.qtm.tc.pm; \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/FieldOption.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/FieldOption.java new file mode 100644 index 0000000..8a0f6ee --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/FieldOption.java @@ -0,0 +1,158 @@ +package io.fluentqa.qtm.pm.requirement; + + +import io.fluentqa.base.model.ModelWithValidFlag; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.fun.ChoiceFetchHandler; +import xyz.erupt.annotation.fun.VLModel; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.ChoiceType; +import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; + +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Erupt(name = "字段选项", + power = @Power(export = true, importable = true), + orderBy = "FieldOption.updateTime desc") +@Table(name = "field_option") +public class FieldOption extends ModelWithValidFlag implements ChoiceFetchHandler { + @EruptField( + views = @View( + title = "名称" + ), + edit = @Edit( + title = "名称",notNull = true + ) + ) + private String name; + @EruptField( + views = @View( + title = "字段code" + ), + edit = @Edit( + title = "字段code" + ) + ) + private String code; + + @EruptField( + views = @View(title = "编辑类型"), + edit = @Edit(title = "编辑类型", + notNull = true, type = EditType.CHOICE, + choiceType = @ChoiceType(type = ChoiceType.Type.RADIO, fetchHandler = FieldOption.class)) + ) + private String type; + + @EruptField( + views = @View( + title = "是否可以为空" + ), + edit = @Edit( + title = "是否可以为空", + type = EditType.BOOLEAN + ) + ) + private boolean notNull; + @EruptField( + views = @View( + title = "字段约束条件" + ), + edit = @Edit( + title = "字段约束条件", + type = EditType.CODE_EDITOR, notNull = true, + codeEditType = @CodeEditorType(language = "text"), + desc = "字段约束条件" + ) + ) + private String constrains; + + @EruptField( + views = @View( + title = "和其他业务关系" + ), + edit = @Edit( + title = "和其他业务关系", + type = EditType.CODE_EDITOR, + codeEditType = @CodeEditorType(language = "text") + ) + ) + private String relatedTo; + + @EruptField( + views = @View(title = "显示顺序", sortable = true), + edit = @Edit(title = "显示顺序", notNull = true) + ) + private Integer sort; + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public boolean isNotNull() { + return notNull; + } + + public void setNotNull(boolean notNull) { + this.notNull = notNull; + } + + public String getConstrains() { + return constrains; + } + + public void setConstrains(String constrains) { + this.constrains = constrains; + } + + public String getRelatedTo() { + return relatedTo; + } + + public void setRelatedTo(String relatedTo) { + this.relatedTo = relatedTo; + } + + public Integer getSort() { + return sort; + } + + public void setSort(Integer sort) { + this.sort = sort; + } + + @Override + public List fetch(String[] params) { + List list = new ArrayList<>(); + for (FieldOptionType value : FieldOptionType.values()) { + list.add(new VLModel(value.name(), value.getDesc())); + } + return list; + } +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/FieldOptionType.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/FieldOptionType.java new file mode 100644 index 0000000..44c921c --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/FieldOptionType.java @@ -0,0 +1,13 @@ +package io.fluentqa.qtm.pm.requirement; + +import lombok.Getter; + +@Getter +public enum FieldOptionType { + NUMBER("数值"),STRING("字符串"),RELATION("关联"),DATE("日期"),ENUM("枚举"),BOOLEAN("布尔"); + + private String desc; + FieldOptionType(String desc) { + this.desc = desc; + } +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/RequirementFeature.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/RequirementFeature.java new file mode 100644 index 0000000..a9432f7 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/RequirementFeature.java @@ -0,0 +1,34 @@ +package io.fluentqa.qtm.pm.requirement; + +import io.fluentqa.base.model.ModelWithValidFlag; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; + +import javax.persistence.Entity; +import javax.persistence.Table; + +@Entity +@Erupt(name = "需求功能点", + power = @Power(export = true, importable = true), + orderBy = "RequirementFeature.updateTime desc") +@Table(name = "requirement_features") +public class RequirementFeature extends ModelWithValidFlag { + + @EruptField( + views = @View( + title = "业务功能相关说明" + ), + edit = @Edit( + title = "业务功能相关说明", + type = EditType.CODE_EDITOR, notNull = true, + codeEditType = @CodeEditorType(language = "text") + ) + ) + private String feature; + +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/RequirementType.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/RequirementType.java new file mode 100644 index 0000000..d960f62 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/RequirementType.java @@ -0,0 +1,15 @@ +package io.fluentqa.qtm.pm.requirement; + +import lombok.Getter; + +@Getter +public enum RequirementType { + CREATE("创建"),UPDATE("更新"),DELETE("删除/归档"),SEARCH("查询"), COMPLEX("复杂业务"), + WORKFLOW("工作流"), + REPORT("报表"),OTHER("其他"); + + private String desc; + RequirementType(String desc) { + this.desc = desc; + } +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/TestRequirement.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/TestRequirement.java new file mode 100644 index 0000000..6f8bd08 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/TestRequirement.java @@ -0,0 +1,192 @@ +package io.fluentqa.qtm.pm.requirement; + + +import io.fluentqa.base.handlers.SqlTagFetchHandler; +import io.fluentqa.base.model.ModelWithValidFlag; +import io.fluentqa.base.product.model.ProductModuleModel; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.fun.ChoiceFetchHandler; +import xyz.erupt.annotation.fun.VLModel; +import xyz.erupt.annotation.sub_erupt.LinkTree; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.ChoiceType; +import xyz.erupt.annotation.sub_field.sub_edit.ReferenceTreeType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; +import xyz.erupt.annotation.sub_field.sub_edit.TagsType; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + + +@Entity +@Erupt(name = "测试需求管理", + power = @Power(export = true, importable = true), + orderBy = "TestRequirement.updateTime desc", + linkTree = @LinkTree(field = "module")) +@Table(name = "test_requirements") +public class TestRequirement extends ModelWithValidFlag implements ChoiceFetchHandler { + + @EruptField( + views = @View(title = "需求概述"), + edit = @Edit(title = "需求概述") + ) + private String summary; + + @EruptField( + views = @View(title = "需求类型"), + edit = @Edit(title = "需求类型", + notNull = true, type = EditType.CHOICE, + choiceType = @ChoiceType(type = ChoiceType.Type.RADIO, + fetchHandler = TestRequirement.class)) + ) + private String type; + + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) + @JoinColumn(name = "test_req_id") + @EruptField( + edit = @Edit(title = "功能点", type = EditType.TAB_TABLE_ADD) + ) + private Set features; + + + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) + @JoinColumn(name = "test_req_id") + @OrderBy("sort") + @EruptField( + edit = @Edit(title = "字段管理", type = EditType.TAB_TABLE_ADD) + ) + private Set fieldOptions; + + @ManyToOne + @JoinColumn(name = "product_id") + @EruptField( + views = @View(title = "所属模块", column = "details"), + edit = @Edit( + notNull = true, + search = @Search, + title = "产品模块选择", + type = EditType.REFERENCE_TREE, + desc = "动态获取产品", + referenceTreeType = @ReferenceTreeType(id = "id", label = "name", + pid = "parent.id")) + ) + private ProductModuleModel module; + + @EruptField( + views = @View( + title = "提示词" + ), + edit = @Edit( + title = "提示词" + ) + ) + private String prompts; + + @EruptField( + views = @View( + title = "优先级" + ), + edit = @Edit( + search = @Search, + title = "优先级", + type = EditType.TAGS, + tagsType = @TagsType( + fetchHandler = SqlTagFetchHandler.class, + fetchHandlerParams = "select distinct key,detail from master_data where category_code = 'PRIORITY' order by 1 " + ) + ) + ) + private String priority = "P2"; + + @EruptField( + views = @View( + title = "需求状态" + ), + edit = @Edit( + title = "需求状态" + ) + ) + private String status; + + @Override + public List fetch(String[] params) { + List list = new ArrayList<>(); + for (RequirementType value : RequirementType.values()) { + list.add(new VLModel(value.name(), value.getDesc())); + } + return list; + } + + public String getPriority() { + return priority; + } + + public void setPriority(String priority) { + this.priority = priority; + } + + + public String getPrompts() { + return prompts; + } + + public void setPrompts(String prompts) { + this.prompts = prompts; + } + + + public Set getFieldOptions() { + return fieldOptions; + } + + public void setFieldOptions(Set fieldOptions) { + this.fieldOptions = fieldOptions; + } + + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Set getFeatures() { + return features; + } + + public void setFeatures(Set features) { + this.features = features; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public ProductModuleModel getModule() { + return module; + } + + public void setModule(ProductModuleModel module) { + this.module = module; + } +} \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/dto/TestCaseDTO.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/dto/TestCaseDTO.java new file mode 100644 index 0000000..bc1c15f --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/dto/TestCaseDTO.java @@ -0,0 +1,28 @@ +package io.fluentqa.qtm.tc.dto; + +import com.github.crab2died.annotation.ExcelField; +import lombok.Data; + +@Data +public class TestCaseDTO { + @ExcelField(title = "产品名称") + private String productName; + @ExcelField(title = "模块名称") + private String moduleName; + @ExcelField(title = "功能点") + private String feature; + @ExcelField(title = "用例描述") + private String summary; + @ExcelField(title = "优先级") + private String priority = "P2"; //check it + @ExcelField(title = "用例前提条件") + private String precondition; + @ExcelField(title = "测试步骤") + private String steps; + @ExcelField(title = "期望结果") + private String expectedResult; + + @ExcelField(title = "用例ID") + private String uuid; + +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/handlers/GenerateTestRecordHandler.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/handlers/GenerateTestRecordHandler.java new file mode 100644 index 0000000..f5b2fe9 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/handlers/GenerateTestRecordHandler.java @@ -0,0 +1,49 @@ +package io.fluentqa.qtm.tc.handlers; + + +import cn.hutool.core.bean.BeanUtil; +import io.fluentqa.qtm.tc.model.TestCase; +import io.fluentqa.qtm.tc.model.TestResult; +import io.fluentqa.qtm.tc.model.TestRun; +import io.fluentqa.qtm.tc.model.TestScenario; +import io.fluentqa.qtm.tc.repo.TestResultRepo; + +import org.springframework.stereotype.Service; +import xyz.erupt.annotation.fun.OperationHandler; + + +import javax.annotation.Resource; +import java.util.List; + +@Service +public class GenerateTestRecordHandler implements OperationHandler { + @Resource + private TestResultRepo testResultRepo; + + @Override + public String exec(List data, Void unused, String[] param) { + for (TestRun testRun : data) { + //get all test cases + for (TestCase testCase : testRun.getTestCases()) { + TestResult result = BeanUtil.copyProperties(testCase, TestResult.class); + result.setTestCaseUUID(testCase.getUuid()); + result.setTestRun(testRun); + result.setQaOwner(testRun.getTestOwner()); + testResultRepo.save(result); + } + for (TestScenario tc : testRun.getTestScenarios()) { + for (TestCase testCase : tc.getTestCases()) { + TestResult result = BeanUtil.copyProperties(testCase, TestResult.class); + result.setTestCaseUUID(testCase.getUuid()); + result.setTestRun(testRun); + result.setQaOwner(testRun.getTestOwner()); + result.setTestScenario(tc.getName()); + testResultRepo.save(result); + } + } + } + return null; + } + + +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestCase.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestCase.java new file mode 100644 index 0000000..4b8a23f --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestCase.java @@ -0,0 +1,238 @@ +package io.fluentqa.qtm.tc.model; + +import io.fluentqa.base.handlers.SqlTagFetchHandler; +import io.fluentqa.base.model.ModelWithValidFlagVo; +import io.fluentqa.base.product.model.ProductModuleModel; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Layout; +import xyz.erupt.annotation.sub_erupt.LinkTree; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.*; + +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +/** + * + */ +@Entity +@Erupt(name = "测试用例", + power = @Power(export = true), + orderBy = "TestCase.updateTime desc", + linkTree = @LinkTree(field = "module"),layout = @Layout( + tableLeftFixed = 3, + pageSize = 30)) +@Table(name = "test_cases") +public class TestCase extends ModelWithValidFlagVo { + + @ManyToOne + @JoinColumn(name = "product_id") + @EruptField( + views = @View(title = "所属模块",column = "details"), + edit = @Edit( + notNull = true, + search = @Search, + title = "产品模块选择", + type = EditType.REFERENCE_TREE, + desc = "动态获取产品", + referenceTreeType = @ReferenceTreeType(id = "id", label = "name", + pid = "parent.id")) + ) + private ProductModuleModel module; + + @ManyToOne + @JoinColumn(name = "parent_product_id") + @EruptField( + views = @View(title = "父模块",column = "details"), + edit = @Edit( + notNull = true, + search = @Search, + title = "产品模块选择", + type = EditType.REFERENCE_TREE, + desc = "动态获取产品", + referenceTreeType = @ReferenceTreeType(id = "id", label = "name", + pid = "parent.id")) + ) + private ProductModuleModel parent; + + @ManyToOne + @JoinColumn(name = "root_product_id") + @EruptField( + views = @View(title = "所属产品",column = "details"), + edit = @Edit( + notNull = true, + search = @Search, + title = "产品模块选择", + type = EditType.REFERENCE_TREE, + desc = "动态获取产品", + referenceTreeType = @ReferenceTreeType(id = "id", label = "name", + pid = "parent.id")) + ) + private ProductModuleModel product; + + @EruptField( + views = @View( + title = "功能点" + ), + edit = @Edit( + title = "功能点", + type = EditType.INPUT, search = @Search, notNull = true + ) + ) + private String feature; + @EruptField( + views = @View( + title = "用例描述" + ), + edit = @Edit( + title = "用例描述", + type = EditType.INPUT, notNull = true + ) + ) + private String summary; + + @EruptField( + views = @View( + title = "优先级" + ), + edit = @Edit( + title = "优先级", + type = EditType.TAGS, + search = @Search, + tagsType = @TagsType( + fetchHandler = SqlTagFetchHandler.class, + fetchHandlerParams = "select distinct key,detail from master_data where category_code = 'PRIORITY' order by 1 " + ) + ) + ) + private String priority = "P2"; + + + @EruptField( + views = @View( + title = "测试步骤" + ), + edit = @Edit( + title = "测试步骤", + type = EditType.CODE_EDITOR, notNull = true, + codeEditType = @CodeEditorType(language = "text") + ) + ) + private String steps; + @EruptField( + views = @View( + title = "期望结果" + ), + edit = @Edit( + title = "期望结果", + type = EditType.CODE_EDITOR, + codeEditType = @CodeEditorType(language = "text") + ) + ) + private String expectedResult; + + @EruptField( + views = @View( + title = "用例ID" + ) + ) + private String uuid; + @EruptField( + views = @View( + title = "用例前提条件" + ), + edit = @Edit( + title = "用例前提条件", + type = EditType.CODE_EDITOR, + codeEditType = @CodeEditorType(language = "text") + ) + ) + private String precondition; + + public String getFeature() { + return feature; + } + + public void setFeature(String feature) { + this.feature = feature; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public String getPriority() { + return priority; + } + + public void setPriority(String priority) { + this.priority = priority; + } + + public String getSteps() { + return steps; + } + + public void setSteps(String steps) { + this.steps = steps; + } + + public String getExpectedResult() { + return expectedResult; + } + + public void setExpectedResult(String expectedResult) { + this.expectedResult = expectedResult; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getPrecondition() { + return precondition; + } + + public void setPrecondition(String precondition) { + this.precondition = precondition; + } + + + public ProductModuleModel getModule() { + return module; + } + + public void setModule(ProductModuleModel module) { + this.module = module; + } + + public ProductModuleModel getParent() { + return parent; + } + + public void setParent(ProductModuleModel parent) { + this.parent = parent; + } + + public ProductModuleModel getProduct() { + return product; + } + + public void setProduct(ProductModuleModel product) { + this.product = product; + } +} \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestResult.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestResult.java new file mode 100644 index 0000000..bdb52e0 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestResult.java @@ -0,0 +1,206 @@ +package io.fluentqa.qtm.tc.model; + + +import io.fluentqa.base.model.ModelWithValidFlagVo; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.LinkTree; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; +import xyz.erupt.annotation.sub_field.sub_edit.ReferenceTreeType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; + +import javax.persistence.Entity; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + + +@Table(name = "test_results") +@Entity +@Erupt(name = "测试执行结果", + power = @Power(importable = true, export = true) + , linkTree = @LinkTree(field = "testRun") +) +public class TestResult extends ModelWithValidFlagVo { + @ManyToOne + @EruptField( + views = @View(title = "所属测试安排", column = "name"), + edit = @Edit(title = "所属测试安排", type = EditType.REFERENCE_TREE, + referenceTreeType = @ReferenceTreeType(pid = "parent.id", expandLevel = 2)) + ) + private TestRun testRun; + + @EruptField( + views = @View( + title = "测试场景" + ), + edit = @Edit( + title = "测试场景", + type = EditType.INPUT, search = @Search, notNull = true + ) + ) + private String testScenario; + @EruptField( + views = @View( + title = "功能点" + ), + edit = @Edit( + title = "功能点", + type = EditType.INPUT, search = @Search, notNull = true + ) + ) + private String feature; + @EruptField( + views = @View( + title = "用例描述" + ), + edit = @Edit( + title = "用例描述", + type = EditType.INPUT, notNull = true + ) + ) + private String summary; + @EruptField( + views = @View( + title = "用例优先级" + ), + edit = @Edit( + title = "用例优先级", + type = EditType.INPUT, search = @Search, notNull = true + ) + ) + private String priority = "P2"; //check it + @EruptField( + views = @View( + title = "测试步骤" + ), + edit = @Edit( + title = "测试步骤", + type = EditType.CODE_EDITOR, notNull = true, + codeEditType = @CodeEditorType(language = "text") + ) + ) + private String steps; + @EruptField( + views = @View( + title = "用例期望结果" + ), + edit = @Edit( + title = "用例期望结果", + type = EditType.CODE_EDITOR, notNull = true, + codeEditType = @CodeEditorType(language = "text") + ) + ) + private String expectedResult; + + + @EruptField( + views = @View( + title = "测试测试结果" + ), + edit = @Edit( + title = "测试测试结果", + type = EditType.INPUT, search = @Search, notNull = true + ) + ) + private String qaTestResult; + + + @EruptField( + views = @View( + title = "测试负责人" + ), + edit = @Edit( + title = "测试负责人", + type = EditType.INPUT, search = @Search, notNull = true + ) + ) + private String qaOwner; + + private String testCaseUUID; + + + public String getTestCaseUUID() { + return testCaseUUID; + } + + public void setTestCaseUUID(String testCaseUUID) { + this.testCaseUUID = testCaseUUID; + } + + public String getFeature() { + return feature; + } + + public void setFeature(String feature) { + this.feature = feature; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public String getPriority() { + return priority; + } + + public void setPriority(String priority) { + this.priority = priority; + } + + public String getSteps() { + return steps; + } + + public void setSteps(String steps) { + this.steps = steps; + } + + public String getExpectedResult() { + return expectedResult; + } + + public void setExpectedResult(String expectedResult) { + this.expectedResult = expectedResult; + } + + + public String getQaTestResult() { + return qaTestResult; + } + + public void setQaTestResult(String qaTestResult) { + this.qaTestResult = qaTestResult; + } + + public String getQaOwner() { + return qaOwner; + } + + public void setQaOwner(String qaOwner) { + this.qaOwner = qaOwner; + } + + public TestRun getTestRun() { + return testRun; + } + + public void setTestRun(TestRun testRun) { + this.testRun = testRun; + } + + public String getTestScenario() { + return testScenario; + } + + public void setTestScenario(String testScenario) { + this.testScenario = testScenario; + } +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestRun.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestRun.java new file mode 100644 index 0000000..e054be6 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestRun.java @@ -0,0 +1,251 @@ +package io.fluentqa.qtm.tc.model; + + +import io.fluentqa.base.product.model.ProductModuleModel; +import io.fluentqa.qtm.tc.handlers.GenerateTestRecordHandler; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_erupt.RowOperation; +import xyz.erupt.annotation.sub_erupt.Tree; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.BoolType; +import xyz.erupt.annotation.sub_field.sub_edit.ReferenceTreeType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; +import xyz.erupt.jpa.model.MetaModel; + +import javax.persistence.*; +import java.time.LocalDate; +import java.util.Set; + +//TODO: Filter By Status +//TODO: input by Uploaded File or File Sync +@Entity +@Table(name = "test_runs") +@Erupt(name = "测试执行计划", + power = @Power(importable = true, export = true), + tree = @Tree(id = "id", label = "name", pid = "parent.id") + ,rowOperation = {@RowOperation( + title = "生成执行测试用例", + operationHandler = GenerateTestRecordHandler.class)} +) + +public class TestRun extends MetaModel { + + @ManyToOne + @JoinColumn(name = "product_id") + @EruptField( + views = @View(title = "产品名称", column = "name"), + edit = @Edit( + search = @Search, + title = "产品选择", + type = EditType.REFERENCE_TREE, + desc = "动态获取产品", + referenceTreeType = @ReferenceTreeType( + pid = "parent.id")) + ) + private ProductModuleModel product; + + @ManyToOne + @EruptField( + edit = @Edit( + title = "父级测试安排", + type = EditType.REFERENCE_TREE, + referenceTreeType = @ReferenceTreeType(pid = "parent.id") + ) + ) + private TestRun parent; + + + @EruptField( + views = @View( + title = "测试负责人" + ), + edit = @Edit( + title = "测试负责人", + type = EditType.INPUT, search = @Search + ) + ) + private String testOwner; + + @JoinTable(name = "test_run_cases", + joinColumns = @JoinColumn(name = "test_run_id", referencedColumnName = "id"), + inverseJoinColumns = @JoinColumn(name = "test_case_id", referencedColumnName = "id")) + @ManyToMany(fetch = FetchType.EAGER) + @EruptField( + views = @View(title = "包含用例"), + edit = @Edit( + title = "包含用例", + type = EditType.TAB_TABLE_REFER + ) + ) + private Set testCases; + + @JoinTable(name = "test_run_scenario", + joinColumns = @JoinColumn(name = "test_run_id", referencedColumnName = "id"), + inverseJoinColumns = @JoinColumn(name = "test_scenario_id", referencedColumnName = "id")) + @ManyToMany(fetch = FetchType.EAGER) + @EruptField( + views = @View(title = "包含测试场景"), + edit = @Edit( + title = "包含测试场景", + type = EditType.TAB_TABLE_REFER + ) + ) + private Set testScenarios; + + public ProductModuleModel getProduct() { + return product; + } + + public void setProduct(ProductModuleModel product) { + this.product = product; + } + + public String getTestOwner() { + return testOwner; + } + + public void setTestOwner(String testOwner) { + this.testOwner = testOwner; + } + + public Set getTestCases() { + return testCases; + } + + public void setTestCases(Set testCases) { + this.testCases = testCases; + } + + public Set getTestScenarios() { + return testScenarios; + } + + public void setTestScenarios(Set testScenarios) { + this.testScenarios = testScenarios; + } + + public TestRun getParent() { + return parent; + } + + public void setParent(TestRun parent) { + this.parent = parent; + } + + @EruptField( + views = @View( + title = "名称" + ), + edit = @Edit( + title = "名称", + type = EditType.INPUT, search = @Search, notNull = true + ) + ) + private String name; + @EruptField( + views = @View( + title = "详细" + ), + edit = @Edit( + title = "详细", + type = EditType.INPUT, search = @Search, notNull = true + ) + ) + private String detail; + @EruptField( + views = @View( + title = "开始时间" + ), + edit = @Edit( + title = "开始时间", + type = EditType.DATE, search = @Search, + boolType = @BoolType + ) + ) + private LocalDate startDate; + @EruptField( + views = @View( + title = "预计完成时间" + ), + edit = @Edit( + title = "预计完成时间", + type = EditType.DATE, search = @Search, + boolType = @BoolType + ) + ) + private LocalDate estimatedCompletedDate; + @EruptField( + views = @View( + title = "完成时间" + ), + edit = @Edit( + title = "完成时间", + type = EditType.DATE, search = @Search, + boolType = @BoolType + ) + ) + private LocalDate completedDate; + @EruptField( + views = @View( + title = "当前状态" + ), + edit = @Edit( + title = "当前状态", + type = EditType.INPUT, search = @Search, notNull = true, + boolType = @BoolType + ) + ) + private String status; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDetail() { + return detail; + } + + public void setDetail(String detail) { + this.detail = detail; + } + + public LocalDate getStartDate() { + return startDate; + } + + public void setStartDate(LocalDate startDate) { + this.startDate = startDate; + } + + public LocalDate getEstimatedCompletedDate() { + return estimatedCompletedDate; + } + + public void setEstimatedCompletedDate(LocalDate estimatedCompletedDate) { + this.estimatedCompletedDate = estimatedCompletedDate; + } + + public LocalDate getCompletedDate() { + return completedDate; + } + + public void setCompletedDate(LocalDate completedDate) { + this.completedDate = completedDate; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestScenario.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestScenario.java new file mode 100644 index 0000000..eb2efa8 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestScenario.java @@ -0,0 +1,43 @@ +package io.fluentqa.qtm.tc.model; + +import io.fluentqa.base.model.NamedModelVO; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; + +import javax.persistence.*; +import java.util.Set; + +@Entity +@Table(name = "test_scenarios") +@Erupt(name = "测试场景管理", + power = @Power(importable = true, export = true) +) +//@PreDataProxy(value= TestScenarioCaseProxy.class) + +public class TestScenario extends NamedModelVO { + + @JoinTable(name = "test_scenario_cases", + joinColumns = @JoinColumn(name = "test_scenario_id", referencedColumnName = "id"), + inverseJoinColumns = @JoinColumn(name = "test_case_id", referencedColumnName = "id")) + @ManyToMany(fetch = FetchType.EAGER) + @EruptField( + views = @View(title = "包含用例"), + edit = @Edit( + title = "包含用例", + type = EditType.TAB_TABLE_REFER + ) + ) + private Set testCases; + + public Set getTestCases() { + return testCases; + } + + public void setTestCases(Set testCases) { + this.testCases = testCases; + } +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/package-info.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/package-info.java new file mode 100644 index 0000000..263ec49 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/package-info.java @@ -0,0 +1 @@ +package io.fluentqa.qtm.tc; \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/repo/TestCaseRepo.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/repo/TestCaseRepo.java new file mode 100644 index 0000000..dbc1f7d --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/repo/TestCaseRepo.java @@ -0,0 +1,15 @@ +package io.fluentqa.qtm.tc.repo; + +import io.fluentqa.qtm.tc.model.TestCase; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface TestCaseRepo extends JpaRepository { + public TestCase findByUuid(String uuid); + + +// @Query(nativeQuery = true,value = "delete from test_cases where test_plan=:testPlan") +// @Modifying +// public void deleteByTestPlan(@Param("testPlan") String testPlan); +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/repo/TestResultRepo.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/repo/TestResultRepo.java new file mode 100644 index 0000000..6af93c7 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/repo/TestResultRepo.java @@ -0,0 +1,11 @@ +package io.fluentqa.qtm.tc.repo; + + +import io.fluentqa.qtm.tc.model.TestResult; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository(value = "testResultRepo") +public interface TestResultRepo extends JpaRepository { + +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/repo/TestRunRepo.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/repo/TestRunRepo.java new file mode 100644 index 0000000..19d7da7 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/repo/TestRunRepo.java @@ -0,0 +1,11 @@ +package io.fluentqa.qtm.tc.repo; + + +import io.fluentqa.qtm.tc.model.TestRun; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface TestRunRepo extends JpaRepository { + +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/service/TestCaseService.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/service/TestCaseService.java new file mode 100644 index 0000000..45ffd97 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/service/TestCaseService.java @@ -0,0 +1,14 @@ +package io.fluentqa.qtm.tc.service; + +import io.fluentqa.base.product.model.ProductModuleModel; +import io.fluentqa.qtm.tc.dto.TestCaseDTO; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public interface TestCaseService { + + public void saveTestCases(List cases, + ProductModuleModel product, ProductModuleModel module,String updater); +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/service/impl/MindMappingService.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/service/impl/MindMappingService.java new file mode 100644 index 0000000..04f6154 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/service/impl/MindMappingService.java @@ -0,0 +1,41 @@ +package io.fluentqa.qtm.tc.service.impl; + + + +import cn.hutool.core.bean.BeanUtil; +import io.fluentqa.mindmap.api.MindMapAccessor; +import io.fluentqa.base.product.model.ProductModuleModel; +import io.fluentqa.qtm.tc.dto.TestCaseDTO; +import io.fluentqa.qtm.tc.service.TestCaseService; +import org.springframework.stereotype.Service; +import xyz.erupt.jpa.model.MetaModel; + +import javax.annotation.Resource; +import javax.transaction.Transactional; +import java.util.List; + +/** + * 1.Import MindMapping file to test case database + * 2.export selected test cases as mindmapping file + */ +//TODO: convert to same TestCase Converter +@Service("mindMappingService") +public class MindMappingService { + + @Resource + private TestCaseService testCaseService; + + public List toTestCaseModel(String xmlFilePath) { + MindMapAccessor accessor = new MindMapAccessor(); + return accessor.readMindMapToBean(xmlFilePath, TestCaseDTO.class); + } + + @Transactional + public void saveTestCases(String xmlFilePath, MetaModel model) { + List testCaseModels = toTestCaseModel(xmlFilePath); + ProductModuleModel product = BeanUtil.getProperty(model, "product"); + ProductModuleModel module = BeanUtil.getProperty(model, "module"); + testCaseService.saveTestCases(testCaseModels,product,module,model.getUpdateBy()); + } + +} diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/service/impl/TestCaseServiceImpl.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/service/impl/TestCaseServiceImpl.java new file mode 100644 index 0000000..bab5b68 --- /dev/null +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/service/impl/TestCaseServiceImpl.java @@ -0,0 +1,94 @@ +package io.fluentqa.qtm.tc.service.impl; + + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.lang.UUID; +import cn.hutool.core.util.StrUtil; +import io.fluentqa.base.proxies.AuditDataEnhancerProxy; +import io.fluentqa.base.product.model.ProductModuleModel; +import io.fluentqa.base.product.service.ProductModuleService; +import io.fluentqa.qtm.tc.dto.TestCaseDTO; +import io.fluentqa.qtm.tc.model.TestCase; +import io.fluentqa.qtm.tc.repo.TestCaseRepo; +import io.fluentqa.qtm.tc.service.TestCaseService; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import javax.transaction.Transactional; +import java.util.List; + +@Service +public class TestCaseServiceImpl implements TestCaseService { + @Resource + private TestCaseRepo testCaseRepo; + @Resource + private ProductModuleService productMetaService; + + @Resource + private AuditDataEnhancerProxy dataEnhancerProxy; + @Override + @Transactional + @Async + /** + * notice:parent Product can't be created,parent product must be configured + * 1. 如果UUID没有或者找不到,则新增测试用例 + * 2. 新增测试用例中, + */ + public void saveTestCases(List cases, + ProductModuleModel parentProduct, + ProductModuleModel module,String updater) { + for (TestCaseDTO aCase : cases) { + TestCase tcEntity = createOrUseExistingTestCase(aCase); + ProductModuleModel rootProduct = getRootProductMeta(aCase); + ProductModuleModel parentModule = productMetaService.createModuleIfNotExist(rootProduct.getId(), + aCase.getModuleName(),updater); + ProductModuleModel subModule = whichSubModule(parentModule, aCase,updater); + tcEntity.setModule(subModule); + tcEntity.setProduct(rootProduct); + tcEntity.setParent(parentModule); + if (StrUtil.isBlankIfStr(aCase.getPriority())) { + tcEntity.setPriority("P2"); + } + tcEntity.setSteps(StrUtil.join(":\n", aCase.getFeature(), + aCase.getSummary(), aCase.getSteps())); + + testCaseRepo.save(tcEntity); + } + } + + private TestCase createOrUseExistingTestCase(TestCaseDTO aCase) { + TestCase tcEntity; + if (StrUtil.isBlank(aCase.getUuid())) { + tcEntity = BeanUtil.copyProperties(aCase, TestCase.class); + tcEntity.setUuid(UUID.fastUUID().toString(true)); + + } else { + tcEntity = testCaseRepo.findByUuid(aCase.getUuid()); + if (tcEntity == null) { + tcEntity = BeanUtil.copyProperties(aCase, TestCase.class); //生成新的的UUID + } else { + BeanUtil.copyProperties(aCase, tcEntity, "id"); //更新数据库数据 + } + } + return tcEntity; + } + + + private ProductModuleModel getRootProductMeta(TestCaseDTO aCase) { + ProductModuleModel rootProductMeta = productMetaService.findByName(aCase.getProductName()); + if (rootProductMeta == null) { + throw new RuntimeException("找不到产品"); + } + return rootProductMeta; + } + + + private ProductModuleModel whichSubModule(ProductModuleModel parentProduct, TestCaseDTO aCase,String updater) { + if (parentProduct.getName().equalsIgnoreCase(aCase.getModuleName())) return parentProduct; + return productMetaService.createModuleIfNotExist(parentProduct.getId(), aCase.getModuleName(),updater); + + } + + +} diff --git a/fluent-apps/qaserver/src/main/resources/application-dev.yaml b/fluent-apps/qaserver/src/main/resources/application-dev.yaml new file mode 100644 index 0000000..6777e4b --- /dev/null +++ b/fluent-apps/qaserver/src/main/resources/application-dev.yaml @@ -0,0 +1,101 @@ +erupt-app: + # 是否开启水印,1.12.0 及以上版本支持 + waterMark: false + # 登录失败几次出现验证码,值为0时表示一直需要登录验证码 + verifyCodeCount: 2 + # 登录密码是否加密传输,特殊场景如:LDAP登录可关闭该功能获取密码明文 + pwdTransferEncrypt: true + # 多语言配置,默认支持:简体中文、繁体中文、英文、日文;具体配置详见erupt-i18n模块 + locales: [ "zh-CN","zh-TW","en-US","ja-JP" ] +erupt: + # 是否开启csrf防御 + csrfInspect: true + # 是否开启redis方式存储session,默认false,开启后需在配置文件中添加redis配置(同 spring boot) + redisSession: false + # 附件上传存储路径, 默认路径为:/opt/erupt-attachment + uploadPath: /Users/patrick/data/temp + # 是否保留上传文件原始名称 + keepUploadFileName: false + # 登录session时长(redisSession为true时有效) + upms.expireTimeByLogin: 120 + # 是否记录操作日志,默认true,该功能开启后可在【系统管理 → 操作日志】中查看操作日志 + security.recordOperateLog: false + +spring: + datasource: + url: jdbc:postgresql://db.supabase.orb.local:5432/postgres?currentSchema=workspace + username: postgres + password: postgres + jpa: + show-sql: true + generate-ddl: true + database-platform: org.hibernate.dialect.PostgreSQLDialect + database: postgresql + +# mail: +# username: xxxx@qq.com +# password: xxxxxxx +# host: smtp.qq.com +# properties: +# mail.smtp.ssl.auth: true +# mail.smtp.ssl.enable: true +# mail.smtp.ssl.required: true + servlet: + multipart: + max-file-size: 100MB + max-request-size: 100MB + +# springdoc-openapi项目配置 +#springdoc: +# swagger-ui: +# path: /swagger-ui.html +# tags-sorter: alpha +# operations-sorter: alpha +# api-docs: +# path: /v3/api-docs +# group-configs: +# - group: 'default' +# paths-to-match: '/**' +# packages-to-scan: io.fluentqa +# knife4j的增强配置,不需要增强可以不配 +knife4j: + enable: true + openapi: + title: QA Workspace API + description: "`QA Workspace API + # workspace" + email: fluentqa@163.com + concat: fluent-qa +# url: https://docs.xiaominfo.com +# version: v4.0 +# license: Apache 2.0 +# license-url: https://stackoverflow.com/ +# terms-of-service-url: https://stackoverflow.com/ + group: + test1: + group-name: qa workspace api + api-rule: package +# api-rule-resources: +# - com.knife4j.demo.new3 + +server: + # 启用 gzip 压缩 + compression: + mime-types: application/javascript,text/css,application/json,application/xml,text/html,text/xml,text/plain + enabled: true + error: + includeException: true + includeStacktrace: ALWAYS + includeMessage: ALWAYS + port: 9090 +logging: + level: + root: TRACE + io.fluentqa: DEBUG + org.hibernate: DEBUG + io.fluent: DEBUG + xyz.erupt: DEBUG + +magic-api: + web: /fluentapi/v1 + resource.location: ./magic-script \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/resources/application.yaml b/fluent-apps/qaserver/src/main/resources/application.yaml new file mode 100644 index 0000000..caf4dfc --- /dev/null +++ b/fluent-apps/qaserver/src/main/resources/application.yaml @@ -0,0 +1,3 @@ +spring: + profiles: + active: dev \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/resources/public/app.css b/fluent-apps/qaserver/src/main/resources/public/app.css new file mode 100644 index 0000000..44c63ae --- /dev/null +++ b/fluent-apps/qaserver/src/main/resources/public/app.css @@ -0,0 +1,24 @@ +layout-header { + background: #3f51b5 !important; +} + +/* 例:修改登录页样式 */ +layout-passport > .container { + background-position: center !important; + background-repeat: repeat !important; + background-size: cover !important; + background-color: #fff !important; + background-image: url(https://www.erupt.xyz/demo/login-bg.svg) !important; +} + +layout-passport .title { + font-family: Courier New, Menlo, Monaco, Consolas, monospace !important; +} + +layout-passport form { + padding: 26px !important; + margin: 8px !important; + background: rgba(255, 255, 255, 0.9); + border-radius: 3px; + box-shadow: 1px 1px 10px rgba(190, 184, 184, 0.3); +} \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/resources/public/app.js b/fluent-apps/qaserver/src/main/resources/public/app.js new file mode 100644 index 0000000..c4435ce --- /dev/null +++ b/fluent-apps/qaserver/src/main/resources/public/app.js @@ -0,0 +1,71 @@ +window.eruptSiteConfig = { + //erupt接口地址,在前后端分离时指定 + domain: "", + //附件地址,一般情况下不需要指定,如果自定义对象存储空间,则需在此指定附件资源访问地址 + fileDomain: "", + //标题 + title: "QA Workspace", + //描述 + desc: "QA Base", + //是否展示版权信息 + copyright: true, + //高德地图 api key,使用地图组件须指定此属性,amapKey获取地址:https://lbs.amap.com (服务平台为:Web端(JS API)) + amapKey: "xxxx", + //高德地图 SecurityJsCode + amapSecurityJsCode: "xxxxx", + //logo路径 + logoPath: "erupt.svg", + //logo文字 + logoText: "erupt", + //注册页地址(仅是一个链接,需要自定义实际样式) + registerPage: "", + //自定义导航栏按钮,配置后将会出现在页面右上角 + r_tools: [{ + text: "自定义功能按钮", + icon: "fa-eercast", + mobileHidden: true, + click: function (event) { + alert("Function button"); + } + }], + // //登录成功事件 + // login: function(user){ + // + // }, + // //注销事件 + // logout: function(user){ + // + // } +}; + +// //路由回调函数 +// window.eruptRouterEvent = { +// //key表示要监听的路由切换地址,为url hash地址最后一段 +// //例如:http://www.erupt.xyz:9999/#/build/table/demo中demo为回调key +// demo: { +// //路由载入事件 +// load: function (e) { +// +// }, +// //路由退出事件 +// unload: function (e) { +// +// } +// }, +// //$ 为全路径通配符,在任何路由切换时都会执行load与unload事件 +// $: { +// load: function (e) { +// +// }, +// unload: function (e) { +// } +// } +// }; +// +// //erupt生命周期函数 +// window.eruptEvent = { +// //页面加载完成后回调 +// startup: function () { +// +// } +// } \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/resources/public/home.html b/fluent-apps/qaserver/src/main/resources/public/home.html new file mode 100644 index 0000000..35600cf --- /dev/null +++ b/fluent-apps/qaserver/src/main/resources/public/home.html @@ -0,0 +1,12 @@ + + + + home + + + + + +

测试管理工具箱

+ + \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/resources/tpl/operation.tpl b/fluent-apps/qaserver/src/main/resources/tpl/operation.tpl new file mode 100644 index 0000000..d77f7e5 --- /dev/null +++ b/fluent-apps/qaserver/src/main/resources/tpl/operation.tpl @@ -0,0 +1,17 @@ + +
+ + <#list rows as row> + + + + + + +
${row.id}${row.choice!''}${row.code!''}
+
\ No newline at end of file From fb288e5881cca7afc54779d782882c064e3eaa8c Mon Sep 17 00:00:00 2001 From: patrick Date: Mon, 17 Mar 2025 15:03:07 +0800 Subject: [PATCH 2/5] refactor files,rename package names --- fluent-apps/feeds/pom.xml | 105 ------------------ .../io/fluent/datafam/DataFeedFarmApp.java | 17 --- .../src/main/resources/application-dev.yaml | 100 ----------------- fluent-apps/pom.xml | 3 +- fluent-apps/{qaserver => qam}/pom.xml | 25 +++-- .../src/main/java/io/fluent/QAMApp.java} | 10 +- .../base/FluentProductConfigModule.java | 10 +- .../io/fluent}/base/FluentUploadTCModule.java | 4 +- .../src/main/java/io/fluent}/base/README.md | 0 .../base/masterdata/model/MasterData.java | 6 +- .../base/masterdata/repo/MasterDataRepo.java | 4 +- .../java/io/fluent/base/package-info.java | 1 + .../product/model/ProductModuleModel.java | 4 +- .../model/ProductModuleValidFlagVo.java | 4 +- .../io/fluent/base/product/package-info.java | 1 + .../base/product/repo/ProductModuleRepo.java | 4 +- .../product/service/ProductModuleService.java | 12 +- .../base/project/model/ProjectModel.java | 6 +- .../base/project/repo/ProjectModelRepo.java | 4 +- .../base/upload/model/UploadFileModel.java | 6 +- .../io/fluent/base/upload/package-info.java | 1 + .../upload/proxy/UploadFileDataProxy.java | 12 +- .../base/upload/proxy/UploadFileTypeEnum.java | 2 +- .../io/fluent}/qtm/FluentQAApiModule.java | 4 +- .../GenerateApiCaseByCaptureDataHandler.java | 6 +- .../GenerateApiTestStepByApiTestRecord.java | 6 +- .../handler/GenerateRawApiCaseHandler.java | 6 +- .../qtm/api/model/ApiMonitorRecord.java | 6 +- .../qtm/api/model/ApiSpecChangeModel.java | 2 +- .../qtm/api/model/ApiSpecGitRepoModel.java | 2 +- .../qtm/api/model/ApiSpecVersionModel.java | 4 +- .../io/fluent}/qtm/api/model/ApiStep.java | 4 +- .../fluent}/qtm/api/model/ApiTestRecord.java | 6 +- .../qtm/api/model/ApiTestScenario.java | 8 +- .../fluent}/qtm/api/model/RawApiTestCase.java | 2 +- .../io/fluent}/qtm/api/model/RemoteApi.java | 8 +- .../qtm/api/model/RemoteApiStatus.java | 2 +- .../fluent}/qtm/api/model/RemoteApiType.java | 2 +- .../java/io/fluent/qtm/api/package-info.java | 1 + .../qtm/api/repo/ApiMonitorRecordRepo.java | 4 +- .../qtm/api/repo/ApiSpecChangeRepository.java | 4 +- .../api/repo/ApiSpecGitRepoRepository.java | 4 +- .../api/repo/ApiSpecVersionRepository.java | 4 +- .../qtm/api/repo/ApiTestScenarioRepo.java | 4 +- .../fluent}/qtm/api/repo/ApiTestStepRepo.java | 4 +- .../qtm/api/repo/RawApiTestCaseRepo.java | 4 +- .../qtm/api/repo/RemoteServiceRepo.java | 4 +- .../qtm/api/service/ApiTestCaseService.java | 10 +- .../qtm/api/service/RemoteApiService.java | 14 +-- .../java/io/fluent/qtm/pm/package-info.java | 1 + .../qtm/pm/requirement/FieldOption.java | 4 +- .../qtm/pm/requirement/FieldOptionType.java | 2 +- .../pm/requirement/RequirementFeature.java | 4 +- .../qtm/pm/requirement/RequirementType.java | 2 +- .../qtm/pm/requirement/TestRequirement.java | 8 +- .../io/fluent}/qtm/tc/dto/TestCaseDTO.java | 2 +- .../handlers/GenerateTestRecordHandler.java | 12 +- .../io/fluent}/qtm/tc/model/TestCase.java | 8 +- .../io/fluent}/qtm/tc/model/TestResult.java | 4 +- .../java/io/fluent}/qtm/tc/model/TestRun.java | 6 +- .../io/fluent}/qtm/tc/model/TestScenario.java | 4 +- .../java/io/fluent/qtm/tc/package-info.java | 1 + .../io/fluent}/qtm/tc/repo/TestCaseRepo.java | 4 +- .../fluent}/qtm/tc/repo/TestResultRepo.java | 4 +- .../io/fluent}/qtm/tc/repo/TestRunRepo.java | 4 +- .../qtm/tc/service/TestCaseService.java | 6 +- .../tc/service/impl/MindMappingService.java | 10 +- .../tc/service/impl/TestCaseServiceImpl.java | 16 +-- .../src/main/resources/application-dev.yaml | 14 ++- .../src/main/resources/application.yaml | 0 .../src/main/resources/public/app.css | 0 .../src/main/resources/public/app.js | 0 .../src/main/resources/public/home.html | 0 .../src/main/resources/tpl/operation.tpl | 0 fluent-apps/qaserver/.gitignore | 38 ------- .../java/io/fluentqa/base/package-info.java | 1 - .../fluentqa/base/product/package-info.java | 1 - .../io/fluentqa/base/upload/package-info.java | 1 - .../io/fluentqa/qtm/api/package-info.java | 1 - .../java/io/fluentqa/qtm/pm/package-info.java | 1 - .../java/io/fluentqa/qtm/tc/package-info.java | 1 - .../src/main/resources/application.yaml | 3 - .../base/handlers/SqlTagFetchHandler.java | 2 +- .../base/model/ModelWithValidFlag.java | 2 +- .../base/model/ModelWithValidFlagVo.java | 2 +- .../base/model/NamedModelVO.java | 2 +- .../base/model/NamedTimeStatusModel.java | 2 +- .../java/io/fluent/base/package-info.java | 1 + .../base/proxies/AuditDataEnhancerProxy.java | 2 +- .../java/io/fluentqa/base/package-info.java | 1 - .../generator/model/DataSourceModel.java | 4 +- .../model/DataSourceTableColumModel.java | 4 +- .../generator/model/SqlConfigEntity.java | 2 +- .../excel/ExcelReadWriter.java | 2 +- .../java/io/fluent/excel/package-info.java | 1 + .../java/io/fluentqa/excel/package-info.java | 1 - .../excel/DemoExcelModel.java | 2 +- .../excel/ExcelUtilsTest.java | 2 +- .../jira/config/BasicAuthentication.java | 2 +- .../jira/config/CacheConfig.java | 2 +- .../jira/config/CommentMappingConfig.java | 2 +- .../jira/config/Context.java | 2 +- .../jira/config/DescriptionMappingConfig.java | 2 +- .../jira/config/JiraConnectionProperties.java | 2 +- .../jira/config/JiraProjectSync.java | 2 +- .../jira/config/JiraSyncConfig.java | 5 +- .../jira/config/TransitionConfig.java | 6 +- .../jira/domain/JiraChangeLog.java | 2 +- .../jira/domain/JiraComment.java | 6 +- .../jira/domain/JiraComments.java | 2 +- .../jira/domain/JiraComponent.java | 2 +- .../jira/domain/JiraComponentsList.java | 2 +- .../jira/domain/JiraField.java | 2 +- .../jira/domain/JiraFieldList.java | 2 +- .../jira/domain/JiraFieldSchema.java | 2 +- .../jira/domain/JiraFieldsBean.java | 2 +- .../jira/domain/JiraFieldsUpdate.java | 2 +- .../jira/domain/JiraFilterResult.java | 2 +- .../jira/domain/JiraIdResource.java | 2 +- .../jira/domain/JiraIssue.java | 2 +- .../jira/domain/JiraIssueFields.java | 2 +- .../jira/domain/JiraIssueHistoryEntry.java | 2 +- .../jira/domain/JiraIssueHistoryItem.java | 2 +- .../jira/domain/JiraIssueStatus.java | 2 +- .../jira/domain/JiraIssueType.java | 2 +- .../jira/domain/JiraIssueUpdate.java | 2 +- .../jira/domain/JiraLinkIcon.java | 2 +- .../jira/domain/JiraLoginRequest.java | 2 +- .../jira/domain/JiraLoginResponse.java | 2 +- .../jira/domain/JiraNamedBean.java | 2 +- .../jira/domain/JiraNamedResource.java | 2 +- .../jira/domain/JiraPriority.java | 2 +- .../jira/domain/JiraPriorityList.java | 2 +- .../jira/domain/JiraProject.java | 2 +- .../jira/domain/JiraProjectsList.java | 2 +- .../jira/domain/JiraRemoteLink.java | 2 +- .../jira/domain/JiraRemoteLinkObject.java | 2 +- .../jira/domain/JiraRemoteLinks.java | 2 +- .../jira/domain/JiraResolution.java | 2 +- .../jira/domain/JiraResolutionList.java | 2 +- .../jira/domain/JiraResource.java | 2 +- .../jira/domain/JiraSearchResult.java | 2 +- .../jira/domain/JiraServerInfo.java | 2 +- .../jira/domain/JiraSession.java | 2 +- .../jira/domain/JiraTransition.java | 2 +- .../jira/domain/JiraTransitions.java | 2 +- .../jira/domain/JiraUser.java | 2 +- .../jira/domain/JiraVersion.java | 2 +- .../jira/domain/JiraVersionsList.java | 2 +- .../jira/domain/WellKnownCustomFieldType.java | 2 +- .../jira/domain/WellKnownJiraField.java | 2 +- .../java/io/fluent/jira/package-info.java | 1 + .../java/io/fluentqa/jira/package-info.java | 1 - .../mindmap/api/MindMapAccessor.java | 7 +- .../mindmap/api/MindMapConvertConfig.java | 2 +- .../mindmap/api/MindMapPath.java | 2 +- .../mindmap/api/MindMapPathRecord.java | 2 +- .../mindmap/api/MindMapTransformer.java | 2 +- .../mindmap/api/MindmapTypeEnum.java | 2 +- .../mindmap/api/NodeLevel.java | 2 +- .../mindmap/exception/MindMapException.java | 2 +- .../mindmap/freemind/FreeMindNode.java | 6 +- .../mindmap/freemind/FreeMindTransformer.java | 14 +-- .../mindmap/freemind/model/Arrowlink.java | 2 +- .../mindmap/freemind/model/Attribute.java | 2 +- .../freemind/model/AttributeLayout.java | 2 +- .../mindmap/freemind/model/Cloud.java | 2 +- .../mindmap/freemind/model/Edge.java | 2 +- .../mindmap/freemind/model/Font.java | 2 +- .../mindmap/freemind/model/Hook.java | 2 +- .../mindmap/freemind/model/Html.java | 2 +- .../mindmap/freemind/model/Icon.java | 2 +- .../mindmap/freemind/model/Linktarget.java | 2 +- .../mindmap/freemind/model/Map.java | 2 +- .../mindmap/freemind/model/Node.java | 2 +- .../mindmap/freemind/model/ObjectFactory.java | 2 +- .../mindmap/freemind/model/Parameters.java | 2 +- .../mindmap/freemind/model/Richcontent.java | 2 +- .../mindmap/freemind/model/Text.java | 2 +- .../java/io/fluent/mindmap/package-info.java | 1 + .../mindmap/xmind/XMindNode.java | 6 +- .../mindmap/xmind/XMindUtil.java | 7 +- .../mindmap/xmind/XmindTransformer.java | 17 +-- .../mindmap/xmind/model/Attached.java | 2 +- .../mindmap/xmind/model/Children.java | 2 +- .../mindmap/xmind/model/Comments.java | 2 +- .../mindmap/xmind/model/JsonRootBean.java | 2 +- .../mindmap/xmind/model/Notes.java | 2 +- .../mindmap/xmind/model/RootTopic.java | 2 +- .../mindmap/xmind/model/XmindRawData.java | 2 +- .../io/fluentqa/mindmap/package-info.java | 1 - .../mindmap/api/DemoBean.java | 2 +- .../mindmap/api/DemoBeanConfig.java | 2 +- .../mindmap/api/DemoBeanWOLevel.java | 2 +- .../mindmap/api/MindMapAccessorTest.java | 2 +- .../freemind/FreeMindConverterTest.java | 7 +- .../mindmap/xmind/XMindUtilTest.java | 4 +- .../mindmap/xmind/XmindTransformerTest.java | 7 +- .../md/MarkdownAccessor.java | 2 +- .../main/java/io/fluent/md/package-info.java | 1 + .../md/parser/FieldParseConfig.java | 2 +- .../md/parser/ParseConfig.java | 2 +- .../awesome/AwesomeListParserConfig.java | 4 +- .../md/parser/awesome/AwesomeModel.java | 2 +- .../awesome/MarkdownAwesomeListParser.java | 2 +- .../java/io/fluentqa/md/package-info.java | 1 - .../MarkdownAwesomeListParserTest.java | 7 +- 207 files changed, 353 insertions(+), 595 deletions(-) delete mode 100644 fluent-apps/feeds/pom.xml delete mode 100644 fluent-apps/feeds/src/main/java/io/fluent/datafam/DataFeedFarmApp.java delete mode 100644 fluent-apps/feeds/src/main/resources/application-dev.yaml rename fluent-apps/{qaserver => qam}/pom.xml (89%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa/QAWorkspaceApp.java => qam/src/main/java/io/fluent/QAMApp.java} (71%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/base/FluentProductConfigModule.java (90%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/base/FluentUploadTCModule.java (95%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/base/README.md (100%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/base/masterdata/model/MasterData.java (93%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/base/masterdata/repo/MasterDataRepo.java (80%) create mode 100644 fluent-apps/qam/src/main/java/io/fluent/base/package-info.java rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/base/product/model/ProductModuleModel.java (97%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/base/product/model/ProductModuleValidFlagVo.java (94%) create mode 100644 fluent-apps/qam/src/main/java/io/fluent/base/product/package-info.java rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/base/product/repo/ProductModuleRepo.java (86%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/base/product/service/ProductModuleService.java (86%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/base/project/model/ProjectModel.java (94%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/base/project/repo/ProjectModelRepo.java (86%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/base/upload/model/UploadFileModel.java (93%) create mode 100644 fluent-apps/qam/src/main/java/io/fluent/base/upload/package-info.java rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/base/upload/proxy/UploadFileDataProxy.java (87%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/base/upload/proxy/UploadFileTypeEnum.java (92%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/FluentQAApiModule.java (98%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/handler/GenerateApiCaseByCaptureDataHandler.java (80%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/handler/GenerateApiTestStepByApiTestRecord.java (81%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/handler/GenerateRawApiCaseHandler.java (79%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/model/ApiMonitorRecord.java (96%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/model/ApiSpecChangeModel.java (97%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/model/ApiSpecGitRepoModel.java (97%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/model/ApiSpecVersionModel.java (97%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/model/ApiStep.java (98%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/model/ApiTestRecord.java (96%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/model/ApiTestScenario.java (95%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/model/RawApiTestCase.java (98%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/model/RemoteApi.java (97%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/model/RemoteApiStatus.java (65%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/model/RemoteApiType.java (55%) create mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/package-info.java rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/repo/ApiMonitorRecordRepo.java (82%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/repo/ApiSpecChangeRepository.java (71%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/repo/ApiSpecGitRepoRepository.java (71%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/repo/ApiSpecVersionRepository.java (71%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/repo/ApiTestScenarioRepo.java (79%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/repo/ApiTestStepRepo.java (80%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/repo/RawApiTestCaseRepo.java (79%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/repo/RemoteServiceRepo.java (89%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/service/ApiTestCaseService.java (94%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/api/service/RemoteApiService.java (93%) create mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/pm/package-info.java rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/pm/requirement/FieldOption.java (97%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/pm/requirement/FieldOptionType.java (86%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/pm/requirement/RequirementFeature.java (91%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/pm/requirement/RequirementType.java (89%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/pm/requirement/TestRequirement.java (96%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/tc/dto/TestCaseDTO.java (95%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/tc/handlers/GenerateTestRecordHandler.java (85%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/tc/model/TestCase.java (97%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/tc/model/TestResult.java (98%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/tc/model/TestRun.java (97%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/tc/model/TestScenario.java (94%) create mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/tc/package-info.java rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/tc/repo/TestCaseRepo.java (85%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/tc/repo/TestResultRepo.java (74%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/tc/repo/TestRunRepo.java (72%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/tc/service/TestCaseService.java (66%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/tc/service/impl/MindMappingService.java (82%) rename fluent-apps/{qaserver/src/main/java/io/fluentqa => qam/src/main/java/io/fluent}/qtm/tc/service/impl/TestCaseServiceImpl.java (88%) rename fluent-apps/{qaserver => qam}/src/main/resources/application-dev.yaml (88%) rename fluent-apps/{feeds => qam}/src/main/resources/application.yaml (100%) rename fluent-apps/{qaserver => qam}/src/main/resources/public/app.css (100%) rename fluent-apps/{qaserver => qam}/src/main/resources/public/app.js (100%) rename fluent-apps/{qaserver => qam}/src/main/resources/public/home.html (100%) rename fluent-apps/{qaserver => qam}/src/main/resources/tpl/operation.tpl (100%) delete mode 100644 fluent-apps/qaserver/.gitignore delete mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/base/package-info.java delete mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/package-info.java delete mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/package-info.java delete mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/package-info.java delete mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/package-info.java delete mode 100644 fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/package-info.java delete mode 100644 fluent-apps/qaserver/src/main/resources/application.yaml rename fluent-erupts/fluent-erupts-base/src/main/java/io/{fluentqa => fluent}/base/handlers/SqlTagFetchHandler.java (97%) rename fluent-erupts/fluent-erupts-base/src/main/java/io/{fluentqa => fluent}/base/model/ModelWithValidFlag.java (96%) rename fluent-erupts/fluent-erupts-base/src/main/java/io/{fluentqa => fluent}/base/model/ModelWithValidFlagVo.java (97%) rename fluent-erupts/fluent-erupts-base/src/main/java/io/{fluentqa => fluent}/base/model/NamedModelVO.java (97%) rename fluent-erupts/fluent-erupts-base/src/main/java/io/{fluentqa => fluent}/base/model/NamedTimeStatusModel.java (98%) create mode 100644 fluent-erupts/fluent-erupts-base/src/main/java/io/fluent/base/package-info.java rename fluent-erupts/fluent-erupts-base/src/main/java/io/{fluentqa => fluent}/base/proxies/AuditDataEnhancerProxy.java (94%) delete mode 100644 fluent-erupts/fluent-erupts-base/src/main/java/io/fluentqa/base/package-info.java rename fluent-wrappers/fluent-excel/src/main/java/io/{fluentqa => fluent}/excel/ExcelReadWriter.java (96%) create mode 100644 fluent-wrappers/fluent-excel/src/main/java/io/fluent/excel/package-info.java delete mode 100644 fluent-wrappers/fluent-excel/src/main/java/io/fluentqa/excel/package-info.java rename fluent-wrappers/fluent-excel/src/test/java/io/{fluentqa => fluent}/excel/DemoExcelModel.java (92%) rename fluent-wrappers/fluent-excel/src/test/java/io/{fluentqa => fluent}/excel/ExcelUtilsTest.java (96%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/config/BasicAuthentication.java (91%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/config/CacheConfig.java (93%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/config/CommentMappingConfig.java (97%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/config/Context.java (57%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/config/DescriptionMappingConfig.java (94%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/config/JiraConnectionProperties.java (97%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/config/JiraProjectSync.java (99%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/config/JiraSyncConfig.java (97%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/config/TransitionConfig.java (96%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraChangeLog.java (97%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraComment.java (86%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraComments.java (96%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraComponent.java (86%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraComponentsList.java (82%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraField.java (94%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraFieldList.java (85%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraFieldSchema.java (95%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraFieldsBean.java (94%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraFieldsUpdate.java (98%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraFilterResult.java (89%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraIdResource.java (91%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraIssue.java (97%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraIssueFields.java (99%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraIssueHistoryEntry.java (97%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraIssueHistoryItem.java (98%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraIssueStatus.java (86%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraIssueType.java (86%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraIssueUpdate.java (97%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraLinkIcon.java (92%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraLoginRequest.java (95%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraLoginResponse.java (90%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraNamedBean.java (84%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraNamedResource.java (93%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraPriority.java (86%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraPriorityList.java (85%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraProject.java (96%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraProjectsList.java (81%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraRemoteLink.java (95%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraRemoteLinkObject.java (95%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraRemoteLinks.java (85%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraResolution.java (86%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraResolutionList.java (86%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraResource.java (92%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraSearchResult.java (96%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraServerInfo.java (95%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraSession.java (93%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraTransition.java (94%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraTransitions.java (94%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraUser.java (94%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraVersion.java (86%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/JiraVersionsList.java (81%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/WellKnownCustomFieldType.java (96%) rename fluent-wrappers/fluent-jira/src/main/java/io/{fluentqa => fluent}/jira/domain/WellKnownJiraField.java (96%) create mode 100644 fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/package-info.java delete mode 100644 fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/package-info.java rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/api/MindMapAccessor.java (87%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/api/MindMapConvertConfig.java (92%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/api/MindMapPath.java (94%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/api/MindMapPathRecord.java (98%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/api/MindMapTransformer.java (90%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/api/MindmapTypeEnum.java (94%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/api/NodeLevel.java (84%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/exception/MindMapException.java (92%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/freemind/FreeMindNode.java (76%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/freemind/FreeMindTransformer.java (88%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/freemind/model/Arrowlink.java (99%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/freemind/model/Attribute.java (97%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/freemind/model/AttributeLayout.java (97%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/freemind/model/Cloud.java (96%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/freemind/model/Edge.java (98%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/freemind/model/Font.java (98%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/freemind/model/Hook.java (98%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/freemind/model/Html.java (97%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/freemind/model/Icon.java (96%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/freemind/model/Linktarget.java (99%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/freemind/model/Map.java (97%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/freemind/model/Node.java (99%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/freemind/model/ObjectFactory.java (98%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/freemind/model/Parameters.java (99%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/freemind/model/Richcontent.java (97%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/freemind/model/Text.java (95%) create mode 100644 fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/package-info.java rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/xmind/XMindNode.java (51%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/xmind/XMindUtil.java (94%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/xmind/XmindTransformer.java (88%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/xmind/model/Attached.java (83%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/xmind/model/Children.java (73%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/xmind/model/Comments.java (76%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/xmind/model/JsonRootBean.java (76%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/xmind/model/Notes.java (65%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/xmind/model/RootTopic.java (83%) rename fluent-wrappers/fluent-mindmap/src/main/java/io/{fluentqa => fluent}/mindmap/xmind/model/XmindRawData.java (96%) delete mode 100644 fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/package-info.java rename fluent-wrappers/fluent-mindmap/src/test/java/io/{fluentqa => fluent}/mindmap/api/DemoBean.java (89%) rename fluent-wrappers/fluent-mindmap/src/test/java/io/{fluentqa => fluent}/mindmap/api/DemoBeanConfig.java (94%) rename fluent-wrappers/fluent-mindmap/src/test/java/io/{fluentqa => fluent}/mindmap/api/DemoBeanWOLevel.java (86%) rename fluent-wrappers/fluent-mindmap/src/test/java/io/{fluentqa => fluent}/mindmap/api/MindMapAccessorTest.java (95%) rename fluent-wrappers/fluent-mindmap/src/test/java/io/{fluentqa => fluent}/mindmap/freemind/FreeMindConverterTest.java (74%) rename fluent-wrappers/fluent-mindmap/src/test/java/io/{fluentqa => fluent}/mindmap/xmind/XMindUtilTest.java (84%) rename fluent-wrappers/fluent-mindmap/src/test/java/io/{fluentqa => fluent}/mindmap/xmind/XmindTransformerTest.java (74%) rename fluent-wrappers/fluentqa-md/src/main/java/io/{fluentqa => fluent}/md/MarkdownAccessor.java (58%) create mode 100644 fluent-wrappers/fluentqa-md/src/main/java/io/fluent/md/package-info.java rename fluent-wrappers/fluentqa-md/src/main/java/io/{fluentqa => fluent}/md/parser/FieldParseConfig.java (86%) rename fluent-wrappers/fluentqa-md/src/main/java/io/{fluentqa => fluent}/md/parser/ParseConfig.java (93%) rename fluent-wrappers/fluentqa-md/src/main/java/io/{fluentqa => fluent}/md/parser/awesome/AwesomeListParserConfig.java (76%) rename fluent-wrappers/fluentqa-md/src/main/java/io/{fluentqa => fluent}/md/parser/awesome/AwesomeModel.java (79%) rename fluent-wrappers/fluentqa-md/src/main/java/io/{fluentqa => fluent}/md/parser/awesome/MarkdownAwesomeListParser.java (97%) delete mode 100644 fluent-wrappers/fluentqa-md/src/main/java/io/fluentqa/md/package-info.java rename fluent-wrappers/fluentqa-md/src/test/java/io/{fluentqa => fluent}/md/parsers/MarkdownAwesomeListParserTest.java (95%) diff --git a/fluent-apps/feeds/pom.xml b/fluent-apps/feeds/pom.xml deleted file mode 100644 index 53ddfbf..0000000 --- a/fluent-apps/feeds/pom.xml +++ /dev/null @@ -1,105 +0,0 @@ - - - 4.0.0 - - io.fluentqa - fluent-apps - 1.0-SNAPSHOT - - - feeds - - - UTF-8 - - - - - xyz.erupt - erupt-upms - ${erupt.version} - - - - xyz.erupt - erupt-security - ${erupt.version} - - - xyz.erupt - erupt-job - ${erupt.version} - - - - - - xyz.erupt - erupt-web - ${erupt.version} - - - org.springframework.boot - spring-boot-starter-tomcat - - - - - org.springframework.boot - spring-boot-starter-undertow - 2.7.12 - - - org.postgresql - postgresql - ${postgresql.version} - - - javax.xml.bind - jaxb-api - 2.3.1 - - - com.github.xiaoymin - knife4j-openapi2-spring-boot-starter - 4.4.0 - - - - io.fluent - fleunt-github - ${fluent.version} - - - cn.hutool - hutool-all - - - - - - maven-compiler-plugin - org.apache.maven.plugins - 3.11.0 - - 17 - 17 - - - - org.springframework.boot - spring-boot-maven-plugin - 2.7.2 - - - - repackage - - - - - - - \ No newline at end of file diff --git a/fluent-apps/feeds/src/main/java/io/fluent/datafam/DataFeedFarmApp.java b/fluent-apps/feeds/src/main/java/io/fluent/datafam/DataFeedFarmApp.java deleted file mode 100644 index 1f83913..0000000 --- a/fluent-apps/feeds/src/main/java/io/fluent/datafam/DataFeedFarmApp.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.fluent.datafam; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.domain.EntityScan; -import org.springframework.scheduling.annotation.EnableAsync; -import xyz.erupt.core.annotation.EruptScan; - -@SpringBootApplication -@EnableAsync -@EruptScan -@EntityScan -public class DataFeedFarmApp { - public static void main(String[] args) { - SpringApplication.run(DataFeedFarmApp.class); - } -} diff --git a/fluent-apps/feeds/src/main/resources/application-dev.yaml b/fluent-apps/feeds/src/main/resources/application-dev.yaml deleted file mode 100644 index 98ac74e..0000000 --- a/fluent-apps/feeds/src/main/resources/application-dev.yaml +++ /dev/null @@ -1,100 +0,0 @@ -erupt-app: - # 是否开启水印,1.12.0 及以上版本支持 - waterMark: false - # 登录失败几次出现验证码,值为0时表示一直需要登录验证码 - verifyCodeCount: 2 - # 登录密码是否加密传输,特殊场景如:LDAP登录可关闭该功能获取密码明文 - pwdTransferEncrypt: true - # 多语言配置,默认支持:简体中文、繁体中文、英文、日文;具体配置详见erupt-i18n模块 - locales: [ "zh-CN","zh-TW","en-US","ja-JP" ] -erupt: - # 是否开启csrf防御 - csrfInspect: true - # 是否开启redis方式存储session,默认false,开启后需在配置文件中添加redis配置(同 spring boot) - redisSession: false - # 附件上传存储路径, 默认路径为:/opt/erupt-attachment - uploadPath: /Users/patrick/data/temp - # 是否保留上传文件原始名称 - keepUploadFileName: false - # 登录session时长(redisSession为true时有效) - upms.expireTimeByLogin: 120 - # 是否记录操作日志,默认true,该功能开启后可在【系统管理 → 操作日志】中查看操作日志 - security.recordOperateLog: false - -spring: - datasource: - url: jdbc:postgresql://db.supabase.orb.local:5432/workspace - username: postgres - password: postgres - jpa: - show-sql: true - generate-ddl: true - database-platform: org.hibernate.dialect.PostgreSQLDialect - database: postgresql - -# mail: -# username: xxxx@qq.com -# password: xxxxxxx -# host: smtp.qq.com -# properties: -# mail.smtp.ssl.auth: true -# mail.smtp.ssl.enable: true -# mail.smtp.ssl.required: true - servlet: - multipart: - max-file-size: 100MB - max-request-size: 100MB - -# springdoc-openapi项目配置 -#springdoc: -# swagger-ui: -# path: /swagger-ui.html -# tags-sorter: alpha -# operations-sorter: alpha -# api-docs: -# path: /v3/api-docs -# group-configs: -# - group: 'default' -# paths-to-match: '/**' -# packages-to-scan: io.fluentqa -# knife4j的增强配置,不需要增强可以不配 -knife4j: - enable: true - openapi: - title: QA Workspace API - description: "QA Workspace API" - # workspace" - email: fluentqa@163.com - concat: fluent-qa -# url: https://docs.xiaominfo.com -# version: v4.0 -# license: Apache 2.0 -# license-url: https://stackoverflow.com/ -# terms-of-service-url: https://stackoverflow.com/ - group: - test1: - group-name: feeds api - api-rule: package -# api-rule-resources: -# - com.knife4j.demo.new3 - -server: - # 启用 gzip 压缩 - compression: - mime-types: application/javascript,text/css,application/json,application/xml,text/html,text/xml,text/plain - enabled: true - error: - includeException: true - includeStacktrace: ALWAYS - includeMessage: ALWAYS - port: 9090 -logging: - level: - root: DEBUG - org.hibernate: DEBUG - io.fluent: DEBUG - xyz.erupt: DEBUG - -magic-api: - web: /fluentapi/v1 - resource.location: ./magic-script \ No newline at end of file diff --git a/fluent-apps/pom.xml b/fluent-apps/pom.xml index f04511a..205d53d 100644 --- a/fluent-apps/pom.xml +++ b/fluent-apps/pom.xml @@ -12,9 +12,8 @@ fluent-apps pom - feeds pcinfo - qaserver + qam diff --git a/fluent-apps/qaserver/pom.xml b/fluent-apps/qam/pom.xml similarity index 89% rename from fluent-apps/qaserver/pom.xml rename to fluent-apps/qam/pom.xml index 863f8ac..5355043 100644 --- a/fluent-apps/qaserver/pom.xml +++ b/fluent-apps/qam/pom.xml @@ -9,8 +9,13 @@ 1.0-SNAPSHOT - qaserver + qam + + 17 + 17 + UTF-8 + @@ -87,12 +92,7 @@ io.fluent - fluent-generator - ${fluent.version} - - - io.fluent - fluent-git + fleunt-github ${fluent.version} @@ -111,11 +111,11 @@ 1.0-SNAPSHOT - - - - - + + + + + @@ -147,4 +147,5 @@ + \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/QAWorkspaceApp.java b/fluent-apps/qam/src/main/java/io/fluent/QAMApp.java similarity index 71% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/QAWorkspaceApp.java rename to fluent-apps/qam/src/main/java/io/fluent/QAMApp.java index c6516b4..35eb484 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/QAWorkspaceApp.java +++ b/fluent-apps/qam/src/main/java/io/fluent/QAMApp.java @@ -1,4 +1,4 @@ -package io.fluentqa; +package io.fluent; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -10,8 +10,12 @@ @EnableAsync @EruptScan @EntityScan -public class QAWorkspaceApp { +public class QAMApp { + /** + * QA Management Application Entrypoint + * @param args + */ public static void main(String[] args) { - SpringApplication.run(QAWorkspaceApp.class); + SpringApplication.run(QAMApp.class); } } diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/FluentProductConfigModule.java b/fluent-apps/qam/src/main/java/io/fluent/base/FluentProductConfigModule.java similarity index 90% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/base/FluentProductConfigModule.java rename to fluent-apps/qam/src/main/java/io/fluent/base/FluentProductConfigModule.java index 3dd958c..c384967 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/FluentProductConfigModule.java +++ b/fluent-apps/qam/src/main/java/io/fluent/base/FluentProductConfigModule.java @@ -1,9 +1,9 @@ -package io.fluentqa.base; +package io.fluent.base; -import io.fluentqa.base.masterdata.model.MasterData; -import io.fluentqa.base.product.model.ProductModuleModel; -import io.fluentqa.base.project.model.ProjectModel; -import io.fluentqa.base.upload.model.UploadFileModel; +import io.fluent.base.masterdata.model.MasterData; +import io.fluent.base.product.model.ProductModuleModel; +import io.fluent.base.project.model.ProjectModel; +import io.fluent.base.upload.model.UploadFileModel; import org.apache.commons.math3.stat.descriptive.summary.Product; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.boot.context.properties.EnableConfigurationProperties; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/FluentUploadTCModule.java b/fluent-apps/qam/src/main/java/io/fluent/base/FluentUploadTCModule.java similarity index 95% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/base/FluentUploadTCModule.java rename to fluent-apps/qam/src/main/java/io/fluent/base/FluentUploadTCModule.java index 9e44381..52e5b7d 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/FluentUploadTCModule.java +++ b/fluent-apps/qam/src/main/java/io/fluent/base/FluentUploadTCModule.java @@ -1,6 +1,6 @@ -package io.fluentqa.base; +package io.fluent.base; -import io.fluentqa.base.upload.model.UploadFileModel; +import io.fluent.base.upload.model.UploadFileModel; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.ComponentScan; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/README.md b/fluent-apps/qam/src/main/java/io/fluent/base/README.md similarity index 100% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/base/README.md rename to fluent-apps/qam/src/main/java/io/fluent/base/README.md diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/masterdata/model/MasterData.java b/fluent-apps/qam/src/main/java/io/fluent/base/masterdata/model/MasterData.java similarity index 93% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/base/masterdata/model/MasterData.java rename to fluent-apps/qam/src/main/java/io/fluent/base/masterdata/model/MasterData.java index fab03e9..e8d0150 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/masterdata/model/MasterData.java +++ b/fluent-apps/qam/src/main/java/io/fluent/base/masterdata/model/MasterData.java @@ -1,7 +1,7 @@ -package io.fluentqa.base.masterdata.model; +package io.fluent.base.masterdata.model; -import io.fluentqa.base.handlers.SqlTagFetchHandler; -import io.fluentqa.base.model.ModelWithValidFlagVo; +import io.fluent.base.handlers.SqlTagFetchHandler; +import io.fluent.base.model.ModelWithValidFlagVo; import lombok.Data; import xyz.erupt.annotation.Erupt; import xyz.erupt.annotation.EruptField; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/masterdata/repo/MasterDataRepo.java b/fluent-apps/qam/src/main/java/io/fluent/base/masterdata/repo/MasterDataRepo.java similarity index 80% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/base/masterdata/repo/MasterDataRepo.java rename to fluent-apps/qam/src/main/java/io/fluent/base/masterdata/repo/MasterDataRepo.java index db4adae..2618156 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/masterdata/repo/MasterDataRepo.java +++ b/fluent-apps/qam/src/main/java/io/fluent/base/masterdata/repo/MasterDataRepo.java @@ -1,6 +1,6 @@ -package io.fluentqa.base.masterdata.repo; +package io.fluent.base.masterdata.repo; -import io.fluentqa.base.masterdata.model.MasterData; +import io.fluent.base.masterdata.model.MasterData; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.stereotype.Repository; diff --git a/fluent-apps/qam/src/main/java/io/fluent/base/package-info.java b/fluent-apps/qam/src/main/java/io/fluent/base/package-info.java new file mode 100644 index 0000000..b1b2672 --- /dev/null +++ b/fluent-apps/qam/src/main/java/io/fluent/base/package-info.java @@ -0,0 +1 @@ +package io.fluent.base; \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/model/ProductModuleModel.java b/fluent-apps/qam/src/main/java/io/fluent/base/product/model/ProductModuleModel.java similarity index 97% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/model/ProductModuleModel.java rename to fluent-apps/qam/src/main/java/io/fluent/base/product/model/ProductModuleModel.java index 4a63a5e..a86390c 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/model/ProductModuleModel.java +++ b/fluent-apps/qam/src/main/java/io/fluent/base/product/model/ProductModuleModel.java @@ -1,6 +1,6 @@ -package io.fluentqa.base.product.model; +package io.fluent.base.product.model; -import io.fluentqa.base.model.ModelWithValidFlagVo; +import io.fluent.base.model.ModelWithValidFlagVo; import xyz.erupt.annotation.Erupt; import xyz.erupt.annotation.EruptField; import xyz.erupt.annotation.sub_erupt.Power; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/model/ProductModuleValidFlagVo.java b/fluent-apps/qam/src/main/java/io/fluent/base/product/model/ProductModuleValidFlagVo.java similarity index 94% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/model/ProductModuleValidFlagVo.java rename to fluent-apps/qam/src/main/java/io/fluent/base/product/model/ProductModuleValidFlagVo.java index 4aa8bee..b267168 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/model/ProductModuleValidFlagVo.java +++ b/fluent-apps/qam/src/main/java/io/fluent/base/product/model/ProductModuleValidFlagVo.java @@ -1,6 +1,6 @@ -package io.fluentqa.base.product.model; +package io.fluent.base.product.model; -import io.fluentqa.base.model.ModelWithValidFlagVo; +import io.fluent.base.model.ModelWithValidFlagVo; import lombok.Getter; import lombok.Setter; import xyz.erupt.annotation.EruptField; diff --git a/fluent-apps/qam/src/main/java/io/fluent/base/product/package-info.java b/fluent-apps/qam/src/main/java/io/fluent/base/product/package-info.java new file mode 100644 index 0000000..15f9d3f --- /dev/null +++ b/fluent-apps/qam/src/main/java/io/fluent/base/product/package-info.java @@ -0,0 +1 @@ +package io.fluent.base.product; \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/repo/ProductModuleRepo.java b/fluent-apps/qam/src/main/java/io/fluent/base/product/repo/ProductModuleRepo.java similarity index 86% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/repo/ProductModuleRepo.java rename to fluent-apps/qam/src/main/java/io/fluent/base/product/repo/ProductModuleRepo.java index ac9d87f..7898e5f 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/repo/ProductModuleRepo.java +++ b/fluent-apps/qam/src/main/java/io/fluent/base/product/repo/ProductModuleRepo.java @@ -1,7 +1,7 @@ -package io.fluentqa.base.product.repo; +package io.fluent.base.product.repo; -import io.fluentqa.base.product.model.ProductModuleModel; +import io.fluent.base.product.model.ProductModuleModel; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.stereotype.Repository; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/service/ProductModuleService.java b/fluent-apps/qam/src/main/java/io/fluent/base/product/service/ProductModuleService.java similarity index 86% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/service/ProductModuleService.java rename to fluent-apps/qam/src/main/java/io/fluent/base/product/service/ProductModuleService.java index 72afcdf..0c42dac 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/service/ProductModuleService.java +++ b/fluent-apps/qam/src/main/java/io/fluent/base/product/service/ProductModuleService.java @@ -1,11 +1,11 @@ -package io.fluentqa.base.product.service; +package io.fluent.base.product.service; import io.fluent.builtin.PingYinUtils; -import io.fluentqa.base.product.repo.ProductModuleRepo; -import io.fluentqa.base.proxies.AuditDataEnhancerProxy; -import io.fluentqa.base.masterdata.repo.MasterDataRepo; -import io.fluentqa.base.product.model.ProductModuleModel; -import io.fluentqa.base.masterdata.model.MasterData; +import io.fluent.base.product.repo.ProductModuleRepo; +import io.fluent.base.proxies.AuditDataEnhancerProxy; +import io.fluent.base.masterdata.repo.MasterDataRepo; +import io.fluent.base.product.model.ProductModuleModel; +import io.fluent.base.masterdata.model.MasterData; import org.springframework.stereotype.Service; import javax.annotation.Resource; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/project/model/ProjectModel.java b/fluent-apps/qam/src/main/java/io/fluent/base/project/model/ProjectModel.java similarity index 94% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/base/project/model/ProjectModel.java rename to fluent-apps/qam/src/main/java/io/fluent/base/project/model/ProjectModel.java index 8109405..573eace 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/project/model/ProjectModel.java +++ b/fluent-apps/qam/src/main/java/io/fluent/base/project/model/ProjectModel.java @@ -1,8 +1,8 @@ -package io.fluentqa.base.project.model; +package io.fluent.base.project.model; -import io.fluentqa.base.model.ModelWithValidFlagVo; -import io.fluentqa.base.product.model.ProductModuleModel; +import io.fluent.base.model.ModelWithValidFlagVo; +import io.fluent.base.product.model.ProductModuleModel; import lombok.Data; import xyz.erupt.annotation.Erupt; import xyz.erupt.annotation.EruptField; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/project/repo/ProjectModelRepo.java b/fluent-apps/qam/src/main/java/io/fluent/base/project/repo/ProjectModelRepo.java similarity index 86% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/base/project/repo/ProjectModelRepo.java rename to fluent-apps/qam/src/main/java/io/fluent/base/project/repo/ProjectModelRepo.java index 5115fd8..098b228 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/project/repo/ProjectModelRepo.java +++ b/fluent-apps/qam/src/main/java/io/fluent/base/project/repo/ProjectModelRepo.java @@ -1,7 +1,7 @@ -package io.fluentqa.base.project.repo; +package io.fluent.base.project.repo; -import io.fluentqa.base.product.model.ProductModuleModel; +import io.fluent.base.product.model.ProductModuleModel; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.stereotype.Repository; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/model/UploadFileModel.java b/fluent-apps/qam/src/main/java/io/fluent/base/upload/model/UploadFileModel.java similarity index 93% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/model/UploadFileModel.java rename to fluent-apps/qam/src/main/java/io/fluent/base/upload/model/UploadFileModel.java index 484acb0..cb29089 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/model/UploadFileModel.java +++ b/fluent-apps/qam/src/main/java/io/fluent/base/upload/model/UploadFileModel.java @@ -1,7 +1,7 @@ -package io.fluentqa.base.upload.model; +package io.fluent.base.upload.model; -import io.fluentqa.base.upload.proxy.UploadFileDataProxy; -import io.fluentqa.base.product.model.ProductModuleModel; +import io.fluent.base.upload.proxy.UploadFileDataProxy; +import io.fluent.base.product.model.ProductModuleModel; import lombok.Data; import xyz.erupt.annotation.Erupt; import xyz.erupt.annotation.EruptField; diff --git a/fluent-apps/qam/src/main/java/io/fluent/base/upload/package-info.java b/fluent-apps/qam/src/main/java/io/fluent/base/upload/package-info.java new file mode 100644 index 0000000..67c0eee --- /dev/null +++ b/fluent-apps/qam/src/main/java/io/fluent/base/upload/package-info.java @@ -0,0 +1 @@ +package io.fluent.base.upload; \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/proxy/UploadFileDataProxy.java b/fluent-apps/qam/src/main/java/io/fluent/base/upload/proxy/UploadFileDataProxy.java similarity index 87% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/proxy/UploadFileDataProxy.java rename to fluent-apps/qam/src/main/java/io/fluent/base/upload/proxy/UploadFileDataProxy.java index ccd7116..db927e0 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/proxy/UploadFileDataProxy.java +++ b/fluent-apps/qam/src/main/java/io/fluent/base/upload/proxy/UploadFileDataProxy.java @@ -1,13 +1,13 @@ -package io.fluentqa.base.upload.proxy; +package io.fluent.base.upload.proxy; import cn.hutool.core.bean.BeanUtil; -import io.fluentqa.excel.ExcelReadWriter; -import io.fluentqa.base.product.model.ProductModuleModel; -import io.fluentqa.qtm.tc.dto.TestCaseDTO; -import io.fluentqa.qtm.tc.service.TestCaseService; -import io.fluentqa.qtm.tc.service.impl.MindMappingService; +import io.fluent.excel.ExcelReadWriter; +import io.fluent.base.product.model.ProductModuleModel; +import io.fluent.qtm.tc.dto.TestCaseDTO; +import io.fluent.qtm.tc.service.TestCaseService; +import io.fluent.qtm.tc.service.impl.MindMappingService; import lombok.extern.slf4j.Slf4j; import xyz.erupt.core.prop.EruptProp; import xyz.erupt.core.util.EruptSpringUtil; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/proxy/UploadFileTypeEnum.java b/fluent-apps/qam/src/main/java/io/fluent/base/upload/proxy/UploadFileTypeEnum.java similarity index 92% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/proxy/UploadFileTypeEnum.java rename to fluent-apps/qam/src/main/java/io/fluent/base/upload/proxy/UploadFileTypeEnum.java index f9b14ca..8a44582 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/proxy/UploadFileTypeEnum.java +++ b/fluent-apps/qam/src/main/java/io/fluent/base/upload/proxy/UploadFileTypeEnum.java @@ -1,4 +1,4 @@ -package io.fluentqa.base.upload.proxy; +package io.fluent.base.upload.proxy; public enum UploadFileTypeEnum { EXCEL_TC,FREEMIND,PM,MINDMAP; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/FluentQAApiModule.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/FluentQAApiModule.java similarity index 98% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/FluentQAApiModule.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/FluentQAApiModule.java index 58a13d4..2a8570a 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/FluentQAApiModule.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/FluentQAApiModule.java @@ -1,7 +1,7 @@ -package io.fluentqa.qtm; +package io.fluent.qtm; -import io.fluentqa.qtm.api.model.*; +import io.fluent.qtm.api.model.*; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.ComponentScan; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/handler/GenerateApiCaseByCaptureDataHandler.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/handler/GenerateApiCaseByCaptureDataHandler.java similarity index 80% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/handler/GenerateApiCaseByCaptureDataHandler.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/handler/GenerateApiCaseByCaptureDataHandler.java index 3ad4fc3..1466ab1 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/handler/GenerateApiCaseByCaptureDataHandler.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/handler/GenerateApiCaseByCaptureDataHandler.java @@ -1,7 +1,7 @@ -package io.fluentqa.qtm.api.handler; +package io.fluent.qtm.api.handler; -import io.fluentqa.qtm.api.model.ApiMonitorRecord; -import io.fluentqa.qtm.api.service.ApiTestCaseService; +import io.fluent.qtm.api.model.ApiMonitorRecord; +import io.fluent.qtm.api.service.ApiTestCaseService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import xyz.erupt.annotation.fun.OperationHandler; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/handler/GenerateApiTestStepByApiTestRecord.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/handler/GenerateApiTestStepByApiTestRecord.java similarity index 81% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/handler/GenerateApiTestStepByApiTestRecord.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/handler/GenerateApiTestStepByApiTestRecord.java index 2ccb338..02361bd 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/handler/GenerateApiTestStepByApiTestRecord.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/handler/GenerateApiTestStepByApiTestRecord.java @@ -1,7 +1,7 @@ -package io.fluentqa.qtm.api.handler; +package io.fluent.qtm.api.handler; -import io.fluentqa.qtm.api.model.ApiTestRecord; -import io.fluentqa.qtm.api.service.ApiTestCaseService; +import io.fluent.qtm.api.model.ApiTestRecord; +import io.fluent.qtm.api.service.ApiTestCaseService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import xyz.erupt.annotation.fun.OperationHandler; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/handler/GenerateRawApiCaseHandler.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/handler/GenerateRawApiCaseHandler.java similarity index 79% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/handler/GenerateRawApiCaseHandler.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/handler/GenerateRawApiCaseHandler.java index c64a72e..bd98597 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/handler/GenerateRawApiCaseHandler.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/handler/GenerateRawApiCaseHandler.java @@ -1,7 +1,7 @@ -package io.fluentqa.qtm.api.handler; +package io.fluent.qtm.api.handler; -import io.fluentqa.qtm.api.model.RemoteApi; -import io.fluentqa.qtm.api.service.ApiTestCaseService; +import io.fluent.qtm.api.model.RemoteApi; +import io.fluent.qtm.api.service.ApiTestCaseService; import org.springframework.stereotype.Service; import xyz.erupt.annotation.fun.OperationHandler; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiMonitorRecord.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiMonitorRecord.java similarity index 96% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiMonitorRecord.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiMonitorRecord.java index ba9ced6..cc2999e 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiMonitorRecord.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiMonitorRecord.java @@ -1,8 +1,8 @@ -package io.fluentqa.qtm.api.model; +package io.fluent.qtm.api.model; -import io.fluentqa.qtm.api.handler.GenerateApiCaseByCaptureDataHandler; -import io.fluentqa.base.handlers.SqlTagFetchHandler; +import io.fluent.qtm.api.handler.GenerateApiCaseByCaptureDataHandler; +import io.fluent.base.handlers.SqlTagFetchHandler; import lombok.Data; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiSpecChangeModel.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiSpecChangeModel.java similarity index 97% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiSpecChangeModel.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiSpecChangeModel.java index bbb757b..723930d 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiSpecChangeModel.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiSpecChangeModel.java @@ -1,4 +1,4 @@ -package io.fluentqa.qtm.api.model; +package io.fluent.qtm.api.model; import lombok.Data; import org.hibernate.annotations.DynamicInsert; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiSpecGitRepoModel.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiSpecGitRepoModel.java similarity index 97% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiSpecGitRepoModel.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiSpecGitRepoModel.java index 36d69e8..cb97dfd 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiSpecGitRepoModel.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiSpecGitRepoModel.java @@ -1,4 +1,4 @@ -package io.fluentqa.qtm.api.model; +package io.fluent.qtm.api.model; import lombok.Data; import xyz.erupt.annotation.Erupt; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiSpecVersionModel.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiSpecVersionModel.java similarity index 97% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiSpecVersionModel.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiSpecVersionModel.java index 45d1154..6bd226d 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiSpecVersionModel.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiSpecVersionModel.java @@ -1,6 +1,6 @@ -package io.fluentqa.qtm.api.model; +package io.fluent.qtm.api.model; -import io.fluentqa.base.model.ModelWithValidFlagVo; +import io.fluent.base.model.ModelWithValidFlagVo; import lombok.Data; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiStep.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiStep.java similarity index 98% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiStep.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiStep.java index 7d72936..9f26690 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiStep.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiStep.java @@ -1,6 +1,6 @@ -package io.fluentqa.qtm.api.model; +package io.fluent.qtm.api.model; -import io.fluentqa.base.handlers.SqlTagFetchHandler; +import io.fluent.base.handlers.SqlTagFetchHandler; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; import xyz.erupt.annotation.Erupt; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiTestRecord.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiTestRecord.java similarity index 96% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiTestRecord.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiTestRecord.java index d901d45..e03248c 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiTestRecord.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiTestRecord.java @@ -1,7 +1,7 @@ -package io.fluentqa.qtm.api.model; +package io.fluent.qtm.api.model; -import io.fluentqa.qtm.api.handler.GenerateApiTestStepByApiTestRecord; -import io.fluentqa.base.handlers.SqlTagFetchHandler; +import io.fluent.qtm.api.handler.GenerateApiTestStepByApiTestRecord; +import io.fluent.base.handlers.SqlTagFetchHandler; import lombok.Data; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiTestScenario.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiTestScenario.java similarity index 95% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiTestScenario.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiTestScenario.java index 4055930..eb187f8 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/ApiTestScenario.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiTestScenario.java @@ -1,8 +1,8 @@ -package io.fluentqa.qtm.api.model; +package io.fluent.qtm.api.model; -import io.fluentqa.base.handlers.SqlTagFetchHandler; -import io.fluentqa.base.model.ModelWithValidFlag; -import io.fluentqa.base.product.model.ProductModuleModel; +import io.fluent.base.handlers.SqlTagFetchHandler; +import io.fluent.base.model.ModelWithValidFlag; +import io.fluent.base.product.model.ProductModuleModel; import xyz.erupt.annotation.Erupt; import xyz.erupt.annotation.EruptField; import xyz.erupt.annotation.sub_erupt.Layout; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RawApiTestCase.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RawApiTestCase.java similarity index 98% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RawApiTestCase.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RawApiTestCase.java index 96441b0..9498f09 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RawApiTestCase.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RawApiTestCase.java @@ -1,4 +1,4 @@ -package io.fluentqa.qtm.api.model; +package io.fluent.qtm.api.model; import lombok.Data; import org.hibernate.annotations.DynamicInsert; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RemoteApi.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RemoteApi.java similarity index 97% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RemoteApi.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RemoteApi.java index 239ca7a..84763bd 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RemoteApi.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RemoteApi.java @@ -1,9 +1,9 @@ -package io.fluentqa.qtm.api.model; +package io.fluent.qtm.api.model; import cn.hutool.core.lang.UUID; -import io.fluentqa.qtm.api.handler.GenerateRawApiCaseHandler; -import io.fluentqa.base.handlers.SqlTagFetchHandler; -import io.fluentqa.base.model.ModelWithValidFlag; +import io.fluent.qtm.api.handler.GenerateRawApiCaseHandler; +import io.fluent.base.handlers.SqlTagFetchHandler; +import io.fluent.base.model.ModelWithValidFlag; import lombok.Data; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RemoteApiStatus.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RemoteApiStatus.java similarity index 65% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RemoteApiStatus.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RemoteApiStatus.java index 7803e71..4b66a5c 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RemoteApiStatus.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RemoteApiStatus.java @@ -1,4 +1,4 @@ -package io.fluentqa.qtm.api.model; +package io.fluent.qtm.api.model; public enum RemoteApiStatus { NEW,IN_USE,UPDATED,END_OF_LIFE diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RemoteApiType.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RemoteApiType.java similarity index 55% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RemoteApiType.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RemoteApiType.java index 77bcba5..74c2557 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/model/RemoteApiType.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RemoteApiType.java @@ -1,4 +1,4 @@ -package io.fluentqa.qtm.api.model; +package io.fluent.qtm.api.model; public enum RemoteApiType { API,RPC diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/package-info.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/package-info.java new file mode 100644 index 0000000..73f4e8e --- /dev/null +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/package-info.java @@ -0,0 +1 @@ +package io.fluent.qtm.api; \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiMonitorRecordRepo.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiMonitorRecordRepo.java similarity index 82% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiMonitorRecordRepo.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiMonitorRecordRepo.java index fc3cf8a..e03552b 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiMonitorRecordRepo.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiMonitorRecordRepo.java @@ -1,6 +1,6 @@ -package io.fluentqa.qtm.api.repo; +package io.fluent.qtm.api.repo; -import io.fluentqa.qtm.api.model.ApiMonitorRecord; +import io.fluent.qtm.api.model.ApiMonitorRecord; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.stereotype.Repository; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiSpecChangeRepository.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiSpecChangeRepository.java similarity index 71% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiSpecChangeRepository.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiSpecChangeRepository.java index f186062..ff3cdf3 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiSpecChangeRepository.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiSpecChangeRepository.java @@ -1,7 +1,7 @@ -package io.fluentqa.qtm.api.repo; +package io.fluent.qtm.api.repo; -import io.fluentqa.qtm.api.model.ApiSpecChangeModel; +import io.fluent.qtm.api.model.ApiSpecChangeModel; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiSpecGitRepoRepository.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiSpecGitRepoRepository.java similarity index 71% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiSpecGitRepoRepository.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiSpecGitRepoRepository.java index 64af875..4a6af7a 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiSpecGitRepoRepository.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiSpecGitRepoRepository.java @@ -1,6 +1,6 @@ -package io.fluentqa.qtm.api.repo; +package io.fluent.qtm.api.repo; -import io.fluentqa.qtm.api.model.ApiSpecGitRepoModel; +import io.fluent.qtm.api.model.ApiSpecGitRepoModel; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiSpecVersionRepository.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiSpecVersionRepository.java similarity index 71% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiSpecVersionRepository.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiSpecVersionRepository.java index 5f8c1bd..361f79d 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiSpecVersionRepository.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiSpecVersionRepository.java @@ -1,6 +1,6 @@ -package io.fluentqa.qtm.api.repo; +package io.fluent.qtm.api.repo; -import io.fluentqa.qtm.api.model.ApiSpecVersionModel; +import io.fluent.qtm.api.model.ApiSpecVersionModel; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiTestScenarioRepo.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiTestScenarioRepo.java similarity index 79% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiTestScenarioRepo.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiTestScenarioRepo.java index d0c358a..e389772 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiTestScenarioRepo.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiTestScenarioRepo.java @@ -1,6 +1,6 @@ -package io.fluentqa.qtm.api.repo; +package io.fluent.qtm.api.repo; -import io.fluentqa.qtm.api.model.ApiTestScenario; +import io.fluent.qtm.api.model.ApiTestScenario; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.stereotype.Repository; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiTestStepRepo.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiTestStepRepo.java similarity index 80% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiTestStepRepo.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiTestStepRepo.java index 82cac02..f867291 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/ApiTestStepRepo.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiTestStepRepo.java @@ -1,6 +1,6 @@ -package io.fluentqa.qtm.api.repo; +package io.fluent.qtm.api.repo; -import io.fluentqa.qtm.api.model.ApiStep; +import io.fluent.qtm.api.model.ApiStep; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.stereotype.Repository; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/RawApiTestCaseRepo.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/RawApiTestCaseRepo.java similarity index 79% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/RawApiTestCaseRepo.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/RawApiTestCaseRepo.java index cd2e625..fb9e408 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/RawApiTestCaseRepo.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/RawApiTestCaseRepo.java @@ -1,6 +1,6 @@ -package io.fluentqa.qtm.api.repo; +package io.fluent.qtm.api.repo; -import io.fluentqa.qtm.api.model.RawApiTestCase; +import io.fluent.qtm.api.model.RawApiTestCase; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.stereotype.Repository; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/RemoteServiceRepo.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/RemoteServiceRepo.java similarity index 89% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/RemoteServiceRepo.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/RemoteServiceRepo.java index 5213fd1..f6f4ad0 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/repo/RemoteServiceRepo.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/RemoteServiceRepo.java @@ -1,6 +1,6 @@ -package io.fluentqa.qtm.api.repo; +package io.fluent.qtm.api.repo; -import io.fluentqa.qtm.api.model.RemoteApi; +import io.fluent.qtm.api.model.RemoteApi; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.stereotype.Repository; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/service/ApiTestCaseService.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/service/ApiTestCaseService.java similarity index 94% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/service/ApiTestCaseService.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/service/ApiTestCaseService.java index 018a7e4..cd465be 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/service/ApiTestCaseService.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/service/ApiTestCaseService.java @@ -1,12 +1,12 @@ -package io.fluentqa.qtm.api.service; +package io.fluent.qtm.api.service; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.StrUtil; import io.fluent.builtin.CollectionsUtils; -import io.fluentqa.qtm.api.model.*; -import io.fluentqa.qtm.api.repo.ApiMonitorRecordRepo; -import io.fluentqa.qtm.api.repo.ApiTestStepRepo; -import io.fluentqa.qtm.api.repo.RawApiTestCaseRepo; +import io.fluent.qtm.api.model.*; +import io.fluent.qtm.api.repo.ApiMonitorRecordRepo; +import io.fluent.qtm.api.repo.ApiTestStepRepo; +import io.fluent.qtm.api.repo.RawApiTestCaseRepo; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/service/RemoteApiService.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/service/RemoteApiService.java similarity index 93% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/service/RemoteApiService.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/api/service/RemoteApiService.java index 91fcd73..27c3803 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/service/RemoteApiService.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/service/RemoteApiService.java @@ -1,15 +1,15 @@ -package io.fluentqa.qtm.api.service; +package io.fluent.qtm.api.service; import cn.hutool.core.bean.BeanUtil; import io.fluent.postman.PostmanParser; import io.fluent.postman.model.PostmanCollection; import io.fluent.postman.model.PostmanItem; -import io.fluentqa.qtm.api.model.ApiSpecVersionModel; -import io.fluentqa.qtm.api.model.RemoteApi; -import io.fluentqa.qtm.api.model.RemoteApiStatus; -import io.fluentqa.qtm.api.repo.RemoteServiceRepo; -import io.fluentqa.base.product.model.ProductModuleModel; -import io.fluentqa.base.product.service.ProductModuleService; +import io.fluent.qtm.api.model.ApiSpecVersionModel; +import io.fluent.qtm.api.model.RemoteApi; +import io.fluent.qtm.api.model.RemoteApiStatus; +import io.fluent.qtm.api.repo.RemoteServiceRepo; +import io.fluent.base.product.model.ProductModuleModel; +import io.fluent.base.product.service.ProductModuleService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/package-info.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/package-info.java new file mode 100644 index 0000000..b8c6392 --- /dev/null +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/package-info.java @@ -0,0 +1 @@ +package io.fluent.qtm.pm; \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/FieldOption.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/FieldOption.java similarity index 97% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/FieldOption.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/FieldOption.java index 8a0f6ee..d0727ce 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/FieldOption.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/FieldOption.java @@ -1,7 +1,7 @@ -package io.fluentqa.qtm.pm.requirement; +package io.fluent.qtm.pm.requirement; -import io.fluentqa.base.model.ModelWithValidFlag; +import io.fluent.base.model.ModelWithValidFlag; import xyz.erupt.annotation.Erupt; import xyz.erupt.annotation.EruptField; import xyz.erupt.annotation.fun.ChoiceFetchHandler; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/FieldOptionType.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/FieldOptionType.java similarity index 86% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/FieldOptionType.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/FieldOptionType.java index 44c921c..69b306b 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/FieldOptionType.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/FieldOptionType.java @@ -1,4 +1,4 @@ -package io.fluentqa.qtm.pm.requirement; +package io.fluent.qtm.pm.requirement; import lombok.Getter; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/RequirementFeature.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/RequirementFeature.java similarity index 91% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/RequirementFeature.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/RequirementFeature.java index a9432f7..a91ffc6 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/RequirementFeature.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/RequirementFeature.java @@ -1,6 +1,6 @@ -package io.fluentqa.qtm.pm.requirement; +package io.fluent.qtm.pm.requirement; -import io.fluentqa.base.model.ModelWithValidFlag; +import io.fluent.base.model.ModelWithValidFlag; import xyz.erupt.annotation.Erupt; import xyz.erupt.annotation.EruptField; import xyz.erupt.annotation.sub_erupt.Power; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/RequirementType.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/RequirementType.java similarity index 89% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/RequirementType.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/RequirementType.java index d960f62..23f7c44 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/RequirementType.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/RequirementType.java @@ -1,4 +1,4 @@ -package io.fluentqa.qtm.pm.requirement; +package io.fluent.qtm.pm.requirement; import lombok.Getter; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/TestRequirement.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/TestRequirement.java similarity index 96% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/TestRequirement.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/TestRequirement.java index 6f8bd08..31a5222 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/requirement/TestRequirement.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/TestRequirement.java @@ -1,9 +1,9 @@ -package io.fluentqa.qtm.pm.requirement; +package io.fluent.qtm.pm.requirement; -import io.fluentqa.base.handlers.SqlTagFetchHandler; -import io.fluentqa.base.model.ModelWithValidFlag; -import io.fluentqa.base.product.model.ProductModuleModel; +import io.fluent.base.handlers.SqlTagFetchHandler; +import io.fluent.base.model.ModelWithValidFlag; +import io.fluent.base.product.model.ProductModuleModel; import xyz.erupt.annotation.Erupt; import xyz.erupt.annotation.EruptField; import xyz.erupt.annotation.fun.ChoiceFetchHandler; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/dto/TestCaseDTO.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/dto/TestCaseDTO.java similarity index 95% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/dto/TestCaseDTO.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/tc/dto/TestCaseDTO.java index bc1c15f..bc776ff 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/dto/TestCaseDTO.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/dto/TestCaseDTO.java @@ -1,4 +1,4 @@ -package io.fluentqa.qtm.tc.dto; +package io.fluent.qtm.tc.dto; import com.github.crab2died.annotation.ExcelField; import lombok.Data; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/handlers/GenerateTestRecordHandler.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/handlers/GenerateTestRecordHandler.java similarity index 85% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/handlers/GenerateTestRecordHandler.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/tc/handlers/GenerateTestRecordHandler.java index f5b2fe9..cc87109 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/handlers/GenerateTestRecordHandler.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/handlers/GenerateTestRecordHandler.java @@ -1,12 +1,12 @@ -package io.fluentqa.qtm.tc.handlers; +package io.fluent.qtm.tc.handlers; import cn.hutool.core.bean.BeanUtil; -import io.fluentqa.qtm.tc.model.TestCase; -import io.fluentqa.qtm.tc.model.TestResult; -import io.fluentqa.qtm.tc.model.TestRun; -import io.fluentqa.qtm.tc.model.TestScenario; -import io.fluentqa.qtm.tc.repo.TestResultRepo; +import io.fluent.qtm.tc.model.TestCase; +import io.fluent.qtm.tc.model.TestResult; +import io.fluent.qtm.tc.model.TestRun; +import io.fluent.qtm.tc.model.TestScenario; +import io.fluent.qtm.tc.repo.TestResultRepo; import org.springframework.stereotype.Service; import xyz.erupt.annotation.fun.OperationHandler; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestCase.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestCase.java similarity index 97% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestCase.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestCase.java index 4b8a23f..2db1f68 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestCase.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestCase.java @@ -1,8 +1,8 @@ -package io.fluentqa.qtm.tc.model; +package io.fluent.qtm.tc.model; -import io.fluentqa.base.handlers.SqlTagFetchHandler; -import io.fluentqa.base.model.ModelWithValidFlagVo; -import io.fluentqa.base.product.model.ProductModuleModel; +import io.fluent.base.handlers.SqlTagFetchHandler; +import io.fluent.base.model.ModelWithValidFlagVo; +import io.fluent.base.product.model.ProductModuleModel; import xyz.erupt.annotation.Erupt; import xyz.erupt.annotation.EruptField; import xyz.erupt.annotation.sub_erupt.Layout; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestResult.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestResult.java similarity index 98% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestResult.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestResult.java index bdb52e0..63039fb 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestResult.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestResult.java @@ -1,7 +1,7 @@ -package io.fluentqa.qtm.tc.model; +package io.fluent.qtm.tc.model; -import io.fluentqa.base.model.ModelWithValidFlagVo; +import io.fluent.base.model.ModelWithValidFlagVo; import xyz.erupt.annotation.Erupt; import xyz.erupt.annotation.EruptField; import xyz.erupt.annotation.sub_erupt.LinkTree; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestRun.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestRun.java similarity index 97% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestRun.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestRun.java index e054be6..47c9af6 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestRun.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestRun.java @@ -1,8 +1,8 @@ -package io.fluentqa.qtm.tc.model; +package io.fluent.qtm.tc.model; -import io.fluentqa.base.product.model.ProductModuleModel; -import io.fluentqa.qtm.tc.handlers.GenerateTestRecordHandler; +import io.fluent.base.product.model.ProductModuleModel; +import io.fluent.qtm.tc.handlers.GenerateTestRecordHandler; import xyz.erupt.annotation.Erupt; import xyz.erupt.annotation.EruptField; import xyz.erupt.annotation.sub_erupt.Power; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestScenario.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestScenario.java similarity index 94% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestScenario.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestScenario.java index eb2efa8..58478ab 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/model/TestScenario.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestScenario.java @@ -1,6 +1,6 @@ -package io.fluentqa.qtm.tc.model; +package io.fluent.qtm.tc.model; -import io.fluentqa.base.model.NamedModelVO; +import io.fluent.base.model.NamedModelVO; import xyz.erupt.annotation.Erupt; import xyz.erupt.annotation.EruptField; import xyz.erupt.annotation.sub_erupt.Power; diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/package-info.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/package-info.java new file mode 100644 index 0000000..0f28f0f --- /dev/null +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/package-info.java @@ -0,0 +1 @@ +package io.fluent.qtm.tc; \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/repo/TestCaseRepo.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/repo/TestCaseRepo.java similarity index 85% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/repo/TestCaseRepo.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/tc/repo/TestCaseRepo.java index dbc1f7d..354138a 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/repo/TestCaseRepo.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/repo/TestCaseRepo.java @@ -1,6 +1,6 @@ -package io.fluentqa.qtm.tc.repo; +package io.fluent.qtm.tc.repo; -import io.fluentqa.qtm.tc.model.TestCase; +import io.fluent.qtm.tc.model.TestCase; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/repo/TestResultRepo.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/repo/TestResultRepo.java similarity index 74% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/repo/TestResultRepo.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/tc/repo/TestResultRepo.java index 6af93c7..570f55a 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/repo/TestResultRepo.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/repo/TestResultRepo.java @@ -1,7 +1,7 @@ -package io.fluentqa.qtm.tc.repo; +package io.fluent.qtm.tc.repo; -import io.fluentqa.qtm.tc.model.TestResult; +import io.fluent.qtm.tc.model.TestResult; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/repo/TestRunRepo.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/repo/TestRunRepo.java similarity index 72% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/repo/TestRunRepo.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/tc/repo/TestRunRepo.java index 19d7da7..f84b76c 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/repo/TestRunRepo.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/repo/TestRunRepo.java @@ -1,7 +1,7 @@ -package io.fluentqa.qtm.tc.repo; +package io.fluent.qtm.tc.repo; -import io.fluentqa.qtm.tc.model.TestRun; +import io.fluent.qtm.tc.model.TestRun; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/service/TestCaseService.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/service/TestCaseService.java similarity index 66% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/service/TestCaseService.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/tc/service/TestCaseService.java index 45ffd97..d4cda3f 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/service/TestCaseService.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/service/TestCaseService.java @@ -1,7 +1,7 @@ -package io.fluentqa.qtm.tc.service; +package io.fluent.qtm.tc.service; -import io.fluentqa.base.product.model.ProductModuleModel; -import io.fluentqa.qtm.tc.dto.TestCaseDTO; +import io.fluent.base.product.model.ProductModuleModel; +import io.fluent.qtm.tc.dto.TestCaseDTO; import org.springframework.stereotype.Service; import java.util.List; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/service/impl/MindMappingService.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/service/impl/MindMappingService.java similarity index 82% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/service/impl/MindMappingService.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/tc/service/impl/MindMappingService.java index 04f6154..28dad0b 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/service/impl/MindMappingService.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/service/impl/MindMappingService.java @@ -1,12 +1,12 @@ -package io.fluentqa.qtm.tc.service.impl; +package io.fluent.qtm.tc.service.impl; import cn.hutool.core.bean.BeanUtil; -import io.fluentqa.mindmap.api.MindMapAccessor; -import io.fluentqa.base.product.model.ProductModuleModel; -import io.fluentqa.qtm.tc.dto.TestCaseDTO; -import io.fluentqa.qtm.tc.service.TestCaseService; +import io.fluent.base.product.model.ProductModuleModel; +import io.fluent.qtm.tc.dto.TestCaseDTO; +import io.fluent.qtm.tc.service.TestCaseService; +import io.fluent.mindmap.api.MindMapAccessor; import org.springframework.stereotype.Service; import xyz.erupt.jpa.model.MetaModel; diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/service/impl/TestCaseServiceImpl.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/service/impl/TestCaseServiceImpl.java similarity index 88% rename from fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/service/impl/TestCaseServiceImpl.java rename to fluent-apps/qam/src/main/java/io/fluent/qtm/tc/service/impl/TestCaseServiceImpl.java index bab5b68..7fdb66d 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/service/impl/TestCaseServiceImpl.java +++ b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/service/impl/TestCaseServiceImpl.java @@ -1,16 +1,16 @@ -package io.fluentqa.qtm.tc.service.impl; +package io.fluent.qtm.tc.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.lang.UUID; import cn.hutool.core.util.StrUtil; -import io.fluentqa.base.proxies.AuditDataEnhancerProxy; -import io.fluentqa.base.product.model.ProductModuleModel; -import io.fluentqa.base.product.service.ProductModuleService; -import io.fluentqa.qtm.tc.dto.TestCaseDTO; -import io.fluentqa.qtm.tc.model.TestCase; -import io.fluentqa.qtm.tc.repo.TestCaseRepo; -import io.fluentqa.qtm.tc.service.TestCaseService; +import io.fluent.base.proxies.AuditDataEnhancerProxy; +import io.fluent.base.product.model.ProductModuleModel; +import io.fluent.base.product.service.ProductModuleService; +import io.fluent.qtm.tc.dto.TestCaseDTO; +import io.fluent.qtm.tc.model.TestCase; +import io.fluent.qtm.tc.repo.TestCaseRepo; +import io.fluent.qtm.tc.service.TestCaseService; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; diff --git a/fluent-apps/qaserver/src/main/resources/application-dev.yaml b/fluent-apps/qam/src/main/resources/application-dev.yaml similarity index 88% rename from fluent-apps/qaserver/src/main/resources/application-dev.yaml rename to fluent-apps/qam/src/main/resources/application-dev.yaml index 6777e4b..522c82e 100644 --- a/fluent-apps/qaserver/src/main/resources/application-dev.yaml +++ b/fluent-apps/qam/src/main/resources/application-dev.yaml @@ -21,9 +21,20 @@ erupt: # 是否记录操作日志,默认true,该功能开启后可在【系统管理 → 操作日志】中查看操作日志 security.recordOperateLog: false +#spring: +# datasource: +# url: jdbc:postgresql://db.supabase.orb.local:5432/postgres?currentSchema=workspace +# username: postgres +# password: postgres +# jpa: +# show-sql: true +# generate-ddl: true +# database-platform: org.hibernate.dialect.PostgreSQLDialect +# database: postgresql + spring: datasource: - url: jdbc:postgresql://db.supabase.orb.local:5432/postgres?currentSchema=workspace + url: jdbc:postgresql://db.supabase.orb.local:5432/workspace username: postgres password: postgres jpa: @@ -31,7 +42,6 @@ spring: generate-ddl: true database-platform: org.hibernate.dialect.PostgreSQLDialect database: postgresql - # mail: # username: xxxx@qq.com # password: xxxxxxx diff --git a/fluent-apps/feeds/src/main/resources/application.yaml b/fluent-apps/qam/src/main/resources/application.yaml similarity index 100% rename from fluent-apps/feeds/src/main/resources/application.yaml rename to fluent-apps/qam/src/main/resources/application.yaml diff --git a/fluent-apps/qaserver/src/main/resources/public/app.css b/fluent-apps/qam/src/main/resources/public/app.css similarity index 100% rename from fluent-apps/qaserver/src/main/resources/public/app.css rename to fluent-apps/qam/src/main/resources/public/app.css diff --git a/fluent-apps/qaserver/src/main/resources/public/app.js b/fluent-apps/qam/src/main/resources/public/app.js similarity index 100% rename from fluent-apps/qaserver/src/main/resources/public/app.js rename to fluent-apps/qam/src/main/resources/public/app.js diff --git a/fluent-apps/qaserver/src/main/resources/public/home.html b/fluent-apps/qam/src/main/resources/public/home.html similarity index 100% rename from fluent-apps/qaserver/src/main/resources/public/home.html rename to fluent-apps/qam/src/main/resources/public/home.html diff --git a/fluent-apps/qaserver/src/main/resources/tpl/operation.tpl b/fluent-apps/qam/src/main/resources/tpl/operation.tpl similarity index 100% rename from fluent-apps/qaserver/src/main/resources/tpl/operation.tpl rename to fluent-apps/qam/src/main/resources/tpl/operation.tpl diff --git a/fluent-apps/qaserver/.gitignore b/fluent-apps/qaserver/.gitignore deleted file mode 100644 index 5ff6309..0000000 --- a/fluent-apps/qaserver/.gitignore +++ /dev/null @@ -1,38 +0,0 @@ -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ - -### IntelliJ IDEA ### -.idea/modules.xml -.idea/jarRepositories.xml -.idea/compiler.xml -.idea/libraries/ -*.iws -*.iml -*.ipr - -### Eclipse ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ - -### Mac OS ### -.DS_Store \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/package-info.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/package-info.java deleted file mode 100644 index 3130485..0000000 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package io.fluentqa.base; \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/package-info.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/package-info.java deleted file mode 100644 index 98160a8..0000000 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/product/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package io.fluentqa.base.product; \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/package-info.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/package-info.java deleted file mode 100644 index 0998acf..0000000 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/base/upload/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package io.fluentqa.base.upload; \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/package-info.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/package-info.java deleted file mode 100644 index 60e98c6..0000000 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/api/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package io.fluentqa.qtm.api; \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/package-info.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/package-info.java deleted file mode 100644 index 65ca3a8..0000000 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/pm/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package io.fluentqa.qtm.tc.pm; \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/package-info.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/package-info.java deleted file mode 100644 index 263ec49..0000000 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/qtm/tc/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package io.fluentqa.qtm.tc; \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/resources/application.yaml b/fluent-apps/qaserver/src/main/resources/application.yaml deleted file mode 100644 index caf4dfc..0000000 --- a/fluent-apps/qaserver/src/main/resources/application.yaml +++ /dev/null @@ -1,3 +0,0 @@ -spring: - profiles: - active: dev \ No newline at end of file diff --git a/fluent-erupts/fluent-erupts-base/src/main/java/io/fluentqa/base/handlers/SqlTagFetchHandler.java b/fluent-erupts/fluent-erupts-base/src/main/java/io/fluent/base/handlers/SqlTagFetchHandler.java similarity index 97% rename from fluent-erupts/fluent-erupts-base/src/main/java/io/fluentqa/base/handlers/SqlTagFetchHandler.java rename to fluent-erupts/fluent-erupts-base/src/main/java/io/fluent/base/handlers/SqlTagFetchHandler.java index 044f686..49c3f00 100644 --- a/fluent-erupts/fluent-erupts-base/src/main/java/io/fluentqa/base/handlers/SqlTagFetchHandler.java +++ b/fluent-erupts/fluent-erupts-base/src/main/java/io/fluent/base/handlers/SqlTagFetchHandler.java @@ -1,4 +1,4 @@ -package io.fluentqa.base.handlers; +package io.fluent.base.handlers; import java.util.List; import java.util.function.Supplier; diff --git a/fluent-erupts/fluent-erupts-base/src/main/java/io/fluentqa/base/model/ModelWithValidFlag.java b/fluent-erupts/fluent-erupts-base/src/main/java/io/fluent/base/model/ModelWithValidFlag.java similarity index 96% rename from fluent-erupts/fluent-erupts-base/src/main/java/io/fluentqa/base/model/ModelWithValidFlag.java rename to fluent-erupts/fluent-erupts-base/src/main/java/io/fluent/base/model/ModelWithValidFlag.java index 4f6e8b7..52d898e 100644 --- a/fluent-erupts/fluent-erupts-base/src/main/java/io/fluentqa/base/model/ModelWithValidFlag.java +++ b/fluent-erupts/fluent-erupts-base/src/main/java/io/fluent/base/model/ModelWithValidFlag.java @@ -1,4 +1,4 @@ -package io.fluentqa.base.model; +package io.fluent.base.model; import javax.persistence.MappedSuperclass; import xyz.erupt.annotation.EruptField; diff --git a/fluent-erupts/fluent-erupts-base/src/main/java/io/fluentqa/base/model/ModelWithValidFlagVo.java b/fluent-erupts/fluent-erupts-base/src/main/java/io/fluent/base/model/ModelWithValidFlagVo.java similarity index 97% rename from fluent-erupts/fluent-erupts-base/src/main/java/io/fluentqa/base/model/ModelWithValidFlagVo.java rename to fluent-erupts/fluent-erupts-base/src/main/java/io/fluent/base/model/ModelWithValidFlagVo.java index e93ded9..dcddd18 100644 --- a/fluent-erupts/fluent-erupts-base/src/main/java/io/fluentqa/base/model/ModelWithValidFlagVo.java +++ b/fluent-erupts/fluent-erupts-base/src/main/java/io/fluent/base/model/ModelWithValidFlagVo.java @@ -1,4 +1,4 @@ -package io.fluentqa.base.model; +package io.fluent.base.model; import javax.persistence.MappedSuperclass; import org.hibernate.annotations.DynamicInsert; diff --git a/fluent-erupts/fluent-erupts-base/src/main/java/io/fluentqa/base/model/NamedModelVO.java b/fluent-erupts/fluent-erupts-base/src/main/java/io/fluent/base/model/NamedModelVO.java similarity index 97% rename from fluent-erupts/fluent-erupts-base/src/main/java/io/fluentqa/base/model/NamedModelVO.java rename to fluent-erupts/fluent-erupts-base/src/main/java/io/fluent/base/model/NamedModelVO.java index 89e28ec..f92a302 100644 --- a/fluent-erupts/fluent-erupts-base/src/main/java/io/fluentqa/base/model/NamedModelVO.java +++ b/fluent-erupts/fluent-erupts-base/src/main/java/io/fluent/base/model/NamedModelVO.java @@ -1,4 +1,4 @@ -package io.fluentqa.base.model; +package io.fluent.base.model; import javax.persistence.MappedSuperclass; import org.hibernate.annotations.DynamicInsert; diff --git a/fluent-erupts/fluent-erupts-base/src/main/java/io/fluentqa/base/model/NamedTimeStatusModel.java b/fluent-erupts/fluent-erupts-base/src/main/java/io/fluent/base/model/NamedTimeStatusModel.java similarity index 98% rename from fluent-erupts/fluent-erupts-base/src/main/java/io/fluentqa/base/model/NamedTimeStatusModel.java rename to fluent-erupts/fluent-erupts-base/src/main/java/io/fluent/base/model/NamedTimeStatusModel.java index d29d492..fc6d038 100644 --- a/fluent-erupts/fluent-erupts-base/src/main/java/io/fluentqa/base/model/NamedTimeStatusModel.java +++ b/fluent-erupts/fluent-erupts-base/src/main/java/io/fluent/base/model/NamedTimeStatusModel.java @@ -1,4 +1,4 @@ -package io.fluentqa.base.model; +package io.fluent.base.model; import java.time.LocalDate; import javax.persistence.MappedSuperclass; diff --git a/fluent-erupts/fluent-erupts-base/src/main/java/io/fluent/base/package-info.java b/fluent-erupts/fluent-erupts-base/src/main/java/io/fluent/base/package-info.java new file mode 100644 index 0000000..f741592 --- /dev/null +++ b/fluent-erupts/fluent-erupts-base/src/main/java/io/fluent/base/package-info.java @@ -0,0 +1 @@ +package io.fluent.base; diff --git a/fluent-erupts/fluent-erupts-base/src/main/java/io/fluentqa/base/proxies/AuditDataEnhancerProxy.java b/fluent-erupts/fluent-erupts-base/src/main/java/io/fluent/base/proxies/AuditDataEnhancerProxy.java similarity index 94% rename from fluent-erupts/fluent-erupts-base/src/main/java/io/fluentqa/base/proxies/AuditDataEnhancerProxy.java rename to fluent-erupts/fluent-erupts-base/src/main/java/io/fluent/base/proxies/AuditDataEnhancerProxy.java index 2ede92d..31ff66d 100644 --- a/fluent-erupts/fluent-erupts-base/src/main/java/io/fluentqa/base/proxies/AuditDataEnhancerProxy.java +++ b/fluent-erupts/fluent-erupts-base/src/main/java/io/fluent/base/proxies/AuditDataEnhancerProxy.java @@ -1,4 +1,4 @@ -package io.fluentqa.base.proxies; +package io.fluent.base.proxies; import java.time.LocalDateTime; import org.springframework.stereotype.Component; diff --git a/fluent-erupts/fluent-erupts-base/src/main/java/io/fluentqa/base/package-info.java b/fluent-erupts/fluent-erupts-base/src/main/java/io/fluentqa/base/package-info.java deleted file mode 100644 index 3bce169..0000000 --- a/fluent-erupts/fluent-erupts-base/src/main/java/io/fluentqa/base/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package io.fluentqa.base; diff --git a/fluent-erupts/fluent-generator/src/main/java/io/fluentqa/generator/model/DataSourceModel.java b/fluent-erupts/fluent-generator/src/main/java/io/fluentqa/generator/model/DataSourceModel.java index 463c0b6..a30b72a 100644 --- a/fluent-erupts/fluent-generator/src/main/java/io/fluentqa/generator/model/DataSourceModel.java +++ b/fluent-erupts/fluent-generator/src/main/java/io/fluentqa/generator/model/DataSourceModel.java @@ -1,7 +1,7 @@ package io.fluentqa.generator.model; -import io.fluentqa.base.handlers.SqlTagFetchHandler; -import io.fluentqa.base.model.ModelWithValidFlagVo; +import io.fluent.base.handlers.SqlTagFetchHandler; +import io.fluent.base.model.ModelWithValidFlagVo; import io.fluentqa.generator.handlers.DataSourceTableSyncHandler; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; diff --git a/fluent-erupts/fluent-generator/src/main/java/io/fluentqa/generator/model/DataSourceTableColumModel.java b/fluent-erupts/fluent-generator/src/main/java/io/fluentqa/generator/model/DataSourceTableColumModel.java index af89933..be40c77 100644 --- a/fluent-erupts/fluent-generator/src/main/java/io/fluentqa/generator/model/DataSourceTableColumModel.java +++ b/fluent-erupts/fluent-generator/src/main/java/io/fluentqa/generator/model/DataSourceTableColumModel.java @@ -1,7 +1,7 @@ package io.fluentqa.generator.model; -import io.fluentqa.base.handlers.SqlTagFetchHandler; -import io.fluentqa.base.model.ModelWithValidFlag; +import io.fluent.base.handlers.SqlTagFetchHandler; +import io.fluent.base.model.ModelWithValidFlag; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; import org.hibernate.annotations.Where; diff --git a/fluent-erupts/fluent-generator/src/main/java/io/fluentqa/generator/model/SqlConfigEntity.java b/fluent-erupts/fluent-generator/src/main/java/io/fluentqa/generator/model/SqlConfigEntity.java index 39a0717..3f5d4c4 100644 --- a/fluent-erupts/fluent-generator/src/main/java/io/fluentqa/generator/model/SqlConfigEntity.java +++ b/fluent-erupts/fluent-generator/src/main/java/io/fluentqa/generator/model/SqlConfigEntity.java @@ -1,6 +1,6 @@ package io.fluentqa.generator.model; -import io.fluentqa.base.model.NamedModelVO; +import io.fluent.base.model.NamedModelVO; import lombok.Data; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; diff --git a/fluent-wrappers/fluent-excel/src/main/java/io/fluentqa/excel/ExcelReadWriter.java b/fluent-wrappers/fluent-excel/src/main/java/io/fluent/excel/ExcelReadWriter.java similarity index 96% rename from fluent-wrappers/fluent-excel/src/main/java/io/fluentqa/excel/ExcelReadWriter.java rename to fluent-wrappers/fluent-excel/src/main/java/io/fluent/excel/ExcelReadWriter.java index 83765cf..40aa141 100644 --- a/fluent-wrappers/fluent-excel/src/main/java/io/fluentqa/excel/ExcelReadWriter.java +++ b/fluent-wrappers/fluent-excel/src/main/java/io/fluent/excel/ExcelReadWriter.java @@ -1,4 +1,4 @@ -package io.fluentqa.excel; +package io.fluent.excel; import com.github.crab2died.ExcelUtils; import com.github.crab2died.exceptions.Excel4JException; diff --git a/fluent-wrappers/fluent-excel/src/main/java/io/fluent/excel/package-info.java b/fluent-wrappers/fluent-excel/src/main/java/io/fluent/excel/package-info.java new file mode 100644 index 0000000..b27cea0 --- /dev/null +++ b/fluent-wrappers/fluent-excel/src/main/java/io/fluent/excel/package-info.java @@ -0,0 +1 @@ +package io.fluent.excel; diff --git a/fluent-wrappers/fluent-excel/src/main/java/io/fluentqa/excel/package-info.java b/fluent-wrappers/fluent-excel/src/main/java/io/fluentqa/excel/package-info.java deleted file mode 100644 index a033ad3..0000000 --- a/fluent-wrappers/fluent-excel/src/main/java/io/fluentqa/excel/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package io.fluentqa.excel; diff --git a/fluent-wrappers/fluent-excel/src/test/java/io/fluentqa/excel/DemoExcelModel.java b/fluent-wrappers/fluent-excel/src/test/java/io/fluent/excel/DemoExcelModel.java similarity index 92% rename from fluent-wrappers/fluent-excel/src/test/java/io/fluentqa/excel/DemoExcelModel.java rename to fluent-wrappers/fluent-excel/src/test/java/io/fluent/excel/DemoExcelModel.java index 0cbbcc5..87dbadb 100644 --- a/fluent-wrappers/fluent-excel/src/test/java/io/fluentqa/excel/DemoExcelModel.java +++ b/fluent-wrappers/fluent-excel/src/test/java/io/fluent/excel/DemoExcelModel.java @@ -1,4 +1,4 @@ -package io.fluentqa.excel; +package io.fluent.excel; import com.github.crab2died.annotation.ExcelField; import lombok.AllArgsConstructor; diff --git a/fluent-wrappers/fluent-excel/src/test/java/io/fluentqa/excel/ExcelUtilsTest.java b/fluent-wrappers/fluent-excel/src/test/java/io/fluent/excel/ExcelUtilsTest.java similarity index 96% rename from fluent-wrappers/fluent-excel/src/test/java/io/fluentqa/excel/ExcelUtilsTest.java rename to fluent-wrappers/fluent-excel/src/test/java/io/fluent/excel/ExcelUtilsTest.java index 1742770..3da3361 100644 --- a/fluent-wrappers/fluent-excel/src/test/java/io/fluentqa/excel/ExcelUtilsTest.java +++ b/fluent-wrappers/fluent-excel/src/test/java/io/fluent/excel/ExcelUtilsTest.java @@ -1,4 +1,4 @@ -package io.fluentqa.excel; +package io.fluent.excel; import java.io.File; import java.io.IOException; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/BasicAuthentication.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/BasicAuthentication.java similarity index 91% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/BasicAuthentication.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/BasicAuthentication.java index d41aa52..d8d2eeb 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/BasicAuthentication.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/BasicAuthentication.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.config; +package io.fluent.jira.config; public class BasicAuthentication { diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/CacheConfig.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/CacheConfig.java similarity index 93% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/CacheConfig.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/CacheConfig.java index 47ff8ab..c28ac13 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/CacheConfig.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/CacheConfig.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.config; +package io.fluent.jira.config; public class CacheConfig { diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/CommentMappingConfig.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/CommentMappingConfig.java similarity index 97% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/CommentMappingConfig.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/CommentMappingConfig.java index 4c77242..182e487 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/CommentMappingConfig.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/CommentMappingConfig.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.config; +package io.fluent.jira.config; public class CommentMappingConfig { diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/Context.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/Context.java similarity index 57% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/Context.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/Context.java index e5b8292..e07e14d 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/Context.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/Context.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.config; +package io.fluent.jira.config; public enum Context { SOURCE, diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/DescriptionMappingConfig.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/DescriptionMappingConfig.java similarity index 94% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/DescriptionMappingConfig.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/DescriptionMappingConfig.java index 51e30eb..247ae3e 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/DescriptionMappingConfig.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/DescriptionMappingConfig.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.config; +package io.fluent.jira.config; public class DescriptionMappingConfig { diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/JiraConnectionProperties.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/JiraConnectionProperties.java similarity index 97% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/JiraConnectionProperties.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/JiraConnectionProperties.java index ce0c4bc..aee16fb 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/JiraConnectionProperties.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/JiraConnectionProperties.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.config; +package io.fluent.jira.config; import java.io.File; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/JiraProjectSync.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/JiraProjectSync.java similarity index 99% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/JiraProjectSync.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/JiraProjectSync.java index cfaa299..d57b459 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/JiraProjectSync.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/JiraProjectSync.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.config; +package io.fluent.jira.config; import java.net.URL; import java.util.Arrays; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/JiraSyncConfig.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/JiraSyncConfig.java similarity index 97% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/JiraSyncConfig.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/JiraSyncConfig.java index bf6841c..1503031 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/JiraSyncConfig.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/JiraSyncConfig.java @@ -1,6 +1,7 @@ -package io.fluentqa.jira.config; +package io.fluent.jira.config; + +import io.fluent.jira.domain.JiraProject; -import io.fluentqa.jira.domain.JiraProject; import java.util.LinkedHashMap; import java.util.Map; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/TransitionConfig.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/TransitionConfig.java similarity index 96% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/TransitionConfig.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/TransitionConfig.java index 91c8271..ad98666 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/config/TransitionConfig.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/config/TransitionConfig.java @@ -1,7 +1,7 @@ -package io.fluentqa.jira.config; +package io.fluent.jira.config; -import io.fluentqa.jira.domain.JiraIssue; -import io.fluentqa.jira.domain.JiraIssueType; +import io.fluent.jira.domain.JiraIssue; +import io.fluent.jira.domain.JiraIssueType; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraChangeLog.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraChangeLog.java similarity index 97% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraChangeLog.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraChangeLog.java index f6dd3c7..706a9b1 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraChangeLog.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraChangeLog.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import com.fasterxml.jackson.annotation.JsonIgnore; import java.io.Serializable; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraComment.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraComment.java similarity index 86% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraComment.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraComment.java index 17c5e0b..4757e94 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraComment.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraComment.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import com.fasterxml.jackson.annotation.JsonFormat; import java.time.ZonedDateTime; @@ -11,10 +11,10 @@ public class JiraComment extends JiraIdResource { private String body; - @JsonFormat(pattern = JiraResource.JIRA_DATE_FORMAT) + @JsonFormat(pattern = JIRA_DATE_FORMAT) private ZonedDateTime created; - @JsonFormat(pattern = JiraResource.JIRA_DATE_FORMAT) + @JsonFormat(pattern = JIRA_DATE_FORMAT) private ZonedDateTime updated; public JiraComment() {} diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraComments.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraComments.java similarity index 96% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraComments.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraComments.java index 1f9cb07..c047fcd 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraComments.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraComments.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import java.io.Serializable; import java.util.ArrayList; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraComponent.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraComponent.java similarity index 86% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraComponent.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraComponent.java index 71a9174..89dcad2 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraComponent.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraComponent.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; public class JiraComponent extends JiraNamedResource { diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraComponentsList.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraComponentsList.java similarity index 82% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraComponentsList.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraComponentsList.java index 53549ac..e3d84be 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraComponentsList.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraComponentsList.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import java.util.ArrayList; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraField.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraField.java similarity index 94% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraField.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraField.java index f0d772c..a7016ac 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraField.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraField.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; public class JiraField extends JiraNamedResource { diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraFieldList.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraFieldList.java similarity index 85% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraFieldList.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraFieldList.java index 5ec98a4..12826c0 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraFieldList.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraFieldList.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import java.io.Serializable; import java.util.ArrayList; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraFieldSchema.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraFieldSchema.java similarity index 95% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraFieldSchema.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraFieldSchema.java index 4abe18e..ac6a3da 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraFieldSchema.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraFieldSchema.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import java.io.Serializable; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraFieldsBean.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraFieldsBean.java similarity index 94% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraFieldsBean.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraFieldsBean.java index 171dd90..c5a7137 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraFieldsBean.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraFieldsBean.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import java.util.Set; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraFieldsUpdate.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraFieldsUpdate.java similarity index 98% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraFieldsUpdate.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraFieldsUpdate.java index 6326d23..c1c73f3 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraFieldsUpdate.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraFieldsUpdate.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraFilterResult.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraFilterResult.java similarity index 89% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraFilterResult.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraFilterResult.java index 3272532..6a415c3 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraFilterResult.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraFilterResult.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import java.io.Serializable; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIdResource.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIdResource.java similarity index 91% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIdResource.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIdResource.java index a6a33e8..4ef4f82 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIdResource.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIdResource.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; public abstract class JiraIdResource extends JiraResource { diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIssue.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIssue.java similarity index 97% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIssue.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIssue.java index 2a95a3e..408989d 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIssue.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIssue.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import com.fasterxml.jackson.annotation.JsonIgnore; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIssueFields.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIssueFields.java similarity index 99% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIssueFields.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIssueFields.java index 4df5048..ffcd374 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIssueFields.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIssueFields.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIssueHistoryEntry.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIssueHistoryEntry.java similarity index 97% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIssueHistoryEntry.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIssueHistoryEntry.java index 7196b51..74ee3f8 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIssueHistoryEntry.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIssueHistoryEntry.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIssueHistoryItem.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIssueHistoryItem.java similarity index 98% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIssueHistoryItem.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIssueHistoryItem.java index 9d6c32b..a91d1d6 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIssueHistoryItem.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIssueHistoryItem.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import com.fasterxml.jackson.annotation.JsonIgnore; import java.io.Serializable; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIssueStatus.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIssueStatus.java similarity index 86% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIssueStatus.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIssueStatus.java index cbe49d7..b3c058c 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIssueStatus.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIssueStatus.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; public class JiraIssueStatus extends JiraNamedResource { diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIssueType.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIssueType.java similarity index 86% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIssueType.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIssueType.java index a4d4c9c..b116c0e 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIssueType.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIssueType.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; public class JiraIssueType extends JiraNamedResource { diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIssueUpdate.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIssueUpdate.java similarity index 97% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIssueUpdate.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIssueUpdate.java index ba10c05..1846d78 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraIssueUpdate.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraIssueUpdate.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import com.fasterxml.jackson.annotation.JsonIgnore; import java.io.Serializable; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraLinkIcon.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraLinkIcon.java similarity index 92% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraLinkIcon.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraLinkIcon.java index 2c2f9f0..5c48b13 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraLinkIcon.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraLinkIcon.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import java.io.Serializable; import java.net.URL; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraLoginRequest.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraLoginRequest.java similarity index 95% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraLoginRequest.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraLoginRequest.java index ce330c9..6a101f8 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraLoginRequest.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraLoginRequest.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import java.io.Serializable; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraLoginResponse.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraLoginResponse.java similarity index 90% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraLoginResponse.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraLoginResponse.java index 1e1676e..333ded5 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraLoginResponse.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraLoginResponse.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import java.io.Serializable; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraNamedBean.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraNamedBean.java similarity index 84% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraNamedBean.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraNamedBean.java index 679ccec..b92e45f 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraNamedBean.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraNamedBean.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; public interface JiraNamedBean { diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraNamedResource.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraNamedResource.java similarity index 93% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraNamedResource.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraNamedResource.java index 2ab329e..b8a0ef7 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraNamedResource.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraNamedResource.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import java.util.Objects; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraPriority.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraPriority.java similarity index 86% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraPriority.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraPriority.java index db412cb..93a1027 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraPriority.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraPriority.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; public class JiraPriority extends JiraNamedResource { diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraPriorityList.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraPriorityList.java similarity index 85% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraPriorityList.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraPriorityList.java index b652794..84bb255 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraPriorityList.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraPriorityList.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import java.io.Serializable; import java.util.ArrayList; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraProject.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraProject.java similarity index 96% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraProject.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraProject.java index 44ef64f..1f57ddd 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraProject.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraProject.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import java.util.List; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraProjectsList.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraProjectsList.java similarity index 81% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraProjectsList.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraProjectsList.java index dba3939..76f6aa1 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraProjectsList.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraProjectsList.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import java.util.ArrayList; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraRemoteLink.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraRemoteLink.java similarity index 95% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraRemoteLink.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraRemoteLink.java index b6f674f..bc9c9a5 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraRemoteLink.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraRemoteLink.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import java.net.MalformedURLException; import java.net.URL; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraRemoteLinkObject.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraRemoteLinkObject.java similarity index 95% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraRemoteLinkObject.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraRemoteLinkObject.java index c0bd5cc..eab71d9 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraRemoteLinkObject.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraRemoteLinkObject.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import java.io.Serializable; import java.net.URL; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraRemoteLinks.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraRemoteLinks.java similarity index 85% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraRemoteLinks.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraRemoteLinks.java index 8cce824..d809f9b 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraRemoteLinks.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraRemoteLinks.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import java.io.Serializable; import java.util.ArrayList; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraResolution.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraResolution.java similarity index 86% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraResolution.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraResolution.java index 5868dd7..27b7823 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraResolution.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraResolution.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; public class JiraResolution extends JiraNamedResource { diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraResolutionList.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraResolutionList.java similarity index 86% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraResolutionList.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraResolutionList.java index 3d7d404..b4997d8 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraResolutionList.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraResolutionList.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import java.io.Serializable; import java.util.ArrayList; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraResource.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraResource.java similarity index 92% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraResource.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraResource.java index 3361877..7bf1e91 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraResource.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraResource.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import java.io.Serializable; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraSearchResult.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraSearchResult.java similarity index 96% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraSearchResult.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraSearchResult.java index 8eb7277..900d1f9 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraSearchResult.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraSearchResult.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import java.io.Serializable; import java.util.List; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraServerInfo.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraServerInfo.java similarity index 95% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraServerInfo.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraServerInfo.java index 3e63647..14f4d52 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraServerInfo.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraServerInfo.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; public class JiraServerInfo extends JiraResource { diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraSession.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraSession.java similarity index 93% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraSession.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraSession.java index 2e11c79..a368178 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraSession.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraSession.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import java.io.Serializable; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraTransition.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraTransition.java similarity index 94% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraTransition.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraTransition.java index 32da31a..85c5498 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraTransition.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraTransition.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraTransitions.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraTransitions.java similarity index 94% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraTransitions.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraTransitions.java index 23c970f..56ed8fe 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraTransitions.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraTransitions.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import java.io.Serializable; import java.util.List; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraUser.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraUser.java similarity index 94% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraUser.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraUser.java index 96154d5..2e408a6 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraUser.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraUser.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; public class JiraUser extends JiraNamedResource { diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraVersion.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraVersion.java similarity index 86% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraVersion.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraVersion.java index 4133ad0..a3d9f7f 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraVersion.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraVersion.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; public class JiraVersion extends JiraNamedResource { diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraVersionsList.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraVersionsList.java similarity index 81% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraVersionsList.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraVersionsList.java index 075c654..fff280b 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/JiraVersionsList.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/JiraVersionsList.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import java.util.ArrayList; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/WellKnownCustomFieldType.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/WellKnownCustomFieldType.java similarity index 96% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/WellKnownCustomFieldType.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/WellKnownCustomFieldType.java index 63c2718..c0c3fb6 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/WellKnownCustomFieldType.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/WellKnownCustomFieldType.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; public enum WellKnownCustomFieldType { LABELS("com.atlassian.jira.plugin.system.customfieldtypes:labels"), diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/WellKnownJiraField.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/WellKnownJiraField.java similarity index 96% rename from fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/WellKnownJiraField.java rename to fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/WellKnownJiraField.java index c45e7e1..e0e1d97 100644 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/domain/WellKnownJiraField.java +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/domain/WellKnownJiraField.java @@ -1,4 +1,4 @@ -package io.fluentqa.jira.domain; +package io.fluent.jira.domain; import de.cronn.reflection.util.PropertyGetter; import de.cronn.reflection.util.PropertyUtils; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/package-info.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/package-info.java new file mode 100644 index 0000000..c642f78 --- /dev/null +++ b/fluent-wrappers/fluent-jira/src/main/java/io/fluent/jira/package-info.java @@ -0,0 +1 @@ +package io.fluent.jira; diff --git a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/package-info.java b/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/package-info.java deleted file mode 100644 index 4c96b8f..0000000 --- a/fluent-wrappers/fluent-jira/src/main/java/io/fluentqa/jira/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package io.fluentqa.jira; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/api/MindMapAccessor.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/api/MindMapAccessor.java similarity index 87% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/api/MindMapAccessor.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/api/MindMapAccessor.java index aefed58..81ec664 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/api/MindMapAccessor.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/api/MindMapAccessor.java @@ -1,7 +1,8 @@ -package io.fluentqa.mindmap.api; +package io.fluent.mindmap.api; + +import io.fluent.mindmap.freemind.FreeMindTransformer; +import io.fluent.mindmap.xmind.XmindTransformer; -import io.fluentqa.mindmap.freemind.FreeMindTransformer; -import io.fluentqa.mindmap.xmind.XmindTransformer; import java.util.List; public class MindMapAccessor { diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/api/MindMapConvertConfig.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/api/MindMapConvertConfig.java similarity index 92% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/api/MindMapConvertConfig.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/api/MindMapConvertConfig.java index 05e5b64..7cf09ab 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/api/MindMapConvertConfig.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/api/MindMapConvertConfig.java @@ -1,4 +1,4 @@ -package io.fluentqa.mindmap.api; +package io.fluent.mindmap.api; import java.util.ArrayList; import java.util.List; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/api/MindMapPath.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/api/MindMapPath.java similarity index 94% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/api/MindMapPath.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/api/MindMapPath.java index 861f6dc..b3cd0bb 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/api/MindMapPath.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/api/MindMapPath.java @@ -1,4 +1,4 @@ -package io.fluentqa.mindmap.api; +package io.fluent.mindmap.api; import java.util.LinkedList; import lombok.Data; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/api/MindMapPathRecord.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/api/MindMapPathRecord.java similarity index 98% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/api/MindMapPathRecord.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/api/MindMapPathRecord.java index c0e7d12..727a88c 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/api/MindMapPathRecord.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/api/MindMapPathRecord.java @@ -1,4 +1,4 @@ -package io.fluentqa.mindmap.api; +package io.fluent.mindmap.api; import cn.hutool.core.bean.BeanUtil; import io.fluent.builtin.meta.ReflectionUtils; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/api/MindMapTransformer.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/api/MindMapTransformer.java similarity index 90% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/api/MindMapTransformer.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/api/MindMapTransformer.java index 16477f6..13fa297 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/api/MindMapTransformer.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/api/MindMapTransformer.java @@ -1,4 +1,4 @@ -package io.fluentqa.mindmap.api; +package io.fluent.mindmap.api; import java.util.List; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/api/MindmapTypeEnum.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/api/MindmapTypeEnum.java similarity index 94% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/api/MindmapTypeEnum.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/api/MindmapTypeEnum.java index 0e8dc1b..11818f1 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/api/MindmapTypeEnum.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/api/MindmapTypeEnum.java @@ -1,4 +1,4 @@ -package io.fluentqa.mindmap.api; +package io.fluent.mindmap.api; import io.fluent.builtin.JavaProjectFileUtils; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/api/NodeLevel.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/api/NodeLevel.java similarity index 84% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/api/NodeLevel.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/api/NodeLevel.java index 267af71..e8bf9ce 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/api/NodeLevel.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/api/NodeLevel.java @@ -1,4 +1,4 @@ -package io.fluentqa.mindmap.api; +package io.fluent.mindmap.api; import java.lang.annotation.*; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/exception/MindMapException.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/exception/MindMapException.java similarity index 92% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/exception/MindMapException.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/exception/MindMapException.java index 6ddf5ff..4b026e3 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/exception/MindMapException.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/exception/MindMapException.java @@ -1,4 +1,4 @@ -package io.fluentqa.mindmap.exception; +package io.fluent.mindmap.exception; public class MindMapException extends RuntimeException { public MindMapException() {} diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/FreeMindNode.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/FreeMindNode.java similarity index 76% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/FreeMindNode.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/FreeMindNode.java index a2cb3e2..31c0a57 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/FreeMindNode.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/FreeMindNode.java @@ -1,7 +1,7 @@ -package io.fluentqa.mindmap.freemind; +package io.fluent.mindmap.freemind; -import io.fluentqa.mindmap.api.MindMapPath; -import io.fluentqa.mindmap.freemind.model.Node; +import io.fluent.mindmap.api.MindMapPath; +import io.fluent.mindmap.freemind.model.Node; public class FreeMindNode extends MindMapPath { diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/FreeMindTransformer.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/FreeMindTransformer.java similarity index 88% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/FreeMindTransformer.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/FreeMindTransformer.java index c994778..0f6746c 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/FreeMindTransformer.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/FreeMindTransformer.java @@ -1,12 +1,12 @@ -package io.fluentqa.mindmap.freemind; +package io.fluent.mindmap.freemind; import io.fluent.builtin.XmlUtils; -import io.fluentqa.mindmap.api.MindMapConvertConfig; -import io.fluentqa.mindmap.api.MindMapPath; -import io.fluentqa.mindmap.api.MindMapPathRecord; -import io.fluentqa.mindmap.api.MindMapTransformer; -import io.fluentqa.mindmap.freemind.model.Map; -import io.fluentqa.mindmap.freemind.model.Node; +import io.fluent.mindmap.api.MindMapConvertConfig; +import io.fluent.mindmap.api.MindMapPath; +import io.fluent.mindmap.api.MindMapPathRecord; +import io.fluent.mindmap.api.MindMapTransformer; +import io.fluent.mindmap.freemind.model.Map; +import io.fluent.mindmap.freemind.model.Node; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Arrowlink.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Arrowlink.java similarity index 99% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Arrowlink.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Arrowlink.java index 4e29899..472c39e 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Arrowlink.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Arrowlink.java @@ -5,7 +5,7 @@ // 生成时间: 2022.09.23 时间 08:46:06 PM CST // -package io.fluentqa.mindmap.freemind.model; +package io.fluent.mindmap.freemind.model; import jakarta.xml.bind.annotation.*; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Attribute.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Attribute.java similarity index 97% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Attribute.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Attribute.java index beabd13..4d8c3e3 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Attribute.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Attribute.java @@ -5,7 +5,7 @@ // 生成时间: 2022.09.23 时间 08:46:06 PM CST // -package io.fluentqa.mindmap.freemind.model; +package io.fluent.mindmap.freemind.model; import jakarta.xml.bind.annotation.*; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/AttributeLayout.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/AttributeLayout.java similarity index 97% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/AttributeLayout.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/AttributeLayout.java index 4b8836e..8f96380 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/AttributeLayout.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/AttributeLayout.java @@ -5,7 +5,7 @@ // 生成时间: 2022.09.23 时间 08:46:06 PM CST // -package io.fluentqa.mindmap.freemind.model; +package io.fluent.mindmap.freemind.model; import jakarta.xml.bind.annotation.*; import java.math.BigInteger; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Cloud.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Cloud.java similarity index 96% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Cloud.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Cloud.java index f2cfc2a..0dc2343 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Cloud.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Cloud.java @@ -5,7 +5,7 @@ // 生成时间: 2022.09.23 时间 08:46:06 PM CST // -package io.fluentqa.mindmap.freemind.model; +package io.fluent.mindmap.freemind.model; import jakarta.xml.bind.annotation.*; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Edge.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Edge.java similarity index 98% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Edge.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Edge.java index 6719d30..10db299 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Edge.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Edge.java @@ -5,7 +5,7 @@ // 生成时间: 2022.09.23 时间 08:46:06 PM CST // -package io.fluentqa.mindmap.freemind.model; +package io.fluent.mindmap.freemind.model; import jakarta.xml.bind.annotation.*; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Font.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Font.java similarity index 98% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Font.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Font.java index 34bc2c1..b4dba65 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Font.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Font.java @@ -5,7 +5,7 @@ // 生成时间: 2022.09.23 时间 08:46:06 PM CST // -package io.fluentqa.mindmap.freemind.model; +package io.fluent.mindmap.freemind.model; import jakarta.xml.bind.annotation.*; import java.math.BigInteger; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Hook.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Hook.java similarity index 98% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Hook.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Hook.java index f6930c0..7f33895 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Hook.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Hook.java @@ -5,7 +5,7 @@ // 生成时间: 2022.09.23 时间 08:46:06 PM CST // -package io.fluentqa.mindmap.freemind.model; +package io.fluent.mindmap.freemind.model; import jakarta.xml.bind.annotation.*; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Html.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Html.java similarity index 97% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Html.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Html.java index 5b9a44f..f7fec81 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Html.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Html.java @@ -5,7 +5,7 @@ // 生成时间: 2022.09.23 时间 08:46:06 PM CST // -package io.fluentqa.mindmap.freemind.model; +package io.fluent.mindmap.freemind.model; import jakarta.xml.bind.annotation.*; import java.util.ArrayList; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Icon.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Icon.java similarity index 96% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Icon.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Icon.java index f50b327..6751303 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Icon.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Icon.java @@ -5,7 +5,7 @@ // 生成时间: 2022.09.23 时间 08:46:06 PM CST // -package io.fluentqa.mindmap.freemind.model; +package io.fluent.mindmap.freemind.model; import jakarta.xml.bind.annotation.*; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Linktarget.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Linktarget.java similarity index 99% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Linktarget.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Linktarget.java index 17b25c0..d9b48c0 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Linktarget.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Linktarget.java @@ -5,7 +5,7 @@ // 生成时间: 2022.09.23 时间 08:46:06 PM CST // -package io.fluentqa.mindmap.freemind.model; +package io.fluent.mindmap.freemind.model; import jakarta.xml.bind.annotation.*; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Map.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Map.java similarity index 97% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Map.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Map.java index 62af792..5448d3b 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Map.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Map.java @@ -5,7 +5,7 @@ // 生成时间: 2022.09.23 时间 08:46:06 PM CST // -package io.fluentqa.mindmap.freemind.model; +package io.fluent.mindmap.freemind.model; import jakarta.xml.bind.annotation.*; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Node.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Node.java similarity index 99% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Node.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Node.java index 4f97a80..c119228 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Node.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Node.java @@ -5,7 +5,7 @@ // 生成时间: 2022.09.23 时间 08:46:06 PM CST // -package io.fluentqa.mindmap.freemind.model; +package io.fluent.mindmap.freemind.model; import jakarta.xml.bind.annotation.*; import jakarta.xml.bind.annotation.adapters.CollapsedStringAdapter; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/ObjectFactory.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/ObjectFactory.java similarity index 98% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/ObjectFactory.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/ObjectFactory.java index efc3dd1..9450b5f 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/ObjectFactory.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/ObjectFactory.java @@ -5,7 +5,7 @@ // 生成时间: 2022.09.23 时间 08:46:06 PM CST // -package io.fluentqa.mindmap.freemind.model; +package io.fluent.mindmap.freemind.model; import jakarta.xml.bind.annotation.XmlRegistry; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Parameters.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Parameters.java similarity index 99% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Parameters.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Parameters.java index a639fbb..960c7fe 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Parameters.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Parameters.java @@ -5,7 +5,7 @@ // 生成时间: 2022.09.23 时间 08:46:06 PM CST // -package io.fluentqa.mindmap.freemind.model; +package io.fluent.mindmap.freemind.model; import jakarta.xml.bind.annotation.*; import java.math.BigInteger; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Richcontent.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Richcontent.java similarity index 97% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Richcontent.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Richcontent.java index 0e91af4..bd2dec3 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Richcontent.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Richcontent.java @@ -5,7 +5,7 @@ // 生成时间: 2022.09.23 时间 08:46:06 PM CST // -package io.fluentqa.mindmap.freemind.model; +package io.fluent.mindmap.freemind.model; import jakarta.xml.bind.annotation.*; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Text.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Text.java similarity index 95% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Text.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Text.java index d6e535f..7f0e79a 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/freemind/model/Text.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/freemind/model/Text.java @@ -5,7 +5,7 @@ // 生成时间: 2022.09.23 时间 08:46:06 PM CST // -package io.fluentqa.mindmap.freemind.model; +package io.fluent.mindmap.freemind.model; import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessorType; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/package-info.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/package-info.java new file mode 100644 index 0000000..c0f43e5 --- /dev/null +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/package-info.java @@ -0,0 +1 @@ +package io.fluent.mindmap; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/XMindNode.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/XMindNode.java similarity index 51% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/XMindNode.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/XMindNode.java index 45b8b42..e811f79 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/XMindNode.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/XMindNode.java @@ -1,7 +1,7 @@ -package io.fluentqa.mindmap.xmind; +package io.fluent.mindmap.xmind; -import io.fluentqa.mindmap.api.MindMapPath; -import io.fluentqa.mindmap.xmind.model.Attached; +import io.fluent.mindmap.xmind.model.Attached; +import io.fluent.mindmap.api.MindMapPath; public class XMindNode extends MindMapPath implements Cloneable { public XMindNode(Attached root) { diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/XMindUtil.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/XMindUtil.java similarity index 94% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/XMindUtil.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/XMindUtil.java index b78516a..145156d 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/XMindUtil.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/XMindUtil.java @@ -1,11 +1,12 @@ -package io.fluentqa.mindmap.xmind; +package io.fluent.mindmap.xmind; import cn.hutool.core.compress.ZipReader; import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.ZipUtil; -import io.fluentqa.mindmap.exception.MindMapException; -import io.fluentqa.mindmap.xmind.model.XmindRawData; +import io.fluent.mindmap.xmind.model.XmindRawData; +import io.fluent.mindmap.exception.MindMapException; + import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/XmindTransformer.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/XmindTransformer.java similarity index 88% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/XmindTransformer.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/XmindTransformer.java index f5ef381..5555b88 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/XmindTransformer.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/XmindTransformer.java @@ -1,13 +1,14 @@ -package io.fluentqa.mindmap.xmind; +package io.fluent.mindmap.xmind; import cn.hutool.json.JSONUtil; -import io.fluentqa.mindmap.api.MindMapConvertConfig; -import io.fluentqa.mindmap.api.MindMapPath; -import io.fluentqa.mindmap.api.MindMapPathRecord; -import io.fluentqa.mindmap.api.MindMapTransformer; -import io.fluentqa.mindmap.xmind.model.Attached; -import io.fluentqa.mindmap.xmind.model.JsonRootBean; -import io.fluentqa.mindmap.xmind.model.XmindRawData; +import io.fluent.mindmap.xmind.model.Attached; +import io.fluent.mindmap.xmind.model.JsonRootBean; +import io.fluent.mindmap.xmind.model.XmindRawData; +import io.fluent.mindmap.api.MindMapConvertConfig; +import io.fluent.mindmap.api.MindMapPath; +import io.fluent.mindmap.api.MindMapPathRecord; +import io.fluent.mindmap.api.MindMapTransformer; + import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/model/Attached.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/model/Attached.java similarity index 83% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/model/Attached.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/model/Attached.java index d39006d..fdb5244 100755 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/model/Attached.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/model/Attached.java @@ -1,4 +1,4 @@ -package io.fluentqa.mindmap.xmind.model; +package io.fluent.mindmap.xmind.model; import java.util.List; import lombok.Data; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/model/Children.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/model/Children.java similarity index 73% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/model/Children.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/model/Children.java index 96a5cb5..780973a 100755 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/model/Children.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/model/Children.java @@ -1,4 +1,4 @@ -package io.fluentqa.mindmap.xmind.model; +package io.fluent.mindmap.xmind.model; import java.util.List; import lombok.Data; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/model/Comments.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/model/Comments.java similarity index 76% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/model/Comments.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/model/Comments.java index 72b7b70..875a00b 100755 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/model/Comments.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/model/Comments.java @@ -1,4 +1,4 @@ -package io.fluentqa.mindmap.xmind.model; +package io.fluent.mindmap.xmind.model; import lombok.Data; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/model/JsonRootBean.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/model/JsonRootBean.java similarity index 76% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/model/JsonRootBean.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/model/JsonRootBean.java index 20e984d..08ac87a 100755 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/model/JsonRootBean.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/model/JsonRootBean.java @@ -1,4 +1,4 @@ -package io.fluentqa.mindmap.xmind.model; +package io.fluent.mindmap.xmind.model; import lombok.Data; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/model/Notes.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/model/Notes.java similarity index 65% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/model/Notes.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/model/Notes.java index c563e3d..ab40ce6 100755 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/model/Notes.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/model/Notes.java @@ -1,4 +1,4 @@ -package io.fluentqa.mindmap.xmind.model; +package io.fluent.mindmap.xmind.model; import lombok.Data; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/model/RootTopic.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/model/RootTopic.java similarity index 83% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/model/RootTopic.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/model/RootTopic.java index 53dcbe9..5333dc5 100755 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/model/RootTopic.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/model/RootTopic.java @@ -1,4 +1,4 @@ -package io.fluentqa.mindmap.xmind.model; +package io.fluent.mindmap.xmind.model; import java.util.List; import lombok.Data; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/model/XmindRawData.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/model/XmindRawData.java similarity index 96% rename from fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/model/XmindRawData.java rename to fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/model/XmindRawData.java index 2761267..7009dc5 100644 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/xmind/model/XmindRawData.java +++ b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluent/mindmap/xmind/model/XmindRawData.java @@ -1,4 +1,4 @@ -package io.fluentqa.mindmap.xmind.model; +package io.fluent.mindmap.xmind.model; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONArray; diff --git a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/package-info.java b/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/package-info.java deleted file mode 100644 index 3ce5b18..0000000 --- a/fluent-wrappers/fluent-mindmap/src/main/java/io/fluentqa/mindmap/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package io.fluentqa.mindmap; diff --git a/fluent-wrappers/fluent-mindmap/src/test/java/io/fluentqa/mindmap/api/DemoBean.java b/fluent-wrappers/fluent-mindmap/src/test/java/io/fluent/mindmap/api/DemoBean.java similarity index 89% rename from fluent-wrappers/fluent-mindmap/src/test/java/io/fluentqa/mindmap/api/DemoBean.java rename to fluent-wrappers/fluent-mindmap/src/test/java/io/fluent/mindmap/api/DemoBean.java index 36d160b..03390f0 100644 --- a/fluent-wrappers/fluent-mindmap/src/test/java/io/fluentqa/mindmap/api/DemoBean.java +++ b/fluent-wrappers/fluent-mindmap/src/test/java/io/fluent/mindmap/api/DemoBean.java @@ -1,4 +1,4 @@ -package io.fluentqa.mindmap.api; +package io.fluent.mindmap.api; import lombok.Data; diff --git a/fluent-wrappers/fluent-mindmap/src/test/java/io/fluentqa/mindmap/api/DemoBeanConfig.java b/fluent-wrappers/fluent-mindmap/src/test/java/io/fluent/mindmap/api/DemoBeanConfig.java similarity index 94% rename from fluent-wrappers/fluent-mindmap/src/test/java/io/fluentqa/mindmap/api/DemoBeanConfig.java rename to fluent-wrappers/fluent-mindmap/src/test/java/io/fluent/mindmap/api/DemoBeanConfig.java index 0e33039..c35e198 100644 --- a/fluent-wrappers/fluent-mindmap/src/test/java/io/fluentqa/mindmap/api/DemoBeanConfig.java +++ b/fluent-wrappers/fluent-mindmap/src/test/java/io/fluent/mindmap/api/DemoBeanConfig.java @@ -1,4 +1,4 @@ -package io.fluentqa.mindmap.api; +package io.fluent.mindmap.api; public class DemoBeanConfig { diff --git a/fluent-wrappers/fluent-mindmap/src/test/java/io/fluentqa/mindmap/api/DemoBeanWOLevel.java b/fluent-wrappers/fluent-mindmap/src/test/java/io/fluent/mindmap/api/DemoBeanWOLevel.java similarity index 86% rename from fluent-wrappers/fluent-mindmap/src/test/java/io/fluentqa/mindmap/api/DemoBeanWOLevel.java rename to fluent-wrappers/fluent-mindmap/src/test/java/io/fluent/mindmap/api/DemoBeanWOLevel.java index 77b9a7d..a889473 100644 --- a/fluent-wrappers/fluent-mindmap/src/test/java/io/fluentqa/mindmap/api/DemoBeanWOLevel.java +++ b/fluent-wrappers/fluent-mindmap/src/test/java/io/fluent/mindmap/api/DemoBeanWOLevel.java @@ -1,4 +1,4 @@ -package io.fluentqa.mindmap.api; +package io.fluent.mindmap.api; import lombok.Data; diff --git a/fluent-wrappers/fluent-mindmap/src/test/java/io/fluentqa/mindmap/api/MindMapAccessorTest.java b/fluent-wrappers/fluent-mindmap/src/test/java/io/fluent/mindmap/api/MindMapAccessorTest.java similarity index 95% rename from fluent-wrappers/fluent-mindmap/src/test/java/io/fluentqa/mindmap/api/MindMapAccessorTest.java rename to fluent-wrappers/fluent-mindmap/src/test/java/io/fluent/mindmap/api/MindMapAccessorTest.java index 2cadd5b..a6b8d71 100644 --- a/fluent-wrappers/fluent-mindmap/src/test/java/io/fluentqa/mindmap/api/MindMapAccessorTest.java +++ b/fluent-wrappers/fluent-mindmap/src/test/java/io/fluent/mindmap/api/MindMapAccessorTest.java @@ -1,4 +1,4 @@ -package io.fluentqa.mindmap.api; +package io.fluent.mindmap.api; import java.util.List; import org.junit.jupiter.api.Test; diff --git a/fluent-wrappers/fluent-mindmap/src/test/java/io/fluentqa/mindmap/freemind/FreeMindConverterTest.java b/fluent-wrappers/fluent-mindmap/src/test/java/io/fluent/mindmap/freemind/FreeMindConverterTest.java similarity index 74% rename from fluent-wrappers/fluent-mindmap/src/test/java/io/fluentqa/mindmap/freemind/FreeMindConverterTest.java rename to fluent-wrappers/fluent-mindmap/src/test/java/io/fluent/mindmap/freemind/FreeMindConverterTest.java index 9f5727e..b61be43 100644 --- a/fluent-wrappers/fluent-mindmap/src/test/java/io/fluentqa/mindmap/freemind/FreeMindConverterTest.java +++ b/fluent-wrappers/fluent-mindmap/src/test/java/io/fluent/mindmap/freemind/FreeMindConverterTest.java @@ -1,7 +1,8 @@ -package io.fluentqa.mindmap.freemind; +package io.fluent.mindmap.freemind; + +import io.fluent.mindmap.api.MindMapPath; +import io.fluent.mindmap.freemind.model.Node; -import io.fluentqa.mindmap.api.MindMapPath; -import io.fluentqa.mindmap.freemind.model.Node; import java.util.List; import org.junit.jupiter.api.BeforeEach; diff --git a/fluent-wrappers/fluent-mindmap/src/test/java/io/fluentqa/mindmap/xmind/XMindUtilTest.java b/fluent-wrappers/fluent-mindmap/src/test/java/io/fluent/mindmap/xmind/XMindUtilTest.java similarity index 84% rename from fluent-wrappers/fluent-mindmap/src/test/java/io/fluentqa/mindmap/xmind/XMindUtilTest.java rename to fluent-wrappers/fluent-mindmap/src/test/java/io/fluent/mindmap/xmind/XMindUtilTest.java index cc7bec9..22ae77d 100644 --- a/fluent-wrappers/fluent-mindmap/src/test/java/io/fluentqa/mindmap/xmind/XMindUtilTest.java +++ b/fluent-wrappers/fluent-mindmap/src/test/java/io/fluent/mindmap/xmind/XMindUtilTest.java @@ -1,6 +1,6 @@ -package io.fluentqa.mindmap.xmind; +package io.fluent.mindmap.xmind; -import io.fluentqa.mindmap.xmind.model.XmindRawData; +import io.fluent.mindmap.xmind.model.XmindRawData; import org.junit.jupiter.api.Test; class XMindUtilTest { diff --git a/fluent-wrappers/fluent-mindmap/src/test/java/io/fluentqa/mindmap/xmind/XmindTransformerTest.java b/fluent-wrappers/fluent-mindmap/src/test/java/io/fluent/mindmap/xmind/XmindTransformerTest.java similarity index 74% rename from fluent-wrappers/fluent-mindmap/src/test/java/io/fluentqa/mindmap/xmind/XmindTransformerTest.java rename to fluent-wrappers/fluent-mindmap/src/test/java/io/fluent/mindmap/xmind/XmindTransformerTest.java index f307acc..f655e60 100644 --- a/fluent-wrappers/fluent-mindmap/src/test/java/io/fluentqa/mindmap/xmind/XmindTransformerTest.java +++ b/fluent-wrappers/fluent-mindmap/src/test/java/io/fluent/mindmap/xmind/XmindTransformerTest.java @@ -1,7 +1,8 @@ -package io.fluentqa.mindmap.xmind; +package io.fluent.mindmap.xmind; + +import io.fluent.mindmap.api.MindMapPath; +import io.fluent.mindmap.xmind.model.Attached; -import io.fluentqa.mindmap.api.MindMapPath; -import io.fluentqa.mindmap.xmind.model.Attached; import java.util.List; import org.junit.jupiter.api.Test; diff --git a/fluent-wrappers/fluentqa-md/src/main/java/io/fluentqa/md/MarkdownAccessor.java b/fluent-wrappers/fluentqa-md/src/main/java/io/fluent/md/MarkdownAccessor.java similarity index 58% rename from fluent-wrappers/fluentqa-md/src/main/java/io/fluentqa/md/MarkdownAccessor.java rename to fluent-wrappers/fluentqa-md/src/main/java/io/fluent/md/MarkdownAccessor.java index 71c6e55..b452338 100644 --- a/fluent-wrappers/fluentqa-md/src/main/java/io/fluentqa/md/MarkdownAccessor.java +++ b/fluent-wrappers/fluentqa-md/src/main/java/io/fluent/md/MarkdownAccessor.java @@ -1,3 +1,3 @@ -package io.fluentqa.md; +package io.fluent.md; public class MarkdownAccessor {} diff --git a/fluent-wrappers/fluentqa-md/src/main/java/io/fluent/md/package-info.java b/fluent-wrappers/fluentqa-md/src/main/java/io/fluent/md/package-info.java new file mode 100644 index 0000000..93f2e6c --- /dev/null +++ b/fluent-wrappers/fluentqa-md/src/main/java/io/fluent/md/package-info.java @@ -0,0 +1 @@ +package io.fluent.md; diff --git a/fluent-wrappers/fluentqa-md/src/main/java/io/fluentqa/md/parser/FieldParseConfig.java b/fluent-wrappers/fluentqa-md/src/main/java/io/fluent/md/parser/FieldParseConfig.java similarity index 86% rename from fluent-wrappers/fluentqa-md/src/main/java/io/fluentqa/md/parser/FieldParseConfig.java rename to fluent-wrappers/fluentqa-md/src/main/java/io/fluent/md/parser/FieldParseConfig.java index d4a5c1c..90949b9 100644 --- a/fluent-wrappers/fluentqa-md/src/main/java/io/fluentqa/md/parser/FieldParseConfig.java +++ b/fluent-wrappers/fluentqa-md/src/main/java/io/fluent/md/parser/FieldParseConfig.java @@ -1,4 +1,4 @@ -package io.fluentqa.md.parser; +package io.fluent.md.parser; import java.util.function.Function; diff --git a/fluent-wrappers/fluentqa-md/src/main/java/io/fluentqa/md/parser/ParseConfig.java b/fluent-wrappers/fluentqa-md/src/main/java/io/fluent/md/parser/ParseConfig.java similarity index 93% rename from fluent-wrappers/fluentqa-md/src/main/java/io/fluentqa/md/parser/ParseConfig.java rename to fluent-wrappers/fluentqa-md/src/main/java/io/fluent/md/parser/ParseConfig.java index aff5edb..4c329f5 100644 --- a/fluent-wrappers/fluentqa-md/src/main/java/io/fluentqa/md/parser/ParseConfig.java +++ b/fluent-wrappers/fluentqa-md/src/main/java/io/fluent/md/parser/ParseConfig.java @@ -1,4 +1,4 @@ -package io.fluentqa.md.parser; +package io.fluent.md.parser; import cn.hutool.core.util.ReflectUtil; import java.util.ArrayList; diff --git a/fluent-wrappers/fluentqa-md/src/main/java/io/fluentqa/md/parser/awesome/AwesomeListParserConfig.java b/fluent-wrappers/fluentqa-md/src/main/java/io/fluent/md/parser/awesome/AwesomeListParserConfig.java similarity index 76% rename from fluent-wrappers/fluentqa-md/src/main/java/io/fluentqa/md/parser/awesome/AwesomeListParserConfig.java rename to fluent-wrappers/fluentqa-md/src/main/java/io/fluent/md/parser/awesome/AwesomeListParserConfig.java index 856eaeb..47c8705 100644 --- a/fluent-wrappers/fluentqa-md/src/main/java/io/fluentqa/md/parser/awesome/AwesomeListParserConfig.java +++ b/fluent-wrappers/fluentqa-md/src/main/java/io/fluent/md/parser/awesome/AwesomeListParserConfig.java @@ -1,6 +1,6 @@ -package io.fluentqa.md.parser.awesome; +package io.fluent.md.parser.awesome; -import io.fluentqa.md.parser.FieldParseConfig; +import io.fluent.md.parser.FieldParseConfig; import lombok.Data; @Data diff --git a/fluent-wrappers/fluentqa-md/src/main/java/io/fluentqa/md/parser/awesome/AwesomeModel.java b/fluent-wrappers/fluentqa-md/src/main/java/io/fluent/md/parser/awesome/AwesomeModel.java similarity index 79% rename from fluent-wrappers/fluentqa-md/src/main/java/io/fluentqa/md/parser/awesome/AwesomeModel.java rename to fluent-wrappers/fluentqa-md/src/main/java/io/fluent/md/parser/awesome/AwesomeModel.java index e9f23c6..12dba73 100644 --- a/fluent-wrappers/fluentqa-md/src/main/java/io/fluentqa/md/parser/awesome/AwesomeModel.java +++ b/fluent-wrappers/fluentqa-md/src/main/java/io/fluent/md/parser/awesome/AwesomeModel.java @@ -1,4 +1,4 @@ -package io.fluentqa.md.parser.awesome; +package io.fluent.md.parser.awesome; import lombok.Data; diff --git a/fluent-wrappers/fluentqa-md/src/main/java/io/fluentqa/md/parser/awesome/MarkdownAwesomeListParser.java b/fluent-wrappers/fluentqa-md/src/main/java/io/fluent/md/parser/awesome/MarkdownAwesomeListParser.java similarity index 97% rename from fluent-wrappers/fluentqa-md/src/main/java/io/fluentqa/md/parser/awesome/MarkdownAwesomeListParser.java rename to fluent-wrappers/fluentqa-md/src/main/java/io/fluent/md/parser/awesome/MarkdownAwesomeListParser.java index c45bbe3..17539c8 100644 --- a/fluent-wrappers/fluentqa-md/src/main/java/io/fluentqa/md/parser/awesome/MarkdownAwesomeListParser.java +++ b/fluent-wrappers/fluentqa-md/src/main/java/io/fluent/md/parser/awesome/MarkdownAwesomeListParser.java @@ -1,4 +1,4 @@ -package io.fluentqa.md.parser.awesome; +package io.fluent.md.parser.awesome; import com.vladsch.flexmark.html.HtmlRenderer; import com.vladsch.flexmark.parser.Parser; diff --git a/fluent-wrappers/fluentqa-md/src/main/java/io/fluentqa/md/package-info.java b/fluent-wrappers/fluentqa-md/src/main/java/io/fluentqa/md/package-info.java deleted file mode 100644 index cd7930c..0000000 --- a/fluent-wrappers/fluentqa-md/src/main/java/io/fluentqa/md/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package io.fluentqa.md; diff --git a/fluent-wrappers/fluentqa-md/src/test/java/io/fluentqa/md/parsers/MarkdownAwesomeListParserTest.java b/fluent-wrappers/fluentqa-md/src/test/java/io/fluent/md/parsers/MarkdownAwesomeListParserTest.java similarity index 95% rename from fluent-wrappers/fluentqa-md/src/test/java/io/fluentqa/md/parsers/MarkdownAwesomeListParserTest.java rename to fluent-wrappers/fluentqa-md/src/test/java/io/fluent/md/parsers/MarkdownAwesomeListParserTest.java index 61fbe09..7b40749 100644 --- a/fluent-wrappers/fluentqa-md/src/test/java/io/fluentqa/md/parsers/MarkdownAwesomeListParserTest.java +++ b/fluent-wrappers/fluentqa-md/src/test/java/io/fluent/md/parsers/MarkdownAwesomeListParserTest.java @@ -1,4 +1,4 @@ -package io.fluentqa.md.parsers; +package io.fluent.md.parsers; import cn.hutool.json.JSONUtil; import com.vladsch.flexmark.ast.BulletList; @@ -8,8 +8,9 @@ import com.vladsch.flexmark.parser.Parser; import com.vladsch.flexmark.util.ast.Node; import io.fluent.builtin.JavaProjectFileUtils; -import io.fluentqa.md.parser.awesome.AwesomeModel; -import io.fluentqa.md.parser.awesome.MarkdownAwesomeListParser; +import io.fluent.md.parser.awesome.AwesomeModel; +import io.fluent.md.parser.awesome.MarkdownAwesomeListParser; + import java.io.File; import java.nio.charset.Charset; import java.util.List; From 559d8c7506da2b10044ec52a7fc4be756a307f64 Mon Sep 17 00:00:00 2001 From: patrick Date: Wed, 19 Mar 2025 15:53:48 +0800 Subject: [PATCH 3/5] ##35 add base project --- fluent-apps/qam/qtm/FluentUploadTCModule.java | 53 +++++++ .../qam/qtm/upload/model/UploadFileModel.java | 62 ++++++++ fluent-apps/qam/qtm/upload/package-info.java | 1 + .../qtm/upload/proxy/UploadFileDataProxy.java | 64 ++++++++ .../qtm/upload/proxy/UploadFileTypeEnum.java | 14 ++ fluent-apps/workspace/pom.xml | 20 +++ .../java/io/fluent/WorkspaceApplication.java | 21 +++ .../base/FluentProductConfigModule.java | 65 ++++++++ .../src/main/java/io/fluent/base/README.md | 8 + .../base/masterdata/model/MasterData.java | 76 ++++++++++ .../base/masterdata/repo/MasterDataRepo.java | 15 ++ .../java/io/fluent/base/package-info.java | 1 + .../product/model/ProductModuleModel.java | 140 ++++++++++++++++++ .../model/ProductModuleValidFlagVo.java | 47 ++++++ .../io/fluent/base/product/package-info.java | 1 + .../base/product/repo/ProductModuleRepo.java | 18 +++ .../product/service/ProductModuleService.java | 57 +++++++ .../base/project/model/ProjectModel.java | 89 +++++++++++ .../base/project/repo/ProjectModelRepo.java | 18 +++ .../src/main/resources/application-dev.yaml | 129 ++++++++++++++++ .../src/main/resources/application.yaml | 3 + .../src/main/resources/public/app.css | 24 +++ .../src/main/resources/public/app.js | 71 +++++++++ .../src/main/resources/public/home.html | 12 ++ .../src/main/resources/tpl/operation.tpl | 17 +++ 25 files changed, 1026 insertions(+) create mode 100644 fluent-apps/qam/qtm/FluentUploadTCModule.java create mode 100644 fluent-apps/qam/qtm/upload/model/UploadFileModel.java create mode 100644 fluent-apps/qam/qtm/upload/package-info.java create mode 100644 fluent-apps/qam/qtm/upload/proxy/UploadFileDataProxy.java create mode 100644 fluent-apps/qam/qtm/upload/proxy/UploadFileTypeEnum.java create mode 100644 fluent-apps/workspace/pom.xml create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/WorkspaceApplication.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/base/FluentProductConfigModule.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/base/README.md create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/base/masterdata/model/MasterData.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/base/masterdata/repo/MasterDataRepo.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/base/package-info.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/base/product/model/ProductModuleModel.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/base/product/model/ProductModuleValidFlagVo.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/base/product/package-info.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/base/product/repo/ProductModuleRepo.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/base/product/service/ProductModuleService.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/base/project/model/ProjectModel.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/base/project/repo/ProjectModelRepo.java create mode 100644 fluent-apps/workspace/src/main/resources/application-dev.yaml create mode 100644 fluent-apps/workspace/src/main/resources/application.yaml create mode 100644 fluent-apps/workspace/src/main/resources/public/app.css create mode 100644 fluent-apps/workspace/src/main/resources/public/app.js create mode 100644 fluent-apps/workspace/src/main/resources/public/home.html create mode 100644 fluent-apps/workspace/src/main/resources/tpl/operation.tpl diff --git a/fluent-apps/qam/qtm/FluentUploadTCModule.java b/fluent-apps/qam/qtm/FluentUploadTCModule.java new file mode 100644 index 0000000..52e5b7d --- /dev/null +++ b/fluent-apps/qam/qtm/FluentUploadTCModule.java @@ -0,0 +1,53 @@ +package io.fluent.base; + +import io.fluent.base.upload.model.UploadFileModel; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import xyz.erupt.core.annotation.EruptScan; +import xyz.erupt.core.constant.MenuTypeEnum; +import xyz.erupt.core.module.EruptModule; +import xyz.erupt.core.module.EruptModuleInvoke; +import xyz.erupt.core.module.MetaMenu; +import xyz.erupt.core.module.ModuleInfo; + +import java.util.ArrayList; +import java.util.List; + +@Configuration +@ComponentScan +@EntityScan +@EruptScan +@EnableConfigurationProperties +public class FluentUploadTCModule implements EruptModule { + + public FluentUploadTCModule() { + } + + @Override + public ModuleInfo info() { + return ModuleInfo.builder().name("fluent-tc-sync").build(); + } + + @Override + public void run() { + EruptModule.super.run(); + } + + @Override + public List initMenus() { + List menus = new ArrayList<>(); + menus.add(MetaMenu.createRootMenu("$tc-upload", "测试文件管理", "fa fa-file", 100)); + MetaMenu tfUploadSyncMenu = MetaMenu.createEruptClassMenu(UploadFileModel.class, menus.get(0), 0, MenuTypeEnum.TABLE); + tfUploadSyncMenu.setIcon("fa fa-folder-open"); + tfUploadSyncMenu.setName("测试文件同步"); + tfUploadSyncMenu.setCode("$tc-upload-sync"); + menus.add(tfUploadSyncMenu); + return menus; + } + + static { + EruptModuleInvoke.addEruptModule(FluentUploadTCModule.class); + } +} diff --git a/fluent-apps/qam/qtm/upload/model/UploadFileModel.java b/fluent-apps/qam/qtm/upload/model/UploadFileModel.java new file mode 100644 index 0000000..cb29089 --- /dev/null +++ b/fluent-apps/qam/qtm/upload/model/UploadFileModel.java @@ -0,0 +1,62 @@ +package io.fluent.base.upload.model; + +import io.fluent.base.upload.proxy.UploadFileDataProxy; +import io.fluent.base.product.model.ProductModuleModel; +import lombok.Data; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.PreDataProxy; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.AttachmentType; +import xyz.erupt.annotation.sub_field.sub_edit.ChoiceType; +import xyz.erupt.annotation.sub_field.sub_edit.InputType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; +import xyz.erupt.toolkit.handler.SqlChoiceFetchHandler; + +import javax.persistence.Entity; +import javax.persistence.Table; + +@Erupt(name = "测试相关文件上传同步", orderBy = "UploadFileModel.createTime desc") +@Table(name = "uploaded_files") +@Entity +@Data +@PreDataProxy(value = UploadFileDataProxy.class) +public class UploadFileModel extends ProductModuleModel { + + @EruptField( + views = @View(title = "用途"), + edit = @Edit( + search = @Search, + title = "获取可选类型", + type = EditType.CHOICE, + desc = "动态获取可选类型", + notNull = true, + choiceType = @ChoiceType( + fetchHandler = SqlChoiceFetchHandler.class, + fetchHandlerParams = "select distinct code,name from master_data where category='UPLOAD_FILE_USAGE' and valid=true" + )) + ) + private String usage; + + + @EruptField( + views = @View(title = "文件上传"), + edit = @Edit(title = "文件上传", type = EditType.ATTACHMENT, + attachmentType = @AttachmentType(size = 100000)) + ) + private String attachment; + + @EruptField( + views = @View( + title = "用途描述" + ), + edit = @Edit( + title = "用途描述", + type = EditType.TEXTAREA, notNull = true, + inputType = @InputType + ) + ) + private String comments; +} \ No newline at end of file diff --git a/fluent-apps/qam/qtm/upload/package-info.java b/fluent-apps/qam/qtm/upload/package-info.java new file mode 100644 index 0000000..67c0eee --- /dev/null +++ b/fluent-apps/qam/qtm/upload/package-info.java @@ -0,0 +1 @@ +package io.fluent.base.upload; \ No newline at end of file diff --git a/fluent-apps/qam/qtm/upload/proxy/UploadFileDataProxy.java b/fluent-apps/qam/qtm/upload/proxy/UploadFileDataProxy.java new file mode 100644 index 0000000..db927e0 --- /dev/null +++ b/fluent-apps/qam/qtm/upload/proxy/UploadFileDataProxy.java @@ -0,0 +1,64 @@ +package io.fluent.base.upload.proxy; + + + +import cn.hutool.core.bean.BeanUtil; +import io.fluent.excel.ExcelReadWriter; +import io.fluent.base.product.model.ProductModuleModel; +import io.fluent.qtm.tc.dto.TestCaseDTO; +import io.fluent.qtm.tc.service.TestCaseService; +import io.fluent.qtm.tc.service.impl.MindMappingService; +import lombok.extern.slf4j.Slf4j; +import xyz.erupt.core.prop.EruptProp; +import xyz.erupt.core.util.EruptSpringUtil; +import xyz.erupt.jpa.model.MetaDataProxy; +import xyz.erupt.jpa.model.MetaModel; + +import java.util.List; + +@Slf4j +public class UploadFileDataProxy extends MetaDataProxy { + private final MindMappingService mindMappingService; + private final TestCaseService testCaseService; + private final EruptProp eruptProp; + private final ExcelReadWriter excelReadWriter; + + public UploadFileDataProxy() { + mindMappingService = EruptSpringUtil.getBean(MindMappingService.class); + testCaseService = EruptSpringUtil.getBean(TestCaseService.class); + eruptProp = EruptSpringUtil.getBean(EruptProp.class); + excelReadWriter = new ExcelReadWriter(); + } + + @Override + public void beforeAdd(MetaModel metaModel) { + //before add, add some check here + super.beforeAdd(metaModel); + } + + @Override + public void afterAdd(MetaModel metaModel) { + //after add, then doing business process + log.info("start handler uploaded file"); + String filePath = getUploaderFilePath(metaModel); + String uploadType = BeanUtil.getProperty(metaModel, "usage"); + + if(UploadFileTypeEnum.parseType(uploadType).equals(UploadFileTypeEnum.EXCEL_TC)){ + ProductModuleModel product = BeanUtil.getProperty(metaModel, "product"); + ProductModuleModel module = BeanUtil.getProperty(metaModel, "module"); + testCaseService.saveTestCases(getExcelTestCases(filePath),product,module,metaModel.getUpdateBy()); + } + if(UploadFileTypeEnum.parseType(uploadType).equals(UploadFileTypeEnum.FREEMIND)){ + mindMappingService.saveTestCases(filePath,metaModel); + } + } + + private List getExcelTestCases(String filePath){ + return excelReadWriter.readExcel(filePath, TestCaseDTO.class); + } + + private String getUploaderFilePath(MetaModel metaModel) { + return eruptProp.getUploadPath() + BeanUtil.getProperty(metaModel, "attachment"); + } + +} diff --git a/fluent-apps/qam/qtm/upload/proxy/UploadFileTypeEnum.java b/fluent-apps/qam/qtm/upload/proxy/UploadFileTypeEnum.java new file mode 100644 index 0000000..8a44582 --- /dev/null +++ b/fluent-apps/qam/qtm/upload/proxy/UploadFileTypeEnum.java @@ -0,0 +1,14 @@ +package io.fluent.base.upload.proxy; + +public enum UploadFileTypeEnum { + EXCEL_TC,FREEMIND,PM,MINDMAP; + + public static UploadFileTypeEnum parseType(String uploadFileType) { + for (UploadFileTypeEnum uploadFileTypeEnum : UploadFileTypeEnum.values()) { + if (uploadFileTypeEnum.name().equals(uploadFileType)) { + return uploadFileTypeEnum; + } + } + throw new RuntimeException("UploadFileTypeEnum is not found for " + uploadFileType); + } +} diff --git a/fluent-apps/workspace/pom.xml b/fluent-apps/workspace/pom.xml new file mode 100644 index 0000000..b498a84 --- /dev/null +++ b/fluent-apps/workspace/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + io.fluentqa + fluent-apps + 1.0-SNAPSHOT + + + workspace + + + 21 + 21 + UTF-8 + + + \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/java/io/fluent/WorkspaceApplication.java b/fluent-apps/workspace/src/main/java/io/fluent/WorkspaceApplication.java new file mode 100644 index 0000000..35eb484 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/WorkspaceApplication.java @@ -0,0 +1,21 @@ +package io.fluent; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.scheduling.annotation.EnableAsync; +import xyz.erupt.core.annotation.EruptScan; + +@SpringBootApplication +@EnableAsync +@EruptScan +@EntityScan +public class QAMApp { + /** + * QA Management Application Entrypoint + * @param args + */ + public static void main(String[] args) { + SpringApplication.run(QAMApp.class); + } +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/base/FluentProductConfigModule.java b/fluent-apps/workspace/src/main/java/io/fluent/base/FluentProductConfigModule.java new file mode 100644 index 0000000..d4f42ec --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/base/FluentProductConfigModule.java @@ -0,0 +1,65 @@ +package io.fluent.base; + +import io.fluent.base.masterdata.model.MasterData; +import io.fluent.base.product.model.ProductModuleModel; +import io.fluent.base.project.model.ProjectModel; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import xyz.erupt.core.annotation.EruptScan; +import xyz.erupt.core.constant.MenuTypeEnum; +import xyz.erupt.core.module.EruptModule; +import xyz.erupt.core.module.EruptModuleInvoke; +import xyz.erupt.core.module.MetaMenu; +import xyz.erupt.core.module.ModuleInfo; + +import java.util.ArrayList; +import java.util.List; + +@Configuration +@ComponentScan +@EntityScan +@EruptScan +@EnableConfigurationProperties +public class FluentProductConfigModule implements EruptModule { + + public FluentProductConfigModule() { + } + + @Override + public ModuleInfo info() { + return ModuleInfo.builder().name("fluent-product").build(); + } + + @Override + public void run() { + EruptModule.super.run(); + } + + @Override + public List initMenus() { + List menus = new ArrayList<>(); + menus.add(MetaMenu.createRootMenu("$fluent-master", "产品配置", "fa fa-product-hunt", 90)); + MetaMenu productMetaMenu = MetaMenu.createEruptClassMenu(ProductModuleModel.class, menus.get(0), 0, MenuTypeEnum.TABLE); + productMetaMenu.setIcon("fa fa-group"); + productMetaMenu.setName("产品元数据"); + productMetaMenu.setCode("$product-meta"); + menus.add(productMetaMenu); + MetaMenu masterDataMenu = MetaMenu.createEruptClassMenu(MasterData.class, menus.get(0), 1, MenuTypeEnum.TABLE); + masterDataMenu.setIcon("fa fa-times"); + masterDataMenu.setName("产品字典表配置"); + masterDataMenu.setCode("$master-data"); + menus.add(masterDataMenu); + MetaMenu projectMenu = MetaMenu.createEruptClassMenu(ProjectModel.class, menus.get(0), 2, MenuTypeEnum.TABLE); + projectMenu.setIcon("fa fa-linode"); + projectMenu.setName("项目配置"); + projectMenu.setCode("$project-meta"); + menus.add(projectMenu); + return menus; + } + + static { + EruptModuleInvoke.addEruptModule(FluentProductConfigModule.class); + } +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/base/README.md b/fluent-apps/workspace/src/main/java/io/fluent/base/README.md new file mode 100644 index 0000000..9fb066c --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/base/README.md @@ -0,0 +1,8 @@ +# README + +shared component: + +- master data: shared configurations +- upload data: upload component +- product: product module +- project: project module \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/java/io/fluent/base/masterdata/model/MasterData.java b/fluent-apps/workspace/src/main/java/io/fluent/base/masterdata/model/MasterData.java new file mode 100644 index 0000000..e8d0150 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/base/masterdata/model/MasterData.java @@ -0,0 +1,76 @@ +package io.fluent.base.masterdata.model; + +import io.fluent.base.handlers.SqlTagFetchHandler; +import io.fluent.base.model.ModelWithValidFlagVo; +import lombok.Data; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.InputType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; +import xyz.erupt.annotation.sub_field.sub_edit.TagsType; + +import javax.persistence.Entity; +import javax.persistence.Table; + + +@Erupt(name = "产品字典值配置", power = @Power(importable = true, export = true)) +@Table(name = "master_data") +@Entity +@Data +public class MasterData extends ModelWithValidFlagVo { + + @EruptField( + views = @View(title = "分类"), + edit = @Edit( + search = @Search(vague = true), + title = "获取可选种类", + type = EditType.TAGS, + desc = "动态获取可选种类", + tagsType = @TagsType( + fetchHandler = SqlTagFetchHandler.class, + fetchHandlerParams = "select distinct category from master_data where valid=true" + )) + ) + private String category; + + @EruptField( + views = @View( + title = "名称" + ), + edit = @Edit( + title = "名称", + type = EditType.INPUT, search = @Search, notNull = true, + inputType = @InputType + ) + ) + private String name; + + @EruptField( + views = @View( + title = "详细描述" + ), + edit = @Edit( + title = "详细描述", + type = EditType.INPUT, + inputType = @InputType + ) + ) + private String detail; + + @EruptField( + views = @View( + title = "代号" + ), + edit = @Edit( + title = "代号", + type = EditType.INPUT, search = @Search, notNull = true, + inputType = @InputType + ) + ) + private String code; + +} \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/java/io/fluent/base/masterdata/repo/MasterDataRepo.java b/fluent-apps/workspace/src/main/java/io/fluent/base/masterdata/repo/MasterDataRepo.java new file mode 100644 index 0000000..2618156 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/base/masterdata/repo/MasterDataRepo.java @@ -0,0 +1,15 @@ +package io.fluent.base.masterdata.repo; + +import io.fluent.base.masterdata.model.MasterData; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface MasterDataRepo extends JpaRepository, JpaSpecificationExecutor { + + Optional findMasterDataByCode(String code); + +} \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/java/io/fluent/base/package-info.java b/fluent-apps/workspace/src/main/java/io/fluent/base/package-info.java new file mode 100644 index 0000000..b1b2672 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/base/package-info.java @@ -0,0 +1 @@ +package io.fluent.base; \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/java/io/fluent/base/product/model/ProductModuleModel.java b/fluent-apps/workspace/src/main/java/io/fluent/base/product/model/ProductModuleModel.java new file mode 100644 index 0000000..a86390c --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/base/product/model/ProductModuleModel.java @@ -0,0 +1,140 @@ +package io.fluent.base.product.model; + +import io.fluent.base.model.ModelWithValidFlagVo; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_erupt.Tree; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.ChoiceType; +import xyz.erupt.annotation.sub_field.sub_edit.InputType; +import xyz.erupt.annotation.sub_field.sub_edit.ReferenceTreeType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; +import xyz.erupt.toolkit.handler.SqlChoiceFetchHandler; + +import javax.persistence.*; +import java.util.UUID; + +@Erupt(name = "产品模块配置", + power = @Power(importable = true, export = true), + tree = @Tree(pid = "parent.id")) +@Entity +@Table(name = "products") +public class ProductModuleModel extends ModelWithValidFlagVo { + + @EruptField( + views = @View( + title = "名称" + ), + edit = @Edit( + title = "名称", + type = EditType.INPUT, search = @Search, + notNull = true, + inputType = @InputType + ) + ) + private String name; + + @EruptField( + views = @View( + title = "代号" + ), + edit = @Edit( + title = "代号", + type = EditType.INPUT, search = @Search, + notNull = true, + inputType = @InputType + ) + ) + private String code; + + @EruptField( + views = @View( + title = "详细描述" + ), + edit = @Edit( + title = "详细描述", + type = EditType.INPUT, search = @Search, notNull = true, + inputType = @InputType + ) + ) + private String details; + + @EruptField( + views = @View(title = "类型"), + edit = @Edit( + search = @Search, + title = "获取可选类型", + type = EditType.CHOICE, + desc = "动态获取可选类型", + choiceType = @ChoiceType( + fetchHandler = SqlChoiceFetchHandler.class, + fetchHandlerParams = "select id,name from master_data where category='PRODUCT'" + )) + ) + private String metaType; + + @ManyToOne + @EruptField( + edit = @Edit( + title = "上级树节点", + type = EditType.REFERENCE_TREE, + referenceTreeType = @ReferenceTreeType(pid = "parent.id") + ) + ) + private ProductModuleModel parent; + + + @Column(length = 36, nullable = true, updatable = false) + private String uuid = UUID.randomUUID().toString(); + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getDetails() { + return details; + } + + public void setDetails(String details) { + this.details = details; + } + + public String getMetaType() { + return metaType; + } + + public void setMetaType(String metaType) { + this.metaType = metaType; + } + + public ProductModuleModel getParent() { + return parent; + } + + public void setParent(ProductModuleModel parent) { + this.parent = parent; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } +} \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/java/io/fluent/base/product/model/ProductModuleValidFlagVo.java b/fluent-apps/workspace/src/main/java/io/fluent/base/product/model/ProductModuleValidFlagVo.java new file mode 100644 index 0000000..b267168 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/base/product/model/ProductModuleValidFlagVo.java @@ -0,0 +1,47 @@ +package io.fluent.base.product.model; + +import io.fluent.base.model.ModelWithValidFlagVo; +import lombok.Getter; +import lombok.Setter; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.ReferenceTreeType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; + +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.MappedSuperclass; + +@MappedSuperclass +@Getter +@Setter +public class ProductModuleValidFlagVo extends ModelWithValidFlagVo { + @ManyToOne + @JoinColumn(name = "product_id") + @EruptField( + views = @View(title = "产品名称",column = "details"), + edit = @Edit( + search = @Search, + title = "产品选择", + type = EditType.REFERENCE_TREE, + desc = "动态获取产品", + referenceTreeType = @ReferenceTreeType(id = "id", label = "name", + pid = "parent.id")) + ) + private ProductModuleModel product; + + @ManyToOne + @JoinColumn(name = "module_id") + @EruptField( + views = @View(title = "模块名称",column = "details"), + edit = @Edit(title = "模块选择", search = @Search, type = EditType.REFERENCE_TREE, + referenceTreeType = @ReferenceTreeType(id = "id", label = "name", + dependField = "product", + dependColumn = "parent.id" + )) + ) + private ProductModuleModel module; + +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/base/product/package-info.java b/fluent-apps/workspace/src/main/java/io/fluent/base/product/package-info.java new file mode 100644 index 0000000..15f9d3f --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/base/product/package-info.java @@ -0,0 +1 @@ +package io.fluent.base.product; \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/java/io/fluent/base/product/repo/ProductModuleRepo.java b/fluent-apps/workspace/src/main/java/io/fluent/base/product/repo/ProductModuleRepo.java new file mode 100644 index 0000000..7898e5f --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/base/product/repo/ProductModuleRepo.java @@ -0,0 +1,18 @@ +package io.fluent.base.product.repo; + + +import io.fluent.base.product.model.ProductModuleModel; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface ProductModuleRepo extends JpaRepository, JpaSpecificationExecutor { + + Optional findProductByNameAndValid(String name, boolean valid); + + Optional findProductByCodeAndValid(String codeName, boolean valid); + Optional findProductByParentIdAndNameAndValid(Long parentId, String name, boolean valid); +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/base/product/service/ProductModuleService.java b/fluent-apps/workspace/src/main/java/io/fluent/base/product/service/ProductModuleService.java new file mode 100644 index 0000000..0c42dac --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/base/product/service/ProductModuleService.java @@ -0,0 +1,57 @@ +package io.fluent.base.product.service; + +import io.fluent.builtin.PingYinUtils; +import io.fluent.base.product.repo.ProductModuleRepo; +import io.fluent.base.proxies.AuditDataEnhancerProxy; +import io.fluent.base.masterdata.repo.MasterDataRepo; +import io.fluent.base.product.model.ProductModuleModel; +import io.fluent.base.masterdata.model.MasterData; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.Optional; + +@Service +public class ProductModuleService { + @Resource + private ProductModuleRepo metaRepo; + + @Resource + private MasterDataRepo masterDataRepo; + + @Resource + AuditDataEnhancerProxy dataEnhancerProxy; + + public ProductModuleModel createModuleIfNotExist(Long productId, String moduleName, String updater) { + Optional meta = metaRepo.findProductByParentIdAndNameAndValid(productId, + moduleName, true); + if (meta.isPresent()) return meta.get(); + ProductModuleModel parent = new ProductModuleModel(); + parent.setId(productId); + ProductModuleModel module = new ProductModuleModel(); + module.setName(moduleName); + module.setDetails(moduleName); + module.setParent(parent); + module.setCode(PingYinUtils.convertToPinyinAbbreviation(moduleName)); + MasterData data = masterDataRepo.findMasterDataByCode("MODULE").get(); + module.setMetaType(data.getId().toString()); + dataEnhancerProxy.enhanceTimeAndUserAuditData(module,updater); + return metaRepo.save(module); + } + + public ProductModuleModel findApiServiceProduct() { + String API_SERVICE = "API"; + Optional meta = metaRepo.findProductByCodeAndValid(API_SERVICE, true); + if (meta.isPresent()) return meta.get(); + throw new RuntimeException("Please config API Service as a Product in Product Meta"); + } + + public ProductModuleModel createApiModuleIfNotExist(String moduleName,String updater) { + ProductModuleModel parent = findApiServiceProduct(); + return createModuleIfNotExist(parent.getId(), moduleName,updater); + } + + public ProductModuleModel findByName(String productName) { + return metaRepo.findProductByNameAndValid(productName, true).orElse(null); + } +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/base/project/model/ProjectModel.java b/fluent-apps/workspace/src/main/java/io/fluent/base/project/model/ProjectModel.java new file mode 100644 index 0000000..573eace --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/base/project/model/ProjectModel.java @@ -0,0 +1,89 @@ +package io.fluent.base.project.model; + + +import io.fluent.base.model.ModelWithValidFlagVo; +import io.fluent.base.product.model.ProductModuleModel; +import lombok.Data; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_erupt.Tree; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.InputType; +import xyz.erupt.annotation.sub_field.sub_edit.ReferenceTreeType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; + +import javax.persistence.*; +import java.util.UUID; + +@Erupt(name = "项目", + power = @Power(importable = true, export = true), + tree = @Tree(pid = "parent.id")) +@Entity +@Table(name = "projects") +@Data +public class ProjectModel extends ModelWithValidFlagVo { + + @EruptField( + views = @View( + title = "名称" + ), + edit = @Edit( + title = "名称", + type = EditType.INPUT, search = @Search, + notNull = true, + inputType = @InputType + ) + ) + private String name; + + @EruptField( + views = @View( + title = "代号" + ), + edit = @Edit( + title = "代号", + type = EditType.INPUT, search = @Search, + notNull = true, + inputType = @InputType + ) + ) + private String code; + + @EruptField( + views = @View( + title = "详细描述" + ), + edit = @Edit( + title = "详细描述", + type = EditType.INPUT, search = @Search, notNull = true, + inputType = @InputType + ) + ) + private String details; + + @ManyToOne + @EruptField( + edit = @Edit( + title = "产品列表", + type = EditType.REFERENCE_TREE, + referenceTreeType = @ReferenceTreeType(pid = "parent.id") + ) + ) + private ProductModuleModel productId; + + @ManyToOne + @EruptField( + edit = @Edit( + title = "上级树节点", + type = EditType.REFERENCE_TREE, + referenceTreeType = @ReferenceTreeType(pid = "parent.id") + ) + ) + private ProjectModel parent; + + @Column(length = 36, nullable = false, updatable = false) + private String uuid = UUID.randomUUID().toString(); +} \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/java/io/fluent/base/project/repo/ProjectModelRepo.java b/fluent-apps/workspace/src/main/java/io/fluent/base/project/repo/ProjectModelRepo.java new file mode 100644 index 0000000..098b228 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/base/project/repo/ProjectModelRepo.java @@ -0,0 +1,18 @@ +package io.fluent.base.project.repo; + + +import io.fluent.base.product.model.ProductModuleModel; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface ProjectModelRepo extends JpaRepository, JpaSpecificationExecutor { + + Optional findProductByNameAndValid(String name, boolean valid); + + Optional findProductByCodeAndValid(String codeName, boolean valid); + Optional findProductByParentIdAndNameAndValid(Long parentId, String name, boolean valid); +} diff --git a/fluent-apps/workspace/src/main/resources/application-dev.yaml b/fluent-apps/workspace/src/main/resources/application-dev.yaml new file mode 100644 index 0000000..97197cc --- /dev/null +++ b/fluent-apps/workspace/src/main/resources/application-dev.yaml @@ -0,0 +1,129 @@ +erupt-app: + # 是否开启水印,1.12.0 及以上版本支持 + waterMark: false + # 登录失败几次出现验证码,值为0时表示一直需要登录验证码 + verifyCodeCount: 2 + # 登录密码是否加密传输,特殊场景如:LDAP登录可关闭该功能获取密码明文 + pwdTransferEncrypt: true + # 多语言配置,默认支持:简体中文、繁体中文、英文、日文;具体配置详见erupt-i18n模块 + locales: [ "zh-CN","zh-TW","en-US","ja-JP" ] +erupt: + # 是否开启csrf防御 + csrfInspect: true + # 是否开启redis方式存储session,默认false,开启后需在配置文件中添加redis配置(同 spring boot) + redisSession: false + # 附件上传存储路径, 默认路径为:/opt/erupt-attachment + uploadPath: /Users/patrick/data/temp + # 是否保留上传文件原始名称 + keepUploadFileName: false + # 登录session时长(redisSession为true时有效) + upms.expireTimeByLogin: 120 + # 是否记录操作日志,默认true,该功能开启后可在【系统管理 → 操作日志】中查看操作日志 + security.recordOperateLog: false + +#spring: +# datasource: +# url: jdbc:postgresql://db.supabase.orb.local:5432/postgres?currentSchema=workspace +# username: postgres +# password: postgres +# jpa: +# show-sql: true +# generate-ddl: true +# database-platform: org.hibernate.dialect.PostgreSQLDialect +# database: postgresql + +spring: + datasource: + url: jdbc:postgresql://db.supabase.orb.local:5432/workspace + username: postgres + password: postgres + jpa: + show-sql: true + generate-ddl: true + database-platform: org.hibernate.dialect.PostgreSQLDialect + database: postgresql +# mail: +# username: xxxx@qq.com +# password: xxxxxxx +# host: smtp.qq.com +# properties: +# mail.smtp.ssl.auth: true +# mail.smtp.ssl.enable: true +# mail.smtp.ssl.required: true + servlet: + multipart: + max-file-size: 100MB + max-request-size: 100MB + +# springdoc-openapi项目配置 +#springdoc: +# swagger-ui: +# path: /swagger-ui.html +# tags-sorter: alpha +# operations-sorter: alpha +# api-docs: +# path: /v3/api-docs +# group-configs: +# - group: 'default' +# paths-to-match: '/**' +# packages-to-scan: io.fluentqa +# knife4j的增强配置,不需要增强可以不配 +knife4j: + enable: true + openapi: + title: QA Workspace API + description: "`QA Workspace API workspace" + email: fluentqa@163.com + concat: fluent-qa +# url: https://docs.xiaominfo.com +# version: v4.0 +# license: Apache 2.0 +# license-url: https://stackoverflow.com/ +# terms-of-service-url: https://stackoverflow.com/ + group: + test1: + group-name: qa workspace api + api-rule: package +# api-rule-resources: +# - com.knife4j.demo.new3 + +server: + # 启用 gzip 压缩 + compression: + mime-types: application/javascript,text/css,application/json,application/xml,text/html,text/xml,text/plain + enabled: true + error: + includeException: true + includeStacktrace: ALWAYS + includeMessage: ALWAYS + port: 9090 + +# Enhanced logging configuration +logging: + level: + root: INFO + io.fluentqa: DEBUG + org.hibernate: INFO + io.fluent: DEBUG + xyz.erupt: DEBUG + org.springframework: DEBUG + org.springframework.boot: DEBUG + org.springframework.boot.autoconfigure: DEBUG + org.springframework.context: DEBUG + org.springframework.beans: DEBUG + org.springframework.jdbc: DEBUG + org.springframework.orm: DEBUG + org.hibernate.SQL: DEBUG + org.hibernate.type.descriptor.sql: TRACE + pattern: + console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" + file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" + file: + name: ../temp/qam-application.log + +# Enable Spring Boot debug mode +debug: true + +magic-api: + web: /fluentapi/v1 + resource.location: ./magic-script \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/resources/application.yaml b/fluent-apps/workspace/src/main/resources/application.yaml new file mode 100644 index 0000000..caf4dfc --- /dev/null +++ b/fluent-apps/workspace/src/main/resources/application.yaml @@ -0,0 +1,3 @@ +spring: + profiles: + active: dev \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/resources/public/app.css b/fluent-apps/workspace/src/main/resources/public/app.css new file mode 100644 index 0000000..44c63ae --- /dev/null +++ b/fluent-apps/workspace/src/main/resources/public/app.css @@ -0,0 +1,24 @@ +layout-header { + background: #3f51b5 !important; +} + +/* 例:修改登录页样式 */ +layout-passport > .container { + background-position: center !important; + background-repeat: repeat !important; + background-size: cover !important; + background-color: #fff !important; + background-image: url(https://www.erupt.xyz/demo/login-bg.svg) !important; +} + +layout-passport .title { + font-family: Courier New, Menlo, Monaco, Consolas, monospace !important; +} + +layout-passport form { + padding: 26px !important; + margin: 8px !important; + background: rgba(255, 255, 255, 0.9); + border-radius: 3px; + box-shadow: 1px 1px 10px rgba(190, 184, 184, 0.3); +} \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/resources/public/app.js b/fluent-apps/workspace/src/main/resources/public/app.js new file mode 100644 index 0000000..c4435ce --- /dev/null +++ b/fluent-apps/workspace/src/main/resources/public/app.js @@ -0,0 +1,71 @@ +window.eruptSiteConfig = { + //erupt接口地址,在前后端分离时指定 + domain: "", + //附件地址,一般情况下不需要指定,如果自定义对象存储空间,则需在此指定附件资源访问地址 + fileDomain: "", + //标题 + title: "QA Workspace", + //描述 + desc: "QA Base", + //是否展示版权信息 + copyright: true, + //高德地图 api key,使用地图组件须指定此属性,amapKey获取地址:https://lbs.amap.com (服务平台为:Web端(JS API)) + amapKey: "xxxx", + //高德地图 SecurityJsCode + amapSecurityJsCode: "xxxxx", + //logo路径 + logoPath: "erupt.svg", + //logo文字 + logoText: "erupt", + //注册页地址(仅是一个链接,需要自定义实际样式) + registerPage: "", + //自定义导航栏按钮,配置后将会出现在页面右上角 + r_tools: [{ + text: "自定义功能按钮", + icon: "fa-eercast", + mobileHidden: true, + click: function (event) { + alert("Function button"); + } + }], + // //登录成功事件 + // login: function(user){ + // + // }, + // //注销事件 + // logout: function(user){ + // + // } +}; + +// //路由回调函数 +// window.eruptRouterEvent = { +// //key表示要监听的路由切换地址,为url hash地址最后一段 +// //例如:http://www.erupt.xyz:9999/#/build/table/demo中demo为回调key +// demo: { +// //路由载入事件 +// load: function (e) { +// +// }, +// //路由退出事件 +// unload: function (e) { +// +// } +// }, +// //$ 为全路径通配符,在任何路由切换时都会执行load与unload事件 +// $: { +// load: function (e) { +// +// }, +// unload: function (e) { +// } +// } +// }; +// +// //erupt生命周期函数 +// window.eruptEvent = { +// //页面加载完成后回调 +// startup: function () { +// +// } +// } \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/resources/public/home.html b/fluent-apps/workspace/src/main/resources/public/home.html new file mode 100644 index 0000000..35600cf --- /dev/null +++ b/fluent-apps/workspace/src/main/resources/public/home.html @@ -0,0 +1,12 @@ + + + + home + + + + + +

测试管理工具箱

+ + \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/resources/tpl/operation.tpl b/fluent-apps/workspace/src/main/resources/tpl/operation.tpl new file mode 100644 index 0000000..d77f7e5 --- /dev/null +++ b/fluent-apps/workspace/src/main/resources/tpl/operation.tpl @@ -0,0 +1,17 @@ + +
+ + <#list rows as row> + + + + + + +
${row.id}${row.choice!''}${row.code!''}
+
\ No newline at end of file From 54f79f5443755790043a8e80f860dde0de573ea4 Mon Sep 17 00:00:00 2001 From: patrick Date: Wed, 19 Mar 2025 17:21:02 +0800 Subject: [PATCH 4/5] #36 Completed, Add QA Testing Process Codes --- fluent-apps/qam/qtm/upload/package-info.java | 1 - .../java/io/fluent/qtm/FluentQAApiModule.java | 125 ++++++++ .../io/fluent}/qtm/FluentUploadTCModule.java | 4 +- .../GenerateApiCaseByCaptureDataHandler.java | 24 ++ .../GenerateApiTestStepByApiTestRecord.java | 24 ++ .../handler/GenerateRawApiCaseHandler.java | 22 ++ .../qtm/api/model/ApiMonitorRecord.java | 131 +++++++++ .../qtm/api/model/ApiSpecChangeModel.java | 52 ++++ .../qtm/api/model/ApiSpecGitRepoModel.java | 47 +++ .../qtm/api/model/ApiSpecVersionModel.java | 102 +++++++ .../java/io/fluent/qtm/api/model/ApiStep.java | 156 ++++++++++ .../fluent/qtm/api/model/ApiTestRecord.java | 131 +++++++++ .../fluent/qtm/api/model/ApiTestScenario.java | 157 ++++++++++ .../fluent/qtm/api/model/RawApiTestCase.java | 96 ++++++ .../io/fluent/qtm/api/model/RemoteApi.java | 275 ++++++++++++++++++ .../fluent/qtm/api/model/RemoteApiStatus.java | 5 + .../fluent/qtm/api/model/RemoteApiType.java | 5 + .../java/io/fluent/qtm/api/package-info.java | 1 + .../qtm/api/repo/ApiMonitorRecordRepo.java | 14 + .../qtm/api/repo/ApiSpecChangeRepository.java | 11 + .../api/repo/ApiSpecGitRepoRepository.java | 10 + .../api/repo/ApiSpecVersionRepository.java | 10 + .../qtm/api/repo/ApiTestScenarioRepo.java | 10 + .../fluent/qtm/api/repo/ApiTestStepRepo.java | 10 + .../qtm/api/repo/RawApiTestCaseRepo.java | 10 + .../qtm/api/repo/RemoteServiceRepo.java | 23 ++ .../qtm/api/service/ApiTestCaseService.java | 107 +++++++ .../qtm/api/service/RemoteApiService.java | 142 +++++++++ .../java/io/fluent/qtm/pm/package-info.java | 1 + .../qtm/pm/requirement/FieldOption.java | 158 ++++++++++ .../qtm/pm/requirement/FieldOptionType.java | 13 + .../pm/requirement/RequirementFeature.java | 34 +++ .../qtm/pm/requirement/RequirementType.java | 15 + .../qtm/pm/requirement/TestRequirement.java | 192 ++++++++++++ .../io/fluent/qtm/tc/dto/TestCaseDTO.java | 28 ++ .../handlers/GenerateTestRecordHandler.java | 49 ++++ .../java/io/fluent/qtm/tc/model/TestCase.java | 238 +++++++++++++++ .../io/fluent/qtm/tc/model/TestResult.java | 206 +++++++++++++ .../java/io/fluent/qtm/tc/model/TestRun.java | 251 ++++++++++++++++ .../io/fluent/qtm/tc/model/TestScenario.java | 43 +++ .../java/io/fluent/qtm/tc/package-info.java | 1 + .../io/fluent/qtm/tc/repo/TestCaseRepo.java | 15 + .../io/fluent/qtm/tc/repo/TestResultRepo.java | 11 + .../io/fluent/qtm/tc/repo/TestRunRepo.java | 11 + .../qtm/tc/service/TestCaseService.java | 14 + .../tc/service/impl/MindMappingService.java | 41 +++ .../tc/service/impl/TestCaseServiceImpl.java | 94 ++++++ .../qtm/upload/model/UploadFileModel.java | 4 +- .../io/fluent/qtm/upload/package-info.java | 1 + .../qtm/upload/proxy/UploadFileDataProxy.java | 2 +- .../qtm/upload/proxy/UploadFileTypeEnum.java | 2 +- 51 files changed, 3122 insertions(+), 7 deletions(-) delete mode 100644 fluent-apps/qam/qtm/upload/package-info.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/FluentQAApiModule.java rename fluent-apps/{qam => workspace/src/main/java/io/fluent}/qtm/FluentUploadTCModule.java (95%) create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/handler/GenerateApiCaseByCaptureDataHandler.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/handler/GenerateApiTestStepByApiTestRecord.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/handler/GenerateRawApiCaseHandler.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiMonitorRecord.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiSpecChangeModel.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiSpecGitRepoModel.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiSpecVersionModel.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiStep.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiTestRecord.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiTestScenario.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/RawApiTestCase.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/RemoteApi.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/RemoteApiStatus.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/RemoteApiType.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/package-info.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiMonitorRecordRepo.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiSpecChangeRepository.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiSpecGitRepoRepository.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiSpecVersionRepository.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiTestScenarioRepo.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiTestStepRepo.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/RawApiTestCaseRepo.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/RemoteServiceRepo.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/service/ApiTestCaseService.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/api/service/RemoteApiService.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/package-info.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/requirement/FieldOption.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/requirement/FieldOptionType.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/requirement/RequirementFeature.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/requirement/RequirementType.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/requirement/TestRequirement.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/dto/TestCaseDTO.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/handlers/GenerateTestRecordHandler.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/model/TestCase.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/model/TestResult.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/model/TestRun.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/model/TestScenario.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/package-info.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/repo/TestCaseRepo.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/repo/TestResultRepo.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/repo/TestRunRepo.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/service/TestCaseService.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/service/impl/MindMappingService.java create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/service/impl/TestCaseServiceImpl.java rename fluent-apps/{qam => workspace/src/main/java/io/fluent}/qtm/upload/model/UploadFileModel.java (95%) create mode 100644 fluent-apps/workspace/src/main/java/io/fluent/qtm/upload/package-info.java rename fluent-apps/{qam => workspace/src/main/java/io/fluent}/qtm/upload/proxy/UploadFileDataProxy.java (98%) rename fluent-apps/{qam => workspace/src/main/java/io/fluent}/qtm/upload/proxy/UploadFileTypeEnum.java (92%) diff --git a/fluent-apps/qam/qtm/upload/package-info.java b/fluent-apps/qam/qtm/upload/package-info.java deleted file mode 100644 index 67c0eee..0000000 --- a/fluent-apps/qam/qtm/upload/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package io.fluent.base.upload; \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/FluentQAApiModule.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/FluentQAApiModule.java new file mode 100644 index 0000000..2a8570a --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/FluentQAApiModule.java @@ -0,0 +1,125 @@ +package io.fluent.qtm; + + +import io.fluent.qtm.api.model.*; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import xyz.erupt.core.annotation.EruptScan; +import xyz.erupt.core.constant.MenuTypeEnum; +import xyz.erupt.core.module.EruptModule; +import xyz.erupt.core.module.EruptModuleInvoke; +import xyz.erupt.core.module.MetaMenu; +import xyz.erupt.core.module.ModuleInfo; + +import java.util.ArrayList; +import java.util.List; + + +@Configuration +@ComponentScan +@EntityScan +@EruptScan +@EnableConfigurationProperties +public class FluentQAApiModule implements EruptModule { + public FluentQAApiModule() { + } + + @Override + public ModuleInfo info() { + return ModuleInfo.builder().name("fluent-api").build(); + } + + @Override + public void run() { + EruptModule.super.run(); + } + + /** + * API管理: + *

+ * 1. API 仓库管理 + * 2. API 接口定义 + * 3. API 接口录制记录 + * 4. API 接口测试 + * + * @return + */ + @Override + public List initMenus() { + List menus = new ArrayList<>(); + menus.add(MetaMenu.createRootMenu("$APIMgr", "接口管理", "fa fa-exchange", 1)); + + MetaMenu menuForAdded = MetaMenu.createEruptClassMenu(RemoteApi.class, + menus.get(0), 1, MenuTypeEnum.TABLE); + menuForAdded.setIcon("fa fa-scissors"); + menuForAdded.setName("API清单"); + menuForAdded.setCode("$API-List"); + menus.add(menuForAdded); + + MetaMenu rawApiTestCaseMenu = MetaMenu.createEruptClassMenu(RawApiTestCase.class, + menus.get(0), 1, MenuTypeEnum.TABLE); + rawApiTestCaseMenu.setIcon("fa fa-scissors"); + rawApiTestCaseMenu.setName("API生成原始测试用例"); + rawApiTestCaseMenu.setCode("$API-TC-GEN"); + menus.add(rawApiTestCaseMenu); + + MetaMenu apiMonitorRecordMenu = MetaMenu.createEruptClassMenu(ApiMonitorRecord.class, + menus.get(0), 2, MenuTypeEnum.TABLE); + apiMonitorRecordMenu.setIcon("fa fa-repeat"); + apiMonitorRecordMenu.setName("API录制记录"); + apiMonitorRecordMenu.setCode("$API-Record"); + menus.add(apiMonitorRecordMenu); + + MetaMenu apiTestRecord = MetaMenu.createEruptClassMenu( + ApiTestRecord + .class, + menus.get(0), 3, MenuTypeEnum.TABLE); + apiTestRecord.setIcon("fa fa-thumbs-up"); + apiTestRecord.setName("API测试结果记录"); + apiTestRecord.setCode("$API-TestResult"); + menus.add(apiTestRecord); + + MetaMenu apiTestScenarioMenu = MetaMenu.createEruptClassMenu( + ApiTestScenario + .class, + menus.get(0), 4, MenuTypeEnum.TABLE); + apiTestScenarioMenu.setIcon("fa fa-folder"); + apiTestScenarioMenu.setName("API测试场景"); + apiTestScenarioMenu.setCode("$API-TestScenario"); + menus.add(apiTestScenarioMenu); + + MetaMenu apiStepMenu = MetaMenu.createEruptClassMenu( + ApiStep + .class, + menus.get(0), 5, MenuTypeEnum.TABLE); + apiStepMenu.setIcon("fa fa-folder"); + apiStepMenu.setName("API用例步骤"); + apiStepMenu.setCode("$API-Step"); + menus.add(apiStepMenu); +// MetaMenu apiDefMenu = MetaMenu.createSimpleMenu("$API-def", "接口定义", "fa fa-check-square-o", +// menus.get(0), 1, ""); +// menus.add(apiDefMenu); +// addNewMenu( +// menus,"$API-Spec-Git","API定义仓库", "fa fa-meetup", ApiSpecGitRepoModel.class, +// MenuTypeEnum.TABLE,1,0 +// ); +// addNewMenu( +// menus,"$API-Spec","API最新版本", "fa fa-gitlab", ApiSpecVersionModel.class, +// MenuTypeEnum.TABLE,1,1 +// ); + + return menus; + } + + + static { + EruptModuleInvoke.addEruptModule(FluentQAApiModule.class); + } + + @Override + public void initFun() { + EruptModule.super.initFun(); + } +} diff --git a/fluent-apps/qam/qtm/FluentUploadTCModule.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/FluentUploadTCModule.java similarity index 95% rename from fluent-apps/qam/qtm/FluentUploadTCModule.java rename to fluent-apps/workspace/src/main/java/io/fluent/qtm/FluentUploadTCModule.java index 52e5b7d..70d4748 100644 --- a/fluent-apps/qam/qtm/FluentUploadTCModule.java +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/FluentUploadTCModule.java @@ -1,6 +1,6 @@ -package io.fluent.base; +package io.fluent.qtm; -import io.fluent.base.upload.model.UploadFileModel; +import io.fluent.qtm.upload.model.UploadFileModel; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.ComponentScan; diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/handler/GenerateApiCaseByCaptureDataHandler.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/handler/GenerateApiCaseByCaptureDataHandler.java new file mode 100644 index 0000000..1466ab1 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/handler/GenerateApiCaseByCaptureDataHandler.java @@ -0,0 +1,24 @@ +package io.fluent.qtm.api.handler; + +import io.fluent.qtm.api.model.ApiMonitorRecord; +import io.fluent.qtm.api.service.ApiTestCaseService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import xyz.erupt.annotation.fun.OperationHandler; + +import javax.annotation.Resource; +import java.util.List; + +@Service +@Slf4j +public class GenerateApiCaseByCaptureDataHandler implements OperationHandler { + + @Resource + private ApiTestCaseService apiService; + @Override + public String exec(List data, Void unused, String[] param) { + log.info("start convert api capture data"); + apiService.convertApiMonitorRecordToTestCase(data); + return null; + } +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/handler/GenerateApiTestStepByApiTestRecord.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/handler/GenerateApiTestStepByApiTestRecord.java new file mode 100644 index 0000000..02361bd --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/handler/GenerateApiTestStepByApiTestRecord.java @@ -0,0 +1,24 @@ +package io.fluent.qtm.api.handler; + +import io.fluent.qtm.api.model.ApiTestRecord; +import io.fluent.qtm.api.service.ApiTestCaseService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import xyz.erupt.annotation.fun.OperationHandler; + +import javax.annotation.Resource; +import java.util.List; + +@Service +@Slf4j +public class GenerateApiTestStepByApiTestRecord implements OperationHandler { + + @Resource + private ApiTestCaseService apiService; + @Override + public String exec(List data, Void unused, String[] param) { + log.info("start convert api capture data"); + apiService.convertApiTestResultToApiTestStep(data); + return null; + } +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/handler/GenerateRawApiCaseHandler.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/handler/GenerateRawApiCaseHandler.java new file mode 100644 index 0000000..bd98597 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/handler/GenerateRawApiCaseHandler.java @@ -0,0 +1,22 @@ +package io.fluent.qtm.api.handler; + +import io.fluent.qtm.api.model.RemoteApi; +import io.fluent.qtm.api.service.ApiTestCaseService; +import org.springframework.stereotype.Service; +import xyz.erupt.annotation.fun.OperationHandler; + +import javax.annotation.Resource; +import java.util.List; + +@Service +public class GenerateRawApiCaseHandler implements OperationHandler { + + @Resource + private ApiTestCaseService apiService; + @Override + public String exec(List data, Void unused, String[] param) { + System.out.println("this is tests"); + apiService.convertToRawTestCase(data); + return null; + } +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiMonitorRecord.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiMonitorRecord.java new file mode 100644 index 0000000..cc2999e --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiMonitorRecord.java @@ -0,0 +1,131 @@ +package io.fluent.qtm.api.model; + + +import io.fluent.qtm.api.handler.GenerateApiCaseByCaptureDataHandler; +import io.fluent.base.handlers.SqlTagFetchHandler; +import lombok.Data; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Layout; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_erupt.RowOperation; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.ViewType; +import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; +import xyz.erupt.annotation.sub_field.sub_edit.TagsType; +import xyz.erupt.jpa.model.MetaModel; + +import javax.persistence.Entity; +import javax.persistence.Table; + +@DynamicUpdate +@DynamicInsert +@Entity +@Table(name = "api_monitor_record") +@Erupt( + name = "接口访问记录", + layout = @Layout( + tableLeftFixed = 3, + pageSize = 30), + power = @Power(importable = true, export = true), + rowOperation = {@RowOperation( + title = "生成接口用例数据", + operationHandler = GenerateApiCaseByCaptureDataHandler.class)}, + orderBy = "ApiMonitorRecord.id desc" + +) +@Data +public class ApiMonitorRecord extends MetaModel { + + @EruptField( + views = @View(title = "app"), + edit = @Edit( + title = "app应用名", + type = EditType.TAGS, search = @Search(vague = true), notNull = true, + tagsType = @TagsType( + fetchHandler = SqlTagFetchHandler.class, + fetchHandlerParams = "select distinct app from api_monitor_record" + ) + )) + private String app; + @EruptField( + views = @View(title = "录制名称"), + edit = @Edit(title = "录制名称", notNull = true, search = @Search) + ) + private String recordName; + @EruptField( + views = @View(title = "请求地址"), + edit = @Edit(title = "请求地址", notNull = true, search = @Search) + ) + private String requestUrl; + + @EruptField( + views = @View(title = "服务"), + edit = @Edit( + title = "服务", + type = EditType.TAGS, search = @Search(vague = true), notNull = true, + tagsType = @TagsType( + fetchHandler = SqlTagFetchHandler.class, + fetchHandlerParams = "select distinct service from api_monitor_record" + ) + ) + ) + + private String service; + @EruptField( + views = @View(title = "接口名称"), + edit = @Edit(title = "接口名称", notNull = true, search = @Search) + ) + private String api; + + @EruptField( + views = @View(title = "服务URL"), + edit = @Edit(title = "服务URL", notNull = true, search = @Search) + ) + private String path; + + @EruptField( + views = @View(title = "请求头"), + edit = @Edit(title = "请求报文", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String requestHeaders; + + @EruptField( + views = @View(title = "HTTP方法"), + edit = @Edit(title = "HTTP方法", notNull = true, search = @Search) + ) + private String method; + + @EruptField( + views = @View(title = "请求报文", type = ViewType.CODE), + edit = @Edit(title = "请求报文", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String requestBody; + + + @EruptField( + views = @View(title = "response_headers"), + edit = @Edit(title = "responseHeaders", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String responseHeaders; + + @EruptField( + views = @View(title = "status_code"), + edit = @Edit(title = "status_code", notNull = true, search = @Search) + ) + private int statusCode; + + @EruptField( + views = @View(title = "返回报文", type = ViewType.CODE), + edit = @Edit(title = "返回报文", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String responseBody; + + + +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiSpecChangeModel.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiSpecChangeModel.java new file mode 100644 index 0000000..723930d --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiSpecChangeModel.java @@ -0,0 +1,52 @@ +package io.fluent.qtm.api.model; + +import lombok.Data; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Layout; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.jpa.model.BaseModel; + +import javax.persistence.Entity; +import javax.persistence.Table; +import java.time.LocalDateTime; + +@DynamicUpdate +@DynamicInsert +@Entity +@Table(name = "api_spec_change") +@Erupt( + layout = @Layout( + tableLeftFixed = 3, + pageSize = 30), + name = "api spec 变化记录", power = @Power(export = true) +) +@Data +public class ApiSpecChangeModel extends BaseModel { + @EruptField( + views = @View(title = "应用名-appName") + ) + private String name; + @EruptField( + views = @View(title = "GIT URL") + ) + private String gitUrl; + @EruptField( + views = @View(title = "GIT分支") + ) + private String branch; + + @EruptField( + views = @View(title = "创建时间") + ) + private LocalDateTime createdTime; + + @EruptField( + views = @View(title = "appVersion") + ) + private String appVersion; + +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiSpecGitRepoModel.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiSpecGitRepoModel.java new file mode 100644 index 0000000..cb97dfd --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiSpecGitRepoModel.java @@ -0,0 +1,47 @@ +package io.fluent.qtm.api.model; + +import lombok.Data; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Layout; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.Search; +import xyz.erupt.jpa.model.MetaModel; + +import javax.persistence.Entity; +import javax.persistence.Table; + +@Entity +@Table(name = "apispec_git_repo") +@Data +@Erupt(name = "skel仓库设置", layout = @Layout( + tableLeftFixed = 3, + pageSize = 30), + power = @Power(importable = true, export = true)) +public class ApiSpecGitRepoModel extends MetaModel { + @EruptField( + views = @View(title = "应用名-appName"), + edit = @Edit(title = "应用名-App Name", notNull = true, search = @Search) + ) + private String name; + @EruptField( + views = @View(title = "gitUrl"), + edit = @Edit(title = "gitUrl", notNull = true) + ) + private String gitUrl; + + @EruptField( + views = @View(title = "gitlabId"), + edit = @Edit(title = "gitlabId", notNull = true) + ) + private Integer gitlabId; + + @EruptField( + views = @View(title = "webUrl"), + edit = @Edit(title = "webUrl", notNull = true) + ) + private String webUrl; + +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiSpecVersionModel.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiSpecVersionModel.java new file mode 100644 index 0000000..6bd226d --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiSpecVersionModel.java @@ -0,0 +1,102 @@ +package io.fluent.qtm.api.model; + +import io.fluent.base.model.ModelWithValidFlagVo; +import lombok.Data; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.Where; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Layout; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.ViewType; +import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; +import xyz.erupt.annotation.sub_field.sub_edit.InputType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; + +import javax.persistence.Entity; +import javax.persistence.Table; + +@DynamicUpdate +@DynamicInsert +@Entity +@Table(name = "api_spec_version") +@Erupt( + name = "远程服务原始文件", + power = @Power(export = true), + layout = @Layout( + tableLeftFixed = 3, + pageSize = 30) +) +@Data +@SQLDelete(sql = "update api_spec_version set valid=false where id=?") +@Where(clause = "valid = true") +public class ApiSpecVersionModel extends ModelWithValidFlagVo { + @EruptField( + views = @View( + title = "名称" + ), + edit = @Edit( + title = "名称", + type = EditType.INPUT, search = @Search, notNull = true, + inputType = @InputType + ) + ) + private String name; + + + @EruptField( + views = @View( + title = "名称" + ), + edit = @Edit( + title = "名称", + type = EditType.INPUT, search = @Search, notNull = true, + inputType = @InputType + ) + ) + private String type="POSTMAN"; + + @EruptField( + views = @View( + title = "服务类型" + ), + edit = @Edit( + title = "服务类型", + type = EditType.INPUT, search = @Search, notNull = true, + inputType = @InputType + ) + ) + private String serviceType; //API or RPC + + @EruptField( + views = @View( + title = "版本" + ), + edit = @Edit( + title = "版本", + type = EditType.INPUT, search = @Search, notNull = true, + inputType = @InputType + ) + ) + private String appVersion; + + @EruptField( + views = @View(title = "GIT URL") + ) + private String gitUrl; + @EruptField( + views = @View(title = "GIT分支") + ) + private String branch; + + @EruptField( + views = @View(title = "接口定义", type = ViewType.CODE), + edit = @Edit(title = "接口定义", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String spec; +} \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiStep.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiStep.java new file mode 100644 index 0000000..9f26690 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiStep.java @@ -0,0 +1,156 @@ +package io.fluent.qtm.api.model; + +import io.fluent.base.handlers.SqlTagFetchHandler; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Layout; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; +import xyz.erupt.annotation.sub_field.sub_edit.TagsType; +import xyz.erupt.jpa.model.MetaModel; + +import javax.persistence.Entity; +import javax.persistence.Table; + +@DynamicUpdate +@DynamicInsert +@Entity +@Table(name = "api_steps") +@Erupt( + name = "接口测试用例", layout = @Layout( + tableLeftFixed = 3, + pageSize = 30), power = @Power(importable = true, export = true), + orderBy = "ApiTestStep.updateTime desc" +) +public class ApiStep extends MetaModel{ + + @EruptField( + views = @View(title = "场景"), + edit = @Edit(title = "场景", notNull = true, search = @Search) + ) + private String scenario; + + @EruptField( + views = @View(title = "用例名称"), + edit = @Edit(title = "用例名称", notNull = true, search = @Search) + ) + private String caseName; + + @EruptField( + views = @View(title = "服务"), + edit = @Edit( + search = @Search, + title = "获取可选服务", + type = EditType.TAGS, + desc = "获取可选服务", + tagsType = @TagsType( + fetchHandler = SqlTagFetchHandler.class, + fetchHandlerParams = "select distinct service_name from remote_services where type='API' and valid=true" + )) + ) + private String serviceName; + @EruptField( + views = @View(title = "服务方法"), + edit = @Edit(title = "服务方法", notNull = true, search = @Search) + ) + private String serviceMethod; + + @EruptField( + views = @View(title = "接口路径"), + edit = @Edit(title = "接口路径", notNull = true, search = @Search) + ) + private String path; + + @EruptField( +// views = @View(title = "测试请求", type = ViewType.CODE), + edit = @Edit(title = "测试请求", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String request; + @EruptField( +// views = @View(title = "接口请求结果"), + edit = @Edit(title = "接口请求结果", + type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String result; + + + @EruptField( +// views = @View(title = "预期结果"), + edit = @Edit(title = "预期结果", notNull = true, type = EditType.CODE_EDITOR, + codeEditType = @CodeEditorType(language = "json")) + ) + private String expect; + + + public String getScenario() { + return scenario; + } + + public void setScenario(String scenario) { + this.scenario = scenario; + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getServiceMethod() { + return serviceMethod; + } + + public void setServiceMethod(String serviceMethod) { + this.serviceMethod = serviceMethod; + } + + public String getCaseName() { + return caseName; + } + + public void setCaseName(String caseName) { + this.caseName = caseName; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getRequest() { + return request; + } + + public void setRequest(String request) { + this.request = request; + } + + public String getResult() { + return result; + } + + public void setResult(String result) { + this.result = result; + } + + + + public String getExpect() { + return expect; + } + + public void setExpect(String expect) { + this.expect = expect; + } +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiTestRecord.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiTestRecord.java new file mode 100644 index 0000000..e03248c --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiTestRecord.java @@ -0,0 +1,131 @@ +package io.fluent.qtm.api.model; + +import io.fluent.qtm.api.handler.GenerateApiTestStepByApiTestRecord; +import io.fluent.base.handlers.SqlTagFetchHandler; +import lombok.Data; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Layout; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_erupt.RowOperation; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; +import xyz.erupt.annotation.sub_field.sub_edit.TagsType; +import xyz.erupt.jpa.model.MetaModel; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 1. 原先的表结构设计几个问题: + * - 没有办法区分那次测试运行记录 + * - 查找不太方便 + * - name 和service name 重复,没有必要同时使用 + * - serviceName 从remote service里面取不过滤API,不够精确 + * 修改方式: + * - name 修改为测试用例运行名称 + * - serviceName 取tags 从remote service 的API 中取 + */ +@DynamicUpdate +@DynamicInsert +@Entity +@Table(name = "api_test_record") +@Erupt( + name = "接口测试结果", layout = @Layout( + tableLeftFixed = 3, + pageSize = 30), power = @Power(importable = true, export = true), + orderBy = "ApiTestRecord.id desc", + rowOperation = {@RowOperation( + title = "生成可用测试步骤", + operationHandler = GenerateApiTestStepByApiTestRecord.class)} +) +@Data +public class ApiTestRecord extends MetaModel { + @EruptField( + views = @View(title = "测试运行名称"), + edit = @Edit(title = "name", search = @Search) + ) + private String name; + + @EruptField( + views = @View(title = "场景"), + edit = @Edit(title = "场景", notNull = true, search = @Search) + ) + private String scenario; + + @EruptField( + views = @View(title = "用例名称"), + edit = @Edit(title = "用例名称", notNull = true, search = @Search) + ) + private String caseName; + + @EruptField( + views = @View(title = "服务"), + edit = @Edit( + search = @Search, + title = "获取可选服务", + type = EditType.TAGS, + desc = "获取可选服务", + tagsType = @TagsType( + fetchHandler = SqlTagFetchHandler.class, + fetchHandlerParams = "select distinct service_name from remote_services where type='API' and valid=true" + )) + ) + private String serviceName; + @EruptField( + views = @View(title = "服务方法"), + edit = @Edit(title = "服务方法", notNull = true, search = @Search) + ) + private String serviceMethod; + + @EruptField( + views = @View(title = "接口路径"), + edit = @Edit(title = "接口路径", notNull = true, search = @Search) + ) + private String path; + + @EruptField( +// views = @View(title = "测试请求", type = ViewType.CODE), + edit = @Edit(title = "测试请求", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String request; + @EruptField( +// views = @View(title = "接口请求结果"), + edit = @Edit(title = "接口请求结果", + type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String result; + + @EruptField( + views = @View(title = "状态码"), + edit = @Edit(title = "状态码", notNull = true, search = @Search) + ) + private String statusCode; + + @EruptField( + views = @View(title = "错误日志"), + edit = @Edit(title = "错误日志", + type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String errorLog; + + @EruptField( +// views = @View(title = "预期结果"), + edit = @Edit(title = "预期结果", notNull = true, type = EditType.CODE_EDITOR, + codeEditType = @CodeEditorType(language = "json")) + ) + private String expect; + + //TODO: 通过或者失败 + @EruptField( + views = @View(title = "用例执行结果"), + edit = @Edit(title = "用例执行结果", search = @Search) + ) + private boolean isSuccess; + +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiTestScenario.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiTestScenario.java new file mode 100644 index 0000000..eb187f8 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/ApiTestScenario.java @@ -0,0 +1,157 @@ +package io.fluent.qtm.api.model; + +import io.fluent.base.handlers.SqlTagFetchHandler; +import io.fluent.base.model.ModelWithValidFlag; +import io.fluent.base.product.model.ProductModuleModel; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Layout; +import xyz.erupt.annotation.sub_erupt.LinkTree; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; +import xyz.erupt.annotation.sub_field.sub_edit.ReferenceTreeType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; +import xyz.erupt.annotation.sub_field.sub_edit.TagsType; + +import javax.persistence.*; +import java.util.Set; + +/** + * + */ +@Entity +@Erupt(name = "接口测试用例", + power = @Power(export = true), + orderBy = "ApiTestScenario.updateTime desc", + linkTree = @LinkTree(field = "module"), + layout = @Layout( + tableLeftFixed = 3, + pageSize = 30)) +@Table(name = "api_test_scenario") +public class ApiTestScenario extends ModelWithValidFlag { + + @ManyToOne + @JoinColumn(name = "product_id") + @EruptField( + views = @View(title = "所属模块", column = "details"), + edit = @Edit( + notNull = true, + search = @Search, + title = "产品模块选择", + type = EditType.REFERENCE_TREE, + desc = "动态获取产品", + referenceTreeType = @ReferenceTreeType(id = "id", label = "name", + pid = "parent.id")) + ) + private ProductModuleModel module; + + + @EruptField( + views = @View( + title = "测试场景" + ), + edit = @Edit( + title = "测试场景", + type = EditType.INPUT, search = @Search, notNull = true + ) + ) + private String testScenario; + + @EruptField( + views = @View( + title = "测试场景详细描述" + ), + edit = @Edit( + title = "测试场景详细描述", + type = EditType.TEXTAREA, search = @Search, notNull = true + ) + ) + private String details; + + @EruptField( + views = @View( + title = "优先级" + ), + edit = @Edit( + title = "优先级", + type = EditType.TAGS, + search = @Search, + tagsType = @TagsType( + fetchHandler = SqlTagFetchHandler.class, + fetchHandlerParams = "select distinct key,detail from master_data where category_code = 'PRIORITY' order by 1 " + ) + ) + ) + private String priority = "P2"; + + @EruptField( + views = @View(title = "场景参数"), + edit = @Edit(title = "场景参数", + type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String scenarioParameters; + + @JoinTable(name = "api_test_scenario_steps", + joinColumns = @JoinColumn(name = "api_test_scenario_id", referencedColumnName = "id"), + inverseJoinColumns = @JoinColumn(name = "api_test_step_id", referencedColumnName = "id")) + @ManyToMany(fetch = FetchType.EAGER) + @EruptField( + views = @View(title = "包含用例"), + edit = @Edit( + title = "包含用例", + type = EditType.TAB_TABLE_REFER + ) + ) + private Set testSteps; + public String getPriority() { + return priority; + } + + public void setPriority(String priority) { + this.priority = priority; + } + + + public ProductModuleModel getModule() { + return module; + } + + public void setModule(ProductModuleModel module) { + this.module = module; + } + + public String getTestScenario() { + return testScenario; + } + + public void setTestScenario(String testScenario) { + this.testScenario = testScenario; + } + + public String getDetails() { + return details; + } + + public void setDetails(String details) { + this.details = details; + } + + public String getScenarioParameters() { + return scenarioParameters; + } + + public void setScenarioParameters(String scenarioParameters) { + this.scenarioParameters = scenarioParameters; + } + + public Set getTestSteps() { + return testSteps; + } + + public void setTestSteps(Set testSteps) { + this.testSteps = testSteps; + } +} \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/RawApiTestCase.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/RawApiTestCase.java new file mode 100644 index 0000000..9498f09 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/RawApiTestCase.java @@ -0,0 +1,96 @@ +package io.fluent.qtm.api.model; + +import lombok.Data; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Layout; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.ViewType; +import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; +import xyz.erupt.jpa.model.MetaModel; + +import javax.persistence.Entity; +import javax.persistence.Table; + + +@DynamicUpdate +@DynamicInsert +@Entity +@Table(name = "raw_api_cases") +@Erupt( + name = "接口测试用例生成", layout = @Layout( + tableLeftFixed = 3, + pageSize = 30), power = @Power(importable = true, export = true), + orderBy = "RawApiTestCase.createTime " +) +@Data +public class RawApiTestCase extends MetaModel{ + @EruptField( + views = @View(title = "测试场景"), + edit = @Edit(title = "测试场景", search = @Search) + ) + private String scenario; + + @EruptField( + views = @View(title = "服务名称"), + edit = @Edit(title = "服务名称", notNull = true, search = @Search) + ) + private String serviceName; + + @EruptField( + views = @View(title = "API接口"), + edit = @Edit(title = "API接口", notNull = true, search = @Search) + ) + private String serviceMethod; + + @EruptField( + views = @View(title = "用例名称"), + edit = @Edit(title = "用例名称", notNull = true, search = @Search) + ) + private String name; + + @EruptField( + views = @View(title = "请求路径"), + edit = @Edit(title = "请求路径", notNull = true, search = @Search) + ) + private String uri; + + @EruptField( + views = @View(title = "请求方法"), + edit = @Edit(title = "请求方法", notNull = true) + ) + private String method = "POST"; + + @EruptField( + views = @View(title = "输入", type = ViewType.CODE), + edit = @Edit(title = "输入", + type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String input; + + @EruptField( + views = @View(title = "期望结果", type = ViewType.CODE), + edit = @Edit(title = "期望结果", type = EditType.CODE_EDITOR, + codeEditType = @CodeEditorType(language = "json")) + ) + private String expected; + + @EruptField( + views = @View(title = "优先级"), + edit = @Edit(title = "优先级") + ) + private String priority = "P1"; + + @EruptField( + views = @View(title = "是否运行"), + edit = @Edit(title = "是否运行") + ) + private boolean isRun = true; + +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/RemoteApi.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/RemoteApi.java new file mode 100644 index 0000000..84763bd --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/RemoteApi.java @@ -0,0 +1,275 @@ +package io.fluent.qtm.api.model; + +import cn.hutool.core.lang.UUID; +import io.fluent.qtm.api.handler.GenerateRawApiCaseHandler; +import io.fluent.base.handlers.SqlTagFetchHandler; +import io.fluent.base.model.ModelWithValidFlag; +import lombok.Data; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.Where; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Layout; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_erupt.RowOperation; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.ViewType; +import xyz.erupt.annotation.sub_field.sub_edit.*; +import xyz.erupt.toolkit.handler.SqlChoiceFetchHandler; + +import javax.persistence.Entity; +import javax.persistence.Table; + +@DynamicUpdate +@DynamicInsert +@Entity +@Table(name = "remote_services") +@Erupt( + name = "远程服务清单", layout = @Layout( + tableLeftFixed = 3, + pageSize = 30), + power = @Power(export = true), + rowOperation = {@RowOperation( + title = "生成原始接口用例", + operationHandler = GenerateRawApiCaseHandler.class)} +) +@Data +@SQLDelete(sql = "update remote_services set valid=false where id=?") +@Where(clause = "valid = true") +public class RemoteApi extends ModelWithValidFlag { + @EruptField( + views = @View( + title = "名称" + ), + edit = @Edit( + title = "名称", + type = EditType.INPUT, search = @Search, notNull = true, + inputType = @InputType + ) + ) + private String name; + + @EruptField( + views = @View( + title = "产品" + ), + edit = @Edit( + title = "产品", + type = EditType.CHOICE, + desc = "获取产品", + choiceType = @ChoiceType( + fetchHandler = SqlChoiceFetchHandler.class, + fetchHandlerParams = "select id,name,details from products where valid =true and parent_id is NULL" + )) + ) + private Long productId; + + @EruptField( + views = @View(title = "模块名"), + edit = @Edit( + search = @Search, + title = "获取可选模块", + type = EditType.TAGS, + desc = "动态获取可选模块", + tagsType = @TagsType( + fetchHandler = SqlTagFetchHandler.class, + fetchHandlerParams = "select distinct module_name from remote_services where valid=true" + )) + ) + private String moduleName; + + @EruptField( + views = @View(title = "服务"), + edit = @Edit( + search = @Search, + title = "获取可选服务", + type = EditType.TAGS, + desc = "获取可选服务", + tagsType = @TagsType( + fetchHandler = SqlTagFetchHandler.class, + fetchHandlerParams = "select distinct service_name from remote_services where valid=true" + )) + ) + private String serviceName; + + @EruptField( + views = @View(title = "地址"), + edit = @Edit(title = "地址", notNull = true) + ) + private String endpoint; + + @EruptField( + views = @View(title = "方法"), + edit = @Edit(title = "方法", notNull = true, search = @Search) + ) + private String serviceMethod; + + @EruptField( + views = @View(title = "http请求方法"), + edit = @Edit(title = "http请求方法", notNull = true) + ) + private String httpMethod; + + @EruptField( + views = @View(title = "请求报文", type = ViewType.CODE), + edit = @Edit(title = "请求报文", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) + ) + private String body; + + @EruptField( + views = @View(title = "接口类型"), + edit = @Edit(title = "接口类型", search = @Search, + type = EditType.CHOICE, choiceType = @ChoiceType( + vl = {@VL(value = "API", label = "API"), @VL(value = "RPC", label = "RPC")} + )) + ) + private String type; + + @EruptField( + views = @View(title = "服务使用状态"), + edit = @Edit(title = "服务使用状态", + search = @Search, notNull = true, + type = EditType.CHOICE, choiceType = @ChoiceType( + vl = {@VL(value = "NEW", label = "新增"), + @VL(value = "IN_USE", label = "使用中"), + @VL(value = "UPDATE", label = "更新"), + @VL(value = "END_OF_LIFE", label = "已作废"),}))) + private String status; + + @EruptField( + views = @View( + title = "引入时版本" + ), + edit = @Edit( + title = "引入时版本", + type = EditType.INPUT, search = @Search, notNull = true, + inputType = @InputType + ) + ) + private String addedVersion; + + @EruptField( + views = @View( + title = "最新版本" + ), + edit = @Edit( + title = "最新版本", + type = EditType.INPUT, search = @Search, notNull = true, + inputType = @InputType + ) + ) + private String latestVersion; + + @EruptField( + views = @View(show = false, title = "uid") + ) + private String uId = UUID.fastUUID().toString(); + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Long getProductId() { + return productId; + } + + public void setProductId(Long productId) { + this.productId = productId; + } + + public String getModuleName() { + return moduleName; + } + + public void setModuleName(String moduleName) { + this.moduleName = moduleName; + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getEndpoint() { + return endpoint; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public String getServiceMethod() { + return serviceMethod; + } + + public void setServiceMethod(String serviceMethod) { + this.serviceMethod = serviceMethod; + } + + public String getHttpMethod() { + return httpMethod; + } + + public void setHttpMethod(String httpMethod) { + this.httpMethod = httpMethod; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getAddedVersion() { + return addedVersion; + } + + public void setAddedVersion(String addedVersion) { + this.addedVersion = addedVersion; + } + + public String getLatestVersion() { + return latestVersion; + } + + public void setLatestVersion(String latestVersion) { + this.latestVersion = latestVersion; + } + + public String getuId() { + return uId; + } + + public void setuId(String uId) { + this.uId = uId; + } +} \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/RemoteApiStatus.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/RemoteApiStatus.java new file mode 100644 index 0000000..4b66a5c --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/RemoteApiStatus.java @@ -0,0 +1,5 @@ +package io.fluent.qtm.api.model; + +public enum RemoteApiStatus { + NEW,IN_USE,UPDATED,END_OF_LIFE +} \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/RemoteApiType.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/RemoteApiType.java new file mode 100644 index 0000000..74c2557 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/model/RemoteApiType.java @@ -0,0 +1,5 @@ +package io.fluent.qtm.api.model; + +public enum RemoteApiType { + API,RPC +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/package-info.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/package-info.java new file mode 100644 index 0000000..73f4e8e --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/package-info.java @@ -0,0 +1 @@ +package io.fluent.qtm.api; \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiMonitorRecordRepo.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiMonitorRecordRepo.java new file mode 100644 index 0000000..e03552b --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiMonitorRecordRepo.java @@ -0,0 +1,14 @@ +package io.fluent.qtm.api.repo; + +import io.fluent.qtm.api.model.ApiMonitorRecord; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface ApiMonitorRecordRepo extends JpaRepository, JpaSpecificationExecutor { + + List findApiMonitorRecordByPath(String path); +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiSpecChangeRepository.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiSpecChangeRepository.java new file mode 100644 index 0000000..ff3cdf3 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiSpecChangeRepository.java @@ -0,0 +1,11 @@ +package io.fluent.qtm.api.repo; + + +import io.fluent.qtm.api.model.ApiSpecChangeModel; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ApiSpecChangeRepository extends JpaRepository { + +} \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiSpecGitRepoRepository.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiSpecGitRepoRepository.java new file mode 100644 index 0000000..4a6af7a --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiSpecGitRepoRepository.java @@ -0,0 +1,10 @@ +package io.fluent.qtm.api.repo; + +import io.fluent.qtm.api.model.ApiSpecGitRepoModel; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ApiSpecGitRepoRepository extends JpaRepository { + +} \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiSpecVersionRepository.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiSpecVersionRepository.java new file mode 100644 index 0000000..361f79d --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiSpecVersionRepository.java @@ -0,0 +1,10 @@ +package io.fluent.qtm.api.repo; + +import io.fluent.qtm.api.model.ApiSpecVersionModel; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ApiSpecVersionRepository extends JpaRepository { + +} \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiTestScenarioRepo.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiTestScenarioRepo.java new file mode 100644 index 0000000..e389772 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiTestScenarioRepo.java @@ -0,0 +1,10 @@ +package io.fluent.qtm.api.repo; + +import io.fluent.qtm.api.model.ApiTestScenario; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.stereotype.Repository; + +@Repository +public interface ApiTestScenarioRepo extends JpaRepository, JpaSpecificationExecutor { +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiTestStepRepo.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiTestStepRepo.java new file mode 100644 index 0000000..f867291 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/ApiTestStepRepo.java @@ -0,0 +1,10 @@ +package io.fluent.qtm.api.repo; + +import io.fluent.qtm.api.model.ApiStep; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.stereotype.Repository; + +@Repository +public interface ApiTestStepRepo extends JpaRepository, JpaSpecificationExecutor { +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/RawApiTestCaseRepo.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/RawApiTestCaseRepo.java new file mode 100644 index 0000000..fb9e408 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/RawApiTestCaseRepo.java @@ -0,0 +1,10 @@ +package io.fluent.qtm.api.repo; + +import io.fluent.qtm.api.model.RawApiTestCase; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.stereotype.Repository; + +@Repository +public interface RawApiTestCaseRepo extends JpaRepository, JpaSpecificationExecutor { +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/RemoteServiceRepo.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/RemoteServiceRepo.java new file mode 100644 index 0000000..f6f4ad0 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/repo/RemoteServiceRepo.java @@ -0,0 +1,23 @@ +package io.fluent.qtm.api.repo; + +import io.fluent.qtm.api.model.RemoteApi; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface RemoteServiceRepo extends JpaRepository, JpaSpecificationExecutor { + + Optional findRemoteApiByEndpointAndServiceNameAndServiceMethod( + String endpoint,String serviceName,String serviceMethod + ); + + Optional> findRemoteApiByModuleNameAndServiceNameAndLatestVersionNot( + String moduleName,String serviceName,String latestVersion + ); + + +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/service/ApiTestCaseService.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/service/ApiTestCaseService.java new file mode 100644 index 0000000..cd465be --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/service/ApiTestCaseService.java @@ -0,0 +1,107 @@ +package io.fluent.qtm.api.service; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.StrUtil; +import io.fluent.builtin.CollectionsUtils; +import io.fluent.qtm.api.model.*; +import io.fluent.qtm.api.repo.ApiMonitorRecordRepo; +import io.fluent.qtm.api.repo.ApiTestStepRepo; +import io.fluent.qtm.api.repo.RawApiTestCaseRepo; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +@Service +public class ApiTestCaseService { + private final String DEFAULT_EXPECTED = "{ \"status_code\":200,\"values\":{}\n" + + "}"; + @Resource + RawApiTestCaseRepo rawApiTestCaseRepo; + + @Resource + ApiMonitorRecordRepo apiMonitorRecordRepo; + + @Resource + ApiTestStepRepo apiTestStepRepo; + + + /** + * @param services: HTTP API Services + * 1. Merge All HttpAPI Services if method,service and request-body/input is same + */ + public void convertToRawTestCase(List services) { + List cases = new ArrayList<>(); + services.forEach(service -> { + RawApiTestCase apiCase = new RawApiTestCase(); + BeanUtils.copyProperties(service, apiCase); + apiCase.setUri(service.getEndpoint()); + apiCase.setExpected(DEFAULT_EXPECTED); + //get body + String path = service.getEndpoint().replaceAll("https://\\{\\{base_url\\}\\}", ""); + List result = apiMonitorRecordRepo.findApiMonitorRecordByPath(path); + if (!result.isEmpty()) { + apiCase.setInput(result.get(0).getRequestBody()); + } else { + apiCase.setInput(service.getBody()); + } + cases.add(apiCase); + }); + rawApiTestCaseRepo.saveAll(cases); + } + + /** + * @param records Http traffic + * 1. Merge All HttpAPI Services if method,service and request-body/input is same + */ + public void convertApiMonitorRecordToTestCase(List records) { + List cases = new ArrayList<>(); + List result = CollectionsUtils.filterToReduceRedundant( + records, new Function() { + @Override + public String apply(ApiMonitorRecord apiMonitorRecord) { + return StrUtil.join( + "-", apiMonitorRecord.getApi(), apiMonitorRecord.getApp(), + apiMonitorRecord.getService(), apiMonitorRecord.getPath(), + apiMonitorRecord.getMethod(), apiMonitorRecord.getRequestBody() + ); + } + } + ); + result.forEach(service -> { + RawApiTestCase apiCase = new RawApiTestCase(); + BeanUtils.copyProperties(service, apiCase); + apiCase.setUri(service.getPath()); + apiCase.setExpected(DEFAULT_EXPECTED); + apiCase.setServiceMethod(service.getMethod()); + apiCase.setServiceName(service.getService()); + apiCase.setName(service.getService()); + apiCase.setServiceMethod(service.getApi()); + //get body + apiCase.setInput(service.getRequestBody()); + apiCase.setScenario(service.getRecordName()); + cases.add(apiCase); + }); + rawApiTestCaseRepo.saveAll(cases); + } + + /** + * Only Passed Test Scenario could be converted into test steps + * + * @param data + */ + public void convertApiTestResultToApiTestStep(List data) { + List apiTestSteps = new ArrayList<>(); + data.forEach((apiTestRecord) -> { + if (apiTestRecord.isSuccess()) { + apiTestSteps.add(BeanUtil.copyProperties(apiTestRecord, ApiStep.class)); + } else { + throw new RuntimeException("some cases are failed, can't convert to api test step"); + } + }); + apiTestStepRepo.saveAll(apiTestSteps); + } +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/service/RemoteApiService.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/service/RemoteApiService.java new file mode 100644 index 0000000..27c3803 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/api/service/RemoteApiService.java @@ -0,0 +1,142 @@ +package io.fluent.qtm.api.service; + +import cn.hutool.core.bean.BeanUtil; +import io.fluent.postman.PostmanParser; +import io.fluent.postman.model.PostmanCollection; +import io.fluent.postman.model.PostmanItem; +import io.fluent.qtm.api.model.ApiSpecVersionModel; +import io.fluent.qtm.api.model.RemoteApi; +import io.fluent.qtm.api.model.RemoteApiStatus; +import io.fluent.qtm.api.repo.RemoteServiceRepo; +import io.fluent.base.product.model.ProductModuleModel; +import io.fluent.base.product.service.ProductModuleService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.util.List; +import java.util.Optional; + +@Service +@Slf4j +public class RemoteApiService { + + @Autowired + private ProductModuleService productMetaService; + + @Autowired + private RemoteServiceRepo remoteServiceRepo; + + + /** + * 1. 解析postman 文件 + * 2. 确认接口是 + * - 新增:NEW: 当前记录无记录,则更新 + * - 更新:UPDATED: 当前记录中有记录,但是接口定义发生变化,比如请求内容 + * - 使用中,IN_USE: + * - 移除:END_OF_LIFE: 接口不在最新清单中 + * 3. + * + * @param apiSpec + */ + @Transactional + public void apiSpecToApiList(ApiSpecVersionModel apiSpec, String updater) { + ProductModuleModel productMeta = productMetaService.createApiModuleIfNotExist(apiSpec.getName(), updater); + if (apiSpec.getSpec().isEmpty()) return; + PostmanCollection collection = PostmanParser.create().toPostmanCollection(apiSpec.getSpec()); + for (PostmanItem postmanItem : collection.getItem()) { + for (PostmanItem item : postmanItem.getItem()) { + RemoteApi rs = toRemoteApi(apiSpec, productMeta, postmanItem, item); + createOrUpdateRemoteApi(rs, apiSpec); + } + updateStatusToEndOfLife(apiSpec, postmanItem); + } + } + + @Transactional + public void apiSpecsToApiList(List apiSpecs, String updater) { + for (ApiSpecVersionModel apiSpec : apiSpecs) { + try { + this.apiSpecToApiList(apiSpec, updater); + } catch (Exception e) { + log.error("%s-api-failed,error=%s".formatted( + apiSpec.getName(), e.getMessage() + )); + } + } + } + + private RemoteApi toRemoteApi(ApiSpecVersionModel apiSpec, ProductModuleModel productMeta, + PostmanItem postmanItem, PostmanItem item) { + RemoteApi rs = new RemoteApi(); + rs.setName(item.getName()); + rs.setServiceName(postmanItem.getName()); + rs.setServiceMethod(item.getName()); + rs.setBody(item.getRequest().getBody().get("raw").toString()); + rs.setHttpMethod(item.getRequest().getMethod()); + rs.setEndpoint(item.getRequest().getUrl().getRaw()); + rs.setType(apiSpec.getServiceType()); + rs.setModuleName(apiSpec.getName()); + rs.setProductId(productMeta.getParent().getId()); + return rs; + } + + /** + * create or update 基本可以使用同一种方式处理 + * 1. 输入实体 + * 2. 字段检验规则 + * 3. 判断重复确认条件 + * 4. 更新字段处理,保存记录 + * TODO: try to integrate with Feishu + * + * @param newApi + */ + public void createOrUpdateRemoteApi(RemoteApi newApi, ApiSpecVersionModel apiSpec) { + Optional api = remoteServiceRepo.findRemoteApiByEndpointAndServiceNameAndServiceMethod( + newApi.getEndpoint(), + newApi.getServiceName(), + newApi.getServiceMethod()); + if (api.isEmpty()) { + newApi.setStatus(RemoteApiStatus.NEW.toString()); + newApi.setAddedVersion(apiSpec.getAppVersion()); + newApi.setLatestVersion(apiSpec.getAppVersion()); + remoteServiceRepo.save(newApi); + } else { + RemoteApi existApi = api.get(); + BeanUtil.copyProperties(newApi, existApi, "id"); + //TODO: 如何确认接口变更-暂时不确认 + if (existApi.getBody().equalsIgnoreCase(newApi.getBody())) { + existApi.setStatus(RemoteApiStatus.IN_USE.toString()); + existApi.setLatestVersion(apiSpec.getAppVersion()); + } else { + existApi.setStatus(RemoteApiStatus.UPDATED.toString()); + existApi.setLatestVersion(apiSpec.getAppVersion()); + } + remoteServiceRepo.save(existApi); + } + } + + public void updateStatusToEndOfLife(ApiSpecVersionModel apiSpec, PostmanItem postmanItem) { + Optional> apiListOptional = remoteServiceRepo.findRemoteApiByModuleNameAndServiceNameAndLatestVersionNot( + apiSpec.getName(), + postmanItem.getName(), + apiSpec.getAppVersion() + ); + if (apiListOptional.isEmpty()) { + log.info("没有需要删除的数据"); + } else { + List apiList = apiListOptional.get(); + for (RemoteApi api : apiList) { + api.setStatus(RemoteApiStatus.END_OF_LIFE.toString()); + remoteServiceRepo.save(api); + log.info("有需要删除的数据"); + } + + } + } + + +} + + diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/package-info.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/package-info.java new file mode 100644 index 0000000..b8c6392 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/package-info.java @@ -0,0 +1 @@ +package io.fluent.qtm.pm; \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/requirement/FieldOption.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/requirement/FieldOption.java new file mode 100644 index 0000000..d0727ce --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/requirement/FieldOption.java @@ -0,0 +1,158 @@ +package io.fluent.qtm.pm.requirement; + + +import io.fluent.base.model.ModelWithValidFlag; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.fun.ChoiceFetchHandler; +import xyz.erupt.annotation.fun.VLModel; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.ChoiceType; +import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; + +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Erupt(name = "字段选项", + power = @Power(export = true, importable = true), + orderBy = "FieldOption.updateTime desc") +@Table(name = "field_option") +public class FieldOption extends ModelWithValidFlag implements ChoiceFetchHandler { + @EruptField( + views = @View( + title = "名称" + ), + edit = @Edit( + title = "名称",notNull = true + ) + ) + private String name; + @EruptField( + views = @View( + title = "字段code" + ), + edit = @Edit( + title = "字段code" + ) + ) + private String code; + + @EruptField( + views = @View(title = "编辑类型"), + edit = @Edit(title = "编辑类型", + notNull = true, type = EditType.CHOICE, + choiceType = @ChoiceType(type = ChoiceType.Type.RADIO, fetchHandler = FieldOption.class)) + ) + private String type; + + @EruptField( + views = @View( + title = "是否可以为空" + ), + edit = @Edit( + title = "是否可以为空", + type = EditType.BOOLEAN + ) + ) + private boolean notNull; + @EruptField( + views = @View( + title = "字段约束条件" + ), + edit = @Edit( + title = "字段约束条件", + type = EditType.CODE_EDITOR, notNull = true, + codeEditType = @CodeEditorType(language = "text"), + desc = "字段约束条件" + ) + ) + private String constrains; + + @EruptField( + views = @View( + title = "和其他业务关系" + ), + edit = @Edit( + title = "和其他业务关系", + type = EditType.CODE_EDITOR, + codeEditType = @CodeEditorType(language = "text") + ) + ) + private String relatedTo; + + @EruptField( + views = @View(title = "显示顺序", sortable = true), + edit = @Edit(title = "显示顺序", notNull = true) + ) + private Integer sort; + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public boolean isNotNull() { + return notNull; + } + + public void setNotNull(boolean notNull) { + this.notNull = notNull; + } + + public String getConstrains() { + return constrains; + } + + public void setConstrains(String constrains) { + this.constrains = constrains; + } + + public String getRelatedTo() { + return relatedTo; + } + + public void setRelatedTo(String relatedTo) { + this.relatedTo = relatedTo; + } + + public Integer getSort() { + return sort; + } + + public void setSort(Integer sort) { + this.sort = sort; + } + + @Override + public List fetch(String[] params) { + List list = new ArrayList<>(); + for (FieldOptionType value : FieldOptionType.values()) { + list.add(new VLModel(value.name(), value.getDesc())); + } + return list; + } +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/requirement/FieldOptionType.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/requirement/FieldOptionType.java new file mode 100644 index 0000000..69b306b --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/requirement/FieldOptionType.java @@ -0,0 +1,13 @@ +package io.fluent.qtm.pm.requirement; + +import lombok.Getter; + +@Getter +public enum FieldOptionType { + NUMBER("数值"),STRING("字符串"),RELATION("关联"),DATE("日期"),ENUM("枚举"),BOOLEAN("布尔"); + + private String desc; + FieldOptionType(String desc) { + this.desc = desc; + } +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/requirement/RequirementFeature.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/requirement/RequirementFeature.java new file mode 100644 index 0000000..a91ffc6 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/requirement/RequirementFeature.java @@ -0,0 +1,34 @@ +package io.fluent.qtm.pm.requirement; + +import io.fluent.base.model.ModelWithValidFlag; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; + +import javax.persistence.Entity; +import javax.persistence.Table; + +@Entity +@Erupt(name = "需求功能点", + power = @Power(export = true, importable = true), + orderBy = "RequirementFeature.updateTime desc") +@Table(name = "requirement_features") +public class RequirementFeature extends ModelWithValidFlag { + + @EruptField( + views = @View( + title = "业务功能相关说明" + ), + edit = @Edit( + title = "业务功能相关说明", + type = EditType.CODE_EDITOR, notNull = true, + codeEditType = @CodeEditorType(language = "text") + ) + ) + private String feature; + +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/requirement/RequirementType.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/requirement/RequirementType.java new file mode 100644 index 0000000..23f7c44 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/requirement/RequirementType.java @@ -0,0 +1,15 @@ +package io.fluent.qtm.pm.requirement; + +import lombok.Getter; + +@Getter +public enum RequirementType { + CREATE("创建"),UPDATE("更新"),DELETE("删除/归档"),SEARCH("查询"), COMPLEX("复杂业务"), + WORKFLOW("工作流"), + REPORT("报表"),OTHER("其他"); + + private String desc; + RequirementType(String desc) { + this.desc = desc; + } +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/requirement/TestRequirement.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/requirement/TestRequirement.java new file mode 100644 index 0000000..31a5222 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/pm/requirement/TestRequirement.java @@ -0,0 +1,192 @@ +package io.fluent.qtm.pm.requirement; + + +import io.fluent.base.handlers.SqlTagFetchHandler; +import io.fluent.base.model.ModelWithValidFlag; +import io.fluent.base.product.model.ProductModuleModel; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.fun.ChoiceFetchHandler; +import xyz.erupt.annotation.fun.VLModel; +import xyz.erupt.annotation.sub_erupt.LinkTree; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.ChoiceType; +import xyz.erupt.annotation.sub_field.sub_edit.ReferenceTreeType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; +import xyz.erupt.annotation.sub_field.sub_edit.TagsType; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + + +@Entity +@Erupt(name = "测试需求管理", + power = @Power(export = true, importable = true), + orderBy = "TestRequirement.updateTime desc", + linkTree = @LinkTree(field = "module")) +@Table(name = "test_requirements") +public class TestRequirement extends ModelWithValidFlag implements ChoiceFetchHandler { + + @EruptField( + views = @View(title = "需求概述"), + edit = @Edit(title = "需求概述") + ) + private String summary; + + @EruptField( + views = @View(title = "需求类型"), + edit = @Edit(title = "需求类型", + notNull = true, type = EditType.CHOICE, + choiceType = @ChoiceType(type = ChoiceType.Type.RADIO, + fetchHandler = TestRequirement.class)) + ) + private String type; + + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) + @JoinColumn(name = "test_req_id") + @EruptField( + edit = @Edit(title = "功能点", type = EditType.TAB_TABLE_ADD) + ) + private Set features; + + + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) + @JoinColumn(name = "test_req_id") + @OrderBy("sort") + @EruptField( + edit = @Edit(title = "字段管理", type = EditType.TAB_TABLE_ADD) + ) + private Set fieldOptions; + + @ManyToOne + @JoinColumn(name = "product_id") + @EruptField( + views = @View(title = "所属模块", column = "details"), + edit = @Edit( + notNull = true, + search = @Search, + title = "产品模块选择", + type = EditType.REFERENCE_TREE, + desc = "动态获取产品", + referenceTreeType = @ReferenceTreeType(id = "id", label = "name", + pid = "parent.id")) + ) + private ProductModuleModel module; + + @EruptField( + views = @View( + title = "提示词" + ), + edit = @Edit( + title = "提示词" + ) + ) + private String prompts; + + @EruptField( + views = @View( + title = "优先级" + ), + edit = @Edit( + search = @Search, + title = "优先级", + type = EditType.TAGS, + tagsType = @TagsType( + fetchHandler = SqlTagFetchHandler.class, + fetchHandlerParams = "select distinct key,detail from master_data where category_code = 'PRIORITY' order by 1 " + ) + ) + ) + private String priority = "P2"; + + @EruptField( + views = @View( + title = "需求状态" + ), + edit = @Edit( + title = "需求状态" + ) + ) + private String status; + + @Override + public List fetch(String[] params) { + List list = new ArrayList<>(); + for (RequirementType value : RequirementType.values()) { + list.add(new VLModel(value.name(), value.getDesc())); + } + return list; + } + + public String getPriority() { + return priority; + } + + public void setPriority(String priority) { + this.priority = priority; + } + + + public String getPrompts() { + return prompts; + } + + public void setPrompts(String prompts) { + this.prompts = prompts; + } + + + public Set getFieldOptions() { + return fieldOptions; + } + + public void setFieldOptions(Set fieldOptions) { + this.fieldOptions = fieldOptions; + } + + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Set getFeatures() { + return features; + } + + public void setFeatures(Set features) { + this.features = features; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public ProductModuleModel getModule() { + return module; + } + + public void setModule(ProductModuleModel module) { + this.module = module; + } +} \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/dto/TestCaseDTO.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/dto/TestCaseDTO.java new file mode 100644 index 0000000..bc776ff --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/dto/TestCaseDTO.java @@ -0,0 +1,28 @@ +package io.fluent.qtm.tc.dto; + +import com.github.crab2died.annotation.ExcelField; +import lombok.Data; + +@Data +public class TestCaseDTO { + @ExcelField(title = "产品名称") + private String productName; + @ExcelField(title = "模块名称") + private String moduleName; + @ExcelField(title = "功能点") + private String feature; + @ExcelField(title = "用例描述") + private String summary; + @ExcelField(title = "优先级") + private String priority = "P2"; //check it + @ExcelField(title = "用例前提条件") + private String precondition; + @ExcelField(title = "测试步骤") + private String steps; + @ExcelField(title = "期望结果") + private String expectedResult; + + @ExcelField(title = "用例ID") + private String uuid; + +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/handlers/GenerateTestRecordHandler.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/handlers/GenerateTestRecordHandler.java new file mode 100644 index 0000000..cc87109 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/handlers/GenerateTestRecordHandler.java @@ -0,0 +1,49 @@ +package io.fluent.qtm.tc.handlers; + + +import cn.hutool.core.bean.BeanUtil; +import io.fluent.qtm.tc.model.TestCase; +import io.fluent.qtm.tc.model.TestResult; +import io.fluent.qtm.tc.model.TestRun; +import io.fluent.qtm.tc.model.TestScenario; +import io.fluent.qtm.tc.repo.TestResultRepo; + +import org.springframework.stereotype.Service; +import xyz.erupt.annotation.fun.OperationHandler; + + +import javax.annotation.Resource; +import java.util.List; + +@Service +public class GenerateTestRecordHandler implements OperationHandler { + @Resource + private TestResultRepo testResultRepo; + + @Override + public String exec(List data, Void unused, String[] param) { + for (TestRun testRun : data) { + //get all test cases + for (TestCase testCase : testRun.getTestCases()) { + TestResult result = BeanUtil.copyProperties(testCase, TestResult.class); + result.setTestCaseUUID(testCase.getUuid()); + result.setTestRun(testRun); + result.setQaOwner(testRun.getTestOwner()); + testResultRepo.save(result); + } + for (TestScenario tc : testRun.getTestScenarios()) { + for (TestCase testCase : tc.getTestCases()) { + TestResult result = BeanUtil.copyProperties(testCase, TestResult.class); + result.setTestCaseUUID(testCase.getUuid()); + result.setTestRun(testRun); + result.setQaOwner(testRun.getTestOwner()); + result.setTestScenario(tc.getName()); + testResultRepo.save(result); + } + } + } + return null; + } + + +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/model/TestCase.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/model/TestCase.java new file mode 100644 index 0000000..2db1f68 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/model/TestCase.java @@ -0,0 +1,238 @@ +package io.fluent.qtm.tc.model; + +import io.fluent.base.handlers.SqlTagFetchHandler; +import io.fluent.base.model.ModelWithValidFlagVo; +import io.fluent.base.product.model.ProductModuleModel; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Layout; +import xyz.erupt.annotation.sub_erupt.LinkTree; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.*; + +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +/** + * + */ +@Entity +@Erupt(name = "测试用例", + power = @Power(export = true), + orderBy = "TestCase.updateTime desc", + linkTree = @LinkTree(field = "module"),layout = @Layout( + tableLeftFixed = 3, + pageSize = 30)) +@Table(name = "test_cases") +public class TestCase extends ModelWithValidFlagVo { + + @ManyToOne + @JoinColumn(name = "product_id") + @EruptField( + views = @View(title = "所属模块",column = "details"), + edit = @Edit( + notNull = true, + search = @Search, + title = "产品模块选择", + type = EditType.REFERENCE_TREE, + desc = "动态获取产品", + referenceTreeType = @ReferenceTreeType(id = "id", label = "name", + pid = "parent.id")) + ) + private ProductModuleModel module; + + @ManyToOne + @JoinColumn(name = "parent_product_id") + @EruptField( + views = @View(title = "父模块",column = "details"), + edit = @Edit( + notNull = true, + search = @Search, + title = "产品模块选择", + type = EditType.REFERENCE_TREE, + desc = "动态获取产品", + referenceTreeType = @ReferenceTreeType(id = "id", label = "name", + pid = "parent.id")) + ) + private ProductModuleModel parent; + + @ManyToOne + @JoinColumn(name = "root_product_id") + @EruptField( + views = @View(title = "所属产品",column = "details"), + edit = @Edit( + notNull = true, + search = @Search, + title = "产品模块选择", + type = EditType.REFERENCE_TREE, + desc = "动态获取产品", + referenceTreeType = @ReferenceTreeType(id = "id", label = "name", + pid = "parent.id")) + ) + private ProductModuleModel product; + + @EruptField( + views = @View( + title = "功能点" + ), + edit = @Edit( + title = "功能点", + type = EditType.INPUT, search = @Search, notNull = true + ) + ) + private String feature; + @EruptField( + views = @View( + title = "用例描述" + ), + edit = @Edit( + title = "用例描述", + type = EditType.INPUT, notNull = true + ) + ) + private String summary; + + @EruptField( + views = @View( + title = "优先级" + ), + edit = @Edit( + title = "优先级", + type = EditType.TAGS, + search = @Search, + tagsType = @TagsType( + fetchHandler = SqlTagFetchHandler.class, + fetchHandlerParams = "select distinct key,detail from master_data where category_code = 'PRIORITY' order by 1 " + ) + ) + ) + private String priority = "P2"; + + + @EruptField( + views = @View( + title = "测试步骤" + ), + edit = @Edit( + title = "测试步骤", + type = EditType.CODE_EDITOR, notNull = true, + codeEditType = @CodeEditorType(language = "text") + ) + ) + private String steps; + @EruptField( + views = @View( + title = "期望结果" + ), + edit = @Edit( + title = "期望结果", + type = EditType.CODE_EDITOR, + codeEditType = @CodeEditorType(language = "text") + ) + ) + private String expectedResult; + + @EruptField( + views = @View( + title = "用例ID" + ) + ) + private String uuid; + @EruptField( + views = @View( + title = "用例前提条件" + ), + edit = @Edit( + title = "用例前提条件", + type = EditType.CODE_EDITOR, + codeEditType = @CodeEditorType(language = "text") + ) + ) + private String precondition; + + public String getFeature() { + return feature; + } + + public void setFeature(String feature) { + this.feature = feature; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public String getPriority() { + return priority; + } + + public void setPriority(String priority) { + this.priority = priority; + } + + public String getSteps() { + return steps; + } + + public void setSteps(String steps) { + this.steps = steps; + } + + public String getExpectedResult() { + return expectedResult; + } + + public void setExpectedResult(String expectedResult) { + this.expectedResult = expectedResult; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getPrecondition() { + return precondition; + } + + public void setPrecondition(String precondition) { + this.precondition = precondition; + } + + + public ProductModuleModel getModule() { + return module; + } + + public void setModule(ProductModuleModel module) { + this.module = module; + } + + public ProductModuleModel getParent() { + return parent; + } + + public void setParent(ProductModuleModel parent) { + this.parent = parent; + } + + public ProductModuleModel getProduct() { + return product; + } + + public void setProduct(ProductModuleModel product) { + this.product = product; + } +} \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/model/TestResult.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/model/TestResult.java new file mode 100644 index 0000000..63039fb --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/model/TestResult.java @@ -0,0 +1,206 @@ +package io.fluent.qtm.tc.model; + + +import io.fluent.base.model.ModelWithValidFlagVo; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.LinkTree; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; +import xyz.erupt.annotation.sub_field.sub_edit.ReferenceTreeType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; + +import javax.persistence.Entity; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + + +@Table(name = "test_results") +@Entity +@Erupt(name = "测试执行结果", + power = @Power(importable = true, export = true) + , linkTree = @LinkTree(field = "testRun") +) +public class TestResult extends ModelWithValidFlagVo { + @ManyToOne + @EruptField( + views = @View(title = "所属测试安排", column = "name"), + edit = @Edit(title = "所属测试安排", type = EditType.REFERENCE_TREE, + referenceTreeType = @ReferenceTreeType(pid = "parent.id", expandLevel = 2)) + ) + private TestRun testRun; + + @EruptField( + views = @View( + title = "测试场景" + ), + edit = @Edit( + title = "测试场景", + type = EditType.INPUT, search = @Search, notNull = true + ) + ) + private String testScenario; + @EruptField( + views = @View( + title = "功能点" + ), + edit = @Edit( + title = "功能点", + type = EditType.INPUT, search = @Search, notNull = true + ) + ) + private String feature; + @EruptField( + views = @View( + title = "用例描述" + ), + edit = @Edit( + title = "用例描述", + type = EditType.INPUT, notNull = true + ) + ) + private String summary; + @EruptField( + views = @View( + title = "用例优先级" + ), + edit = @Edit( + title = "用例优先级", + type = EditType.INPUT, search = @Search, notNull = true + ) + ) + private String priority = "P2"; //check it + @EruptField( + views = @View( + title = "测试步骤" + ), + edit = @Edit( + title = "测试步骤", + type = EditType.CODE_EDITOR, notNull = true, + codeEditType = @CodeEditorType(language = "text") + ) + ) + private String steps; + @EruptField( + views = @View( + title = "用例期望结果" + ), + edit = @Edit( + title = "用例期望结果", + type = EditType.CODE_EDITOR, notNull = true, + codeEditType = @CodeEditorType(language = "text") + ) + ) + private String expectedResult; + + + @EruptField( + views = @View( + title = "测试测试结果" + ), + edit = @Edit( + title = "测试测试结果", + type = EditType.INPUT, search = @Search, notNull = true + ) + ) + private String qaTestResult; + + + @EruptField( + views = @View( + title = "测试负责人" + ), + edit = @Edit( + title = "测试负责人", + type = EditType.INPUT, search = @Search, notNull = true + ) + ) + private String qaOwner; + + private String testCaseUUID; + + + public String getTestCaseUUID() { + return testCaseUUID; + } + + public void setTestCaseUUID(String testCaseUUID) { + this.testCaseUUID = testCaseUUID; + } + + public String getFeature() { + return feature; + } + + public void setFeature(String feature) { + this.feature = feature; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public String getPriority() { + return priority; + } + + public void setPriority(String priority) { + this.priority = priority; + } + + public String getSteps() { + return steps; + } + + public void setSteps(String steps) { + this.steps = steps; + } + + public String getExpectedResult() { + return expectedResult; + } + + public void setExpectedResult(String expectedResult) { + this.expectedResult = expectedResult; + } + + + public String getQaTestResult() { + return qaTestResult; + } + + public void setQaTestResult(String qaTestResult) { + this.qaTestResult = qaTestResult; + } + + public String getQaOwner() { + return qaOwner; + } + + public void setQaOwner(String qaOwner) { + this.qaOwner = qaOwner; + } + + public TestRun getTestRun() { + return testRun; + } + + public void setTestRun(TestRun testRun) { + this.testRun = testRun; + } + + public String getTestScenario() { + return testScenario; + } + + public void setTestScenario(String testScenario) { + this.testScenario = testScenario; + } +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/model/TestRun.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/model/TestRun.java new file mode 100644 index 0000000..47c9af6 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/model/TestRun.java @@ -0,0 +1,251 @@ +package io.fluent.qtm.tc.model; + + +import io.fluent.base.product.model.ProductModuleModel; +import io.fluent.qtm.tc.handlers.GenerateTestRecordHandler; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_erupt.RowOperation; +import xyz.erupt.annotation.sub_erupt.Tree; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; +import xyz.erupt.annotation.sub_field.sub_edit.BoolType; +import xyz.erupt.annotation.sub_field.sub_edit.ReferenceTreeType; +import xyz.erupt.annotation.sub_field.sub_edit.Search; +import xyz.erupt.jpa.model.MetaModel; + +import javax.persistence.*; +import java.time.LocalDate; +import java.util.Set; + +//TODO: Filter By Status +//TODO: input by Uploaded File or File Sync +@Entity +@Table(name = "test_runs") +@Erupt(name = "测试执行计划", + power = @Power(importable = true, export = true), + tree = @Tree(id = "id", label = "name", pid = "parent.id") + ,rowOperation = {@RowOperation( + title = "生成执行测试用例", + operationHandler = GenerateTestRecordHandler.class)} +) + +public class TestRun extends MetaModel { + + @ManyToOne + @JoinColumn(name = "product_id") + @EruptField( + views = @View(title = "产品名称", column = "name"), + edit = @Edit( + search = @Search, + title = "产品选择", + type = EditType.REFERENCE_TREE, + desc = "动态获取产品", + referenceTreeType = @ReferenceTreeType( + pid = "parent.id")) + ) + private ProductModuleModel product; + + @ManyToOne + @EruptField( + edit = @Edit( + title = "父级测试安排", + type = EditType.REFERENCE_TREE, + referenceTreeType = @ReferenceTreeType(pid = "parent.id") + ) + ) + private TestRun parent; + + + @EruptField( + views = @View( + title = "测试负责人" + ), + edit = @Edit( + title = "测试负责人", + type = EditType.INPUT, search = @Search + ) + ) + private String testOwner; + + @JoinTable(name = "test_run_cases", + joinColumns = @JoinColumn(name = "test_run_id", referencedColumnName = "id"), + inverseJoinColumns = @JoinColumn(name = "test_case_id", referencedColumnName = "id")) + @ManyToMany(fetch = FetchType.EAGER) + @EruptField( + views = @View(title = "包含用例"), + edit = @Edit( + title = "包含用例", + type = EditType.TAB_TABLE_REFER + ) + ) + private Set testCases; + + @JoinTable(name = "test_run_scenario", + joinColumns = @JoinColumn(name = "test_run_id", referencedColumnName = "id"), + inverseJoinColumns = @JoinColumn(name = "test_scenario_id", referencedColumnName = "id")) + @ManyToMany(fetch = FetchType.EAGER) + @EruptField( + views = @View(title = "包含测试场景"), + edit = @Edit( + title = "包含测试场景", + type = EditType.TAB_TABLE_REFER + ) + ) + private Set testScenarios; + + public ProductModuleModel getProduct() { + return product; + } + + public void setProduct(ProductModuleModel product) { + this.product = product; + } + + public String getTestOwner() { + return testOwner; + } + + public void setTestOwner(String testOwner) { + this.testOwner = testOwner; + } + + public Set getTestCases() { + return testCases; + } + + public void setTestCases(Set testCases) { + this.testCases = testCases; + } + + public Set getTestScenarios() { + return testScenarios; + } + + public void setTestScenarios(Set testScenarios) { + this.testScenarios = testScenarios; + } + + public TestRun getParent() { + return parent; + } + + public void setParent(TestRun parent) { + this.parent = parent; + } + + @EruptField( + views = @View( + title = "名称" + ), + edit = @Edit( + title = "名称", + type = EditType.INPUT, search = @Search, notNull = true + ) + ) + private String name; + @EruptField( + views = @View( + title = "详细" + ), + edit = @Edit( + title = "详细", + type = EditType.INPUT, search = @Search, notNull = true + ) + ) + private String detail; + @EruptField( + views = @View( + title = "开始时间" + ), + edit = @Edit( + title = "开始时间", + type = EditType.DATE, search = @Search, + boolType = @BoolType + ) + ) + private LocalDate startDate; + @EruptField( + views = @View( + title = "预计完成时间" + ), + edit = @Edit( + title = "预计完成时间", + type = EditType.DATE, search = @Search, + boolType = @BoolType + ) + ) + private LocalDate estimatedCompletedDate; + @EruptField( + views = @View( + title = "完成时间" + ), + edit = @Edit( + title = "完成时间", + type = EditType.DATE, search = @Search, + boolType = @BoolType + ) + ) + private LocalDate completedDate; + @EruptField( + views = @View( + title = "当前状态" + ), + edit = @Edit( + title = "当前状态", + type = EditType.INPUT, search = @Search, notNull = true, + boolType = @BoolType + ) + ) + private String status; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDetail() { + return detail; + } + + public void setDetail(String detail) { + this.detail = detail; + } + + public LocalDate getStartDate() { + return startDate; + } + + public void setStartDate(LocalDate startDate) { + this.startDate = startDate; + } + + public LocalDate getEstimatedCompletedDate() { + return estimatedCompletedDate; + } + + public void setEstimatedCompletedDate(LocalDate estimatedCompletedDate) { + this.estimatedCompletedDate = estimatedCompletedDate; + } + + public LocalDate getCompletedDate() { + return completedDate; + } + + public void setCompletedDate(LocalDate completedDate) { + this.completedDate = completedDate; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/model/TestScenario.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/model/TestScenario.java new file mode 100644 index 0000000..58478ab --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/model/TestScenario.java @@ -0,0 +1,43 @@ +package io.fluent.qtm.tc.model; + +import io.fluent.base.model.NamedModelVO; +import xyz.erupt.annotation.Erupt; +import xyz.erupt.annotation.EruptField; +import xyz.erupt.annotation.sub_erupt.Power; +import xyz.erupt.annotation.sub_field.Edit; +import xyz.erupt.annotation.sub_field.EditType; +import xyz.erupt.annotation.sub_field.View; + +import javax.persistence.*; +import java.util.Set; + +@Entity +@Table(name = "test_scenarios") +@Erupt(name = "测试场景管理", + power = @Power(importable = true, export = true) +) +//@PreDataProxy(value= TestScenarioCaseProxy.class) + +public class TestScenario extends NamedModelVO { + + @JoinTable(name = "test_scenario_cases", + joinColumns = @JoinColumn(name = "test_scenario_id", referencedColumnName = "id"), + inverseJoinColumns = @JoinColumn(name = "test_case_id", referencedColumnName = "id")) + @ManyToMany(fetch = FetchType.EAGER) + @EruptField( + views = @View(title = "包含用例"), + edit = @Edit( + title = "包含用例", + type = EditType.TAB_TABLE_REFER + ) + ) + private Set testCases; + + public Set getTestCases() { + return testCases; + } + + public void setTestCases(Set testCases) { + this.testCases = testCases; + } +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/package-info.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/package-info.java new file mode 100644 index 0000000..0f28f0f --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/package-info.java @@ -0,0 +1 @@ +package io.fluent.qtm.tc; \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/repo/TestCaseRepo.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/repo/TestCaseRepo.java new file mode 100644 index 0000000..354138a --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/repo/TestCaseRepo.java @@ -0,0 +1,15 @@ +package io.fluent.qtm.tc.repo; + +import io.fluent.qtm.tc.model.TestCase; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface TestCaseRepo extends JpaRepository { + public TestCase findByUuid(String uuid); + + +// @Query(nativeQuery = true,value = "delete from test_cases where test_plan=:testPlan") +// @Modifying +// public void deleteByTestPlan(@Param("testPlan") String testPlan); +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/repo/TestResultRepo.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/repo/TestResultRepo.java new file mode 100644 index 0000000..570f55a --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/repo/TestResultRepo.java @@ -0,0 +1,11 @@ +package io.fluent.qtm.tc.repo; + + +import io.fluent.qtm.tc.model.TestResult; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository(value = "testResultRepo") +public interface TestResultRepo extends JpaRepository { + +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/repo/TestRunRepo.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/repo/TestRunRepo.java new file mode 100644 index 0000000..f84b76c --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/repo/TestRunRepo.java @@ -0,0 +1,11 @@ +package io.fluent.qtm.tc.repo; + + +import io.fluent.qtm.tc.model.TestRun; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface TestRunRepo extends JpaRepository { + +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/service/TestCaseService.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/service/TestCaseService.java new file mode 100644 index 0000000..d4cda3f --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/service/TestCaseService.java @@ -0,0 +1,14 @@ +package io.fluent.qtm.tc.service; + +import io.fluent.base.product.model.ProductModuleModel; +import io.fluent.qtm.tc.dto.TestCaseDTO; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public interface TestCaseService { + + public void saveTestCases(List cases, + ProductModuleModel product, ProductModuleModel module,String updater); +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/service/impl/MindMappingService.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/service/impl/MindMappingService.java new file mode 100644 index 0000000..28dad0b --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/service/impl/MindMappingService.java @@ -0,0 +1,41 @@ +package io.fluent.qtm.tc.service.impl; + + + +import cn.hutool.core.bean.BeanUtil; +import io.fluent.base.product.model.ProductModuleModel; +import io.fluent.qtm.tc.dto.TestCaseDTO; +import io.fluent.qtm.tc.service.TestCaseService; +import io.fluent.mindmap.api.MindMapAccessor; +import org.springframework.stereotype.Service; +import xyz.erupt.jpa.model.MetaModel; + +import javax.annotation.Resource; +import javax.transaction.Transactional; +import java.util.List; + +/** + * 1.Import MindMapping file to test case database + * 2.export selected test cases as mindmapping file + */ +//TODO: convert to same TestCase Converter +@Service("mindMappingService") +public class MindMappingService { + + @Resource + private TestCaseService testCaseService; + + public List toTestCaseModel(String xmlFilePath) { + MindMapAccessor accessor = new MindMapAccessor(); + return accessor.readMindMapToBean(xmlFilePath, TestCaseDTO.class); + } + + @Transactional + public void saveTestCases(String xmlFilePath, MetaModel model) { + List testCaseModels = toTestCaseModel(xmlFilePath); + ProductModuleModel product = BeanUtil.getProperty(model, "product"); + ProductModuleModel module = BeanUtil.getProperty(model, "module"); + testCaseService.saveTestCases(testCaseModels,product,module,model.getUpdateBy()); + } + +} diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/service/impl/TestCaseServiceImpl.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/service/impl/TestCaseServiceImpl.java new file mode 100644 index 0000000..7fdb66d --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/tc/service/impl/TestCaseServiceImpl.java @@ -0,0 +1,94 @@ +package io.fluent.qtm.tc.service.impl; + + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.lang.UUID; +import cn.hutool.core.util.StrUtil; +import io.fluent.base.proxies.AuditDataEnhancerProxy; +import io.fluent.base.product.model.ProductModuleModel; +import io.fluent.base.product.service.ProductModuleService; +import io.fluent.qtm.tc.dto.TestCaseDTO; +import io.fluent.qtm.tc.model.TestCase; +import io.fluent.qtm.tc.repo.TestCaseRepo; +import io.fluent.qtm.tc.service.TestCaseService; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import javax.transaction.Transactional; +import java.util.List; + +@Service +public class TestCaseServiceImpl implements TestCaseService { + @Resource + private TestCaseRepo testCaseRepo; + @Resource + private ProductModuleService productMetaService; + + @Resource + private AuditDataEnhancerProxy dataEnhancerProxy; + @Override + @Transactional + @Async + /** + * notice:parent Product can't be created,parent product must be configured + * 1. 如果UUID没有或者找不到,则新增测试用例 + * 2. 新增测试用例中, + */ + public void saveTestCases(List cases, + ProductModuleModel parentProduct, + ProductModuleModel module,String updater) { + for (TestCaseDTO aCase : cases) { + TestCase tcEntity = createOrUseExistingTestCase(aCase); + ProductModuleModel rootProduct = getRootProductMeta(aCase); + ProductModuleModel parentModule = productMetaService.createModuleIfNotExist(rootProduct.getId(), + aCase.getModuleName(),updater); + ProductModuleModel subModule = whichSubModule(parentModule, aCase,updater); + tcEntity.setModule(subModule); + tcEntity.setProduct(rootProduct); + tcEntity.setParent(parentModule); + if (StrUtil.isBlankIfStr(aCase.getPriority())) { + tcEntity.setPriority("P2"); + } + tcEntity.setSteps(StrUtil.join(":\n", aCase.getFeature(), + aCase.getSummary(), aCase.getSteps())); + + testCaseRepo.save(tcEntity); + } + } + + private TestCase createOrUseExistingTestCase(TestCaseDTO aCase) { + TestCase tcEntity; + if (StrUtil.isBlank(aCase.getUuid())) { + tcEntity = BeanUtil.copyProperties(aCase, TestCase.class); + tcEntity.setUuid(UUID.fastUUID().toString(true)); + + } else { + tcEntity = testCaseRepo.findByUuid(aCase.getUuid()); + if (tcEntity == null) { + tcEntity = BeanUtil.copyProperties(aCase, TestCase.class); //生成新的的UUID + } else { + BeanUtil.copyProperties(aCase, tcEntity, "id"); //更新数据库数据 + } + } + return tcEntity; + } + + + private ProductModuleModel getRootProductMeta(TestCaseDTO aCase) { + ProductModuleModel rootProductMeta = productMetaService.findByName(aCase.getProductName()); + if (rootProductMeta == null) { + throw new RuntimeException("找不到产品"); + } + return rootProductMeta; + } + + + private ProductModuleModel whichSubModule(ProductModuleModel parentProduct, TestCaseDTO aCase,String updater) { + if (parentProduct.getName().equalsIgnoreCase(aCase.getModuleName())) return parentProduct; + return productMetaService.createModuleIfNotExist(parentProduct.getId(), aCase.getModuleName(),updater); + + } + + +} diff --git a/fluent-apps/qam/qtm/upload/model/UploadFileModel.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/upload/model/UploadFileModel.java similarity index 95% rename from fluent-apps/qam/qtm/upload/model/UploadFileModel.java rename to fluent-apps/workspace/src/main/java/io/fluent/qtm/upload/model/UploadFileModel.java index cb29089..bf185b0 100644 --- a/fluent-apps/qam/qtm/upload/model/UploadFileModel.java +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/upload/model/UploadFileModel.java @@ -1,7 +1,7 @@ -package io.fluent.base.upload.model; +package io.fluent.qtm.upload.model; -import io.fluent.base.upload.proxy.UploadFileDataProxy; import io.fluent.base.product.model.ProductModuleModel; +import io.fluent.qtm.upload.proxy.UploadFileDataProxy; import lombok.Data; import xyz.erupt.annotation.Erupt; import xyz.erupt.annotation.EruptField; diff --git a/fluent-apps/workspace/src/main/java/io/fluent/qtm/upload/package-info.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/upload/package-info.java new file mode 100644 index 0000000..3a084f9 --- /dev/null +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/upload/package-info.java @@ -0,0 +1 @@ +package io.fluent.qtm.upload; \ No newline at end of file diff --git a/fluent-apps/qam/qtm/upload/proxy/UploadFileDataProxy.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/upload/proxy/UploadFileDataProxy.java similarity index 98% rename from fluent-apps/qam/qtm/upload/proxy/UploadFileDataProxy.java rename to fluent-apps/workspace/src/main/java/io/fluent/qtm/upload/proxy/UploadFileDataProxy.java index db927e0..193c7ca 100644 --- a/fluent-apps/qam/qtm/upload/proxy/UploadFileDataProxy.java +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/upload/proxy/UploadFileDataProxy.java @@ -1,4 +1,4 @@ -package io.fluent.base.upload.proxy; +package io.fluent.qtm.upload.proxy; diff --git a/fluent-apps/qam/qtm/upload/proxy/UploadFileTypeEnum.java b/fluent-apps/workspace/src/main/java/io/fluent/qtm/upload/proxy/UploadFileTypeEnum.java similarity index 92% rename from fluent-apps/qam/qtm/upload/proxy/UploadFileTypeEnum.java rename to fluent-apps/workspace/src/main/java/io/fluent/qtm/upload/proxy/UploadFileTypeEnum.java index 8a44582..7854549 100644 --- a/fluent-apps/qam/qtm/upload/proxy/UploadFileTypeEnum.java +++ b/fluent-apps/workspace/src/main/java/io/fluent/qtm/upload/proxy/UploadFileTypeEnum.java @@ -1,4 +1,4 @@ -package io.fluent.base.upload.proxy; +package io.fluent.qtm.upload.proxy; public enum UploadFileTypeEnum { EXCEL_TC,FREEMIND,PM,MINDMAP; From ed81be2f7453f768419cc5f58a85917decf560bf Mon Sep 17 00:00:00 2001 From: patrick Date: Thu, 20 Mar 2025 11:54:16 +0800 Subject: [PATCH 5/5] cleanup files --- README.md | 5 +- docs/0-setup/README.md | 4 +- fluent-apps/pom.xml | 2 +- fluent-apps/qam/pom.xml | 151 ---------- .../qam/src/main/java/io/fluent/QAMApp.java | 21 -- .../base/FluentProductConfigModule.java | 67 ----- .../io/fluent/base/FluentUploadTCModule.java | 53 ---- .../src/main/java/io/fluent/base/README.md | 8 - .../base/masterdata/model/MasterData.java | 76 ----- .../base/masterdata/repo/MasterDataRepo.java | 15 - .../java/io/fluent/base/package-info.java | 1 - .../product/model/ProductModuleModel.java | 140 --------- .../model/ProductModuleValidFlagVo.java | 47 --- .../io/fluent/base/product/package-info.java | 1 - .../base/product/repo/ProductModuleRepo.java | 18 -- .../product/service/ProductModuleService.java | 57 ---- .../base/project/model/ProjectModel.java | 89 ------ .../base/project/repo/ProjectModelRepo.java | 18 -- .../base/upload/model/UploadFileModel.java | 62 ---- .../io/fluent/base/upload/package-info.java | 1 - .../upload/proxy/UploadFileDataProxy.java | 64 ---- .../base/upload/proxy/UploadFileTypeEnum.java | 14 - .../java/io/fluent/qtm/FluentQAApiModule.java | 125 -------- .../GenerateApiCaseByCaptureDataHandler.java | 24 -- .../GenerateApiTestStepByApiTestRecord.java | 24 -- .../handler/GenerateRawApiCaseHandler.java | 22 -- .../qtm/api/model/ApiMonitorRecord.java | 131 --------- .../qtm/api/model/ApiSpecChangeModel.java | 52 ---- .../qtm/api/model/ApiSpecGitRepoModel.java | 47 --- .../qtm/api/model/ApiSpecVersionModel.java | 102 ------- .../java/io/fluent/qtm/api/model/ApiStep.java | 156 ---------- .../fluent/qtm/api/model/ApiTestRecord.java | 131 --------- .../fluent/qtm/api/model/ApiTestScenario.java | 157 ---------- .../fluent/qtm/api/model/RawApiTestCase.java | 96 ------ .../io/fluent/qtm/api/model/RemoteApi.java | 275 ------------------ .../fluent/qtm/api/model/RemoteApiStatus.java | 5 - .../fluent/qtm/api/model/RemoteApiType.java | 5 - .../java/io/fluent/qtm/api/package-info.java | 1 - .../qtm/api/repo/ApiMonitorRecordRepo.java | 14 - .../qtm/api/repo/ApiSpecChangeRepository.java | 11 - .../api/repo/ApiSpecGitRepoRepository.java | 10 - .../api/repo/ApiSpecVersionRepository.java | 10 - .../qtm/api/repo/ApiTestScenarioRepo.java | 10 - .../fluent/qtm/api/repo/ApiTestStepRepo.java | 10 - .../qtm/api/repo/RawApiTestCaseRepo.java | 10 - .../qtm/api/repo/RemoteServiceRepo.java | 23 -- .../qtm/api/service/ApiTestCaseService.java | 107 ------- .../qtm/api/service/RemoteApiService.java | 142 --------- .../java/io/fluent/qtm/pm/package-info.java | 1 - .../qtm/pm/requirement/FieldOption.java | 158 ---------- .../qtm/pm/requirement/FieldOptionType.java | 13 - .../pm/requirement/RequirementFeature.java | 34 --- .../qtm/pm/requirement/RequirementType.java | 15 - .../qtm/pm/requirement/TestRequirement.java | 192 ------------ .../io/fluent/qtm/tc/dto/TestCaseDTO.java | 28 -- .../handlers/GenerateTestRecordHandler.java | 49 ---- .../java/io/fluent/qtm/tc/model/TestCase.java | 238 --------------- .../io/fluent/qtm/tc/model/TestResult.java | 206 ------------- .../java/io/fluent/qtm/tc/model/TestRun.java | 251 ---------------- .../io/fluent/qtm/tc/model/TestScenario.java | 43 --- .../java/io/fluent/qtm/tc/package-info.java | 1 - .../io/fluent/qtm/tc/repo/TestCaseRepo.java | 15 - .../io/fluent/qtm/tc/repo/TestResultRepo.java | 11 - .../io/fluent/qtm/tc/repo/TestRunRepo.java | 11 - .../qtm/tc/service/TestCaseService.java | 14 - .../tc/service/impl/MindMappingService.java | 41 --- .../tc/service/impl/TestCaseServiceImpl.java | 94 ------ .../src/main/resources/application-dev.yaml | 111 ------- .../qam/src/main/resources/application.yaml | 3 - .../qam/src/main/resources/public/app.css | 24 -- .../qam/src/main/resources/public/app.js | 71 ----- .../qam/src/main/resources/public/home.html | 12 - .../qam/src/main/resources/tpl/operation.tpl | 17 -- fluent-apps/workspace/pom.xml | 133 ++++++++- .../java/io/fluent/WorkspaceApplication.java | 4 +- pom.xml | 2 +- 76 files changed, 141 insertions(+), 4265 deletions(-) delete mode 100644 fluent-apps/qam/pom.xml delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/QAMApp.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/base/FluentProductConfigModule.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/base/FluentUploadTCModule.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/base/README.md delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/base/masterdata/model/MasterData.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/base/masterdata/repo/MasterDataRepo.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/base/package-info.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/base/product/model/ProductModuleModel.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/base/product/model/ProductModuleValidFlagVo.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/base/product/package-info.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/base/product/repo/ProductModuleRepo.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/base/product/service/ProductModuleService.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/base/project/model/ProjectModel.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/base/project/repo/ProjectModelRepo.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/base/upload/model/UploadFileModel.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/base/upload/package-info.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/base/upload/proxy/UploadFileDataProxy.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/base/upload/proxy/UploadFileTypeEnum.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/FluentQAApiModule.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/handler/GenerateApiCaseByCaptureDataHandler.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/handler/GenerateApiTestStepByApiTestRecord.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/handler/GenerateRawApiCaseHandler.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiMonitorRecord.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiSpecChangeModel.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiSpecGitRepoModel.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiSpecVersionModel.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiStep.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiTestRecord.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiTestScenario.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RawApiTestCase.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RemoteApi.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RemoteApiStatus.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RemoteApiType.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/package-info.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiMonitorRecordRepo.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiSpecChangeRepository.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiSpecGitRepoRepository.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiSpecVersionRepository.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiTestScenarioRepo.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiTestStepRepo.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/RawApiTestCaseRepo.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/RemoteServiceRepo.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/service/ApiTestCaseService.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/api/service/RemoteApiService.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/pm/package-info.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/FieldOption.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/FieldOptionType.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/RequirementFeature.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/RequirementType.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/TestRequirement.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/tc/dto/TestCaseDTO.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/tc/handlers/GenerateTestRecordHandler.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestCase.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestResult.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestRun.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestScenario.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/tc/package-info.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/tc/repo/TestCaseRepo.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/tc/repo/TestResultRepo.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/tc/repo/TestRunRepo.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/tc/service/TestCaseService.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/tc/service/impl/MindMappingService.java delete mode 100644 fluent-apps/qam/src/main/java/io/fluent/qtm/tc/service/impl/TestCaseServiceImpl.java delete mode 100644 fluent-apps/qam/src/main/resources/application-dev.yaml delete mode 100644 fluent-apps/qam/src/main/resources/application.yaml delete mode 100644 fluent-apps/qam/src/main/resources/public/app.css delete mode 100644 fluent-apps/qam/src/main/resources/public/app.js delete mode 100644 fluent-apps/qam/src/main/resources/public/home.html delete mode 100644 fluent-apps/qam/src/main/resources/tpl/operation.tpl diff --git a/README.md b/README.md index dfbcc13..d63df1b 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,11 @@ API Automation Testing Platform Requirements: Build A [restack](https://www.restack.io/) like toolkits but for Software QA. +## Just Work QA Testing Management System -## Features - +## Just Work API Recorder +## Features FluentQA Workspace project is JAVA Project includes: diff --git a/docs/0-setup/README.md b/docs/0-setup/README.md index c6201c1..1e2c8b8 100644 --- a/docs/0-setup/README.md +++ b/docs/0-setup/README.md @@ -10,4 +10,6 @@ ## JAVA MAVEN Project Setup With Erupts -## Run it \ No newline at end of file +- [] Create Main Entry Point Class +- [] Create App Configuration file +- [] Some Page \ No newline at end of file diff --git a/fluent-apps/pom.xml b/fluent-apps/pom.xml index 205d53d..4db3ace 100644 --- a/fluent-apps/pom.xml +++ b/fluent-apps/pom.xml @@ -13,7 +13,7 @@ pom pcinfo - qam + workspace diff --git a/fluent-apps/qam/pom.xml b/fluent-apps/qam/pom.xml deleted file mode 100644 index 5355043..0000000 --- a/fluent-apps/qam/pom.xml +++ /dev/null @@ -1,151 +0,0 @@ - - - 4.0.0 - - io.fluentqa - fluent-apps - 1.0-SNAPSHOT - - - qam - - - 17 - 17 - UTF-8 - - - - - - xyz.erupt - erupt-upms - ${erupt.version} - - - - xyz.erupt - erupt-security - ${erupt.version} - - - xyz.erupt - erupt-job - ${erupt.version} - - - io.fluent - fluent-generator - ${fluent.version} - - - - - xyz.erupt - erupt-web - ${erupt.version} - - - org.springframework.boot - spring-boot-starter-tomcat - - - - - org.springframework.boot - spring-boot-starter-undertow - 2.7.12 - - - org.postgresql - postgresql - ${postgresql.version} - - - javax.xml.bind - jaxb-api - 2.3.1 - - - com.github.xiaoymin - knife4j-openapi2-spring-boot-starter - 4.4.0 - - - - io.fluent - fluent-excel - ${fluent.version} - - - io.fluent - fluent-mindmap - ${fluent.version} - - - io.fluent - fluent-erupts-base - ${fluent.version} - - - - io.fluent - fleunt-github - ${fluent.version} - - - - cn.hutool - hutool-all - - - io.fluent - fluent-quickdao - 1.0-SNAPSHOT - - - io.fluent - fluent-openapi - 1.0-SNAPSHOT - - - - - - - - - - - - - - - - - maven-compiler-plugin - org.apache.maven.plugins - 3.11.0 - - 17 - 17 - - - - org.springframework.boot - spring-boot-maven-plugin - 2.7.2 - - - - repackage - - - - - - - - \ No newline at end of file diff --git a/fluent-apps/qam/src/main/java/io/fluent/QAMApp.java b/fluent-apps/qam/src/main/java/io/fluent/QAMApp.java deleted file mode 100644 index 35eb484..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/QAMApp.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.fluent; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.domain.EntityScan; -import org.springframework.scheduling.annotation.EnableAsync; -import xyz.erupt.core.annotation.EruptScan; - -@SpringBootApplication -@EnableAsync -@EruptScan -@EntityScan -public class QAMApp { - /** - * QA Management Application Entrypoint - * @param args - */ - public static void main(String[] args) { - SpringApplication.run(QAMApp.class); - } -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/base/FluentProductConfigModule.java b/fluent-apps/qam/src/main/java/io/fluent/base/FluentProductConfigModule.java deleted file mode 100644 index c384967..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/base/FluentProductConfigModule.java +++ /dev/null @@ -1,67 +0,0 @@ -package io.fluent.base; - -import io.fluent.base.masterdata.model.MasterData; -import io.fluent.base.product.model.ProductModuleModel; -import io.fluent.base.project.model.ProjectModel; -import io.fluent.base.upload.model.UploadFileModel; -import org.apache.commons.math3.stat.descriptive.summary.Product; -import org.springframework.boot.autoconfigure.domain.EntityScan; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import xyz.erupt.core.annotation.EruptScan; -import xyz.erupt.core.constant.MenuTypeEnum; -import xyz.erupt.core.module.EruptModule; -import xyz.erupt.core.module.EruptModuleInvoke; -import xyz.erupt.core.module.MetaMenu; -import xyz.erupt.core.module.ModuleInfo; - -import java.util.ArrayList; -import java.util.List; - -@Configuration -@ComponentScan -@EntityScan -@EruptScan -@EnableConfigurationProperties -public class FluentProductConfigModule implements EruptModule { - - public FluentProductConfigModule() { - } - - @Override - public ModuleInfo info() { - return ModuleInfo.builder().name("fluent-product").build(); - } - - @Override - public void run() { - EruptModule.super.run(); - } - - @Override - public List initMenus() { - List menus = new ArrayList<>(); - menus.add(MetaMenu.createRootMenu("$fluent-master", "产品配置", "fa fa-product-hunt", 90)); - MetaMenu productMetaMenu = MetaMenu.createEruptClassMenu(ProductModuleModel.class, menus.get(0), 0, MenuTypeEnum.TABLE); - productMetaMenu.setIcon("fa fa-group"); - productMetaMenu.setName("产品元数据"); - productMetaMenu.setCode("$product-meta"); - menus.add(productMetaMenu); - MetaMenu masterDataMenu = MetaMenu.createEruptClassMenu(MasterData.class, menus.get(0), 1, MenuTypeEnum.TABLE); - masterDataMenu.setIcon("fa fa-times"); - masterDataMenu.setName("产品字典表配置"); - masterDataMenu.setCode("$master-data"); - menus.add(masterDataMenu); - MetaMenu projectMenu = MetaMenu.createEruptClassMenu(ProjectModel.class, menus.get(0), 2, MenuTypeEnum.TABLE); - projectMenu.setIcon("fa fa-linode"); - projectMenu.setName("项目配置"); - projectMenu.setCode("$project-meta"); - menus.add(projectMenu); - return menus; - } - - static { - EruptModuleInvoke.addEruptModule(FluentProductConfigModule.class); - } -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/base/FluentUploadTCModule.java b/fluent-apps/qam/src/main/java/io/fluent/base/FluentUploadTCModule.java deleted file mode 100644 index 52e5b7d..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/base/FluentUploadTCModule.java +++ /dev/null @@ -1,53 +0,0 @@ -package io.fluent.base; - -import io.fluent.base.upload.model.UploadFileModel; -import org.springframework.boot.autoconfigure.domain.EntityScan; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import xyz.erupt.core.annotation.EruptScan; -import xyz.erupt.core.constant.MenuTypeEnum; -import xyz.erupt.core.module.EruptModule; -import xyz.erupt.core.module.EruptModuleInvoke; -import xyz.erupt.core.module.MetaMenu; -import xyz.erupt.core.module.ModuleInfo; - -import java.util.ArrayList; -import java.util.List; - -@Configuration -@ComponentScan -@EntityScan -@EruptScan -@EnableConfigurationProperties -public class FluentUploadTCModule implements EruptModule { - - public FluentUploadTCModule() { - } - - @Override - public ModuleInfo info() { - return ModuleInfo.builder().name("fluent-tc-sync").build(); - } - - @Override - public void run() { - EruptModule.super.run(); - } - - @Override - public List initMenus() { - List menus = new ArrayList<>(); - menus.add(MetaMenu.createRootMenu("$tc-upload", "测试文件管理", "fa fa-file", 100)); - MetaMenu tfUploadSyncMenu = MetaMenu.createEruptClassMenu(UploadFileModel.class, menus.get(0), 0, MenuTypeEnum.TABLE); - tfUploadSyncMenu.setIcon("fa fa-folder-open"); - tfUploadSyncMenu.setName("测试文件同步"); - tfUploadSyncMenu.setCode("$tc-upload-sync"); - menus.add(tfUploadSyncMenu); - return menus; - } - - static { - EruptModuleInvoke.addEruptModule(FluentUploadTCModule.class); - } -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/base/README.md b/fluent-apps/qam/src/main/java/io/fluent/base/README.md deleted file mode 100644 index 9fb066c..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/base/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# README - -shared component: - -- master data: shared configurations -- upload data: upload component -- product: product module -- project: project module \ No newline at end of file diff --git a/fluent-apps/qam/src/main/java/io/fluent/base/masterdata/model/MasterData.java b/fluent-apps/qam/src/main/java/io/fluent/base/masterdata/model/MasterData.java deleted file mode 100644 index e8d0150..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/base/masterdata/model/MasterData.java +++ /dev/null @@ -1,76 +0,0 @@ -package io.fluent.base.masterdata.model; - -import io.fluent.base.handlers.SqlTagFetchHandler; -import io.fluent.base.model.ModelWithValidFlagVo; -import lombok.Data; -import xyz.erupt.annotation.Erupt; -import xyz.erupt.annotation.EruptField; -import xyz.erupt.annotation.sub_erupt.Power; -import xyz.erupt.annotation.sub_field.Edit; -import xyz.erupt.annotation.sub_field.EditType; -import xyz.erupt.annotation.sub_field.View; -import xyz.erupt.annotation.sub_field.sub_edit.InputType; -import xyz.erupt.annotation.sub_field.sub_edit.Search; -import xyz.erupt.annotation.sub_field.sub_edit.TagsType; - -import javax.persistence.Entity; -import javax.persistence.Table; - - -@Erupt(name = "产品字典值配置", power = @Power(importable = true, export = true)) -@Table(name = "master_data") -@Entity -@Data -public class MasterData extends ModelWithValidFlagVo { - - @EruptField( - views = @View(title = "分类"), - edit = @Edit( - search = @Search(vague = true), - title = "获取可选种类", - type = EditType.TAGS, - desc = "动态获取可选种类", - tagsType = @TagsType( - fetchHandler = SqlTagFetchHandler.class, - fetchHandlerParams = "select distinct category from master_data where valid=true" - )) - ) - private String category; - - @EruptField( - views = @View( - title = "名称" - ), - edit = @Edit( - title = "名称", - type = EditType.INPUT, search = @Search, notNull = true, - inputType = @InputType - ) - ) - private String name; - - @EruptField( - views = @View( - title = "详细描述" - ), - edit = @Edit( - title = "详细描述", - type = EditType.INPUT, - inputType = @InputType - ) - ) - private String detail; - - @EruptField( - views = @View( - title = "代号" - ), - edit = @Edit( - title = "代号", - type = EditType.INPUT, search = @Search, notNull = true, - inputType = @InputType - ) - ) - private String code; - -} \ No newline at end of file diff --git a/fluent-apps/qam/src/main/java/io/fluent/base/masterdata/repo/MasterDataRepo.java b/fluent-apps/qam/src/main/java/io/fluent/base/masterdata/repo/MasterDataRepo.java deleted file mode 100644 index 2618156..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/base/masterdata/repo/MasterDataRepo.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.fluent.base.masterdata.repo; - -import io.fluent.base.masterdata.model.MasterData; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -import org.springframework.stereotype.Repository; - -import java.util.Optional; - -@Repository -public interface MasterDataRepo extends JpaRepository, JpaSpecificationExecutor { - - Optional findMasterDataByCode(String code); - -} \ No newline at end of file diff --git a/fluent-apps/qam/src/main/java/io/fluent/base/package-info.java b/fluent-apps/qam/src/main/java/io/fluent/base/package-info.java deleted file mode 100644 index b1b2672..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/base/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package io.fluent.base; \ No newline at end of file diff --git a/fluent-apps/qam/src/main/java/io/fluent/base/product/model/ProductModuleModel.java b/fluent-apps/qam/src/main/java/io/fluent/base/product/model/ProductModuleModel.java deleted file mode 100644 index a86390c..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/base/product/model/ProductModuleModel.java +++ /dev/null @@ -1,140 +0,0 @@ -package io.fluent.base.product.model; - -import io.fluent.base.model.ModelWithValidFlagVo; -import xyz.erupt.annotation.Erupt; -import xyz.erupt.annotation.EruptField; -import xyz.erupt.annotation.sub_erupt.Power; -import xyz.erupt.annotation.sub_erupt.Tree; -import xyz.erupt.annotation.sub_field.Edit; -import xyz.erupt.annotation.sub_field.EditType; -import xyz.erupt.annotation.sub_field.View; -import xyz.erupt.annotation.sub_field.sub_edit.ChoiceType; -import xyz.erupt.annotation.sub_field.sub_edit.InputType; -import xyz.erupt.annotation.sub_field.sub_edit.ReferenceTreeType; -import xyz.erupt.annotation.sub_field.sub_edit.Search; -import xyz.erupt.toolkit.handler.SqlChoiceFetchHandler; - -import javax.persistence.*; -import java.util.UUID; - -@Erupt(name = "产品模块配置", - power = @Power(importable = true, export = true), - tree = @Tree(pid = "parent.id")) -@Entity -@Table(name = "products") -public class ProductModuleModel extends ModelWithValidFlagVo { - - @EruptField( - views = @View( - title = "名称" - ), - edit = @Edit( - title = "名称", - type = EditType.INPUT, search = @Search, - notNull = true, - inputType = @InputType - ) - ) - private String name; - - @EruptField( - views = @View( - title = "代号" - ), - edit = @Edit( - title = "代号", - type = EditType.INPUT, search = @Search, - notNull = true, - inputType = @InputType - ) - ) - private String code; - - @EruptField( - views = @View( - title = "详细描述" - ), - edit = @Edit( - title = "详细描述", - type = EditType.INPUT, search = @Search, notNull = true, - inputType = @InputType - ) - ) - private String details; - - @EruptField( - views = @View(title = "类型"), - edit = @Edit( - search = @Search, - title = "获取可选类型", - type = EditType.CHOICE, - desc = "动态获取可选类型", - choiceType = @ChoiceType( - fetchHandler = SqlChoiceFetchHandler.class, - fetchHandlerParams = "select id,name from master_data where category='PRODUCT'" - )) - ) - private String metaType; - - @ManyToOne - @EruptField( - edit = @Edit( - title = "上级树节点", - type = EditType.REFERENCE_TREE, - referenceTreeType = @ReferenceTreeType(pid = "parent.id") - ) - ) - private ProductModuleModel parent; - - - @Column(length = 36, nullable = true, updatable = false) - private String uuid = UUID.randomUUID().toString(); - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getCode() { - return code; - } - - public void setCode(String code) { - this.code = code; - } - - public String getDetails() { - return details; - } - - public void setDetails(String details) { - this.details = details; - } - - public String getMetaType() { - return metaType; - } - - public void setMetaType(String metaType) { - this.metaType = metaType; - } - - public ProductModuleModel getParent() { - return parent; - } - - public void setParent(ProductModuleModel parent) { - this.parent = parent; - } - - public String getUuid() { - return uuid; - } - - public void setUuid(String uuid) { - this.uuid = uuid; - } -} \ No newline at end of file diff --git a/fluent-apps/qam/src/main/java/io/fluent/base/product/model/ProductModuleValidFlagVo.java b/fluent-apps/qam/src/main/java/io/fluent/base/product/model/ProductModuleValidFlagVo.java deleted file mode 100644 index b267168..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/base/product/model/ProductModuleValidFlagVo.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.fluent.base.product.model; - -import io.fluent.base.model.ModelWithValidFlagVo; -import lombok.Getter; -import lombok.Setter; -import xyz.erupt.annotation.EruptField; -import xyz.erupt.annotation.sub_field.Edit; -import xyz.erupt.annotation.sub_field.EditType; -import xyz.erupt.annotation.sub_field.View; -import xyz.erupt.annotation.sub_field.sub_edit.ReferenceTreeType; -import xyz.erupt.annotation.sub_field.sub_edit.Search; - -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.MappedSuperclass; - -@MappedSuperclass -@Getter -@Setter -public class ProductModuleValidFlagVo extends ModelWithValidFlagVo { - @ManyToOne - @JoinColumn(name = "product_id") - @EruptField( - views = @View(title = "产品名称",column = "details"), - edit = @Edit( - search = @Search, - title = "产品选择", - type = EditType.REFERENCE_TREE, - desc = "动态获取产品", - referenceTreeType = @ReferenceTreeType(id = "id", label = "name", - pid = "parent.id")) - ) - private ProductModuleModel product; - - @ManyToOne - @JoinColumn(name = "module_id") - @EruptField( - views = @View(title = "模块名称",column = "details"), - edit = @Edit(title = "模块选择", search = @Search, type = EditType.REFERENCE_TREE, - referenceTreeType = @ReferenceTreeType(id = "id", label = "name", - dependField = "product", - dependColumn = "parent.id" - )) - ) - private ProductModuleModel module; - -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/base/product/package-info.java b/fluent-apps/qam/src/main/java/io/fluent/base/product/package-info.java deleted file mode 100644 index 15f9d3f..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/base/product/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package io.fluent.base.product; \ No newline at end of file diff --git a/fluent-apps/qam/src/main/java/io/fluent/base/product/repo/ProductModuleRepo.java b/fluent-apps/qam/src/main/java/io/fluent/base/product/repo/ProductModuleRepo.java deleted file mode 100644 index 7898e5f..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/base/product/repo/ProductModuleRepo.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.fluent.base.product.repo; - - -import io.fluent.base.product.model.ProductModuleModel; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -import org.springframework.stereotype.Repository; - -import java.util.Optional; - -@Repository -public interface ProductModuleRepo extends JpaRepository, JpaSpecificationExecutor { - - Optional findProductByNameAndValid(String name, boolean valid); - - Optional findProductByCodeAndValid(String codeName, boolean valid); - Optional findProductByParentIdAndNameAndValid(Long parentId, String name, boolean valid); -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/base/product/service/ProductModuleService.java b/fluent-apps/qam/src/main/java/io/fluent/base/product/service/ProductModuleService.java deleted file mode 100644 index 0c42dac..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/base/product/service/ProductModuleService.java +++ /dev/null @@ -1,57 +0,0 @@ -package io.fluent.base.product.service; - -import io.fluent.builtin.PingYinUtils; -import io.fluent.base.product.repo.ProductModuleRepo; -import io.fluent.base.proxies.AuditDataEnhancerProxy; -import io.fluent.base.masterdata.repo.MasterDataRepo; -import io.fluent.base.product.model.ProductModuleModel; -import io.fluent.base.masterdata.model.MasterData; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.util.Optional; - -@Service -public class ProductModuleService { - @Resource - private ProductModuleRepo metaRepo; - - @Resource - private MasterDataRepo masterDataRepo; - - @Resource - AuditDataEnhancerProxy dataEnhancerProxy; - - public ProductModuleModel createModuleIfNotExist(Long productId, String moduleName, String updater) { - Optional meta = metaRepo.findProductByParentIdAndNameAndValid(productId, - moduleName, true); - if (meta.isPresent()) return meta.get(); - ProductModuleModel parent = new ProductModuleModel(); - parent.setId(productId); - ProductModuleModel module = new ProductModuleModel(); - module.setName(moduleName); - module.setDetails(moduleName); - module.setParent(parent); - module.setCode(PingYinUtils.convertToPinyinAbbreviation(moduleName)); - MasterData data = masterDataRepo.findMasterDataByCode("MODULE").get(); - module.setMetaType(data.getId().toString()); - dataEnhancerProxy.enhanceTimeAndUserAuditData(module,updater); - return metaRepo.save(module); - } - - public ProductModuleModel findApiServiceProduct() { - String API_SERVICE = "API"; - Optional meta = metaRepo.findProductByCodeAndValid(API_SERVICE, true); - if (meta.isPresent()) return meta.get(); - throw new RuntimeException("Please config API Service as a Product in Product Meta"); - } - - public ProductModuleModel createApiModuleIfNotExist(String moduleName,String updater) { - ProductModuleModel parent = findApiServiceProduct(); - return createModuleIfNotExist(parent.getId(), moduleName,updater); - } - - public ProductModuleModel findByName(String productName) { - return metaRepo.findProductByNameAndValid(productName, true).orElse(null); - } -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/base/project/model/ProjectModel.java b/fluent-apps/qam/src/main/java/io/fluent/base/project/model/ProjectModel.java deleted file mode 100644 index 573eace..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/base/project/model/ProjectModel.java +++ /dev/null @@ -1,89 +0,0 @@ -package io.fluent.base.project.model; - - -import io.fluent.base.model.ModelWithValidFlagVo; -import io.fluent.base.product.model.ProductModuleModel; -import lombok.Data; -import xyz.erupt.annotation.Erupt; -import xyz.erupt.annotation.EruptField; -import xyz.erupt.annotation.sub_erupt.Power; -import xyz.erupt.annotation.sub_erupt.Tree; -import xyz.erupt.annotation.sub_field.Edit; -import xyz.erupt.annotation.sub_field.EditType; -import xyz.erupt.annotation.sub_field.View; -import xyz.erupt.annotation.sub_field.sub_edit.InputType; -import xyz.erupt.annotation.sub_field.sub_edit.ReferenceTreeType; -import xyz.erupt.annotation.sub_field.sub_edit.Search; - -import javax.persistence.*; -import java.util.UUID; - -@Erupt(name = "项目", - power = @Power(importable = true, export = true), - tree = @Tree(pid = "parent.id")) -@Entity -@Table(name = "projects") -@Data -public class ProjectModel extends ModelWithValidFlagVo { - - @EruptField( - views = @View( - title = "名称" - ), - edit = @Edit( - title = "名称", - type = EditType.INPUT, search = @Search, - notNull = true, - inputType = @InputType - ) - ) - private String name; - - @EruptField( - views = @View( - title = "代号" - ), - edit = @Edit( - title = "代号", - type = EditType.INPUT, search = @Search, - notNull = true, - inputType = @InputType - ) - ) - private String code; - - @EruptField( - views = @View( - title = "详细描述" - ), - edit = @Edit( - title = "详细描述", - type = EditType.INPUT, search = @Search, notNull = true, - inputType = @InputType - ) - ) - private String details; - - @ManyToOne - @EruptField( - edit = @Edit( - title = "产品列表", - type = EditType.REFERENCE_TREE, - referenceTreeType = @ReferenceTreeType(pid = "parent.id") - ) - ) - private ProductModuleModel productId; - - @ManyToOne - @EruptField( - edit = @Edit( - title = "上级树节点", - type = EditType.REFERENCE_TREE, - referenceTreeType = @ReferenceTreeType(pid = "parent.id") - ) - ) - private ProjectModel parent; - - @Column(length = 36, nullable = false, updatable = false) - private String uuid = UUID.randomUUID().toString(); -} \ No newline at end of file diff --git a/fluent-apps/qam/src/main/java/io/fluent/base/project/repo/ProjectModelRepo.java b/fluent-apps/qam/src/main/java/io/fluent/base/project/repo/ProjectModelRepo.java deleted file mode 100644 index 098b228..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/base/project/repo/ProjectModelRepo.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.fluent.base.project.repo; - - -import io.fluent.base.product.model.ProductModuleModel; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -import org.springframework.stereotype.Repository; - -import java.util.Optional; - -@Repository -public interface ProjectModelRepo extends JpaRepository, JpaSpecificationExecutor { - - Optional findProductByNameAndValid(String name, boolean valid); - - Optional findProductByCodeAndValid(String codeName, boolean valid); - Optional findProductByParentIdAndNameAndValid(Long parentId, String name, boolean valid); -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/base/upload/model/UploadFileModel.java b/fluent-apps/qam/src/main/java/io/fluent/base/upload/model/UploadFileModel.java deleted file mode 100644 index cb29089..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/base/upload/model/UploadFileModel.java +++ /dev/null @@ -1,62 +0,0 @@ -package io.fluent.base.upload.model; - -import io.fluent.base.upload.proxy.UploadFileDataProxy; -import io.fluent.base.product.model.ProductModuleModel; -import lombok.Data; -import xyz.erupt.annotation.Erupt; -import xyz.erupt.annotation.EruptField; -import xyz.erupt.annotation.PreDataProxy; -import xyz.erupt.annotation.sub_field.Edit; -import xyz.erupt.annotation.sub_field.EditType; -import xyz.erupt.annotation.sub_field.View; -import xyz.erupt.annotation.sub_field.sub_edit.AttachmentType; -import xyz.erupt.annotation.sub_field.sub_edit.ChoiceType; -import xyz.erupt.annotation.sub_field.sub_edit.InputType; -import xyz.erupt.annotation.sub_field.sub_edit.Search; -import xyz.erupt.toolkit.handler.SqlChoiceFetchHandler; - -import javax.persistence.Entity; -import javax.persistence.Table; - -@Erupt(name = "测试相关文件上传同步", orderBy = "UploadFileModel.createTime desc") -@Table(name = "uploaded_files") -@Entity -@Data -@PreDataProxy(value = UploadFileDataProxy.class) -public class UploadFileModel extends ProductModuleModel { - - @EruptField( - views = @View(title = "用途"), - edit = @Edit( - search = @Search, - title = "获取可选类型", - type = EditType.CHOICE, - desc = "动态获取可选类型", - notNull = true, - choiceType = @ChoiceType( - fetchHandler = SqlChoiceFetchHandler.class, - fetchHandlerParams = "select distinct code,name from master_data where category='UPLOAD_FILE_USAGE' and valid=true" - )) - ) - private String usage; - - - @EruptField( - views = @View(title = "文件上传"), - edit = @Edit(title = "文件上传", type = EditType.ATTACHMENT, - attachmentType = @AttachmentType(size = 100000)) - ) - private String attachment; - - @EruptField( - views = @View( - title = "用途描述" - ), - edit = @Edit( - title = "用途描述", - type = EditType.TEXTAREA, notNull = true, - inputType = @InputType - ) - ) - private String comments; -} \ No newline at end of file diff --git a/fluent-apps/qam/src/main/java/io/fluent/base/upload/package-info.java b/fluent-apps/qam/src/main/java/io/fluent/base/upload/package-info.java deleted file mode 100644 index 67c0eee..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/base/upload/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package io.fluent.base.upload; \ No newline at end of file diff --git a/fluent-apps/qam/src/main/java/io/fluent/base/upload/proxy/UploadFileDataProxy.java b/fluent-apps/qam/src/main/java/io/fluent/base/upload/proxy/UploadFileDataProxy.java deleted file mode 100644 index db927e0..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/base/upload/proxy/UploadFileDataProxy.java +++ /dev/null @@ -1,64 +0,0 @@ -package io.fluent.base.upload.proxy; - - - -import cn.hutool.core.bean.BeanUtil; -import io.fluent.excel.ExcelReadWriter; -import io.fluent.base.product.model.ProductModuleModel; -import io.fluent.qtm.tc.dto.TestCaseDTO; -import io.fluent.qtm.tc.service.TestCaseService; -import io.fluent.qtm.tc.service.impl.MindMappingService; -import lombok.extern.slf4j.Slf4j; -import xyz.erupt.core.prop.EruptProp; -import xyz.erupt.core.util.EruptSpringUtil; -import xyz.erupt.jpa.model.MetaDataProxy; -import xyz.erupt.jpa.model.MetaModel; - -import java.util.List; - -@Slf4j -public class UploadFileDataProxy extends MetaDataProxy { - private final MindMappingService mindMappingService; - private final TestCaseService testCaseService; - private final EruptProp eruptProp; - private final ExcelReadWriter excelReadWriter; - - public UploadFileDataProxy() { - mindMappingService = EruptSpringUtil.getBean(MindMappingService.class); - testCaseService = EruptSpringUtil.getBean(TestCaseService.class); - eruptProp = EruptSpringUtil.getBean(EruptProp.class); - excelReadWriter = new ExcelReadWriter(); - } - - @Override - public void beforeAdd(MetaModel metaModel) { - //before add, add some check here - super.beforeAdd(metaModel); - } - - @Override - public void afterAdd(MetaModel metaModel) { - //after add, then doing business process - log.info("start handler uploaded file"); - String filePath = getUploaderFilePath(metaModel); - String uploadType = BeanUtil.getProperty(metaModel, "usage"); - - if(UploadFileTypeEnum.parseType(uploadType).equals(UploadFileTypeEnum.EXCEL_TC)){ - ProductModuleModel product = BeanUtil.getProperty(metaModel, "product"); - ProductModuleModel module = BeanUtil.getProperty(metaModel, "module"); - testCaseService.saveTestCases(getExcelTestCases(filePath),product,module,metaModel.getUpdateBy()); - } - if(UploadFileTypeEnum.parseType(uploadType).equals(UploadFileTypeEnum.FREEMIND)){ - mindMappingService.saveTestCases(filePath,metaModel); - } - } - - private List getExcelTestCases(String filePath){ - return excelReadWriter.readExcel(filePath, TestCaseDTO.class); - } - - private String getUploaderFilePath(MetaModel metaModel) { - return eruptProp.getUploadPath() + BeanUtil.getProperty(metaModel, "attachment"); - } - -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/base/upload/proxy/UploadFileTypeEnum.java b/fluent-apps/qam/src/main/java/io/fluent/base/upload/proxy/UploadFileTypeEnum.java deleted file mode 100644 index 8a44582..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/base/upload/proxy/UploadFileTypeEnum.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.fluent.base.upload.proxy; - -public enum UploadFileTypeEnum { - EXCEL_TC,FREEMIND,PM,MINDMAP; - - public static UploadFileTypeEnum parseType(String uploadFileType) { - for (UploadFileTypeEnum uploadFileTypeEnum : UploadFileTypeEnum.values()) { - if (uploadFileTypeEnum.name().equals(uploadFileType)) { - return uploadFileTypeEnum; - } - } - throw new RuntimeException("UploadFileTypeEnum is not found for " + uploadFileType); - } -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/FluentQAApiModule.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/FluentQAApiModule.java deleted file mode 100644 index 2a8570a..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/FluentQAApiModule.java +++ /dev/null @@ -1,125 +0,0 @@ -package io.fluent.qtm; - - -import io.fluent.qtm.api.model.*; -import org.springframework.boot.autoconfigure.domain.EntityScan; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import xyz.erupt.core.annotation.EruptScan; -import xyz.erupt.core.constant.MenuTypeEnum; -import xyz.erupt.core.module.EruptModule; -import xyz.erupt.core.module.EruptModuleInvoke; -import xyz.erupt.core.module.MetaMenu; -import xyz.erupt.core.module.ModuleInfo; - -import java.util.ArrayList; -import java.util.List; - - -@Configuration -@ComponentScan -@EntityScan -@EruptScan -@EnableConfigurationProperties -public class FluentQAApiModule implements EruptModule { - public FluentQAApiModule() { - } - - @Override - public ModuleInfo info() { - return ModuleInfo.builder().name("fluent-api").build(); - } - - @Override - public void run() { - EruptModule.super.run(); - } - - /** - * API管理: - *

- * 1. API 仓库管理 - * 2. API 接口定义 - * 3. API 接口录制记录 - * 4. API 接口测试 - * - * @return - */ - @Override - public List initMenus() { - List menus = new ArrayList<>(); - menus.add(MetaMenu.createRootMenu("$APIMgr", "接口管理", "fa fa-exchange", 1)); - - MetaMenu menuForAdded = MetaMenu.createEruptClassMenu(RemoteApi.class, - menus.get(0), 1, MenuTypeEnum.TABLE); - menuForAdded.setIcon("fa fa-scissors"); - menuForAdded.setName("API清单"); - menuForAdded.setCode("$API-List"); - menus.add(menuForAdded); - - MetaMenu rawApiTestCaseMenu = MetaMenu.createEruptClassMenu(RawApiTestCase.class, - menus.get(0), 1, MenuTypeEnum.TABLE); - rawApiTestCaseMenu.setIcon("fa fa-scissors"); - rawApiTestCaseMenu.setName("API生成原始测试用例"); - rawApiTestCaseMenu.setCode("$API-TC-GEN"); - menus.add(rawApiTestCaseMenu); - - MetaMenu apiMonitorRecordMenu = MetaMenu.createEruptClassMenu(ApiMonitorRecord.class, - menus.get(0), 2, MenuTypeEnum.TABLE); - apiMonitorRecordMenu.setIcon("fa fa-repeat"); - apiMonitorRecordMenu.setName("API录制记录"); - apiMonitorRecordMenu.setCode("$API-Record"); - menus.add(apiMonitorRecordMenu); - - MetaMenu apiTestRecord = MetaMenu.createEruptClassMenu( - ApiTestRecord - .class, - menus.get(0), 3, MenuTypeEnum.TABLE); - apiTestRecord.setIcon("fa fa-thumbs-up"); - apiTestRecord.setName("API测试结果记录"); - apiTestRecord.setCode("$API-TestResult"); - menus.add(apiTestRecord); - - MetaMenu apiTestScenarioMenu = MetaMenu.createEruptClassMenu( - ApiTestScenario - .class, - menus.get(0), 4, MenuTypeEnum.TABLE); - apiTestScenarioMenu.setIcon("fa fa-folder"); - apiTestScenarioMenu.setName("API测试场景"); - apiTestScenarioMenu.setCode("$API-TestScenario"); - menus.add(apiTestScenarioMenu); - - MetaMenu apiStepMenu = MetaMenu.createEruptClassMenu( - ApiStep - .class, - menus.get(0), 5, MenuTypeEnum.TABLE); - apiStepMenu.setIcon("fa fa-folder"); - apiStepMenu.setName("API用例步骤"); - apiStepMenu.setCode("$API-Step"); - menus.add(apiStepMenu); -// MetaMenu apiDefMenu = MetaMenu.createSimpleMenu("$API-def", "接口定义", "fa fa-check-square-o", -// menus.get(0), 1, ""); -// menus.add(apiDefMenu); -// addNewMenu( -// menus,"$API-Spec-Git","API定义仓库", "fa fa-meetup", ApiSpecGitRepoModel.class, -// MenuTypeEnum.TABLE,1,0 -// ); -// addNewMenu( -// menus,"$API-Spec","API最新版本", "fa fa-gitlab", ApiSpecVersionModel.class, -// MenuTypeEnum.TABLE,1,1 -// ); - - return menus; - } - - - static { - EruptModuleInvoke.addEruptModule(FluentQAApiModule.class); - } - - @Override - public void initFun() { - EruptModule.super.initFun(); - } -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/handler/GenerateApiCaseByCaptureDataHandler.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/handler/GenerateApiCaseByCaptureDataHandler.java deleted file mode 100644 index 1466ab1..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/handler/GenerateApiCaseByCaptureDataHandler.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.fluent.qtm.api.handler; - -import io.fluent.qtm.api.model.ApiMonitorRecord; -import io.fluent.qtm.api.service.ApiTestCaseService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import xyz.erupt.annotation.fun.OperationHandler; - -import javax.annotation.Resource; -import java.util.List; - -@Service -@Slf4j -public class GenerateApiCaseByCaptureDataHandler implements OperationHandler { - - @Resource - private ApiTestCaseService apiService; - @Override - public String exec(List data, Void unused, String[] param) { - log.info("start convert api capture data"); - apiService.convertApiMonitorRecordToTestCase(data); - return null; - } -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/handler/GenerateApiTestStepByApiTestRecord.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/handler/GenerateApiTestStepByApiTestRecord.java deleted file mode 100644 index 02361bd..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/handler/GenerateApiTestStepByApiTestRecord.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.fluent.qtm.api.handler; - -import io.fluent.qtm.api.model.ApiTestRecord; -import io.fluent.qtm.api.service.ApiTestCaseService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import xyz.erupt.annotation.fun.OperationHandler; - -import javax.annotation.Resource; -import java.util.List; - -@Service -@Slf4j -public class GenerateApiTestStepByApiTestRecord implements OperationHandler { - - @Resource - private ApiTestCaseService apiService; - @Override - public String exec(List data, Void unused, String[] param) { - log.info("start convert api capture data"); - apiService.convertApiTestResultToApiTestStep(data); - return null; - } -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/handler/GenerateRawApiCaseHandler.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/handler/GenerateRawApiCaseHandler.java deleted file mode 100644 index bd98597..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/handler/GenerateRawApiCaseHandler.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.fluent.qtm.api.handler; - -import io.fluent.qtm.api.model.RemoteApi; -import io.fluent.qtm.api.service.ApiTestCaseService; -import org.springframework.stereotype.Service; -import xyz.erupt.annotation.fun.OperationHandler; - -import javax.annotation.Resource; -import java.util.List; - -@Service -public class GenerateRawApiCaseHandler implements OperationHandler { - - @Resource - private ApiTestCaseService apiService; - @Override - public String exec(List data, Void unused, String[] param) { - System.out.println("this is tests"); - apiService.convertToRawTestCase(data); - return null; - } -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiMonitorRecord.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiMonitorRecord.java deleted file mode 100644 index cc2999e..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiMonitorRecord.java +++ /dev/null @@ -1,131 +0,0 @@ -package io.fluent.qtm.api.model; - - -import io.fluent.qtm.api.handler.GenerateApiCaseByCaptureDataHandler; -import io.fluent.base.handlers.SqlTagFetchHandler; -import lombok.Data; -import org.hibernate.annotations.DynamicInsert; -import org.hibernate.annotations.DynamicUpdate; -import xyz.erupt.annotation.Erupt; -import xyz.erupt.annotation.EruptField; -import xyz.erupt.annotation.sub_erupt.Layout; -import xyz.erupt.annotation.sub_erupt.Power; -import xyz.erupt.annotation.sub_erupt.RowOperation; -import xyz.erupt.annotation.sub_field.Edit; -import xyz.erupt.annotation.sub_field.EditType; -import xyz.erupt.annotation.sub_field.View; -import xyz.erupt.annotation.sub_field.ViewType; -import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; -import xyz.erupt.annotation.sub_field.sub_edit.Search; -import xyz.erupt.annotation.sub_field.sub_edit.TagsType; -import xyz.erupt.jpa.model.MetaModel; - -import javax.persistence.Entity; -import javax.persistence.Table; - -@DynamicUpdate -@DynamicInsert -@Entity -@Table(name = "api_monitor_record") -@Erupt( - name = "接口访问记录", - layout = @Layout( - tableLeftFixed = 3, - pageSize = 30), - power = @Power(importable = true, export = true), - rowOperation = {@RowOperation( - title = "生成接口用例数据", - operationHandler = GenerateApiCaseByCaptureDataHandler.class)}, - orderBy = "ApiMonitorRecord.id desc" - -) -@Data -public class ApiMonitorRecord extends MetaModel { - - @EruptField( - views = @View(title = "app"), - edit = @Edit( - title = "app应用名", - type = EditType.TAGS, search = @Search(vague = true), notNull = true, - tagsType = @TagsType( - fetchHandler = SqlTagFetchHandler.class, - fetchHandlerParams = "select distinct app from api_monitor_record" - ) - )) - private String app; - @EruptField( - views = @View(title = "录制名称"), - edit = @Edit(title = "录制名称", notNull = true, search = @Search) - ) - private String recordName; - @EruptField( - views = @View(title = "请求地址"), - edit = @Edit(title = "请求地址", notNull = true, search = @Search) - ) - private String requestUrl; - - @EruptField( - views = @View(title = "服务"), - edit = @Edit( - title = "服务", - type = EditType.TAGS, search = @Search(vague = true), notNull = true, - tagsType = @TagsType( - fetchHandler = SqlTagFetchHandler.class, - fetchHandlerParams = "select distinct service from api_monitor_record" - ) - ) - ) - - private String service; - @EruptField( - views = @View(title = "接口名称"), - edit = @Edit(title = "接口名称", notNull = true, search = @Search) - ) - private String api; - - @EruptField( - views = @View(title = "服务URL"), - edit = @Edit(title = "服务URL", notNull = true, search = @Search) - ) - private String path; - - @EruptField( - views = @View(title = "请求头"), - edit = @Edit(title = "请求报文", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) - ) - private String requestHeaders; - - @EruptField( - views = @View(title = "HTTP方法"), - edit = @Edit(title = "HTTP方法", notNull = true, search = @Search) - ) - private String method; - - @EruptField( - views = @View(title = "请求报文", type = ViewType.CODE), - edit = @Edit(title = "请求报文", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) - ) - private String requestBody; - - - @EruptField( - views = @View(title = "response_headers"), - edit = @Edit(title = "responseHeaders", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) - ) - private String responseHeaders; - - @EruptField( - views = @View(title = "status_code"), - edit = @Edit(title = "status_code", notNull = true, search = @Search) - ) - private int statusCode; - - @EruptField( - views = @View(title = "返回报文", type = ViewType.CODE), - edit = @Edit(title = "返回报文", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) - ) - private String responseBody; - - - -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiSpecChangeModel.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiSpecChangeModel.java deleted file mode 100644 index 723930d..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiSpecChangeModel.java +++ /dev/null @@ -1,52 +0,0 @@ -package io.fluent.qtm.api.model; - -import lombok.Data; -import org.hibernate.annotations.DynamicInsert; -import org.hibernate.annotations.DynamicUpdate; -import xyz.erupt.annotation.Erupt; -import xyz.erupt.annotation.EruptField; -import xyz.erupt.annotation.sub_erupt.Layout; -import xyz.erupt.annotation.sub_erupt.Power; -import xyz.erupt.annotation.sub_field.View; -import xyz.erupt.jpa.model.BaseModel; - -import javax.persistence.Entity; -import javax.persistence.Table; -import java.time.LocalDateTime; - -@DynamicUpdate -@DynamicInsert -@Entity -@Table(name = "api_spec_change") -@Erupt( - layout = @Layout( - tableLeftFixed = 3, - pageSize = 30), - name = "api spec 变化记录", power = @Power(export = true) -) -@Data -public class ApiSpecChangeModel extends BaseModel { - @EruptField( - views = @View(title = "应用名-appName") - ) - private String name; - @EruptField( - views = @View(title = "GIT URL") - ) - private String gitUrl; - @EruptField( - views = @View(title = "GIT分支") - ) - private String branch; - - @EruptField( - views = @View(title = "创建时间") - ) - private LocalDateTime createdTime; - - @EruptField( - views = @View(title = "appVersion") - ) - private String appVersion; - -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiSpecGitRepoModel.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiSpecGitRepoModel.java deleted file mode 100644 index cb97dfd..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiSpecGitRepoModel.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.fluent.qtm.api.model; - -import lombok.Data; -import xyz.erupt.annotation.Erupt; -import xyz.erupt.annotation.EruptField; -import xyz.erupt.annotation.sub_erupt.Layout; -import xyz.erupt.annotation.sub_erupt.Power; -import xyz.erupt.annotation.sub_field.Edit; -import xyz.erupt.annotation.sub_field.View; -import xyz.erupt.annotation.sub_field.sub_edit.Search; -import xyz.erupt.jpa.model.MetaModel; - -import javax.persistence.Entity; -import javax.persistence.Table; - -@Entity -@Table(name = "apispec_git_repo") -@Data -@Erupt(name = "skel仓库设置", layout = @Layout( - tableLeftFixed = 3, - pageSize = 30), - power = @Power(importable = true, export = true)) -public class ApiSpecGitRepoModel extends MetaModel { - @EruptField( - views = @View(title = "应用名-appName"), - edit = @Edit(title = "应用名-App Name", notNull = true, search = @Search) - ) - private String name; - @EruptField( - views = @View(title = "gitUrl"), - edit = @Edit(title = "gitUrl", notNull = true) - ) - private String gitUrl; - - @EruptField( - views = @View(title = "gitlabId"), - edit = @Edit(title = "gitlabId", notNull = true) - ) - private Integer gitlabId; - - @EruptField( - views = @View(title = "webUrl"), - edit = @Edit(title = "webUrl", notNull = true) - ) - private String webUrl; - -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiSpecVersionModel.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiSpecVersionModel.java deleted file mode 100644 index 6bd226d..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiSpecVersionModel.java +++ /dev/null @@ -1,102 +0,0 @@ -package io.fluent.qtm.api.model; - -import io.fluent.base.model.ModelWithValidFlagVo; -import lombok.Data; -import org.hibernate.annotations.DynamicInsert; -import org.hibernate.annotations.DynamicUpdate; -import org.hibernate.annotations.SQLDelete; -import org.hibernate.annotations.Where; -import xyz.erupt.annotation.Erupt; -import xyz.erupt.annotation.EruptField; -import xyz.erupt.annotation.sub_erupt.Layout; -import xyz.erupt.annotation.sub_erupt.Power; -import xyz.erupt.annotation.sub_field.Edit; -import xyz.erupt.annotation.sub_field.EditType; -import xyz.erupt.annotation.sub_field.View; -import xyz.erupt.annotation.sub_field.ViewType; -import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; -import xyz.erupt.annotation.sub_field.sub_edit.InputType; -import xyz.erupt.annotation.sub_field.sub_edit.Search; - -import javax.persistence.Entity; -import javax.persistence.Table; - -@DynamicUpdate -@DynamicInsert -@Entity -@Table(name = "api_spec_version") -@Erupt( - name = "远程服务原始文件", - power = @Power(export = true), - layout = @Layout( - tableLeftFixed = 3, - pageSize = 30) -) -@Data -@SQLDelete(sql = "update api_spec_version set valid=false where id=?") -@Where(clause = "valid = true") -public class ApiSpecVersionModel extends ModelWithValidFlagVo { - @EruptField( - views = @View( - title = "名称" - ), - edit = @Edit( - title = "名称", - type = EditType.INPUT, search = @Search, notNull = true, - inputType = @InputType - ) - ) - private String name; - - - @EruptField( - views = @View( - title = "名称" - ), - edit = @Edit( - title = "名称", - type = EditType.INPUT, search = @Search, notNull = true, - inputType = @InputType - ) - ) - private String type="POSTMAN"; - - @EruptField( - views = @View( - title = "服务类型" - ), - edit = @Edit( - title = "服务类型", - type = EditType.INPUT, search = @Search, notNull = true, - inputType = @InputType - ) - ) - private String serviceType; //API or RPC - - @EruptField( - views = @View( - title = "版本" - ), - edit = @Edit( - title = "版本", - type = EditType.INPUT, search = @Search, notNull = true, - inputType = @InputType - ) - ) - private String appVersion; - - @EruptField( - views = @View(title = "GIT URL") - ) - private String gitUrl; - @EruptField( - views = @View(title = "GIT分支") - ) - private String branch; - - @EruptField( - views = @View(title = "接口定义", type = ViewType.CODE), - edit = @Edit(title = "接口定义", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) - ) - private String spec; -} \ No newline at end of file diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiStep.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiStep.java deleted file mode 100644 index 9f26690..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiStep.java +++ /dev/null @@ -1,156 +0,0 @@ -package io.fluent.qtm.api.model; - -import io.fluent.base.handlers.SqlTagFetchHandler; -import org.hibernate.annotations.DynamicInsert; -import org.hibernate.annotations.DynamicUpdate; -import xyz.erupt.annotation.Erupt; -import xyz.erupt.annotation.EruptField; -import xyz.erupt.annotation.sub_erupt.Layout; -import xyz.erupt.annotation.sub_erupt.Power; -import xyz.erupt.annotation.sub_field.Edit; -import xyz.erupt.annotation.sub_field.EditType; -import xyz.erupt.annotation.sub_field.View; -import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; -import xyz.erupt.annotation.sub_field.sub_edit.Search; -import xyz.erupt.annotation.sub_field.sub_edit.TagsType; -import xyz.erupt.jpa.model.MetaModel; - -import javax.persistence.Entity; -import javax.persistence.Table; - -@DynamicUpdate -@DynamicInsert -@Entity -@Table(name = "api_steps") -@Erupt( - name = "接口测试用例", layout = @Layout( - tableLeftFixed = 3, - pageSize = 30), power = @Power(importable = true, export = true), - orderBy = "ApiTestStep.updateTime desc" -) -public class ApiStep extends MetaModel{ - - @EruptField( - views = @View(title = "场景"), - edit = @Edit(title = "场景", notNull = true, search = @Search) - ) - private String scenario; - - @EruptField( - views = @View(title = "用例名称"), - edit = @Edit(title = "用例名称", notNull = true, search = @Search) - ) - private String caseName; - - @EruptField( - views = @View(title = "服务"), - edit = @Edit( - search = @Search, - title = "获取可选服务", - type = EditType.TAGS, - desc = "获取可选服务", - tagsType = @TagsType( - fetchHandler = SqlTagFetchHandler.class, - fetchHandlerParams = "select distinct service_name from remote_services where type='API' and valid=true" - )) - ) - private String serviceName; - @EruptField( - views = @View(title = "服务方法"), - edit = @Edit(title = "服务方法", notNull = true, search = @Search) - ) - private String serviceMethod; - - @EruptField( - views = @View(title = "接口路径"), - edit = @Edit(title = "接口路径", notNull = true, search = @Search) - ) - private String path; - - @EruptField( -// views = @View(title = "测试请求", type = ViewType.CODE), - edit = @Edit(title = "测试请求", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) - ) - private String request; - @EruptField( -// views = @View(title = "接口请求结果"), - edit = @Edit(title = "接口请求结果", - type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) - ) - private String result; - - - @EruptField( -// views = @View(title = "预期结果"), - edit = @Edit(title = "预期结果", notNull = true, type = EditType.CODE_EDITOR, - codeEditType = @CodeEditorType(language = "json")) - ) - private String expect; - - - public String getScenario() { - return scenario; - } - - public void setScenario(String scenario) { - this.scenario = scenario; - } - - public String getServiceName() { - return serviceName; - } - - public void setServiceName(String serviceName) { - this.serviceName = serviceName; - } - - public String getServiceMethod() { - return serviceMethod; - } - - public void setServiceMethod(String serviceMethod) { - this.serviceMethod = serviceMethod; - } - - public String getCaseName() { - return caseName; - } - - public void setCaseName(String caseName) { - this.caseName = caseName; - } - - public String getPath() { - return path; - } - - public void setPath(String path) { - this.path = path; - } - - public String getRequest() { - return request; - } - - public void setRequest(String request) { - this.request = request; - } - - public String getResult() { - return result; - } - - public void setResult(String result) { - this.result = result; - } - - - - public String getExpect() { - return expect; - } - - public void setExpect(String expect) { - this.expect = expect; - } -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiTestRecord.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiTestRecord.java deleted file mode 100644 index e03248c..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiTestRecord.java +++ /dev/null @@ -1,131 +0,0 @@ -package io.fluent.qtm.api.model; - -import io.fluent.qtm.api.handler.GenerateApiTestStepByApiTestRecord; -import io.fluent.base.handlers.SqlTagFetchHandler; -import lombok.Data; -import org.hibernate.annotations.DynamicInsert; -import org.hibernate.annotations.DynamicUpdate; -import xyz.erupt.annotation.Erupt; -import xyz.erupt.annotation.EruptField; -import xyz.erupt.annotation.sub_erupt.Layout; -import xyz.erupt.annotation.sub_erupt.Power; -import xyz.erupt.annotation.sub_erupt.RowOperation; -import xyz.erupt.annotation.sub_field.Edit; -import xyz.erupt.annotation.sub_field.EditType; -import xyz.erupt.annotation.sub_field.View; -import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; -import xyz.erupt.annotation.sub_field.sub_edit.Search; -import xyz.erupt.annotation.sub_field.sub_edit.TagsType; -import xyz.erupt.jpa.model.MetaModel; - -import javax.persistence.Entity; -import javax.persistence.Table; - -/** - * 1. 原先的表结构设计几个问题: - * - 没有办法区分那次测试运行记录 - * - 查找不太方便 - * - name 和service name 重复,没有必要同时使用 - * - serviceName 从remote service里面取不过滤API,不够精确 - * 修改方式: - * - name 修改为测试用例运行名称 - * - serviceName 取tags 从remote service 的API 中取 - */ -@DynamicUpdate -@DynamicInsert -@Entity -@Table(name = "api_test_record") -@Erupt( - name = "接口测试结果", layout = @Layout( - tableLeftFixed = 3, - pageSize = 30), power = @Power(importable = true, export = true), - orderBy = "ApiTestRecord.id desc", - rowOperation = {@RowOperation( - title = "生成可用测试步骤", - operationHandler = GenerateApiTestStepByApiTestRecord.class)} -) -@Data -public class ApiTestRecord extends MetaModel { - @EruptField( - views = @View(title = "测试运行名称"), - edit = @Edit(title = "name", search = @Search) - ) - private String name; - - @EruptField( - views = @View(title = "场景"), - edit = @Edit(title = "场景", notNull = true, search = @Search) - ) - private String scenario; - - @EruptField( - views = @View(title = "用例名称"), - edit = @Edit(title = "用例名称", notNull = true, search = @Search) - ) - private String caseName; - - @EruptField( - views = @View(title = "服务"), - edit = @Edit( - search = @Search, - title = "获取可选服务", - type = EditType.TAGS, - desc = "获取可选服务", - tagsType = @TagsType( - fetchHandler = SqlTagFetchHandler.class, - fetchHandlerParams = "select distinct service_name from remote_services where type='API' and valid=true" - )) - ) - private String serviceName; - @EruptField( - views = @View(title = "服务方法"), - edit = @Edit(title = "服务方法", notNull = true, search = @Search) - ) - private String serviceMethod; - - @EruptField( - views = @View(title = "接口路径"), - edit = @Edit(title = "接口路径", notNull = true, search = @Search) - ) - private String path; - - @EruptField( -// views = @View(title = "测试请求", type = ViewType.CODE), - edit = @Edit(title = "测试请求", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) - ) - private String request; - @EruptField( -// views = @View(title = "接口请求结果"), - edit = @Edit(title = "接口请求结果", - type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) - ) - private String result; - - @EruptField( - views = @View(title = "状态码"), - edit = @Edit(title = "状态码", notNull = true, search = @Search) - ) - private String statusCode; - - @EruptField( - views = @View(title = "错误日志"), - edit = @Edit(title = "错误日志", - type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) - ) - private String errorLog; - - @EruptField( -// views = @View(title = "预期结果"), - edit = @Edit(title = "预期结果", notNull = true, type = EditType.CODE_EDITOR, - codeEditType = @CodeEditorType(language = "json")) - ) - private String expect; - - //TODO: 通过或者失败 - @EruptField( - views = @View(title = "用例执行结果"), - edit = @Edit(title = "用例执行结果", search = @Search) - ) - private boolean isSuccess; - -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiTestScenario.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiTestScenario.java deleted file mode 100644 index eb187f8..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/ApiTestScenario.java +++ /dev/null @@ -1,157 +0,0 @@ -package io.fluent.qtm.api.model; - -import io.fluent.base.handlers.SqlTagFetchHandler; -import io.fluent.base.model.ModelWithValidFlag; -import io.fluent.base.product.model.ProductModuleModel; -import xyz.erupt.annotation.Erupt; -import xyz.erupt.annotation.EruptField; -import xyz.erupt.annotation.sub_erupt.Layout; -import xyz.erupt.annotation.sub_erupt.LinkTree; -import xyz.erupt.annotation.sub_erupt.Power; -import xyz.erupt.annotation.sub_field.Edit; -import xyz.erupt.annotation.sub_field.EditType; -import xyz.erupt.annotation.sub_field.View; -import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; -import xyz.erupt.annotation.sub_field.sub_edit.ReferenceTreeType; -import xyz.erupt.annotation.sub_field.sub_edit.Search; -import xyz.erupt.annotation.sub_field.sub_edit.TagsType; - -import javax.persistence.*; -import java.util.Set; - -/** - * - */ -@Entity -@Erupt(name = "接口测试用例", - power = @Power(export = true), - orderBy = "ApiTestScenario.updateTime desc", - linkTree = @LinkTree(field = "module"), - layout = @Layout( - tableLeftFixed = 3, - pageSize = 30)) -@Table(name = "api_test_scenario") -public class ApiTestScenario extends ModelWithValidFlag { - - @ManyToOne - @JoinColumn(name = "product_id") - @EruptField( - views = @View(title = "所属模块", column = "details"), - edit = @Edit( - notNull = true, - search = @Search, - title = "产品模块选择", - type = EditType.REFERENCE_TREE, - desc = "动态获取产品", - referenceTreeType = @ReferenceTreeType(id = "id", label = "name", - pid = "parent.id")) - ) - private ProductModuleModel module; - - - @EruptField( - views = @View( - title = "测试场景" - ), - edit = @Edit( - title = "测试场景", - type = EditType.INPUT, search = @Search, notNull = true - ) - ) - private String testScenario; - - @EruptField( - views = @View( - title = "测试场景详细描述" - ), - edit = @Edit( - title = "测试场景详细描述", - type = EditType.TEXTAREA, search = @Search, notNull = true - ) - ) - private String details; - - @EruptField( - views = @View( - title = "优先级" - ), - edit = @Edit( - title = "优先级", - type = EditType.TAGS, - search = @Search, - tagsType = @TagsType( - fetchHandler = SqlTagFetchHandler.class, - fetchHandlerParams = "select distinct key,detail from master_data where category_code = 'PRIORITY' order by 1 " - ) - ) - ) - private String priority = "P2"; - - @EruptField( - views = @View(title = "场景参数"), - edit = @Edit(title = "场景参数", - type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) - ) - private String scenarioParameters; - - @JoinTable(name = "api_test_scenario_steps", - joinColumns = @JoinColumn(name = "api_test_scenario_id", referencedColumnName = "id"), - inverseJoinColumns = @JoinColumn(name = "api_test_step_id", referencedColumnName = "id")) - @ManyToMany(fetch = FetchType.EAGER) - @EruptField( - views = @View(title = "包含用例"), - edit = @Edit( - title = "包含用例", - type = EditType.TAB_TABLE_REFER - ) - ) - private Set testSteps; - public String getPriority() { - return priority; - } - - public void setPriority(String priority) { - this.priority = priority; - } - - - public ProductModuleModel getModule() { - return module; - } - - public void setModule(ProductModuleModel module) { - this.module = module; - } - - public String getTestScenario() { - return testScenario; - } - - public void setTestScenario(String testScenario) { - this.testScenario = testScenario; - } - - public String getDetails() { - return details; - } - - public void setDetails(String details) { - this.details = details; - } - - public String getScenarioParameters() { - return scenarioParameters; - } - - public void setScenarioParameters(String scenarioParameters) { - this.scenarioParameters = scenarioParameters; - } - - public Set getTestSteps() { - return testSteps; - } - - public void setTestSteps(Set testSteps) { - this.testSteps = testSteps; - } -} \ No newline at end of file diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RawApiTestCase.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RawApiTestCase.java deleted file mode 100644 index 9498f09..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RawApiTestCase.java +++ /dev/null @@ -1,96 +0,0 @@ -package io.fluent.qtm.api.model; - -import lombok.Data; -import org.hibernate.annotations.DynamicInsert; -import org.hibernate.annotations.DynamicUpdate; -import xyz.erupt.annotation.Erupt; -import xyz.erupt.annotation.EruptField; -import xyz.erupt.annotation.sub_erupt.Layout; -import xyz.erupt.annotation.sub_erupt.Power; -import xyz.erupt.annotation.sub_field.Edit; -import xyz.erupt.annotation.sub_field.EditType; -import xyz.erupt.annotation.sub_field.View; -import xyz.erupt.annotation.sub_field.ViewType; -import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; -import xyz.erupt.annotation.sub_field.sub_edit.Search; -import xyz.erupt.jpa.model.MetaModel; - -import javax.persistence.Entity; -import javax.persistence.Table; - - -@DynamicUpdate -@DynamicInsert -@Entity -@Table(name = "raw_api_cases") -@Erupt( - name = "接口测试用例生成", layout = @Layout( - tableLeftFixed = 3, - pageSize = 30), power = @Power(importable = true, export = true), - orderBy = "RawApiTestCase.createTime " -) -@Data -public class RawApiTestCase extends MetaModel{ - @EruptField( - views = @View(title = "测试场景"), - edit = @Edit(title = "测试场景", search = @Search) - ) - private String scenario; - - @EruptField( - views = @View(title = "服务名称"), - edit = @Edit(title = "服务名称", notNull = true, search = @Search) - ) - private String serviceName; - - @EruptField( - views = @View(title = "API接口"), - edit = @Edit(title = "API接口", notNull = true, search = @Search) - ) - private String serviceMethod; - - @EruptField( - views = @View(title = "用例名称"), - edit = @Edit(title = "用例名称", notNull = true, search = @Search) - ) - private String name; - - @EruptField( - views = @View(title = "请求路径"), - edit = @Edit(title = "请求路径", notNull = true, search = @Search) - ) - private String uri; - - @EruptField( - views = @View(title = "请求方法"), - edit = @Edit(title = "请求方法", notNull = true) - ) - private String method = "POST"; - - @EruptField( - views = @View(title = "输入", type = ViewType.CODE), - edit = @Edit(title = "输入", - type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) - ) - private String input; - - @EruptField( - views = @View(title = "期望结果", type = ViewType.CODE), - edit = @Edit(title = "期望结果", type = EditType.CODE_EDITOR, - codeEditType = @CodeEditorType(language = "json")) - ) - private String expected; - - @EruptField( - views = @View(title = "优先级"), - edit = @Edit(title = "优先级") - ) - private String priority = "P1"; - - @EruptField( - views = @View(title = "是否运行"), - edit = @Edit(title = "是否运行") - ) - private boolean isRun = true; - -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RemoteApi.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RemoteApi.java deleted file mode 100644 index 84763bd..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RemoteApi.java +++ /dev/null @@ -1,275 +0,0 @@ -package io.fluent.qtm.api.model; - -import cn.hutool.core.lang.UUID; -import io.fluent.qtm.api.handler.GenerateRawApiCaseHandler; -import io.fluent.base.handlers.SqlTagFetchHandler; -import io.fluent.base.model.ModelWithValidFlag; -import lombok.Data; -import org.hibernate.annotations.DynamicInsert; -import org.hibernate.annotations.DynamicUpdate; -import org.hibernate.annotations.SQLDelete; -import org.hibernate.annotations.Where; -import xyz.erupt.annotation.Erupt; -import xyz.erupt.annotation.EruptField; -import xyz.erupt.annotation.sub_erupt.Layout; -import xyz.erupt.annotation.sub_erupt.Power; -import xyz.erupt.annotation.sub_erupt.RowOperation; -import xyz.erupt.annotation.sub_field.Edit; -import xyz.erupt.annotation.sub_field.EditType; -import xyz.erupt.annotation.sub_field.View; -import xyz.erupt.annotation.sub_field.ViewType; -import xyz.erupt.annotation.sub_field.sub_edit.*; -import xyz.erupt.toolkit.handler.SqlChoiceFetchHandler; - -import javax.persistence.Entity; -import javax.persistence.Table; - -@DynamicUpdate -@DynamicInsert -@Entity -@Table(name = "remote_services") -@Erupt( - name = "远程服务清单", layout = @Layout( - tableLeftFixed = 3, - pageSize = 30), - power = @Power(export = true), - rowOperation = {@RowOperation( - title = "生成原始接口用例", - operationHandler = GenerateRawApiCaseHandler.class)} -) -@Data -@SQLDelete(sql = "update remote_services set valid=false where id=?") -@Where(clause = "valid = true") -public class RemoteApi extends ModelWithValidFlag { - @EruptField( - views = @View( - title = "名称" - ), - edit = @Edit( - title = "名称", - type = EditType.INPUT, search = @Search, notNull = true, - inputType = @InputType - ) - ) - private String name; - - @EruptField( - views = @View( - title = "产品" - ), - edit = @Edit( - title = "产品", - type = EditType.CHOICE, - desc = "获取产品", - choiceType = @ChoiceType( - fetchHandler = SqlChoiceFetchHandler.class, - fetchHandlerParams = "select id,name,details from products where valid =true and parent_id is NULL" - )) - ) - private Long productId; - - @EruptField( - views = @View(title = "模块名"), - edit = @Edit( - search = @Search, - title = "获取可选模块", - type = EditType.TAGS, - desc = "动态获取可选模块", - tagsType = @TagsType( - fetchHandler = SqlTagFetchHandler.class, - fetchHandlerParams = "select distinct module_name from remote_services where valid=true" - )) - ) - private String moduleName; - - @EruptField( - views = @View(title = "服务"), - edit = @Edit( - search = @Search, - title = "获取可选服务", - type = EditType.TAGS, - desc = "获取可选服务", - tagsType = @TagsType( - fetchHandler = SqlTagFetchHandler.class, - fetchHandlerParams = "select distinct service_name from remote_services where valid=true" - )) - ) - private String serviceName; - - @EruptField( - views = @View(title = "地址"), - edit = @Edit(title = "地址", notNull = true) - ) - private String endpoint; - - @EruptField( - views = @View(title = "方法"), - edit = @Edit(title = "方法", notNull = true, search = @Search) - ) - private String serviceMethod; - - @EruptField( - views = @View(title = "http请求方法"), - edit = @Edit(title = "http请求方法", notNull = true) - ) - private String httpMethod; - - @EruptField( - views = @View(title = "请求报文", type = ViewType.CODE), - edit = @Edit(title = "请求报文", type = EditType.CODE_EDITOR, codeEditType = @CodeEditorType(language = "json")) - ) - private String body; - - @EruptField( - views = @View(title = "接口类型"), - edit = @Edit(title = "接口类型", search = @Search, - type = EditType.CHOICE, choiceType = @ChoiceType( - vl = {@VL(value = "API", label = "API"), @VL(value = "RPC", label = "RPC")} - )) - ) - private String type; - - @EruptField( - views = @View(title = "服务使用状态"), - edit = @Edit(title = "服务使用状态", - search = @Search, notNull = true, - type = EditType.CHOICE, choiceType = @ChoiceType( - vl = {@VL(value = "NEW", label = "新增"), - @VL(value = "IN_USE", label = "使用中"), - @VL(value = "UPDATE", label = "更新"), - @VL(value = "END_OF_LIFE", label = "已作废"),}))) - private String status; - - @EruptField( - views = @View( - title = "引入时版本" - ), - edit = @Edit( - title = "引入时版本", - type = EditType.INPUT, search = @Search, notNull = true, - inputType = @InputType - ) - ) - private String addedVersion; - - @EruptField( - views = @View( - title = "最新版本" - ), - edit = @Edit( - title = "最新版本", - type = EditType.INPUT, search = @Search, notNull = true, - inputType = @InputType - ) - ) - private String latestVersion; - - @EruptField( - views = @View(show = false, title = "uid") - ) - private String uId = UUID.fastUUID().toString(); - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Long getProductId() { - return productId; - } - - public void setProductId(Long productId) { - this.productId = productId; - } - - public String getModuleName() { - return moduleName; - } - - public void setModuleName(String moduleName) { - this.moduleName = moduleName; - } - - public String getServiceName() { - return serviceName; - } - - public void setServiceName(String serviceName) { - this.serviceName = serviceName; - } - - public String getEndpoint() { - return endpoint; - } - - public void setEndpoint(String endpoint) { - this.endpoint = endpoint; - } - - public String getServiceMethod() { - return serviceMethod; - } - - public void setServiceMethod(String serviceMethod) { - this.serviceMethod = serviceMethod; - } - - public String getHttpMethod() { - return httpMethod; - } - - public void setHttpMethod(String httpMethod) { - this.httpMethod = httpMethod; - } - - public String getBody() { - return body; - } - - public void setBody(String body) { - this.body = body; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getStatus() { - return status; - } - - public void setStatus(String status) { - this.status = status; - } - - public String getAddedVersion() { - return addedVersion; - } - - public void setAddedVersion(String addedVersion) { - this.addedVersion = addedVersion; - } - - public String getLatestVersion() { - return latestVersion; - } - - public void setLatestVersion(String latestVersion) { - this.latestVersion = latestVersion; - } - - public String getuId() { - return uId; - } - - public void setuId(String uId) { - this.uId = uId; - } -} \ No newline at end of file diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RemoteApiStatus.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RemoteApiStatus.java deleted file mode 100644 index 4b66a5c..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RemoteApiStatus.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.fluent.qtm.api.model; - -public enum RemoteApiStatus { - NEW,IN_USE,UPDATED,END_OF_LIFE -} \ No newline at end of file diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RemoteApiType.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RemoteApiType.java deleted file mode 100644 index 74c2557..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/model/RemoteApiType.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.fluent.qtm.api.model; - -public enum RemoteApiType { - API,RPC -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/package-info.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/package-info.java deleted file mode 100644 index 73f4e8e..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package io.fluent.qtm.api; \ No newline at end of file diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiMonitorRecordRepo.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiMonitorRecordRepo.java deleted file mode 100644 index e03552b..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiMonitorRecordRepo.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.fluent.qtm.api.repo; - -import io.fluent.qtm.api.model.ApiMonitorRecord; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -import org.springframework.stereotype.Repository; - -import java.util.List; - -@Repository -public interface ApiMonitorRecordRepo extends JpaRepository, JpaSpecificationExecutor { - - List findApiMonitorRecordByPath(String path); -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiSpecChangeRepository.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiSpecChangeRepository.java deleted file mode 100644 index ff3cdf3..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiSpecChangeRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.fluent.qtm.api.repo; - - -import io.fluent.qtm.api.model.ApiSpecChangeModel; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface ApiSpecChangeRepository extends JpaRepository { - -} \ No newline at end of file diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiSpecGitRepoRepository.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiSpecGitRepoRepository.java deleted file mode 100644 index 4a6af7a..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiSpecGitRepoRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.fluent.qtm.api.repo; - -import io.fluent.qtm.api.model.ApiSpecGitRepoModel; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface ApiSpecGitRepoRepository extends JpaRepository { - -} \ No newline at end of file diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiSpecVersionRepository.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiSpecVersionRepository.java deleted file mode 100644 index 361f79d..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiSpecVersionRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.fluent.qtm.api.repo; - -import io.fluent.qtm.api.model.ApiSpecVersionModel; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface ApiSpecVersionRepository extends JpaRepository { - -} \ No newline at end of file diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiTestScenarioRepo.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiTestScenarioRepo.java deleted file mode 100644 index e389772..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiTestScenarioRepo.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.fluent.qtm.api.repo; - -import io.fluent.qtm.api.model.ApiTestScenario; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -import org.springframework.stereotype.Repository; - -@Repository -public interface ApiTestScenarioRepo extends JpaRepository, JpaSpecificationExecutor { -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiTestStepRepo.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiTestStepRepo.java deleted file mode 100644 index f867291..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/ApiTestStepRepo.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.fluent.qtm.api.repo; - -import io.fluent.qtm.api.model.ApiStep; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -import org.springframework.stereotype.Repository; - -@Repository -public interface ApiTestStepRepo extends JpaRepository, JpaSpecificationExecutor { -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/RawApiTestCaseRepo.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/RawApiTestCaseRepo.java deleted file mode 100644 index fb9e408..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/RawApiTestCaseRepo.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.fluent.qtm.api.repo; - -import io.fluent.qtm.api.model.RawApiTestCase; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -import org.springframework.stereotype.Repository; - -@Repository -public interface RawApiTestCaseRepo extends JpaRepository, JpaSpecificationExecutor { -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/RemoteServiceRepo.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/RemoteServiceRepo.java deleted file mode 100644 index f6f4ad0..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/repo/RemoteServiceRepo.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.fluent.qtm.api.repo; - -import io.fluent.qtm.api.model.RemoteApi; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -import org.springframework.stereotype.Repository; - -import java.util.List; -import java.util.Optional; - -@Repository -public interface RemoteServiceRepo extends JpaRepository, JpaSpecificationExecutor { - - Optional findRemoteApiByEndpointAndServiceNameAndServiceMethod( - String endpoint,String serviceName,String serviceMethod - ); - - Optional> findRemoteApiByModuleNameAndServiceNameAndLatestVersionNot( - String moduleName,String serviceName,String latestVersion - ); - - -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/service/ApiTestCaseService.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/service/ApiTestCaseService.java deleted file mode 100644 index cd465be..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/service/ApiTestCaseService.java +++ /dev/null @@ -1,107 +0,0 @@ -package io.fluent.qtm.api.service; - -import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.util.StrUtil; -import io.fluent.builtin.CollectionsUtils; -import io.fluent.qtm.api.model.*; -import io.fluent.qtm.api.repo.ApiMonitorRecordRepo; -import io.fluent.qtm.api.repo.ApiTestStepRepo; -import io.fluent.qtm.api.repo.RawApiTestCaseRepo; -import org.springframework.beans.BeanUtils; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; - -@Service -public class ApiTestCaseService { - private final String DEFAULT_EXPECTED = "{ \"status_code\":200,\"values\":{}\n" + - "}"; - @Resource - RawApiTestCaseRepo rawApiTestCaseRepo; - - @Resource - ApiMonitorRecordRepo apiMonitorRecordRepo; - - @Resource - ApiTestStepRepo apiTestStepRepo; - - - /** - * @param services: HTTP API Services - * 1. Merge All HttpAPI Services if method,service and request-body/input is same - */ - public void convertToRawTestCase(List services) { - List cases = new ArrayList<>(); - services.forEach(service -> { - RawApiTestCase apiCase = new RawApiTestCase(); - BeanUtils.copyProperties(service, apiCase); - apiCase.setUri(service.getEndpoint()); - apiCase.setExpected(DEFAULT_EXPECTED); - //get body - String path = service.getEndpoint().replaceAll("https://\\{\\{base_url\\}\\}", ""); - List result = apiMonitorRecordRepo.findApiMonitorRecordByPath(path); - if (!result.isEmpty()) { - apiCase.setInput(result.get(0).getRequestBody()); - } else { - apiCase.setInput(service.getBody()); - } - cases.add(apiCase); - }); - rawApiTestCaseRepo.saveAll(cases); - } - - /** - * @param records Http traffic - * 1. Merge All HttpAPI Services if method,service and request-body/input is same - */ - public void convertApiMonitorRecordToTestCase(List records) { - List cases = new ArrayList<>(); - List result = CollectionsUtils.filterToReduceRedundant( - records, new Function() { - @Override - public String apply(ApiMonitorRecord apiMonitorRecord) { - return StrUtil.join( - "-", apiMonitorRecord.getApi(), apiMonitorRecord.getApp(), - apiMonitorRecord.getService(), apiMonitorRecord.getPath(), - apiMonitorRecord.getMethod(), apiMonitorRecord.getRequestBody() - ); - } - } - ); - result.forEach(service -> { - RawApiTestCase apiCase = new RawApiTestCase(); - BeanUtils.copyProperties(service, apiCase); - apiCase.setUri(service.getPath()); - apiCase.setExpected(DEFAULT_EXPECTED); - apiCase.setServiceMethod(service.getMethod()); - apiCase.setServiceName(service.getService()); - apiCase.setName(service.getService()); - apiCase.setServiceMethod(service.getApi()); - //get body - apiCase.setInput(service.getRequestBody()); - apiCase.setScenario(service.getRecordName()); - cases.add(apiCase); - }); - rawApiTestCaseRepo.saveAll(cases); - } - - /** - * Only Passed Test Scenario could be converted into test steps - * - * @param data - */ - public void convertApiTestResultToApiTestStep(List data) { - List apiTestSteps = new ArrayList<>(); - data.forEach((apiTestRecord) -> { - if (apiTestRecord.isSuccess()) { - apiTestSteps.add(BeanUtil.copyProperties(apiTestRecord, ApiStep.class)); - } else { - throw new RuntimeException("some cases are failed, can't convert to api test step"); - } - }); - apiTestStepRepo.saveAll(apiTestSteps); - } -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/service/RemoteApiService.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/api/service/RemoteApiService.java deleted file mode 100644 index 27c3803..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/api/service/RemoteApiService.java +++ /dev/null @@ -1,142 +0,0 @@ -package io.fluent.qtm.api.service; - -import cn.hutool.core.bean.BeanUtil; -import io.fluent.postman.PostmanParser; -import io.fluent.postman.model.PostmanCollection; -import io.fluent.postman.model.PostmanItem; -import io.fluent.qtm.api.model.ApiSpecVersionModel; -import io.fluent.qtm.api.model.RemoteApi; -import io.fluent.qtm.api.model.RemoteApiStatus; -import io.fluent.qtm.api.repo.RemoteServiceRepo; -import io.fluent.base.product.model.ProductModuleModel; -import io.fluent.base.product.service.ProductModuleService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import javax.transaction.Transactional; -import java.util.List; -import java.util.Optional; - -@Service -@Slf4j -public class RemoteApiService { - - @Autowired - private ProductModuleService productMetaService; - - @Autowired - private RemoteServiceRepo remoteServiceRepo; - - - /** - * 1. 解析postman 文件 - * 2. 确认接口是 - * - 新增:NEW: 当前记录无记录,则更新 - * - 更新:UPDATED: 当前记录中有记录,但是接口定义发生变化,比如请求内容 - * - 使用中,IN_USE: - * - 移除:END_OF_LIFE: 接口不在最新清单中 - * 3. - * - * @param apiSpec - */ - @Transactional - public void apiSpecToApiList(ApiSpecVersionModel apiSpec, String updater) { - ProductModuleModel productMeta = productMetaService.createApiModuleIfNotExist(apiSpec.getName(), updater); - if (apiSpec.getSpec().isEmpty()) return; - PostmanCollection collection = PostmanParser.create().toPostmanCollection(apiSpec.getSpec()); - for (PostmanItem postmanItem : collection.getItem()) { - for (PostmanItem item : postmanItem.getItem()) { - RemoteApi rs = toRemoteApi(apiSpec, productMeta, postmanItem, item); - createOrUpdateRemoteApi(rs, apiSpec); - } - updateStatusToEndOfLife(apiSpec, postmanItem); - } - } - - @Transactional - public void apiSpecsToApiList(List apiSpecs, String updater) { - for (ApiSpecVersionModel apiSpec : apiSpecs) { - try { - this.apiSpecToApiList(apiSpec, updater); - } catch (Exception e) { - log.error("%s-api-failed,error=%s".formatted( - apiSpec.getName(), e.getMessage() - )); - } - } - } - - private RemoteApi toRemoteApi(ApiSpecVersionModel apiSpec, ProductModuleModel productMeta, - PostmanItem postmanItem, PostmanItem item) { - RemoteApi rs = new RemoteApi(); - rs.setName(item.getName()); - rs.setServiceName(postmanItem.getName()); - rs.setServiceMethod(item.getName()); - rs.setBody(item.getRequest().getBody().get("raw").toString()); - rs.setHttpMethod(item.getRequest().getMethod()); - rs.setEndpoint(item.getRequest().getUrl().getRaw()); - rs.setType(apiSpec.getServiceType()); - rs.setModuleName(apiSpec.getName()); - rs.setProductId(productMeta.getParent().getId()); - return rs; - } - - /** - * create or update 基本可以使用同一种方式处理 - * 1. 输入实体 - * 2. 字段检验规则 - * 3. 判断重复确认条件 - * 4. 更新字段处理,保存记录 - * TODO: try to integrate with Feishu - * - * @param newApi - */ - public void createOrUpdateRemoteApi(RemoteApi newApi, ApiSpecVersionModel apiSpec) { - Optional api = remoteServiceRepo.findRemoteApiByEndpointAndServiceNameAndServiceMethod( - newApi.getEndpoint(), - newApi.getServiceName(), - newApi.getServiceMethod()); - if (api.isEmpty()) { - newApi.setStatus(RemoteApiStatus.NEW.toString()); - newApi.setAddedVersion(apiSpec.getAppVersion()); - newApi.setLatestVersion(apiSpec.getAppVersion()); - remoteServiceRepo.save(newApi); - } else { - RemoteApi existApi = api.get(); - BeanUtil.copyProperties(newApi, existApi, "id"); - //TODO: 如何确认接口变更-暂时不确认 - if (existApi.getBody().equalsIgnoreCase(newApi.getBody())) { - existApi.setStatus(RemoteApiStatus.IN_USE.toString()); - existApi.setLatestVersion(apiSpec.getAppVersion()); - } else { - existApi.setStatus(RemoteApiStatus.UPDATED.toString()); - existApi.setLatestVersion(apiSpec.getAppVersion()); - } - remoteServiceRepo.save(existApi); - } - } - - public void updateStatusToEndOfLife(ApiSpecVersionModel apiSpec, PostmanItem postmanItem) { - Optional> apiListOptional = remoteServiceRepo.findRemoteApiByModuleNameAndServiceNameAndLatestVersionNot( - apiSpec.getName(), - postmanItem.getName(), - apiSpec.getAppVersion() - ); - if (apiListOptional.isEmpty()) { - log.info("没有需要删除的数据"); - } else { - List apiList = apiListOptional.get(); - for (RemoteApi api : apiList) { - api.setStatus(RemoteApiStatus.END_OF_LIFE.toString()); - remoteServiceRepo.save(api); - log.info("有需要删除的数据"); - } - - } - } - - -} - - diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/package-info.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/package-info.java deleted file mode 100644 index b8c6392..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package io.fluent.qtm.pm; \ No newline at end of file diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/FieldOption.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/FieldOption.java deleted file mode 100644 index d0727ce..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/FieldOption.java +++ /dev/null @@ -1,158 +0,0 @@ -package io.fluent.qtm.pm.requirement; - - -import io.fluent.base.model.ModelWithValidFlag; -import xyz.erupt.annotation.Erupt; -import xyz.erupt.annotation.EruptField; -import xyz.erupt.annotation.fun.ChoiceFetchHandler; -import xyz.erupt.annotation.fun.VLModel; -import xyz.erupt.annotation.sub_erupt.Power; -import xyz.erupt.annotation.sub_field.Edit; -import xyz.erupt.annotation.sub_field.EditType; -import xyz.erupt.annotation.sub_field.View; -import xyz.erupt.annotation.sub_field.sub_edit.ChoiceType; -import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; - -import javax.persistence.Entity; -import javax.persistence.Table; -import java.util.ArrayList; -import java.util.List; - -@Entity -@Erupt(name = "字段选项", - power = @Power(export = true, importable = true), - orderBy = "FieldOption.updateTime desc") -@Table(name = "field_option") -public class FieldOption extends ModelWithValidFlag implements ChoiceFetchHandler { - @EruptField( - views = @View( - title = "名称" - ), - edit = @Edit( - title = "名称",notNull = true - ) - ) - private String name; - @EruptField( - views = @View( - title = "字段code" - ), - edit = @Edit( - title = "字段code" - ) - ) - private String code; - - @EruptField( - views = @View(title = "编辑类型"), - edit = @Edit(title = "编辑类型", - notNull = true, type = EditType.CHOICE, - choiceType = @ChoiceType(type = ChoiceType.Type.RADIO, fetchHandler = FieldOption.class)) - ) - private String type; - - @EruptField( - views = @View( - title = "是否可以为空" - ), - edit = @Edit( - title = "是否可以为空", - type = EditType.BOOLEAN - ) - ) - private boolean notNull; - @EruptField( - views = @View( - title = "字段约束条件" - ), - edit = @Edit( - title = "字段约束条件", - type = EditType.CODE_EDITOR, notNull = true, - codeEditType = @CodeEditorType(language = "text"), - desc = "字段约束条件" - ) - ) - private String constrains; - - @EruptField( - views = @View( - title = "和其他业务关系" - ), - edit = @Edit( - title = "和其他业务关系", - type = EditType.CODE_EDITOR, - codeEditType = @CodeEditorType(language = "text") - ) - ) - private String relatedTo; - - @EruptField( - views = @View(title = "显示顺序", sortable = true), - edit = @Edit(title = "显示顺序", notNull = true) - ) - private Integer sort; - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getCode() { - return code; - } - - public void setCode(String code) { - this.code = code; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public boolean isNotNull() { - return notNull; - } - - public void setNotNull(boolean notNull) { - this.notNull = notNull; - } - - public String getConstrains() { - return constrains; - } - - public void setConstrains(String constrains) { - this.constrains = constrains; - } - - public String getRelatedTo() { - return relatedTo; - } - - public void setRelatedTo(String relatedTo) { - this.relatedTo = relatedTo; - } - - public Integer getSort() { - return sort; - } - - public void setSort(Integer sort) { - this.sort = sort; - } - - @Override - public List fetch(String[] params) { - List list = new ArrayList<>(); - for (FieldOptionType value : FieldOptionType.values()) { - list.add(new VLModel(value.name(), value.getDesc())); - } - return list; - } -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/FieldOptionType.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/FieldOptionType.java deleted file mode 100644 index 69b306b..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/FieldOptionType.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.fluent.qtm.pm.requirement; - -import lombok.Getter; - -@Getter -public enum FieldOptionType { - NUMBER("数值"),STRING("字符串"),RELATION("关联"),DATE("日期"),ENUM("枚举"),BOOLEAN("布尔"); - - private String desc; - FieldOptionType(String desc) { - this.desc = desc; - } -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/RequirementFeature.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/RequirementFeature.java deleted file mode 100644 index a91ffc6..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/RequirementFeature.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.fluent.qtm.pm.requirement; - -import io.fluent.base.model.ModelWithValidFlag; -import xyz.erupt.annotation.Erupt; -import xyz.erupt.annotation.EruptField; -import xyz.erupt.annotation.sub_erupt.Power; -import xyz.erupt.annotation.sub_field.Edit; -import xyz.erupt.annotation.sub_field.EditType; -import xyz.erupt.annotation.sub_field.View; -import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; - -import javax.persistence.Entity; -import javax.persistence.Table; - -@Entity -@Erupt(name = "需求功能点", - power = @Power(export = true, importable = true), - orderBy = "RequirementFeature.updateTime desc") -@Table(name = "requirement_features") -public class RequirementFeature extends ModelWithValidFlag { - - @EruptField( - views = @View( - title = "业务功能相关说明" - ), - edit = @Edit( - title = "业务功能相关说明", - type = EditType.CODE_EDITOR, notNull = true, - codeEditType = @CodeEditorType(language = "text") - ) - ) - private String feature; - -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/RequirementType.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/RequirementType.java deleted file mode 100644 index 23f7c44..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/RequirementType.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.fluent.qtm.pm.requirement; - -import lombok.Getter; - -@Getter -public enum RequirementType { - CREATE("创建"),UPDATE("更新"),DELETE("删除/归档"),SEARCH("查询"), COMPLEX("复杂业务"), - WORKFLOW("工作流"), - REPORT("报表"),OTHER("其他"); - - private String desc; - RequirementType(String desc) { - this.desc = desc; - } -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/TestRequirement.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/TestRequirement.java deleted file mode 100644 index 31a5222..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/pm/requirement/TestRequirement.java +++ /dev/null @@ -1,192 +0,0 @@ -package io.fluent.qtm.pm.requirement; - - -import io.fluent.base.handlers.SqlTagFetchHandler; -import io.fluent.base.model.ModelWithValidFlag; -import io.fluent.base.product.model.ProductModuleModel; -import xyz.erupt.annotation.Erupt; -import xyz.erupt.annotation.EruptField; -import xyz.erupt.annotation.fun.ChoiceFetchHandler; -import xyz.erupt.annotation.fun.VLModel; -import xyz.erupt.annotation.sub_erupt.LinkTree; -import xyz.erupt.annotation.sub_erupt.Power; -import xyz.erupt.annotation.sub_field.Edit; -import xyz.erupt.annotation.sub_field.EditType; -import xyz.erupt.annotation.sub_field.View; -import xyz.erupt.annotation.sub_field.sub_edit.ChoiceType; -import xyz.erupt.annotation.sub_field.sub_edit.ReferenceTreeType; -import xyz.erupt.annotation.sub_field.sub_edit.Search; -import xyz.erupt.annotation.sub_field.sub_edit.TagsType; - -import javax.persistence.*; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - - -@Entity -@Erupt(name = "测试需求管理", - power = @Power(export = true, importable = true), - orderBy = "TestRequirement.updateTime desc", - linkTree = @LinkTree(field = "module")) -@Table(name = "test_requirements") -public class TestRequirement extends ModelWithValidFlag implements ChoiceFetchHandler { - - @EruptField( - views = @View(title = "需求概述"), - edit = @Edit(title = "需求概述") - ) - private String summary; - - @EruptField( - views = @View(title = "需求类型"), - edit = @Edit(title = "需求类型", - notNull = true, type = EditType.CHOICE, - choiceType = @ChoiceType(type = ChoiceType.Type.RADIO, - fetchHandler = TestRequirement.class)) - ) - private String type; - - @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) - @JoinColumn(name = "test_req_id") - @EruptField( - edit = @Edit(title = "功能点", type = EditType.TAB_TABLE_ADD) - ) - private Set features; - - - @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) - @JoinColumn(name = "test_req_id") - @OrderBy("sort") - @EruptField( - edit = @Edit(title = "字段管理", type = EditType.TAB_TABLE_ADD) - ) - private Set fieldOptions; - - @ManyToOne - @JoinColumn(name = "product_id") - @EruptField( - views = @View(title = "所属模块", column = "details"), - edit = @Edit( - notNull = true, - search = @Search, - title = "产品模块选择", - type = EditType.REFERENCE_TREE, - desc = "动态获取产品", - referenceTreeType = @ReferenceTreeType(id = "id", label = "name", - pid = "parent.id")) - ) - private ProductModuleModel module; - - @EruptField( - views = @View( - title = "提示词" - ), - edit = @Edit( - title = "提示词" - ) - ) - private String prompts; - - @EruptField( - views = @View( - title = "优先级" - ), - edit = @Edit( - search = @Search, - title = "优先级", - type = EditType.TAGS, - tagsType = @TagsType( - fetchHandler = SqlTagFetchHandler.class, - fetchHandlerParams = "select distinct key,detail from master_data where category_code = 'PRIORITY' order by 1 " - ) - ) - ) - private String priority = "P2"; - - @EruptField( - views = @View( - title = "需求状态" - ), - edit = @Edit( - title = "需求状态" - ) - ) - private String status; - - @Override - public List fetch(String[] params) { - List list = new ArrayList<>(); - for (RequirementType value : RequirementType.values()) { - list.add(new VLModel(value.name(), value.getDesc())); - } - return list; - } - - public String getPriority() { - return priority; - } - - public void setPriority(String priority) { - this.priority = priority; - } - - - public String getPrompts() { - return prompts; - } - - public void setPrompts(String prompts) { - this.prompts = prompts; - } - - - public Set getFieldOptions() { - return fieldOptions; - } - - public void setFieldOptions(Set fieldOptions) { - this.fieldOptions = fieldOptions; - } - - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public Set getFeatures() { - return features; - } - - public void setFeatures(Set features) { - this.features = features; - } - - public String getSummary() { - return summary; - } - - public void setSummary(String summary) { - this.summary = summary; - } - - public String getStatus() { - return status; - } - - public void setStatus(String status) { - this.status = status; - } - - public ProductModuleModel getModule() { - return module; - } - - public void setModule(ProductModuleModel module) { - this.module = module; - } -} \ No newline at end of file diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/dto/TestCaseDTO.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/dto/TestCaseDTO.java deleted file mode 100644 index bc776ff..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/dto/TestCaseDTO.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.fluent.qtm.tc.dto; - -import com.github.crab2died.annotation.ExcelField; -import lombok.Data; - -@Data -public class TestCaseDTO { - @ExcelField(title = "产品名称") - private String productName; - @ExcelField(title = "模块名称") - private String moduleName; - @ExcelField(title = "功能点") - private String feature; - @ExcelField(title = "用例描述") - private String summary; - @ExcelField(title = "优先级") - private String priority = "P2"; //check it - @ExcelField(title = "用例前提条件") - private String precondition; - @ExcelField(title = "测试步骤") - private String steps; - @ExcelField(title = "期望结果") - private String expectedResult; - - @ExcelField(title = "用例ID") - private String uuid; - -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/handlers/GenerateTestRecordHandler.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/handlers/GenerateTestRecordHandler.java deleted file mode 100644 index cc87109..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/handlers/GenerateTestRecordHandler.java +++ /dev/null @@ -1,49 +0,0 @@ -package io.fluent.qtm.tc.handlers; - - -import cn.hutool.core.bean.BeanUtil; -import io.fluent.qtm.tc.model.TestCase; -import io.fluent.qtm.tc.model.TestResult; -import io.fluent.qtm.tc.model.TestRun; -import io.fluent.qtm.tc.model.TestScenario; -import io.fluent.qtm.tc.repo.TestResultRepo; - -import org.springframework.stereotype.Service; -import xyz.erupt.annotation.fun.OperationHandler; - - -import javax.annotation.Resource; -import java.util.List; - -@Service -public class GenerateTestRecordHandler implements OperationHandler { - @Resource - private TestResultRepo testResultRepo; - - @Override - public String exec(List data, Void unused, String[] param) { - for (TestRun testRun : data) { - //get all test cases - for (TestCase testCase : testRun.getTestCases()) { - TestResult result = BeanUtil.copyProperties(testCase, TestResult.class); - result.setTestCaseUUID(testCase.getUuid()); - result.setTestRun(testRun); - result.setQaOwner(testRun.getTestOwner()); - testResultRepo.save(result); - } - for (TestScenario tc : testRun.getTestScenarios()) { - for (TestCase testCase : tc.getTestCases()) { - TestResult result = BeanUtil.copyProperties(testCase, TestResult.class); - result.setTestCaseUUID(testCase.getUuid()); - result.setTestRun(testRun); - result.setQaOwner(testRun.getTestOwner()); - result.setTestScenario(tc.getName()); - testResultRepo.save(result); - } - } - } - return null; - } - - -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestCase.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestCase.java deleted file mode 100644 index 2db1f68..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestCase.java +++ /dev/null @@ -1,238 +0,0 @@ -package io.fluent.qtm.tc.model; - -import io.fluent.base.handlers.SqlTagFetchHandler; -import io.fluent.base.model.ModelWithValidFlagVo; -import io.fluent.base.product.model.ProductModuleModel; -import xyz.erupt.annotation.Erupt; -import xyz.erupt.annotation.EruptField; -import xyz.erupt.annotation.sub_erupt.Layout; -import xyz.erupt.annotation.sub_erupt.LinkTree; -import xyz.erupt.annotation.sub_erupt.Power; -import xyz.erupt.annotation.sub_field.Edit; -import xyz.erupt.annotation.sub_field.EditType; -import xyz.erupt.annotation.sub_field.View; -import xyz.erupt.annotation.sub_field.sub_edit.*; - -import javax.persistence.Entity; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.Table; - -/** - * - */ -@Entity -@Erupt(name = "测试用例", - power = @Power(export = true), - orderBy = "TestCase.updateTime desc", - linkTree = @LinkTree(field = "module"),layout = @Layout( - tableLeftFixed = 3, - pageSize = 30)) -@Table(name = "test_cases") -public class TestCase extends ModelWithValidFlagVo { - - @ManyToOne - @JoinColumn(name = "product_id") - @EruptField( - views = @View(title = "所属模块",column = "details"), - edit = @Edit( - notNull = true, - search = @Search, - title = "产品模块选择", - type = EditType.REFERENCE_TREE, - desc = "动态获取产品", - referenceTreeType = @ReferenceTreeType(id = "id", label = "name", - pid = "parent.id")) - ) - private ProductModuleModel module; - - @ManyToOne - @JoinColumn(name = "parent_product_id") - @EruptField( - views = @View(title = "父模块",column = "details"), - edit = @Edit( - notNull = true, - search = @Search, - title = "产品模块选择", - type = EditType.REFERENCE_TREE, - desc = "动态获取产品", - referenceTreeType = @ReferenceTreeType(id = "id", label = "name", - pid = "parent.id")) - ) - private ProductModuleModel parent; - - @ManyToOne - @JoinColumn(name = "root_product_id") - @EruptField( - views = @View(title = "所属产品",column = "details"), - edit = @Edit( - notNull = true, - search = @Search, - title = "产品模块选择", - type = EditType.REFERENCE_TREE, - desc = "动态获取产品", - referenceTreeType = @ReferenceTreeType(id = "id", label = "name", - pid = "parent.id")) - ) - private ProductModuleModel product; - - @EruptField( - views = @View( - title = "功能点" - ), - edit = @Edit( - title = "功能点", - type = EditType.INPUT, search = @Search, notNull = true - ) - ) - private String feature; - @EruptField( - views = @View( - title = "用例描述" - ), - edit = @Edit( - title = "用例描述", - type = EditType.INPUT, notNull = true - ) - ) - private String summary; - - @EruptField( - views = @View( - title = "优先级" - ), - edit = @Edit( - title = "优先级", - type = EditType.TAGS, - search = @Search, - tagsType = @TagsType( - fetchHandler = SqlTagFetchHandler.class, - fetchHandlerParams = "select distinct key,detail from master_data where category_code = 'PRIORITY' order by 1 " - ) - ) - ) - private String priority = "P2"; - - - @EruptField( - views = @View( - title = "测试步骤" - ), - edit = @Edit( - title = "测试步骤", - type = EditType.CODE_EDITOR, notNull = true, - codeEditType = @CodeEditorType(language = "text") - ) - ) - private String steps; - @EruptField( - views = @View( - title = "期望结果" - ), - edit = @Edit( - title = "期望结果", - type = EditType.CODE_EDITOR, - codeEditType = @CodeEditorType(language = "text") - ) - ) - private String expectedResult; - - @EruptField( - views = @View( - title = "用例ID" - ) - ) - private String uuid; - @EruptField( - views = @View( - title = "用例前提条件" - ), - edit = @Edit( - title = "用例前提条件", - type = EditType.CODE_EDITOR, - codeEditType = @CodeEditorType(language = "text") - ) - ) - private String precondition; - - public String getFeature() { - return feature; - } - - public void setFeature(String feature) { - this.feature = feature; - } - - public String getSummary() { - return summary; - } - - public void setSummary(String summary) { - this.summary = summary; - } - - public String getPriority() { - return priority; - } - - public void setPriority(String priority) { - this.priority = priority; - } - - public String getSteps() { - return steps; - } - - public void setSteps(String steps) { - this.steps = steps; - } - - public String getExpectedResult() { - return expectedResult; - } - - public void setExpectedResult(String expectedResult) { - this.expectedResult = expectedResult; - } - - public String getUuid() { - return uuid; - } - - public void setUuid(String uuid) { - this.uuid = uuid; - } - - public String getPrecondition() { - return precondition; - } - - public void setPrecondition(String precondition) { - this.precondition = precondition; - } - - - public ProductModuleModel getModule() { - return module; - } - - public void setModule(ProductModuleModel module) { - this.module = module; - } - - public ProductModuleModel getParent() { - return parent; - } - - public void setParent(ProductModuleModel parent) { - this.parent = parent; - } - - public ProductModuleModel getProduct() { - return product; - } - - public void setProduct(ProductModuleModel product) { - this.product = product; - } -} \ No newline at end of file diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestResult.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestResult.java deleted file mode 100644 index 63039fb..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestResult.java +++ /dev/null @@ -1,206 +0,0 @@ -package io.fluent.qtm.tc.model; - - -import io.fluent.base.model.ModelWithValidFlagVo; -import xyz.erupt.annotation.Erupt; -import xyz.erupt.annotation.EruptField; -import xyz.erupt.annotation.sub_erupt.LinkTree; -import xyz.erupt.annotation.sub_erupt.Power; -import xyz.erupt.annotation.sub_field.Edit; -import xyz.erupt.annotation.sub_field.EditType; -import xyz.erupt.annotation.sub_field.View; -import xyz.erupt.annotation.sub_field.sub_edit.CodeEditorType; -import xyz.erupt.annotation.sub_field.sub_edit.ReferenceTreeType; -import xyz.erupt.annotation.sub_field.sub_edit.Search; - -import javax.persistence.Entity; -import javax.persistence.ManyToOne; -import javax.persistence.Table; - - -@Table(name = "test_results") -@Entity -@Erupt(name = "测试执行结果", - power = @Power(importable = true, export = true) - , linkTree = @LinkTree(field = "testRun") -) -public class TestResult extends ModelWithValidFlagVo { - @ManyToOne - @EruptField( - views = @View(title = "所属测试安排", column = "name"), - edit = @Edit(title = "所属测试安排", type = EditType.REFERENCE_TREE, - referenceTreeType = @ReferenceTreeType(pid = "parent.id", expandLevel = 2)) - ) - private TestRun testRun; - - @EruptField( - views = @View( - title = "测试场景" - ), - edit = @Edit( - title = "测试场景", - type = EditType.INPUT, search = @Search, notNull = true - ) - ) - private String testScenario; - @EruptField( - views = @View( - title = "功能点" - ), - edit = @Edit( - title = "功能点", - type = EditType.INPUT, search = @Search, notNull = true - ) - ) - private String feature; - @EruptField( - views = @View( - title = "用例描述" - ), - edit = @Edit( - title = "用例描述", - type = EditType.INPUT, notNull = true - ) - ) - private String summary; - @EruptField( - views = @View( - title = "用例优先级" - ), - edit = @Edit( - title = "用例优先级", - type = EditType.INPUT, search = @Search, notNull = true - ) - ) - private String priority = "P2"; //check it - @EruptField( - views = @View( - title = "测试步骤" - ), - edit = @Edit( - title = "测试步骤", - type = EditType.CODE_EDITOR, notNull = true, - codeEditType = @CodeEditorType(language = "text") - ) - ) - private String steps; - @EruptField( - views = @View( - title = "用例期望结果" - ), - edit = @Edit( - title = "用例期望结果", - type = EditType.CODE_EDITOR, notNull = true, - codeEditType = @CodeEditorType(language = "text") - ) - ) - private String expectedResult; - - - @EruptField( - views = @View( - title = "测试测试结果" - ), - edit = @Edit( - title = "测试测试结果", - type = EditType.INPUT, search = @Search, notNull = true - ) - ) - private String qaTestResult; - - - @EruptField( - views = @View( - title = "测试负责人" - ), - edit = @Edit( - title = "测试负责人", - type = EditType.INPUT, search = @Search, notNull = true - ) - ) - private String qaOwner; - - private String testCaseUUID; - - - public String getTestCaseUUID() { - return testCaseUUID; - } - - public void setTestCaseUUID(String testCaseUUID) { - this.testCaseUUID = testCaseUUID; - } - - public String getFeature() { - return feature; - } - - public void setFeature(String feature) { - this.feature = feature; - } - - public String getSummary() { - return summary; - } - - public void setSummary(String summary) { - this.summary = summary; - } - - public String getPriority() { - return priority; - } - - public void setPriority(String priority) { - this.priority = priority; - } - - public String getSteps() { - return steps; - } - - public void setSteps(String steps) { - this.steps = steps; - } - - public String getExpectedResult() { - return expectedResult; - } - - public void setExpectedResult(String expectedResult) { - this.expectedResult = expectedResult; - } - - - public String getQaTestResult() { - return qaTestResult; - } - - public void setQaTestResult(String qaTestResult) { - this.qaTestResult = qaTestResult; - } - - public String getQaOwner() { - return qaOwner; - } - - public void setQaOwner(String qaOwner) { - this.qaOwner = qaOwner; - } - - public TestRun getTestRun() { - return testRun; - } - - public void setTestRun(TestRun testRun) { - this.testRun = testRun; - } - - public String getTestScenario() { - return testScenario; - } - - public void setTestScenario(String testScenario) { - this.testScenario = testScenario; - } -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestRun.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestRun.java deleted file mode 100644 index 47c9af6..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestRun.java +++ /dev/null @@ -1,251 +0,0 @@ -package io.fluent.qtm.tc.model; - - -import io.fluent.base.product.model.ProductModuleModel; -import io.fluent.qtm.tc.handlers.GenerateTestRecordHandler; -import xyz.erupt.annotation.Erupt; -import xyz.erupt.annotation.EruptField; -import xyz.erupt.annotation.sub_erupt.Power; -import xyz.erupt.annotation.sub_erupt.RowOperation; -import xyz.erupt.annotation.sub_erupt.Tree; -import xyz.erupt.annotation.sub_field.Edit; -import xyz.erupt.annotation.sub_field.EditType; -import xyz.erupt.annotation.sub_field.View; -import xyz.erupt.annotation.sub_field.sub_edit.BoolType; -import xyz.erupt.annotation.sub_field.sub_edit.ReferenceTreeType; -import xyz.erupt.annotation.sub_field.sub_edit.Search; -import xyz.erupt.jpa.model.MetaModel; - -import javax.persistence.*; -import java.time.LocalDate; -import java.util.Set; - -//TODO: Filter By Status -//TODO: input by Uploaded File or File Sync -@Entity -@Table(name = "test_runs") -@Erupt(name = "测试执行计划", - power = @Power(importable = true, export = true), - tree = @Tree(id = "id", label = "name", pid = "parent.id") - ,rowOperation = {@RowOperation( - title = "生成执行测试用例", - operationHandler = GenerateTestRecordHandler.class)} -) - -public class TestRun extends MetaModel { - - @ManyToOne - @JoinColumn(name = "product_id") - @EruptField( - views = @View(title = "产品名称", column = "name"), - edit = @Edit( - search = @Search, - title = "产品选择", - type = EditType.REFERENCE_TREE, - desc = "动态获取产品", - referenceTreeType = @ReferenceTreeType( - pid = "parent.id")) - ) - private ProductModuleModel product; - - @ManyToOne - @EruptField( - edit = @Edit( - title = "父级测试安排", - type = EditType.REFERENCE_TREE, - referenceTreeType = @ReferenceTreeType(pid = "parent.id") - ) - ) - private TestRun parent; - - - @EruptField( - views = @View( - title = "测试负责人" - ), - edit = @Edit( - title = "测试负责人", - type = EditType.INPUT, search = @Search - ) - ) - private String testOwner; - - @JoinTable(name = "test_run_cases", - joinColumns = @JoinColumn(name = "test_run_id", referencedColumnName = "id"), - inverseJoinColumns = @JoinColumn(name = "test_case_id", referencedColumnName = "id")) - @ManyToMany(fetch = FetchType.EAGER) - @EruptField( - views = @View(title = "包含用例"), - edit = @Edit( - title = "包含用例", - type = EditType.TAB_TABLE_REFER - ) - ) - private Set testCases; - - @JoinTable(name = "test_run_scenario", - joinColumns = @JoinColumn(name = "test_run_id", referencedColumnName = "id"), - inverseJoinColumns = @JoinColumn(name = "test_scenario_id", referencedColumnName = "id")) - @ManyToMany(fetch = FetchType.EAGER) - @EruptField( - views = @View(title = "包含测试场景"), - edit = @Edit( - title = "包含测试场景", - type = EditType.TAB_TABLE_REFER - ) - ) - private Set testScenarios; - - public ProductModuleModel getProduct() { - return product; - } - - public void setProduct(ProductModuleModel product) { - this.product = product; - } - - public String getTestOwner() { - return testOwner; - } - - public void setTestOwner(String testOwner) { - this.testOwner = testOwner; - } - - public Set getTestCases() { - return testCases; - } - - public void setTestCases(Set testCases) { - this.testCases = testCases; - } - - public Set getTestScenarios() { - return testScenarios; - } - - public void setTestScenarios(Set testScenarios) { - this.testScenarios = testScenarios; - } - - public TestRun getParent() { - return parent; - } - - public void setParent(TestRun parent) { - this.parent = parent; - } - - @EruptField( - views = @View( - title = "名称" - ), - edit = @Edit( - title = "名称", - type = EditType.INPUT, search = @Search, notNull = true - ) - ) - private String name; - @EruptField( - views = @View( - title = "详细" - ), - edit = @Edit( - title = "详细", - type = EditType.INPUT, search = @Search, notNull = true - ) - ) - private String detail; - @EruptField( - views = @View( - title = "开始时间" - ), - edit = @Edit( - title = "开始时间", - type = EditType.DATE, search = @Search, - boolType = @BoolType - ) - ) - private LocalDate startDate; - @EruptField( - views = @View( - title = "预计完成时间" - ), - edit = @Edit( - title = "预计完成时间", - type = EditType.DATE, search = @Search, - boolType = @BoolType - ) - ) - private LocalDate estimatedCompletedDate; - @EruptField( - views = @View( - title = "完成时间" - ), - edit = @Edit( - title = "完成时间", - type = EditType.DATE, search = @Search, - boolType = @BoolType - ) - ) - private LocalDate completedDate; - @EruptField( - views = @View( - title = "当前状态" - ), - edit = @Edit( - title = "当前状态", - type = EditType.INPUT, search = @Search, notNull = true, - boolType = @BoolType - ) - ) - private String status; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDetail() { - return detail; - } - - public void setDetail(String detail) { - this.detail = detail; - } - - public LocalDate getStartDate() { - return startDate; - } - - public void setStartDate(LocalDate startDate) { - this.startDate = startDate; - } - - public LocalDate getEstimatedCompletedDate() { - return estimatedCompletedDate; - } - - public void setEstimatedCompletedDate(LocalDate estimatedCompletedDate) { - this.estimatedCompletedDate = estimatedCompletedDate; - } - - public LocalDate getCompletedDate() { - return completedDate; - } - - public void setCompletedDate(LocalDate completedDate) { - this.completedDate = completedDate; - } - - public String getStatus() { - return status; - } - - public void setStatus(String status) { - this.status = status; - } -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestScenario.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestScenario.java deleted file mode 100644 index 58478ab..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/model/TestScenario.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.fluent.qtm.tc.model; - -import io.fluent.base.model.NamedModelVO; -import xyz.erupt.annotation.Erupt; -import xyz.erupt.annotation.EruptField; -import xyz.erupt.annotation.sub_erupt.Power; -import xyz.erupt.annotation.sub_field.Edit; -import xyz.erupt.annotation.sub_field.EditType; -import xyz.erupt.annotation.sub_field.View; - -import javax.persistence.*; -import java.util.Set; - -@Entity -@Table(name = "test_scenarios") -@Erupt(name = "测试场景管理", - power = @Power(importable = true, export = true) -) -//@PreDataProxy(value= TestScenarioCaseProxy.class) - -public class TestScenario extends NamedModelVO { - - @JoinTable(name = "test_scenario_cases", - joinColumns = @JoinColumn(name = "test_scenario_id", referencedColumnName = "id"), - inverseJoinColumns = @JoinColumn(name = "test_case_id", referencedColumnName = "id")) - @ManyToMany(fetch = FetchType.EAGER) - @EruptField( - views = @View(title = "包含用例"), - edit = @Edit( - title = "包含用例", - type = EditType.TAB_TABLE_REFER - ) - ) - private Set testCases; - - public Set getTestCases() { - return testCases; - } - - public void setTestCases(Set testCases) { - this.testCases = testCases; - } -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/package-info.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/package-info.java deleted file mode 100644 index 0f28f0f..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package io.fluent.qtm.tc; \ No newline at end of file diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/repo/TestCaseRepo.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/repo/TestCaseRepo.java deleted file mode 100644 index 354138a..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/repo/TestCaseRepo.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.fluent.qtm.tc.repo; - -import io.fluent.qtm.tc.model.TestCase; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface TestCaseRepo extends JpaRepository { - public TestCase findByUuid(String uuid); - - -// @Query(nativeQuery = true,value = "delete from test_cases where test_plan=:testPlan") -// @Modifying -// public void deleteByTestPlan(@Param("testPlan") String testPlan); -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/repo/TestResultRepo.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/repo/TestResultRepo.java deleted file mode 100644 index 570f55a..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/repo/TestResultRepo.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.fluent.qtm.tc.repo; - - -import io.fluent.qtm.tc.model.TestResult; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -@Repository(value = "testResultRepo") -public interface TestResultRepo extends JpaRepository { - -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/repo/TestRunRepo.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/repo/TestRunRepo.java deleted file mode 100644 index f84b76c..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/repo/TestRunRepo.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.fluent.qtm.tc.repo; - - -import io.fluent.qtm.tc.model.TestRun; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface TestRunRepo extends JpaRepository { - -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/service/TestCaseService.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/service/TestCaseService.java deleted file mode 100644 index d4cda3f..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/service/TestCaseService.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.fluent.qtm.tc.service; - -import io.fluent.base.product.model.ProductModuleModel; -import io.fluent.qtm.tc.dto.TestCaseDTO; -import org.springframework.stereotype.Service; - -import java.util.List; - -@Service -public interface TestCaseService { - - public void saveTestCases(List cases, - ProductModuleModel product, ProductModuleModel module,String updater); -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/service/impl/MindMappingService.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/service/impl/MindMappingService.java deleted file mode 100644 index 28dad0b..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/service/impl/MindMappingService.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.fluent.qtm.tc.service.impl; - - - -import cn.hutool.core.bean.BeanUtil; -import io.fluent.base.product.model.ProductModuleModel; -import io.fluent.qtm.tc.dto.TestCaseDTO; -import io.fluent.qtm.tc.service.TestCaseService; -import io.fluent.mindmap.api.MindMapAccessor; -import org.springframework.stereotype.Service; -import xyz.erupt.jpa.model.MetaModel; - -import javax.annotation.Resource; -import javax.transaction.Transactional; -import java.util.List; - -/** - * 1.Import MindMapping file to test case database - * 2.export selected test cases as mindmapping file - */ -//TODO: convert to same TestCase Converter -@Service("mindMappingService") -public class MindMappingService { - - @Resource - private TestCaseService testCaseService; - - public List toTestCaseModel(String xmlFilePath) { - MindMapAccessor accessor = new MindMapAccessor(); - return accessor.readMindMapToBean(xmlFilePath, TestCaseDTO.class); - } - - @Transactional - public void saveTestCases(String xmlFilePath, MetaModel model) { - List testCaseModels = toTestCaseModel(xmlFilePath); - ProductModuleModel product = BeanUtil.getProperty(model, "product"); - ProductModuleModel module = BeanUtil.getProperty(model, "module"); - testCaseService.saveTestCases(testCaseModels,product,module,model.getUpdateBy()); - } - -} diff --git a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/service/impl/TestCaseServiceImpl.java b/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/service/impl/TestCaseServiceImpl.java deleted file mode 100644 index 7fdb66d..0000000 --- a/fluent-apps/qam/src/main/java/io/fluent/qtm/tc/service/impl/TestCaseServiceImpl.java +++ /dev/null @@ -1,94 +0,0 @@ -package io.fluent.qtm.tc.service.impl; - - -import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.lang.UUID; -import cn.hutool.core.util.StrUtil; -import io.fluent.base.proxies.AuditDataEnhancerProxy; -import io.fluent.base.product.model.ProductModuleModel; -import io.fluent.base.product.service.ProductModuleService; -import io.fluent.qtm.tc.dto.TestCaseDTO; -import io.fluent.qtm.tc.model.TestCase; -import io.fluent.qtm.tc.repo.TestCaseRepo; -import io.fluent.qtm.tc.service.TestCaseService; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import javax.transaction.Transactional; -import java.util.List; - -@Service -public class TestCaseServiceImpl implements TestCaseService { - @Resource - private TestCaseRepo testCaseRepo; - @Resource - private ProductModuleService productMetaService; - - @Resource - private AuditDataEnhancerProxy dataEnhancerProxy; - @Override - @Transactional - @Async - /** - * notice:parent Product can't be created,parent product must be configured - * 1. 如果UUID没有或者找不到,则新增测试用例 - * 2. 新增测试用例中, - */ - public void saveTestCases(List cases, - ProductModuleModel parentProduct, - ProductModuleModel module,String updater) { - for (TestCaseDTO aCase : cases) { - TestCase tcEntity = createOrUseExistingTestCase(aCase); - ProductModuleModel rootProduct = getRootProductMeta(aCase); - ProductModuleModel parentModule = productMetaService.createModuleIfNotExist(rootProduct.getId(), - aCase.getModuleName(),updater); - ProductModuleModel subModule = whichSubModule(parentModule, aCase,updater); - tcEntity.setModule(subModule); - tcEntity.setProduct(rootProduct); - tcEntity.setParent(parentModule); - if (StrUtil.isBlankIfStr(aCase.getPriority())) { - tcEntity.setPriority("P2"); - } - tcEntity.setSteps(StrUtil.join(":\n", aCase.getFeature(), - aCase.getSummary(), aCase.getSteps())); - - testCaseRepo.save(tcEntity); - } - } - - private TestCase createOrUseExistingTestCase(TestCaseDTO aCase) { - TestCase tcEntity; - if (StrUtil.isBlank(aCase.getUuid())) { - tcEntity = BeanUtil.copyProperties(aCase, TestCase.class); - tcEntity.setUuid(UUID.fastUUID().toString(true)); - - } else { - tcEntity = testCaseRepo.findByUuid(aCase.getUuid()); - if (tcEntity == null) { - tcEntity = BeanUtil.copyProperties(aCase, TestCase.class); //生成新的的UUID - } else { - BeanUtil.copyProperties(aCase, tcEntity, "id"); //更新数据库数据 - } - } - return tcEntity; - } - - - private ProductModuleModel getRootProductMeta(TestCaseDTO aCase) { - ProductModuleModel rootProductMeta = productMetaService.findByName(aCase.getProductName()); - if (rootProductMeta == null) { - throw new RuntimeException("找不到产品"); - } - return rootProductMeta; - } - - - private ProductModuleModel whichSubModule(ProductModuleModel parentProduct, TestCaseDTO aCase,String updater) { - if (parentProduct.getName().equalsIgnoreCase(aCase.getModuleName())) return parentProduct; - return productMetaService.createModuleIfNotExist(parentProduct.getId(), aCase.getModuleName(),updater); - - } - - -} diff --git a/fluent-apps/qam/src/main/resources/application-dev.yaml b/fluent-apps/qam/src/main/resources/application-dev.yaml deleted file mode 100644 index 522c82e..0000000 --- a/fluent-apps/qam/src/main/resources/application-dev.yaml +++ /dev/null @@ -1,111 +0,0 @@ -erupt-app: - # 是否开启水印,1.12.0 及以上版本支持 - waterMark: false - # 登录失败几次出现验证码,值为0时表示一直需要登录验证码 - verifyCodeCount: 2 - # 登录密码是否加密传输,特殊场景如:LDAP登录可关闭该功能获取密码明文 - pwdTransferEncrypt: true - # 多语言配置,默认支持:简体中文、繁体中文、英文、日文;具体配置详见erupt-i18n模块 - locales: [ "zh-CN","zh-TW","en-US","ja-JP" ] -erupt: - # 是否开启csrf防御 - csrfInspect: true - # 是否开启redis方式存储session,默认false,开启后需在配置文件中添加redis配置(同 spring boot) - redisSession: false - # 附件上传存储路径, 默认路径为:/opt/erupt-attachment - uploadPath: /Users/patrick/data/temp - # 是否保留上传文件原始名称 - keepUploadFileName: false - # 登录session时长(redisSession为true时有效) - upms.expireTimeByLogin: 120 - # 是否记录操作日志,默认true,该功能开启后可在【系统管理 → 操作日志】中查看操作日志 - security.recordOperateLog: false - -#spring: -# datasource: -# url: jdbc:postgresql://db.supabase.orb.local:5432/postgres?currentSchema=workspace -# username: postgres -# password: postgres -# jpa: -# show-sql: true -# generate-ddl: true -# database-platform: org.hibernate.dialect.PostgreSQLDialect -# database: postgresql - -spring: - datasource: - url: jdbc:postgresql://db.supabase.orb.local:5432/workspace - username: postgres - password: postgres - jpa: - show-sql: true - generate-ddl: true - database-platform: org.hibernate.dialect.PostgreSQLDialect - database: postgresql -# mail: -# username: xxxx@qq.com -# password: xxxxxxx -# host: smtp.qq.com -# properties: -# mail.smtp.ssl.auth: true -# mail.smtp.ssl.enable: true -# mail.smtp.ssl.required: true - servlet: - multipart: - max-file-size: 100MB - max-request-size: 100MB - -# springdoc-openapi项目配置 -#springdoc: -# swagger-ui: -# path: /swagger-ui.html -# tags-sorter: alpha -# operations-sorter: alpha -# api-docs: -# path: /v3/api-docs -# group-configs: -# - group: 'default' -# paths-to-match: '/**' -# packages-to-scan: io.fluentqa -# knife4j的增强配置,不需要增强可以不配 -knife4j: - enable: true - openapi: - title: QA Workspace API - description: "`QA Workspace API - # workspace" - email: fluentqa@163.com - concat: fluent-qa -# url: https://docs.xiaominfo.com -# version: v4.0 -# license: Apache 2.0 -# license-url: https://stackoverflow.com/ -# terms-of-service-url: https://stackoverflow.com/ - group: - test1: - group-name: qa workspace api - api-rule: package -# api-rule-resources: -# - com.knife4j.demo.new3 - -server: - # 启用 gzip 压缩 - compression: - mime-types: application/javascript,text/css,application/json,application/xml,text/html,text/xml,text/plain - enabled: true - error: - includeException: true - includeStacktrace: ALWAYS - includeMessage: ALWAYS - port: 9090 -logging: - level: - root: TRACE - io.fluentqa: DEBUG - org.hibernate: DEBUG - io.fluent: DEBUG - xyz.erupt: DEBUG - -magic-api: - web: /fluentapi/v1 - resource.location: ./magic-script \ No newline at end of file diff --git a/fluent-apps/qam/src/main/resources/application.yaml b/fluent-apps/qam/src/main/resources/application.yaml deleted file mode 100644 index caf4dfc..0000000 --- a/fluent-apps/qam/src/main/resources/application.yaml +++ /dev/null @@ -1,3 +0,0 @@ -spring: - profiles: - active: dev \ No newline at end of file diff --git a/fluent-apps/qam/src/main/resources/public/app.css b/fluent-apps/qam/src/main/resources/public/app.css deleted file mode 100644 index 44c63ae..0000000 --- a/fluent-apps/qam/src/main/resources/public/app.css +++ /dev/null @@ -1,24 +0,0 @@ -layout-header { - background: #3f51b5 !important; -} - -/* 例:修改登录页样式 */ -layout-passport > .container { - background-position: center !important; - background-repeat: repeat !important; - background-size: cover !important; - background-color: #fff !important; - background-image: url(https://www.erupt.xyz/demo/login-bg.svg) !important; -} - -layout-passport .title { - font-family: Courier New, Menlo, Monaco, Consolas, monospace !important; -} - -layout-passport form { - padding: 26px !important; - margin: 8px !important; - background: rgba(255, 255, 255, 0.9); - border-radius: 3px; - box-shadow: 1px 1px 10px rgba(190, 184, 184, 0.3); -} \ No newline at end of file diff --git a/fluent-apps/qam/src/main/resources/public/app.js b/fluent-apps/qam/src/main/resources/public/app.js deleted file mode 100644 index c4435ce..0000000 --- a/fluent-apps/qam/src/main/resources/public/app.js +++ /dev/null @@ -1,71 +0,0 @@ -window.eruptSiteConfig = { - //erupt接口地址,在前后端分离时指定 - domain: "", - //附件地址,一般情况下不需要指定,如果自定义对象存储空间,则需在此指定附件资源访问地址 - fileDomain: "", - //标题 - title: "QA Workspace", - //描述 - desc: "QA Base", - //是否展示版权信息 - copyright: true, - //高德地图 api key,使用地图组件须指定此属性,amapKey获取地址:https://lbs.amap.com (服务平台为:Web端(JS API)) - amapKey: "xxxx", - //高德地图 SecurityJsCode - amapSecurityJsCode: "xxxxx", - //logo路径 - logoPath: "erupt.svg", - //logo文字 - logoText: "erupt", - //注册页地址(仅是一个链接,需要自定义实际样式) - registerPage: "", - //自定义导航栏按钮,配置后将会出现在页面右上角 - r_tools: [{ - text: "自定义功能按钮", - icon: "fa-eercast", - mobileHidden: true, - click: function (event) { - alert("Function button"); - } - }], - // //登录成功事件 - // login: function(user){ - // - // }, - // //注销事件 - // logout: function(user){ - // - // } -}; - -// //路由回调函数 -// window.eruptRouterEvent = { -// //key表示要监听的路由切换地址,为url hash地址最后一段 -// //例如:http://www.erupt.xyz:9999/#/build/table/demo中demo为回调key -// demo: { -// //路由载入事件 -// load: function (e) { -// -// }, -// //路由退出事件 -// unload: function (e) { -// -// } -// }, -// //$ 为全路径通配符,在任何路由切换时都会执行load与unload事件 -// $: { -// load: function (e) { -// -// }, -// unload: function (e) { -// } -// } -// }; -// -// //erupt生命周期函数 -// window.eruptEvent = { -// //页面加载完成后回调 -// startup: function () { -// -// } -// } \ No newline at end of file diff --git a/fluent-apps/qam/src/main/resources/public/home.html b/fluent-apps/qam/src/main/resources/public/home.html deleted file mode 100644 index 35600cf..0000000 --- a/fluent-apps/qam/src/main/resources/public/home.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - home - - - - - -

测试管理工具箱

- - \ No newline at end of file diff --git a/fluent-apps/qam/src/main/resources/tpl/operation.tpl b/fluent-apps/qam/src/main/resources/tpl/operation.tpl deleted file mode 100644 index d77f7e5..0000000 --- a/fluent-apps/qam/src/main/resources/tpl/operation.tpl +++ /dev/null @@ -1,17 +0,0 @@ - -
- - <#list rows as row> - - - - - - -
${row.id}${row.choice!''}${row.code!''}
-
\ No newline at end of file diff --git a/fluent-apps/workspace/pom.xml b/fluent-apps/workspace/pom.xml index b498a84..e2e0407 100644 --- a/fluent-apps/workspace/pom.xml +++ b/fluent-apps/workspace/pom.xml @@ -12,9 +12,138 @@ workspace - 21 - 21 + 17 + 17 UTF-8 + + + + xyz.erupt + erupt-upms + ${erupt.version} + + + + xyz.erupt + erupt-security + ${erupt.version} + + + xyz.erupt + erupt-job + ${erupt.version} + + + + xyz.erupt + erupt-web + ${erupt.version} + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + org.springframework.boot + spring-boot-starter-undertow + 2.7.12 + + + org.postgresql + postgresql + ${postgresql.version} + + + javax.xml.bind + jaxb-api + 2.3.1 + + + com.github.xiaoymin + knife4j-openapi2-spring-boot-starter + 4.4.0 + + + + io.fluent + fluent-excel + ${fluent.version} + + + io.fluent + fluent-mindmap + ${fluent.version} + + + io.fluent + fluent-erupts-base + ${fluent.version} + + + + io.fluent + fleunt-github + ${fluent.version} + + + + + + + + cn.hutool + hutool-all + + + io.fluent + fluent-quickdao + 1.0-SNAPSHOT + + + io.fluent + fluent-openapi + 1.0-SNAPSHOT + + + + + + + + + + + + + + + + + maven-compiler-plugin + org.apache.maven.plugins + 3.11.0 + + 17 + 17 + + + + org.springframework.boot + spring-boot-maven-plugin + 2.7.2 + + + + repackage + + + + + + + \ No newline at end of file diff --git a/fluent-apps/workspace/src/main/java/io/fluent/WorkspaceApplication.java b/fluent-apps/workspace/src/main/java/io/fluent/WorkspaceApplication.java index 35eb484..ba3e056 100644 --- a/fluent-apps/workspace/src/main/java/io/fluent/WorkspaceApplication.java +++ b/fluent-apps/workspace/src/main/java/io/fluent/WorkspaceApplication.java @@ -10,12 +10,12 @@ @EnableAsync @EruptScan @EntityScan -public class QAMApp { +public class WorkspaceApplication { /** * QA Management Application Entrypoint * @param args */ public static void main(String[] args) { - SpringApplication.run(QAMApp.class); + SpringApplication.run(WorkspaceApplication.class); } } diff --git a/pom.xml b/pom.xml index 0686b51..6bf4e89 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ - 1.12.18 + 1.12.19 1.0-SNAPSHOT 42.7.2 2.7.18