Skip to content

Commit 8513c02

Browse files
committed
Clients and settings
1 parent e83d407 commit 8513c02

File tree

3 files changed

+206
-0
lines changed

3 files changed

+206
-0
lines changed

src/ApiSettings.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace ApiClients\Twitter;
4+
5+
use ApiClients\Foundation\Hydrator\Options as HydratorOptions;
6+
use ApiClients\Foundation\Oauth1\Middleware\Oauth1Middleware;
7+
use ApiClients\Foundation\Options;
8+
use ApiClients\Foundation\Transport\Middleware\JsonDecodeMiddleware;
9+
use ApiClients\Foundation\Transport\Options as TransportOptions;
10+
use ApiClients\Foundation\Transport\UserAgentStrategies;
11+
use JacobKiers\OAuth\Consumer\Consumer;
12+
use JacobKiers\OAuth\Token\Token;
13+
14+
class ApiSettings
15+
{
16+
const NAMESPACE = 'ApiClients\\Twitter\\Resource';
17+
18+
const TRANSPORT_OPTIONS = [
19+
Options::HYDRATOR_OPTIONS => [
20+
HydratorOptions::NAMESPACE => self::NAMESPACE,
21+
HydratorOptions::NAMESPACE_DIR => __DIR__ . DIRECTORY_SEPARATOR . 'Resource' . DIRECTORY_SEPARATOR,
22+
],
23+
Options::TRANSPORT_OPTIONS => [
24+
TransportOptions::HOST => 'api.twitter.com',
25+
TransportOptions::PATH => '/1.1/',
26+
TransportOptions::USER_AGENT_STRATEGY => UserAgentStrategies::PACKAGE_VERSION,
27+
TransportOptions::PACKAGE => 'api-clients/twitter',
28+
],
29+
];
30+
31+
public static function getOptions(
32+
string $consumerKey,
33+
string $consumerSecret,
34+
string $accessToken,
35+
string $accessTokenSecret,
36+
string $suffix
37+
): array {
38+
$options = self::TRANSPORT_OPTIONS;
39+
$options[Options::HYDRATOR_OPTIONS][HydratorOptions::NAMESPACE_SUFFIX] = $suffix;
40+
$options['auth'] = [
41+
'consumer' => [
42+
'key' => $consumerKey,
43+
'secret' => $consumerSecret,
44+
],
45+
'access_token' => [
46+
'token' => $accessToken,
47+
'secret' => $accessTokenSecret,
48+
],
49+
];
50+
$options[Options::TRANSPORT_OPTIONS][TransportOptions::MIDDLEWARE] = [
51+
Oauth1Middleware::class,
52+
JsonDecodeMiddleware::class,
53+
];
54+
$options[Options::TRANSPORT_OPTIONS][TransportOptions::DEFAULT_REQUEST_OPTIONS] = [
55+
Oauth1Middleware::class => [
56+
\ApiClients\Foundation\Oauth1\Options::CONSUMER => new Consumer($options['auth']['consumer']['key'], $options['auth']['consumer']['secret']),
57+
\ApiClients\Foundation\Oauth1\Options::TOKEN => new Token($options['auth']['access_token']['token'], $options['auth']['access_token']['secret']),
58+
],
59+
];
60+
return $options;
61+
}
62+
}

