Passed
Push — master ( c954ee...074c96 )
by Robert
04:06
created

LiveEngageLaravel   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 537
Duplicated Lines 0 %

Test Coverage

Coverage 63.21%

Importance

Changes 0
Metric Value
wmc 42
dl 0
loc 537
ccs 122
cts 193
cp 0.6321
rs 9.0399
c 0
b 0
f 0

27 Methods

Rating   Name   Duplication   Size   Complexity  
A __get() 0 3 1
A __construct() 0 5 2
A retry() 0 5 1
A limit() 0 5 1
A nonInteractive() 0 4 1
A domain() 0 7 1
A key() 0 6 1
A account() 0 5 1
A visitor() 0 12 2
A active() 0 4 1
A chat() 0 14 1
A updateAgent() 0 13 1
A login() 0 25 1
A getConversation() 0 16 2
A getSkill() 0 7 1
A skills() 0 7 1
A requestV2() 0 20 2
A engagementHistory() 0 19 3
A getAgent() 0 7 1
A conversationHistory() 0 19 3
A retrieveMsgHistory() 0 19 3
A requestV1() 0 26 4
A getAgentStatus() 0 15 2
A requestClient() 0 22 1
A status() 0 7 1
A retrieveHistory() 0 20 2
A agents() 0 35 1

How to fix   Complexity   

Complex Class

Complex classes like LiveEngageLaravel 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.

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 LiveEngageLaravel, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace LivePersonInc\LiveEngageLaravel;
4
5
use Carbon\Carbon;
6
use GuzzleHttp\Client;
7
use GuzzleHttp\HandlerStack;
8
use GuzzleHttp\Subscriber\Oauth\Oauth1;
9
use LivePersonInc\LiveEngageLaravel\Models\Info;
10
use LivePersonInc\LiveEngageLaravel\Models\MetaData;
11
use LivePersonInc\LiveEngageLaravel\Models\AccountStatus;
12
use LivePersonInc\LiveEngageLaravel\Models\MessagingInfo;
13
use LivePersonInc\LiveEngageLaravel\Models\Payload;
14
use LivePersonInc\LiveEngageLaravel\Models\Visitor;
15
use LivePersonInc\LiveEngageLaravel\Models\Agent;
16
use LivePersonInc\LiveEngageLaravel\Models\Skill;
17
use LivePersonInc\LiveEngageLaravel\Models\Campaign;
18
use LivePersonInc\LiveEngageLaravel\Models\Engagement;
19
use LivePersonInc\LiveEngageLaravel\Models\Conversation;
20
use LivePersonInc\LiveEngageLaravel\Models\Message;
21
use LivePersonInc\LiveEngageLaravel\Collections\EngagementHistory;
22
use LivePersonInc\LiveEngageLaravel\Collections\Skills;
23
use LivePersonInc\LiveEngageLaravel\Collections\AgentParticipants;
24
use LivePersonInc\LiveEngageLaravel\Exceptions\LiveEngageException;
25
use LivePersonInc\LiveEngageLaravel\Collections\ConversationHistory;
26
27
/**
28
 * LiveEngageLaravel class holds all of the root package functions.
29
 *
30
 * All "setting" functions will return `$this` so method can be chained. Most methods will return a class object or Laravel collection.
31
 */
