Browse Source

Restructure and fix of reserved word

Joachim M. Giæver 6 years ago
parent
commit
e3669c55d7

+ 143 - 2
README.md

@@ -10,8 +10,7 @@ A guide on how to use will come shortly!
 
 Clone the repo. Set the `API_URL` and `API_TOKEN` in the index-file. 
 
-```
-
+```php
 $client =  new \Client\GogsAPI(API_URL, API_TOKEN);
 
 $me = $client->user();
@@ -24,3 +23,145 @@ for($repo = $repos->current(); $repo != false; $repo = $repos->next())
     var_dump($repo);
 ```
 
+# Progress
+
+## Users
+
+### Search
+```
+GET /users/search
+```
+```php
+$users = $client->users()->search(array("name" => /* username */));
+```
+The array-param can be
+* name/q: The username
+* limit: maximum number of results, default: 10
+
+Returns a `CollectionResult`, e.g
+```php
+foreach ($users as $user)
+    echo $user->ufull_name . "\n";
+```
+
+### Get authenticated user
+```
+GET /user
+```
+```php
+$me = $client->user(); // alt: $client->users()->get()
+```
+
+### Get a single user
+```
+GET /users/:username
+```
+```php
+$user = $client->user("username"); // alt: $client->users()->get("username")
+```
+
+### Create a user
+```
+POST /admin/users
+```
+```php
+$user = $clients->users()->new(
+    "new user", "users@email.com"
+);
+```
+The params should be in the order
+* username (required)
+* email (required)
+* source_id
+* login_name
+* password
+* send_notify
+
+Requires the authenticated user to have admin rights.
+### Delete a user
+```
+DELETE /admin/users/:username
+```
+```php
+$user->delete();
+```
+Requires the authenticated user to have admin rights.
+
+### Get a user's repos
+```
+GET /user/repos
+```
+or
+```
+GET /users/:username/repos
+```
+```php
+$repos = $user->repos()->load();
+```
+
+### Get specific user's repo
+```
+GET /repos/:owner/:repo
+```
+```php
+$repo = $user->repo( /* repo */ );
+```
+
+### Create a user's repo
+```
+POST /user/repos
+```
+or
+```
+POST /admin/users/:username/repos
+```
+```php
+$user->repos()->new(
+    "name"
+);
+```
+
+The params should be in the order
+* name
+* description
+* private
+* bool
+* auto_init
+* gitignores
+* license
+* readme
+
+Requires the authenticated user to have admin rights to be able to create repos for other users than itself.
+
+### Delete a user's repo
+```
+DELETE /repos/:owner/:repo
+```
+```php
+$repo->delete();
+```
+Requires the authenticated user to have admin rights to be able to delete repos for other users than itself.
+
+### Get a user's organizations
+```
+GET /user/orgs
+```
+or
+```
+GET /users/:username/orgs
+```
+```php
+$orgs = $user->organizations();
+```
+
+## Organizations
+
+### Get an organizations repositories
+```
+GET /orgs/:orgname/repos
+```
+```php
+$org = $user->organizations()->load()->current();
+
+$repos = $org->repos();
+```

+ 0 - 25
client/gogsapi.php

@@ -1,25 +0,0 @@
-<?php namespace Client;
-
-class GogsAPI {
-    use \Lib\Curl;
-
-    public function __construct($api_url, $api_token) {
-        $this->url = $api_url;
-        $this->token = $api_token;
-    }
-
-    public function users() {
-        return new Request\Users($this->url, $this->token);
-    }
-
-    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() {}
-}
-?>

+ 0 - 47
client/request/user.php

@@ -1,47 +0,0 @@
-<?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);
-    }
-
-    protected function set_scope(string $method) {
-        switch($method) {
-        default:
-            $this->scope = ($this->ulogin == "me" ? "/user" : "/users/" . $this->ulogin);
-        }
-    }
-
-    public function authenticated() {
-        return $this->authenticated;
-    }
-
-    public function repos() {
-        return new Repos($this->url, $this->token, $this);
-    }
-
-    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;
-        }
-    }
-}
-
-?>

+ 82 - 32
index.php

@@ -7,16 +7,12 @@
  *
  * */
 
-define('BASE_PATH', realpath(dirname(__FILE__)));
+require "./src/gpac.php";
+
 define('API_URL', 'https://git.giaever.org/api/v1');
-define('API_TOKEN', '142efbfd6fbdf147f03d289f8b22a438eaa1b5d1');
+define('API_TOKEN', 'ccc9a0ea6fefa8df558f4994e8c8ada2efa97f12');
+//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;
-}
-spl_autoload_register('my_autoloader');
 
 try {
     $client =  new \Client\GogsAPI(API_URL, API_TOKEN);
@@ -25,30 +21,84 @@ try {
 
     $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";
-    } 
+    #try {
+    #    // Create new repo under authorized user
+    #    $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 '" . $repo->rfull_name . "' 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 '" . $repo->rfull_name . "' deleted\n";
+    #}
+
+    //$orgs = $me->organizations();
+
+    // Requires ADMIN rights to the authenticated user
+    #try {
+        // Create new user "tester2"
+    #    $user = $client->users()->new("tester2", "git@git.giaever.org");
+
+    #    echo "New user '" . $user->uusername . "' created!\n";
+
+        // And a new repository under this user
+    #    $repo = $user->repos()->new(
+    #        /*name*/        "gogs-php-api-client-auth-test", 
+    #        /*desc*/        "Repository created from test file.",
+    #        /*private*/     false,
+    #        /*auto init*/   true,
+    #        /*git ignore*/  "Vim",
+    #        /*license*/     "MIT License"
+    #        /* default read me */
+    #    );
+           
+    #    echo "New repo '" . $repo->rfull_name . "' created\n";
+
+    #    $org = $user->organizations()->new(
+    #        /*username*/ "tester-org",
+    #        /*full name*/ "Tester's organization",
+    #        /*desc*/ "Just a test organization",
+    #        /*website*/ "https://git.giaever.org/",
+    #        /*location*/ "Norway"
+    #    );
+
+    #}catch (\Lib\NotAuthorizedException $e) {
+    #    echo "User (me) '" . $me->uusername . "' not authorized for this action: " . $e->getMessage();
+    #} catch (\Lib\HTTPUnexpectedResponse $e) {
+    
+        // Most likely user exist; then delete it
+    #    $user = $client->users()->get("tester2");
+
+        // Delete it's repositories
+    #    foreach ($user->repos()->load()->all() as $repo)
+    #        $repo->delete();
+
+    #    foreach ($user->organizations()->load()->all() as $org)
+    #        $org->delete();
+
+    #    $user->delete();
+    #    echo "User '" . $user->uusername . "' deleted!\n";
+    #}
+
+    var_dump($me->organizations()->load()->all()["FlyViking"]->repos()->create(
+        "test-repo-for-org",
+        "this is desc."
+    ));
+
 } catch (\Lib\NotAuthorizedException $e) {
     die("NOT AUTH: " . $e->getMessage());
 } catch (\Lib\HTTPUnexpectedResponse $e) {

+ 74 - 0
src/client/gogsapi.php

@@ -0,0 +1,74 @@
+<?php namespace Client;
+
+/** 
+ * Gogs API client. This class initially provide 
+ * the programmer with a starting point and a kind
+ * of an interface to start from, to keep  track of
+ * the context.
+ * 
+ * @author Joachim M. Giaever (joachim[]giaever.org)
+ * @version 0.1
+ */
+
+class GogsAPI {
+    use \Lib\Curl;
+
+    /** 
+     * Constructor of the class to store the api data.
+     * 
+     * @param string $api_url 
+     * @param string $api_token 
+     * @return null
+     */
+    public function __construct(string $api_url, string $api_token) {
+        $this->url = $api_url;
+        $this->token = $api_token;
+    }
+
+    /** 
+     * Returns an object to fetch users from the
+     * Gogs installation. See \Request\Users class
+     * to understand usage (e.g ->load() to fetch all, 
+     * ->search(array params) to search for one or several 
+     * users etc).
+     * 
+     * @return \Request\Users
+     */
+    public function users() {
+        return new Request\Users($this->url, $this->token);
+    }
+
+    /** 
+     * Returns an object to fetch the repositories
+     * on the Gogs installation. See \Request\Repos to
+     * understand usage. Inherits the same class as \R\Users,
+     * but the usage may differ!
+     *
+     * Note! To fetch a particular repo under a user, you
+     * should go through the user (see method below).
+     * 
+     * @return \Request\Repos
+     */
+    public function repos() {
+        return new Request\Repos($this->url, $this->token);
+    }
+
+    /** 
+     * Returns either
+     * * the authorized user ($name = "" or "me")
+     * * the specified user ($name = anything else)
+     * 
+     * @return \Request\User
+     */
+    public function user(string $name = "me") {
+        return new Request\User($this->url, $this->token, $name);
+    }
+
+    /** 
+     * Nothing to "destruct" at this time. 
+     * 
+     * @return null
+     */
+    public function __destruct() {}
+}
+?>

+ 20 - 7
client/request/base.php → src/client/request/base.php

@@ -7,8 +7,8 @@ abstract class Base implements RequestInterface {
 
     use \Lib\Curl {
         get as private mget;
-        post as protected mpost;
-        delete as protected mdelete;
+        post as private mpost;
+        delete as private mdelete;
     }
 
     public function __construct(string $api_url, string $api_token) {
@@ -43,25 +43,38 @@ abstract class Base implements RequestInterface {
     }
 
     public function get(string $s) {
-        $this->set_scope("get");
+        if (!$this->set_scope("get"))
+            throw new NotImplementedException("::get:: Not implemented for class");
     }
 
     public function create(...$args) {
-        $this->set_scope("create");
+
+        if ($this->loaded)
+            throw new Exception("::create:: Cant create on an git-initialized object. Create new object.");
+
+        if (!$this->set_scope("create"))
+            throw new NotImplementedException("::create:: Not implemented for class");
+
         $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");
+
+        if (!$this->loaded)
+            throw new InvalidMethodRequest("::patch:: Cant patch an git-uninitialized object. Load it first.");
+
+        if (!$this->set_scope("patch"))
+            throw new NotImplementedException("::patch:: Not implemented for class");
     }
 
     public function delete() {
-        $this->set_scope("delete");
+        if (!$this->set_scope("delete"))
+            throw new NotImplementedException("::delete:: Not implemented for class");
+
         return $this->method_delete();
     }
 

+ 1 - 1
client/request/collection.php → src/client/request/collection.php

@@ -55,7 +55,7 @@ abstract class Collection extends Base implements \Lib\ArrayIterator {
         return reset($this->objs);
     }
 
-    abstract public function new(...$args);
+    //abstract public function create(...$args);
     abstract public function search(array $params = array());
 }
 

+ 50 - 0
src/client/request/org.php

@@ -0,0 +1,50 @@
+<?php namespace Client\Request;
+
+class Org extends User {
+    public $udescription;
+    public $uwebsite;
+    public $ulocation;
+
+    private $owner;
+
+    public function __construct(string $api_url, string $api_token, string $oname = null, User $owner = null) {
+        $this->ousername = $oname;
+        $this->owner = $owner;
+        parent::__construct($api_url, $api_token);
+    }
+
+    protected function set_scope(string $method) {
+        switch ($method) {
+        case "create":
+            if ($this->owner == null);
+
+            $this->scope = "/admin/users/" . $this->owner->uusername . "/orgs";
+            return true;
+        case "get":
+            $this->scope = "/orgs/" . $this->uusername;
+            return true;
+        }
+    }
+
+    public function search(string $q) {
+        $searchable = sprintf("%s %s %s", $this->ufull_name, $this->uusername, $this->udescription);
+
+        return stripos($searchable, $q) !== false;
+    }
+
+    public function create(...$args) {
+        $params = array(
+            "username" => isset($args[0]) && is_string($args[0]) ? $args[0] : null,
+            "ufull_name" => isset($args[1]) && is_string($args[1]) ? $args[1] : null,
+            "description" => isset($args[2]) && is_string($args[2]) ? $args[2] : null,
+            "website" => isset($args[3]) && is_string($args[3]) ? $args[3] : null,
+            "location" => isset($args[4]) && is_string($args[4]) ? $args[4] : null
+        );
+
+        $params = array_filter($params, function($val) {
+            return $val != null;
+        });
+
+        parent::create($params);
+    }
+}

+ 66 - 0
src/client/request/orgs.php

@@ -0,0 +1,66 @@
+<?php namespace Client\Request;
+
+class Orgs extends Collection {
+    protected $owner;
+
+    public function __construct(string $api_url, string $api_token, User $owner) {
+        $this->owner = $owner;
+        parent::__construct($api_url, $api_token);
+    }
+
+    protected function set_scope(string $method) {
+        switch ($method) {
+        case "get":
+            $this->scope = ($this->owner == null || $this->owner->authenticated() ? "/user" : "/users/" . $this->owner->uusername) . "/orgs";
+            return true;
+        }
+    }
+
+    public function create(...$args) {
+
+        $org = new Org($this->url, $this->token, null, $this->owner);
+
+        if (count($args) > 0)
+            $org->create(...$args);
+
+        return $org;
+    }
+
+    public function get(string $s) {
+
+        if (($org = $this->by_key($s)))
+            return $org;
+
+        return new Org($this->url, $this->token, $s, $this->owner);
+    }
+
+    public function search(array $params = array()) {
+
+        if (!isset($params["name"]) && !isset($params["q"]))
+            throw new SearchParamException("Missing param <name>|<q>");
+
+        $q = isset($params["name"]) ? $params["name"] : $params["q"];
+        $l = isset($params["limit"]) ? $params["limit"] : 10;
+
+        $this->load();
+
+        $orgs = new CollectionResult();
+
+        foreach ($this->all() as $key => $org) {
+            if ($org->search($q))
+                $orgs->set($org);
+            if ($orgs->len() == $l)
+                break;
+        }
+
+        return $orgs;
+    }
+
+    protected function json_set_property($obj) {
+        foreach($obj as $val) {
+            $org = new Org($this->url, $this->token, null, $this->owner);
+            $org->json_set_property($val);
+            $this->add($org, $val->username);
+        }
+    }
+}

+ 12 - 7
client/request/repo.php → src/client/request/repo.php

@@ -29,8 +29,6 @@ class Repo extends Base {
     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;
@@ -40,14 +38,24 @@ class Repo extends Base {
     protected function set_scope(string $method) {
         switch ($method) {
         case "create":
-            $this->scope = "/user" . ($this->rowner->authenticated() ? "" : "/" . $this->rowner->uusername) . "/repos";
+            if ($this->rowner instanceof Org)
+                $this->scope = "/org/" . $this->rowner->uusername . "/repos";
+            elseif ($this->rowner->authenticated())
+                $this->scope = "/user/repos";
+            else
+                $this->scope = "/admin/users/" . $this->rowner->uusername . "/repos";
             break;
         case "delete":
             $this->scope = "/repos/" . $this->rowner->uusername . "/" . $this->rname;
             break;
-        default:
+        case "get":
             $this->scope = "/repos/" . ($this->rowner ? $this->rowner->uusername . "/" . $this->rname : $this->rfull_name);
+            break;
+        default:
+            return false;
         }
+
+        return true;
     }
 
     protected function json_set_property($obj) {
@@ -78,9 +86,6 @@ class Repo extends Base {
 
     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,

+ 10 - 2
client/request/repos.php → src/client/request/repos.php

@@ -11,17 +11,25 @@ class Repos extends Collection {
 
     protected function set_scope(string $method) {
         switch ($method) {
+        case "get":
+            if ($this->owner instanceof Org)
+                $this->scope = "/orgs/" . $this->owner->uusername . "/repos";
+            else
+                $this->scope = ($this->owner == null || $this->owner->authenticated() ? "/user" : "/users/" . $this->owner->uusername ) . "/repos";
         default:
-            $this->scope = ($this->owner ? $this->owner->scope : "" ) . "/repos";
+            return false;
         }
+
+        return true;
     }
 
-    public function new(...$args) {
+    public function create(...$args) {
 
         $repo = new Repo($this->url, $this->token, $this->owner);
 
         if (count($args) > 0)
             $repo->create(...$args);
+
         return $repo;
     }
 

+ 1 - 0
client/request/requestinterface.php → src/client/request/requestinterface.php

@@ -1,5 +1,6 @@
 <?php namespace Client\Request;
 
+class InvalidMethodRequest extends \Exception {};
 class RequestErrorException extends \Exception {};
 class NotImplementedException extends \BadMethodCallException{}
 

+ 79 - 0
src/client/request/user.php

@@ -0,0 +1,79 @@
+<?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->uusername = (strlen($user) == 0 || $user == "me" ? "me" : $user);
+        $this->authenticated = $this->uusername == "me";
+        parent::__construct($api_url, $api_token);
+    }
+
+    protected function set_scope(string $method) {
+        switch($method) {
+        case "create":
+
+            if ($this->loaded)
+                throw new InvalidMethodRequest("Cannot create user of existing user");
+
+            $this->scope = "/admin/users";
+            
+            return true;
+        case "delete":
+            $this->scope = "/admin/users/" . $this->uusername;
+            return true;
+        default:
+            $this->scope = ($this->authenticated ? "/user" : "/users/" . $this->uusername);
+        }
+    }
+
+    public function authenticated() {
+        return $this->authenticated;
+    }
+
+    public function repos() {
+        return new Repos($this->url, $this->token, $this);
+    }
+
+    public function repo(string $name) {
+        return (new Repo($this->url, $this->token, $this, $name))->load();
+    }
+
+    public function organizations() {
+        return new Orgs($this->url, $this->token, $this);
+    }
+
+    public function create(...$args) {
+        $params = array(
+            "username" => isset($args[0]) && is_string($args[0]) ? $args[0] : null,
+            "email" => isset($args[1]) && is_string($args[1]) ? $args[1] : null,
+            "source_id" => isset($args[2]) && is_numeric($args[2]) ? $args[2] : null,
+            "login_name" => isset($args[3]) && is_string($args[3]) ? $args[3] : null,
+            "password" => isset($args[4]) && is_string($args[4]) ? $args[4] : null,
+            "send_notify" => isset($args[5]) && is_bool($args[5]) ? $args[5] : null
+        );
+
+        $params = array_filter($params, function($val) {
+            return $val != null;
+        });
+        parent::create($params);
+    }
+
+    protected function json_set_property($obj) {
+        foreach ($obj as $key => $value) {
+            $key = 'u' . $key;
+            if (property_exists($this, $key))
+                $this->{$key} = $value;
+        }
+        $this->loaded = true;
+    }
+}
+
+?>

+ 27 - 5
client/request/users.php → src/client/request/users.php

@@ -1,20 +1,38 @@
 <?php namespace Client\Request;
 
+/** 
+ * Returns one or more users in the Gogs installation, 
+ * depending on the called method. 
+ * 
+ * @author Joachim M. Giaever (joachim[]giaever.org)
+ * @package request
+ */
 class Users extends Collection {
 
     protected function set_scope(string $method) {
-        switch ($method) {
+        var_dump("METHOD", $method);
+        switch ($method) {
         default:
             $this->scope = "/users";
         }
     }
 
-    public function new(...$args) {
+    /**
+     * Returns a new user object. If arguments
+     * is specified the user will be "created".
+     *
+     * The arguments can be left out to "create" the
+     * user through the user object iteself.
+     *
+     * @param ...$args User->create arguments
+     * @return \User
+     */
+    public function create(...$args) {
 
-        $user = new User($this->url, $this->token);
+        $user = new User($this->url, $this->token, "-");
 
         if (count($args) != 0)
-            $user->create($args);
+            $user->create(...$args);
 
         return $user;
     }
@@ -24,7 +42,11 @@ class Users extends Collection {
         if ($this->by_key($s))
             return $this->by_key($s);
 
-        return new User($this->url, $this->token, $s);
+        $user = (new User($this->url, $this->token, $s))->load();
+
+        $this->add($user, $user->ulogin);
+
+        return $user;
     }
 
     public function search(array $params = array()) {

+ 10 - 0
src/gpac.php

@@ -0,0 +1,10 @@
+<?php
+
+define('GPAC_BASE_PATH', realpath(dirname(__FILE__)));
+
+spl_autoload_register(function($class) {
+    $file = GPAC_BASE_PATH . DIRECTORY_SEPARATOR . str_replace("\\", DIRECTORY_SEPARATOR, strtolower($class)) . ".php";
+
+    if (file_exists($file))
+        require_once $file;
+});

+ 0 - 0
lib/arrayiterator.php → src/lib/arrayiterator.php


+ 142 - 16
lib/curl.php → src/lib/curl.php

@@ -1,7 +1,21 @@
 <?php namespace Lib;
 
+/** 
+ * Defines an unexpected response.
+ *
+ * @author Joachim M. Giaever (joachim[]giaever.org)
+ * @package curl
+ * @version 0.1
+ */
 class HTTPUnexpectedResponse extends \Exception {
+    /**
+     * Includes valid codes, as a valid code can also be unexpeted.
+     *
+     * @var array $ecode HTTP status codes
+     * @static
+     */
     static $ecode = array(
+        0 => "Unknown error",
         100 => 'Continue',
         101 => 'Switching Protocols',
         102 => 'Processing', // WebDAV; RFC 2518
@@ -81,46 +95,120 @@ class HTTPUnexpectedResponse extends \Exception {
         599 => 'Network connect timeout error', // Unknown
     );
 
+    /**
+     * The response from server (body)
+     * @access private
+     */
     private $response;
 
-    public function __construct($message, $code = 0, Exception $previous = null) {
+    /**
+     * Sets the exceptions.
+     *
+     * @string $message - the response from the server.
+     * @string $code - the HTTP status code.
+     * @exception $prev - Previous exceptions
+     **/
+    public function __construct(string $message, int $code = 0, Exception $previous = null) {
         $this->response = $message;
         parent::__construct(HTTPUnexpectedResponse::$ecode[$code], $code, $previous);
     }
 
+    /** 
+     * Visual representation of the exception.
+     *
+     * @return string
+     */
     public function __toString() {
         return __CLASS__ . ": [{$this->code} | {$this->message}]: {$this->response}\n";
     }
 
+    /** 
+     * Get the actual response from the body or the request.
+     *
+     * @return string
+     */
     public function getResponse() {
         return $this->response;
     }
 }
 
+/** 
+ * When the request fails because of an unauthorized token,
+ * this is thrown instead.
+ *
+ * @author Joachim M. Giaever (joachim[]giaever.org)
+ * @package curl
+ * @version 0.1
+ */
 class NotAuthorizedException extends HTTPUnexpectedResponse {
-    public function __construct($message, Exception $previous = null) {
-        parent::__contruct($message, 401, $previous);
+
+    /**
+     * Sets the exceptions.
+     *
+     * @string $message - the response from the server.
+     * @string $code - the HTTP status code, @default 401
+     * @exception $prev - Previous exceptions
+     **/
+    public function __construct($message, $code = 401, Exception $previous = null) {
+        parent::__construct($message, $code, $previous);
     }
 }
 
+/** 
+ * A trait used for every class referencing the api-url and token.
+ *
+ * @author Joachim M. Giaever (joachim[]giaever.org)
+ * @package curl
+ * @version 0.1
+ */
 trait Curl {
     protected $url;
     protected $token;
 
-    protected $user_agent = "Gogs PHP Api Client/0.0 (compatible; LINUX)";
+    // TODO: Change this to something!
+    protected $user_agent = "Gogs PHP Api Client/0.1 (compatible; LINUX)";
     protected $timeout = 30;
     protected $max_redirects = 4;
 
+    /** 
+     * array_2_params takes an array and converts it into a
+     * query string (e.g param=val&param2=val2).
+     *
+     * @param array $params parameters to pass
+     * @return string
+     */
     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));
     }
 
+    /** 
+     * array_2_json takes an array and converts it into a
+     * json-string (e.g {'name': 'This'}) which is typically
+     * used in a request body.
+     * 
+     * @param array $params paramters to pass
+     * @return string
+     */
     private function array_2_json(array $params) {
-        return json_encode($params);
+        return count($params) == 0 ? null : json_encode($params);
     }
 
+    /** 
+     * Initializes a curl request of different kinds, depending
+     * on the specified method. This can be
+     *
+     * DELETE, PATCH, POST or GET. An unidentified value will
+     * become a GET-request.
+     * 
+     * @param string $method either DELETE, PATCH, POST, GET
+     * @param string &$req variable to store request body in
+     * @param string $scope scope within the API (e.g /user/repos)
+     * @param array $params parameters to pass
+     * @param bool $ret return transfer
+     * @return int the status code
+     */
     protected function method(string $method, string &$req, string $scope, array $params, bool $ret) {
         $c = curl_init();
 
@@ -134,24 +222,22 @@ trait Curl {
 
         $url = sprintf("%s%s", $this->url, $scope);
 
+        echo sprintf("%s: %s, params: %s\n", $method, $url, $this->array_2_json($params));
+
         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, $url);
         curl_setopt($c, CURLOPT_HTTPHEADER, $headers);
-        curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
+        curl_setopt($c, CURLOPT_RETURNTRANSFER, $ret);
         curl_setopt($c, CURLOPT_TIMEOUT, $this->timeout);
         curl_setopt($c, CURLOPT_MAXREDIRS, $this->max_redirects);
         curl_setopt($c, CURLOPT_FOLLOWLOCATION, true);
@@ -165,7 +251,18 @@ trait Curl {
         return $status_code;
     }
 
-    protected function authorized($scope) {
+    /** 
+     * Checks if the user is authorized for the scope. Shouldn't
+     * be used frequently. One test for one scope should be enough,
+     * but if you know for sure thats you're programming with the
+     * use of an authorized user you should leave this and just
+     * handle the NotAuthorizedExeption whenever thrown.
+     *
+     * @param $scope the scope, a relative uri.
+     * @throws Not AuthorizedException if server responde with a 401
+     * @return bool
+     */
+    protected function authorized(string $scope = "") {
         $ret = "";
         if ($this->method("GET", $ret, $scope, array(), false) == 401) {
             throw new NotAuthorizedException("Not authorized", 401);
@@ -173,7 +270,16 @@ trait Curl {
         return true;
     }
 
-    private function post($scope = "", $params = array()) {
+    /**
+     * Post method.
+     *
+     * @param string $scope the scope, a relative uri.
+     * @param array $params the parameters to post.
+     * @throws NotAuthorizedException on 401, 403
+     * @throws HTTPUnexpectedResponse when not 200,201,401,403
+     * @return string the request content.
+     */
+    private function post(string $scope = "", array $params = array()) {
         $req = "";
 
         $code = $this->method("POST", $req, $scope, $params, true);
@@ -183,13 +289,22 @@ trait Curl {
         case 201:
             return $req;
         case 401:
-            throw new NotAuthorizedException($req);
+        case 403:
+            throw new NotAuthorizedException($req, $code);
         default:
             throw new HTTPUnexpectedResponse($req, $code);
         }
     }
 
-    private function delete($scope = "") {
+    /**
+     * Delete method.
+     *
+     * @param string $scope the scope, a relative uri.
+     * @throws NotAuthorizedException on 401, 403
+     * @throws HTTPUnexpectedResponse when not 200,204,401,403
+     * @return string the request content.
+     */
+    private function delete(string $scope = "") {
         $req = "";
 
         $code = $this->method("DELETE", $req, $scope, array(), true);
@@ -199,12 +314,22 @@ trait Curl {
         case 204:
             return $req;
         case 401:
-            throw new NotAuthorizedException($req);
+        case 403:
+            throw new NotAuthorizedException($req, $code);
         default:
             throw new HTTPUnexpectedResponse($req, $code);
         }
     }
 
+    /**
+     * GET method.
+     *
+     * @param string $scope the scope, a relative uri.
+     * @param array $params the parameters to post.
+     * @throws NotAuthorizedException on 401, 403
+     * @throws HTTPUnexpectedResponse when not 200,401,403
+     * @return string the request content.
+     */
     private function get($scope = "", $params = array()) {
         $req = "";
 
@@ -214,7 +339,8 @@ trait Curl {
         case 200:
             return $req;
         case 401:
-            throw new NotAuthorizedException($req);
+        case 403:
+            throw new NotAuthorizedException($req, $code);
         default:
             throw new HTTPUnexpectedResponse($req, $code);