Completed
Pull Request — master (#35)
by
unknown
03:39
created

Client::callApi()   C

Complexity

Conditions 9
Paths 48

Size

Total Lines 69
Code Lines 38

Duplication

Lines 8
Ratio 11.59 %

Code Coverage

Tests 5
CRAP Score 62.045

Importance

Changes 0
Metric Value
dl 8
loc 69
ccs 5
cts 38
cp 0.1316
rs 6.2192
c 0
b 0
f 0
cc 9
eloc 38
nc 48
nop 4
crap 62.045

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Onesky\Api;
4
5
/**
6
 * Onesky API wrapper PHP5 library
7
 */
8
9
/**
10
 * @method string projectGroups(string $action, array $arrayWithProjectGroupId) Possible actions: list, show, create, delete, languages
11
 * @method string project(string $action, array $arrayWithProjectIdOrProjectGroupId = null) Possible actions: list, show, create, delete, languages
12
 * @method string files(string $action, array $arrayWithProjectId) Possible actions: list, upload, delete
13
 * @method string translations(string $action, array $arrayWithProjectId) Possible actions: export, status
14
 * @method string importTasks(string $action, array $arrayWithProjectIdAndOrImportId) Possible actions: list, show
15
 * @method string quotations(string $action, array $arrayWithProjectId) Possible actions: show
16
 * @method string orders(string $action, array $arrayWithProjectIdAndOrOrderId) Possible actions: list, show, create
17
 * @method string locales(string $action) Possible actions: list
18
 * @method string projectTypes(string $action) Possible actions: list
19
 */
20
21
class Client
22
{
23
    /**
24
     * Onesky API endpoint
25
     */
26
    protected $endpoint = 'https://platform.api.onesky.io/1';
27
28
    /**
29
     * Client authenticate token
30
     */
31
    protected $apiKey = null;
32
33
    /**
34
     * Client authenticate secret
35
     */
36
    protected $secret = null;
37
38
    /**
39
     * Resources with actions
40
     */
41
    protected $resources = array(
42
        'project_groups' => array(
43
            'list'      => '/project-groups',
44
            'show'      => '/project-groups/:project_group_id',
45
            'create'    => '/project-groups',
46
            'delete'    => '/project-groups/:project_group_id',
47
            'languages' => '/project-groups/:project_group_id/languages',
48
        ),
49
        'projects'       => array(
50
            'list'      => '/project-groups/:project_group_id/projects',
51
            'show'      => '/projects/:project_id',
52
            'create'    => '/project-groups/:project_group_id/projects',
53
            'update'    => '/projects/:project_id',
54
            'delete'    => '/projects/:project_id',
55
            'languages' => '/projects/:project_id/languages',
56
        ),
57
        'files'          => array(
58
            'list'   => '/projects/:project_id/files',
59
            'upload' => '/projects/:project_id/files',
60
            'delete' => '/projects/:project_id/files',
61
        ),
62
        'translations'   => array(
63
            'export' => '/projects/:project_id/translations',
64
            'multilingual' => '/projects/:project_id/translations/multilingual',
65
            'status' => '/projects/:project_id/translations/status',
66
        ),
67
        'import_tasks'   => array(
68
            'list' => '/projects/:project_id/import-tasks/',
69
            'show' => '/projects/:project_id/import-tasks/:import_id'
70
        ),
71
        'quotations'     => array(
72
            'show' => '/projects/:project_id/quotations'
73
        ),
74
        'orders'         => array(
75
            'list'   => '/projects/:project_id/orders',
76
            'show'   => '/projects/:project_id/orders/:order_id',
77
            'create' => '/projects/:project_id/orders'
78
        ),
79
        'locales'        => array(
80
            'list' => '/locales'
81
        ),
82
        'project_types'  => array(
83
            'list' => '/project-types'
84
        ),
85
    );
86
87
    /**
88
     * Actions to use multipart to upload file
89
     */
90
    protected $multiPartActions = array(
91
        'files' => array('upload'),
92
    );
93
94
    /**
95
     * Actions to use multipart to upload file
96
     */
97
    protected $exportFileActions = array(
98
        'translations' => array('export'),
99
    );
100
101
    /**
102
     * Methods of actions mapping
103
     */
104
    protected $methods = array(
105
        // 'get'    => array('list', 'show', 'languages', 'export', 'status'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
106
        'post'   => array('create', 'upload'),
107
        'put'    => array('update'),
108
        'delete' => array('delete'),
109
    );
110
111
    /**
112
     * Default curl settings
113
     */
114
    protected $curlSettings = array(
115
        CURLOPT_RETURNTRANSFER => true,
116
    );
117
118
    protected $httpHeaders = array(
119
        "Onesky-Plugin: php-wrapper",
120
    );
121
122 26
    public function __construct()
123
    {
124 26
        if (!function_exists('curl_init')) {
125
            throw new \Exception('OneSky needs the CURL PHP extension.');
126
        }
127 26
    }
128
129
    /**
130
     * @param string $apiKey
131
     * @return $this
132
     */
133
    public function setApiKey($apiKey)
134
    {
135
        $this->apiKey = $apiKey;
136
        return $this;
137
    }
138
139
    /**
140
     * @param string $secret
141
     * @return $this
142
     */
143
    public function setSecret($secret)
144
    {
145
        $this->secret = $secret;
146
        return $this;
147
    }
148
149
    /**
150
     * Retrieve resources
151
     * @return array
152
     */
153 25
    public function getResources()
154
    {
155 25
        return array_keys($this->resources);
156
    }
157
158
    /**
159
     * Retrieve actions of a resource
160
     * @param  string $resource Resource name
161
     * @return array|null
162
     */
163 25
    public function getActionsByResource($resource)
164
    {
165 25
        if (!isset($this->resources[$resource])) {
166
            return null; // no resource found
167
        }
168
169 25
        $actions = array();
170 25
        foreach ($this->resources[$resource] as $action => $path) {
171 25
            $actions[] = $action;
172 25
        }
173
174 25
        return $actions;
175
    }
176
177
    /**
178
     * Retrieve the corresponding method to use
179
     * @param  string $action Action name
180
     * @return string
181
     */
182 22
    public function getMethodByAction($action)
183
    {
184 22
        foreach ($this->methods as $method => $actions) {
185 22
            if (in_array($action, $actions)) {
186 10
                return $method;
187
            }
188 18
        }
189
190 12
        return 'get';
191
    }
192
193
    /**
194
     * Determine if using mulit-part to upload file
195
     * @param  string  $resource Resource name
196
     * @param  string  $action   Action name
197
     * @return boolean
198
     */
199 11
    public function isMultiPartAction($resource, $action)
200
    {
201 11
        return isset($this->multiPartActions[$resource]) && in_array($action, $this->multiPartActions[$resource]);
202
    }
203
204
    /**
205
     * Determine if it is to export (download) file
206
     * @param  string  $resource Resource name
207
     * @param  string  $action   Action name
208
     * @return boolean
209
     */
210
    public function isExportFileAction($resource, $action)
211
    {
212
        return isset($this->exportFileActions[$resource]) && in_array($action, $this->exportFileActions[$resource]);
213
    }
214
215
    /**
216
     * For developers to initial request to Onesky API
217
     * 
218
     * Example:
219
     *     $onesky = new Onesky_Api();
220
     *     $onesky->setApiKey('<api-key>')->setSecret('<api-secret>');
221
     *     
222
     *     // To list project type
223
     *     $onesky->projectTypes('list');
224
     *
225
     *     // To create project
226
     *     $onesky->projects('create', array('project_group_id' => 999));
227
     *
228
     *     // To upload string file
229
     *     $onesky->files('upload', array('project_id' => 1099, 'file' => 'path/to/file.yml', 'file_format' => 'YAML'));
230
     * 
231
     * @param  string $fn_name Function name acted as resource name
232
     * @param  array  $params  Parameters passed in request
233
     * @return array  Response
234
     */
235 25
    public function __call($fn_name, $params)
236
    {
237
        // is valid resource
238 25
        $resource = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $fn_name)); // camelcase to underscore
239 25
        if (!in_array($resource, $this->getResources())) {
240
            throw new \BadMethodCallException('Invalid resource');
241
        }
242
243
        // is valid action
244 25
        $action = array_shift($params); // action name
245 25
        if (!in_array($action, $this->getActionsByResource($resource))) {
246 3
            throw new \InvalidArgumentException('Invalid resource action');
247
        }
248
249
        // parameters
250 22
        if (count($params) > 0) {
251 11
            $params = $this->normalizeParams(array_shift($params));
252 11
        } else {
253 11
            $params = array();
254
        }
255
256
        // get request method
257 22
        $method = $this->getMethodByAction($action);
258
259
        // get path
260 22
        $path = $this->getRequestPath($resource, $action, $params);
261
262
        // is multi-part
263 11
        $isMultiPart = $this->isMultiPartAction($resource, $action);
264
265
        // return response
266 11
        return $this->callApi($method, $path, $params, $isMultiPart);
267
    }
