Completed
Push — master ( 7f9dbb...a8dcc6 )
by Kevin
02:19
created

Request   C

Complexity

Total Complexity 56

Size/Duplication

Total Lines 549
Duplicated Lines 13.3 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 56
lcom 1
cbo 4
dl 73
loc 549
ccs 139
cts 139
cp 1
rs 6.5957
c 0
b 0
f 0

30 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 14 1
A getHttpClient() 0 19 3
A setHandlerStack() 0 4 1
A setLogger() 0 4 1
B setLogLevel() 0 16 9
A setMessageFormatter() 0 4 1
A me() 0 4 1
A updateMe() 0 13 3
A listClients() 0 4 1
A createClient() 9 9 2
A deleteClient() 0 4 1
A listGauges() 9 9 2
A createGauge() 16 16 3
A gaugeDetail() 0 4 1
A updateGauge() 16 16 3
A deleteGauge() 0 4 1
A listShares() 0 4 1
A shareGauge() 0 8 1
B topContent() 0 20 5
A unshareGauge() 0 4 1
A topReferrers() 9 9 2
A traffic() 0 4 1
A browserResolutions() 0 4 1
A technology() 0 4 1
A searchTerms() 10 10 2
A searchEngines() 0 4 1
A locations() 0 4 1
A browserStats() 0 4 1
A makeApiCall() 0 15 1
A formatDateParameter() 0 13 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Request often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Request, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Kevintweber\Gauges;
4
5
use Doctrine\Instantiator\Exception\InvalidArgumentException;
6
use GuzzleHttp\Client;
7
use GuzzleHttp\ClientInterface;
8
use GuzzleHttp\HandlerStack;
9
use GuzzleHttp\Middleware;
10
use GuzzleHttp\MessageFormatter;
11
use GuzzleHttp\Psr7\Response;
12
use Psr\Log\LoggerAwareInterface;
13
use Psr\Log\LoggerInterface;
14
use Psr\Log\LogLevel;
15
16
/**
17
 * Used to make Gauges API calls.
18
 */