src/AsyncClient.php

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace ApiClients\Twitter;
4+
5+
use ApiClients\Foundation\Factory;
6+
use ApiClients\Foundation\Hydrator\CommandBus\Command\HydrateCommand;
7+
use ApiClients\Foundation\Client;
8+
use ApiClients\Foundation\Transport\CommandBus\Command\RequestCommand;
9+
use ApiClients\Foundation\Transport\CommandBus\Command\StreamingRequestCommand;
10+
use GuzzleHttp\Psr7\Request;
11+
use JacobKiers\OAuth\Consumer\Consumer;
12+
use JacobKiers\OAuth\Token\Token;
13+
use Psr\Http\Message\RequestInterface;
14+
use Psr\Http\Message\ResponseInterface;
15+
use React\EventLoop\LoopInterface;
16+
use React\Promise\PromiseInterface;
17+
use function React\Promise\resolve;
18+
use Rx\Extra\Operator\CutOperator;
19+
use Rx\Observable;
20+
use Rx\React\Promise;
21+
22+
class AsyncClient
23+
{
24+
const STREAM_DELIMITER = "\r\n";
25+
26+
/**
27+
* @var Client
28+
*/
29+
protected $transport;
30+
31+
public function __construct(
32+
string $consumerKey,
33+
string $consumerSecret,
34+
string $accessToken,
35+
string $accessTokenSecret,
36+
LoopInterface $loop,
37+
Client $client = null
38+
) {
39+
if (!($client instanceof Client)) {
40+
$this->options = ApiSettings::getOptions($consumerKey, $consumerSecret, $accessToken, $accessTokenSecret, 'Async');
41+
$client = Factory::create($loop, $this->options);
42+
}
43+
$this->client = $client;
44+
$this->consumer = new Consumer($consumerKey, $consumerSecret);
45+
$this->token = new Token($accessToken, $accessTokenSecret);
46+
}
47+
48+
public function user(string $user): PromiseInterface
49+
{
50+
return $this->client->handle(new RequestCommand(
51+
new Request('GET', 'users/show.json?screen_name=' . $user)
52+
))->then(function (ResponseInterface $response) {
53+
return resolve($this->client->handle(new HydrateCommand('User', $response->getBody()->getJson())));
54+
});
55+
}
56+
57+
public function sampleStream(): Observable
58+
{
59+
return $this->stream(
60+
new Request('GET', 'https://stream.twitter.com/1.1/statuses/sample.json')
61+
);
62+
}
63+
64+
public function filteredStream(array $filter = []): Observable
65+
{
66+
$postData = http_build_query($filter);
67+
return $this->stream(
68+
new Request(
69+
'POST',
70+
'https://stream.twitter.com/1.1/statuses/filter.json',
71+
[
72+
'Content-Type' => 'application/x-www-form-urlencoded',
73+
'Content-Length' => strlen($postData),
74+
],
75+
$postData
76+
)
77+
);
78+
}
79+
80+
protected function stream(RequestInterface $request): Observable
81+
{
82+
return Promise::toObservable($this->client->handle(new StreamingRequestCommand(
83+
$request
84+
)))->switchLatest()->lift(function () {
85+
return new CutOperator(self::STREAM_DELIMITER);
86+
})->filter(function (string $json) {
87+
return trim($json) !== ''; // To keep the stream alive Twitter sends an empty line at times
88+
})->jsonDecode()->flatMap(function (array $document) {
89+
if (isset($document['delete'])) {
90+
return Promise::toObservable($this->client->handle(new HydrateCommand('DeletedTweet', $document['delete'])));
91+
}
92+
93+
return Promise::toObservable($this->client->handle(new HydrateCommand('Tweet', $document)));
94+
});
95+
}
96+
}

src/Client.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace ApiClients\Twitter;
4+
5+
use ApiClients\Twitter\Resource\TweetInterface;
6+
use ApiClients\Twitter\Resource\UserInterface;
7+
use React\EventLoop\Factory as LoopFactory;
8+
use function Clue\React\Block\await;
9+
use React\EventLoop\LoopInterface;
10+
11+
class Client
12+
{
13+
/**
14+
* @var LoopInterface
15+
*/
16+
protected $loop;
17+
18+
/**
19+
* @var AsyncClient
20+
*/
21+
protected $client;
22+
23+
public function __construct(
24+
string $consumerKey,
25+
string $consumerSecret,
26+
string $accessToken,
27+
string $accessTokenSecret
28+
) {
29+
$this->loop = LoopFactory::create();
30+
$this->client = new AsyncClient($consumerKey, $consumerSecret, $accessToken, $accessTokenSecret, $this->loop);
31+
}
32+
33+
public function tweet(string $tweet): TweetInterface
34+
{
35+
return await(
36+
$this->client->tweet($tweet),
37+
$this->loop
38+
);
39+
}
40+
41+
public function user(string $tweet): UserInterface
42+
{
43+
return await(
44+
$this->client->user($tweet),
45+
$this->loop
46+
);
47+
}
48+
}

0 commit comments

Comments
 (0)