268
269
    /**
270
     * Retrieve request path and replace variables with values
271
     * @param  string $resource Resource name
272
     * @param  string $action   Action name
273
     * @param  array  $params   Parameters
274
     * @return string Request path
275
     */
276 22
    private function getRequestPath($resource, $action, &$params)
277
    {
278 22
        if (!isset($this->resources[$resource]) || !isset($this->resources[$resource][$action])) {
279
            throw new \UnexpectedValueException('Resource path not found');
280
        }
281
282
        // get path
283 22
        $path = $this->resources[$resource][$action];
284
285
        // replace variables
286 22
        $matchCount = preg_match_all("/:(\w*)/", $path, $variables);
287 22
        if ($matchCount) {
288 22
            foreach ($variables[0] as $index => $placeholder) {
289 22
                if (!isset($params[$variables[1][$index]])) {
290 11
                    throw new \InvalidArgumentException('Missing parameter: ' . $variables[1][$index]);
291
                }
292
293 11
                $path = str_replace($placeholder, $params[$variables[1][$index]], $path);
294 11
                unset($params[$variables[1][$index]]); // remove parameter from $params
295 11
            }
296 11
        }
297
298 11
        return $path;
299
    }
300
301 11
    protected function verifyTokenAndSecret()
302
    {
303 11
        if (empty($this->apiKey) || empty($this->secret)) {
304 11
            throw new \UnexpectedValueException('Invalid authenticate data of api key or secret');
305
        }
306
    }
