Browse Source

Rewritten; see index file for examples.

Joachim M. Giæver 6 years ago
parent
commit
7616dcda15

+ 7 - 5
client/gogsapi.php

@@ -8,14 +8,16 @@ class GogsAPI {
         $this->token = $api_token;
     }
 
-    public function user($name = "") {
-        return new Request\User($this->url, $this->token, $name);
+    public function users() {
+        return new Request\Users($this->url, $this->token);
     }
 
-    public function repos($user = "") {
-        if ($user == "")
-            return $this->user()->get("/repos");
+    public function repos() {
+        return new Request\Repos($this->url, $this->token);
+    }
 
+    public function user(string $name = "me") {
+        return new Request\User($this->url, $this->token, $name);
     }
 
     public function __destruct() {}

+ 95 - 0
client/request/base.php

@@ -0,0 +1,95 @@
+<?php namespace Client\Request;
+
+abstract class Base implements RequestInterface {
+
+    protected $loaded = false;
+    protected $scope;
+
+    use \Lib\Curl {
+        get as private mget;
+        post as protected mpost;
+        delete as protected mdelete;
+    }
+
+    public function __construct(string $api_url, string $api_token) {
+        $this->url = $api_url;
+        $this->token = $api_token;
+    }
+
+    public function load(bool $force = false) {
+        $this->set_scope("get");
+
+        if ($this->loaded && !$force)
+            return $this;
+
+        $jenc =  $this->mget($this->scope);
+
+        $this->json_set_property($this->json_decode($jenc));
+        $this->loaded = true;
+
+        return $this;
+    }
+
+    protected function method_get(array $params = array()) {
+        return $this->mget($this->scope, $params);
+    }
+
+    protected function method_post(array $params = array()) {
+        return $this->mpost($this->scope, $params);
+    }
+
+    protected function method_delete() {
+        return $this->mdelete($this->scope);
+    }
+
+    public function get(string $s) {
+        $this->set_scope("get");
+    }
+
+    public function create(...$args) {
+        $this->set_scope("create");
+        $ret = $this->method_post(...$args);
+
+        $this->json_set_property($this->json_decode($ret));
+        $this->loaded = true;
+
+        return true;
+    }
+
+    public function patch() {
+        $this->set_scope("patch");
+    }
+
+    public function delete() {
+        $this->set_scope("delete");
+        return $this->method_delete();
+    }
+
+    protected function json_decode(string $jenc) {
+        $obj = json_decode($jenc);
+
+        $this->json_error();
+
+        return $obj;
+    }
+
+    protected function json_encode(iterable $jdec) {
+        $jenc = json_encode($jdec);
+
+        $this->json_error();
+
+        return $jenc;
+    }
+
+    protected function json_error() {
+        if (($err = json_last_error()) != JSON_ERROR_NONE)
+            throw new RequestErrorException(json_last_error_msg(), $err);
+    }
+
+    abstract protected function json_set_property($obj);
+    abstract protected function set_scope(string $method);
+
+    public function __destruct() {}
+}
+
+?>

+ 99 - 0
client/request/collection.php

@@ -0,0 +1,99 @@
+<?php namespace Client\Request;
+
+class SearchParamException extends \Exception {};
+
+abstract class Collection extends Base implements \Lib\ArrayIterator {
+    private $objs = array();
+
+    public function add($obj, $key = null) {
+
+        if ($obj == false)
+            echo "OBJ ($key) = FALSE\n";
+
+        if (!isset($key))
+            array_push($this->objs, $obj);
+        else
+            $this->objs[$key] = $obj;
+    }
+
+    public function remove($any) {
+        if (isset($this->objs[$any])) {
+            unset($this->objs[$any]);
+            return true;
+        } else if (in_array($any, $this->objs)) {
+            $key = array_search($any, $this->objs, true);
+            return $this->remove($key);
+        }
+        return false;
+    }
+
+    public function all() {
+        return $this->objs;
+    }
+
+    public function len() {
+        return $this->objs;
+    }
+
+    public function by_key($idx) {
+        return isset($this->objs[$idx]) ? $this->objs[$idx] : false;
+    }
+
+    public function next() {
+        return next($this->objs);
+    }
+
+    public function prev() {
+        return prev($this->objs);
+    }
+
+    public function current() {
+        return current($this->objs);
+    }
+
+    public function reset() {
+        return reset($this->objs);
+    }
+
+    abstract public function new(...$args);
+    abstract public function search(array $params = array());
+}
+
+class CollectionResult implements \Lib\ArrayIterator {
+    private $objs = array();
+
+    public function set($val, $key = null) {
+        if ($key == null && is_array($val))
+            $this->objs = $val;
+        else if ($key != null)
+            $this->objs[$key] = $val;
+        else 
+            array_push($this->objs, $val);
+    }
+
+    public function by_key($idx) {
+        return isset($this->objs[$idx]) ? $this->objs[$idx] : false;
+    }
+
+    public function all() {
+        return $this->objs;
+    }
+
+    public function len() {
+        return count($this->objs);
+    }
+
+    public function next() {
+        return next($this->objs);
+    }
+
+    public function current() {
+        return current($this->objs);
+    }
+
+    public function reset() {
+        return current($this->objs);
+    }
+
+}
+?>

+ 100 - 0
client/request/repo.php

@@ -0,0 +1,100 @@
+<?php namespace Client\Request;
+
+class Repo extends Base {
+    
+    public $rid;
+    public $rowner;
+    public $rname;
+    public $rfull_name;
+    public $rdescription;
+    public $rprivate;
+    public $rfork;
+    public $rparent;
+    public $rempty;
+    public $rmirror;
+    public $rsize;
+    public $rhtml_url;
+    public $rssh_url;
+    public $rclone_url;
+    public $rwebsite;
+    public $rstars_count;
+    public $rforks_count;
+    public $rwatchers_count;
+    public $ropen_issues_count;
+    public $rdefault_branch;
+    public $rcreated_at;
+    public $rupdated_at;
+    public $rpermissions;
+    public $radmin;
+    public $rpush;
+    public $rpull;
+
+    protected $owner;
+
+    public function __construct(string $api_url, string $api_token, User $owner = null, string $name = null) {
+        $this->rowner = $owner;
+        $this->rname = $name;
+        parent::__construct($api_url, $api_token);
+    }
+
+    protected function set_scope(string $method) {
+        switch ($method) {
+        case "create":
+            $this->scope = "/user" . ($this->rowner->authenticated() ? "" : "/" . $this->rowner->uusername) . "/repos";
+            break;
+        case "delete":
+            $this->scope = "/repos/" . $this->rowner->uusername . "/" . $this->rname;
+            break;
+        default:
+            $this->scope = "/repos/" . ($this->rowner ? $this->rowner->uusername . "/" . $this->rname : $this->rfull_name);
+        }
+    }
+
+    protected function json_set_property($obj) {
+        foreach($obj as $key => $val) {
+            $key = 'r' . $key;
+            if (property_exists($this, $key)) {
+                switch ($key) {
+                case 'rowner':
+                    if (!$this->rowner) {
+                        $user = new User($this->url, $this->token);
+                        $user->json_set_property($val);
+                        $this->{$key} = $user;
+                    }
+                    break;
+                default:
+                    $this->{$key} = $val;
+                }
+            }
+        }
+        $this->loaded = true;
+    }
+
+    public function search(string $q) {
+        $searchable = sprintf("%s %s", $this->rname, $this->rdescription);
+
+        return stripos($searchable, $q) !== false;
+    }
+
+    public function create(...$args) {
+
+        if ($this->loaded)
+            return false;
+
+        $params = array(
+            "name" => isset($args[0]) && is_string($args[0]) ? $args[0] : null,
+            "description" => isset($args[1]) && is_string($args[1]) ? $args[1] : null,
+            "private" => isset($args[2]) && is_bool($args[2]) ? $args[2] : false,
+            "auto_init" => isset($args[3]) && is_bool($args[3]) ? $args[3] : false,
+            "gitignores" => isset($args[4]) && is_string($args[4]) ? $args[4] : null,
+            "licence" => isset($args[5]) && is_string($args[5]) ? $args[5] : null,
+            "readme" => isset($args[6]) && is_string($args[6]) ? $args[6] : "Default"
+        );
+
+        $params = array_filter($params, function($val) {
+            return $val != null;
+        });
+
+        parent::create($params);
+    }
+}

+ 89 - 0
client/request/repos.php

@@ -0,0 +1,89 @@
+<?php namespace Client\Request;
+
+class Repos extends Collection {
+
+    protected $owner;
+
+    public function __construct(string $api_url, string $api_token, User $owner = null) {
+        $this->owner = $owner;
+        parent::__construct($api_url, $api_token);
+    }
+
+    protected function set_scope(string $method) {
+        switch ($method) {
+        default:
+            $this->scope = ($this->owner ? $this->owner->scope : "" ) . "/repos";
+        }
+    }
+
+    public function new(...$args) {
+
+        $repo = new Repo($this->url, $this->token, $this->owner);
+
+        if (count($args) > 0)
+            $repo->create(...$args);
+        return $repo;
+    }
+
+    public function search(array $params = array()) {
+        $scope = substr($this->scope, 1, strpos($this->scope, "/", 1)-1);
+
+        if (!isset($params["name"]) && !isset($params["q"]))
+            throw new SearchParamException("Missing param <name>|<q>");
+
+        if (isset($params["name"])) {
+            $params["q"] = $params["name"];
+            unset($params["name"]);
+        }
+
+        if (!isset($params["user"]))
+            $params["user"] = 0;
+
+        if (!isset($params["limit"]))
+            $params["limit"] = 10;
+
+        $repos = new CollectionResult();
+        
+        switch ($scope) {
+        case "user":
+        case "users":
+        case "org":
+            $this->load();
+
+            foreach($this->all() as $key => $repo) {
+                if ($repo->search($params["q"]))
+                    $repos->set($repo);
+                if ($repos->len() == $params["limit"])
+                    break;
+            }
+
+            return $repos;
+        default:
+            $jenc = $this->method_get("/search", $params);
+            $jdec = $this->json_decode($jenc);
+
+            foreach($this->json_set_property($jdec) as $key)
+                $repos->set($this->by_key($key), $key);
+
+            return $repos;
+        }
+    }
+
+    protected function json_set_property($obj) {
+        if (isset($obj->data))
+            return $this->json_set_property($obj->data);
+
+        $rnames = array();
+
+        if (is_array($obj)) {
+            foreach ($obj as $key => $val) {
+                $repo = new Repo($this->url, $this->token);
+                $repo->json_set_property($val);
+                $this->add($repo, $repo->rfull_name);
+                array_push($rnames, $repo->rfull_name);
+            }
+        }
+
+        return $rnames;
+    }
+}

+ 0 - 62
client/request/requestbase.php

@@ -1,62 +0,0 @@
-<?php namespace Client\Request;
-
-abstract class RequestBase implements RequestInterface {
-
-    protected $loaded = false;
-    protected $scope;
-
-    use \Lib\Curl {
-        get as protected mget;
-    }
-
-    public function __construct($api_url, $api_token) {
-        $this->url = $api_url;
-        $this->token = $api_token;
-        $this->set_scope();
-    }
-
-    public function load($force = false) {
-
-        if ($this->loaded && !$force)
-            return $this;
-
-        $jenc =  $this->mget();
-
-        $this->json_set_property($this->json_decode($jenc));
-        $this->loaded = true;
-
-        return $this;
-    }
-
-    public function get($scope) {
-        throw new NotImplementedException("Method not implemented for " . __CLASS__ . ", cant lookup " . $scope);
-    }
-
-    public function create() {
-        throw new NotImplementedException("Method not implemented for " . __CLASS__ . ", cant create");
-    }
-
-    public function patch() {
-        throw new NotImplementedException("Method not implemented for " . __CLASS__ . ", cant create");
-    }
-
-    public function delete() {
-        throw new NotImplementedException("Method not implemented for " . __CLASS__ . ", cant create");
-    }
-
-    private function json_decode($jenc) {
-        $obj = json_decode($jenc);
-
-        if (($err = json_last_error()) != JSON_ERROR_NONE)
-            throw new RequestErrorException(json_last_error_msg(), $err);
-
-        return $obj;
-    }
-
-    abstract protected function json_set_property($obj);
-    abstract protected function set_scope();
-
-    public function __destruct() {}
-}
-
-?>

+ 3 - 3
client/request/requestinterface.php

@@ -5,9 +5,9 @@ class NotImplementedException extends \BadMethodCallException{}
 
 interface RequestInterface {
 
-    public function load($force = true);
-    public function get($scope);
-    public function create();
+    public function load(bool $force = true);
+    public function get(string $s);
+    public function create(...$args);
     public function patch();
     public function delete();
 }

+ 37 - 41
client/request/user.php

@@ -1,51 +1,47 @@
-<?php 
-
-namespace Client\Request {
-    class User extends RequestBase {
-
-        public $uid;
-        public $ulogin;
-        public $ufull_name;
-        public $uemail;
-        public $uavatar_url;
-        public $uusername;
-
-        protected $_scope = "/user";
+<?php  namespace Client\Request;
+
+class User extends Base {
+
+    private $authenticated;
+    public $uid;
+    public $ulogin;
+    public $ufull_name;
+    public $uemail;
+    public $uavatar_url;
+    public $uusername;
+
+    public function __construct(string $api_url, string $api_token, string $user = "") {
+        $this->ulogin = (strlen($user) == 0 || $user == "me" ? "me" : $user);
+        $this->authenticated = $this->ulogin == "me";
+        parent::__construct($api_url, $api_token);
+    }
 
-        public function __construct($api_url, $api_token, $user) {
-            if (strlen($user) != 0)
-                $this->_scope .= "s/" . $user;
-            parent::__construct($api_url, $api_token);
+    protected function set_scope(string $method) {
+        switch($method) {
+        default:
+            $this->scope = ($this->ulogin == "me" ? "/user" : "/users/" . $this->ulogin);
         }
+    }
 
-        protected function set_scope() {
-            $this->url .= $this->_scope;
-        }
+    public function authenticated() {
+        return $this->authenticated;
+    }
 
-        public function load($force = true) {
-            $ret = parent::load($force);
-            //$this->url = API_URL . "/users/" . $this->ulogin;
-            return $ret;
-        } 
-
-        public function get($scope) {
-            switch ($scope) {
-            case "/repos":
-                return new User\Repos($this->url, $this->token);
-            case "/tokens":
-                return new User\Tokens($this->url, $this->token);
-            }
-        }
+    public function repos() {
+        return new Repos($this->url, $this->token, $this);
+    }
 
-        protected function json_set_property($obj) {
-            foreach ($obj as $key => $value) {
-                $key = 'u' . $key;
-                if (property_exists($this, $key))
-                    $this->{$key} = $value;
-            }
-        }
+    public function repo(string $name) {
+        return (new Repo($this->url, $this->token, $this, $name))->load();
     }
 
+    protected function json_set_property($obj) {
+        foreach ($obj as $key => $value) {
+            $key = 'u' . $key;
+            if (property_exists($this, $key))
+                $this->{$key} = $value;
+        }
+    }
 }
 
 ?>

+ 56 - 0
client/request/users.php

@@ -0,0 +1,56 @@
+<?php namespace Client\Request;
+
+class Users extends Collection {
+
+    protected function set_scope(string $method) {
+        switch ($method) {
+        default:
+            $this->scope = "/users";
+        }
+    }
+
+    public function new(...$args) {
+
+        $user = new User($this->url, $this->token);
+
+        if (count($args) != 0)
+            $user->create($args);
+
+        return $user;
+    }
+
+    public function get(string $s = "") {
+
+        if ($this->by_key($s))
+            return $this->by_key($s);
+
+        return new User($this->url, $this->token, $s);
+    }
+
+    public function search(array $params = array()) {
+
+        if (!isset($params["name"]) && !isset($params['q']))
+            throw new SearchParamException("Missing param <name>|<q>");
+
+        if (isset($params["name"])) {
+            $params["q"] = $params["name"];
+            unset($params["name"]);
+        }
+
+        $jenc = $this->method_get("/search", $params);
+
+        $this->json_set_property($this->json_decode($jenc));
+
+        return $this;
+    }
+
+    protected function json_set_property($obj) {
+        if (isset($obj->data)) {
+            foreach($obj->data as $key => $val) {
+                $user = new User($this->url, $this->token, $val->login);
+                $user->json_set_property($val);
+                $this->add($user, $user->ulogin);
+            }
+        }
+    }
+}

+ 31 - 13
index.php

@@ -9,11 +9,9 @@
 
 define('BASE_PATH', realpath(dirname(__FILE__)));
 define('API_URL', 'https://git.giaever.org/api/v1');
-define('API_TOKEN', '');
-
-function my_autoloader($class)
-{
+define('API_TOKEN', '142efbfd6fbdf147f03d289f8b22a438eaa1b5d1');
 
+function my_autoloader($class) {
     $file = str_replace("\\", DIRECTORY_SEPARATOR, strtolower($class)) . ".php";
     echo $class . ' -> ' . $file . "\n";
     require_once BASE_PATH . DIRECTORY_SEPARATOR . $file;
@@ -23,17 +21,37 @@ spl_autoload_register('my_autoloader');
 try {
     $client =  new \Client\GogsAPI(API_URL, API_TOKEN);
 
-    $me = $client->user()->load();
-    $you = $client->user("INF3910-3-CSS-IoT")->load();
-
-    $mine = $me->get("/repos")->load();
-    //$yours = $you->get("/repos")->load();
-
-    //$me->get("/tokens")->load();
+    //$users = $client->users()->search(array("name" => "bje007"));
 
+    $me = $client->user()->load();
+    
+    try {
+        // Create new repo
+        $repo = $me->repos()->new(
+            /*name*/        "gogs-php-api-client-test", 
+            /*desc*/        "Repository created from test file.",
+            /*private*/     false,
+            /*auto init*/   true,
+            /*git ignore*/  "Vim",
+            /*license*/     "MIT License"
+            /* default read me */
+        );
+
+        echo "New repo created!\n";
+
+    } catch(\Lib\HTTPUnexpectedResponse $e) {
+
+        // Repo exists, get it!
+        $repo = $me->repo("gogs-php-api-client-test");
+
+        // Delete it (it will now be created on reload!)
+        $repo->delete();
+
+        echo "Repo deleted\n";
+    } 
 } catch (\Lib\NotAuthorizedException $e) {
-    die($e->getMessage());
+    die("NOT AUTH: " . $e->getMessage());
 } catch (\Lib\HTTPUnexpectedResponse $e) {
-    die($e->getMessage());
+    die($e);
 }
 ?>

+ 11 - 0
lib/arrayiterator.php

@@ -0,0 +1,11 @@
+<?php namespace Lib;
+
+interface ArrayIterator {
+    public function current();
+    public function next();
+    public function reset();
+    public function len();
+    public function all();
+    public function by_key($idx);
+}
+

+ 75 - 17
lib/curl.php

@@ -80,9 +80,28 @@ class HTTPUnexpectedResponse extends \Exception {
         598 => 'Network read timeout error', // Unknown
         599 => 'Network connect timeout error', // Unknown
     );
+
+    private $response;
+
+    public function __construct($message, $code = 0, Exception $previous = null) {
+        $this->response = $message;
+        parent::__construct(HTTPUnexpectedResponse::$ecode[$code], $code, $previous);
+    }
+
+    public function __toString() {
+        return __CLASS__ . ": [{$this->code} | {$this->message}]: {$this->response}\n";
+    }
+
+    public function getResponse() {
+        return $this->response;
+    }
 }
 
-class NotAuthorizedException extends \Exception{}
+class NotAuthorizedException extends HTTPUnexpectedResponse {
+    public function __construct($message, Exception $previous = null) {
+        parent::__contruct($message, 401, $previous);
+    }
+}
 
 trait Curl {
     protected $url;
@@ -92,24 +111,46 @@ trait Curl {
     protected $timeout = 30;
     protected $max_redirects = 4;
 
-    protected function method(&$req, $scope, $params, $post, $ret) {
-        echo sprintf("%s: %s%s\n", $post ? "POST" : "GET", $this->url, $scope);
+    private function array_2_params(array $params) {
+        return join("&", array_map(function($k, $v) {
+            return sprintf("%s=%s", $k, rawurlencode(is_bool($v) ? ($v ? "true" : "false") : $v ));
+        }, array_keys($params), $params));
+    }
+
+    private function array_2_json(array $params) {
+        return json_encode($params);
+    }
+
+    protected function method(string $method, string &$req, string $scope, array $params, bool $ret) {
         $c = curl_init();
 
         if (!$c) {
             return false;
         }
 
-        if ($post) {
-            curl_setopt($c, CURLOPT_POST, 1);
-            curl_setopt($c, CURLOPT_POSTFIELDS, $params);
+        $headers = array(
+            sprintf("Authorization: token %s", $this->token),
+        );
+
+        $url = sprintf("%s%s", $this->url, $scope);
+
+        if (in_array($method, array("DELETE", "PATCH", "POST"))) {
+            $json = $this->array_2_json($params);
+            curl_setopt($c, CURLOPT_CUSTOMREQUEST, $method);
+            curl_setopt($c, CURLOPT_POSTFIELDS, $json);
+            echo sprintf("JSON: %s", $json);
+            array_unshift($headers, "Content-Type: application/json");
+            array_push($headers, "Content-Length: " . strlen($json));
+            var_dump($headers);
+        } else {
+            $url .= "?" . $this->array_2_params($params);
         }
 
+        echo sprintf("%s: %s\n", $method, $url);
+
         curl_setopt($c, CURLOPT_USERAGENT, $this->user_agent);
-        curl_setopt($c, CURLOPT_URL, sprintf("%s%s", $this->url, $scope));
-        curl_setopt($c, CURLOPT_HTTPHEADER, array(
-            sprintf("Authorization: token %s", $this->token)
-        ));
+        curl_setopt($c, CURLOPT_URL, $url);
+        curl_setopt($c, CURLOPT_HTTPHEADER, $headers);
         curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
         curl_setopt($c, CURLOPT_TIMEOUT, $this->timeout);
         curl_setopt($c, CURLOPT_MAXREDIRS, $this->max_redirects);
@@ -126,7 +167,7 @@ trait Curl {
 
     protected function authorized($scope) {
         $ret = "";
-        if ($this->method($ret, $scope, array(), false, false) == 401) {
+        if ($this->method("GET", $ret, $scope, array(), false) == 401) {
             throw new NotAuthorizedException("Not authorized", 401);
         }
         return true;
@@ -135,30 +176,47 @@ trait Curl {
     private function post($scope = "", $params = array()) {
         $req = "";
 
-        $code = $this->method($req, $scope, $params, true, true);
+        $code = $this->method("POST", $req, $scope, $params, true);
+
+        switch ($code) {
+        case 200:
+        case 201:
+            return $req;
+        case 401:
+            throw new NotAuthorizedException($req);
+        default:
+            throw new HTTPUnexpectedResponse($req, $code);
+        }
+    }
+
+    private function delete($scope = "") {
+        $req = "";
+
+        $code = $this->method("DELETE", $req, $scope, array(), true);
 
         switch ($code) {
         case 200:
+        case 204:
             return $req;
         case 401:
-            throw new NotAuthorizedException(HTTPUnexpectedResponse::$ecode[401], 401);
+            throw new NotAuthorizedException($req);
         default:
-            throw new HTTPUnexpectedResponse(HTTPUnexpectedResponse::$ecode[$code], $code);
+            throw new HTTPUnexpectedResponse($req, $code);
         }
     }
 
     private function get($scope = "", $params = array()) {
         $req = "";
 
-        $code = $this->method($req, $scope, $params, false, true);
+        $code = $this->method("GET", $req, $scope, $params, true);
 
         switch ($code) {
         case 200:
             return $req;
         case 401:
-            throw new NotAuthorizedException(HTTPUnexpectedResponse::$ecode[401], 401);
+            throw new NotAuthorizedException($req);
         default:
-            throw new HTTPUnexpectedResponse(HTTPUnexpectedResponse::$ecode[$code], $code);
+            throw new HTTPUnexpectedResponse($req, $code);
 
         }
     }