32
class LiveEngageLaravel
33
{
34
	private $account = false;
35
	private $results = [];
36
	private $skills = [];
37
	private $next = false;
38
	private $prev = false;
39
	private $start;
40
	private $end;
41
	private $config = 'services.liveperson.default';
42
	private $version = '1.0';
43
	private $history_limit = 50;
44
	private $history = false;
45
	private $context = 'interactionHistoryRecords';
46
	private $interactive = true;
47
	private $ended = true;
48
	private $bearer = false;
49
	private $revision = 0;
50
51
	private $domain = false;
52
53
	private $retry_limit = 5;
54
	private $retry_counter = 0;
55
56
	/**
57
	 * __get magic function to retrieve private properties of the class.
58
	 * 
59
	 * @access public
60
	 * @param mixed $attribute
61
	 * @return mixed
62
	 */
63 5
	public function __get($attribute)
64
	{
65 5
		return $this->$attribute;
66
	}
67
68
	/**
69
	 * __construct function.
70
	 * 
71
	 * @access public
72
	 * @return void
73
	 */
74 8
	public function __construct()
75
	{
76 8
		$this->account = config("{$this->config}.account");
77
		//$this->domain = config("{$this->config}.domain");
78 8
		$this->version = config("{$this->config}.version") ?: $this->version;
79 8
	}
80
81
	/**
82
	 * key function sets the keyset the class should use. Setting this once will be stored for script execution, but not for the session.
83
	 * 
84
	 * @access public
85
	 * @param string $key (default: 'default')
86
	 * @return this
87
	 */
88 1
	public function key($key = 'default')
89
	{
90 1
		$this->config = "services.liveperson.$key";
91 1
		$this->__construct();
92
93 1
		return $this;
94
	}
95
	
96 1
	public function nonInteractive()
97
	{
98 1
		$this->interactive = false;
99 1
		return $this;
100
	}
101
	
102 1
	public function active()
103
	{
104 1
		$this->ended = false;
105 1
		return $this;
106
	}
107
108 1
	public function limit($limit)
109
	{
110 1
		$this->history_limit = $limit;
111
112 1
		return $this;
113
	}
114
115 1
	public function retry($limit)
116
	{
117 1
		$this->retry_limit = $limit;
118
119 1
		return $this;
120
	}
121
122 1
	public function account($accountid)
123
	{
124 1
		$this->account = $accountid;
125
126 1
		return $this;
127
	}
128
129
	/**
130
	 * domain function sets the LivePerson domain for the secified service. Like `key` it is set for the execution script, but not session. It must be run each time.
131
	 * 
132
	 * @access public
133
	 * @param mixed $service
134
	 * @return this
135
	 */
136 1
	public function domain($service)
137
	{
138 1
		$response = $this->requestV1("https://api.liveperson.net/api/account/{$this->account}/service/{$service}/baseURI.json?version={$this->version}", 'GET');
139
		
140 1
		$this->domain = $response->baseURI;
141
142 1
		return $this;
143
	}
144
145
	/**
146
	 * visitor function gets or sets visitor attribute information - this only works for CHAT, not messaging.
147
	 * 
148
	 * @access public
149
	 * @param string $visitorID
150
	 * @param string $sessionID
151
	 * @param mixed $setData (default: false)
152
	 * @return mixed
153
	 */
154
	public function visitor($visitorID, $sessionID, $setData = false)
155
	{
156
		$this->domain('smt');
157
158
		if ($setData) {
159
			$url = "https://{$this->domain}/api/account/{$this->account}/monitoring/visitors/{$visitorID}/visits/current/events?v=1&sid={$sessionID}";
160
161
			return $this->requestV1($url, 'POST', $setData);
162
		} else {
163
			$url = "https://{$this->domain}/api/account/{$this->account}/monitoring/visitors/{$visitorID}/visits/current/state?v=1&sid={$sessionID}";
164
165
			return $this->requestV1($url, 'GET');
166
		}
167
	}
168
	
169
	// TODO: Enable server chat api here. I actually may create new class
170
	public function chat()
171
	{
172
		$this->domain('conversationVep');
173
		
174
		$url = "https://{$this->domain}/api/account/{$this->account}/chat/request?v=1&NC=true";
175
		
176
		$args = [
177
			
178
		];
179
		$payload = new Payload($args);
180
		
181
		$response = $this->requestV1($url, 'POST', $payload);
0 ignored issues
show
Bug introduced by
$payload of type LivePersonInc\LiveEngageLaravel\Models\Payload is incompatible with the type array expected by parameter $payload of LivePersonInc\LiveEngage...ageLaravel::requestV1(). ( Ignorable by Annotation )

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

181
		$response = $this->requestV1($url, 'POST', /** @scrutinizer ignore-type */ $payload);
Loading history...
182
		
183
		return $response;
184
	}
185
186 1
	final public function retrieveHistory(Carbon $start, Carbon $end, $url = false)
187
	{
188 1
		$this->domain('engHistDomain');
189
190 1
		$url = $url ?: "https://{$this->domain}/interaction_history/api/account/{$this->account}/interactions/search?limit={$this->history_limit}&offset=0";
191
192 1
		$start_str = $start->toW3cString();
193 1
		$end_str = $end->toW3cString();
194
195 1
		$data = new Payload([
196 1
			'interactive' => $this->interactive,
197 1
			'ended' => $this->ended,
198
			'start' => [
199 1
				'from' => strtotime($start_str) . '000',
200 1
				'to' => strtotime($end_str) . '000',
201
			],
202 1
			'skillIds' => $this->skills
203
		]);
204
205 1
		return $this->requestV1($url, 'POST', $data);
0 ignored issues
show
Bug introduced by
$data of type LivePersonInc\LiveEngageLaravel\Models\Payload is incompatible with the type array expected by parameter $payload of LivePersonInc\LiveEngage...ageLaravel::requestV1(). ( Ignorable by Annotation )

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

205
		return $this->requestV1($url, 'POST', /** @scrutinizer ignore-type */ $data);
Loading history...
206
	}
207
208 1
	final public function retrieveMsgHistory(Carbon $start, Carbon $end, $url = false)
209
	{
210 1
		$this->domain('msgHist');
211
212 1
		$url = $url ?: "https://{$this->domain}/messaging_history/api/account/{$this->account}/conversations/search?limit={$this->history_limit}&offset=0&sort=start:desc";
213
214 1
		$start_str = $start->toW3cString();
215 1
		$end_str = $end->toW3cString();
216
217 1
		$data = new Payload([
218 1
			'status' => $this->ended ? ['CLOSE'] : ['OPEN', 'CLOSE'],
219
			'start' => [
220 1
				'from' => strtotime($start_str) . '000',
221 1
				'to' => strtotime($end_str) . '000',
222
			],
223 1
			'skillIds' => $this->skills
224
		]);
225
		
226 1
		return $this->requestV1($url, 'POST', $data);
0 ignored issues
show
Bug introduced by
$data of type LivePersonInc\LiveEngageLaravel\Models\Payload is incompatible with the type array expected by parameter $payload of LivePersonInc\LiveEngage...ageLaravel::requestV1(). ( Ignorable by Annotation )

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

226
		return $this->requestV1($url, 'POST', /** @scrutinizer ignore-type */ $data);
Loading history...
227
	}
228
	
229
	/**
230
	 * skills function gets collection of skills associated with the account.
231
	 * 
232
	 * @access public
233
	 * @return Collections\Skills
234
	 */
235 1
	public function skills()
236
	{
237 1
		$this->domain('accountConfigReadOnly');
238
		
239 1
		$url = "https://{$this->domain}/api/account/{$this->account}/configuration/le-users/skills?v=4.0";
240
		
241 1
		return new Skills($this->requestV2($url, 'GET'));
242
	}
243
	
244
	/**
245
	 * getSkill function gets skill object based on ID.
246
	 * 
247
	 * @access public
248
	 * @param int $skillId
249
	 * @return Models\Skill
250
	 */
251
	public function getSkill($skillId)
252
	{
253
		$this->domain('accountConfigReadOnly');
254
		
255
		$url = "https://{$this->domain}/api/account/{$this->account}/configuration/le-users/skills/{$skillId}?v=4.0";
256
		
257
		return new Skill((array) $this->requestV2($url, 'GET'));
258
	}
259
	
260
	/**
261
	 * getAgent function gets agent object based on ID.
262
	 * 
263
	 * @access public
264
	 * @param int $userId
265
	 * @return Models\Agent
266
	 */
267
	public function getAgent($userId)
268
	{
269
		$this->domain('accountConfigReadOnly');
270
		
271
		$url = "https://{$this->domain}/api/account/{$this->account}/configuration/le-users/users/{$userId}?v=4.0";
272
		
273
		return new Agent((array) $this->requestV2($url, 'GET'));
274
	}
275
	
276
	public function updateAgent($userId, $properties)
277
	{
278
		$agent = $this->getAgent($userId);
0 ignored issues
show
Unused Code introduced by
The assignment to $agent is dead and can be removed.
Loading history...
279
		
280
		$this->domain('accountConfigReadWrite');
281
		
282
		$url = "https://{$this->domain}/api/account/{$this->account}/configuration/le-users/users/{$userId}?v=4.0";
283
		$headers = [
284
			'X-HTTP-Method-Override' => 'PUT',
285
			'if-Match' => '*'
286
		];
287
		
288
		return new Agent((array) $this->requestV2($url, 'PUT', $properties, $headers));
289
	}
290
	
291
	/**
292
	 * agents function gets collection of agents from account.
293
	 * 
294
	 * @access public
295
	 * @return Collections\AgentParticipants
296
	 */
297 1
	public function agents()
298
	{
299 1
		$this->domain('accountConfigReadOnly');
300
		
301 1
		$select = implode(',', [
302 1
			'id',
303
			'pid',
304
			'deleted',
305
			'loginName',
306
			'skills',
307
			'nickname',
308
			'dateCreated',
309
			'userTypeId',
310
			'isApiUser',
311
			'profileIds',
312
			'permissionGroups',
313
			'allowedAppKeys',
314
			'changePwdNextLogin',
315
			'maxChats',
316
			'skillIds',
317
			'lpaCreatedUser',
318
			'email',
319
			'lobs',
320
			'profiles',
321
			'fullName',
322
			'employeeId',
323
			'managedAgentGroups',
324
			'dateUpdated',
325
			'isEnabled',
326
			'lastPwdChangeDate'
327
		]);
328
		
329 1
		$url = "https://{$this->domain}/api/account/{$this->account}/configuration/le-users/users?v=4.0&select=$select";
330
		
331 1
		return new AgentParticipants($this->requestV2($url, 'GET'));
332
	}
333
	
334
	/**
335
	 * getAgentStatus function gets status of agents based on provided Skill IDs.
336
	 * 
337
	 * @access public
338
	 * @param int/array $skills
0 ignored issues
show
Documentation Bug introduced by
The doc comment int/array at position 0 could not be parsed: Unknown type name 'int/array' at position 0 in int/array.
Loading history...
339
	 * @return Collections\AgentParticipants
340
	 */
341 1
	public function getAgentStatus($skills)
342
	{
343 1
		$skills = is_array($skills) ? $skills : [$skills];
344
	
345 1
		$this->domain('msgHist');
346
		
347 1
		$url = "https://{$this->domain}/messaging_history/api/account/{$this->account}/agent-view/status";
348
		
349 1
		$data = ['skillIds' => $skills];
350
		
351 1
		$response = $this->requestV1($url, 'POST', $data);
352 1
		$collection = new AgentParticipants($response->agentStatusRecords);
353 1
		$collection->metaData = new MetaData((array) $response->_metadata);
354
		
355 1
		return $collection;
356
		
357
	}
358
	
359
	/**
360
	 * conversationHistory function.
361
	 * 
362
	 * @access public
363
	 * @param Carbon $start (default: null)
364
	 * @param Carbon $end (default: null)
365
	 * @param int/array $skills (default: [])
0 ignored issues
show
Documentation Bug introduced by
The doc comment int/array at position 0 could not be parsed: Unknown type name 'int/array' at position 0 in int/array.
Loading history...
366
	 * @return Collections\ConversationHistory
367
	 */
368 1
	public function conversationHistory(Carbon $start = null, Carbon $end = null, $skills = [])
369
	{
370 1
		$this->retry_counter = 0;
371 1
		$this->skills = $skills;
372
373 1
		$start = $start ?: (new Carbon())->today();
374 1
		$end = $end ?: (new Carbon())->today()->addHours(23)->addMinutes(59);
375
376 1
		$results_object = $this->retrieveMsgHistory($start, $end);
377
		
378 1
		$results_object->_metadata->start = $start;
379 1
		$results_object->_metadata->end = $end;
380
	
381 1
		$meta = new MetaData((array) $results_object->_metadata);
382
		
383 1
		$collection = new ConversationHistory($results_object->conversationHistoryRecords);
384 1
		$collection->metaData = $meta;
385
		
386 1
		return $collection;
387
			
388
	}
389
	
390
	/**
391
	 * getConversation function.
392
	 * 
393
	 * @access public
394
	 * @param mixed $conversationId
395
	 * @return Models\Conversation
396
	 */
397
	public function getConversation($conversationId)
398
	{
399
		$this->domain('msgHist');
400
		
401
		$url = "https://{$this->domain}/messaging_history/api/account/{$this->account}/conversations/conversation/search";
402
		
403
		$data = new Payload([
404
			'conversationId' => $conversationId
405
		]);
406
		
407
		$result = $this->requestV1($url, 'POST', $data);
0 ignored issues
show
Bug introduced by
$data of type LivePersonInc\LiveEngageLaravel\Models\Payload is incompatible with the type array expected by parameter $payload of LivePersonInc\LiveEngage...ageLaravel::requestV1(). ( Ignorable by Annotation )

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

407
		$result = $this->requestV1($url, 'POST', /** @scrutinizer ignore-type */ $data);
Loading history...
408
		if (!count($result->conversationHistoryRecords)) {
409
			return null;
410
		}
411
		
412
		return new Conversation((array) $result->conversationHistoryRecords[0]);
413
	}
414
415
	/**
416
	 * engagementHistory function.
417
	 * 
418
	 * @access public
419
	 * @param Carbon $start (default: null)
420
	 * @param Carbon $end (default: null)
421
	 * @param int/array $skills (default: [])
0 ignored issues
show
Documentation Bug introduced by
The doc comment int/array at position 0 could not be parsed: Unknown type name 'int/array' at position 0 in int/array.
Loading history...
422
	 * @return Collections\EngagementHistory
423
	 */
424 1
	public function engagementHistory(Carbon $start = null, Carbon $end = null, $skills = [])
425
	{
426 1
		$this->retry_counter = 0;
427 1
		$this->skills = $skills;
428
429 1
		$start = $start ?: (new Carbon())->today();
430 1
		$end = $end ?: (new Carbon())->today()->addHours(23)->addMinutes(59);
431
432 1
		$results_object = $this->retrieveHistory($start, $end);
433
		
434 1
		$results_object->_metadata->start = $start;
435 1
		$results_object->_metadata->end = $end;
436
	
437 1
		$meta = new MetaData((array) $results_object->_metadata);
438
		
439 1
		$collection = new EngagementHistory($results_object->interactionHistoryRecords);
440 1
		$collection->metaData = $meta;
441
		
442 1
		return $collection;
443
			
444
	}
445
	
446
	/**
447
	 * status function gets status of the account.
448
	 * 
449
	 * @access public
450
	 * @return Models\AccountStatus
451
	 */
452 1
	public function status()
453
	{
454 1
		$url = "https://status.liveperson.com/json?site={$this->account}";
455
		
456 1
		$response = $this->requestV1($url, 'GET');
457
		
458 1
		return new AccountStatus((array) $response);
459
	}
460
	
461
	public function login()
462
	{
463
		$this->domain('agentVep');
464
		
465
		$consumer_key = config("{$this->config}.key");
466
		$consumer_secret = config("{$this->config}.secret");
467
		$token = config("{$this->config}.token");
468
		$secret = config("{$this->config}.token_secret");
469
		$username = config("{$this->config}.user_name");
470
		
471
		$auth = [
472
			'username'		  => $username,
473
			'appKey'			=> $consumer_key,
474
			'secret'			=> $consumer_secret,
475
			'accessToken'		=> $token,
476
			'accessTokenSecret' => $secret,
477
		];
478
		
479
		$url = "https://{$this->domain}/api/account/{$this->account}/login?v=1.3";
480
		
481
		$response = $this->requestV1($url, 'POST', $auth);
482
		
483
		$this->bearer = $response->bearer;
484
		
485
		return $this;
486
	}
487
	
488
	private function requestV2($url, $method, $payload = [], $headers = [])
489
	{
490
		$this->login();
491
		
492
		$client = new Client();
493
		$args = [
494
			'headers' => array_merge([
495
				'content-type' => 'application/json',
496
				'Authorization' => 'Bearer ' . $this->bearer
497
			], $headers),
498
			'body' => json_encode($payload)
499
		];
500
		
501
		try {
502
			$res = $client->request($method, $url, $args);
503
		} catch (\Exception $e) {
504
			throw $e;
505
		} 
506
		
507
		return json_decode($res->getBody());
508
	}
509
	
510 2
	private function requestClient()
511
	{
512 2
		$consumer_key = config("{$this->config}.key");
513 2
		$consumer_secret = config("{$this->config}.secret");
514 2
		$token = config("{$this->config}.token");
515 2
		$secret = config("{$this->config}.token_secret");
516
517 2
		$stack = HandlerStack::create();
518 2
		$auth = new Oauth1([
519 2
			'consumer_key'	=> $consumer_key,
520 2
			'consumer_secret' => $consumer_secret,
521 2
			'token'		   => $token,
522 2
			'token_secret'	=> $secret,
523 2
			'signature_method'=> Oauth1::SIGNATURE_METHOD_HMAC,
524
		]);
525 2
		$stack->push($auth);
526
527 2
		$client = new Client([
528 2
			'handler' => $stack,
529
		]);
530
		
531 2
		return $client;
532
	}
533
	
534
	/**
535
	 * requestV1 - request bootstrap for older oauth supported APIs.
536
	 * 
537
	 * @access private
538
	 * @param string $url
539
	 * @param string $method
540
	 * @param array $payload (default: [])
541
	 * @return mixed
542
	 */
543 1
	private function requestV1($url, $method, $payload = [])
544
	{
545 1
		$client = $this->requestClient();
546
547
		$args = [
548 1
			'auth' => 'oauth',
549
			'headers' => [
550
				'content-type' => 'application/json',
551
			],
552 1
			'body' => json_encode($payload)
553
		];
554
555
		try {
556 1
			$res = $client->request($method, $url, $args);
557 1
			$response = json_decode($res->getBody());
558
		} catch (\Exception $e) {
559
			if ($this->retry_counter < $this->retry_limit || $this->retry_limit == -1) {
560
				usleep(1500);
561
				$this->retry_counter++;
562
				$response = $this->requestV1($url, $payload);
0 ignored issues
show
Bug introduced by
$payload of type array is incompatible with the type string expected by parameter $method of LivePersonInc\LiveEngage...ageLaravel::requestV1(). ( Ignorable by Annotation )

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

562
				$response = $this->requestV1($url, /** @scrutinizer ignore-type */ $payload);
Loading history...
563
			} else {
564
				throw $e; //new LiveEngageException("Retry limit has been exceeded ($this->retry_limit)", 100);
565
			}
566
		}
567
568 1
		return $response;
569
	}
570
}
571