Skip to content

Commit

Permalink
service/settings: Fix job polling mechanism
Browse files Browse the repository at this point in the history
Instead of using the unauthorized internal job URL, which is returned by the API, parse the job uuid from the URL and use that ID for polling on the proper endpoint.

Ideally this is fixed in the API, but this is how it's been done in kamikaze so far as well, so let's first start here.
  • Loading branch information
tdgroot committed Apr 29, 2024
1 parent 2c7a821 commit 9235483
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 31 deletions.
9 changes: 9 additions & 0 deletions src/Defaults.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace Hypernode\Api;

class Defaults {
const HYPERNODE_API_URL = 'https://api.hypernode.com/';
}
4 changes: 1 addition & 3 deletions src/HypernodeClientFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@

class HypernodeClientFactory
{
public const HYPERNODE_API_URL = 'https://api.hypernode.com/';

public static function create(string $authToken, ?ClientInterface $httpClient = null): HypernodeClient
{
$httpHeaders = [
Expand All @@ -29,7 +27,7 @@ public static function create(string $authToken, ?ClientInterface $httpClient =
'Accept' => 'application/json',
'Content-Type' => 'application/json',
];
$httpClient = self::getHttpClient(self::HYPERNODE_API_URL, $httpHeaders, $httpClient);
$httpClient = self::getHttpClient(Defaults::HYPERNODE_API_URL, $httpHeaders, $httpClient);

$apiClient = new HttpMethodsClient(
$httpClient, Psr17FactoryDiscovery::findRequestFactory(), Psr17FactoryDiscovery::findStreamFactory()
Expand Down
58 changes: 42 additions & 16 deletions src/Resource/Logbook/Job.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Hypernode\Api\HypernodeClient;
use Hypernode\Api\Resource\AbstractResource;
use Hypernode\Api\Service\App;

/**
* @property-read string $result
Expand All @@ -15,17 +16,19 @@
*/
class Job extends AbstractResource
{
private string $url;
private string $id;
private string $appName;
private bool $exists = false;
private bool $running = false;
private bool $success = false;
private bool $complete = false;

protected HypernodeClient $client;

public function __construct(HypernodeClient $client, string $urlOrId, array $data = [])
public function __construct(HypernodeClient $client, string $appName, string $id, array $data = [])
{
$this->client = $client;
$this->url = $urlOrId;
$this->appName = $appName;
$this->id = $id;
$this->data = $data;
}

Expand All @@ -37,37 +40,60 @@ public function __construct(HypernodeClient $client, string $urlOrId, array $dat
*/
public function refresh()
{
$response = $this->client->api->get($this->url);
$url = sprintf(App::V1_APP_FLOWS_URL, $this->appName) . '?' . http_build_query(['tracker_uuid' => $this->id]);
$response = $this->client->api->get($url);
$this->data = $this->client->getJsonFromResponse($response);

if ($response->getStatusCode() === 404) {
if ($response->getStatusCode() === 404 || $this->data['count'] === 0) {
$this->data = [];
$this->exists = false;
$this->running = false;
return;
}

if ($response->getStatusCode() === 303) {
$this->data = [];
$this->exists = true;
$this->running = false;
$this->complete = true;
return;
$this->exists = true;

$result = $this->data['results'][0];
switch ($result['state']) {
case 'running':
$this->running = true;
break;
case 'success':
$this->success = true;
$this->running = false;
$this->complete = true;
break;
case 'reverted':
$this->running = false;
$this->complete = true;
break;
}

$this->client->maybeThrowApiExceptions($response);
}

$this->data = $this->client->getJsonFromResponse($response);
$this->exists = true;
$this->running = true;
public function id()
{
return $this->id;
}

public function exists(): bool
{
return $this->exists;
}

public function running(): bool
{
return $this->running;
}

public function completed(): bool
{
return $this->complete;
}
}

public function success(): bool
{
return $this->success;
}
}
9 changes: 8 additions & 1 deletion src/Service/Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@

namespace Hypernode\Api\Service;

use Hypernode\Api\Defaults;
use Hypernode\Api\Resource\Logbook\Job;

class Settings extends AbstractService
{
public const JOB_URL_REGEX = '#' . Defaults::HYPERNODE_API_URL. 'logbook/v1/jobs/(.*)/#';

public function set(string $app, string $key, string $value): ?Job
{
return $this->setBatch($app, [$key => $value]);
Expand All @@ -21,7 +24,11 @@ public function setBatch(string $app, array $settings): ?Job
$this->client->maybeThrowApiExceptions($response);

if ($response->getStatusCode() === 202) {
$job = new Job($this->client, $response->getHeaderLine('Location'));
$location = $response->getHeaderLine('Location');
if (!preg_match(self::JOB_URL_REGEX, $location, $matches)) {
return null;
}
$job = new Job($this->client, $app, $matches[1]);
return $job;
}

Expand Down
101 changes: 92 additions & 9 deletions tests/unit/Resource/Logbook/JobTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ class JobTest extends HypernodeClientTestCase
protected function setUp(): void
{
parent::setUp();
$this->jobUrl = "https://api.hypernode.com/logbook/v1/jobs/abcd/";
$this->job = new Job($this->client, $this->jobUrl);
$this->job = new Job($this->client, 'johndoe', 'abcd');
}

public function testIsInstanceOfAbstractResource()
Expand All @@ -29,17 +28,100 @@ public function testRefresh()
{
$this->responses->append(
new Response(404, [], json_encode([])),
new Response(200, [], json_encode(['count' => 0, 'results' => []])),
new Response(200, [], json_encode([
'result' => 'pending',
'flow_name' => 'update_node',
'app_name' => 'johndoe'
'count' => 1,
'results' => [
[
'uuid' => 'abcd',
'state' => NULL,
'name' => 'update_node'
]
]
])),
new Response(200, [], json_encode([
'result' => 'running',
'flow_name' => 'update_node',
'app_name' => 'johndoe'
'count' => 1,
'results' => [
[
'uuid' => 'abcd',
'state' => 'running',
'name' => 'update_node'
]
]
])),
new Response(200, [], json_encode([
'count' => 1,
'results' => [
[
'uuid' => 'abcd',
'state' => 'success',
'name' => 'update_node'
]
]
])),
);

$this->job->refresh();

$this->assertFalse($this->job->exists());
$this->assertFalse($this->job->completed());

$this->job->refresh();

$this->assertFalse($this->job->exists());
$this->assertFalse($this->job->completed());

$this->job->refresh();

$this->assertTrue($this->job->exists());
$this->assertFalse($this->job->completed());

$this->job->refresh();

$this->assertTrue($this->job->exists());
$this->assertFalse($this->job->completed());

$this->job->refresh();

$this->assertTrue($this->job->exists());
$this->assertTrue($this->job->completed());
$this->assertTrue($this->job->success());
}

public function testRefreshFailedJob()
{
$this->responses->append(
new Response(404, [], json_encode([])),
new Response(200, [], json_encode([
'count' => 1,
'results' => [
[
'uuid' => 'abcd',
'state' => NULL,
'name' => 'update_node'
]
]
])),
new Response(200, [], json_encode([
'count' => 1,
'results' => [
[
'uuid' => 'abcd',
'state' => 'running',
'name' => 'update_node'
]
]
])),
new Response(200, [], json_encode([
'count' => 1,
'results' => [
[
'uuid' => 'abcd',
'state' => 'reverted',
'name' => 'update_node'
]
]
])),
new Response(303, [], json_encode([])),
);

$this->job->refresh();
Expand All @@ -61,6 +143,7 @@ public function testRefresh()

$this->assertTrue($this->job->exists());
$this->assertTrue($this->job->completed());
$this->assertFalse($this->job->success());
}

public function testExistsReturnsFalseByDefault()
Expand Down
6 changes: 4 additions & 2 deletions tests/unit/Service/SettingsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,15 @@ public function testSetSingleSettingToDifferentValue()
{
$jobUrl = 'https://api.hypernode.com/logbook/v1/jobs/abcd/';
$this->responses->append(
new Response(202, ['Location: ' . $jobUrl], null),
new Response(202, ['Location' => $jobUrl], null),
);

$job = $this->client->settings->set('johndoe', 'php_version', '8.1');

$request = $this->responses->getLastRequest();

$this->assertNotNull($job);
$this->assertEquals('abcd', $job->id());
$this->assertEquals('PATCH', $request->getMethod());
$this->assertEquals('/v2/app/johndoe/', $request->getUri());
$this->assertJson((string)$request->getBody());
Expand All @@ -54,7 +55,7 @@ public function testSetMultipleSettings()
{
$jobUrl = 'https://api.hypernode.com/logbook/v1/jobs/abcd/';
$this->responses->append(
new Response(202, ['Location: ' . $jobUrl], null),
new Response(202, ['Location' => $jobUrl], null),
);

$job = $this->client->settings->setBatch(
Expand All @@ -68,6 +69,7 @@ public function testSetMultipleSettings()
$request = $this->responses->getLastRequest();

$this->assertNotNull($job);
$this->assertEquals('abcd', $job->id());
$this->assertEquals('PATCH', $request->getMethod());
$this->assertEquals('/v2/app/johndoe/', $request->getUri());
$this->assertJson((string)$request->getBody());
Expand Down

0 comments on commit 9235483

Please sign in to comment.