Skip to content

Commit a86fbf5

Browse files
committed
Store file blobs in the Git database
1 parent b96dc45 commit a86fbf5

File tree

3 files changed

+231
-0
lines changed

3 files changed

+231
-0
lines changed
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\Client\Github\CommandBus\Command\Repository;
4+
5+
use React\Stream\ReadableStreamInterface;
6+
use WyriHaximus\Tactician\CommandHandler\Annotations\Handler;
7+
8+
/**
9+
* @Handler("ApiClients\Client\Github\CommandBus\Handler\Repository\BlobHandler")
10+
*/
11+
final class BlobCommand
12+
{
13+
/**
14+
* @var string
15+
*/
16+
private $repository;
17+
18+
/**
19+
* @var ReadableStreamInterface
20+
*/
21+
private $contents;
22+
23+
/**
24+
* @param string $repository
25+
* @param ReadableStreamInterface $contents
26+
*/
27+
public function __construct(string $repository, ReadableStreamInterface $contents)
28+
{
29+
$this->repository = $repository;
30+
$this->contents = $contents;
31+
}
32+
33+
/**
34+
* @return string
35+
*/
36+
public function getRepository(): string
37+
{
38+
return $this->repository;
39+
}
40+
41+
/**
42+
* @return ReadableStreamInterface
43+
*/
44+
public function getContents(): ReadableStreamInterface
45+
{
46+
return $this->contents;
47+
}
48+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace ApiClients\Client\Github\CommandBus\Handler\Repository;
4+
5+
use ApiClients\Client\Github\CommandBus\Command\Repository\BlobCommand;
6+
use ApiClients\Client\Github\Resource\TreeInterface;
7+
use ApiClients\Foundation\Hydrator\Hydrator;
8+
use ApiClients\Foundation\Transport\Service\RequestService;
9+
use Clue\React\Buzz\Message\ReadableBodyStream;
10+
use React\EventLoop\LoopInterface;
11+
use React\Promise\PromiseInterface;
12+
use React\Stream\ThroughStream;
13+
use RingCentral\Psr7\Request;
14+
use WyriHaximus\React\Stream\Base64\ReadableStreamBase64Encode;
15+
use WyriHaximus\React\Stream\Json\JsonStream;
16+
17+
final class BlobHandler
18+
{
19+
/**
20+
* @var RequestService
21+
*/
22+
private $requestService;
23+
24+
/**
25+
* @var Hydrator
26+
*/
27+
private $hydrator;
28+
29+
/**
30+
* @var LoopInterface
31+
*/
32+
private $loop;
33+
34+
/**
35+
* @param RequestService $requestService
36+
* @param Hydrator $hydrator
37+
* @param LoopInterface $loop
38+
*/
39+
public function __construct(RequestService $requestService, Hydrator $hydrator, LoopInterface $loop)
40+
{
41+
$this->requestService = $requestService;
42+
$this->hydrator = $hydrator;
43+
$this->loop = $loop;
44+
}
45+
46+
/**
47+
* @param BlobCommand $command
48+
* @return PromiseInterface
49+
*/
50+
public function handle(BlobCommand $command): PromiseInterface
51+
{
52+
$stream = new JsonStream();
53+
$this->loop->futureTick(function () use ($stream, $command) {
54+
$contentsStream = new ThroughStream();
55+
$stream->write('encoding', 'base64');
56+
$stream->write('content', new ReadableStreamBase64Encode($contentsStream));
57+
$stream->end();
58+
59+
$this->loop->futureTick(function () use ($contentsStream, $command) {
60+
$command->getContents()->pipe($contentsStream);
61+
});
62+
});
63+
64+
return $this->requestService->request(
65+
new Request(
66+
'POST',
67+
'repos/' . $command->getRepository() . '/git/blobs',
68+
[],
69+
new ReadableBodyStream($stream)
70+
)
71+
)->then(function ($tree) {
72+
return $this->hydrator->hydrate(TreeInterface::HYDRATE_CLASS, $tree->getBody()->getParsedContents());
73+
});
74+
}
75+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace ApiClients\Tests\Github\CommandBus\Handler\Repository;
4+
5+
use ApiClients\Client\Github\CommandBus\Command\Repository\BlobCommand;
6+
use ApiClients\Client\Github\CommandBus\Handler\Repository\BlobHandler;
7+
use ApiClients\Client\Github\Resource\TreeInterface;
8+
use ApiClients\Foundation\Hydrator\Hydrator;
9+
use ApiClients\Foundation\Transport\Service\RequestService;
10+
use ApiClients\Middleware\Json\JsonStream;
11+
use ApiClients\Tools\TestUtilities\TestCase;
12+
use Prophecy\Argument;
13+
use Psr\Http\Message\RequestInterface;
14+
use React\EventLoop\Factory;
15+
use function React\Promise\Stream\buffer;
16+
use React\Stream\ThroughStream;
17+
use RingCentral\Psr7\Response;
18+
use function WyriHaximus\React\timedPromise;
19+
20+
/**
21+
* @internal
22+
*/
23+
final class BlobHandlerTest extends TestCase
24+
{
25+
public function provideCommands()
26+
{
27+
/*yield [
28+
function () {
29+
$body = 'foo.bar';
30+
$loop = Factory::create();
31+
$stream = new ThroughStream();
32+
$loop->addTimer(1, function () use ($stream, $body) {
33+
$stream->end($body);
34+
});
35+
$request = new RenderMarkdownCommand(
36+
$stream,
37+
'gfm',
38+
'php-api-clients/github'
39+
);
40+
$expectedJson = [
41+
'text' => $body,
42+
'mode' => 'gfm',
43+
'context' => 'php-api-clients/github',
44+
];
45+
46+
return [$loop, $request, $expectedJson];
47+
},
48+
];*/
49+
50+
yield [
51+
function () {
52+
$body = 'foo.bar';
53+
$loop = Factory::create();
54+
$stream = new ThroughStream();
55+
$loop->addTimer(1, function () use ($stream, $body) {
56+
$stream->end($body);
57+
});
58+
$request = new BlobCommand(
59+
'login/repo',
60+
$stream
61+
);
62+
$expectedJson = [
63+
'encoding' => 'base64',
64+
'content' => \base64_encode($body),
65+
];
66+
67+
return [$loop, $request, $expectedJson];
68+
},
69+
];
70+
}
71+
72+
/**
73+
* @dataProvider provideCommands
74+
*/
75+
public function testCommand(callable $callable)
76+
{
77+
list($loop, $command, $expectedjson) = $callable();
78+
$json = [
79+
'foo' => 'bar',
80+
];
81+
$stream = null;
82+
$jsonStream = new JsonStream($json);
83+
84+
$tree = $this->prophesize(TreeInterface::class)->reveal();
85+
86+
$requestService = $this->prophesize(RequestService::class);
87+
$requestService->request(Argument::that(function (RequestInterface $request) use (&$stream) {
88+
buffer($request->getBody())->done(function ($json) use (&$stream) {
89+
$stream = $json;
90+
});
91+
92+
return true;
93+
}))->willReturn(timedPromise($loop, 1, new Response(
94+
200,
95+
[],
96+
$jsonStream
97+
)));
98+
99+
$hydrator = $this->prophesize(Hydrator::class);
100+
$hydrator->hydrate(TreeInterface::HYDRATE_CLASS, $json)->shouldBeCalled()->willReturn($tree);
101+
102+
$handler = new BlobHandler($requestService->reveal(), $hydrator->reveal(), $loop);
103+
104+
$result = $this->await($handler->handle($command), $loop);
105+
self::assertSame($tree, $result);
106+
self::assertSame($expectedjson, \json_decode($stream, true));
107+
}
108+
}

0 commit comments

Comments
 (0)