Skip to content

Commit 28a5688

Browse files
committed
Merge branch 'master' of github.com:ByteInternet/hypernode-api-php into ephemeral_apps
� Conflicts: � src/HypernodeClient.php � src/Service/App.php
2 parents 12d3fe9 + 60681c5 commit 28a5688

File tree

10 files changed

+323
-4
lines changed

10 files changed

+323
-4
lines changed

.github/workflows/test.yaml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Run tests for PR
2+
on:
3+
pull_request:
4+
push:
5+
branches:
6+
- master
7+
8+
jobs:
9+
code_quality:
10+
strategy:
11+
matrix:
12+
php_version: [7.4, 8.0, 8.1]
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: Checkout hypernode-deploy
16+
uses: actions/checkout@v3
17+
- name: Install PHP
18+
uses: shivammathur/setup-php@2.21.1
19+
with:
20+
php-version: ${{ matrix.php_version }}
21+
tools: composer:v2
22+
- name: Install dependencies
23+
run: composer update --prefer-dist --no-progress --no-suggest
24+
- name: Run PHP unit
25+
run: php vendor/bin/phpunit tests/unit

composer.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@
2121
"require": {
2222
"ext-curl": "*",
2323
"ext-json": "*",
24-
"symfony/polyfill-php80": "^1.0",
24+
"nesbot/carbon": "^2.0",
2525
"psr/http-client-implementation": "^1.0",
2626
"php-http/client-common": "^2.5",
27-
"php-http/discovery": "^1.14"
27+
"php-http/discovery": "^1.14",
28+
"symfony/polyfill-php80": "^1.0"
2829
},
2930
"require-dev": {
3031
"friendsofphp/php-cs-fixer": "^3.9",

src/HypernodeClient.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Hypernode\Api\Exception\HypernodeApiServerException;
1010
use Hypernode\Api\Service\App;
1111
use Hypernode\Api\Service\EphemeralApp;
12+
use Hypernode\Api\Service\Logbook;
1213
use Hypernode\Api\Service\Settings;
1314
use Psr\Http\Message\ResponseInterface;
1415

@@ -20,13 +21,15 @@ class HypernodeClient
2021
public App $app;
2122
public EphemeralApp $ephemeralApp;
2223
public Settings $settings;
24+
public Logbook $logbook;
2325

2426
public function __construct(HttpMethodsClientInterface $apiClient)
2527
{
2628
$this->api = $apiClient;
2729
$this->app = new App($this);
2830
$this->ephemeralApp = new EphemeralApp($this);
2931
$this->settings = new Settings($this);
32+
$this->logbook = new Logbook($this);
3033
}
3134

3235
public function getJsonFromResponse(ResponseInterface $response)

src/Resource/AbstractResource.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,22 @@
44

55
namespace Hypernode\Api\Resource;
66

7+
use Carbon\Carbon;
8+
79
class AbstractResource
810
{
911
protected array $data;
12+
protected array $dateAttributes = [];
1013

1114
public function __get(string $name)
1215
{
13-
return $this->data[$name] ?? null;
16+
$value = $this->data[$name] ?? null;
17+
18+
if (in_array($name, $this->dateAttributes)) {
19+
return Carbon::create($value);
20+
}
21+
22+
return $value;
1423
}
1524

1625
public function __isset($name)

src/Resource/Logbook/Flow.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Hypernode\Api\Resource\Logbook;
6+
7+
use Carbon\Carbon;
8+
use Hypernode\Api\Resource\AbstractResource;
9+
10+
/**
11+
* @property-read string $state
12+
* @property-read string $name
13+
* @property-read string $app_name
14+
* @property-read Carbon $created_at
15+
* @property-read Carbon $updated_at
16+
*/
17+
class Flow extends AbstractResource
18+
{
19+
public const STATE_SUCCESS = 'success';
20+
public const STATE_REVERTED = 'reverted';
21+
public const STATE_RUNNING = 'running';
22+
public const STATE_WAITING = 'waiting';
23+
24+
protected array $dateAttributes = [
25+
'created_at',
26+
'updated_at',
27+
];
28+
29+
public function __construct(array $data)
30+
{
31+
$this->data = $data;
32+
}
33+
34+
public function isReverted(): bool
35+
{
36+
return $this->state === self::STATE_REVERTED;
37+
}
38+
39+
public function isComplete(): bool
40+
{
41+
return $this->state === self::STATE_SUCCESS;
42+
}
43+
44+
public function isRunning(): bool
45+
{
46+
return $this->state === self::STATE_RUNNING;
47+
}
48+
49+
public function isWaiting(): bool
50+
{
51+
return empty($this->state) || $this->state === self::STATE_WAITING;
52+
}
53+
}

src/Service/App.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ class App extends AbstractService
99
public const V2_APP_DETAIL_URL = "/v2/app/%s/";
1010
public const V2_APP_CANCEL_URL = "/v2/app/%s/cancel/";
1111
public const V2_APP_EPHEMERAL_URL = "/v2/app/%s/ephemeral/";
12-
}
12+
public const V1_APP_FLOWS_URL = "/logbook/v1/logbooks/%s/flows/";
13+
}

