Completed
Push — master ( 4f056b...dcf641 )
by Robbie
23s queued 11s
created

ResourceFieldPopulator::doFieldRequest()   B

Complexity

Conditions 7
Paths 4

Size

Total Lines 27
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 15
nc 4
nop 1
dl 0
loc 27
rs 8.8333
c 0
b 0
f 0
1
<?php
2
namespace SilverStripe\CKANRegistry\Service;
3
4
use GuzzleHttp\Client;
5
use GuzzleHttp\Psr7\Request;
6
use RuntimeException;
7
use SilverStripe\CKANRegistry\Model\Resource;
8
use SilverStripe\CKANRegistry\Model\ResourceField;
9
use SilverStripe\Core\Extensible;
10
use SilverStripe\Core\Injector\Injectable;
11
12
/**
13
 * This service will take a CKAN Resource and populate its Fields `has_many` relationship from the CKAN API
14
 */
15
class ResourceFieldPopulator implements ResourceFieldPopulatorInterface
16
{
17
    use Extensible;
18
    use Injectable;
19
20
    private static $dependencies = [
0 ignored issues
show
introduced by
The private property $dependencies is not used, and could be removed.
Loading history...
21
        'GuzzleClient' => '%$' . Client::class,
22
    ];
23
24
    /**
25
     * @var Client
26
     */
27
    protected $guzzleClient;
28
29
    public function populateFields(Resource $resource)
30
    {
31
        if (!$resource->Endpoint && !$resource->Identifier) {
32
            throw new RuntimeException('Could not fetch fields for a resource that is not fully configured');
33
        }
34
35
        $fieldSpecs = $this->doFieldRequest($resource);
36
37
        $newFields = [];
38
        $fields = $resource->Fields();
39
40
        foreach ($fieldSpecs as $fieldSpec) {
41
            $id = $fieldSpec['id'];
42
43
            // Skip fields that may already exist
44
            if ($fields->find('Name', $id)) {
45
                continue;
46
            }
47
48
            // Create a new field
49
            $newFields[] = $field = ResourceField::create();
50
            $field->Name = $id;
51
            // Attempt to parse a readable name
52
            $field->ReadableName = $this->parseName($id);
53
            $field->Type = $fieldSpec['type'];
54
        }
55
56
        // Add our new fields
57
        $fields->addMany($newFields);
58
    }
59
60
    /**
61
     * Perform a request to the CKAN endpoint provided by the given resource to fetch field definitons
62
     *
63
     * @param Resource $resource
64
     * @return array
65
     * @throws \GuzzleHttp\Exception\GuzzleException|RuntimeException
66
     */
67
    protected function doFieldRequest(Resource $resource)
68
    {
69
        $endpoint = sprintf(
70
            '%s/api/3/action/datastore_search?id=%s',
71
            trim($resource->Endpoint, '/'),
72
            $resource->Identifier
73
        );
74
75
        $request = new Request('GET', $endpoint);
76
        $response = $this->getGuzzleClient()->send($request, $this->getClientOptions());
77
78
        $statusCode = $response->getStatusCode();
79
        if ($statusCode < 200 || $statusCode >= 300) {
80
            throw new RuntimeException('CKAN API is not available. Error code ' . $statusCode);
81
        }
82
83
        if (!count(preg_grep('#application/json#', $response->getHeader('Content-Type')))) {
84
            throw new RuntimeException('CKAN API returns an invalid response: Content-Type is not JSON');
85
        }
86
87
        $responseBody = json_decode($response->getBody()->getContents(), true);
88
89
        if (!$responseBody || !isset($responseBody['success']) || !$responseBody['success']) {
90
            throw new RuntimeException('CKAN API returns an invalid response: Responded as invalid');
91
        }
92
93
        return $responseBody['result']['fields'];
94
    }
95
96
    /**
97
     * Parse given column ID for a more readable version
98
     *
99
     * @param string $id
100
     * @return string
101
     */
102
    protected function parseName($id)
103
    {
104
        // Parse "camelCase" to "space case"
105
        $name = strtolower(preg_replace('/(?<=[a-z\d])([A-Z])/', ' \1', $id));
106
107
        // Swap out certain characters with spaces
108
        $name = str_replace(['_', '-'], ' ', $name);
109
110
        return ucfirst($name);
111
    }
112
113
    /**
114
     * @return Client
115
     */
116
    protected function getGuzzleClient()
117
    {
118
        return $this->guzzleClient;
119
    }
120
121
    /**
122
     * @param Client $guzzleClient
123
     * @return $this
124
     */
125
    public function setGuzzleClient(Client $guzzleClient)
126
    {
127
        $this->guzzleClient = $guzzleClient;
128
        return $this;
129
    }
130
131
    /**
132
     * Get Guzzle client options
133
     *
134
     * @return array
135
     */
136
    protected function getClientOptions()
137
    {
138
        $options = [
139
            'http_errors' => false,
140
        ];
141
        $this->extend('updateClientOptions', $options);
142
        return $options;
143
    }
144
}
145