diff --git a/README.md b/README.md index 294f5ba..04a0411 100644 --- a/README.md +++ b/README.md @@ -13,37 +13,38 @@ composer require bbaga/buildkite-php ## Usage -[Direct API calls](#direct-api-calls) -* [Organizations API](#organizations-api) - * [List the ](#list-the-organizations) - * [Get a specific organization](#get-a-specific-organization) -* [Pipelines API](#pipelines-api) - * [List pipelines in an organization](#list-pipelines-in-an-organizations) - * [Get a specific pipeline](#get-a-specific-pipeline) - * [Create a pipeline](#create-a-pipeline) - * [Update a pipeline](#update-a-pipeline) - * [Delete a pipeline](#delete-a-pipelne) -* [Builds API](#builds-api) - * [List all builds across all the organizations](#list-all-builds-across-all-the-organizations) - * [Get a specific build](#get-a-specific-build) - * [Get builds in an organization](#get-builds-in-an-organization) - * [Get builds for a pipeline](#get-builds-for-a-pipeline) - * [Create new build](#create-a-new-build) - * [Cancel a running build](#cancel-a-running-build) - * [Restarting a build](#restarting-a-build) -* [Jobs API](#jobs-api) -* [Artifacts API](#artifacts-api) -* [Agents API](#agents-api) -* [Annotations API](#annotations-api) -* [Users API](#users-api) -* [Emojis API](#emojis-api) +* [Interacting with Buildkite's REST API](#interacting-with-buildkites-rest-api) + * [Example of traversing through resources](#example-of-traversing-through-resources) + * [Getting straight to the point](#getting-straight-to-the-point) +* [Direct API calls](#direct-api-calls) + * [Organizations API](#organizations-api) + * [List the ](#list-the-organizations) + * [Get a specific organization](#get-a-specific-organization) + * [Pipelines API](#pipelines-api) + * [List pipelines in an organization](#list-pipelines-in-an-organizations) + * [Get a specific pipeline](#get-a-specific-pipeline) + * [Create a pipeline](#create-a-pipeline) + * [Update a pipeline](#update-a-pipeline) + * [Delete a pipeline](#delete-a-pipelne) + * [Builds API](#builds-api) + * [List all builds across all the organizations](#list-all-builds-across-all-the-organizations) + * [Get a specific build](#get-a-specific-build) + * [Get builds in an organization](#get-builds-in-an-organization) + * [Get builds for a pipeline](#get-builds-for-a-pipeline) + * [Create new build](#create-a-new-build) + * [Cancel a running build](#cancel-a-running-build) + * [Restarting a build](#restarting-a-build) + * [Jobs API](#jobs-api) + * [Artifacts API](#artifacts-api) + * [Agents API](#agents-api) + * [Annotations API](#annotations-api) + * [Users API](#users-api) + * [Emojis API](#emojis-api) ### Setting up the RestApi object ```php use bbaga\BuildkiteApi\Api\RestApi; -require __DIR__.'/vendor/autoload.php'; - /** @var \BuildkiteApi\Api\HttpClientInterface $client */ $client = new MyHttpClient(); @@ -53,6 +54,72 @@ $api = new RestApi($client, 'MY_BUILDKITE_API_TOKEN'); `\BuildkiteApi\Api\HttpClientInterface` implementation is available in the [`bbaga/buildkite-php-guzzle-client`](https://github.com/bbaga/buildkite-php-guzzle-client) package. `\BuildkiteApi\Api\HttpClientInterface` is available in the [`bbaga/buildkite-php-http-interface`](https://github.com/bbaga/buildkite-php-http-interface) package. +### Interacting with Buildkite's REST API + +#### Example of traversing through resources +```php +use bbaga\BuildkiteApi\Api\GuzzleClient; +use bbaga\BuildkiteApi\Api\Rest\Fluent; +use bbaga\BuildkiteApi\Api\RestApi; + +$client = new GuzzleClient(); +$api = new RestApi($client, 'MY_BUILDKITE_API_TOKEN'); + +/** Getting all the organizations that are visible with the TOKEN */ +/** @var Fluent\Organization[] $organizations */ +$organizations = (new Fluent\Organizations($api))->get(); + +/** @var Fluent\Organization $organization */ +$organization = $organizations[0]; + +/** @var Fluent\Pipeline $pipelines */ +$pipelines = $organization->getPipelines(); + +/** @var Fluent\Pipeline $pipeline */ +$pipeline = $pipelines[0]; + +/** @var Fluent\Build[] $builds */ +$builds = $pipeline->getBuilds(); + +/** @var Fluent\Build $build */ +$build = $builds[0]; + +/** @var Fluent\Job[] $jobs */ +$jobs = $build->getJobs(); + +/** @var Fluent\Emoji[] $emojis */ +$emojis = $organizations[0]->getEmojis(); + +/** @var Fluent\Agent[] $emojis */ +$agents = $organizations[0]->getAgents(); +``` + +#### Getting straight to the point + +Fetching data for a specific build without traversing through the hierarchy. + +```php +use bbaga\BuildkiteApi\Api\GuzzleClient; +use bbaga\BuildkiteApi\Api\Rest\Fluent; +use bbaga\BuildkiteApi\Api\RestApi; + +$client = new GuzzleClient(); +$api = new RestApi($client, 'MY_BUILDKITE_API_TOKEN'); + +/** + * Builds are identified by the follwoing three values + */ +$organizationSlug = 'my-org'; +$pipelineSlug = 'my-pipeline'; +$buildNumber = 23; + +$organization = new Fluent\Organization($api, ['slug' => $organizationSlug]); +$pipeline = new Fluent\Pipeline($api, $organization, ['slug' => $pipelineSlug]); +$build = new Fluent\Build($api, $organization, ['number' => $buildNumber, 'pipeline' => $pipeline]); + +$build->fetch()->getJobs(); +``` + ### Direct API calls ### Organizations API diff --git a/src/Api/Rest/Fluent/Build.php b/src/Api/Rest/Fluent/Build.php index 715a6ad..0b861c6 100644 --- a/src/Api/Rest/Fluent/Build.php +++ b/src/Api/Rest/Fluent/Build.php @@ -6,6 +6,7 @@ use bbaga\BuildkiteApi\Api\RestApiInterface; use function is_array; +use function is_int; final class Build { @@ -129,6 +130,10 @@ public function __construct(RestApiInterface $api, Organization $organization, a $this->api = $api; $this->organization = $organization; + if (!isset($map['number']) || !is_int($map['number'])) { + throw new \InvalidArgumentException('The "number" must be an integer representing the build number'); + } + if (!isset($map['pipeline']) || (!$map['pipeline'] instanceof Pipeline && !is_array($map['pipeline']))) { throw new \InvalidArgumentException('The "pipeline" must be an array or an instance of ' . Pipeline::class); } @@ -367,19 +372,6 @@ public function getArtifacts(): array return $artifacts; } - public function create(array $data): self - { - $result = $this->api->build()->create( - $this->getOrganizationSlug(), - $this->getPipelineSlug(), - $data - ); - - $this->populate($result); - - return $this; - } - public function cancel(): self { $result = $this->api->build()->cancel( diff --git a/src/Api/Rest/Fluent/Organization.php b/src/Api/Rest/Fluent/Organization.php index 37222d3..235b907 100644 --- a/src/Api/Rest/Fluent/Organization.php +++ b/src/Api/Rest/Fluent/Organization.php @@ -149,6 +149,16 @@ public function fetch(): self return $this; } + public function createPipeline(array $data): Pipeline + { + $result = $this->api->pipeline()->create( + $this->getSlug(), + $data + ); + + return new Pipeline($this->api, $this, $result); + } + /** * @return Pipeline[] */ diff --git a/src/Api/Rest/Fluent/Pipeline.php b/src/Api/Rest/Fluent/Pipeline.php index 20aced6..3e005b3 100644 --- a/src/Api/Rest/Fluent/Pipeline.php +++ b/src/Api/Rest/Fluent/Pipeline.php @@ -153,6 +153,10 @@ public function __construct(RestApiInterface $api, Organization $organization, a $this->api = $api; $this->organization = $organization; + if (!isset($map['slug']) || !is_string($map['slug'])) { + throw new \InvalidArgumentException('The "slug" (representing the pipeline\'s slug) must be a string value'); + } + $this->populate($map); } @@ -372,6 +376,17 @@ public function getOrganization(): Organization return $this->organization; } + public function createBuild(array $data): Build + { + $result = $this->api->build()->create( + $this->getOrganization()->getSlug(), + $this->getSlug(), + $data + ); + + return new Build($this->api, $this->getOrganization(), $result); + } + /** * @return Build[] */ @@ -395,18 +410,6 @@ public function getBuilds(array $queryParameters = []): array return $list; } - public function create(array $data): self - { - $result = $this->api->pipeline()->create( - $this->organization->getSlug(), - $data - ); - - $this->populate($result); - - return $this; - } - public function update(array $data): self { $result = $this->api->pipeline()->update( diff --git a/tests/Unit/Api/Fluent/BuildTest.php b/tests/Unit/Api/Fluent/BuildTest.php index a46d19d..ece268c 100644 --- a/tests/Unit/Api/Fluent/BuildTest.php +++ b/tests/Unit/Api/Fluent/BuildTest.php @@ -24,7 +24,7 @@ final class BuildTest extends TestCase public function buildDataProvider(): array { $data = [ - 'number' => '12', + 'number' => 12, 'id' => '123fr4wsdfrew', 'url' => 'url', 'web_url' => 'web-url', diff --git a/tests/Unit/Api/Fluent/OrganizationTest.php b/tests/Unit/Api/Fluent/OrganizationTest.php index d1b4ccf..ca49f1a 100644 --- a/tests/Unit/Api/Fluent/OrganizationTest.php +++ b/tests/Unit/Api/Fluent/OrganizationTest.php @@ -65,6 +65,24 @@ public function testFetch(array $orgData): void $this->assertEquals($orgData['created_at'], $org->getCreatedAt()); } + public function testCreatePipeline(): void + { + $pipelineData = ['name' => 'My Pipeline']; + $restApi = $this->prophesize(RestApiInterface::class); + $organization = new Organization($restApi->reveal(), ['slug' => 'my-org']); + + $pipelineApi = $this->prophesize(PipelineInterface::class); + $pipelineApi->create( + Argument::exact($organization->getSlug()), + Argument::exact($pipelineData) + )->willReturn(['slug' => 'my-pipeline']) + ->shouldBeCalled(); + + $restApi->pipeline()->willReturn($pipelineApi->reveal()); + + $organization->createPipeline($pipelineData); + } + public function testGetPipelines(): void { $orgSlug = 'my-org'; @@ -72,7 +90,7 @@ public function testGetPipelines(): void $restApi = $this->prophesize(RestApiInterface::class); $pipelineApi = $this->prophesize(PipelineInterface::class); - $pipelineApi->list(Argument::exact($orgSlug), Argument::any())->willReturn([[]]); + $pipelineApi->list(Argument::exact($orgSlug), Argument::any())->willReturn([['slug' => 'my-pipeline']]); $restApi->pipeline()->willReturn($pipelineApi->reveal()); $org = new Organization($restApi->reveal(), ['slug' => $orgSlug]); diff --git a/tests/Unit/Api/Fluent/PipelineTest.php b/tests/Unit/Api/Fluent/PipelineTest.php index 1e04c0c..39e61b7 100644 --- a/tests/Unit/Api/Fluent/PipelineTest.php +++ b/tests/Unit/Api/Fluent/PipelineTest.php @@ -119,7 +119,7 @@ public function testGetBuilds(): void Argument::exact($orgSlug), Argument::exact($pipelineSlug), Argument::any() - )->willReturn([[]]); + )->willReturn([['number' => 1]]); $restApi->build()->willReturn($buildApi->reveal()); @@ -134,30 +134,30 @@ public function testGetBuilds(): void $this->assertSame($pipeline, $builds[0]->getPipeline()); } - public function testCreate(): void + public function testCreateBuild(): void { - $pipelineData = ['name' => 'My Pipeline']; - $organization = new Organization( - $this->prophesize(RestApiInterface::class)->reveal(), - ['slug' => 'my-org'] - ); + $orgSlug = 'my-org'; + $pipelineSlug = 'my-pipeline'; $restApi = $this->prophesize(RestApiInterface::class); - $pipelineApi = $this->prophesize(PipelineInterface::class); - $pipelineApi->create( - Argument::exact($organization->getSlug()), - Argument::exact($pipelineData) - )->willReturn([]) - ->shouldBeCalled(); - - $restApi->pipeline()->willReturn($pipelineApi->reveal()); + $buildApi = $this->prophesize(BuildInterface::class); + $restApi->build()->willReturn($buildApi->reveal()); $restApiMock = $restApi->reveal(); $pipeline = new Pipeline( $restApiMock, - $organization + new Organization($restApiMock, ['slug' => $orgSlug]), + ['slug' => $pipelineSlug] ); - $pipeline->create($pipelineData); + + $buildApi->create( + Argument::exact($orgSlug), + Argument::exact($pipelineSlug), + Argument::any() + )->willReturn(['number' => 1, 'pipeline' => $pipeline]); + + $build = $pipeline->createBuild([]); + $this->assertSame($pipeline, $build->getPipeline()); } public function testUpdate(): void