Completed
Push — master ( 971973...329ed0 )
by William Johnson S.
03:18
created

HttpApi   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 359
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 4.76%

Importance

Changes 5
Bugs 0 Features 1
Metric Value
wmc 33
c 5
b 0
f 1
lcom 1
cbo 5
dl 0
loc 359
ccs 5
cts 105
cp 0.0476
rs 9.3999

21 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A setCompanyId() 0 7 1
A setUsername() 0 7 1
A setPassword() 0 7 1
A doLogin() 0 18 3
A getEmployeeRole() 0 4 1
A getDepartment() 0 4 1
A parsePunches() 0 9 2
A executeLogin() 0 8 1
A checkAccessEnabled() 0 6 1
A checkLoginStatus() 0 10 3
A getResponseLoginStatus() 0 12 3
A setLoggedIn() 0 11 2
A companyUrl() 0 7 1
A loginUrl() 0 7 1
A punchesUrl() 0 8 1
A getPunchesPage() 0 4 1
A getPunchesFromPage() 0 12 2
A parsePunchesPage() 0 12 2
A createPunchesDate() 0 9 2
A getPunches() 0 17 2
1
<?php
2
3
namespace Katapoka\Ahgora;
4
5
use DateTime;
6
use InvalidArgumentException;
7
use Katapoka\Ahgora\Contracts\IHttpClient;
8
use Katapoka\Ahgora\Contracts\IHttpResponse;
9
use LogicException;
10
11
/**
12
 * Class responsible for getting the data from the Ahgora system.
13
 */