307
308
    /**
309
     * Initial request to Onesky API
310
     * @param  string  $method
311
     * @param  string  $path
312
     * @param  array   $params
313
     * @param  boolean $isMultiPart
314
     * @return array
315
     */
316 11
    private function callApi($method, $path, $params, $isMultiPart)
317
    {
318
        // init session
319 11
        $ch = curl_init();
320
321
        // request settings
322 11
        curl_setopt_array($ch, $this->curlSettings); // basic settings
323
324
        // url
325 11
        $url = $this->endpoint . $path;
326 11
        $url .= $method == 'get' ? $this->getAuthQueryStringWithParams($params) : $this->getAuthQueryString();
327
328
        curl_setopt($ch, CURLOPT_URL, $url);
329
        curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '/certs/AmazonRootCA1.pem');
330
331
        // http header
332
        $requestHeaders = $this->httpHeaders;
333
        if (!$isMultiPart) {
334
            $requestHeaders[] = "Content-Type: application/json";
335
        }
336
        curl_setopt($ch, CURLOPT_HTTPHEADER, $requestHeaders);
337
338
        // method specific settings
339
        switch ($method) {
340
            case 'post':
341
                curl_setopt($ch, CURLOPT_POST, 1);
342
343
                // request body
344
                if ($isMultiPart) {
345
                    if (version_compare(PHP_VERSION, '5.5.0') === -1) {
346
                        // fallback to old method
347
                        $params['file'] = '@' . $params['file'];
348
                    } else {
349
                        // make use of CURLFile
350
                        curl_setopt($ch, CURLOPT_SAFE_UPLOAD, true);
351
                        $params['file'] = new \CURLFile($params['file']);
352
                    }
353
                    $postBody = $params;
354
                } else {
355
                    $postBody = json_encode($params);
356
                }
357
                curl_setopt($ch, CURLOPT_POSTFIELDS, $postBody);
358
                break;
359
360 View Code Duplication
            case 'put':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
361
                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
362
                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
363
                break;
364
365 View Code Duplication
            case 'delete':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
366
                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
367
                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
368
                break;
369
        }
370
371
        // execute request
372
        $response = curl_exec($ch);
373
374
        // error handling
375
        if ($response === false) {
376
            throw new \UnexpectedValueException(curl_error($ch));
377
        }
378
379
        // close connection
380
        curl_close($ch);
381
382
        // return response
383
        return $response;
384
    }
385
386
    /**
387
     * @param $params
388
     * @return string
389
     */
390 6
    private function getAuthQueryStringWithParams($params)
391
    {
392 6
        $queryString = $this->getAuthQueryString();
393
394
        if (count($params) > 0) {
395
            $queryString .= '&' . http_build_query($params);
396
        }
397
398
        return $queryString;
399
    }
400
401
    /**
402
     * @return string
403
     */
404 11
    private function getAuthQueryString()
405
    {
406 11
        $this->verifyTokenAndSecret();
407
408
        $timestamp = time();
409
        $devHash = md5($timestamp . $this->secret);
410
411
        $queryString  = '?api_key=' . $this->apiKey;
412
        $queryString .= '&timestamp=' . $timestamp;
413
        $queryString .= '&dev_hash=' . $devHash;
414
415
        return $queryString;
416
    }
417
418
    /**
419
     * @param array $params
420
     * @return array
421
     */
422 11
    private function normalizeParams(array $params)
423
    {
424
        // change boolean value to integer for curl
425 11
        foreach ($params as $key => $value) {
426 11
            if (is_bool($value)) {
427
                $params[$key] = (int)$value;
428
            }
429 11
        }
430
431 11
        return $params;
432
    }
433
}
434