|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace One; |
|
4
|
|
|
|
|
5
|
|
|
use Guzzle\Http\Client; |
|
6
|
|
|
use Guzzle\Http\Exception\BadResponseException; |
|
7
|
|
|
use Guzzle\Http\Exception\ClientErrorResponseException; |
|
8
|
|
|
use Guzzle\Http\Message\RequestInterface; |
|
9
|
|
|
use One\Model\Article; |
|
10
|
|
|
use One\Model\Model; |
|
11
|
|
|
use Psr\Log\LoggerAwareInterface; |
|
12
|
|
|
use Psr\Log\LoggerInterface; |
|
13
|
|
|
|
|
14
|
|
|
/** |
|
15
|
|
|
* Publisher class |
|
16
|
|
|
* main class to be used that interfacing to the API |
|
17
|
|
|
*/ |
|
18
|
|
|
class Publisher implements LoggerAwareInterface { |
|
19
|
|
|
const DEFAULT_MAX_ATTEMPT = 4; |
|
20
|
|
|
|
|
21
|
|
|
const REST_SERVER = 'https://dev.one.co.id'; |
|
22
|
|
|
const AUTHENTICATION = '/oauth/token'; |
|
23
|
|
|
const ARTICLE_CHECK_ENDPOINT = '/api/article'; |
|
24
|
|
|
const ARTICLE_ENDPOINT = '/api/publisher/article'; |
|
25
|
|
|
|
|
26
|
|
|
/** |
|
27
|
|
|
* attachment url destination |
|
28
|
|
|
* |
|
29
|
|
|
* @var array |
|
30
|
|
|
*/ |
|
31
|
|
|
private $attachmentUrl; |
|
32
|
|
|
|
|
33
|
|
|
/** |
|
34
|
|
|
* Logger variable, if set log activity to this obejct each time sending request and receiving response |
|
35
|
|
|
* |
|
36
|
|
|
* @var \Psr\Log\LoggerInterface |
|
37
|
|
|
*/ |
|
38
|
|
|
private $logger = null; |
|
39
|
|
|
|
|
40
|
|
|
/** |
|
41
|
|
|
* credentials props |
|
42
|
|
|
* |
|
43
|
|
|
* @var string $clientId |
|
44
|
|
|
* @var string $clientSecret |
|
45
|
|
|
*/ |
|
46
|
|
|
private $clientId; |
|
47
|
|
|
private $clientSecret; |
|
48
|
|
|
|
|
49
|
|
|
/** |
|
50
|
|
|
* Oauth access token response |
|
51
|
|
|
* |
|
52
|
|
|
* @var string $accessToken |
|
53
|
|
|
*/ |
|
54
|
|
|
private $accessToken = null; |
|
55
|
|
|
|
|
56
|
|
|
/** |
|
57
|
|
|
* publisher custom options |
|
58
|
|
|
* |
|
59
|
|
|
* @var \One\Collection $options |
|
60
|
|
|
*/ |
|
61
|
|
|
private $options; |
|
62
|
|
|
|
|
63
|
|
|
/** |
|
64
|
|
|
* http transaction Client |
|
65
|
|
|
* |
|
66
|
|
|
* @var \Guzzle\Http\Client |
|
67
|
|
|
*/ |
|
68
|
|
|
private $httpClient; |
|
69
|
|
|
|
|
70
|
|
|
/** |
|
71
|
|
|
* constructor |
|
72
|
|
|
* |
|
73
|
|
|
* @param string $clientId |
|
74
|
|
|
* @param string $clientSecret |
|
75
|
|
|
* @param array $options |
|
76
|
|
|
*/ |
|
77
|
|
|
public function __construct($clientId, $clientSecret, $options = array()) { |
|
78
|
|
|
$this->clientId = $clientId; |
|
79
|
|
|
$this->clientSecret = $clientSecret; |
|
80
|
|
|
|
|
81
|
|
|
$this->assessOptions($options); |
|
82
|
|
|
|
|
83
|
|
|
$this->attachmentUrl = array( |
|
84
|
|
|
Article::ATTACHMENT_FIELD_GALLERY => self::ARTICLE_ENDPOINT . '/{article_id}/gallery', |
|
85
|
|
|
Article::ATTACHMENT_FIELD_PAGE => self::ARTICLE_ENDPOINT . '/{article_id}/page', |
|
86
|
|
|
Article::ATTACHMENT_FIELD_PHOTO => self::ARTICLE_ENDPOINT . '/{article_id}/photo', |
|
87
|
|
|
Article::ATTACHMENT_FIELD_VIDEO => self::ARTICLE_ENDPOINT . '/{article_id}/video', |
|
88
|
|
|
); |
|
89
|
|
|
} |
|
90
|
|
|
|
|
91
|
|
|
/** |
|
92
|
|
|
* recycleToken from callback. If use external token storage could leveraged on this |
|
93
|
|
|
* |
|
94
|
|
|
* @param \Closure $tokenProducer |
|
95
|
|
|
* @return self |
|
96
|
|
|
*/ |
|
97
|
|
|
public function recycleToken(\Closure $tokenProducer) { |
|
98
|
|
|
return $this->setAuthorizationHeader($tokenProducer()); |
|
99
|
|
|
} |
|
100
|
|
|
|
|
101
|
|
|
/** |
|
102
|
|
|
* assessing and custom option |
|
103
|
|
|
* |
|
104
|
|
|
* @param array $options |
|
105
|
|
|
* @return void |
|
106
|
|
|
*/ |
|
107
|
|
|
private function assessOptions($options) { |
|
108
|
|
|
$defaultOptions = array( |
|
109
|
|
|
'rest_server' => self::REST_SERVER, |
|
110
|
|
|
'auth_url' => self::AUTHENTICATION, |
|
111
|
|
|
'max_attempt' => self::DEFAULT_MAX_ATTEMPT, |
|
112
|
|
|
'default_headers' => array( |
|
113
|
|
|
"Accept" => "application/json", |
|
114
|
|
|
), |
|
115
|
|
|
); |
|
116
|
|
|
|
|
117
|
|
|
$this->options = new Collection( |
|
118
|
|
|
array_merge( |
|
119
|
|
|
$defaultOptions, |
|
120
|
|
|
$options |
|
121
|
|
|
) |
|
122
|
|
|
); |
|
123
|
|
|
|
|
124
|
|
|
if (isset($options['access_token'])) { |
|
125
|
|
|
$this->setAuthorizationHeader($options['access_token']); |
|
126
|
|
|
} |
|
127
|
|
|
|
|
128
|
|
|
$this->httpClient = new Client( |
|
129
|
|
|
$this->options->get('rest_server') |
|
130
|
|
|
); |
|
131
|
|
|
} |
|
132
|
|
|
|
|
133
|
|
|
/** |
|
134
|
|
|
* one gate menu for request creation. |
|
135
|
|
|
* |
|
136
|
|
|
* @param string $method |
|
137
|
|
|
* @param string $path |
|
138
|
|
|
* @param \One\Collection|array $header |
|
139
|
|
|
* @param \One\Collection|array $body |
|
140
|
|
|
* @param array $options |
|
141
|
|
|
* @return string |
|
142
|
|
|
*/ |
|
143
|
|
|
private function requestGate($method, $path, $header = array(), $body = array(), $options = array()) { |
|
144
|
|
|
if (empty($this->accessToken)) { |
|
145
|
|
|
$this->renewAuthToken(); |
|
146
|
|
|
} |
|
147
|
|
|
|
|
148
|
|
|
return (string) $this->sendRequest( |
|
149
|
|
|
$this->httpClient->createRequest( |
|
150
|
|
|
$method, |
|
151
|
|
|
$path, |
|
152
|
|
|
array_merge( |
|
153
|
|
|
$this->options->get('default_headers'), |
|
154
|
|
|
$header |
|
155
|
|
|
), |
|
156
|
|
|
$body, |
|
|
|
|
|
|
157
|
|
|
$options |
|
158
|
|
|
) |
|
159
|
|
|
); |
|
160
|
|
|
} |
|
161
|
|
|
|
|
162
|
|
|
/** |
|
163
|
|
|
* actually send request created here, separated for easier attempt count and handling exception |
|
164
|
|
|
* |
|
165
|
|
|
* @param \Guzzle\Http\Message\RequestInterface $request |
|
166
|
|
|
* @param integer $attempt |
|
167
|
|
|
* @return \Guzzle\Http\EntityBodyInterface|string|null |
|
168
|
|
|
* @throws \Exception |
|
169
|
|
|
* @throws \Guzzle\Http\Exception\ClientErrorResponseException |
|
170
|
|
|
* @throws \Guzzle\Http\Exception\BadResponseException |
|
171
|
|
|
*/ |
|
172
|
|
|
private function sendRequest(RequestInterface $request, $attempt = 0) { |
|
173
|
|
|
if ($attempt >= $this->options->get('max_attempt')) { |
|
174
|
|
|
throw new \Exception("MAX attempt reached for " . $request->getUrl() . " with payload " . (string) $request); |
|
175
|
|
|
} |
|
176
|
|
|
|
|
177
|
|
|
try { |
|
178
|
|
|
$response = $request->send(); |
|
179
|
|
|
if ($response->getStatusCode() == 200) { |
|
180
|
|
|
return $response->getBody(); |
|
181
|
|
|
} |
|
182
|
|
|
if ($response->getStatusCode() == 429) { |
|
183
|
|
|
$this->renewAuthToken(); |
|
184
|
|
|
} |
|
185
|
|
|
|
|
186
|
|
|
return $this->sendRequest($request, $attempt++); |
|
187
|
|
|
} catch (ClientErrorResponseException $err) { |
|
188
|
|
|
if ($err->getResponse()->getStatusCode() == 429) { |
|
189
|
|
|
$this->renewAuthToken(); |
|
190
|
|
|
return $this->sendRequest($err->getRequest(), $attempt++); |
|
191
|
|
|
} |
|
192
|
|
|
|
|
193
|
|
|
throw $err; |
|
194
|
|
|
} catch (\Exception $err) { |
|
195
|
|
|
throw $err; |
|
196
|
|
|
} |
|
197
|
|
|
} |
|
198
|
|
|
|
|
199
|
|
|
/** |
|
200
|
|
|
* renewing access_token |
|
201
|
|
|
* |
|
202
|
|
|
* @return self |
|
203
|
|
|
* @throws \Exception |
|
204
|
|
|
*/ |
|
205
|
|
|
private function renewAuthToken() { |
|
206
|
|
|
$token = (string) $this->sendRequest( |
|
207
|
|
|
$this->httpClient->post( |
|
208
|
|
|
self::AUTHENTICATION, |
|
209
|
|
|
$this->options->get('default_headers'), |
|
210
|
|
|
array( |
|
211
|
|
|
"grant_type" => "client_credentials", |
|
212
|
|
|
"client_id" => $this->clientId, |
|
213
|
|
|
"client_secret" => $this->clientSecret, |
|
214
|
|
|
) |
|
215
|
|
|
) |
|
216
|
|
|
); |
|
217
|
|
|
|
|
218
|
|
|
$token = json_decode($token, true); |
|
219
|
|
|
|
|
220
|
|
|
if (empty($token)) { |
|
221
|
|
|
throw new \Exception("Access token request return empty response"); |
|
222
|
|
|
} |
|
223
|
|
|
|
|
224
|
|
|
return $this->setAuthorizationHeader( |
|
225
|
|
|
$token['access_token'] |
|
226
|
|
|
); |
|
227
|
|
|
} |
|
228
|
|
|
|
|
229
|
|
|
/** |
|
230
|
|
|
* set header for OAuth 2.0 |
|
231
|
|
|
* |
|
232
|
|
|
* @param string $accessToken |
|
233
|
|
|
* @return self |
|
234
|
|
|
*/ |
|
235
|
|
|
private function setAuthorizationHeader($accessToken) { |
|
236
|
|
|
$this->accessToken = $accessToken; |
|
237
|
|
|
|
|
238
|
|
|
$this->options->set( |
|
239
|
|
|
'default_headers', |
|
240
|
|
|
array_merge( |
|
241
|
|
|
$this->options->get('default_headers'), |
|
242
|
|
|
array( |
|
243
|
|
|
"Authorization" => "Bearer " . $accessToken, |
|
244
|
|
|
) |
|
245
|
|
|
) |
|
246
|
|
|
); |
|
247
|
|
|
|
|
248
|
|
|
return $this; |
|
249
|
|
|
} |
|
250
|
|
|
|
|
251
|
|
|
/** |
|
252
|
|
|
* get Attachment Submission url Endpoint at rest API |
|
253
|
|
|
* |
|
254
|
|
|
* @param string $idArticle |
|
255
|
|
|
* @param string $field |
|
256
|
|
|
* @return string |
|
257
|
|
|
*/ |
|
258
|
|
|
private function getAttachmentEndPoint($idArticle, $field) { |
|
259
|
|
|
return $this->replaceEndPointId( |
|
260
|
|
|
$idArticle, |
|
261
|
|
|
$this->attachmentUrl[$field] |
|
262
|
|
|
); |
|
263
|
|
|
} |
|
264
|
|
|
|
|
265
|
|
|
/** |
|
266
|
|
|
* get article endpoint for deleting api |
|
267
|
|
|
* |
|
268
|
|
|
* @param string $identifier |
|
269
|
|
|
* @return string |
|
270
|
|
|
*/ |
|
271
|
|
|
private function getArticleWithIdEndPoint($identifier) { |
|
272
|
|
|
return self::ARTICLE_ENDPOINT . "/$identifier"; |
|
273
|
|
|
} |
|
274
|
|
|
|
|
275
|
|
|
/** |
|
276
|
|
|
* function that actually replace article_id inside endpoint pattern |
|
277
|
|
|
* |
|
278
|
|
|
* @param string $identifier |
|
279
|
|
|
* @param string $url |
|
280
|
|
|
* @return string |
|
281
|
|
|
*/ |
|
282
|
|
|
private function replaceEndPointId($identifier, $url) { |
|
283
|
|
|
return str_replace( |
|
284
|
|
|
'{article_id}', |
|
285
|
|
|
$identifier, |
|
286
|
|
|
$url |
|
287
|
|
|
); |
|
288
|
|
|
} |
|
289
|
|
|
|
|
290
|
|
|
/** |
|
291
|
|
|
* normalizing payload. not yet implemented totally, currently just bypass a toArray() function from collection. |
|
292
|
|
|
* |
|
293
|
|
|
* @param \One\Collection $collection |
|
294
|
|
|
* @return array |
|
295
|
|
|
*/ |
|
296
|
|
|
private function normalizePayload(Collection $collection) { |
|
297
|
|
|
return $collection->toArray(); |
|
298
|
|
|
} |
|
299
|
|
|
|
|
300
|
|
|
/** |
|
301
|
|
|
* submitting article here, return new Object cloned from original |
|
302
|
|
|
* |
|
303
|
|
|
* @param \One\Model\Article $article |
|
304
|
|
|
* @return \One\Model\Article |
|
305
|
|
|
*/ |
|
306
|
|
|
public function submitArticle(Article $article) { |
|
307
|
|
|
$responseArticle = $this->post( |
|
308
|
|
|
self::ARTICLE_ENDPOINT, |
|
309
|
|
|
$this->normalizePayload( |
|
310
|
|
|
$article->getCollection() |
|
311
|
|
|
) |
|
312
|
|
|
); |
|
313
|
|
|
|
|
314
|
|
|
$responseArticle = json_decode($responseArticle, true); |
|
315
|
|
|
$article->setId($responseArticle['data']['id']); |
|
316
|
|
|
|
|
317
|
|
|
foreach (Article::getPossibleAttachment() as $field) { |
|
318
|
|
|
if ($article->hasAttachment($field)) { |
|
319
|
|
|
foreach ($article->getAttachmentByField($field) as $attachment) { |
|
320
|
|
|
$this->submitAttachment( |
|
321
|
|
|
$article->getId(), |
|
322
|
|
|
$attachment, |
|
323
|
|
|
$field |
|
324
|
|
|
); |
|
325
|
|
|
} |
|
326
|
|
|
} |
|
327
|
|
|
} |
|
328
|
|
|
|
|
329
|
|
|
return $article; |
|
330
|
|
|
} |
|
331
|
|
|
|
|
332
|
|
|
/** |
|
333
|
|
|
* submit each attachment of an article here |
|
334
|
|
|
* |
|
335
|
|
|
* @param string $idArticle |
|
336
|
|
|
* @param \One\Model\Model $attachment |
|
337
|
|
|
* @param string $field |
|
338
|
|
|
* @return array |
|
339
|
|
|
*/ |
|
340
|
|
|
public function submitAttachment($idArticle, Model $attachment, $field) { |
|
341
|
|
|
return json_decode( |
|
342
|
|
|
$this->post( |
|
343
|
|
|
$this->getAttachmentEndPoint($idArticle, $field), |
|
344
|
|
|
$this->normalizePayload( |
|
345
|
|
|
$attachment->getCollection() |
|
346
|
|
|
) |
|
347
|
|
|
), |
|
348
|
|
|
true |
|
349
|
|
|
); |
|
350
|
|
|
} |
|
351
|
|
|
|
|
352
|
|
|
/** |
|
353
|
|
|
* get article from rest API |
|
354
|
|
|
* |
|
355
|
|
|
* @param string $idArticle |
|
356
|
|
|
* @return string json |
|
357
|
|
|
*/ |
|
358
|
|
|
public function getArticle($idArticle) { |
|
359
|
|
|
return $this->get( |
|
360
|
|
|
self::ARTICLE_CHECK_ENDPOINT . "/$idArticle" |
|
361
|
|
|
); |
|
362
|
|
|
} |
|
363
|
|
|
|
|
364
|
|
|
/** |
|
365
|
|
|
* get list article by publisher |
|
366
|
|
|
* |
|
367
|
|
|
* @return string json |
|
368
|
|
|
*/ |
|
369
|
|
|
public function listArticle() { |
|
370
|
|
|
return $this->get( |
|
371
|
|
|
self::ARTICLE_ENDPOINT |
|
372
|
|
|
); |
|
373
|
|
|
} |
|
374
|
|
|
|
|
375
|
|
|
/** |
|
376
|
|
|
* delete article based on id |
|
377
|
|
|
* |
|
378
|
|
|
* @param string $idArticle |
|
379
|
|
|
* @return string |
|
380
|
|
|
*/ |
|
381
|
|
|
public function deleteArticle($idArticle) { |
|
382
|
|
|
$articleOnRest = $this->getArticle($idArticle); |
|
383
|
|
|
|
|
384
|
|
|
if (!empty($articleOnRest)) { |
|
385
|
|
|
$articleOnRest = json_decode($articleOnRest, true); |
|
386
|
|
|
|
|
387
|
|
|
if (isset($articleOnRest['data'])) { |
|
388
|
|
|
foreach (Article::getDeleteableAttachment() as $field) { |
|
389
|
|
|
if (isset($articleOnRest['data'][$field])) { |
|
390
|
|
|
foreach ($articleOnRest['data'][$field] as $attachment) { |
|
391
|
|
|
if (isset($attachment[$field . '_order'])) { |
|
392
|
|
|
$this->deleteAttachment($idArticle, $field, $attachment[$field . '_order']); |
|
393
|
|
|
} |
|
394
|
|
|
} |
|
395
|
|
|
} |
|
396
|
|
|
} |
|
397
|
|
|
} |
|
398
|
|
|
|
|
399
|
|
|
return $this->delete( |
|
400
|
|
|
$this->getArticleWithIdEndPoint($idArticle) |
|
401
|
|
|
); |
|
402
|
|
|
} |
|
403
|
|
|
} |
|
404
|
|
|
|
|
405
|
|
|
/** |
|
406
|
|
|
* delete attachment of an article |
|
407
|
|
|
* |
|
408
|
|
|
* @param string $idArticle |
|
409
|
|
|
* @param string $field |
|
410
|
|
|
* @param string $order |
|
411
|
|
|
* @return string |
|
412
|
|
|
*/ |
|
413
|
|
|
public function deleteAttachment($idArticle, $field, $order) { |
|
414
|
|
|
return $this->delete( |
|
415
|
|
|
$this->getAttachmentEndPoint($idArticle, $field) . "/$order" |
|
416
|
|
|
); |
|
417
|
|
|
} |
|
418
|
|
|
|
|
419
|
|
|
/** |
|
420
|
|
|
* get proxy |
|
421
|
|
|
* |
|
422
|
|
|
* @param string $path |
|
423
|
|
|
* @param \One\Collection|array $header |
|
424
|
|
|
* @param array $options |
|
425
|
|
|
* @return string |
|
426
|
|
|
*/ |
|
427
|
|
View Code Duplication |
final public function get($path, $header = array(), $options = array()) { |
|
|
|
|
|
|
428
|
|
|
if (isset($this->logger) && !is_null($this->logger)) { |
|
429
|
|
|
$this->logger->info("Hi, from post, path is " . $path); |
|
430
|
|
|
} |
|
431
|
|
|
return $this->requestGate( |
|
432
|
|
|
'GET', |
|
433
|
|
|
$path, |
|
434
|
|
|
$header, |
|
435
|
|
|
array(), |
|
436
|
|
|
$options |
|
437
|
|
|
); |
|
438
|
|
|
} |
|
439
|
|
|
|
|
440
|
|
|
/** |
|
441
|
|
|
* post proxy |
|
442
|
|
|
* |
|
443
|
|
|
* @param string $path |
|
444
|
|
|
* @param \One\Collection|array $body |
|
445
|
|
|
* @param \One\Collection|array $header |
|
446
|
|
|
* @param array $options |
|
447
|
|
|
* @return string |
|
448
|
|
|
*/ |
|
449
|
|
View Code Duplication |
final public function post($path, $body, $header = array(), $options = array()) { |
|
|
|
|
|
|
450
|
|
|
|
|
451
|
|
|
if (isset($this->logger) && !is_null($this->logger)) { |
|
452
|
|
|
$this->logger->info("Hi, from post " . $path); |
|
453
|
|
|
} |
|
454
|
|
|
return $this->requestGate( |
|
455
|
|
|
'POST', |
|
456
|
|
|
$path, |
|
457
|
|
|
$header, |
|
458
|
|
|
$body, |
|
459
|
|
|
$options |
|
460
|
|
|
); |
|
461
|
|
|
} |
|
462
|
|
|
|
|
463
|
|
|
/** |
|
464
|
|
|
* delete proxy |
|
465
|
|
|
* |
|
466
|
|
|
* @param string $path |
|
467
|
|
|
* @param \One\Collection|array $body |
|
468
|
|
|
* @param \One\Collection|array $header |
|
469
|
|
|
* @param array $options |
|
470
|
|
|
* @return string |
|
471
|
|
|
*/ |
|
472
|
|
|
final public function delete($path, $body = array(), $header = array(), $options = array()) { |
|
473
|
|
|
return $this->requestGate( |
|
474
|
|
|
'DELETE', |
|
475
|
|
|
$path, |
|
476
|
|
|
$header, |
|
477
|
|
|
$body, |
|
478
|
|
|
$options |
|
479
|
|
|
); |
|
480
|
|
|
} |
|
481
|
|
|
|
|
482
|
|
|
/** |
|
483
|
|
|
* @inheritDoc |
|
484
|
|
|
*/ |
|
485
|
|
|
public function setLogger(LoggerInterface $logger) { |
|
486
|
|
|
$this->logger = $logger; |
|
487
|
|
|
} |
|
488
|
|
|
|
|
489
|
|
|
} |
|
490
|
|
|
|
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.