Test Failed
Push — master ( 6a2cd1...15b1c9 )
by Mr
01:29
created

CouchDbMigrationAdapter::buildUri()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 2
1
<?php
2
3
namespace Daikon\CouchDb\Migration;
4
5
use Daikon\CouchDb\Connector\CouchDbConnector;
6
use Daikon\Dbal\Connector\ConnectorInterface;
7
use Daikon\Dbal\Exception\MigrationException;
8
use Daikon\Dbal\Migration\MigrationAdapterInterface;
9
use Daikon\Dbal\Migration\MigrationList;
10
use GuzzleHttp\Exception\RequestException;
11
use GuzzleHttp\Psr7\Request;
12
13
final class CouchDbMigrationAdapter implements MigrationAdapterInterface
14
{
15
    private $connector;
16
17
    private $settings;
18
19
    public function __construct(CouchDbConnector $connector, array $settings = [])
20
    {
21
        $this->connector = $connector;
22
        $this->settings = $settings;
23
    }
24
25
    public function read(string $identifier): MigrationList
26
    {
27
        try {
28
            $response = $this->request($identifier, 'GET');
29
            $rawResponse = json_decode($response->getBody(), true);
30
        } catch (RequestException $error) {
31
            if ($error->hasResponse() && $error->getResponse()->getStatusCode() === 404) {
32
                return new MigrationList;
33
            }
34
            throw $error;
35
        }
36
        return $this->createMigrationList($rawResponse['migrations']);
37
    }
38
39
    public function write(string $identifier, MigrationList $executedMigrations): void
40
    {
41
        $body = [
42
            'target' => $identifier,
43
            'migrations' => $executedMigrations->toArray()
44
        ];
45
46
        if ($revision = $this->getCurrentRevision($identifier)) {
47
            $body['_rev'] = $revision;
48
        }
49
50
        $response = $this->request($identifier, 'PUT', $body);
51
        $rawResponse = json_decode($response->getBody(), true);
52
53
        if (!isset($rawResponse['ok']) || !isset($rawResponse['rev'])) {
54
            throw new MigrationException('Failed to write migration data for '.$identifier);
55
        }
56
    }
57
58
    public function getConnector(): ConnectorInterface
59
    {
60
        return $this->connector;
61
    }
62
63
    private function createMigrationList(array $migrationData)
64
    {
65
        $migrations = [];
66
        foreach ($migrationData as $migration) {
67
            $migrationClass = $migration['@type'];
68
            /*
69
             * Explicitly not using a service locator to make migration classes here because
70
             * it could enable unusual behaviour.
71
             */
72
            $migrations[] = new $migrationClass(new \DateTimeImmutable($migration['executedAt']));
73
        }
74
        return new MigrationList($migrations);
75
    }
76
77
    private function getCurrentRevision(string $identifier): ?string
78
    {
79
        try {
80
            $response = $this->request($identifier, 'HEAD');
81
            $revision = trim(current($response->getHeader('ETag')), '"');
82
        } catch (RequestException $error) {
83
            if (!$error->hasResponse() || $error->getResponse()->getStatusCode() !== 404) {
84
                throw $error;
85
            }
86
        }
87
88
        return $revision ?? null;
89
    }
90
91
    private function request(string $identifier, string $method, array $body = [], array $params = [])
92
    {
93
        $uri = $this->buildUri($identifier, $params);
94
95
        $request = empty($body)
96
            ? new Request($method, $uri)
97
            : new Request($method, $uri, [], json_encode($body));
98
99
        return $this->connector->getConnection()->send($request);
100
    }
101
102
    private function buildUri(string $identifier, array $params = [])
103
    {
104
        $settings = $this->connector->getSettings();
105
        $uri = sprintf('/%s/%s', $settings['database'], $identifier);
106
        if (!empty($params)) {
107
            $uri .= '?'.http_build_query($params);
108
        }
109
        return str_replace('//', '/', $uri);
110
    }
111
}
112