19
class Request implements LoggerAwareInterface
20
{
21
    const URI = 'https://secure.gaug.es/';
22
23
    /** @var null|ClientInterface */
24
    private $client;
25
26
    /** @var HandlerStack */
27
    private $handlerStack;
28
29
    /** @var null|LoggerInterface */
30
    private $logger;
31
32
    /** @var string */
33
    private $logLevel;
34
35
    /** @var MessageFormatter */
36
    private $messageFormatter;
37
38
    /** @var array */
39
    private $options;
40
41
    /** @var string */
42
    protected $token;
43
44
    /**
45
     * Constructor
46
     *
47
     * @param string $token     Your API token
48
     * @param array  $options   See Guzzle documentation (proxy, etc.)
49
     */
50 26
    public function __construct(string $token, array $options = array())
51
    {
52 26
        $this->client = null;
53 26
        $this->handlerStack = HandlerStack::create();
54 26
        $this->logger = null;
55 26
        $this->logLevel = LogLevel::INFO;
56 26
        $this->messageFormatter = new MessageFormatter();
57 26
        $this->options = array_merge(
58 26
            array('timeout' => 10),
59
            $options
60
        );
61 26
        $this->options['base_uri'] = self::URI;
62 26
        $this->token = $token;
63 26
    }
64
65
    /**
66
     * Getter for the HTTP client.
67
     *
68
     * @return Client
69
     */
70 23
    protected function getHttpClient() : Client
71
    {
72 23
        if ($this->client === null) {
73 23
            if ($this->logger instanceof LoggerInterface) {
74 23
                $this->handlerStack->push(
75 23
                    Middleware::log(
76 23
                        $this->logger,
77 23
                        $this->messageFormatter,
78 23
                        $this->logLevel
79
                    )
80
                );
81
            }
82
83 23
            $this->options['handler'] = $this->handlerStack;
84 23
            $this->client = new Client($this->options);
85
        }
86
87 23
        return $this->client;
88
    }
89
90
    /**
91
     * Setter for the Guzzle HandlerStack
92
     */
93 25
    public function setHandlerStack(HandlerStack $handlerStack)
94
    {
95 25
        $this->handlerStack = $handlerStack;
96 25
    }
97
98 26
    public function setLogger(LoggerInterface $logger)
99
    {
100 26
        $this->logger = $logger;
101 26
    }
102
103 25
    public function setLogLevel(string $logLevel)
104
    {
105 25
        $logLevel = strtolower($logLevel);
106 25
        if ($logLevel !== LogLevel::ALERT &&
107 25
            $logLevel !== LogLevel::CRITICAL &&
108 25
            $logLevel !== LogLevel::DEBUG &&
109 25
            $logLevel !== LogLevel::EMERGENCY &&
110 25
            $logLevel !== LogLevel::ERROR &&
111 25
            $logLevel !== LogLevel::INFO &&
112 25
            $logLevel !== LogLevel::NOTICE &&
113 25
            $logLevel !== LogLevel::WARNING) {
114 1
            throw new \InvalidArgumentException('Invalid log level: ' . $logLevel);
115
        }
116
117 25
        $this->logLevel = $logLevel;
118 25
    }
119
120
    /**
121
     * Setter for the Guzzle MessageFormatter
122
     */
123 26
    public function setMessageFormatter(MessageFormatter $messageFormatter)
124
    {
125 26
        $this->messageFormatter = $messageFormatter;
126 26
    }
127
128
    /**
129
     * Get Your Information
130
     *
131
     * Returns your information.
132
     *
133
     * @return Response
134
     */
135 2
    public function me() : Response
136
    {
137 2
        return $this->makeApiCall('GET', 'me');
138
    }
139
140
    /**
141
     * Update Your Information
142
     *
143
     * Updates and returns your information with the updates applied.
144
     *
145
     * @param string $first_name Your first name. (Optional)
146
     * @param string $last_name  Your last name. (Optional)
147
     *
148
     * @return Response
149
     */
150 1
    public function updateMe(string $first_name = null, string $last_name = null) : Response
151
    {
152 1
        $params = array();
153 1
        if (isset($first_name)) {
154 1
            $params['first_name'] = $first_name;
155
        }
156
157 1
        if (isset($last_name)) {
158 1
            $params['last_name'] = $last_name;
159
        }
160
161 1
        return $this->makeApiCall('PUT', 'me', $params);
162
    }
163
164
    /**
165
     * API Client List
166
     *
167
     * Returns an array of your API clients.
168
     *
169
     * @return Response
170
     */
171 1
    public function listClients() : Response
172
    {
173 1
        return $this->makeApiCall('GET', 'clients');
174
    }
175
176
    /**
177
     * Creating an API Client
178
     *
179
     * Creates an API client, which can be used to authenticate against
180
     * the Gaug.es API.
181
     *
182
     * @param string $description Short description for the key (Optional)
183
     *
184
     * @return Response
185
     */
186 1 View Code Duplication
    public function createClient(string $description = null) : Response
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
187
    {
188 1
        $params = array();
189 1
        if (isset($description)) {
190 1
            $params['description'] = $description;
191
        }
192
193 1
        return $this->makeApiCall('POST', 'clients', $params);
194
    }
195
196
    /**
197
     * Delete an API Client
198
     *
199
     * Permanently deletes an API client key.
200
     *
201
     * @param string $id
202
     *
203
     * @return Response
204
     */
205 1
    public function deleteClient(string $id) : Response
206
    {
207 1
        return $this->makeApiCall('DELETE', 'clients/' . $id);
208
    }
209
210
    /**
211
     * Gauges List
212
     *
213
     * Returns an array of your gauges, with recent traffic included.
214
     *
215
     * @param int $page Page number (Optional)
216
     *
217
     * @return Response
218
     */
219 1 View Code Duplication
    public function listGauges(int $page = null) : Response
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
220
    {
221 1
        $params = array();
222 1
        if (isset($page)) {
223 1
            $params['page'] = $page;
224
        }
225
226 1
        return $this->makeApiCall('GET', 'gauges', $params);
227
    }
228
229
    /**
230
     * Create a New Gauge
231
     *
232
     * Creates a gauge.
233
     *
234
     * @param string               $title
235
     * @param string|\DateTimeZone $tz
236
     * @param string               $allowedHosts (Optional)
237
     *
238
     * @return Response
239
     */
240 1 View Code Duplication
    public function createGauge(string $title, $tz, string $allowedHosts = null) : Response
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
241
    {
242 1
        if (!$tz instanceof \DateTimeZone) {
243 1
            $tz = new \DateTimeZone($tz);
244
        }
245
246
        $params = array(
247 1
            'title' => $title,
248 1
            'tz' => $tz->getName()
249
        );
250 1
        if (isset($allowedHosts)) {
251 1
            $params['allowed_hosts'] = $allowedHosts;
252
        }
253
254 1
        return $this->makeApiCall('POST', 'gauges', $params);
255
    }
256
257
    /**
258
     * Gauge Detail
259
     *
260
     * Gets details for a gauge.
261
     *
262
     * @param string $id
263
     *
264
     * @return Response
265
     */
266 1
    public function gaugeDetail(string $id) : Response
267
    {
268 1
        return $this->makeApiCall('GET', 'gauges/' . $id);
269
    }
270
271
    /**
272
     * Update a Gauge
273
     *
274
     * Updates and returns a gauge with the updates applied.
275
     *
276
     * @param string               $id
277
     * @param string               $title
278
     * @param string|\DateTimeZone $tz
279
     * @param string               $allowedHosts (Optional)
280
     *
281
     * @return Response
282
     */
283 1 View Code Duplication
    public function updateGauge(string $id, string $title, $tz, string $allowedHosts = null) : Response
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
284
    {
285 1
        if (!$tz instanceof \DateTimeZone) {
286 1
            $tz = new \DateTimeZone($tz);
287
        }
288
289
        $params = array(
290 1
            'title' => $title,
291 1
            'tz' => $tz->getName()
292
        );
293 1
        if (isset($allowedHosts)) {
294 1
            $params['allowed_hosts'] = $allowedHosts;
295
        }
296
297 1
        return $this->makeApiCall('PUT', 'gauges/' . $id, $params);
298
    }
299
300
    /**
301
     * Delete a Gauge
302
     *
303
     * Permanently deletes a gauge.
304
     *
305
     * @param string $id
306
     *
307
     * @return Response
308
     */
309 1
    public function deleteGauge(string $id) : Response
310
    {
311 1
        return $this->makeApiCall('DELETE', 'gauges/' . $id);
312
    }
313
314
    /**
315
     * List Shares
316
     *
317
     * Lists the people that have access to a Gauge.
318
     *
319
     * @param string $id
320
     *
321
     * @return Response
322
     */
323 1
    public function listShares(string $id) : Response
324
    {
325 1
        return $this->makeApiCall('GET', 'gauges/' . $id . '/shares');
326
    }
327
328
    /**
329
     * Share a Gauge
330
     *
331
     * Shares gauge with a person by their email. Any valid email will work
332
     * and will receive an invite even if there is no existing Gauges user
333
     * with that email.
334
     *
335
     * @param string $id
336
     * @param string $email
337
     *
338
     * @return Response
339
     */
340 1
    public function shareGauge(string $id, string $email) : Response
341
    {
342
        $params = array(
343 1
            'email' => $email
344
        );
345
346 1
        return $this->makeApiCall('POST', 'gauges/' . $id . '/shares', $params);
347
    }
348
349
    /**
350
     * Top Content
351
     *
352
     * Gets top content for a gauge, paginated.
353
     *
354
     * @param string           $id
355
     * @param string|\DateTime $date  (Optional) Date in format YYYY-MM-DD
356
     * @param string           $group (Optional) Either "day" or "month".  Default is "day".
357
     * @param int              $page  (Optional)
358
     *
359
     * @return Response
360
     */
361 2
    public function topContent(string $id, $date = null, string $group = null, int $page = null) : Response
362
    {
363 2
        $params = $this->formatDateParameter($date);
364 2
        if (isset($group)) {
365 2
            $group = strtolower($group);
366 2
            if ($group !== 'month' && $group !== 'day') {
367 1
                throw new \InvalidArgumentException(
368 1
                    'Invalid group parameter for "topContent" call.  Allowed values are "day" or "month".  Actual value is : ' . $group
369
                );
370
            }
371
372 1
            $params['group'] = $group;
373
        }
374
375 1
        if (isset($page)) {
376 1
            $params['page'] = $page;
377
        }
378
379 1
        return $this->makeApiCall('GET', 'gauges/' . $id . '/content', $params);
380
    }
381
382
    /**
383
     * Un-share Gauge
384
     *
385
     * @param string $id
386
     * @param string $user_id
387
     *
388
     * @return Response
389
     */
390 1
    public function unshareGauge(string $id, string $user_id) : Response
391
    {
392 1
        return $this->makeApiCall('DELETE', 'gauges/' . $id . '/shares/' . $user_id);
393
    }
394
395
    /**
396
     * Top Referrers
397
     *
398
     * Gets top referrers for a gauge, paginated.
399
     *
400
     * @param string           $id
401
     * @param string|\DateTime $date (Optional) Date in format YYYY-MM-DD
402
     * @param int              $page (Optional)
403
     *
404
     * @return Response
405
     */
406 1 View Code Duplication
    public function topReferrers(string $id, $date = null, int $page = null) : Response
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
407
    {
408 1
        $params = $this->formatDateParameter($date);
409 1
        if (isset($page)) {
410 1
            $params['page'] = (int) $page;
411
        }
412
413 1
        return $this->makeApiCall('GET', 'gauges/' . $id . '/referrers', $params);
414
    }
415
416
    /**
417
     * Traffic
418
     *
419
     * Gets traffic for a gauge.
420
     *
421
     * @param string           $id
422
     * @param string|\DateTime $date (Optional) Date in format YYYY-MM-DD
423
     *
424
     * @return Response
425
     */
426 1
    public function traffic(string $id, $date = null) : Response
427
    {
428 1
        return $this->makeApiCall('GET', 'gauges/' . $id . '/traffic', $this->formatDateParameter($date));
429
    }
430
431
    /**
432
     * Browser Resolutions
433
     *
434
     * Gets browsers heights, browser widths, and screen widths for a gauge.
435
     *
436
     * @param string $id
437
     * @param string|\DateTime $date (Optional) Date in format YYYY-MM-DD
438
     *
439
     * @return Response
440
     */
441 1
    public function browserResolutions(string $id, $date = null) : Response
442
    {
443 1
        return $this->makeApiCall('GET', 'gauges/' . $id . '/resolutions', $this->formatDateParameter($date));
444
    }
445
446
    /**
447
     * Technology
448
     *
449
     * Gets browsers and platforms for a gauge.
450
     *
451
     * @param string           $id
452
     * @param string|\DateTime $date (Optional) Date in format YYYY-MM-DD
453
     *
454
     * @return Response
455
     */
456 1
    public function technology(string $id, $date = null) : Response
457
    {
458 1
        return $this->makeApiCall('GET', 'gauges/' . $id . '/technology', $this->formatDateParameter($date));
459
    }
460
461
    /**
462
     * Search Terms
463
     *
464
     * Gets search terms for a gauge, paginated.
465
     *
466
     * @param string $id
467
     * @param string|\DateTime $date (Optional) Date in format YYYY-MM-DD
468
     * @param int    $page (Optional)
469
     *
470
     * @return Response
471
     */
472 1 View Code Duplication
    public function searchTerms(string $id, $date = null, int $page = null) : Response
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
473
    {
474 1
        $params = $this->formatDateParameter($date);
475
476 1
        if (isset($page)) {
477 1
            $params['page'] = $page;
478
        }
479
480 1
        return $this->makeApiCall('GET', 'gauges/' . $id . '/terms', $params);
481
    }
482
483
    /**
484
     * Search Engines
485
     *
486
     * Gets search engines for a gauge.
487
     *
488
     * @param string $id
489
     * @param string|\DateTime $date (Optional) Date in format YYYY-MM-DD
490
     *
491
     * @return Response
492
     */
493 1
    public function searchEngines(string $id, $date = null) : Response
494
    {
495 1
        return $this->makeApiCall('GET', 'gauges/' . $id . '/engines', $this->formatDateParameter($date));
496
    }
497
498
    /**
499
     * Locations
500
     *
501
     * Gets locations for a gauge.
502
     *
503
     * @param string $id
504
     * @param string|\DateTime $date (Optional) Date in format YYYY-MM-DD
505
     *
506
     * @return Response
507
     */
508 1
    public function locations(string $id, $date = null) : Response
509
    {
510 1
        return $this->makeApiCall('GET', 'gauges/' . $id . '/locations', $this->formatDateParameter($date));
511
    }
512
513
    /**
514
     * Browser stats
515
     *
516
     * Get the browser statistics in a format used with the browserlist module.
517
     * (See https://github.com/ai/browserslist)
518
     *
519
     * @param string $id
520
     * @param string|\DateTime $date (Optional) Date in format YYYY-MM-DD
521
     *
522
     * @return Response
523
     */
524 1
    public function browserStats(string $id, $date = null) : Response
525
    {
526 1
        return $this->makeApiCall('GET', 'gauges/' . $id . '/browserstats', $this->formatDateParameter($date));
527
    }
528
529
    /**
530
     * Make the actual gauges API call.
531
     *
532
     * @param string $method       [GET|POST|PUT|DELETE]
533
     * @param string $path
534
     * @param array  $params
535
     *
536
     * @return Response
537
     */
538 23
    protected function makeApiCall(string $method, string $path, array $params = array()) : Response
539
    {
540
        // Format method.
541 23
        $method = strtoupper($method);
542
543
        // Make API call.
544 23
        return $this->getHttpClient()->request(
545
            $method,
546
            $path,
547
            array(
548 23
                'headers' => array('X-Gauges-Token' => $this->token),
549 23
                'query' => $params
550
            )
551
        );
552
    }
553
554 10
    private function formatDateParameter($date = null) : array
555
    {
556 10
        $params = array();
557 10
        if (isset($date)) {
558 10
            if (!$date instanceof \DateTime) {
559 10
                $date = new \DateTime($date);
560
            }
561
562 10
            $params['date'] = $date->format('Y-m-d');
563
        }
564
565 10
        return $params;
566
    }
567
}
568