mirror of
				https://github.com/sitelease/sugar-cube-client.git
				synced 2025-10-31 12:02:30 +01:00 
			
		
		
		
	Compare commits
	
		
			11 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 5deac6cf55 | |||
| 714d079287 | |||
| ea64578fd9 | |||
| dbdff87baf | |||
| 785d0b43a2 | |||
| f468b67335 | |||
| d37650a882 | |||
| e60312fab3 | |||
| cc6bd39f82 | |||
| 027b430adc | |||
| 27972b95c7 | 
| @ -1,4 +1,4 @@ | ||||
| The Suger Cube Client (formerly gitea.php) is released with MIT License: | ||||
| The Sugar Cube Client (formerly gitea.php) is released with MIT License: | ||||
|  | ||||
| Copyright © 2018 - 2019 SAB International | ||||
| Copyright © 2020 siteLease Web Solutions | ||||
|  | ||||
| @ -1,10 +1,10 @@ | ||||
| The Suger Cube Client, a Gitea API client for PHP | ||||
| The Sugar Cube Client, a Gitea API client for PHP | ||||
| ======== | ||||
|  | ||||
| The Suger Cube Client allows you to send and recieve data from Gitea's RESTful API using object-oriented PHP. | ||||
| The Sugar Cube Client allows you to send and recieve data from Gitea's RESTful API using object-oriented PHP. | ||||
|  | ||||
| ### How does it work? | ||||
| Under the hood, Suger Cube uses the [Guzzle Library](http://docs.guzzlephp.org/en/stable/) to make and send requests to [Gitea's RESTful API](https://try.gitea.io/api/swagger) routes. It then converts the returned JSON response data into PHP objects which you can use to easily query the data. | ||||
| Under the hood, Sugar Cube uses the [Guzzle Library](http://docs.guzzlephp.org/en/stable/) to make and send requests to [Gitea's RESTful API](https://try.gitea.io/api/swagger) routes. It then converts the returned JSON response data into PHP objects which you can use to easily query the data. | ||||
|  | ||||
| ### Real world Example | ||||
| To see how Sugar Cube can be used in a real world application please refer to [Acapella's code base](https://github.com/sitelease/acappella) (e.g. [line 39](https://github.com/sitelease/acappella/blob/1.0.0/src/Application/GiteaRepositoryManager.php#L39) of GiteaRepositoryManager.php). | ||||
| @ -13,7 +13,7 @@ To see how Sugar Cube can be used in a real world application please refer to [A | ||||
| - [ ] Add Example code to the README | ||||
| - [ ] Create PHPUnit tests for each API requester | ||||
| - [ ] Create more API requesters for Gitea's API routes | ||||
| - [ ] Submit the Suger Cube composer package to Packagist | ||||
| - [ ] Submit the Sugar Cube composer package to Packagist | ||||
|  | ||||
| ### Credits | ||||
| * First thank you goes out to the folks over on the [Gitea.php](https://github.com/sab-international/gitea.php) project. Your Model and Push Event objects provided the foundation of this project. | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| { | ||||
|   "name": "sitelease/suger-cube-client", | ||||
|   "description": "Suger Cube Client", | ||||
|   "homepage": "https://github.com/sitelease/suger-cube-client", | ||||
|   "name": "sitelease/sugar-cube-client", | ||||
|   "description": "A sweet Gitea API client for PHP", | ||||
|   "homepage": "https://github.com/sitelease/sugar-cube-client", | ||||
|   "type": "library", | ||||
|   "license": "MIT", | ||||
|   "authors": [ | ||||
| @ -17,7 +17,7 @@ | ||||
|     "client", | ||||
|     "git", | ||||
|     "gitea", | ||||
|     "suger", | ||||
|     "sugar", | ||||
|     "scm", | ||||
|     "vcs" | ||||
|   ], | ||||
| @ -25,7 +25,7 @@ | ||||
|     "php": ">=7.2.0", | ||||
|     "ext-curl": "*", | ||||
|     "ext-json": "*", | ||||
|     "cedx/enum": "^7.4.0", | ||||
|     "myclabs/php-enum": "^1.8", | ||||
|     "guzzlehttp/guzzle": "~6.0" | ||||
|   }, | ||||
|   "require-dev": { | ||||
| @ -33,7 +33,7 @@ | ||||
|     "phpunit/phpunit": "^8.1.2" | ||||
|   }, | ||||
|   "support": { | ||||
|     "docs": "https://github.com/sitelease/suger-cube-client", | ||||
|     "issues": "https://github.com/sitelease/suger-cube-client/issues" | ||||
|     "docs": "https://github.com/sitelease/sugar-cube-client", | ||||
|     "issues": "https://github.com/sitelease/sugar-cube-client/issues" | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -112,28 +112,73 @@ class Repositories extends AbstractAllApiRequester | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the raw contents of a file stored inside a repository | ||||
|      * Get a repository using its ID | ||||
|      * | ||||
|      * Example: | ||||
|      * ``` | ||||
|      * $client->repositories()->getByID($repoId); | ||||
|      * ``` | ||||
|      * | ||||
|      * @param string repoId The ID of the repository | ||||
|      * @return Repository | ||||
|      */ | ||||
|     public function getById(int $repoId) | ||||
|     { | ||||
|         $client = $this->getClient(); | ||||
|         try { | ||||
|             $response = $this->get("repositories/$repoId"); | ||||
|             $statusCode = $response->getStatusCode(); | ||||
|             $body = (string) $response->getBody(); | ||||
|             if ($statusCode == 200) { | ||||
|                 return Repository::fromJson( | ||||
|                     $client, | ||||
|                     $this, | ||||
|                     json_decode($body) | ||||
|                 ); | ||||
|             } | ||||
|             return false; | ||||
|  | ||||
|         } catch (ServerException $serverError) { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the contents of a file in a certain in repository commit/branch/tag | ||||
|      * using the repository's name and owner | ||||
|      * | ||||
|      * Example: | ||||
|      * ``` | ||||
|      * $client->repositories()->getRawFile($owner, $repoName, "README.md"); | ||||
|      * $client->repositories()->getFileContents($owner, $repoName, "README.md", "v2.0.0"); | ||||
|      * ``` | ||||
|      * | ||||
|      * @param string $owner The owner of the repository | ||||
|      * @param string $repoName The name of the repository | ||||
|      * @param string $filepath The path to the raw file (relative to the repository root) | ||||
|      * @param string $filepath The path to the file (relative to the repository root) | ||||
|      * @param string $ref The name of the commit/branch/tag. Default the repository’s default branch (usually master) | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getRawFile(string $owner, string $repoName, string $filepath) | ||||
|     public function getFileContents(string $owner, string $repoName, string $filepath, string $ref="") | ||||
|     { | ||||
|         $client = $this->getClient(); | ||||
|         try { | ||||
|             $response = $this->get("repos/$owner/$repoName/raw/$filepath"); | ||||
|             if ($ref !== "") { | ||||
|                 $response = $this->get("repos/$owner/$repoName/contents/$filepath",[ | ||||
|                     "ref" => $ref | ||||
|                 ]); | ||||
|             } else { | ||||
|                 $response = $this->get("repos/$owner/$repoName/contents/$filepath"); | ||||
|             } | ||||
|  | ||||
|             $statusCode = $response->getStatusCode(); | ||||
|             $body = (string) $response->getBody(); | ||||
|             if ($statusCode == 200) { | ||||
|                 return $body; | ||||
|                 $body = (string) $response->getBody(); | ||||
|                 $jsonObj = json_decode($body, true); | ||||
|                 if (array_key_exists("content", $jsonObj)) { | ||||
|                     $base64FileContents = $jsonObj["content"]; | ||||
|                     $fileContents = base64_decode($base64FileContents); | ||||
|                     return $fileContents; | ||||
|                 } | ||||
|             } | ||||
|             return false; | ||||
|  | ||||
|  | ||||
| @ -66,7 +66,7 @@ class Client implements RequestChainableInterface | ||||
|     { | ||||
|         // Append a slash to any URL that doesn't end in '/' | ||||
|         if (!$this->endsWith($giteaURL, '/')) { | ||||
|             $giteaURL += "/"; | ||||
|             $giteaURL .= "/"; | ||||
|         } | ||||
|         $this->giteaURL = $giteaURL; | ||||
|         $this->authToken = $authToken; | ||||
|  | ||||
| @ -48,7 +48,7 @@ class Owner extends AbstractApiModel { | ||||
|         if (count($args) >= 2) { | ||||
|             $id = $args[0]; | ||||
|             $login = $args[1]; | ||||
|             if (!is_int($id)) { | ||||
|             if (!is_numeric($id)) { | ||||
|                 $argumentType = gettype($id); | ||||
|                 throw new InvalidArgumentException("The \"__construct()\" function requires the 3rd parameter to be of the integer type, but a \"$argumentType\" was passed in"); | ||||
|             } | ||||
| @ -76,7 +76,7 @@ class Owner extends AbstractApiModel { | ||||
|             new static( | ||||
|                 $client, | ||||
|                 $caller, | ||||
|                 isset($map->id) && is_int($map->id) ? $map->id : -1, | ||||
|                 isset($map->id) && is_numeric($map->id) ? $map->id : -1, | ||||
|                 isset($map->login) && is_string($map->login) ? $map->login : '' | ||||
|             ) | ||||
|         ) | ||||
| @ -203,7 +203,7 @@ class Owner extends AbstractApiModel { | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function getIsAdmin(): boolean { | ||||
|     public function getIsAdmin(): bool { | ||||
|         return $this->isAdmin; | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -100,7 +100,7 @@ class Repository extends AbstractApiModel { | ||||
|         if (count($args) >= 2) { | ||||
|             $id = $args[0]; | ||||
|             $fullName = $args[1]; | ||||
|             if (!is_int($id)) { | ||||
|             if (!is_numeric($id)) { | ||||
|                 $argumentType = gettype($id); | ||||
|                 throw new InvalidArgumentException("The \"construct()\" function requires the 3rd parameter to be of the integer type, but a \"$argumentType\" was passed in"); | ||||
|             } | ||||
| @ -128,7 +128,7 @@ class Repository extends AbstractApiModel { | ||||
|             new static( | ||||
|                 $client, | ||||
|                 $caller, | ||||
|                 isset($map->id) && is_int($map->id) ? $map->id : -1, | ||||
|                 isset($map->id) && is_numeric($map->id) ? $map->id : -1, | ||||
|                 isset($map->full_name) && is_string($map->full_name) ? $map->full_name : '' | ||||
|             ) | ||||
|         ) | ||||
| @ -138,20 +138,20 @@ class Repository extends AbstractApiModel { | ||||
|         ->setDescription(isset($map->description) && is_string($map->description) ? $map->description : '') | ||||
|         ->setEmpty(isset($map->empty) && is_bool($map->empty) ? $map->empty : true) | ||||
|         ->setFork(isset($map->fork) && is_bool($map->fork) ? $map->fork : false) | ||||
|         ->setForksCount(isset($map->forks_count) && is_int($map->forks_count) ? $map->forks_count : 0) | ||||
|         ->setForksCount(isset($map->forks_count) && is_numeric($map->forks_count) ? $map->forks_count : 0) | ||||
|         ->setHtmlUrl(isset($map->html_url) && is_string($map->html_url) ? new Uri($map->html_url) : null) | ||||
|         ->setMirror(isset($map->mirror) && is_bool($map->mirror) ? $map->mirror : false) | ||||
|         ->setName(isset($map->name) && is_string($map->name) ? $map->name : '') | ||||
|         ->setOpenIssuesCount(isset($map->open_issues_count) && is_int($map->open_issues_count) ? $map->open_issues_count : 0) | ||||
|         ->setOpenIssuesCount(isset($map->open_issues_count) && is_numeric($map->open_issues_count) ? $map->open_issues_count : 0) | ||||
|         ->setOwner(isset($map->owner) && is_object($map->owner) ? Owner::fromJson($client, null, $map->owner) : null) | ||||
|         ->setParent(isset($map->parent) && is_object($map->parent) ? Repository::fromJson($client, null, $map->parent) : null) | ||||
|         ->setPermissions(isset($map->permissions) && is_object($map->permissions) ? Permission::fromJson($client, null, $map->permissions) : null) | ||||
|         ->setPrivate(isset($map->private) && is_bool($map->private) ? $map->private : false) | ||||
|         ->setSize(isset($map->size) && is_int($map->size) ? $map->size : 0) | ||||
|         ->setSize(isset($map->size) && is_numeric($map->size) ? $map->size : 0) | ||||
|         ->setSshUrl(isset($map->ssh_url) && is_string($map->ssh_url) ? new Uri($map->ssh_url) : null) | ||||
|         ->setStarsCount(isset($map->stars_count) && is_int($map->stars_count) ? $map->stars_count : 0) | ||||
|         ->setStarsCount(isset($map->stars_count) && is_numeric($map->stars_count) ? $map->stars_count : 0) | ||||
|         ->setUpdatedAt(isset($map->updated_at) && is_string($map->updated_at) ? new \DateTime($map->updated_at) : null) | ||||
|         ->setWatchersCount(isset($map->watchers_count) && is_int($map->watchers_count) ? $map->watchers_count : 0) | ||||
|         ->setWatchersCount(isset($map->watchers_count) && is_numeric($map->watchers_count) ? $map->watchers_count : 0) | ||||
|         ->setWebsite(isset($map->website) && is_string($map->website) ? new Uri($map->website) : null); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -1,11 +1,13 @@ | ||||
| <?php declare(strict_types=1); | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Gitea\Model; | ||||
|  | ||||
| use Enum\{EnumTrait}; | ||||
| use MyCLabs\Enum\Enum; | ||||
|  | ||||
| /** Defines the state of a Gitea status. */ | ||||
| final class StatusState { | ||||
|     use EnumTrait; | ||||
| final class StatusState extends Enum { | ||||
|  | ||||
|     /** @var string The status is an error. */ | ||||
|     const error = 'error'; | ||||
|  | ||||
| @ -36,7 +36,7 @@ class Tag extends AbstractApiModel { | ||||
|      * Creates a new tag | ||||
|      * @param object $client The Gitea client that originally made the request for this object's data | ||||
|      * @param object|null $caller The object that called this method | ||||
|      * @param int $id The tag identifier | ||||
|      * @param int|string $id The tag identifier | ||||
|      * @param string $name The tag name | ||||
|      */ | ||||
|     public function __construct(Client &$client , ?object $caller, ...$args) { | ||||
| @ -44,9 +44,9 @@ class Tag extends AbstractApiModel { | ||||
|         if (count($args) >= 2) { | ||||
|             $id = $args[0]; | ||||
|             $name = $args[1]; | ||||
|             if (!is_int($id)) { | ||||
|             if (!is_string($id)) { | ||||
|                 $argumentType = gettype($id); | ||||
|                 throw new InvalidArgumentException("The \"__construct()\" function requires the 3rd parameter to be of the integer type, but a \"$argumentType\" was passed in"); | ||||
|                 throw new InvalidArgumentException("The \"__construct()\" function requires the 3rd parameter to be of the string type, but a \"$argumentType\" was passed in"); | ||||
|             } | ||||
|             if (!is_string($name)) { | ||||
|                 $argumentType = gettype($name); | ||||
| @ -71,7 +71,7 @@ class Tag extends AbstractApiModel { | ||||
|         $newTag = new static( | ||||
|             $client, | ||||
|             $caller, | ||||
|             isset($map->id) && is_int($map->id) ? $map->id : -1, | ||||
|             isset($map->id) && is_string($map->id) ? $map->id : -1, | ||||
|             isset($map->name) && is_string($map->name) ? $map->name : '' | ||||
|         ); | ||||
|         $newTag->setTarballURL(isset($map->tarball_url) && is_string($map->tarball_url) ? new Uri($map->tarball_url) : null); | ||||
| @ -104,7 +104,7 @@ class Tag extends AbstractApiModel { | ||||
|      * Gets the tag identifier. | ||||
|      * @return int The tag identifier. | ||||
|      */ | ||||
|     function getId(): int { | ||||
|     function getId(): string { | ||||
|         return $this->id; | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -32,7 +32,7 @@ class Team implements \JsonSerializable { | ||||
|      * @return static The instance corresponding to the specified JSON map. | ||||
|      */ | ||||
|     static function fromJson(object $map): self { | ||||
|         return (new static(isset($map->id) && is_int($map->id) ? $map->id : -1, isset($map->name) && is_string($map->name) ? $map->name : '')) | ||||
|         return (new static(isset($map->id) && is_numeric($map->id) ? $map->id : -1, isset($map->name) && is_string($map->name) ? $map->name : '')) | ||||
|             ->setDescription(isset($map->description) && is_string($map->description) ? $map->description : '') | ||||
|             ->setPermission(isset($map->permission) && is_string($map->permission) ? $map->permission : TeamPermission::none); | ||||
|     } | ||||
|  | ||||
| @ -1,11 +1,10 @@ | ||||
| <?php declare(strict_types=1); | ||||
| namespace Gitea\Model; | ||||
|  | ||||
| use Enum\{EnumTrait}; | ||||
| use MyCLabs\Enum\Enum; | ||||
|  | ||||
| /** Defines the permission of a team. */ | ||||
| final class TeamPermission { | ||||
|     use EnumTrait; | ||||
| final class TeamPermission extends Enum { | ||||
|  | ||||
|     /** @var string The team has the administrator permission. */ | ||||
|     const admin = 'admin'; | ||||
|  | ||||
| @ -35,10 +35,10 @@ class TrackedTime implements \JsonSerializable { | ||||
|      * @return static The instance corresponding to the specified JSON map. | ||||
|      */ | ||||
|     static function fromJson(object $map): self { | ||||
|         return (new static(isset($map->id) && is_int($map->id) ? $map->id : -1, isset($map->time) && is_int($map->time) ? $map->time : 0)) | ||||
|         return (new static(isset($map->id) && is_numeric($map->id) ? $map->id : -1, isset($map->time) && is_numeric($map->time) ? $map->time : 0)) | ||||
|             ->setCreatedAt(isset($map->created) && is_string($map->created) ? new \DateTime($map->created) : null) | ||||
|             ->setIssueId(isset($map->issue_id) && is_int($map->issue_id) ? $map->issue_id : -1) | ||||
|             ->setUserId(isset($map->user_id) && is_int($map->user_id) ? $map->user_id : -1); | ||||
|             ->setIssueId(isset($map->issue_id) && is_numeric($map->issue_id) ? $map->issue_id : -1) | ||||
|             ->setUserId(isset($map->user_id) && is_numeric($map->user_id) ? $map->user_id : -1); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|  | ||||
| @ -41,7 +41,7 @@ class User implements \JsonSerializable { | ||||
|      * @return static The instance corresponding to the specified JSON map. | ||||
|      */ | ||||
|     static function fromJson(object $map): self { | ||||
|         return (new static(isset($map->id) && is_int($map->id) ? $map->id : -1, isset($map->login) && is_string($map->login) ? $map->login : '')) | ||||
|         return (new static(isset($map->id) && is_numeric($map->id) ? $map->id : -1, isset($map->login) && is_string($map->login) ? $map->login : '')) | ||||
|             ->setAvatarUrl(isset($map->avatar_url) && is_string($map->avatar_url) ? new Uri($map->avatar_url) : null) | ||||
|             ->setEmail(isset($map->email) && is_string($map->email) ? mb_strtolower($map->email) : '') | ||||
|             ->setFullName(isset($map->full_name) && is_string($map->full_name) ? $map->full_name : '') | ||||
|  | ||||
| @ -59,9 +59,10 @@ class PushEvent extends AbstractApiModel { | ||||
|      * @param array $server The HTTP SERVER array for the push event | ||||
|      * @param string $body The raw data from the request body | ||||
|      * @param string $secretKey The secret key to from your server | ||||
|      * @return void | ||||
|      * @param bool $skipSecretValidation If set to true, secret key validation will be skipped (used for newer versions of Gitea) | ||||
|      * @return bool | ||||
|      */ | ||||
|     public static function validateRequest(array $server, string $body, string $secretKey) | ||||
|     public static function validateRequest(array $server, string $body, string $secretKey, bool $skipSecretValidation = false) | ||||
|     { | ||||
|         // Validate request protocol | ||||
|         if ($server['REQUEST_METHOD'] != 'POST') { | ||||
| @ -80,18 +81,20 @@ class PushEvent extends AbstractApiModel { | ||||
|             throw new \RuntimeException("FAILED: Empty Body - The request has an empty body"); | ||||
|         } | ||||
|  | ||||
|         // Validate header signature | ||||
|         $headerSignature = isset($server['HTTP_X_GITEA_SIGNATURE']) ? $server['HTTP_X_GITEA_SIGNATURE'] : ''; | ||||
|         if (empty($headerSignature)) { | ||||
|             throw new \RuntimeException("FAILED: Signature Missing - The request is missing the Gitea signature"); | ||||
|         } | ||||
|         if (!$skipSecretValidation) { | ||||
|             // Validate header signature | ||||
|             $headerSignature = isset($server['HTTP_X_GITEA_SIGNATURE']) ? $server['HTTP_X_GITEA_SIGNATURE'] : ''; | ||||
|             if (empty($headerSignature)) { | ||||
|                 throw new \RuntimeException("FAILED: Signature Missing - The request is missing the Gitea signature"); | ||||
|             } | ||||
|      | ||||
|         // calculate payload signature | ||||
|         $payload_signature = hash_hmac('sha256', $rawContent, $secretKey, false); | ||||
|             // calculate payload signature | ||||
|             $payload_signature = hash_hmac('sha256', $rawContent, $secretKey, false); | ||||
|      | ||||
|         // check payload signature against header signature | ||||
|         if ($headerSignature != $payload_signature) { | ||||
|             throw new \RuntimeException("FAILED: Access Denied - The push event's secret does not match the expected secret"); | ||||
|             // check payload signature against header signature | ||||
|             if ($headerSignature != $payload_signature) { | ||||
|                 throw new \RuntimeException("FAILED: Access Denied - The push event's secret does not match the expected secret"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|  | ||||
| @ -82,7 +82,7 @@ if ($repository) { | ||||
| } | ||||
|  | ||||
| // print("Getting contents of \"composer.json\" file \n\n"); | ||||
| // $rawFile = $giteaClient->repositories()->getRawFile("Sitelease", "sl-theme-recipe", "composer.json"); | ||||
| // $rawFile = $giteaClient->repositories()->getFileContents("Sitelease", "sl-theme-recipe", "composer.json"); | ||||
| // if ($rawFile) { | ||||
| //     var_dump(json_encode($rawFile)); | ||||
| //     print("\n\n"); | ||||
| @ -91,7 +91,7 @@ if ($repository) { | ||||
| // } | ||||
|  | ||||
| // print("Getting contents of \"composer.json\" file \n\n"); | ||||
| // $rawFile = $giteaClient->repositories()->getRawFile("Sitelease", "sl-theme-recipe", "composer.json"); | ||||
| // $rawFile = $giteaClient->repositories()->getFileContents("Sitelease", "sl-theme-recipe", "composer.json"); | ||||
| // if ($rawFile) { | ||||
| //     var_dump($rawFile); | ||||
| //     print("\n\n"); | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	