Skip to content

Commit 5094a19

Browse files
committed
Added a Client class as a wrapper for the Github API package, to be used by the Adapter
Logic moved over from the Adapter class.
1 parent 8c14b13 commit 5094a19

File tree

1 file changed

+370
-0
lines changed

1 file changed

+370
-0
lines changed

src/Client.php

Lines changed: 370 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,370 @@
1+
<?php
2+
3+
namespace Potherca\Flysystem\Github;
4+
5+
use Github\Api\GitData;
6+
use Github\Api\Repo;
7+
use Github\Client as GithubClient;
8+
use Github\Exception\RuntimeException;
9+
use League\Flysystem\AdapterInterface;
10+
use League\Flysystem\Util\MimeType;
11+
12+
class Client
13+
{
14+
const ERROR_NOT_FOUND = 'Not Found';
15+
16+
const KEY_BLOB = 'blob';
17+
const KEY_CONTENTS = 'contents';
18+
const KEY_DIRECTORY = 'dir';
19+
const KEY_FILE = 'file';
20+
const KEY_FILENAME = 'basename';
21+
const KEY_GIT_DATA = 'git';
22+
const KEY_MODE = 'mode';
23+
const KEY_NAME = 'name';
24+
const KEY_PATH = 'path';
25+
const KEY_REPO = 'repo';
26+
const KEY_SHA = 'sha';
27+
const KEY_SIZE = 'size';
28+
const KEY_STREAM = 'stream';
29+
const KEY_TIMESTAMP = 'timestamp';
30+
const KEY_TREE = 'tree';
31+
const KEY_TYPE = 'type';
32+
const KEY_VISIBILITY = 'visibility';
33+
34+
/** @var GithubClient */
35+
private $client;
36+
/** @var Settings */
37+
private $settings;
38+
/** @var string */
39+
private $package;
40+
/** @var string */
41+
private $vendor;
42+
43+
final public function __construct(GithubClient $client, Settings $settings)
44+
{
45+
$this->client = $client;
46+
$this->settings = $settings;
47+
48+
/* @NOTE: If $settings contains `credentials` but not an `author` we are
49+
* still in `read-only` mode.
50+
*/
51+
list($this->vendor, $this->package) = explode('/', $this->settings->getRepository());
52+
}
53+
54+
/**
55+
* @param string $path
56+
*
57+
* @return bool
58+
*/
59+
final public function exists($path)
60+
{
61+
return $this->repositoryContents()->exists(
62+
$this->vendor,
63+
$this->package,
64+
$path,
65+
$this->settings->getReference()
66+
);
67+
}
68+
69+
/**
70+
* @param $path
71+
*
72+
* @return null|string
73+
*
74+
* @throws \Github\Exception\ErrorException
75+
*/
76+
final public function download($path)
77+
{
78+
$fileContent = $this->repositoryContents()->download(
79+
$this->vendor,
80+
$this->package,
81+
$path,
82+
$this->settings->getReference()
83+
);
84+
85+
return $fileContent;
86+
}
87+
88+
/**
89+
* @param string $path
90+
* @param bool $recursive
91+
*
92+
* @return array
93+
*/
94+
final public function metadata($path, $recursive)
95+
{
96+
// If $info['truncated'] is `true`, the number of items in the tree array
97+
// exceeded the github maximum limit. If you need to fetch more items,
98+
// multiple calls will be needed
99+
100+
$info = $this->trees($recursive);
101+
$tree = $this->getPathFromTree($info, $path, $recursive);
102+
$result = $this->normalizeMetadata($tree);
103+
104+
return $result;
105+
}
106+
107+
/**
108+
* @param string $path
109+
*
110+
* @return array
111+
*/
112+
final public function show($path)
113+
{
114+
// Get information about a repository file or directory
115+
$fileInfo = $this->repositoryContents()->show(
116+
$this->vendor,
117+
$this->package,
118+
$path,
119+
$this->settings->getReference()
120+
);
121+
return $fileInfo;
122+
}
123+
124+
/**
125+
* @param string $path
126+
*
127+
* @return array|bool
128+
*/
129+
final public function getMetaData($path)
130+
{
131+
try {
132+
$metadata = $this->show($path);
133+
} catch (RuntimeException $exception) {
134+
if ($exception->getMessage() === self::ERROR_NOT_FOUND) {
135+
$metadata = false;
136+
} else {
137+
throw $exception;
138+
}
139+
}
140+
141+
return $metadata;
142+
}
143+
144+
/**
145+
* @param string $path
146+
*
147+
* @return null|string
148+
*/
149+
final public function guessMimeType($path)
150+
{
151+
//@NOTE: The github API does not return a MIME type, so we have to guess :-(
152+
if (strrpos($path, '.') > 1) {
153+
$extension = substr($path, strrpos($path, '.')+1);
154+
}
155+
156+
if (isset($extension)) {
157+
$mimeType = MimeType::detectByFileExtension($extension) ?: 'text/plain';
158+
} else {
159+
$content = $this->download($path);
160+
$mimeType = MimeType::detectByContent($content);
161+
}
162+
163+
return $mimeType;
164+
}
165+
166+
/**
167+
* @param string $path
168+
*
169+
* @return array
170+
*/
171+
final public function updated($path)
172+
{
173+
// List commits for a file
174+
$commits = $this->repository()->commits()->all(
175+
$this->vendor,
176+
$this->package,
177+
array(
178+
'sha' => $this->settings->getBranch(),
179+
'path' => $path
180+
)
181+
);
182+
183+
$updated = array_shift($commits);
184+
//@NOTE: $created = array_pop($commits);
185+
186+
$time = new \DateTime($updated['commit']['committer']['date']);
187+
188+
return ['timestamp' => $time->getTimestamp()];
189+
}
190+
191+
/**
192+
* @return \Github\Api\Repository\Contents
193+
*/
194+
private function repositoryContents()
195+
{
196+
return $this->repository()->contents();
197+
}
198+
199+
/**
200+
*
201+
*/
202+
private function authenticate()
203+
{
204+
static $hasRun;
205+
206+
if ($hasRun === null) {
207+
if (empty($this->settings->getCredentials()) === false) {
208+
$credentials = array_replace(
209+
[null, null, null],
210+
$this->settings->getCredentials()
211+
);
212+
213+
$this->client->authenticate(
214+
$credentials[1],
215+
$credentials[2],
216+
$credentials[0]
217+
);
218+
}
219+
$hasRun = true;
220+
}
221+
}
222+
223+
/**
224+
* @return Repo
225+
*/
226+
private function repository()
227+
{
228+
return $this->fetchApi(self::KEY_REPO);
229+
}
230+
231+
/**
232+
* @param string $name
233+
* @return \Github\Api\ApiInterface
234+
*/
235+
private function fetchApi($name)
236+
{
237+
$this->authenticate();
238+
return $this->client->api($name);
239+
}
240+
241+
/**
242+
* @param array $metadata
243+
* @param string $path
244+
* @param bool $recursive
245+
*
246+
* @return array
247+
*/
248+
private function getPathFromTree(array $metadata, $path, $recursive)
249+
{
250+
if (empty($path)) {
251+
if ($recursive === false) {
252+
$metadata = array_filter($metadata, function ($entry) use ($path) {
253+
return (strpos($entry[self::KEY_PATH], '/', strlen($path)) === false);
254+
});
255+
}
256+
} else {
257+
$metadata = array_filter($metadata, function ($entry) use ($path, $recursive) {
258+
$match = false;
259+
260+
if (strpos($entry[self::KEY_PATH], $path) === 0) {
261+
if ($recursive === true) {
262+
$match = true;
263+
} else {
264+
$length = strlen($path);
265+
$match = (strpos($entry[self::KEY_PATH], '/', $length) === false);
266+
}
267+
}
268+
269+
return $match;
270+
});
271+
}
272+
273+
return $metadata;
274+
}
275+
276+
/**
277+
* @param array $metadata
278+
*
279+
* @return array
280+
*/
281+
private function normalizeMetadata($metadata)
282+
{
283+
$result = [];
284+
285+
if (is_array(current($metadata)) === false) {
286+
$metadata = [$metadata];
287+
}
288+
289+
foreach ($metadata as $entry) {
290+
if (isset($entry[self::KEY_NAME]) === false){
291+
if(isset($entry[self::KEY_FILENAME]) === true) {
292+
$entry[self::KEY_NAME] = $entry[self::KEY_FILENAME];
293+
} elseif(isset($entry[self::KEY_PATH]) === true) {
294+
$entry[self::KEY_NAME] = $entry[self::KEY_PATH];
295+
} else {
296+
// ?
297+
}
298+
}
299+
300+
if (isset($entry[self::KEY_TYPE]) === true) {
301+
switch ($entry[self::KEY_TYPE]) {
302+
case self::KEY_BLOB:
303+
$entry[self::KEY_TYPE] = self::KEY_FILE;
304+
break;
305+
306+
case self::KEY_TREE:
307+
$entry[self::KEY_TYPE] = self::KEY_DIRECTORY;
308+
break;
309+
}
310+
}
311+
312+
if (isset($entry[self::KEY_CONTENTS]) === false) {
313+
$entry[self::KEY_CONTENTS] = false;
314+
}
315+
316+
if (isset($entry[self::KEY_STREAM]) === false) {
317+
$entry[self::KEY_STREAM] = false;
318+
}
319+
320+
if (isset($entry[self::KEY_TIMESTAMP]) === false) {
321+
$entry[self::KEY_TIMESTAMP] = false;
322+
}
323+
324+
if (isset($entry[self::KEY_MODE])) {
325+
$entry[self::KEY_VISIBILITY] = $this->visibility($entry[self::KEY_MODE]);
326+
} else {
327+
$entry[self::KEY_VISIBILITY] = false;
328+
}
329+
330+
$result[] = $entry;
331+
}
332+
333+
return $result;
334+
}
335+
336+
/**
337+
* @return GitData
338+
*/
339+
private function gitData()
340+
{
341+
return $this->fetchApi(self::KEY_GIT_DATA);
342+
}
343+
344+
/**
345+
* @param bool $recursive
346+
* @return \Guzzle\Http\EntityBodyInterface|mixed|string
347+
*/
348+
private function trees($recursive)
349+
{
350+
$trees = $this->gitData()->trees();
351+
352+
$info = $trees->show(
353+
$this->vendor,
354+
$this->package,
355+
$this->settings->getReference(),
356+
$recursive
357+
);
358+
359+
return $info[self::KEY_TREE];
360+
}
361+
362+
/**
363+
* @param $permissions
364+
* @return string
365+
*/
366+
private function visibility($permissions)
367+
{
368+
return $permissions & 0044 ? AdapterInterface::VISIBILITY_PUBLIC : AdapterInterface::VISIBILITY_PRIVATE;
369+
}
370+
}

0 commit comments

Comments
 (0)