From 04d57fa202866876b69a91a86e0a744b0fbffe2e Mon Sep 17 00:00:00 2001 From: sinkcup Date: Mon, 18 Oct 2021 15:35:37 +0800 Subject: [PATCH 1/3] feat: #73 coding sdk create issue --- app/Coding/Issue.php | 22 ++++ tests/Unit/CodingIssueTest.php | 46 ++++++++ tests/data/coding/CreateIssueResponse.json | 124 +++++++++++++++++++++ 3 files changed, 192 insertions(+) create mode 100644 app/Coding/Issue.php create mode 100644 tests/Unit/CodingIssueTest.php create mode 100644 tests/data/coding/CreateIssueResponse.json diff --git a/app/Coding/Issue.php b/app/Coding/Issue.php new file mode 100644 index 0000000..48ec48b --- /dev/null +++ b/app/Coding/Issue.php @@ -0,0 +1,22 @@ +client->request('POST', 'https://e.coding.net/open-api', [ + 'headers' => [ + 'Accept' => 'application/json', + 'Authorization' => "token ${token}", + 'Content-Type' => 'application/json' + ], + 'json' => array_merge([ + 'Action' => 'CreateIssue', + 'ProjectName' => $projectName, + ], $data), + ]); + return json_decode($response->getBody(), true)['Response']['Issue']; + } +} diff --git a/tests/Unit/CodingIssueTest.php b/tests/Unit/CodingIssueTest.php new file mode 100644 index 0000000..6ed8704 --- /dev/null +++ b/tests/Unit/CodingIssueTest.php @@ -0,0 +1,46 @@ +dataDir . 'coding/CreateIssueResponse.json'); + $codingToken = $this->faker->md5; + $codingProjectUri = $this->faker->slug; + $data = [ + 'Type' => 'REQUIREMENT', + 'Name' => $this->faker->title, + 'Priority' => $this->faker->randomElement([0, 1, 2, 3]), + ]; + + $clientMock = $this->getMockBuilder(Client::class)->getMock(); + $clientMock->expects($this->once()) + ->method('request') + ->with( + 'POST', + 'https://e.coding.net/open-api', + [ + 'headers' => [ + 'Accept' => 'application/json', + 'Authorization' => "token ${codingToken}", + 'Content-Type' => 'application/json' + ], + 'json' => array_merge([ + 'Action' => 'CreateIssue', + 'ProjectName' => $codingProjectUri, + ], $data) + ] + ) + ->willReturn(new Response(200, [], $responseBody)); + $coding = new Issue($clientMock); + $result = $coding->create($codingToken, $codingProjectUri, $data); + $this->assertEquals(json_decode($responseBody, true)['Response']['Issue'], $result); + } +} diff --git a/tests/data/coding/CreateIssueResponse.json b/tests/data/coding/CreateIssueResponse.json new file mode 100644 index 0000000..4e31528 --- /dev/null +++ b/tests/data/coding/CreateIssueResponse.json @@ -0,0 +1,124 @@ +{ + "Response" : { + "Issue" : { + "Assignee" : { + "Avatar" : "", + "Email" : "", + "GlobalKey" : "", + "Id" : 0, + "Name" : "", + "Phone" : "", + "Status" : 0, + "TeamGlobalKey" : "", + "TeamId" : 0 + }, + "Code" : 2742, + "CompletedAt" : 0, + "CreatedAt" : 1634541907501, + "Creator" : { + "Avatar" : "https://coding-net-production-static-ci.codehub.cn/2cb665a3-bebc-4b09-aa00-2b6df3e33edc.jpg?imageMogr2/auto-orient/format/jpeg/cut/400x400x0x0", + "Email" : "", + "GlobalKey" : "", + "Id" : 183478, + "Name" : "sinkcup", + "Phone" : "", + "Status" : 1, + "TeamGlobalKey" : "", + "TeamId" : 0 + }, + "CustomFields" : [], + "DefectType" : { + "IconUrl" : "", + "Id" : 0, + "Name" : "" + }, + "Description" : "", + "DueDate" : 0, + "Epic" : { + "Assignee" : { + "Avatar" : "", + "Email" : "", + "GlobalKey" : "", + "Id" : 0, + "Name" : "", + "Phone" : "", + "Status" : 0, + "TeamGlobalKey" : "", + "TeamId" : 0 + }, + "Code" : 0, + "IssueStatusId" : 0, + "IssueStatusName" : "", + "Name" : "", + "Priority" : "", + "Type" : "" + }, + "Files" : [], + "IssueStatusId" : 1227034, + "IssueStatusName" : "未开始", + "IssueStatusType" : "TODO", + "IssueTypeDetail" : { + "Description" : "需求是指用户解决某一个问题或达到某一目标所需的软件功能。", + "Id" : 213219, + "IsSystem" : true, + "IssueType" : "REQUIREMENT", + "Name" : "需求" + }, + "IssueTypeId" : 213219, + "Iteration" : { + "Code" : 0, + "Name" : "", + "Status" : "" + }, + "IterationId" : 0, + "Labels" : [], + "Name" : "issue by curl", + "Parent" : { + "Assignee" : { + "Avatar" : "", + "Email" : "", + "GlobalKey" : "", + "Id" : 0, + "Name" : "", + "Phone" : "", + "Status" : 0, + "TeamGlobalKey" : "", + "TeamId" : 0 + }, + "Code" : 0, + "IssueStatusId" : 0, + "IssueStatusName" : "", + "IssueStatusType" : "", + "IssueTypeDetail" : { + "Description" : "", + "Id" : 0, + "IsSystem" : false, + "IssueType" : "", + "Name" : "" + }, + "Name" : "", + "Priority" : "", + "Type" : "" + }, + "ParentType" : "REQUIREMENT", + "Priority" : "0", + "ProjectModule" : { + "Id" : 0, + "Name" : "" + }, + "RequirementType" : { + "Id" : 0, + "Name" : "" + }, + "StartDate" : 0, + "StoryPoint" : "", + "SubTasks" : [], + "ThirdLinks" : [], + "Type" : "REQUIREMENT", + "UpdatedAt" : 1634541907501, + "Watchers" : [], + "WorkingHours" : 0 + }, + "RequestId" : "5b7bae01-f26f-16d7-0d61-c8fa67ea0472" + } +} \ No newline at end of file From 814ed46f998101fb2b25f362b3aa8521cad04131 Mon Sep 17 00:00:00 2001 From: sinkcup Date: Tue, 19 Oct 2021 11:15:48 +0800 Subject: [PATCH 2/3] feat: #73 create issue failed --- app/Coding/Issue.php | 8 +++- tests/Unit/CodingIssueTest.php | 37 ++++++++++++++++++- .../coding/CreateIssueFailedResponse.json | 9 +++++ 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 tests/data/coding/CreateIssueFailedResponse.json diff --git a/app/Coding/Issue.php b/app/Coding/Issue.php index 48ec48b..b37ef64 100644 --- a/app/Coding/Issue.php +++ b/app/Coding/Issue.php @@ -2,6 +2,8 @@ namespace App\Coding; +use Exception; + class Issue extends Base { public function create($token, $projectName, $data) @@ -17,6 +19,10 @@ public function create($token, $projectName, $data) 'ProjectName' => $projectName, ], $data), ]); - return json_decode($response->getBody(), true)['Response']['Issue']; + $result = json_decode($response->getBody(), true); + if (isset($result['Response']['Error']['Message'])) { + throw new Exception($result['Response']['Error']['Message']); + } + return $result['Response']['Issue']; } } diff --git a/tests/Unit/CodingIssueTest.php b/tests/Unit/CodingIssueTest.php index 6ed8704..861fe1b 100644 --- a/tests/Unit/CodingIssueTest.php +++ b/tests/Unit/CodingIssueTest.php @@ -9,7 +9,7 @@ class CodingIssueTest extends TestCase { - public function testCreate() + public function testCreateSuccess() { $responseBody = file_get_contents($this->dataDir . 'coding/CreateIssueResponse.json'); $codingToken = $this->faker->md5; @@ -43,4 +43,39 @@ public function testCreate() $result = $coding->create($codingToken, $codingProjectUri, $data); $this->assertEquals(json_decode($responseBody, true)['Response']['Issue'], $result); } + + public function testCreateFailed() + { + $responseBody = file_get_contents($this->dataDir . 'coding/CreateIssueFailedResponse.json'); + $codingToken = $this->faker->md5; + $codingProjectUri = $this->faker->slug; + $data = [ + 'Type' => 'REQUIREMENT', + 'Name' => $this->faker->title, + 'Priority' => $this->faker->randomElement([0, 1, 2, 3]), + ]; + + $clientMock = $this->getMockBuilder(Client::class)->getMock(); + $clientMock->expects($this->once()) + ->method('request') + ->with( + 'POST', + 'https://e.coding.net/open-api', + [ + 'headers' => [ + 'Accept' => 'application/json', + 'Authorization' => "token ${codingToken}", + 'Content-Type' => 'application/json' + ], + 'json' => array_merge([ + 'Action' => 'CreateIssue', + 'ProjectName' => $codingProjectUri, + ], $data) + ] + ) + ->willReturn(new Response(200, [], $responseBody)); + $coding = new Issue($clientMock); + $this->expectException(\Exception::class); + $coding->create($codingToken, $codingProjectUri, $data); + } } diff --git a/tests/data/coding/CreateIssueFailedResponse.json b/tests/data/coding/CreateIssueFailedResponse.json new file mode 100644 index 0000000..e236145 --- /dev/null +++ b/tests/data/coding/CreateIssueFailedResponse.json @@ -0,0 +1,9 @@ +{ + "Response" : { + "Error" : { + "Code" : "FailedOperation", + "Message" : "issue_custom_field_required" + }, + "RequestId" : "504114e3-bcb7-5b51-6ff7-7bb5cb04f121" + } +} From 365e5847f755850f69d35efaf2a8991c87e31c8e Mon Sep 17 00:00:00 2001 From: sinkcup Date: Tue, 19 Oct 2021 11:16:31 +0800 Subject: [PATCH 3/3] feat: #73 cli create issue --- app/Commands/IssueCreateCommand.php | 67 ++++++++++++++++++++++++ tests/Feature/IssueCreateCommandTest.php | 59 +++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 app/Commands/IssueCreateCommand.php create mode 100755 tests/Feature/IssueCreateCommandTest.php diff --git a/app/Commands/IssueCreateCommand.php b/app/Commands/IssueCreateCommand.php new file mode 100644 index 0000000..7a0ba7c --- /dev/null +++ b/app/Commands/IssueCreateCommand.php @@ -0,0 +1,67 @@ +setCodingApi(); + + $data = []; + $data['Type'] = $this->option('type') ?? $this->choice( + '类型:', + ['DEFECT', 'REQUIREMENT', 'MISSION', 'EPIC', 'SUB_TASK'], + 0 + ); + $data['Name'] = $this->option('name') ?? $this->ask('标题:'); + $data['Priority'] = $this->option('priority') ?? $this->choice( + '优先级:', + ['0', '1', '2', '3'], + 0 + ); + + try { + $result = $codingIssue->create($this->codingToken, $this->codingProjectUri, $data); + } catch (\Exception $e) { + $this->error('Error: ' . $e->getMessage()); + return 1; + } + + $this->info('创建成功'); + $this->info("https://{$this->codingTeamDomain}.coding.net/p/{$this->codingProjectUri}" . + "/all/issues/${result['Code']}"); + + return 0; + } +} diff --git a/tests/Feature/IssueCreateCommandTest.php b/tests/Feature/IssueCreateCommandTest.php new file mode 100755 index 0000000..fb43c9f --- /dev/null +++ b/tests/Feature/IssueCreateCommandTest.php @@ -0,0 +1,59 @@ +codingToken = $this->faker->md5; + config(['coding.token' => $this->codingToken]); + $this->codingTeamDomain = $this->faker->domainWord; + config(['coding.team_domain' => $this->codingTeamDomain]); + $this->codingProjectUri = $this->faker->slug; + config(['coding.project_uri' => $this->codingProjectUri]); + } + + public function testCreateSuccess() + { + $mock = \Mockery::mock(Issue::class, [])->makePartial(); + $this->instance(Issue::class, $mock); + + $mock->shouldReceive('create')->times(1)->andReturn(json_decode( + file_get_contents($this->dataDir . 'coding/' . 'CreateIssueResponse.json'), + true + )['Response']['Issue']); + + $this->artisan('issue:create') + ->expectsQuestion('类型:', 'REQUIREMENT') + ->expectsQuestion('标题:', $this->faker->title) + ->expectsOutput('创建成功') + ->expectsOutput("https://$this->codingTeamDomain.coding.net/p/$this->codingProjectUri/all/issues/2742") + ->assertExitCode(0); + } + + public function testCreateFailed() + { + $mock = \Mockery::mock(Issue::class, [])->makePartial(); + $this->instance(Issue::class, $mock); + + $mock->shouldReceive('create')->times(1)->andThrow(\Exception::class, json_decode( + file_get_contents($this->dataDir . 'coding/' . 'CreateIssueFailedResponse.json'), + true + )['Response']['Error']['Message']); + + $this->artisan('issue:create') + ->expectsQuestion('类型:', 'REQUIREMENT') + ->expectsQuestion('标题:', $this->faker->title) + ->expectsOutput('Error: issue_custom_field_required') + ->assertExitCode(1); + } +}