Client::isExportFileAction()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 2
eloc 2
nc 2
nop 2
crap 6
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
330
        // http header
331
        $requestHeaders = $this->httpHeaders;
332
        if (!$isMultiPart) {
333
            $requestHeaders[] = "Content-Type: application/json";
334
        }
335
        curl_setopt($ch, CURLOPT_HTTPHEADER, $requestHeaders);
336
337
        // method specific settings
338
        switch ($method) {
339
            case 'post':
340
                curl_setopt($ch, CURLOPT_POST, 1);
341
342
                // request body
343
                if ($isMultiPart) {
344
                    if (version_compare(PHP_VERSION, '5.5.0') === -1) {
345
                        // fallback to old method
346
                        $params['file'] = '@' . $params['file'];
347
                    } else {
348
                        // make use of CURLFile
349
                        curl_setopt($ch, CURLOPT_SAFE_UPLOAD, true);
350
                        $params['file'] = new \CURLFile($params['file']);
351
                    }
352
                    $postBody = $params;
353
                } else {
354
                    $postBody = json_encode($params);
355
                }
356
                curl_setopt($ch, CURLOPT_POSTFIELDS, $postBody);
357
                break;
358
359 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...
360
                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
361
                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
362
                break;
363
364 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...
365
                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
366
                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
367
                break;
368
        }
369
370
        // execute request
371
        $response = curl_exec($ch);
372
373
        // error handling
374
        if ($response === false) {
375
            throw new \UnexpectedValueException(curl_error($ch));
376
        }
377
378
        // close connection
379
        curl_close($ch);
380
381
        // return response
382
        return $response;
383
    }
384
385
    /**
386
     * @param $params
387
     * @return string
388
     */
389 6
    private function getAuthQueryStringWithParams($params)
390
    {
391 6
        $queryString = $this->getAuthQueryString();
392
393
        if (count($params) > 0) {
394
            $queryString .= '&' . http_build_query($params);
395
        }
396
397
        return $queryString;
398
    }
399
400
    /**
401
     * @return string
402
     */
403 11
    private function getAuthQueryString()
404
    {
405 11
        $this->verifyTokenAndSecret();
406
407
        $timestamp = time();
408
        $devHash = md5($timestamp . $this->secret);
409
410
        $queryString  = '?api_key=' . $this->apiKey;
411
        $queryString .= '&timestamp=' . $timestamp;
412
        $queryString .= '&dev_hash=' . $devHash;
413
414
        return $queryString;
415
    }
416
417
    /**
418
     * @param array $params
419
     * @return array
420
     */
421 11
    private function normalizeParams(array $params)
422
    {
423
        // change boolean value to integer for curl
424 11
        foreach ($params as $key => $value) {
425 11
            if (is_bool($value)) {
426
                $params[$key] = (int)$value;
427
            }
428 11
        }
429
430 11
        return $params;
431
    }
432
}
433