Passed
Pull Request — master (#13)
by Matthew
01:38
created

Fetcher   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 165
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 52
c 1
b 0
f 0
dl 0
loc 165
rs 10
wmc 22

11 Methods

Rating   Name   Duplication   Size   Complexity  
A salsifyRequest() 0 25 5
A createChannelRunUrl() 0 3 1
A __construct() 0 10 3
A channelUrl() 0 3 1
A getExportUrl() 0 3 1
A channelRunsBaseUrl() 0 3 1
A startExportRun() 0 7 2
A channelRunUrl() 0 6 2
A checkExportUrl() 0 9 3
A waitForExportRunToComplete() 0 8 2
A apiUrlSuffix() 0 3 1
1
<?php
2
3
namespace Dynamic\Salsify\Model;
4
5
use Exception;
6
use SilverStripe\Core\Config\Configurable;
7
use SilverStripe\Core\Extensible;
8
use SilverStripe\Core\Injector\Injectable;
9
10
/**
11
 * Class Importer
12
 * @package Dynamic\Salsify\Model
13
 *
14
 * Based off https://github.com/XinV/salsify-php-api/blob/master/lib/Salsify/API.php
15
 *
16
 * @mixin Configurable
17
 * @mixin Extensible
18
 * @mixin Injectable
19
 */
20
class Fetcher extends Service
21
{
22
    /**
23
     * @var string
24
     */
25
    const API_BASE_URL = 'https://app.salsify.com/api/';
26
27
    /**
28
     * @var string
29
     */
30
    protected $channelRunID;
31
32
    /**
33
     * @var string
34
     */
35
    protected $channelRunDataUrl;
36
37
    /**
38
     * Importer constructor.
39
     * @param string $importerKey
40
     * @throws \Exception
41
     */
42
    public function __construct($importerKey)
43
    {
44
        parent::__construct($importerKey);
45
46
        if (!$this->config()->get('apiKey')) {
47
            throw new Exception('An API key needs to be provided');
48
        }
49
50
        if (!$this->config()->get('channel')) {
51
            throw new Exception('A fetcher needs a channel');
52
        }
53
    }
54
55
    /**
56
     * @return string
57
     */
58
    private function channelUrl()
59
    {
60
        return self::API_BASE_URL . 'channels/' . $this->config()->get('channel');
61
    }
62
63
    /**
64
     * @return string
65
     */
66
    private function channelRunsBaseUrl()
67
    {
68
        return $this->channelUrl() . '/runs';
69
    }
70
71
    /**
72
     * @return string
73
     */
74
    private function createChannelRunUrl()
75
    {
76
        return $this->channelRunsBaseUrl();
77
    }
78
79
    /**
80
     * @return string
81
     */
82
    private function channelRunUrl()
83
    {
84
        if ($this->config()->get('useLatest')) {
85
            return $this->channelRunsBaseUrl() . '/latest';
86
        }
87
        return $this->channelRunsBaseUrl() . '/' . $this->channelRunID;
88
    }
89
90
    /**
91
     * @return string
92
     * @throws \Exception
93
     */
94
    private function apiUrlSuffix()
95
    {
96
        return '?format=json&auth_token=' . $this->config()->get('apiKey');
97
    }
98
99
    /**
100
     * @param string $url
101
     * @param string $method
102
     * @param string|null $postBody
103
     * @return array|string
104
     *
105
     * @throws \Exception
106
     */
107
    private function salsifyRequest($url, $method = 'GET', $postBody = null)
108
    {
109
        $defaultCurlOptions = array(
110
            CURLOPT_URL => $url . $this->apiUrlSuffix(),
111
            CURLOPT_CUSTOMREQUEST => $method,
112
            CURLOPT_HEADER => false,
113
            CURLOPT_RETURNTRANSFER => true,
114
            CURLOPT_HTTPHEADER => array('Content-Type: application/json'),
115
            // seemed reasonable settings
116
            CURLOPT_TIMEOUT => $this->config()->get('timeout'),
117
            CURLOPT_FRESH_CONNECT => true,
118
            CURLOPT_FORBID_REUSE => true,
119
        );
120
        if ($method === 'POST' && is_array($postBody)) {
0 ignored issues
show
introduced by
The condition is_array($postBody) is always false.
Loading history...
121
            $postBody = json_encode($postBody);
0 ignored issues
show
Unused Code introduced by
The assignment to $postBody is dead and can be removed.
Loading history...
122
        }
123
        $ch = curl_init($url);
124
        curl_setopt_array($ch, $defaultCurlOptions);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_setopt_array() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

124
        curl_setopt_array(/** @scrutinizer ignore-type */ $ch, $defaultCurlOptions);
Loading history...
125
        $response = curl_exec($ch);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_exec() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

125
        $response = curl_exec(/** @scrutinizer ignore-type */ $ch);
Loading history...
126
        $httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_getinfo() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

126
        $httpStatus = curl_getinfo(/** @scrutinizer ignore-type */ $ch, CURLINFO_HTTP_CODE);
Loading history...
Unused Code introduced by
The assignment to $httpStatus is dead and can be removed.
Loading history...
127
        curl_close($ch);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_close() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

127
        curl_close(/** @scrutinizer ignore-type */ $ch);
Loading history...
128
        if ($response && !empty($response)) {
129
            $response = json_decode($response, true);
0 ignored issues
show
Bug introduced by
It seems like $response can also be of type true; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

129
            $response = json_decode(/** @scrutinizer ignore-type */ $response, true);
Loading history...
130
        }
131
        return $response;
132
    }
133
134
    /**
135
     * @return $this
136
     * @throws \Exception
137
     */
138
    public function startExportRun()
139
    {
140
        if (!$this->config()->get('useLatest')) {
141
            $response = $this->salsifyRequest($this->createChannelRunUrl(), 'POST');
142
            $this->channelRunID = $response['id'];
143
        }
144
        return $this;
145
    }
146
147
    /**
148
     * @throws Exception
149
     */
150
    private function checkExportUrl()
151
    {
152
        $exportRun = $this->salsifyRequest($this->channelRunUrl());
153
        $status = $exportRun['status'];
154
        if ($status === 'completed') {
155
            $this->channelRunDataUrl = $exportRun['product_export_url'];
156
        } elseif ($status === 'failed') {
157
            // this would be an internal error in Salsify
158
            throw new Exception('Salsify failed to produce an export.');
159
        }
160
    }
161
162
    /**
163
     * waits until salsify is done preparing the given export, and returns the URL when done.
164
     * Throws an exception if anything funky occurs.
165
     *
166
     * @return $this
167
     * @throws Exception
168
     */
169
    public function waitForExportRunToComplete()
170
    {
171
        $this->channelRunDataUrl = null;
172
        do {
173
            $this->checkExportUrl();
174
            sleep(5);
175
        } while (!$this->channelRunDataUrl);
176
        return $this;
177
    }
178
179
    /**
180
     * @return string
181
     */
182
    public function getExportUrl()
183
    {
184
        return $this->channelRunDataUrl;
185
    }
186
}
187