Huge number of updates, too tired to list them all

+ Added a bunch of API handler classes that will use the already created models
+ Created a new Client class that will connect to new API handler classes
+ Created new collection classes
This commit is contained in:
Benjamin Blake
2020-02-19 22:10:59 -07:00
parent f530671000
commit 1bf0c070f8
21 changed files with 1007 additions and 3 deletions

View File

@ -3,7 +3,7 @@ root = true
[*]
charset = utf-8
indent_size = 2
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

View File

@ -31,7 +31,7 @@
"ext-curl": "*",
"ext-json": "*",
"cedx/enum": "^7.4.0",
"guzzlehttp/guzzle": "^6.3.3"
"guzzlehttp/guzzle": "~6.0"
},
"require-dev": {
"cedx/coveralls": "^10.1.0",

319
src/Api/AbstractApi.php Normal file
View File

@ -0,0 +1,319 @@
<?php
namespace Gitea\Api;
use Gitea\Client;
/**
* Abstract class for Api classes
*
* @author Benjamin Blake (sitelease.ca)
*/
abstract class AbstractApi
{
/**
* The client
*
* @var Client
*/
protected $client;
/**
* The API authentication token for Gitea
*
* @var string
*/
private $authToken;
/**
* The default parameters that should be sent
* in ALL requests to this api route
*
* @var array
*/
protected $defaultParameters = [];
/**
* The default headers that should be sent
* in ALL requests to this api route
*
* @var array
*/
protected $defaultHeaders = [
'Accept' => 'application/json'
];
/**
* The default parameters that should be sent
* in all GET requests to this api route
*
* @var array
*/
protected $getDefaultParameters = [];
/**
* The default headers that should be sent
* in all GET requests to this api route
*
* @var array
*/
protected $getDefaultHeaders = [];
/**
* The default headers that should be sent
* in all POST requests to this api route
*
* @var array
*/
protected $postDefaultHeaders = [
'Content-Type' => 'application/json'
];
/**
* The default headers that should be sent
* in all PUT requests to this api route
*
* @var array
*/
protected $putDefaultHeaders = [
'Content-Type' => 'application/json'
];
/**
* The default headers that should be sent
* in all DELETE requests to this api route
*
* @var array
*/
protected $deleteDefaultHeaders = [];
/**
* @param Client $client
*/
public function __construct(Client $client, $authToken)
{
$this->client = $client;
$this->authToken = $authToken;
// Set authorization headers and parameters
$this->getDefaultParameters["access_token"] = $authToken;
$this->postDefaultHeaders["Authorization"] = "token $authToken";
$this->putDefaultHeaders["Authorization"] = "token $authToken";
$this->deleteDefaultHeaders["Authorization"] = "token $authToken";
}
public function getClient() {
return $this->client;
}
public function getAuthToken() {
return $this->authToken;
}
/**
* Get the default parameters for a particular type of request
*
* If "all" is passed OR if the $type parameter is left blank
* an array of defaults for ALL request types will be returned
*
* @author Benjamin Blake (sitelease.ca)
*
* @param string $type The type of request ("all","get","post","put",etc.)
* @return array
*/
public function getDefaultParametersForType($type = "all") {
if (!$type || $type == "all") {
return $this->defaultParameters;
} else {
$propertyName = $type."DefaultParameters";
if (property_exists(__CLASS__, $propertyName) && is_array($this->$propertyName)) {
return array_merge($this->defaultParameters, $this->$propertyName);
} else {
return [];
}
}
}
/**
* Get the default headers for a particular type of request
*
* If "all" is passed OR if the $type parameter is left blank
* an array of defaults for ALL request types will be returned
*
* @author Benjamin Blake (sitelease.ca)
*
* @param string $type The type of request ("all", "get","post","put",etc.)
* @return array
*/
public function getDefaultHeadersForType($type = "all") {
if (!$type || $type == "all") {
return $this->defaultHeaders;
} else {
$propertyName = $type."DefaultHeaders";
if (property_exists(__CLASS__, $propertyName) && is_array($this->$propertyName)) {
return array_merge($this->defaultParameters, $this->$propertyName);
} else {
return [];
}
}
}
/**
* @return $this
* @codeCoverageIgnore
*/
public function configure()
{
return $this;
}
/**
* Performs a GET query and returns the response as a PSR-7 response object.
*
* @param string $path
* @param array $parameters
* @param array $requestHeaders
* @return ResponseInterface
*/
protected function getAsResponse($path, array $parameters = array(), $requestHeaders = array())
{
$path = $this->preparePath($path, $parameters);
return $this->client->getHttpClient()->get($path, $requestHeaders);
}
/**
* @param string $path
* @param array $parameters
* @param array $requestHeaders
* @return mixed
*/
protected function get($path, array $parameters = array(), $requestHeaders = array(), $debugRequest = false)
{
$client = $this->getClient();
$guzzleClient = $client->getGuzzleClient();
$defaultParameters = $this->getDefaultParametersForType("get");
$defaultHeaders = $this->getDefaultParametersForType("get");
// Create request options array
// and populate it with defaults
$requestOptions = [];
$requestOptions['query'] = $defaultParameters;
$requestOptions['headers'] = $defaultHeaders;
if ($debugRequest) {
$requestOptions['debug'] = true;
}
if ($parameters) {
$requestOptions['query'] = array_merge($defaultParameters, $parameters);
}
if ($requestHeaders) {
$requestOptions['headers'] = array_merge($defaultHeaders, $requestHeaders);
}
return $guzzleClient->request('GET', $path, $requestOptions);
}
/**
* @param string $path
* @param array $body
* @param array $requestHeaders
* @return mixed
*/
protected function post($path, $body, $requestHeaders = array(), $debugRequest = false)
{
$client = $this->getClient();
$guzzleClient = $client->getGuzzleClient();
$defaultHeaders = $this->getDefaultHeadersForType("post");
// Create request options array
// and populate it with defaults
$requestOptions = [];
$requestOptions['headers'] = $defaultHeaders;
$requestOptions['body'] = "{}";
if ($debugRequest) {
$requestOptions['debug'] = true;
}
if ($body) {
if (is_object($body)) {
$requestOptions['body'] = json_encode($body);
}
if (is_string($body)) {
$requestOptions['body'] = $body;
}
}
if ($requestHeaders) {
$requestOptions['headers'] = array_merge($defaultHeaders, $requestHeaders);
}
return $guzzleClient->request('POST', $path, $requestOptions);
}
/**
* @param string $path
* @param array $body
* @param array $requestHeaders
* @return mixed
*/
protected function put($path, $body, $requestHeaders = array(), $debugRequest = false)
{
$client = $this->getClient();
$guzzleClient = $client->getGuzzleClient();
$defaultHeaders = $this->getDefaultHeadersForType("put");
// Create request options array
// and populate it with defaults
$requestOptions = [];
$requestOptions['headers'] = $defaultHeaders;
$requestOptions['body'] = "{}";
if ($debugRequest) {
$requestOptions['debug'] = true;
}
if ($body) {
if (is_object($body)) {
$requestOptions['body'] = json_encode($body);
}
if (is_string($body)) {
$requestOptions['body'] = $body;
}
}
if ($requestHeaders) {
$requestOptions['headers'] = array_merge($defaultHeaders, $requestHeaders);
}
return $guzzleClient->request('PUT', $path, $requestOptions);
}
/**
* @param string $path
* @param array $requestHeaders
* @return mixed
*/
protected function delete($path, $requestHeaders = array(), $debugRequest = false)
{
$client = $this->getClient();
$guzzleClient = $client->getGuzzleClient();
$defaultHeaders = $this->getDefaultHeadersForType("delete");
// Create request options array
// and populate it with defaults
$requestOptions = [];
$requestOptions['headers'] = $defaultHeaders;
if ($debugRequest) {
$requestOptions['debug'] = true;
}
if ($requestHeaders) {
$requestOptions['headers'] = array_merge($defaultHeaders, $requestHeaders);
}
return $guzzleClient->request('DELETE', $path, $requestOptions);
}
}

46
src/Api/Organizations.php Normal file
View File

@ -0,0 +1,46 @@
<?php
namespace Gitea\Api;
use GuzzleHttp\Psr7\Response;
use Gitea\Client;
use Gitea\Models\Organization;
use Gitea\Api\AbstractApi;
class Organizations extends AbstractApi
{
/**
* Get an organization using its username and parse
* it's information into an organization object
*
* Example:
* ```
* $giteaClient->organizations()->getByUsername($orgUsername);
* ```
*
* @param string $username
* @author Benjamin Blake (sitelease.ca)
*
* @return Organization|Response
*/
public function getByUsername(string $username)
{
$client = $this->getClient();
try {
$response = $this->get("orgs/$username");
$statusCode = $response->getStatusCode();
$body = $response->getBody();
if ($statusCode == 200) {
return Organization::fromJson(json_decode($body));
} else {
return $response;
}
} catch (ServerException $serverError) {
return $response;
}
}
}

115
src/Api/Repositories.php Normal file
View File

@ -0,0 +1,115 @@
<?php
namespace Gitea\Api;
use Gitea\Client;
use Gitea\Collections\ApiItemCollection;
use Gitea\Models\Repository;
use Gitea\Api\AbstractApi;
class Repositories extends AbstractApi
{
/**
* The maximum number of pages to process when
* retrieving all repositories
*
* NOTE: Each page will contain the number of items
* set in the $maxPageCount property (50 by default).
* So you can get the number of records that this will equate to
* by multiplying this number by the $maxPageCount
*
* @var integer
*/
private $maxPageCount = 25;
/**
* The number of items per page
*
* @var integer
*/
private $itemsPerPage = 50;
/**
* Return a collection of all the repositories
*
* @author Benjamin Blake (sitelease.ca)
*
* @return ApiItemCollection
*/
public function all()
{
$allItems = array();
$maxPageCount = $this->getMaxPageCount();
// Loop over pages until the $maxPageCount is reached
for ($pageNum=1; $pageNum < $maxPageCount; $pageNum++) {
$searchItemsCollection = $this->search("", $pageNum);
if ($searchItemsCollection && $searchItemsCollection->count() > 0) {
$searchItemsArray = $searchItemsCollection->toArray();
$allItems = array_merge($allItems, $searchItemsArray);
} else {
break;
}
}
return new ApiItemCollection($allItems);
}
/**
* Search all repositories and return a list of them
*
* Example:
* ```
* $giteaClient->repositories()->search($keyword, $page);
* ```
*
* @param string $keyword Keyword to search by
* @param integer $page The page of items to return
* @param integer $limit Maximum number of items per page
* @param array $extraOptions An array of extra options to pass the API reoute
* @return void
*
* @return ApiItemCollection
*/
public function search(string $keyword = "", int $page = 1, int $limit = null, array $extraOptions = array())
{
$client = $this->getClient();
$limit = $limit ?? $this->getItemsPerPage();
$repositoryCollection = new ApiItemCollection();
try {
$response = $this->get("repos/search",[
"page" => $page,
"limit" => $limit
]);
$statusCode = $response->getStatusCode();
$body = (string) $response->getBody();
if ($statusCode == 200) {
$jsonObj = json_decode($body, true);
$jsonOk = $jsonObj["ok"];
$jsonItemList = $jsonObj["data"];
if ($jsonOk && count($jsonItemList) > 0) {
foreach ($jsonItemList as $jsonItem) {
$encodedItem = json_encode($jsonItem);
$itemObject = Repository::fromJson(json_decode($encodedItem));
$repositoryCollection->addItem($itemObject, $itemObject->getId());
}
}
}
return $repositoryCollection;
} catch (ServerException $serverError) {
return $repositoryCollection;
}
}
public function getMaxPageCount() {
return $this->maxPageCount;
}
public function getItemsPerPage() {
return $this->itemsPerPage;
}
}

147
src/Client.php Normal file
View File

@ -0,0 +1,147 @@
<?php
namespace Gitea;
use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Exception\ServerException;
use Gitea\Models\Repository;
use Gitea\Models\Tag;
use Gitea\Models\Branch;
use Gitea\Api\Repositories;
use Gitea\Api\Organizations;
use \JsonSerializable;
/** Represents a Gitea push event. */
class Client {
/**
* Stores an instance of Guzzle's Client
*
* @var GuzzleClient
*/
private $guzzleClient;
/**
* The base URL for Gitea
*
* All API requests will be relative to this URL
*
* @var string
*/
private $giteaURL;
/**
* The API authentication token for Gitea
*
* @var string
*/
private $authToken;
public function __construct($giteaURL, $authToken = null)
{
// Append a slash to any URL that doesn't end in '/'
if (!$this->endsWith($giteaURL, '/')) {
$giteaURL += "/";
}
$this->giteaURL = $giteaURL;
$this->authToken = $authToken;
// Create a new Guzzle Client
$this->guzzleClient = new GuzzleClient(['base_uri' => $giteaURL]);
}
public function getGuzzleClient()
{
return $this->guzzleClient;
}
public function getAuthToken() {
return $this->authToken;
}
public function setAuthToken($value) {
$this->authToken = $value;
return $this;
}
public function getBaseURL()
{
return $this->giteaURL;
}
/**
* Return true if first string ends with the second string.
* Otherwise return false
*
* @author Benjamin Blake (sitelease.ca)
*
* @param string $haystack
* @param string $needle
* @return boolean
*/
function endsWith($haystack, $needle)
{
return substr($haystack, -strlen($needle))===$needle;
}
/**
* Checks to make sure the auth token is in the right format
*
* Will raise errors if the token is in the wrong format
*
* @author Benjamin Blake (sitelease.ca)
*
* @return boolean
*/
public function checkAuthToken($softErrors = false) {
$authToken = $this->authToken;
if (!$authToken) {
if ($softErrors) {
print("No authentication token was passed in"."\n");
} else {
trigger_error("No authentication token was passed in");
}
return false;
}
if (!strlen($authToken) >= 40) {
if ($softErrors) {
print("The authentication token is too short, it must be at lease 40 characters long"."\n");
} else {
trigger_error("The authentication token is too short, it must be at lease 40 characters long");
}
return false;
}
return true;
}
/**
* Return the Repositories api object
*
* @author Benjamin Blake (sitelease.ca)
*
* @return Repositories
*/
public function repositories()
{
return new Repositories($this, $this->getAuthToken());
}
/**
* Return the Repositories api object
*
* @author Benjamin Blake (sitelease.ca)
*
* @return Organizations
*/
public function organizations()
{
return new Organizations($this, $this->getAuthToken());
}
}

View File

@ -0,0 +1,86 @@
<?php
namespace Gitea\Collections;
use Gitea\Api\AbstractApi;
interface ApiCollectionInterface
{
/**
* Construct the collection
*
* @author Benjamin Blake (sitelease.ca)
*
* @param array $internalArray
*/
public function __construct($internalArray = array());
/**
* Add an item to the collection
*
* @author Benjamin Blake (sitelease.ca)
*
* @param AbstractApi $apiObject
* @param string $key
*/
public function addItem($apiObject, $key = null);
/**
* Remove an existing item from the collection
*
* @author Benjamin Blake (sitelease.ca)
*
* @param string $key
*/
public function deleteItem($key);
/**
* Get an existing item from the collection
*
* @author Benjamin Blake (sitelease.ca)
*
* @param string $key
*/
public function getItem($key);
/**
* Get a list of the keys in the collection
*
* @author Benjamin Blake (sitelease.ca)
*
* @param string $key
*/
public function keys();
/**
* Return the number of items in the collection
*
* @author Benjamin Blake (sitelease.ca)
*/
public function count();
/**
* Return true if a key existing in the collection
*
* @author Benjamin Blake (sitelease.ca)
*
* @param string $key
*/
public function keyExists($key);
/**
* Convert the collection to an array
*
* @author Benjamin Blake (sitelease.ca)
*/
public function toArray();
/**
* Required definition of interface IteratorAggregate
*
* This method makes the collection work with loops
*
* @author Benjamin Blake (sitelease.ca)
*/
public function getIterator();
}

View File

@ -0,0 +1,69 @@
<?php
namespace Gitea\Collections;
use Gitea\Collections\ApiCollectionInterface;
use \Countable;
use \IteratorAggregate;
use \ArrayIterator;
class ApiItemCollection implements ApiCollectionInterface, IteratorAggregate, Countable
{
private $items = array();
public function __construct($internalArray = array()) {
if ($internalArray && is_array($internalArray)) {
$this->items = $internalArray;
}
}
public function addItem($apiObject, $key = null) {
if ($key == null) {
$this->items[] = $apiObject;
}
else {
if (isset($this->items[$key])) {
return false;
} else {
$this->items[$key] = $apiObject;
}
}
}
public function deleteItem($key) {
if (isset($this->items[$key])) {
unset($this->items[$key]);
} else {
return false;
}
}
public function getItem($key) {
if (isset($this->items[$key])) {
return $this->items[$key];
} else {
return false;
}
}
public function keys() {
return array_keys($this->items);
}
public function count() {
return count($this->items);
}
public function keyExists($key) {
return isset($this->items[$key]);
}
public function toArray() {
return $this->items;
}
public function getIterator() {
return new ArrayIterator($this->items);
}
}

149
src/Model/Organization.php Normal file
View File

@ -0,0 +1,149 @@
<?php
namespace Gitea\Models;
use GuzzleHttp\Psr7\Uri;
use Psr\Http\Message\UriInterface;
use \JsonSerializable;
/** Represents a Gitea organization. */
class Organization implements JsonSerializable {
/** @var UriInterface|null A URL pointing to the organization's avatar. */
private $avatarURL = '';
/** @var string The organization description. */
private $description = '';
/** @var string The organization's full name. */
private $fullName = '';
/** @var int The organization identifier. */
private $id = -1;
/** @var string The organization location. */
private $location;
/** @var string The username of the organization */
private $username;
/** @var string The visibility of the organization */
private $visibility;
/** @var UriInterface|null The website URL of the organization */
private $website = null;
/**
* Creates a new organization.
* @param string $username The organization name.
* @param string $visibility The organization visibility.
*/
function __construct(string $username, string $visibility = "private") {
$this->setUserName($username);
$this->setVisibility($visibility);
}
/**
* Creates a new organization from the specified JSON map.
* @param object $map A JSON map representing an organization.
* @return static The instance corresponding to the specified JSON map.
*/
static function fromJson(object $map): self {
return (new static(isset($map->username) && is_string($map->username) ? $map->username : '', isset($map->visibility) && is_string($map->visibility) ? $map->visibility : 'private'))
->setAvatarURL(isset($map->avatar_url) && is_string($map->avatar_url) ? new Uri($map->avatar_url) : null)
->setDescription(isset($map->description) && is_string($map->description) ? $map->description : '')
->setFullName(isset($map->full_name) && is_string($map->full_name) ? $map->full_name : '')
->setLocation(isset($map->location) && is_string($map->location) ? $map->location : '')
->setWebsite(isset($map->website) && is_string($map->website) ? new Uri($map->website) : null);
}
/**
* Converts this object to a map in JSON format.
* @return \stdClass The map in JSON format corresponding to this object.
*/
function jsonSerialize(): \stdClass {
return (object) [
'avatar_url' => ($url = $this->getAvatarURL()) ? (string) $url : null,
'description' => $this->getDescription(),
'full_name' => $this->getFullName(),
'id' => $this->getId(),
'location' => $this->getLocation(),
'username' => $this->getUsername(),
'visibility' => $this->getVisibility(),
'website' => ($url = $this->getWebsite()) ? (string) $url : null
];
}
/**
* Gets the organization identifier.
* @return int The organization identifier.
*/
function getId(): int {
return $this->id;
}
public function getAvatarURL(): ?UriInterface {
return $this->avatarURL;
}
public function setAvatarURL(?UriInterface $value): self {
$this->avatarURL = $value;
return $this;
}
public function getDescription() {
return $this->description;
}
public function setDescription(string $value): self {
$this->description = $value;
return $this;
}
public function getFullName() {
return $this->fullName;
}
public function setFullName(string $value): self {
$this->fullName = $value;
return $this;
}
public function getLocation() {
return $this->location;
}
public function setLocation(string $value): self {
$this->location = $value;
return $this;
}
public function getUsername() {
return $this->username;
}
public function setUsername(string $value): self {
$this->username = $value;
return $this;
}
public function getVisibility() {
return $this->visibility;
}
public function setVisibility(string $value): self {
$this->visibility = $value;
return $this;
}
public function getWebsite(): ?UriInterface {
return $this->website;
}
public function setWebsite(?UriInterface $value): self {
$this->website = $value;
return $this;
}
}

View File

@ -81,7 +81,7 @@ class Repository implements \JsonSerializable {
* @param int $id The repository identifier.
* @param string $fullName The full name of the repository.
*/
function __construct(int $id, string $fullName) {
function __construct(int $id, string $fullName, object $giteaClient = null) {
$this->id = $id;
$this->setFullName($fullName);
}
@ -552,4 +552,38 @@ class Repository implements \JsonSerializable {
$this->website = $value;
return $this;
}
/**
* Undocumented function
*
* Call:
* $giteaClient->projects->fetchall
*
* @author Benjamin Blake (sitelease.ca)
*
* @param Type $var
* @return void
*/
public static function getAll(Type $var = null)
{
}
/**
* Undocumented function
*
* Example:
* ```
* $giteaClient->repositories->fetchall();
* ```
*
* @author Benjamin Blake (sitelease.ca)
*
* @param Type $var
* @return void
*/
public function get(Type $var = null)
{
}
}

39
src/TestRun.php Normal file
View File

@ -0,0 +1,39 @@
<?php
namespace Gitea;
require 'vendor/autoload.php';
use Gitea\Client;
print("Starting Script... \n");
print("Creating Guzzle client \n");
$giteaClient = new Client("https://gitea.devserver.localdomain:3000/api/v1/");
$giteaClient->setAuthToken('32e609ad39539c1d0e8544800bd49b1025ac6b49');
// print("Getting Organization via API \n");
// $organization = $giteaClient->organizations()->getByUsername("Sitelease");
// if ($organization) {
// print("Username: ".$organization->getUsername()."\n");
// print("Full Name: ".$organization->getFullName()."\n");
// print("Description: ".$organization->getDescription()."\n");
// print("Website: ".$organization->getWebsite()."\n");
// print("Location: ".$organization->getLocation()."\n");
// var_dump(json_encode($organization));
// } else {
// print("No Data could be retrieved"."\n");
// }
print("Getting all repos via API \n");
$repositories = $giteaClient->repositories()->all();
if ($repositories && count($repositories) > 0) {
foreach ($repositories as $item) {
print("Name: ".$item->getName()."\n");
print("Full Name: ".$item->getFullName()."\n");
print("Description: ".$item->getDescription()."\n");
print("\n\n");
}
print("Total Items: ".count($repositories)."\n");
} else {
print("No repos could be retrieved"."\n");
}
print("Exiting script"."\n");