From 88a1e80b3247536fe859e2cba55ab7853cb17908 Mon Sep 17 00:00:00 2001 From: Josh Bruce Date: Sat, 18 Jul 2020 13:15:57 -0500 Subject: [PATCH] use graphql for github api --- src/ESGitHubClient.php | 356 +++++++++++++++++++++---------------- tests/GitHubClientTest.php | 105 +++++------ 2 files changed, 246 insertions(+), 215 deletions(-) diff --git a/src/ESGitHubClient.php b/src/ESGitHubClient.php index 021974f..3278899 100644 --- a/src/ESGitHubClient.php +++ b/src/ESGitHubClient.php @@ -48,18 +48,22 @@ public function __construct($main, ...$args) } } - public function metaMember($memberName) + public function plus(...$parts) { - $value = $this->markdown()->meta()->{$memberName}; - if ($value === null) { - return Shoop::string(""); - } - return Shoop::this($value); + $path = $this->parts()->plus(...$parts)->join("/")->start("/"); + return static::fold( + $path, + $this->ghToken, + $this->ghUsername, + $this->ghRepo, + $this->cacheRootPath, + $this->cacheFolderName + ); } - public function plus(...$parts) + public function dropLast($length = 1) { - $path = $this->parts()->plus(...$parts)->join("/")->start("/"); + $path = $this->parts()->dropLast($length)->join("/")->start("/"); return static::fold( $path, $this->ghToken, @@ -70,6 +74,197 @@ public function plus(...$parts) ); } + // TODO: Make a Store interface + public function files() + { + return $this->repoContent()->entries()->each(function($entry) { + $e = Shoop::dictionary($entry)->object(); + return ($e->type()->isUnfolded("blob")) + ? $this->parts()->plus($e->name)->join("/")->unfold() + : ""; + })->noEmpties(); + } + + public function folders() + { + return $this->repoContent()->entries()->each(function($entry) { + $e = Shoop::dictionary($entry)->object(); + + return ($e->type()->isUnfolded("tree")) + ? $this->parts()->plus($e->name)->join("/")->unfold() + : ""; + })->noEmpties(); + } + + public function isFolder(Closure $closure = null) + { + $bool = $this->repoContent()->hasMember("object", function($result, $object) { + if ($result->unfold() and $object->dictionary["object"] === null) { + return false; + + } elseif ($object->hasMemberUnfolded("byteSize")) { + return false; + + } + return true; + }); + return $this->condition($bool, $closure); + } + + public function isNotFolder(Closure $closure = null) + { + $bool = $this->isFolder()->toggle; + return $this->condition($bool, $closure); + } + + public function isFile(Closure $closure = null) + { + $bool = $this->repoContent()->hasMember("object", function($result, $object) { + if ($result->unfold() and $object->dictionary["object"] === null) { + return false; + + } elseif ($object->hasMemberUnfolded("byteSize")) { + return true; + + } + return false; + }); + return $this->condition($bool, $closure); + } + + public function isNotFile(Closure $closure = null) + { + $bool = $this->isFile()->toggle; + return $this->condition($bool, $closure); + } + + // TODO: Use the one from ShoopedImp somehow + public function condition($bool, Closure $closure = null) + { + $bool = Type::sanitizeType($bool, ESBool::class); + $value = $this->value(); + if ($closure === null) { + $closure = function($bool, $value) { + return $bool; + }; + } + return $closure( + $bool, + Shoop::github( + $value, + $this->ghToken, + $this->ghUsername, + $this->ghRepo, + $this->cacheRootPath, + $this->cacheFolderName + ) + ); + } + + public function content($trim = true, $ignore = [".", "..", ".DS_Store"]) + { + return $this->isFile(function($result, $client) use ($trim) { + if ($result->unfold()) { + $content = $client->repoContent(); + if ($content->isBinary) { + die("image or something"); + } + $content = $this->textContent()->text(); + return ($trim) + ? $content->trim() + : $content; + } + return $client->repoContent()->entries()->each(function($entry) { + $title = Shoop::dictionary($entry)->name()->start("/"); + return Shoop::github( + $this->string()->plus($title), + $this->ghToken, + $this->ghUsername, + $this->ghRepo, + $this->cacheRootPath, + $this->cacheFolderName + ); + }); + }); + } + + public function markdown() + { + return Shoop::markdown($this->content()); + } + + public function metaMember($memberName) + { + $value = $this->markdown()->meta()->{$memberName}; + if ($value === null) { + return Shoop::string(""); + } + return Shoop::this($value); + } + + private function repoContent() + { + $query = <<<'QUERY' + query ($owner: String!, $repo: String!, $path: String!) { + repository(owner: $owner, name: $repo) { + object(expression: $path) { + ... on Blob { + byteSize + isBinary + } + ... on Tree { + entries { + name + type + } + } + } + } + } + QUERY; + $vars = [ + "owner" => $this->ghUsername, + "repo" => $this->ghRepo, + "path" => $this->parts()->countIsGreaterThan(0, function($result, $parts) { + return ($result->unfold()) ? "master:". $parts->join("/") : "master:"; + }) + ]; + + $result = Shoop::dictionary( + $this->client()->api("graphql")->execute($query, $vars) + )->object()->data()->repository()->object; + + return Shoop::this($result); + } + + private function textContent() + { + $query = <<<'QUERY' + query ($owner: String!, $repo: String!, $path: String!) { + repository(owner: $owner, name: $repo) { + object(expression: $path) { + ... on Blob { + text + } + } + } + } + QUERY; + $vars = [ + "owner" => $this->ghUsername, + "repo" => $this->ghRepo, + "path" => $this->parts()->countIsGreaterThan(0, function($result, $parts) { + return ($result->unfold()) ? "master:". $parts->join("/") : "master:"; + }) + ]; + + $result = Shoop::dictionary( + $this->client()->api("graphql")->execute($query, $vars) + )->object()->data()->repository()->object; + + return Shoop::this($result); + } + public function client() { if ($this->client === null) { @@ -108,149 +303,4 @@ function($result, $cacheRootPath) use ($cacheFolderName) { $this->cacheFolderName ); } - - public function exists() - { - $bool = $this->client()->api("repo")->contents()->exists( - $this->ghUsername, - $this->ghRepo, - $this->value() - ); - return Shoop::bool($bool); - } - - public function markdown(...$extensions) - { - if ($this->exists()) { - $content = $this->client()->api("repo")->contents()->download( - $this->ghUsername, - $this->ghRepo, - $this->value() - ); - $content = Shoop::string($content)->trim(); - return Shoop::markdown($content, ...$extensions); - } - return Shoop::markdown("", ...$extensions); - } - - // public function metaMember($memberName) - // { - // $value = $this->markdown()->meta()->{$memberName}; - // if ($value === null) { - // return Shoop::string(""); - // } - // return Shoop::this($value); - // } - - // // TODO: Use the one from ShoopedImp somehow - // public function condition($bool, Closure $closure = null) - // { - // $bool = Type::sanitizeType($bool, ESBool::class); - // $value = $this->value(); - // if ($closure === null) { - // $closure = function($bool, $value) { - // return $bool; - // }; - // } - // return $closure($bool, Shoop::store($value)); - // } - - // public function endsWith($needle, Closure $closure = null) - // { - // $needle = Type::sanitizeType($needle, ESString::class); - // $bool = Shoop::string($this->value())->endsWith($needle); - // return $this->condition($bool, $closure); - // } - - // public function isFolder(Closure $closure = null) - // { - // $value = $this->value(); - // $bool = is_dir($value); - // return $this->condition($bool, $closure); - // } - - // public function isNotFolder(Closure $closure = null) - // { - // $bool = $this->isFolder()->toggle; - // return $this->condition($bool, $closure); - // } - - // public function isFile(Closure $closure = null) - // { - // $value = $this->value(); - // $bool = is_file($value); - // return $this->condition($bool, $closure); - // } - - // public function isNotFile(Closure $closure = null) - // { - // $bool = $this->isFile()->toggle; - // return $this->condition($bool, $closure); - // } - - // public function content($trim = true, $ignore = [".", "..", ".DS_Store"]) - // { - // $trim = Type::sanitizeType($trim, ESBool::class); - // $ignore = Type::sanitizeType($ignore, ESArray::class); - - // $path = $this->value(); - // if (file_exists($path) and is_file($path)) { - // $contents = file_get_contents($path); - // if (strlen($contents) > 0) { - // return ($trim) - // ? Shoop::string($contents)->trim() - // : Shoop::string($contents); - // } - - // } elseif (is_dir($path)) { - // return Shoop::array(scandir($path))->each( - // function($item) use ($path, $trim, $ignore) { - // $bool = Shoop::array($ignore)->hasUnfolded($item); - // return ($trim and $bool) - // ? Shoop::string("") - // : Shoop::string($path ."/{$item}"); - - // })->noEmpties()->reindex(); - - // } - // return Shoop::string(""); - // } - - // public function folders() - // { - // return ($this->isFile) - // ? Shoop::array([]) - // : $this->content()->each(function($path) use ($endsWith) { - // $store = Shoop::store($path); - // return ($store->isFolder) - // ? $store - // : Shoop::string(""); - // })->noEmpties()->reindex(); - // } - - // public function files($trim = true, $ignore = [".", "..", ".DS_Store"], $endsWith = "*") - // { - // $trim = Type::sanitizeType($trim, ESBool::class); - // $ignore = Type::sanitizeType($ignore, ESArray::class); - // $endsWith = Type::sanitizeType($endsWith, ESString::class); - // return ($this->isFile) - // ? Shoop::array([]) - // : $this->content(true, $ignore)->each(function($path) use ($ignore, $endsWith) { - // $store = Shoop::store($path); - // return $store->isFile(function($result, $store) use ($endsWith) { - // // TODO: One would think this could be simplified unless check is paramount - // if (! $result->unfold()) { - // return Shoop::string(""); - - // } elseif (Shoop::string($endsWith)->isUnfolded("*")) { - // return $store; - - // } elseif ($store->string()->endsWithUnfolded($endsWith)) { - // return $store; - - // } - // return Shoop::string(""); - // }); - // })->noEmpties()->reindex(); - // } } diff --git a/tests/GitHubClientTest.php b/tests/GitHubClientTest.php index a24fed2..66143d4 100644 --- a/tests/GitHubClientTest.php +++ b/tests/GitHubClientTest.php @@ -47,10 +47,50 @@ public function tearDown(): void $this->rmrf($this->cacheRoot()->plus("/.cache")); } + public function testIsFolder() + { + $actual = $this->client()->isFolder(); + $this->assertTrue($actual->unfold()); + + $actual = $this->client()->plus("README.md")->isNotFolder(); + $this->assertTrue($actual->unfold()); + + $actual = $this->client()->plus("README.me")->isFolder(); + $this->assertFalse($actual->unfold()); + } + + public function testIsFile() + { + $actual = $this->client()->isFile(); + $this->assertFalse($actual->unfold()); + + $actual = $this->client()->plus("README.md")->isFile(); + $this->assertTrue($actual->unfold()); + + $actual = $this->client()->plus("README.me")->isFile(); + $this->assertFalse($actual->unfold()); + } + + public function testCanListFilesAndFolders() + { + $expected = 3; + $actual = $this->client()->folders()->count(); + $this->assertEquals($expected, $actual->unfold()); + + $expected = 8; + $actual = $this->client()->files()->count(); + $this->assertEquals($expected, $actual->unfold()); + } + public function testCanGetContent() { $expected = "/README.md"; - $actual = $this->client()->plus("README.md"); + $actual = $this->client()->plus("README.md")->content() + ->startsWith("# 8fold Shoop Extras"); + $this->assertTrue($actual->unfold()); + + $expected = 9; + $actual = $this->client()->plus(".github")->content()->count(); $this->assertEquals($expected, $actual->unfold()); } @@ -61,8 +101,8 @@ public function testCanCheckFileExists() "data", "inner-folder", "content.md" - )->exists(); - $this->assertTrue($actual->unfold()); + )->isFile; + $this->assertTrue($actual); } public function testCanGetMarkdown() @@ -101,63 +141,4 @@ public function testCanUsePlus() )->plus("content.md")->markdown(); $this->assertEquals($expected, $actual->unfold()); } - // public function testCanGetContent() - // { - // $path = __DIR__ ."/data/inner-folder/subfolder/inner.md"; - // $content = file_get_contents($path); - - // $expected = "---\ntitle: Something\n---\n\nMarkdown text\n"; - // $actual = ESMarkdown::fold($content)->value; - // $this->assertEquals($expected, $actual); - - // $actual = ESMarkdown::fold($content)->unfold(); - // $this->assertSame($expected, $actual); - // } - - // public function testCanGetParsed() - // { - // $path = __DIR__ ."/data/inner-folder/subfolder/inner.md"; - // $content = file_get_contents($path); - - // $expected = new \stdClass(); - // $expected->title = "Something"; - // $actual = ESMarkdown::fold($content)->meta; - // $this->assertEquals($expected, $actual); - - // $expected = "Markdown content"; - // $actual = ESMarkdown::fold($content)->html([ - // "text" => "content" - // ], [ - // "

" => "", - // "

" => "" - // ]); - // $this->assertEquals($expected, $actual->unfold()); - // } - - // public function testExtensions() - // { - // $path = __DIR__ ."/data/table.md"; - - // $expected = "

|THead ||:-----||TBody |

"; - // $actual = ESMarkdown::foldFromPath($path)->html(); - // $this->assertSame($expected, $actual->unfold()); - - // $expected = '
THead
TBody
'; - // $actual = ESMarkdown::foldFromPath($path, TableExtension::class)->html(); - // $this->assertSame($expected, $actual->unfold()); - - // $path = __DIR__ ."/data/link.md"; - // $expected = '

Something

Stripped

'; - // $actual = ESMarkdown::foldFromPath($path)->extensions( - // ExternalLinkExtension::class - // )->html( - // [], [], true, true, [ - // 'html_input' => 'strip', - // "external_link" => [ - // "open_in_new_window" => true - // ] - // ] - // ); - // $this->assertSame($expected, $actual->unfold()); - // } }