14
class HttpApi extends AbstractApi
15
{
16
    const AHGORA_BASE_URL = 'https://www.ahgora.com.br';
17
    const AHGORA_COMPANY_URL = '%s/externo/index/%s';
18
    const AHGORA_LOGIN_URL = '%s/externo/login';
19
    const AHGORA_PUNCHS_URL = '%s/externo/batidas/%d-%d';
20
21
    /** @var \Katapoka\Ahgora\Contracts\IHttpClient */
22
    private $httpClient;
23
    /** @var string */
24
    private $password;
25
    /** @var string */
26
    private $companyId;
27
    /** @var string */
28
    private $username;
29
    /** @var bool */
30
    private $loggedIn = false;
31
    /** @var HtmlPageParser */
32
    private $htmlPageParser;
33
34
    /**
35
     * Api constructor.
36
     *
37
     * @param IHttpClient $httpClient
38
     */
39 1
    public function __construct(IHttpClient $httpClient)
40
    {
41 1
        $this->httpClient = $httpClient;
42 1
        $this->htmlPageParser = new HtmlPageParser();
43 1
        $this->debug('Api instance created');
44 1
    }
45
46
    /**
47
     * Set the company id of the ahgora system.
48
     *
49
     * @param string $companyId
50
     *
51
     * @return $this
52
     */
53
    public function setCompanyId($companyId)
54
    {
55
        $this->companyId = $companyId;
56
        $this->debug('Company ID set', ['company_id' => $companyId]);
57
58
        return $this;
59
    }
60
61
    /**
62
     * Set the username of the employee, from the company set at the setCompanyId.
63
     *
64
     * @param string $username
65
     *
66
     * @return $this
67
     */
68
    public function setUsername($username)
69
    {
70
        $this->username = $username;
71
        $this->debug('Username set', ['username' => $username]);
72
73
        return $this;
74
    }
75
76
    /**
77
     * Set the password of the employee, from the company set at the setCompanyId.
78
     *
79
     * @param string $password
80
     *
81
     * @return $this
82
     */
83
    public function setPassword($password)
84
    {
85
        $this->password = $password;
86
        $this->debug('Password set', ['password' => $password]);
87
88
        return $this;
89
    }
90
91
    /**
92
     * Try to execute the login on the page.
93
     * To execute some actions the user needs to be loggedin.
94
     * After a successful login, the status loggedin is saved as true.
95
     *
96
     * @return bool Returns true if the login was successful and false otherwise
97
     */
98
    public function doLogin()
99
    {
100
        $hasLoggedIn = false;
101
        $this->debug('Started login proccess');
102
103
        $accessEnabled = $this->checkAccessEnabled();
104
105
        if ($accessEnabled) {
106
            $response = $this->executeLogin();
107
            $hasLoggedIn = $this->checkLoginStatus($response);
108
        }
109
110
        $this->debug($accessEnabled ? "Company has external access enabled" : "Company hasn't external access enabled");
111
112
        $this->setLoggedIn($hasLoggedIn);
113
114
        return $hasLoggedIn;
115
    }
116
117
    /**
118
     * Get the punches at the given parameters.
119
     *
120
     * @param int|null $month The month you want to get the punches - Must be between 01 and 12 (both included)
121
     * @param int|null $year  The year you want to get the punches
122
     *
123
     * @return array
124
     */
125
    public function getPunches($month = null, $year = null)
126
    {
127
        if (!$this->loggedIn) {
128
            throw new LogicException('To get punches you need to be loggedIn');
129
        }
130
131
        $month = IntHelper::parseNullableInt($month);
132
        $year = IntHelper::parseNullableInt($year);
133
134
        /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
66% 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...
135
        if (!$this->isValidPeriod($month, $year)) {
136
            throw new InvalidArgumentException(sprintf('Invalid period of time: [%s-%s]', $month, $year));
137
        }
138
        */
139
140
        return $this->getPunchesFromPage($this->getPunchesPage($month, $year));
141
    }
142
143
    /**
144
     * Gets the employer name.
145
     *
146
     * @return string
147
     */
148
    public function getEmployeeRole()
149
    {
150
        return "NOT IMPLEMENTED YET";
151
    }
152
153
    /**
154
     * Get the employer department.
155
     *
156
     * @return string
157
     */
158
    public function getDepartment()
159
    {
160
        return "NOT IMPLEMENTED YET";
161
    }
162
163
    /**
164
     * Retrive all the punches for the given string.
165
     *
166
     * @param string $punchesStr
167
     *
168
     * @return array
169
     */
170
    private function parsePunches($punchesStr)
171
    {
172
        $punches = [];
173
        if (!!preg_match_all('/(\d{2}:\d{2})/is', $punchesStr, $matches)) {
174
            $punches = $matches[0];
175
        }
176
177
        return $punches;
178
    }
179
180
    /**
181
     * Execute the login on the server and returns the server response.
182
     *
183
     * @return IHttpResponse
184
     */
185
    private function executeLogin()
186
    {
187
        return $this->httpClient->post($this->loginUrl(), [
188
            'empresa'   => $this->companyId,
189
            'matricula' => $this->username,
190
            'senha'     => $this->password,
191
        ]);
192
    }
193
194
    /**
195
     * Check if the company has external access on the Ahgora system.
196
     *
197
     * @return bool
198
     */
199
    private function checkAccessEnabled()
200
    {
201
        $response = $this->httpClient->get($this->companyUrl());
202
203
        return stripos($response->getBody(), 'Sua Empresa não liberou o acesso a essa ferramenta') === false;
204
    }
205
206
    /**
207
     * Check the return of the login action.
208
     * How it works: If statusCode 200 and no body, login ok, otherwise, login failed.
209
     * Should return a json with property "r" with "error" and "text" with the message
210
     *
211
     * @param IHttpResponse $response
212
     *
213
     * @return bool
214
     */
215
    private function checkLoginStatus(IHttpResponse $response)
216
    {
217
        try {
218
            return $response->getHttpStatus() === IHttpClient::HTTP_STATUS_OK && $this->getResponseLoginStatus($response);
219
        } catch (InvalidArgumentException $iaex) {
220
            $this->error('checkLoginStatus', ['expcetion' => $iaex]);
221
222
            return false;
223
        }
224
    }
225
226
    /**
227
     * Check if the response can be decoded as json, has the property r and r is 'success'.
228
     *
229
     * @param IHttpResponse $response
230
     *
231
     * @return bool
232
     */
233
    private function getResponseLoginStatus(IHttpResponse $response)
234
    {
235
        try {
236
            $json = $response->json();
237
238
            return array_key_exists('r', $json) && $json->r === 'success';
239
        } catch (InvalidArgumentException $iaex) {
240
            $this->debug('getResponseLoginStatus', ['exception', $iaex]);
241
242
            return false;
243
        }
244
    }
245
246
    /**
247
     * Safely set if the user is loggedin or not.
248
     * Did a separate method do eventually trigger events, if necessary.
249
     *
250
     * @param bool $loggedIn
251
     *
252
     * @return $this
253
     */
254
    private function setLoggedIn($loggedIn = true)
255
    {
256
        if (!is_bool($loggedIn)) {
257
            throw new InvalidArgumentException('LoggedIn parameter must be boolean');
258
        }
259
260
        $this->debug('setLoggedIn', ['logged_in' => $loggedIn]);
261
        $this->loggedIn = $loggedIn;
262
263
        return $this;
264
    }
265
266
    /**
267
     * Build the company url string.
268
     *
269
     * @return string
270
     */
271
    private function companyUrl()
272
    {
273
        $companyUrl = sprintf(self::AHGORA_COMPANY_URL, self::AHGORA_BASE_URL, $this->companyId);
274
        $this->debug('CompanyURL', ['company_url' => $companyUrl]);
275
276
        return $companyUrl;
277
    }
278
279
    /**
280
     * Build the login url.
281
     *
282
     * @return string
283
     */
284
    private function loginUrl()
285
    {
286
        $loginUrl = sprintf(self::AHGORA_LOGIN_URL, self::AHGORA_BASE_URL);
287
        $this->debug('loginUrl', ['login_url' => $loginUrl]);
288
289
        return $loginUrl;
290
    }
291
292
    /**
293
     * Get the built punchesUrl with the given month and year.
294
     *
295
     * @param int $month
296
     * @param int $year
297
     *
298
     * @return string
299
     */
300
    private function punchesUrl($month, $year)
301
    {
302
        $month = str_pad($month, 2, '0', STR_PAD_LEFT);
303
        $punchesUrl = sprintf(self::AHGORA_PUNCHS_URL, self::AHGORA_BASE_URL, $month, $year);
304
        $this->debug('punchesUrl', ['punches_url' => $punchesUrl]);
305
306
        return $punchesUrl;
307
    }
308
309
    /**
310
     * Make the request to the punches page of the requested time period.
311
     *
312
     * @param int $month
313
     * @param int $year
314
     *
315
     * @return IHttpResponse
316
     */
317
    private function getPunchesPage($month, $year)
318
    {
319
        return $this->httpClient->get($this->punchesUrl($month, $year));
320
    }
321
322
    /**
323
     * Get the punches from the given response of the punches page.
324
     *
325
     * @param IHttpResponse $punchesPageResponse
326
     *
327
     * @return array
328
     */
329
    private function getPunchesFromPage(IHttpResponse $punchesPageResponse)
330
    {
331
        if ($punchesPageResponse->getHttpStatus() !== IHttpClient::HTTP_STATUS_OK) {
332
            throw new InvalidArgumentException('The request returned http status ' . $punchesPageResponse->getHttpStatus());
333
        }
334
335
        $punches = $this->parsePunchesPage($punchesPageResponse);
336
337
        return [
338
            'punches' => $punches,
339
        ];
340
    }
341
342
    private function parsePunchesPage(IHttpResponse $punchesPageResponse)
343
    {
344
        $rows = $this->htmlPageParser->getPunchesRows($punchesPageResponse);
345
        $punchCollection = [];
346
347
        foreach ($rows as $row) {
348
            $punches = $this->parsePunches($row['punches']);
349
            $punchCollection = array_merge($punchCollection, $this->createPunchesDate($row['date'], $punches));
350
        }
351
352
        return $punchCollection;
353
    }
354
355
    /**
356
     * Convert the date string and the datepunch array to an array of DateTime's.
357
     *
358
     * @param string $date
359
     * @param array  $punches
360
     *
361
     * @return \DateTime[]
362
     */
363
    private function createPunchesDate($date, array $punches = [])
364
    {
365
        $dates = [];
366
        foreach ($punches as $punch) {
367
            $dates[] = DateTime::createFromFormat($this->datetimeFormat, sprintf('%s %s', $date, $punch));
368
        }
369
370
        return $dates;
371
    }
372
}
373