src/Service/Logbook.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Hypernode\Api\Service;
6+
7+
use Hypernode\Api\Exception\HypernodeApiClientException;
8+
use Hypernode\Api\Exception\HypernodeApiServerException;
9+
use Hypernode\Api\Resource\Logbook\Flow;
10+
11+
class Logbook extends AbstractService
12+
{
13+
/**
14+
* Get jobs (complete and non-complete) from logbook for given Hypernode.
15+
*
16+
* @param string $app Name of the hypernode
17+
* @param int $amount
18+
* @return Flow[]
19+
* @throws HypernodeApiClientException
20+
* @throws HypernodeApiServerException
21+
*/
22+
public function getList(string $app, int $amount = 50): array
23+
{
24+
/** @var Flow[] $result */
25+
$result = [];
26+
27+
$url = sprintf(App::V1_APP_FLOWS_URL, $app);
28+
29+
$data = ['next' => $url];
30+
while (count($result) < $amount && $data['next']) {
31+
$response = $this->client->api->get($data['next']);
32+
$this->client->maybeThrowApiExceptions($response);
33+
$data = $this->client->getJsonFromResponse($response);
34+
foreach ($data['results'] as $flowData) {
35+
$result[] = new Flow($flowData);
36+
}
37+
}
38+
39+
return $result;
40+
}
41+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Hypernode\Api\Resource\Logbook;
6+
7+
use Carbon\Carbon;
8+
use PHPUnit\Framework\TestCase;
9+
10+
class FlowTest extends TestCase
11+
{
12+
public function testCreatedAtDate()
13+
{
14+
$flow = new Flow(['created_at' => '2022-09-12T09:00:00Z']);
15+
$this->assertInstanceOf(Carbon::class, $flow->created_at);
16+
}
17+
18+
public function testUpdatedAtDate()
19+
{
20+
$flow = new Flow(['updated_at' => '2022-09-12T09:00:00Z']);
21+
$this->assertInstanceOf(Carbon::class, $flow->updated_at);
22+
}
23+
24+
public function testIsRevertedReturnsTrue()
25+
{
26+
$flow = new Flow(['state' => 'reverted']);
27+
$this->assertTrue($flow->isReverted());
28+
}
29+
30+
public function testIsRevertedReturnsFalse()
31+
{
32+
$flow = new Flow(['state' => 'running']);
33+
$this->assertFalse($flow->isReverted());
34+
}
35+
36+
public function testIsCompleteReturnTrue()
37+
{
38+
$flow = new Flow(['state' => 'success']);
39+
$this->assertTrue($flow->isComplete());
40+
}
41+
42+
public function testIsCompleteReturnFalse()
43+
{
44+
$flow = new Flow(['state' => 'running']);
45+
$this->assertFalse($flow->isComplete());
46+
}
47+
48+
public function testIsRunningReturnsTrue()
49+
{
50+
$flow = new Flow(['state' => 'running']);
51+
$this->assertTrue($flow->isRunning());
52+
}
53+
54+
public function testIsRunningReturnsFalse()
55+
{
56+
$flow = new Flow(['state' => 'success']);
57+
$this->assertFalse($flow->isRunning());
58+
}
59+
60+
public function testIsWaitingReturnsTrueForEmptyState()
61+
{
62+
$flow = new Flow(['state' => null]);
63+
$this->assertTrue($flow->isWaiting());
64+
}
65+
66+
public function testIsWaitingReturnsTrue()
67+
{
68+
$flow = new Flow(['state' => 'waiting']);
69+
$this->assertTrue($flow->isWaiting());
70+
}
71+
72+
public function testIsWaitingReturnsFalse()
73+
{
74+
$flow = new Flow(['state' => 'running']);
75+
$this->assertFalse($flow->isWaiting());
76+
}
77+
}

tests/unit/Resource/Logbook/JobTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use GuzzleHttp\Psr7\Response;
88
use Hypernode\Api\HypernodeClientTestCase;
9+
use Hypernode\Api\Resource\AbstractResource;
910

1011
class JobTest extends HypernodeClientTestCase
1112
{
@@ -19,6 +20,11 @@ protected function setUp(): void
1920
$this->job = new Job($this->client, $this->jobUrl);
2021
}
2122

23+
public function testIsInstanceOfAbstractResource()
24+
{
25+
$this->assertInstanceOf(AbstractResource::class, $this->job);
26+
}
27+
2228
public function testRefresh()
2329
{
2430
$this->responses->append(

tests/unit/Service/LogbookTest.php

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Hypernode\Api\Service;
6+
7+
use GuzzleHttp\Psr7\Response;
8+
use Hypernode\Api\HypernodeClientTestCase;
9+
use Hypernode\Api\Resource\Logbook\Flow;
10+
11+
class LogbookTest extends HypernodeClientTestCase
12+
{
13+
public function testGetListFetchesLogbook()
14+
{
15+
$this->responses->append(
16+
new Response(200, [], json_encode([
17+
'count' => 2,
18+
'next' => null,
19+
'previous' => null,
20+
'results' => [
21+
[
22+
'uuid' => 'c130c04f-794e-4f55-b34b-eb8f60472224',
23+
'state' => 'running',
24+
'name' => 'update_node',
25+
'created_at' => '2022-09-12T12:19:15Z',
26+
'updated_at' => '2022-09-12T12:20:30Z',
27+
'logbook' => 'johndoe',
28+
],
29+
[
30+
'uuid' => 'e2f39684-3ce0-48d1-acda-13fa064709b5',
31+
'state' => 'success',
32+
'name' => 'create_backup',
33+
'created_at' => '2022-09-12T11:08:10Z',
34+
'updated_at' => '2022-09-12T11:08:27Z',
35+
'logbook' => 'johndoe',
36+
],
37+
]
38+
])),
39+
);
40+
41+
$flows = $this->client->logbook->getList('johndoe');
42+
[$flow1, $flow2] = $flows;
43+
44+
$this->assertCount(2, $flows);
45+
46+
$this->assertInstanceOf(Flow::class, $flow1);
47+
$this->assertEquals('running', $flow1->state);
48+
$this->assertEquals('update_node', $flow1->name);
49+
50+
$this->assertInstanceOf(Flow::class, $flow2);
51+
$this->assertEquals('success', $flow2->state);
52+
$this->assertEquals('create_backup', $flow2->name);
53+
}
54+
55+
public function testGetListPagesThroughLogbook()
56+
{
57+
$this->responses->append(
58+
new Response(200, [], json_encode([
59+
'count' => 1,
60+
'next' => 'https://api.hypernode.com/logbook/v1/logbooks/johndoe/flows/',
61+
'previous' => null,
62+
'results' => [
63+
[
64+
'uuid' => 'c130c04f-794e-4f55-b34b-eb8f60472224',
65+
'state' => 'running',
66+
'name' => 'update_node',
67+
'created_at' => '2022-09-12T12:19:15Z',
68+
'updated_at' => '2022-09-12T12:20:30Z',
69+
'logbook' => 'johndoe',
70+
],
71+
]
72+
])),
73+
new Response(200, [], json_encode([
74+
'count' => 1,
75+
'next' => null,
76+
'previous' => null,
77+
'results' => [
78+
[
79+
'uuid' => 'e2f39684-3ce0-48d1-acda-13fa064709b5',
80+
'state' => 'success',
81+
'name' => 'create_backup',
82+
'created_at' => '2022-09-12T11:08:10Z',
83+
'updated_at' => '2022-09-12T11:08:27Z',
84+
'logbook' => 'johndoe',
85+
],
86+
]
87+
])),
88+
);
89+
90+
$flows = $this->client->logbook->getList('johndoe');
91+
[$flow1, $flow2] = $flows;
92+
93+
$this->assertCount(2, $flows);
94+
95+
$this->assertInstanceOf(Flow::class, $flow1);
96+
$this->assertEquals('running', $flow1->state);
97+
$this->assertEquals('update_node', $flow1->name);
98+
99+
$this->assertInstanceOf(Flow::class, $flow2);
100+
$this->assertEquals('success', $flow2->state);
101+
$this->assertEquals('create_backup', $flow2->name);
102+
}
103+
}

0 commit comments

Comments
 (0)