From ad721161595cc5b548eeaa522eb090e2f9d88a8c Mon Sep 17 00:00:00 2001 From: BugKing Date: Thu, 3 Dec 2020 14:41:42 +0800 Subject: [PATCH 01/16] =?UTF-8?q?build:=20=E5=A2=9E=E5=8A=A0=20GitHub=20ac?= =?UTF-8?q?tion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-push.yml | 36 ++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/build-push.yml diff --git a/.github/workflows/build-push.yml b/.github/workflows/build-push.yml new file mode 100644 index 0000000000..0220027a94 --- /dev/null +++ b/.github/workflows/build-push.yml @@ -0,0 +1,36 @@ +name: Build Docker Image and Push + +on: + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: nelonoel/branch-name@v1.0.1 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + server-id: github # Value of the distributionManagement/repository/id field of the pom.xml + settings-path: ${{ github.workspace }} # location for the settings.xml file + + - name: Build with Maven + run: mvn -B package --file pom.xml + + - name: Docker Setup Buildx + uses: docker/setup-buildx-action@v1.0.3 + + - name: Docker Login + uses: docker/login-action@v1.6.0 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_TOKEN }} + + - name: Build Docker Image + uses: docker/build-push-action@v2.2.0 + with: + build-args: MS_VERSION=${BRANCH_NAME}-b${GITHUB_RUN_NUMBER} + tags: metersphere/metersphere:${BRANCH_NAME} From d00d847e88a616df8b794864adcc625b62d92365 Mon Sep 17 00:00:00 2001 From: shiziyuan9527 Date: Thu, 3 Dec 2020 14:45:55 +0800 Subject: [PATCH 02/16] =?UTF-8?q?fix:=20=E5=8E=BB=E6=8E=89sql=E5=88=86?= =?UTF-8?q?=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml index 9a9a8b2d51..b6f0bc1771 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml @@ -364,6 +364,6 @@ from test_case, project where test_case.project_id = project.id and project.workspace_id = #{workspaceId} - and test_case.id = #{caseId}; + and test_case.id = #{caseId} \ No newline at end of file From d59a05f806635ae5944345adff7040dff5635ec7 Mon Sep 17 00:00:00 2001 From: BugKing Date: Thu, 3 Dec 2020 15:12:28 +0800 Subject: [PATCH 03/16] =?UTF-8?q?build:=20GitHub=20action=20=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-push.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/.github/workflows/build-push.yml b/.github/workflows/build-push.yml index 0220027a94..d279e2e0c7 100644 --- a/.github/workflows/build-push.yml +++ b/.github/workflows/build-push.yml @@ -10,6 +10,33 @@ jobs: steps: - uses: actions/checkout@v2 - uses: nelonoel/branch-name@v1.0.1 + + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + # npm cache files are stored in `~/.npm` on Linux/macOS + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + + - name: Cache maven dependencies + uses: actions/cache@v2 + env: + cache-name: cache-maven-deps + with: + # npm cache files are stored in `~/.npm` on Linux/macOS + path: ~/.m2 + key: ${{ runner.os }}-build-${{ env.cache-name }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - name: Set up JDK 1.8 uses: actions/setup-java@v1 with: From 30e3432d6dea9c3c70d774810680554998a23e47 Mon Sep 17 00:00:00 2001 From: "Captain.B" Date: Thu, 3 Dec 2020 15:23:20 +0800 Subject: [PATCH 04/16] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=B7=A6?= =?UTF-8?q?=E4=B8=8A=E8=A7=92=E7=9A=84logo=E7=9A=84=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/business/App.vue | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/business/App.vue b/frontend/src/business/App.vue index b3bd049661..4efdc55ae0 100644 --- a/frontend/src/business/App.vue +++ b/frontend/src/business/App.vue @@ -7,7 +7,7 @@ - + @@ -100,7 +100,6 @@ export default { height: 37px; background-repeat: no-repeat; background-position: 50% center; - background-image: url("../assets/logo-light-MeterSphere.svg"); } .menus > * { From 3e00db101134cdb2216a9207103fc20d02ea3f35 Mon Sep 17 00:00:00 2001 From: "Captain.B" Date: Thu, 3 Dec 2020 15:57:38 +0800 Subject: [PATCH 05/16] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E5=8F=91?= =?UTF-8?q?=E9=80=81=E9=80=9A=E7=9F=A5=E7=9A=84=E5=90=8E=E5=8F=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/jmeter/APIBackendListenerClient.java | 8 ++++++++ .../io/metersphere/notice/sender/NoticeModel.java | 4 ++++ .../notice/service/NoticeSendService.java | 13 ++++++++++--- .../performance/notice/PerformanceNoticeTask.java | 9 +++++++++ .../io/metersphere/track/service/IssuesService.java | 1 + .../track/service/TestCaseCommentService.java | 1 + .../track/service/TestCaseReviewService.java | 4 ++++ .../metersphere/track/service/TestPlanService.java | 10 +++++++--- 8 files changed, 44 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java b/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java index 1110787a35..46dd2f08b8 100644 --- a/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java +++ b/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java @@ -216,6 +216,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl String successContext = ""; String failedContext = ""; String subject = ""; + String event = ""; if (StringUtils.equals(NoticeConstants.Mode.API, report.getTriggerMode())) { successContext = "接口测试 API任务通知:'" + report.getName() + "'执行成功" + "\n" + "请点击下面链接进入测试报告页面" + "\n" + url; failedContext = "接口测试 API任务通知:'" + report.getName() + "'执行失败" + "\n" + "请点击下面链接进入测试报告页面" + "\n" + url; @@ -226,6 +227,12 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl failedContext = "接口测试定时任务通知:'" + report.getName() + "'执行失败" + "\n" + "请点击下面链接进入测试报告页面" + "\n" + url; subject = Translator.get("task_notification"); } + if (StringUtils.equals("Success", report.getStatus())) { + event = NoticeConstants.Event.EXECUTE_SUCCESSFUL; + } + if (StringUtils.equals("Error", report.getStatus())) { + event = NoticeConstants.Event.EXECUTE_FAILED; + } NoticeModel noticeModel = NoticeModel.builder() .successContext(successContext) @@ -234,6 +241,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl .failedMailTemplate("ApiFailedNotification") .testId(testResult.getTestId()) .status(report.getStatus()) + .event(event) .subject(subject) .build(); noticeSendService.send(report.getTriggerMode(), noticeModel); diff --git a/backend/src/main/java/io/metersphere/notice/sender/NoticeModel.java b/backend/src/main/java/io/metersphere/notice/sender/NoticeModel.java index bdfec0b3d0..bef8bda4d0 100644 --- a/backend/src/main/java/io/metersphere/notice/sender/NoticeModel.java +++ b/backend/src/main/java/io/metersphere/notice/sender/NoticeModel.java @@ -17,6 +17,10 @@ public class NoticeModel { * 保存状态 */ private String status; + /** + * Event + */ + private String event; /** * 消息主题 */ diff --git a/backend/src/main/java/io/metersphere/notice/service/NoticeSendService.java b/backend/src/main/java/io/metersphere/notice/service/NoticeSendService.java index 597820a06a..1adc46a06b 100644 --- a/backend/src/main/java/io/metersphere/notice/service/NoticeSendService.java +++ b/backend/src/main/java/io/metersphere/notice/service/NoticeSendService.java @@ -1,5 +1,6 @@ package io.metersphere.notice.service; +import com.alibaba.nacos.client.utils.StringUtils; import io.metersphere.commons.constants.NoticeConstants; import io.metersphere.commons.utils.CommonBeanFactory; import io.metersphere.notice.domain.MessageDetail; @@ -22,6 +23,10 @@ public class NoticeSendService { @Resource private DingNoticeSender dingNoticeSender; + private void event(String event) { + + } + private NoticeSender getNoticeSender(MessageDetail messageDetail) { NoticeSender noticeSender = null; switch (messageDetail.getType()) { @@ -56,8 +61,10 @@ public class NoticeSendService { messageDetails = noticeService.searchMessageByType(taskType); break; } - messageDetails.forEach(messageDetail -> - this.getNoticeSender(messageDetail).send(messageDetail, noticeModel) - ); + messageDetails.forEach(messageDetail -> { + if (StringUtils.equals(messageDetail.getEvent(), noticeModel.getEvent())) { + this.getNoticeSender(messageDetail).send(messageDetail, noticeModel); + } + }); } } diff --git a/backend/src/main/java/io/metersphere/performance/notice/PerformanceNoticeTask.java b/backend/src/main/java/io/metersphere/performance/notice/PerformanceNoticeTask.java index 8525f51a34..6b9ea8226f 100644 --- a/backend/src/main/java/io/metersphere/performance/notice/PerformanceNoticeTask.java +++ b/backend/src/main/java/io/metersphere/performance/notice/PerformanceNoticeTask.java @@ -63,6 +63,7 @@ public class PerformanceNoticeTask { String successContext = ""; String failedContext = ""; String subject = ""; + String event = ""; if (StringUtils.equals(NoticeConstants.Mode.API, loadTestReport.getTriggerMode())) { successContext = "性能测试 API任务通知:" + loadTestReport.getName() + "执行成功" + "\n" + "请点击下面链接进入测试报告页面" + "\n" + url; failedContext = "性能测试 API任务通知:" + loadTestReport.getName() + "执行失败" + "\n" + "请点击下面链接进入测试报告页面" + "\n" + url; @@ -74,6 +75,13 @@ public class PerformanceNoticeTask { subject = Translator.get("task_notification"); } + if (PerformanceTestStatus.Completed.name().equals(loadTestReport.getStatus())) { + event = NoticeConstants.Event.EXECUTE_SUCCESSFUL; + } + if (PerformanceTestStatus.Error.name().equals(loadTestReport.getStatus())) { + event = NoticeConstants.Event.EXECUTE_FAILED; + } + NoticeModel noticeModel = NoticeModel.builder() .successContext(successContext) .successMailTemplate("PerformanceApiSuccessNotification") @@ -82,6 +90,7 @@ public class PerformanceNoticeTask { .testId(loadTestReport.getTestId()) .status(loadTestReport.getStatus()) .subject(subject) + .event(event) .build(); noticeSendService.send(loadTestReport.getTriggerMode(), noticeModel); } diff --git a/backend/src/main/java/io/metersphere/track/service/IssuesService.java b/backend/src/main/java/io/metersphere/track/service/IssuesService.java index c2aefecbf9..4239dbb49e 100644 --- a/backend/src/main/java/io/metersphere/track/service/IssuesService.java +++ b/backend/src/main/java/io/metersphere/track/service/IssuesService.java @@ -100,6 +100,7 @@ public class IssuesService { .subject(Translator.get("task_defect_notification")) .mailTemplate("IssuesCreate") .paramMap(paramMap) + .event(NoticeConstants.Event.CREATE) .build(); noticeSendService.send(NoticeConstants.TaskType.DEFECT_TASK, noticeModel); } diff --git a/backend/src/main/java/io/metersphere/track/service/TestCaseCommentService.java b/backend/src/main/java/io/metersphere/track/service/TestCaseCommentService.java index 1aa688a420..1410744fc1 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseCommentService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseCommentService.java @@ -73,6 +73,7 @@ public class TestCaseCommentService { .subject(Translator.get("test_review_task_notice")) .mailTemplate("ReviewComments") .paramMap(paramMap) + .event(NoticeConstants.Event.COMMENT) .build(); noticeSendService.send(NoticeConstants.TaskType.REVIEW_TASK, noticeModel); } diff --git a/backend/src/main/java/io/metersphere/track/service/TestCaseReviewService.java b/backend/src/main/java/io/metersphere/track/service/TestCaseReviewService.java index a3ab852fa1..cf68b5e59a 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseReviewService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseReviewService.java @@ -112,6 +112,7 @@ public class TestCaseReviewService { .subject(Translator.get("test_review_task_notice")) .mailTemplate("ReviewInitiate") .paramMap(paramMap) + .event(NoticeConstants.Event.CREATE) .build(); noticeSendService.send(NoticeConstants.TaskType.REVIEW_TASK, noticeModel); } @@ -216,6 +217,7 @@ public class TestCaseReviewService { .subject(Translator.get("test_review_task_notice")) .mailTemplate("ReviewEnd") .paramMap(paramMap) + .event(NoticeConstants.Event.UPDATE) .build(); noticeSendService.send(NoticeConstants.TaskType.REVIEW_TASK, noticeModel); } @@ -318,6 +320,7 @@ public class TestCaseReviewService { .subject(Translator.get("test_review_task_notice")) .mailTemplate("ReviewDelete") .paramMap(paramMap) + .event(NoticeConstants.Event.DELETE) .build(); noticeSendService.send(NoticeConstants.TaskType.REVIEW_TASK, noticeModel); } catch (Exception e) { @@ -460,6 +463,7 @@ public class TestCaseReviewService { .subject(Translator.get("test_review_task_notice")) .mailTemplate("ReviewEnd") .paramMap(paramMap) + .event(NoticeConstants.Event.UPDATE) .build(); noticeSendService.send(NoticeConstants.TaskType.REVIEW_TASK, noticeModel); } catch (Exception e) { diff --git a/backend/src/main/java/io/metersphere/track/service/TestPlanService.java b/backend/src/main/java/io/metersphere/track/service/TestPlanService.java index 850e9fd3e8..36bfad4a84 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestPlanService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestPlanService.java @@ -115,6 +115,7 @@ public class TestPlanService { .subject(Translator.get("test_plan_notification")) .mailTemplate("TestPlanStart") .paramMap(paramMap) + .event(NoticeConstants.Event.CREATE) .build(); noticeSendService.send(NoticeConstants.TaskType.TEST_PLAN_TASK, noticeModel); } @@ -158,6 +159,7 @@ public class TestPlanService { .subject(Translator.get("test_plan_notification")) .mailTemplate("TestPlanEnd") .paramMap(paramMap) + .event(NoticeConstants.Event.UPDATE) .build(); noticeSendService.send(NoticeConstants.TaskType.TEST_PLAN_TASK, noticeModel); } @@ -258,9 +260,9 @@ public class TestPlanService { deleteTestCaseByPlanId(planId); testPlanProjectService.deleteTestPlanProjectByPlanId(planId); int num = testPlanMapper.deleteByPrimaryKey(planId); - List userIds = new ArrayList<>(); + List relatedUsers = new ArrayList<>(); AddTestPlanRequest testPlans = new AddTestPlanRequest(); - userIds.add(testPlan.getCreator()); + relatedUsers.add(testPlan.getCreator()); try { BeanUtils.copyBean(testPlans, testPlan); String context = getTestPlanContext(testPlans, NoticeConstants.Event.DELETE); @@ -269,10 +271,11 @@ public class TestPlanService { paramMap.put("creator", user.getName()); NoticeModel noticeModel = NoticeModel.builder() .context(context) - .relatedUsers(userIds) + .relatedUsers(relatedUsers) .subject(Translator.get("test_plan_notification")) .mailTemplate("TestPlanDelete") .paramMap(paramMap) + .event(NoticeConstants.Event.DELETE) .build(); noticeSendService.send(NoticeConstants.TaskType.TEST_PLAN_TASK, noticeModel); } catch (Exception e) { @@ -510,6 +513,7 @@ public class TestPlanService { .subject(Translator.get("test_plan_notification")) .mailTemplate("TestPlanEnd") .paramMap(paramMap) + .event(NoticeConstants.Event.UPDATE) .build(); noticeSendService.send(NoticeConstants.TaskType.TEST_PLAN_TASK, noticeModel); } catch (Exception e) { From 3ebec6a84a9d369ad9bf7dc25c8dc72fd5c13d66 Mon Sep 17 00:00:00 2001 From: "Captain.B" Date: Thu, 3 Dec 2020 16:17:52 +0800 Subject: [PATCH 06/16] =?UTF-8?q?refactor:=20=E6=B6=88=E6=81=AF=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E6=BB=9A=E5=8A=A8=E6=9D=A1=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../organization/TaskNotification.vue | 35 ++++++++----------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/frontend/src/business/components/settings/organization/TaskNotification.vue b/frontend/src/business/components/settings/organization/TaskNotification.vue index 8b2061db4d..375dc7e5fc 100644 --- a/frontend/src/business/components/settings/organization/TaskNotification.vue +++ b/frontend/src/business/components/settings/organization/TaskNotification.vue @@ -1,20 +1,18 @@ From fde6ae766c08eac21e48fe6a103d653a903abdee Mon Sep 17 00:00:00 2001 From: chenjianxing Date: Thu, 3 Dec 2020 16:27:00 +0800 Subject: [PATCH 07/16] =?UTF-8?q?refactor(=E6=8E=A5=E5=8F=A3=E6=B5=8B?= =?UTF-8?q?=E8=AF=95):=20swagger=20=E5=AF=BC=E5=85=A5=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/parse/{ => old}/ApiImportAbstractParser.java | 2 +- .../io/metersphere/api/parse/{ => old}/ApiImportParser.java | 2 +- .../api/parse/{ => old}/ApiImportParserFactory.java | 2 +- .../api/parse/{ => old}/JmeterDocumentParser.java | 2 +- .../java/io/metersphere/api/parse/{ => old}/MsParser.java | 2 +- .../io/metersphere/api/parse/{ => old}/PostmanParser.java | 2 +- .../io/metersphere/api/parse/{ => old}/Swagger2Parser.java | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) rename backend/src/main/java/io/metersphere/api/parse/{ => old}/ApiImportAbstractParser.java (98%) rename backend/src/main/java/io/metersphere/api/parse/{ => old}/ApiImportParser.java (91%) rename backend/src/main/java/io/metersphere/api/parse/{ => old}/ApiImportParserFactory.java (94%) rename backend/src/main/java/io/metersphere/api/parse/{ => old}/JmeterDocumentParser.java (99%) rename backend/src/main/java/io/metersphere/api/parse/{ => old}/MsParser.java (99%) rename backend/src/main/java/io/metersphere/api/parse/{ => old}/PostmanParser.java (99%) rename backend/src/main/java/io/metersphere/api/parse/{ => old}/Swagger2Parser.java (98%) diff --git a/backend/src/main/java/io/metersphere/api/parse/ApiImportAbstractParser.java b/backend/src/main/java/io/metersphere/api/parse/old/ApiImportAbstractParser.java similarity index 98% rename from backend/src/main/java/io/metersphere/api/parse/ApiImportAbstractParser.java rename to backend/src/main/java/io/metersphere/api/parse/old/ApiImportAbstractParser.java index a8c450c6aa..ac18f2a75a 100644 --- a/backend/src/main/java/io/metersphere/api/parse/ApiImportAbstractParser.java +++ b/backend/src/main/java/io/metersphere/api/parse/old/ApiImportAbstractParser.java @@ -1,4 +1,4 @@ -package io.metersphere.api.parse; +package io.metersphere.api.parse.old; import io.metersphere.api.dto.ApiTestImportRequest; import io.metersphere.api.dto.scenario.KeyValue; diff --git a/backend/src/main/java/io/metersphere/api/parse/ApiImportParser.java b/backend/src/main/java/io/metersphere/api/parse/old/ApiImportParser.java similarity index 91% rename from backend/src/main/java/io/metersphere/api/parse/ApiImportParser.java rename to backend/src/main/java/io/metersphere/api/parse/old/ApiImportParser.java index 6173f71c77..edc1c47908 100644 --- a/backend/src/main/java/io/metersphere/api/parse/ApiImportParser.java +++ b/backend/src/main/java/io/metersphere/api/parse/old/ApiImportParser.java @@ -1,4 +1,4 @@ -package io.metersphere.api.parse; +package io.metersphere.api.parse.old; import io.metersphere.api.dto.ApiTestImportRequest; import io.metersphere.api.dto.definition.parse.ApiDefinitionImport; diff --git a/backend/src/main/java/io/metersphere/api/parse/ApiImportParserFactory.java b/backend/src/main/java/io/metersphere/api/parse/old/ApiImportParserFactory.java similarity index 94% rename from backend/src/main/java/io/metersphere/api/parse/ApiImportParserFactory.java rename to backend/src/main/java/io/metersphere/api/parse/old/ApiImportParserFactory.java index ee41450068..6235617a83 100644 --- a/backend/src/main/java/io/metersphere/api/parse/ApiImportParserFactory.java +++ b/backend/src/main/java/io/metersphere/api/parse/old/ApiImportParserFactory.java @@ -1,4 +1,4 @@ -package io.metersphere.api.parse; +package io.metersphere.api.parse.old; import io.metersphere.commons.constants.ApiImportPlatform; import org.apache.commons.lang3.StringUtils; diff --git a/backend/src/main/java/io/metersphere/api/parse/JmeterDocumentParser.java b/backend/src/main/java/io/metersphere/api/parse/old/JmeterDocumentParser.java similarity index 99% rename from backend/src/main/java/io/metersphere/api/parse/JmeterDocumentParser.java rename to backend/src/main/java/io/metersphere/api/parse/old/JmeterDocumentParser.java index 4acf7f5690..c648df516b 100644 --- a/backend/src/main/java/io/metersphere/api/parse/JmeterDocumentParser.java +++ b/backend/src/main/java/io/metersphere/api/parse/old/JmeterDocumentParser.java @@ -1,4 +1,4 @@ -package io.metersphere.api.parse; +package io.metersphere.api.parse.old; import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.ScriptEngineUtils; diff --git a/backend/src/main/java/io/metersphere/api/parse/MsParser.java b/backend/src/main/java/io/metersphere/api/parse/old/MsParser.java similarity index 99% rename from backend/src/main/java/io/metersphere/api/parse/MsParser.java rename to backend/src/main/java/io/metersphere/api/parse/old/MsParser.java index c9d165e247..5ec129288c 100644 --- a/backend/src/main/java/io/metersphere/api/parse/MsParser.java +++ b/backend/src/main/java/io/metersphere/api/parse/old/MsParser.java @@ -1,4 +1,4 @@ -package io.metersphere.api.parse; +package io.metersphere.api.parse.old; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; diff --git a/backend/src/main/java/io/metersphere/api/parse/PostmanParser.java b/backend/src/main/java/io/metersphere/api/parse/old/PostmanParser.java similarity index 99% rename from backend/src/main/java/io/metersphere/api/parse/PostmanParser.java rename to backend/src/main/java/io/metersphere/api/parse/old/PostmanParser.java index e8eed52279..aba9b3199a 100644 --- a/backend/src/main/java/io/metersphere/api/parse/PostmanParser.java +++ b/backend/src/main/java/io/metersphere/api/parse/old/PostmanParser.java @@ -1,4 +1,4 @@ -package io.metersphere.api.parse; +package io.metersphere.api.parse.old; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; diff --git a/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java b/backend/src/main/java/io/metersphere/api/parse/old/Swagger2Parser.java similarity index 98% rename from backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java rename to backend/src/main/java/io/metersphere/api/parse/old/Swagger2Parser.java index f16c220364..5aaac94a95 100644 --- a/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java +++ b/backend/src/main/java/io/metersphere/api/parse/old/Swagger2Parser.java @@ -1,4 +1,4 @@ -package io.metersphere.api.parse; +package io.metersphere.api.parse.old; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; @@ -79,8 +79,8 @@ public class Swagger2Parser extends ApiImportAbstractParser { headerManager.setId(UUID.randomUUID().toString()); headerManager.setName(res.getName() + "Postman MsHeaderManager"); headerManager.setHeaders(res.getHeaders()); - HashTree tree = new HashTree(); - tree.add(headerManager); +// HashTree tree = new HashTree(); +// tree.add(headerManager); LinkedList list = new LinkedList<>(); list.add(headerManager); requestElement.setHashTree(list); From 3bd7a1d049faa96e0f41ef388a7e804910640101 Mon Sep 17 00:00:00 2001 From: chenjianxing Date: Thu, 3 Dec 2020 16:28:23 +0800 Subject: [PATCH 08/16] =?UTF-8?q?refactor(=E6=8E=A5=E5=8F=A3=E6=B5=8B?= =?UTF-8?q?=E8=AF=95):=20swagger=20=E5=AF=BC=E5=85=A5=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../request/sampler/MsHTTPSamplerProxy.java | 62 +--- .../io/metersphere/api/dto/scenario/Body.java | 75 ++++- .../api/parse/ApiImportAbstractParser.java | 100 ++++++ .../api/parse/ApiImportParser.java | 13 + .../api/parse/ApiImportParserFactory.java | 17 + .../api/parse/JmeterDocumentParser.java | 227 +++++++++++++ .../io/metersphere/api/parse/MsParser.java | 101 ++++++ .../metersphere/api/parse/PostmanParser.java | 184 +++++++++++ .../metersphere/api/parse/Swagger2Parser.java | 309 ++++++++++++++++++ .../api/service/APITestService.java | 6 +- .../api/definition/components/Run.vue | 30 +- .../definition/components/body/ApiBody.vue | 12 +- .../request/http/ApiHttpRequestForm.vue | 32 +- .../common/components/MsJsonCodeEdit.vue | 2 +- 14 files changed, 1075 insertions(+), 95 deletions(-) create mode 100644 backend/src/main/java/io/metersphere/api/parse/ApiImportAbstractParser.java create mode 100644 backend/src/main/java/io/metersphere/api/parse/ApiImportParser.java create mode 100644 backend/src/main/java/io/metersphere/api/parse/ApiImportParserFactory.java create mode 100644 backend/src/main/java/io/metersphere/api/parse/JmeterDocumentParser.java create mode 100644 backend/src/main/java/io/metersphere/api/parse/MsParser.java create mode 100644 backend/src/main/java/io/metersphere/api/parse/PostmanParser.java create mode 100644 backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java index 06706fcdd7..2bf62c351b 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java @@ -1,6 +1,5 @@ package io.metersphere.api.dto.definition.request.sampler; -import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.annotation.JSONField; import com.alibaba.fastjson.annotation.JSONType; @@ -9,7 +8,6 @@ import io.metersphere.api.dto.definition.request.dns.MsDNSCacheManager; import io.metersphere.api.dto.scenario.Body; import io.metersphere.api.dto.scenario.KeyValue; import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; -import io.metersphere.api.dto.scenario.request.BodyFile; import io.metersphere.api.service.ApiTestEnvironmentService; import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs; import io.metersphere.commons.utils.CommonBeanFactory; @@ -23,16 +21,13 @@ import org.apache.jmeter.protocol.http.control.Header; import org.apache.jmeter.protocol.http.control.HeaderManager; import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy; import org.apache.jmeter.protocol.http.util.HTTPArgument; -import org.apache.jmeter.protocol.http.util.HTTPFileArg; import org.apache.jmeter.save.SaveService; import org.apache.jmeter.testelement.TestElement; import org.apache.jorphan.collections.HashTree; import java.net.URL; import java.net.URLDecoder; -import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; @Data @EqualsAndHashCode(callSuper = true) @@ -142,39 +137,11 @@ public class MsHTTPSamplerProxy extends MsTestElement { if (CollectionUtils.isNotEmpty(this.getArguments())) { sampler.setArguments(httpArguments(this.getArguments())); } + // 请求体 if (!StringUtils.equals(this.getMethod(), "GET")) { - List body = new ArrayList<>(); - if (this.getBody().isKV() || this.getBody().isBinary()) { - body = this.getBody().getKvs().stream().filter(KeyValue::isValid).collect(Collectors.toList()); - HTTPFileArg[] httpFileArgs = httpFileArgs(); - // 文件上传 - if (httpFileArgs.length > 0) { - sampler.setHTTPFiles(httpFileArgs()); - sampler.setDoMultipart(true); - } - } else if (this.getBody().isJson()) { - KeyValue keyValue = new KeyValue("", JSON.toJSONString(this.getBody().getJson())); - keyValue.setEnable(true); - keyValue.setEncode(false); - body.add(keyValue); - } else { - if (StringUtils.isNotBlank(this.getBody().getRaw())) { - sampler.setPostBodyRaw(true); - KeyValue keyValue = new KeyValue("", this.getBody().getRaw()); - keyValue.setEnable(true); - keyValue.setEncode(false); - body.add(keyValue); - } - if (StringUtils.isNotBlank(this.getBody().getXml())) { - sampler.setPostBodyRaw(true); - KeyValue keyValue = new KeyValue("", this.getBody().getXml()); - keyValue.setEnable(true); - keyValue.setEncode(false); - body.add(keyValue); - } - } - sampler.setArguments(httpArguments(body)); + List bodyParams = this.body.getBodyParams(sampler, this.getId()); + sampler.setArguments(httpArguments(bodyParams)); } final HashTree httpSamplerTree = tree.add(sampler); @@ -225,29 +192,6 @@ public class MsHTTPSamplerProxy extends MsTestElement { return arguments; } - private void setFileArg(List list, List files, KeyValue keyValue) { - final String BODY_FILE_DIR = "/opt/metersphere/data/body"; - if (files != null) { - files.forEach(file -> { - String paramName = keyValue.getName() == null ? this.getId() : keyValue.getName(); - String path = BODY_FILE_DIR + '/' + file.getId() + '_' + file.getName(); - String mimetype = keyValue.getContentType(); - list.add(new HTTPFileArg(path, paramName, mimetype)); - }); - } - } - - private HTTPFileArg[] httpFileArgs() { - List list = new ArrayList<>(); - this.getBody().getKvs().stream().filter(KeyValue::isFile).filter(KeyValue::isEnable).forEach(keyValue -> { - setFileArg(list, keyValue.getFiles(), keyValue); - }); - this.getBody().getBinary().stream().filter(KeyValue::isFile).filter(KeyValue::isEnable).forEach(keyValue -> { - setFileArg(list, keyValue.getFiles(), keyValue); - }); - return list.toArray(new HTTPFileArg[0]); - } - public void setHeader(HashTree tree) { HeaderManager headerManager = new HeaderManager(); headerManager.setEnabled(true); diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/Body.java b/backend/src/main/java/io/metersphere/api/dto/scenario/Body.java index cbf9a73ea3..bd03c0d8cc 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/Body.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/Body.java @@ -1,27 +1,32 @@ package io.metersphere.api.dto.scenario; +import io.metersphere.api.dto.scenario.request.BodyFile; import lombok.Data; import org.apache.commons.lang3.StringUtils; +import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy; +import org.apache.jmeter.protocol.http.util.HTTPFileArg; +import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; @Data public class Body { private String type; private String raw; private String format; - private List fromUrlencoded; private List kvs; private List binary; private Object json; private String xml; - private final static String KV = "KeyValue"; - private final static String FORM_DATA = "Form Data"; - private final static String RAW = "Raw"; - private final static String BINARY = "BINARY"; - private final static String JSON = "JSON"; - private final static String XML = "XML"; + public final static String KV = "KeyValue"; + public final static String FORM_DATA = "Form Data"; + public final static String WWW_FROM = "WWW_FORM"; + public final static String RAW = "Raw"; + public final static String BINARY = "BINARY"; + public final static String JSON = "JSON"; + public final static String XML = "XML"; public boolean isValid() { if (this.isKV()) { @@ -32,7 +37,54 @@ public class Body { } public boolean isKV() { - return StringUtils.equals(type, KV); + if (StringUtils.equals(type, FORM_DATA) || StringUtils.equals(type, WWW_FROM)) { + return true; + } else return false; + } + + public List getBodyParams(HTTPSamplerProxy sampler, String requestId) { + List body = new ArrayList<>(); + if (this.isKV() || this.isBinary()) { + body = this.getKvs().stream().filter(KeyValue::isValid).collect(Collectors.toList()); + HTTPFileArg[] httpFileArgs = httpFileArgs(requestId); + // 文件上传 + if (httpFileArgs.length > 0) { + sampler.setHTTPFiles(httpFileArgs(requestId)); + sampler.setDoMultipart(true); + } + } else { + if (!this.isJson()) { + sampler.setPostBodyRaw(true); + } + KeyValue keyValue = new KeyValue("", this.getRaw()); + keyValue.setEnable(true); + keyValue.setEncode(false); + body.add(keyValue); + } + return body; + } + + private HTTPFileArg[] httpFileArgs(String requestId) { + List list = new ArrayList<>(); + this.getKvs().stream().filter(KeyValue::isFile).filter(KeyValue::isEnable).forEach(keyValue -> { + setFileArg(list, keyValue.getFiles(), keyValue, requestId); + }); + this.getBinary().stream().filter(KeyValue::isFile).filter(KeyValue::isEnable).forEach(keyValue -> { + setFileArg(list, keyValue.getFiles(), keyValue, requestId); + }); + return list.toArray(new HTTPFileArg[0]); + } + + private void setFileArg(List list, List files, KeyValue keyValue, String requestId) { + final String BODY_FILE_DIR = "/opt/metersphere/data/body"; + if (files != null) { + files.forEach(file -> { + String paramName = keyValue.getName() == null ? requestId : keyValue.getName(); + String path = BODY_FILE_DIR + '/' + file.getId() + '_' + file.getName(); + String mimetype = keyValue.getContentType(); + list.add(new HTTPFileArg(path, paramName, mimetype)); + }); + } } public boolean isBinary() { @@ -47,4 +99,11 @@ public class Body { return StringUtils.equals(type, XML); } + public boolean isWwwFROM() { + return StringUtils.equals(type, WWW_FROM); + } + + public boolean isFromData() { + return StringUtils.equals(type, FORM_DATA); + } } diff --git a/backend/src/main/java/io/metersphere/api/parse/ApiImportAbstractParser.java b/backend/src/main/java/io/metersphere/api/parse/ApiImportAbstractParser.java new file mode 100644 index 0000000000..f8f19bc121 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/parse/ApiImportAbstractParser.java @@ -0,0 +1,100 @@ +package io.metersphere.api.parse; + +import io.metersphere.api.dto.ApiTestImportRequest; +import io.metersphere.api.dto.scenario.KeyValue; +import io.metersphere.api.dto.scenario.Scenario; +import io.metersphere.api.dto.scenario.request.HttpRequest; +import io.metersphere.commons.exception.MSException; +import io.metersphere.commons.utils.LogUtil; +import org.apache.commons.lang3.StringUtils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public abstract class ApiImportAbstractParser implements ApiImportParser { + + protected String getApiTestStr(InputStream source) { + StringBuilder testStr = null; + try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(source, StandardCharsets.UTF_8))) { + testStr = new StringBuilder(); + String inputStr; + while ((inputStr = bufferedReader.readLine()) != null) { + testStr.append(inputStr); + } + } catch (Exception e) { + MSException.throwException(e.getMessage()); + LogUtil.error(e.getMessage(), e); + } finally { + try { + source.close(); + } catch (IOException e) { + MSException.throwException(e.getMessage()); + LogUtil.error(e.getMessage(), e); + } + } + return testStr.toString(); + } + + protected void setScenarioByRequest(Scenario scenario, ApiTestImportRequest request) { + if (request.getUseEnvironment()) { + scenario.setEnvironmentId(request.getEnvironmentId()); + } + } + + protected void addContentType(HttpRequest request, String contentType) { +// addHeader(request, "Content-Type", contentType); + } + + protected void addCookie(List headers, String key, String value) { + addCookie(headers, key, value, ""); + } + + protected void addCookie(List headers, String key, String value, String description) { + boolean hasCookie = false; + for (KeyValue header : headers) { + if (StringUtils.equalsIgnoreCase("Cookie", header.getName())) { + hasCookie = true; + String cookies = Optional.ofNullable(header.getValue()).orElse(""); + header.setValue(cookies + key + "=" + value + ";"); + } + } + if (!hasCookie) { + addHeader(headers, "Cookie", key + "=" + value + ";", description); + } + } + + protected void addHeader(List headers, String key, String value) { + addHeader(headers, key, value, ""); + } + + protected void addHeader(List headers, String key, String value, String description) { + boolean hasContentType = false; + for (KeyValue header : headers) { + if (StringUtils.equalsIgnoreCase(header.getName(), key)) { + hasContentType = true; + } + } + if (!hasContentType) { + headers.add(new KeyValue(key, value, description)); + } + } +// protected void addHeader(HttpRequest request, String key, String value) { +// List headers = Optional.ofNullable(request.getHeaders()).orElse(new ArrayList<>()); +// boolean hasContentType = false; +// for (KeyValue header : headers) { +// if (StringUtils.equalsIgnoreCase(header.getName(), key)) { +// hasContentType = true; +// } +// } +// if (!hasContentType) { +// headers.add(new KeyValue(key, value)); +// } +// request.setHeaders(headers); +// } +} diff --git a/backend/src/main/java/io/metersphere/api/parse/ApiImportParser.java b/backend/src/main/java/io/metersphere/api/parse/ApiImportParser.java new file mode 100644 index 0000000000..6173f71c77 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/parse/ApiImportParser.java @@ -0,0 +1,13 @@ +package io.metersphere.api.parse; + +import io.metersphere.api.dto.ApiTestImportRequest; +import io.metersphere.api.dto.definition.parse.ApiDefinitionImport; +import io.metersphere.api.dto.parse.ApiImport; + +import java.io.InputStream; + +public interface ApiImportParser { + ApiImport parse(InputStream source, ApiTestImportRequest request); + ApiDefinitionImport parseApi(InputStream source, ApiTestImportRequest request); + +} diff --git a/backend/src/main/java/io/metersphere/api/parse/ApiImportParserFactory.java b/backend/src/main/java/io/metersphere/api/parse/ApiImportParserFactory.java new file mode 100644 index 0000000000..ee41450068 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/parse/ApiImportParserFactory.java @@ -0,0 +1,17 @@ +package io.metersphere.api.parse; + +import io.metersphere.commons.constants.ApiImportPlatform; +import org.apache.commons.lang3.StringUtils; + +public class ApiImportParserFactory { + public static ApiImportParser getApiImportParser(String platform) { + if (StringUtils.equals(ApiImportPlatform.Metersphere.name(), platform)) { + return new MsParser(); + } else if (StringUtils.equals(ApiImportPlatform.Postman.name(), platform)) { + return new PostmanParser(); + } else if (StringUtils.equals(ApiImportPlatform.Swagger2.name(), platform)) { + return new Swagger2Parser(); + } + return null; + } +} diff --git a/backend/src/main/java/io/metersphere/api/parse/JmeterDocumentParser.java b/backend/src/main/java/io/metersphere/api/parse/JmeterDocumentParser.java new file mode 100644 index 0000000000..4acf7f5690 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/parse/JmeterDocumentParser.java @@ -0,0 +1,227 @@ +package io.metersphere.api.parse; + +import io.metersphere.commons.utils.LogUtil; +import io.metersphere.commons.utils.ScriptEngineUtils; +import org.apache.commons.lang3.StringUtils; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.ByteArrayInputStream; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Map; + +public class JmeterDocumentParser { + private final static String HASH_TREE_ELEMENT = "hashTree"; + private final static String STRING_PROP = "stringProp"; + private final static String ARGUMENTS = "Arguments"; + private final static String COLLECTION_PROP = "collectionProp"; + private final static String HTTP_SAMPLER_PROXY = "MsHTTPSamplerProxy"; + private final static String ELEMENT_PROP = "elementProp"; + + public static byte[] parse(byte[] source) { + + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + try ( + ByteArrayInputStream byteStream = new ByteArrayInputStream(source) + ) { + InputSource inputSource = new InputSource(byteStream); + DocumentBuilder docBuilder = factory.newDocumentBuilder(); + final Document document = docBuilder.parse(inputSource); + final Element jmeterTestPlan = document.getDocumentElement(); + + NodeList childNodes = jmeterTestPlan.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node node = childNodes.item(i); + if (node instanceof Element) { + Element ele = (Element) node; + parseHashTree(ele); + } + } + return documentToBytes(document); + } catch (Exception e) { + LogUtil.error(e.getMessage(), e); + return source; + } + } + + private static byte[] documentToBytes(Document document) throws TransformerException { + DOMSource domSource = new DOMSource(document); + StringWriter writer = new StringWriter(); + StreamResult result = new StreamResult(writer); + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer transformer = tf.newTransformer(); + transformer.transform(domSource, result); + return writer.toString().getBytes(); + } + + private static void parseHashTree(Element hashTree) { + if (invalid(hashTree)) { + return; + } + + if (hashTree.getChildNodes().getLength() > 0) { + final NodeList childNodes = hashTree.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node node = childNodes.item(i); + if (node instanceof Element) { + Element ele = (Element) node; + if (invalid(ele)) { + continue; + } + + if (nodeNameEquals(ele, HASH_TREE_ELEMENT)) { + parseHashTree(ele); + } else if (nodeNameEquals(ele, ARGUMENTS)) { + processArguments(ele); + } else if (nodeNameEquals(ele, HTTP_SAMPLER_PROXY)) { + processHttpSamplerProxy(ele); + } + } + } + } + } + + private static void processHttpSamplerProxy(Element ele) { + if (invalid(ele)) { + return; + } + NodeList childNodes = ele.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node item = childNodes.item(i); + if (!(item instanceof Element)) { + continue; + } + Element element = (Element) item; + if (nodeNameEquals(element, ELEMENT_PROP) && "HTTPsampler.Arguments".equals(element.getAttribute("name"))) { + processArguments(element); + } else if ("HTTPSampler.path".equals(element.getAttribute("name"))) { + processStringProp(element); + } + } + } + + private static void processArguments(Element ele) { + if (invalid(ele)) { + return; + } + NodeList childNodes = ele.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node item = childNodes.item(i); + if (!(item instanceof Element)) { + continue; + } + Element element = (Element) item; + if (nodeNameEquals(item, COLLECTION_PROP) && "Arguments.arguments".equals(element.getAttribute("name"))) { + NodeList elementProps = item.getChildNodes(); + for (int j = 0; j < elementProps.getLength(); j++) { + Node elementProp = elementProps.item(j); + if (!(elementProp instanceof Element)) { + continue; + } + NodeList stringProps = elementProp.getChildNodes(); + for (int k = 0; k < stringProps.getLength(); k++) { + Node stringProp = stringProps.item(k); + if (!(stringProp instanceof Element)) { + continue; + } + processStringProp((Element) stringProp); + } + } + } + } + } + + private static void processStringProp(Element ele) { + String name = ele.getAttribute("name"); + switch (name) { + case "HTTPSampler.path": + String path = ele.getTextContent(); + Map parser = parserUrl(path); + String url = parser.get("URL"); + String params = parser.keySet().stream().filter(k -> !"URL".equals(k)).reduce("?", (u, k) -> { + String v = parser.get(k); + if (!StringUtils.equals("?", u)) { + u += "&"; + } + u += k + "=" + ScriptEngineUtils.calculate(v); + return u; + }); + ele.setTextContent(url + ((params != null && !params.equals("?")) ? params : "")); + break; + case "Argument.value": + String textContent = ele.getTextContent(); + if (StringUtils.startsWith(textContent, "@")) { + ele.setTextContent(ScriptEngineUtils.calculate(textContent)); + } + break; + default: + break; + } + } + + private static boolean nodeNameEquals(Node node, String desiredName) { + return desiredName.equals(node.getNodeName()) || desiredName.equals(node.getLocalName()); + } + + private static boolean invalid(Element ele) { + return !StringUtils.isBlank(ele.getAttribute("enabled")) && !Boolean.parseBoolean(ele.getAttribute("enabled")); + } + + private static Map parserUrl(String url) { +// 传递的URL参数 + Map strUrlParas = new HashMap<>(); + + String strUrl; + String strUrlParams; + + +// 解析访问地址 + if (url.contains("?")) { + String[] strUrlPatten = url.split("\\?"); + strUrl = strUrlPatten[0]; + strUrlParams = strUrlPatten[1]; + + } else { + strUrl = url; + strUrlParams = url; + } + + strUrlParas.put("URL", strUrl); +// 解析参数 + String[] params = null; + + if (strUrlParams.contains("&")) { + params = strUrlParams.split("&"); + } else { + params = new String[]{strUrlParams}; + } + +// 保存参数到参数容器 + for (String p : params) { + if (p.contains("=")) { + String[] param = p.split("="); + if (param.length == 1) { + strUrlParas.put(param[0], ""); + } else { + + String key = param[0]; + String value = param[1]; + + strUrlParas.put(key, value); + } + } + } + return strUrlParas; + } +} diff --git a/backend/src/main/java/io/metersphere/api/parse/MsParser.java b/backend/src/main/java/io/metersphere/api/parse/MsParser.java new file mode 100644 index 0000000000..c9d165e247 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/parse/MsParser.java @@ -0,0 +1,101 @@ +package io.metersphere.api.parse; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.parser.Feature; +import io.metersphere.api.dto.ApiTestImportRequest; +import io.metersphere.api.dto.definition.parse.ApiDefinitionImport; +import io.metersphere.api.dto.parse.ApiImport; +import io.metersphere.api.dto.scenario.request.RequestType; +import io.metersphere.commons.constants.MsRequestBodyType; +import org.apache.commons.lang3.StringUtils; + +import java.io.InputStream; + +public class MsParser extends ApiImportAbstractParser { + + @Override + public ApiImport parse(InputStream source, ApiTestImportRequest request) { + String testStr = getApiTestStr(source); + ApiImport apiImport = JSON.parseObject(parsePluginFormat(testStr), ApiImport.class); + apiImport.getScenarios().forEach(scenario -> setScenarioByRequest(scenario, request)); + return apiImport; + } + + @Override + public ApiDefinitionImport parseApi(InputStream source, ApiTestImportRequest request) { + String testStr = getApiTestStr(source); + ApiDefinitionImport apiImport = JSON.parseObject(testStr, ApiDefinitionImport.class); + return apiImport; + } + + private String parsePluginFormat(String testStr) { + JSONObject testObject = JSONObject.parseObject(testStr, Feature.OrderedField); + if (testObject.get("scenarios") != null) { + return testStr; + } else { + //插件格式 + JSONArray scenarios = new JSONArray(); + testObject.keySet().forEach(scenarioName -> { + JSONObject scenario = new JSONObject(); + scenario.put("name", scenarioName); + JSONArray requestsObjects = new JSONArray(); + JSONObject requestsObject = testObject.getJSONObject(scenarioName); + requestsObject.keySet().forEach(requestName -> { + JSONObject requestObject = new JSONObject(true); + JSONObject requestTmpObject = requestsObject.getJSONObject(requestName); + //排序,确保type在第一个,否则转换失败 + if (StringUtils.isBlank(requestTmpObject.getString("type"))) { + requestObject.put("type", RequestType.HTTP); + } + + requestTmpObject.keySet().forEach(key -> requestObject.put(key, requestTmpObject.get(key))); + requestObject.put("name", requestName); + parseBody(requestObject); + requestsObjects.add(requestObject); + }); + scenario.put("requests", requestsObjects); + scenarios.add(scenario); + }); + JSONObject result = new JSONObject(); + result.put("scenarios", scenarios); + return result.toJSONString(); + } + } + + private void parseBody(JSONObject requestObject) { + if (requestObject.containsKey("body")) { + Object body = requestObject.get("body"); + if (body instanceof JSONArray) { + JSONArray bodies = requestObject.getJSONArray("body"); + if (bodies != null) { + StringBuilder bodyStr = new StringBuilder(); + for (int i = 0; i < bodies.size(); i++) { + String tmp = bodies.getString(i); + bodyStr.append(tmp); + } + JSONObject bodyObject = new JSONObject(); + bodyObject.put("raw", bodyStr); + bodyObject.put("type", MsRequestBodyType.RAW.value()); + requestObject.put("body", bodyObject); + } + } else if (body instanceof JSONObject) { + JSONObject bodyObj = requestObject.getJSONObject("body"); + if (bodyObj != null) { + JSONArray kvs = new JSONArray(); + bodyObj.keySet().forEach(key -> { + JSONObject kv = new JSONObject(); + kv.put("name", key); + kv.put("value", bodyObj.getString(key)); + kvs.add(kv); + }); + JSONObject bodyRes = new JSONObject(); + bodyRes.put("kvs", kvs); + bodyRes.put("type", MsRequestBodyType.KV.value()); + requestObject.put("body", bodyRes); + } + } + } + } +} diff --git a/backend/src/main/java/io/metersphere/api/parse/PostmanParser.java b/backend/src/main/java/io/metersphere/api/parse/PostmanParser.java new file mode 100644 index 0000000000..e8eed52279 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/parse/PostmanParser.java @@ -0,0 +1,184 @@ +package io.metersphere.api.parse; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import io.metersphere.api.dto.ApiTestImportRequest; +import io.metersphere.api.dto.definition.ApiDefinitionResult; +import io.metersphere.api.dto.definition.parse.ApiDefinitionImport; +import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.definition.request.configurations.MsHeaderManager; +import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy; +import io.metersphere.api.dto.parse.ApiImport; +import io.metersphere.api.dto.parse.postman.*; +import io.metersphere.api.dto.scenario.Body; +import io.metersphere.api.dto.scenario.KeyValue; +import io.metersphere.api.dto.scenario.Scenario; +import io.metersphere.api.dto.scenario.request.HttpRequest; +import io.metersphere.api.dto.scenario.request.Request; +import io.metersphere.api.dto.scenario.request.RequestType; +import io.metersphere.commons.constants.MsRequestBodyType; +import io.metersphere.commons.constants.PostmanRequestBodyMode; +import org.apache.commons.lang3.StringUtils; +import org.apache.jorphan.collections.HashTree; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.UUID; + +public class PostmanParser extends ApiImportAbstractParser { + + @Override + public ApiImport parse(InputStream source, ApiTestImportRequest request) { + + String testStr = getApiTestStr(source); + PostmanCollection postmanCollection = JSON.parseObject(testStr, PostmanCollection.class); + PostmanCollectionInfo info = postmanCollection.getInfo(); + List variables = postmanCollection.getVariable(); + ApiImport apiImport = new ApiImport(); + List scenarios = new ArrayList<>(); + + Scenario scenario = new Scenario(); + scenario.setName(info.getName()); + setScenarioByRequest(scenario, request); + parseItem(postmanCollection.getItem(), scenario, variables, scenarios); + apiImport.setScenarios(scenarios); + + return apiImport; + } + + @Override + public ApiDefinitionImport parseApi(InputStream source, ApiTestImportRequest request) { + String testStr = getApiTestStr(source); + PostmanCollection postmanCollection = JSON.parseObject(testStr, PostmanCollection.class); + List variables = postmanCollection.getVariable(); + ApiDefinitionImport apiImport = new ApiDefinitionImport(); + List requests = new ArrayList<>(); + + parseItem(postmanCollection.getItem(), variables, requests); + apiImport.setData(requests); + return apiImport; + } + + private void parseItem(List items, List variables, List scenarios) { + for (PostmanItem item : items) { + List childItems = item.getItem(); + if (childItems != null) { + parseItem(childItems, variables, scenarios); + } else { + ApiDefinitionResult request = parsePostman(item); + if (request != null) { + scenarios.add(request); + } + } + } + } + + private ApiDefinitionResult parsePostman(PostmanItem requestItem) { + PostmanRequest requestDesc = requestItem.getRequest(); + if (requestDesc == null) { + return null; + } + PostmanUrl url = requestDesc.getUrl(); + ApiDefinitionResult request = new ApiDefinitionResult(); + request.setName(requestItem.getName()); + request.setPath(url.getRaw()); + request.setMethod(requestDesc.getMethod()); + request.setProtocol(RequestType.HTTP); + MsHTTPSamplerProxy requestElement = new MsHTTPSamplerProxy(); + requestElement.setName(requestItem.getName() + "Postman MHTTPSamplerProxy"); + requestElement.setBody(parseBody(requestDesc)); + requestElement.setArguments(parseKeyValue(url.getQuery())); + requestElement.setProtocol(RequestType.HTTP); + requestElement.setPath(url.getRaw()); + requestElement.setMethod(requestDesc.getMethod()); + requestElement.setId(UUID.randomUUID().toString()); + requestElement.setRest(new ArrayList()); + MsHeaderManager headerManager = new MsHeaderManager(); + headerManager.setId(UUID.randomUUID().toString()); + headerManager.setName(requestItem.getName() + "Postman MsHeaderManager"); + headerManager.setHeaders(parseKeyValue(requestDesc.getHeader())); + HashTree tree = new HashTree(); + tree.add(headerManager); + LinkedList list = new LinkedList<>(); + list.add(headerManager); + requestElement.setHashTree(list); + request.setRequest(JSON.toJSONString(requestElement)); + return request; + } + + + private List parseKeyValue(List postmanKeyValues) { + if (postmanKeyValues == null) { + return null; + } + List keyValues = new ArrayList<>(); + postmanKeyValues.forEach(item -> keyValues.add(new KeyValue(item.getKey(), item.getValue()))); + return keyValues; + } + + private void parseItem(List items, Scenario scenario, List variables, List scenarios) { + List requests = new ArrayList<>(); + for (PostmanItem item : items) { + List childItems = item.getItem(); + if (childItems != null) { + Scenario subScenario = new Scenario(); + subScenario.setName(item.getName()); + subScenario.setEnvironmentId(scenario.getEnvironmentId()); + parseItem(childItems, subScenario, variables, scenarios); + } else { + Request request = parseRequest(item); + if (request != null) { + requests.add(request); + } + } + } + scenario.setVariables(parseKeyValue(variables)); + scenario.setRequests(requests); + scenarios.add(scenario); + } + + private Request parseRequest(PostmanItem requestItem) { + HttpRequest request = new HttpRequest(); + PostmanRequest requestDesc = requestItem.getRequest(); + if (requestDesc == null) { + return null; + } + PostmanUrl url = requestDesc.getUrl(); + request.setName(requestItem.getName()); + request.setUrl(url.getRaw()); + request.setUseEnvironment(false); + request.setMethod(requestDesc.getMethod()); + request.setHeaders(parseKeyValue(requestDesc.getHeader())); + request.setParameters(parseKeyValue(url.getQuery())); + request.setBody(parseBody(requestDesc)); + return request; + } + + private Body parseBody(PostmanRequest requestDesc) { + Body body = new Body(); + JSONObject postmanBody = requestDesc.getBody(); + if (postmanBody == null) { + return null; + } + String bodyMode = postmanBody.getString("mode"); + if (StringUtils.equals(bodyMode, PostmanRequestBodyMode.RAW.value())) { + body.setRaw(postmanBody.getString(bodyMode)); + body.setType(MsRequestBodyType.RAW.value()); + JSONObject options = postmanBody.getJSONObject("options"); + if (options != null) { + JSONObject raw = options.getJSONObject(PostmanRequestBodyMode.RAW.value()); + if (raw != null) { + body.setFormat(raw.getString("language")); + } + } + } else if (StringUtils.equals(bodyMode, PostmanRequestBodyMode.FORM_DATA.value()) || StringUtils.equals(bodyMode, PostmanRequestBodyMode.URLENCODED.value())) { + List postmanKeyValues = JSON.parseArray(postmanBody.getString(bodyMode), PostmanKeyValue.class); + body.setType(MsRequestBodyType.KV.value()); + body.setKvs(parseKeyValue(postmanKeyValues)); + } + return body; + } + +} diff --git a/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java b/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java new file mode 100644 index 0000000000..687ab38786 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java @@ -0,0 +1,309 @@ +package io.metersphere.api.parse; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import io.metersphere.api.dto.ApiTestImportRequest; +import io.metersphere.api.dto.definition.ApiDefinitionResult; +import io.metersphere.api.dto.definition.parse.ApiDefinitionImport; +import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy; +import io.metersphere.api.dto.parse.ApiImport; +import io.metersphere.api.dto.scenario.Body; +import io.metersphere.api.dto.scenario.KeyValue; +import io.metersphere.api.dto.scenario.request.RequestType; +import io.metersphere.commons.constants.MsRequestBodyType; +import io.metersphere.commons.constants.SwaggerParameterType; +import io.swagger.models.*; +import io.swagger.models.parameters.*; +import io.swagger.models.properties.*; +import io.swagger.parser.SwaggerParser; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.InputStream; +import java.sql.Connection; +import java.util.*; + +public class Swagger2Parser extends ApiImportAbstractParser { + + private Map definitions = null; + + @Override + public ApiDefinitionImport parseApi(InputStream source, ApiTestImportRequest request) { + Swagger swagger; + if (StringUtils.isNotBlank(request.getSwaggerUrl())) { + swagger = new SwaggerParser().read(request.getSwaggerUrl()); + } else { + swagger = new SwaggerParser().readWithInfo(getApiTestStr(source)).getSwagger(); + } + ApiDefinitionImport definitionImport = new ApiDefinitionImport(); + definitionImport.setData(parseRequests(swagger)); + return definitionImport; + } + + @Override + public ApiImport parse(InputStream source, ApiTestImportRequest request) { + return null; + } + + private List parseRequests(Swagger swagger) { + List results = new LinkedList<>(); + Map paths = swagger.getPaths(); + Set pathNames = paths.keySet(); + + this.definitions = swagger.getDefinitions(); + + for (String pathName : pathNames) { + Path path = paths.get(pathName); + Map operationMap = path.getOperationMap(); + Set httpMethods = operationMap.keySet(); + for (HttpMethod method : httpMethods) { + Operation operation = operationMap.get(method); + + ApiDefinitionResult apiDefinition = buildApiDefinition(operation, pathName, method.name()); + MsHTTPSamplerProxy request = buildRequest(operation, pathName, method.name()); + parseParameters(operation, request); + apiDefinition.setRequest(JSON.toJSONString(request)); + results.add(apiDefinition); + + +// List tags = operation.getTags(); +// if (tags != null) { +// tags.forEach(tag -> { +// Scenario scenario = Optional.ofNullable(scenarioMap.get(tag)).orElse(new Scenario()); +// List requests = Optional.ofNullable(scenario.getRequests()).orElse(new ArrayList<>()); +// requests.add(request); +// scenario.setRequests(requests); +// scenario.setName(tag); +// scenarioMap.put(tag, scenario); +// }); +// } else { +// Scenario scenario = Optional.ofNullable(scenarioMap.get("default")).orElse(new Scenario()); +// List requests = Optional.ofNullable(scenario.getRequests()).orElse(new ArrayList<>()); +// requests.add(request); +// scenario.setRequests(requests); +// scenarioMap.put("default", scenario); +// } + + } + } + + this.definitions = null; + + return results; + } + + private ApiDefinitionResult buildApiDefinition(Operation operation, String path, String method) { + ApiDefinitionResult apiDefinition = new ApiDefinitionResult(); + if (StringUtils.isNotBlank(operation.getSummary())) { + apiDefinition.setName(operation.getSummary()); + } else { + apiDefinition.setName(operation.getOperationId()); + } + apiDefinition.setPath(path); + apiDefinition.setProtocol(RequestType.HTTP); + apiDefinition.setMethod(method); + return apiDefinition; + } + private MsHTTPSamplerProxy buildRequest(Operation operation, String path, String method) { + MsHTTPSamplerProxy request = new MsHTTPSamplerProxy(); + if (StringUtils.isNotBlank(operation.getSummary())) { + request.setName(operation.getSummary()); + } else { + request.setName(operation.getOperationId()); + } + request.setPath(path); + request.setMethod(method); + request.setProtocol(RequestType.HTTP); + return request; + } + + private void parseParameters(Operation operation, MsHTTPSamplerProxy request) { + + List parameters = operation.getParameters(); + request.setId(UUID.randomUUID().toString()); + request.setHeaders(new ArrayList<>()); + request.setArguments(new ArrayList<>()); + request.setRest(new ArrayList<>()); + request.setBody(new Body()); + request.getBody().setType(getBodyType(operation)); + + // todo 路径变量 {xxx} 是否要转换 + + for (Parameter parameter : parameters) { + switch (parameter.getIn()) { + case SwaggerParameterType.PATH: + parsePathParameters(parameter, request.getRest()); + break; + case SwaggerParameterType.QUERY: + parseQueryParameters(parameter, request.getArguments()); + break; + case SwaggerParameterType.FORM_DATA: + parseFormDataParameters((FormParameter) parameter, request.getBody()); + break; + case SwaggerParameterType.BODY: + parseBodyParameters(parameter, request.getBody()); + break; + case SwaggerParameterType.HEADER: + parseHeaderParameters(parameter, request.getHeaders()); + break; + case SwaggerParameterType.COOKIE: + parseCookieParameters(parameter, request.getHeaders()); + break; +// case SwaggerParameterType.FILE: +// parsePathParameters(parameter, request); +// break; + } + } + + +// List responseContentTypes = operation.getProduces(); + } + + private String getBodyType(Operation operation) { + if (CollectionUtils.isEmpty(operation.getConsumes())) { + return Body.RAW; + } + String contentType = operation.getConsumes().get(0); + String bodyType = ""; + switch (contentType) { + case "application/x-www-form-urlencoded": + bodyType = Body.WWW_FROM; + break; + case "multipart/form-data": + bodyType = Body.FORM_DATA; + break; + case "application/json": + bodyType = Body.JSON; + break; + case "application/xml": + bodyType = Body.XML; + break; +// case "": //todo binary 啥类型 +// bodyType = Body.BINARY; +// break; + default: + bodyType = Body.RAW; + } + return bodyType; + } + + private void parsePathParameters(Parameter parameter, List rests) { + PathParameter pathParameter = (PathParameter) parameter; + rests.add(new KeyValue(pathParameter.getName(), "", getDefaultStringValue(parameter.getDescription()))); + } + + private String getDefaultStringValue(String val) { + return StringUtils.isBlank(val) ? "" : val; + } + + private void parseCookieParameters(Parameter parameter, List headers) { + CookieParameter cookieParameter = (CookieParameter) parameter; + addCookie(headers, cookieParameter.getName(), "", getDefaultStringValue(cookieParameter.getDescription())); + } + + private void parseHeaderParameters(Parameter parameter, List headers) { + HeaderParameter headerParameter = (HeaderParameter) parameter; + addHeader(headers, headerParameter.getName(), "", getDefaultStringValue(headerParameter.getDescription())); + } + + private void parseBodyParameters(Parameter parameter, Body body) { + BodyParameter bodyParameter = (BodyParameter) parameter; + Model schema = bodyParameter.getSchema(); + + // 引用模型 + if (schema instanceof RefModel) { + String simpleRef = ""; + RefModel refModel = (RefModel) bodyParameter.getSchema(); + String originalRef = refModel.getOriginalRef(); + if (refModel.getOriginalRef().split("/").length > 3) { + simpleRef = originalRef.replace("#/definitions/", ""); + } else { + simpleRef = refModel.getSimpleRef(); + } + Model model = this.definitions.get(simpleRef); + HashSet refSet = new HashSet<>(); + refSet.add(simpleRef); + if (model != null) { + JSONObject bodyParameters = getBodyParameters(model.getProperties(), refSet); + body.setRaw(bodyParameters.toJSONString()); + } + } else if (schema instanceof ArrayModel) { + //模型数组 + ArrayModel arrayModel = (ArrayModel) bodyParameter.getSchema(); + Property items = arrayModel.getItems(); + if (items instanceof RefProperty) { + RefProperty refProperty = (RefProperty) items; + String simpleRef = refProperty.getSimpleRef(); + HashSet refSet = new HashSet<>(); + refSet.add(simpleRef); + Model model = definitions.get(simpleRef); + JSONArray propertyList = new JSONArray(); + propertyList.add(getBodyParameters(model.getProperties(), refSet)); + body.setRaw(propertyList.toString()); + } + } + body.setFormat("json"); + } + + private JSONObject getBodyParameters(Map properties, HashSet refSet) { + JSONObject jsonObject = new JSONObject(); + if (properties != null) { + properties.forEach((key, value) -> { + if (value instanceof ObjectProperty) { + ObjectProperty objectProperty = (ObjectProperty) value; + jsonObject.put(key, getBodyParameters(objectProperty.getProperties(), refSet)); + } else if (value instanceof ArrayProperty) { + ArrayProperty arrayProperty = (ArrayProperty) value; + Property items = arrayProperty.getItems(); + if (items instanceof RefProperty) { + RefProperty refProperty = (RefProperty) items; + String simpleRef = refProperty.getSimpleRef(); + if (refSet.contains(simpleRef)) { + //避免嵌套死循环 + jsonObject.put(key, new JSONArray()); + return; + } + refSet.add(simpleRef); + Model model = this.definitions.get(simpleRef); + JSONArray propertyList = new JSONArray(); + propertyList.add(getBodyParameters(model.getProperties(), refSet)); + jsonObject.put(key, propertyList); + } else { + jsonObject.put(key, new ArrayList<>()); + } + } else { + jsonObject.put(key, getDefaultValueByPropertyType(value)); + } + }); + } + return jsonObject; + } + + private Object getDefaultValueByPropertyType(Property value) { + if (value instanceof LongProperty || value instanceof IntegerProperty + || value instanceof BaseIntegerProperty) { + return 0; + } else if (value instanceof FloatProperty || value instanceof DoubleProperty + || value instanceof DecimalProperty) { + return 0.0; + } else {// todo 其他类型? + return getDefaultStringValue(value.getDescription()); + } + } + + private void parseFormDataParameters(FormParameter parameter, Body body) { + List keyValues = Optional.ofNullable(body.getKvs()).orElse(new ArrayList<>()); + KeyValue kv = new KeyValue(parameter.getName(), "", getDefaultStringValue(parameter.getDescription())); + if (StringUtils.equals(parameter.getType(), "file") ) { + kv.setType("file"); + } + keyValues.add(kv); + body.setKvs(keyValues); + } + + private void parseQueryParameters(Parameter parameter, List arguments) { + QueryParameter queryParameter = (QueryParameter) parameter; + arguments.add(new KeyValue(queryParameter.getName(), "", getDefaultStringValue(queryParameter.getDescription()))); + } +} diff --git a/backend/src/main/java/io/metersphere/api/service/APITestService.java b/backend/src/main/java/io/metersphere/api/service/APITestService.java index 9dcac1c29f..a3a6dfb917 100644 --- a/backend/src/main/java/io/metersphere/api/service/APITestService.java +++ b/backend/src/main/java/io/metersphere/api/service/APITestService.java @@ -6,9 +6,9 @@ import io.metersphere.api.dto.*; import io.metersphere.api.dto.parse.ApiImport; import io.metersphere.api.dto.scenario.request.dubbo.RegistryCenter; import io.metersphere.api.jmeter.JMeterService; -import io.metersphere.api.parse.ApiImportParser; -import io.metersphere.api.parse.ApiImportParserFactory; -import io.metersphere.api.parse.JmeterDocumentParser; +import io.metersphere.api.parse.old.ApiImportParser; +import io.metersphere.api.parse.old.ApiImportParserFactory; +import io.metersphere.api.parse.old.JmeterDocumentParser; import io.metersphere.base.domain.*; import io.metersphere.base.mapper.ApiTestFileMapper; import io.metersphere.base.mapper.ApiTestMapper; diff --git a/frontend/src/business/components/api/definition/components/Run.vue b/frontend/src/business/components/api/definition/components/Run.vue index 69da69a55e..3d9e370d69 100644 --- a/frontend/src/business/components/api/definition/components/Run.vue +++ b/frontend/src/business/components/api/definition/components/Run.vue @@ -75,21 +75,23 @@ }); } }); - request.body.binary.forEach(param => { - if (param.files) { - param.files.forEach(item => { - if (item.file) { - if (!item.id) { - let fileId = getUUID().substring(0, 12); - item.name = item.file.name; - item.id = fileId; + if (request.body.binary) { + request.body.binary.forEach(param => { + if (param.files) { + param.files.forEach(item => { + if (item.file) { + if (!item.id) { + let fileId = getUUID().substring(0, 12); + item.name = item.file.name; + item.id = fileId; + } + obj.bodyUploadIds.push(item.id); + bodyUploadFiles.push(item.file); } - obj.bodyUploadIds.push(item.id); - bodyUploadFiles.push(item.file); - } - }); - } - }); + }); + } + }); + } } }); return bodyUploadFiles; diff --git a/frontend/src/business/components/api/definition/components/body/ApiBody.vue b/frontend/src/business/components/api/definition/components/body/ApiBody.vue index ce8d599ae8..aaa10515b9 100644 --- a/frontend/src/business/components/api/definition/components/body/ApiBody.vue +++ b/frontend/src/business/components/api/definition/components/body/ApiBody.vue @@ -1,7 +1,7 @@ diff --git a/frontend/src/business/components/settings/organization/components/TestReviewNotification.vue b/frontend/src/business/components/settings/organization/components/TestReviewNotification.vue index 2dbe6148c8..5509ffdbd8 100644 --- a/frontend/src/business/components/settings/organization/components/TestReviewNotification.vue +++ b/frontend/src/business/components/settings/organization/components/TestReviewNotification.vue @@ -11,7 +11,7 @@ {{ $t('commons.cancel') }} { - this.form.reviewTask = response.data; - this.form.reviewTask.forEach(planTask => { + this.reviewTask = response.data; + this.reviewTask.forEach(planTask => { this.handleReviewReceivers(planTask); }); }) @@ -176,7 +171,7 @@ export default { Task.isSet = true; Task.identification = ''; Task.taskType = TASK_TYPE - this.form.reviewTask.push(Task) + this.reviewTask.push(Task) }, handleAddTask(index, data) { @@ -242,6 +237,13 @@ export default { } row.reviewReceiverOptions = reviewReceiverOptions; } + }, + watch: { + reviewReceiverOptions(value) { + if (value && value.length > 0) { + this.initForm(); + } + } } } From 18a4602e62e8d3eb7597d937f961535fdedf45cd Mon Sep 17 00:00:00 2001 From: BugKing Date: Thu, 3 Dec 2020 17:20:02 +0800 Subject: [PATCH 11/16] =?UTF-8?q?build:=20=E8=A7=A3=E5=86=B3=20Docker=20lo?= =?UTF-8?q?gin=20=E7=94=A8=E6=88=B7=E5=90=8D=E4=B8=BA=E7=A9=BA=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-push.yml b/.github/workflows/build-push.yml index d279e2e0c7..a03b6cbd60 100644 --- a/.github/workflows/build-push.yml +++ b/.github/workflows/build-push.yml @@ -53,7 +53,7 @@ jobs: - name: Docker Login uses: docker/login-action@v1.6.0 with: - username: ${{ secrets.DOCKER_HUB_USERNAME }} + username: metersphere password: ${{ secrets.DOCKER_HUB_TOKEN }} - name: Build Docker Image From 71d11f0fb7f4250e09eabbed84844d276b17649c Mon Sep 17 00:00:00 2001 From: BugKing Date: Thu, 3 Dec 2020 17:34:01 +0800 Subject: [PATCH 12/16] =?UTF-8?q?build:=20=E6=9B=B4=E6=96=B0=20cache=20key?= =?UTF-8?q?=20&=20=E4=BF=AE=E5=A4=8D=E5=8F=98=E9=87=8F=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-push.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-push.yml b/.github/workflows/build-push.yml index a03b6cbd60..8b27575860 100644 --- a/.github/workflows/build-push.yml +++ b/.github/workflows/build-push.yml @@ -16,7 +16,6 @@ jobs: env: cache-name: cache-node-modules with: - # npm cache files are stored in `~/.npm` on Linux/macOS path: ~/.npm key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} restore-keys: | @@ -29,11 +28,10 @@ jobs: env: cache-name: cache-maven-deps with: - # npm cache files are stored in `~/.npm` on Linux/macOS path: ~/.m2 key: ${{ runner.os }}-build-${{ env.cache-name }} restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build-${{ env.cache-name }} ${{ runner.os }}-build- ${{ runner.os }}- @@ -59,5 +57,5 @@ jobs: - name: Build Docker Image uses: docker/build-push-action@v2.2.0 with: - build-args: MS_VERSION=${BRANCH_NAME}-b${GITHUB_RUN_NUMBER} - tags: metersphere/metersphere:${BRANCH_NAME} + build-args: MS_VERSION=${{ env.BRANCH_NAME }}-b${{ env.GITHUB_RUN_NUMBER }} + tags: metersphere/metersphere:${{ env.BRANCH_NAME }} From 9e4082720ea9afef327cbff9b336c588b43670f0 Mon Sep 17 00:00:00 2001 From: BugKing Date: Thu, 3 Dec 2020 18:15:40 +0800 Subject: [PATCH 13/16] =?UTF-8?q?build:=20=E6=9B=B4=E6=96=B0=20action?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-push.yml | 37 +++++++++++--------------------- 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/.github/workflows/build-push.yml b/.github/workflows/build-push.yml index 8b27575860..4a629f55fc 100644 --- a/.github/workflows/build-push.yml +++ b/.github/workflows/build-push.yml @@ -4,13 +4,12 @@ on: workflow_dispatch: jobs: - build: + build_push: runs-on: ubuntu-latest - + name: Build Docker Image and Push steps: - uses: actions/checkout@v2 - uses: nelonoel/branch-name@v1.0.1 - - name: Cache node modules uses: actions/cache@v2 env: @@ -23,17 +22,13 @@ jobs: ${{ runner.os }}-build- ${{ runner.os }}- - - name: Cache maven dependencies + - name: Cache local Maven repository uses: actions/cache@v2 - env: - cache-name: cache-maven-deps with: - path: ~/.m2 - key: ${{ runner.os }}-build-${{ env.cache-name }} + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }} - ${{ runner.os }}-build- - ${{ runner.os }}- + ${{ runner.os }}-maven- - name: Set up JDK 1.8 uses: actions/setup-java@v1 @@ -43,19 +38,13 @@ jobs: settings-path: ${{ github.workspace }} # location for the settings.xml file - name: Build with Maven - run: mvn -B package --file pom.xml - - - name: Docker Setup Buildx - uses: docker/setup-buildx-action@v1.0.3 - - - name: Docker Login - uses: docker/login-action@v1.6.0 + run: mvn -B package --file pom.xml + + - name: Push to Docker Hub + uses: docker/build-push-action@v1 with: username: metersphere password: ${{ secrets.DOCKER_HUB_TOKEN }} - - - name: Build Docker Image - uses: docker/build-push-action@v2.2.0 - with: - build-args: MS_VERSION=${{ env.BRANCH_NAME }}-b${{ env.GITHUB_RUN_NUMBER }} - tags: metersphere/metersphere:${{ env.BRANCH_NAME }} + repository: metersphere/metersphere + tag_with_ref: true + tag_with_sha: true From 0cd9fb7680fd70dcccb2860b40c7228ec1245609 Mon Sep 17 00:00:00 2001 From: BugKing Date: Thu, 3 Dec 2020 18:25:25 +0800 Subject: [PATCH 14/16] =?UTF-8?q?build:=20=E4=BF=AE=E6=94=B9=20Dockerfile?= =?UTF-8?q?=20=E5=9F=BA=E7=A1=80=E9=95=9C=E5=83=8F=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 8e0a6e24fa..5dc3c7e4d7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM registry.fit2cloud.com/metersphere/fabric8-java-alpine-openjdk8-jre +FROM metersphere/fabric8-java-alpine-openjdk8-jre MAINTAINER FIT2CLOUD From 9e6de725eb9b2f3f69bf8df263e1bf84fa0086f4 Mon Sep 17 00:00:00 2001 From: BugKing Date: Thu, 3 Dec 2020 19:18:08 +0800 Subject: [PATCH 15/16] =?UTF-8?q?build:=20=E6=9B=B4=E6=96=B0=20action?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-push.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-push.yml b/.github/workflows/build-push.yml index 4a629f55fc..c0e5ac9812 100644 --- a/.github/workflows/build-push.yml +++ b/.github/workflows/build-push.yml @@ -1,7 +1,14 @@ name: Build Docker Image and Push on: - workflow_dispatch: + push: + branches: + - main + - v1* + pull_request: + branches: + - main + - v1* jobs: build_push: @@ -9,7 +16,8 @@ jobs: name: Build Docker Image and Push steps: - uses: actions/checkout@v2 - - uses: nelonoel/branch-name@v1.0.1 + - name: Inject slug/short variables + uses: rlespinasse/github-slug-action@v3.x - name: Cache node modules uses: actions/cache@v2 env: @@ -47,4 +55,4 @@ jobs: password: ${{ secrets.DOCKER_HUB_TOKEN }} repository: metersphere/metersphere tag_with_ref: true - tag_with_sha: true + build_args: MS_VERSION=${{ env.GITHUB_REF_SLUG }}-${{ env.GITHUB_SHA_SHORT }} \ No newline at end of file From 5d37c0941373ff37fba3be4ea2b4814b744b5bc5 Mon Sep 17 00:00:00 2001 From: chenjianxing Date: Thu, 3 Dec 2020 19:36:08 +0800 Subject: [PATCH 16/16] =?UTF-8?q?fix:=20=E5=AF=BC=E5=85=A5id=E5=92=8C?= =?UTF-8?q?=E8=AF=B7=E6=B1=82id=E4=B8=80=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/metersphere/api/parse/Swagger2Parser.java | 2 +- .../metersphere/api/service/ApiDefinitionService.java | 2 -- .../api/definition/components/body/ApiBody.vue | 11 ++--------- .../components/api/definition/model/ApiTestModel.js | 2 +- .../components/common/components/MsJsonCodeEdit.vue | 2 +- 5 files changed, 5 insertions(+), 14 deletions(-) diff --git a/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java b/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java index 687ab38786..b00edd44ca 100644 --- a/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java +++ b/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java @@ -11,7 +11,6 @@ import io.metersphere.api.dto.parse.ApiImport; import io.metersphere.api.dto.scenario.Body; import io.metersphere.api.dto.scenario.KeyValue; import io.metersphere.api.dto.scenario.request.RequestType; -import io.metersphere.commons.constants.MsRequestBodyType; import io.metersphere.commons.constants.SwaggerParameterType; import io.swagger.models.*; import io.swagger.models.parameters.*; @@ -64,6 +63,7 @@ public class Swagger2Parser extends ApiImportAbstractParser { MsHTTPSamplerProxy request = buildRequest(operation, pathName, method.name()); parseParameters(operation, request); apiDefinition.setRequest(JSON.toJSONString(request)); + apiDefinition.setId(request.getId()); results.add(apiDefinition); diff --git a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java index 05f3665256..8786acb243 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java @@ -227,7 +227,6 @@ public class ApiDefinitionService { private ApiDefinition createTest(ApiDefinitionResult request, ApiDefinitionMapper batchMapper) { SaveApiDefinitionRequest saveReq = new SaveApiDefinitionRequest(); BeanUtils.copyBean(saveReq, request); - saveReq.setId(UUID.randomUUID().toString()); checkNameExist(saveReq); final ApiDefinitionWithBLOBs test = new ApiDefinitionWithBLOBs(); BeanUtils.copyBean(test, request); @@ -340,7 +339,6 @@ public class ApiDefinitionService { item.setModuleId(importRequest.getModuleId()); item.setModulePath(importRequest.getModulePath()); item.setEnvironmentId(importRequest.getEnvironmentId()); - item.setId(UUID.randomUUID().toString()); item.setUserId(null); createTest(item, batchMapper); if (i % 300 == 0) { diff --git a/frontend/src/business/components/api/definition/components/body/ApiBody.vue b/frontend/src/business/components/api/definition/components/body/ApiBody.vue index aaa10515b9..adaa1d4d8c 100644 --- a/frontend/src/business/components/api/definition/components/body/ApiBody.vue +++ b/frontend/src/business/components/api/definition/components/body/ApiBody.vue @@ -103,24 +103,20 @@ modeChange(mode) { switch (this.body.type) { case "JSON": - this.body.format = "json"; this.setContentType("application/json"); break; case "XML": - this.body.format = "xml"; this.setContentType("text/xml"); break; case "WWW_FORM": - this.body.format = "form"; this.setContentType("application/x-www-form-urlencoded"); break; + // todo from data case "BINARY": - this.body.format = "binary"; this.setContentType("application/octet-stream"); break; default: this.removeContentType(); - this.body.format = mode; break; } }, @@ -154,10 +150,7 @@ created() { if (!this.body.type) { - this.body.type = BODY_TYPE.KV; - } - if (!this.body.format) { - this.body.format = BODY_FORMAT.TEXT; + this.body.type = BODY_TYPE.FORM_DATA; } this.body.kvs.forEach(param => { if (!param.type) { diff --git a/frontend/src/business/components/api/definition/model/ApiTestModel.js b/frontend/src/business/components/api/definition/model/ApiTestModel.js index e1c13d65f3..adce92b3e1 100644 --- a/frontend/src/business/components/api/definition/model/ApiTestModel.js +++ b/frontend/src/business/components/api/definition/model/ApiTestModel.js @@ -736,7 +736,7 @@ export class Body extends BaseConfig { } isKV() { - return this.type === BODY_TYPE.KV; + return [BODY_TYPE.FORM_DATA, BODY_TYPE.WWW_FORM, BODY_TYPE.BINARY].indexOf(this.type) > 0; } } diff --git a/frontend/src/business/components/common/components/MsJsonCodeEdit.vue b/frontend/src/business/components/common/components/MsJsonCodeEdit.vue index c830d6bd3a..e908453689 100644 --- a/frontend/src/business/components/common/components/MsJsonCodeEdit.vue +++ b/frontend/src/business/components/common/components/MsJsonCodeEdit.vue @@ -42,7 +42,7 @@ return { editor: null, error: false, - json: JSON.parse(this.value), + json: JSON.parse(this.value ? this.value : "{}"), internalChange: false, expandedModes: ["tree", "view", "form"], };