summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrederic Guillot <fred@kanboard.net>2016-01-22 21:23:12 -0500
committerFrederic Guillot <fred@kanboard.net>2016-01-22 21:23:12 -0500
commitad8fcf035ab92d8cd06179959000b9a1681b1505 (patch)
tree80f4e35b16c1c1a6d4c473983bc6cb62b9519494
parentf27bcec2d92af83ee7205c84954cda9d7b2fc55d (diff)
Add new API procedures for groups, roles and project permissions
-rw-r--r--ChangeLog1
-rw-r--r--app/Api/App.php10
-rw-r--r--app/Api/Auth.php2
-rw-r--r--app/Api/File.php10
-rw-r--r--app/Api/Group.php49
-rw-r--r--app/Api/GroupMember.php32
-rw-r--r--app/Api/ProjectPermission.php51
-rw-r--r--app/Core/Security/Role.php2
-rw-r--r--doc/api-application-procedures.markdown60
-rw-r--r--doc/api-group-member-procedures.markdown152
-rw-r--r--doc/api-group-procedures.markdown174
-rw-r--r--doc/api-json-rpc.markdown29
-rw-r--r--doc/api-project-permission-procedures.markdown274
-rw-r--r--doc/api-project-procedures.markdown101
-rw-r--r--doc/tests.markdown128
-rw-r--r--jsonrpc.php4
-rw-r--r--tests/integration.mysql.xml (renamed from tests/functionals.mysql.xml)2
-rw-r--r--tests/integration.postgres.xml (renamed from tests/functionals.postgres.xml)2
-rw-r--r--tests/integration.sqlite.xml (renamed from tests/functionals.sqlite.xml)2
-rw-r--r--tests/integration/ApiTest.php (renamed from tests/functionals/ApiTest.php)10
-rw-r--r--tests/integration/AppTest.php34
-rw-r--r--tests/integration/Base.php62
-rw-r--r--tests/integration/GroupMemberTest.php39
-rw-r--r--tests/integration/GroupTest.php48
-rw-r--r--tests/integration/MeTest.php (renamed from tests/functionals/UserApiTest.php)46
-rw-r--r--tests/integration/ProjectPermissionTest.php64
26 files changed, 1122 insertions, 266 deletions
diff --git a/ChangeLog b/ChangeLog
index 8a004cd8..f34ecf9d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -6,6 +6,7 @@ New features:
* Forgot Password
* Add dropdown menu on each board column title to close all tasks
* Add Malay language
+* Add new API procedures for groups, roles and project permissions
Improvements:
diff --git a/app/Api/App.php b/app/Api/App.php
index d082bcfb..635f1ce2 100644
--- a/app/Api/App.php
+++ b/app/Api/App.php
@@ -34,4 +34,14 @@ class App extends \Kanboard\Core\Base
{
return $this->color->getList();
}
+
+ public function getApplicationRoles()
+ {
+ return $this->role->getApplicationRoles();
+ }
+
+ public function getProjectRoles()
+ {
+ return $this->role->getProjectRoles();
+ }
}
diff --git a/app/Api/Auth.php b/app/Api/Auth.php
index a9d1617c..c7c5298c 100644
--- a/app/Api/Auth.php
+++ b/app/Api/Auth.php
@@ -23,7 +23,7 @@ class Auth extends Base
*/
public function checkCredentials($username, $password, $class, $method)
{
- $this->container['dispatcher']->dispatch('app.bootstrap');
+ $this->dispatcher->dispatch('app.bootstrap');
if ($this->isUserAuthenticated($username, $password)) {
$this->checkProcedurePermission(true, $method);
diff --git a/app/Api/File.php b/app/Api/File.php
index be415ecb..269803e1 100644
--- a/app/Api/File.php
+++ b/app/Api/File.php
@@ -32,14 +32,18 @@ class File extends \Kanboard\Core\Base
}
} catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage());
+ return '';
}
-
- return '';
}
public function createFile($project_id, $task_id, $filename, $blob)
{
- return $this->file->uploadContent($project_id, $task_id, $filename, $blob);
+ try {
+ return $this->file->uploadContent($project_id, $task_id, $filename, $blob);
+ } catch (ObjectStorageException $e) {
+ $this->logger->error($e->getMessage());
+ return false;
+ }
}
public function removeFile($file_id)
diff --git a/app/Api/Group.php b/app/Api/Group.php
new file mode 100644
index 00000000..a1e0a73d
--- /dev/null
+++ b/app/Api/Group.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace Kanboard\Api;
+
+/**
+ * Group API controller
+ *
+ * @package api
+ * @author Frederic Guillot
+ */
+class Group extends \Kanboard\Core\Base
+{
+ public function createGroup($name, $external_id = '')
+ {
+ return $this->group->create($name, $external_id);
+ }
+
+ public function updateGroup($group_id, $name = null, $external_id = null)
+ {
+ $values = array(
+ 'id' => $group_id,
+ 'name' => $name,
+ 'external_id' => $external_id,
+ );
+
+ foreach ($values as $key => $value) {
+ if (is_null($value)) {
+ unset($values[$key]);
+ }
+ }
+
+ return $this->group->update($values);
+ }
+
+ public function removeGroup($group_id)
+ {
+ return $this->group->remove($group_id);
+ }
+
+ public function getGroup($group_id)
+ {
+ return $this->group->getById($group_id);
+ }
+
+ public function getAllGroups()
+ {
+ return $this->group->getAll();
+ }
+}
diff --git a/app/Api/GroupMember.php b/app/Api/GroupMember.php
new file mode 100644
index 00000000..de62f0c6
--- /dev/null
+++ b/app/Api/GroupMember.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Kanboard\Api;
+
+/**
+ * Group Member API controller
+ *
+ * @package api
+ * @author Frederic Guillot
+ */
+class GroupMember extends \Kanboard\Core\Base
+{
+ public function getGroupMembers($group_id)
+ {
+ return $this->groupMember->getMembers($group_id);
+ }
+
+ public function addGroupMember($group_id, $user_id)
+ {
+ return $this->groupMember->addUser($group_id, $user_id);
+ }
+
+ public function removeGroupMember($group_id, $user_id)
+ {
+ return $this->groupMember->removeUser($group_id, $user_id);
+ }
+
+ public function isGroupMember($group_id, $user_id)
+ {
+ return $this->groupMember->isMember($group_id, $user_id);
+ }
+}
diff --git a/app/Api/ProjectPermission.php b/app/Api/ProjectPermission.php
index d4408197..11e92af0 100644
--- a/app/Api/ProjectPermission.php
+++ b/app/Api/ProjectPermission.php
@@ -5,25 +5,68 @@ namespace Kanboard\Api;
use Kanboard\Core\Security\Role;
/**
- * ProjectPermission API controller
+ * Project Permission API controller
*
* @package api
* @author Frederic Guillot
*/
class ProjectPermission extends \Kanboard\Core\Base
{
- public function getMembers($project_id)
+ public function getProjectUsers($project_id)
{
return $this->projectUserRole->getAllUsers($project_id);
}
- public function revokeUser($project_id, $user_id)
+ public function getAssignableUsers($project_id, $prepend_unassigned = false)
+ {
+ return $this->projectUserRole->getAssignableUsersList($project_id, $prepend_unassigned);
+ }
+
+ public function addProjectUser($project_id, $user_id, $role = Role::PROJECT_MEMBER)
+ {
+ return $this->projectUserRole->addUser($project_id, $user_id, $role);
+ }
+
+ public function addProjectGroup($project_id, $group_id, $role = Role::PROJECT_MEMBER)
+ {
+ return $this->projectGroupRole->addGroup($project_id, $group_id, $role);
+ }
+
+ public function removeProjectUser($project_id, $user_id)
{
return $this->projectUserRole->removeUser($project_id, $user_id);
}
+ public function removeProjectGroup($project_id, $group_id)
+ {
+ return $this->projectGroupRole->removeGroup($project_id, $group_id);
+ }
+
+ public function changeProjectUserRole($project_id, $user_id, $role)
+ {
+ return $this->projectUserRole->changeUserRole($project_id, $user_id, $role);
+ }
+
+ public function changeProjectGroupRole($project_id, $group_id, $role)
+ {
+ return $this->projectGroupRole->changeGroupRole($project_id, $group_id, $role);
+ }
+
+ // Deprecated
+ public function getMembers($project_id)
+ {
+ return $this->getProjectUsers($project_id);
+ }
+
+ // Deprecated
+ public function revokeUser($project_id, $user_id)
+ {
+ return $this->removeProjectUser($project_id, $user_id);
+ }
+
+ // Deprecated
public function allowUser($project_id, $user_id)
{
- return $this->projectUserRole->addUser($project_id, $user_id, Role::PROJECT_MEMBER);
+ return $this->addProjectUser($project_id, $user_id);
}
}
diff --git a/app/Core/Security/Role.php b/app/Core/Security/Role.php
index 85d85743..cb45a8af 100644
--- a/app/Core/Security/Role.php
+++ b/app/Core/Security/Role.php
@@ -50,7 +50,7 @@ class Role
}
/**
- * Get application roles
+ * Get role name
*
* @access public
* @param string $role
diff --git a/doc/api-application-procedures.markdown b/doc/api-application-procedures.markdown
index ab1230cc..2897b526 100644
--- a/doc/api-application-procedures.markdown
+++ b/doc/api-application-procedures.markdown
@@ -229,3 +229,63 @@ Response example:
}
}
```
+
+### getApplicationRoles
+
+- Purpose: **Get the application roles**
+- Parameters: none
+- Result: **Dictionary of role => role_name**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getApplicationRoles",
+ "id": 317154243
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 317154243,
+ "result": {
+ "app-admin": "Administrator",
+ "app-manager": "Manager",
+ "app-user": "User"
+ }
+}
+```
+
+### getProjectRoles
+
+- Purpose: **Get the project roles**
+- Parameters: none
+- Result: **Dictionary of role => role_name**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getProjectRoles",
+ "id": 8981960
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 8981960,
+ "result": {
+ "project-manager": "Project Manager",
+ "project-member": "Project Member",
+ "project-viewer": "Project Viewer"
+ }
+}
+``` \ No newline at end of file
diff --git a/doc/api-group-member-procedures.markdown b/doc/api-group-member-procedures.markdown
new file mode 100644
index 00000000..54153537
--- /dev/null
+++ b/doc/api-group-member-procedures.markdown
@@ -0,0 +1,152 @@
+Group Member API Procedures
+===========================
+
+### getGroupMembers
+
+- Purpose: **Get all members of a group**
+- Parameters:
+ - **group_id** (integer, required)
+- Result on success: **List of users**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getGroupMembers",
+ "id": 1987176726,
+ "params": [
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1987176726,
+ "result": [
+ {
+ "group_id": "1",
+ "user_id": "1",
+ "id": "1",
+ "username": "admin",
+ "is_ldap_user": "0",
+ "name": null,
+ "email": null,
+ "notifications_enabled": "0",
+ "timezone": null,
+ "language": null,
+ "disable_login_form": "0",
+ "notifications_filter": "4",
+ "nb_failed_login": "0",
+ "lock_expiration_date": "0",
+ "is_project_admin": "0",
+ "gitlab_id": null,
+ "role": "app-admin"
+ }
+ ]
+}
+```
+
+### addGroupMember
+
+- Purpose: **Add a user to a group**
+- Parameters:
+ - **group_id** (integer, required)
+ - **user_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "addGroupMember",
+ "id": 1589058273,
+ "params": [
+ 1,
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1589058273,
+ "result": true
+}
+```
+
+### removeGroupMember
+
+- Purpose: **Remove a user from a group**
+- Parameters:
+ - **group_id** (integer, required)
+ - **user_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeGroupMember",
+ "id": 1730416406,
+ "params": [
+ 1,
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1730416406,
+ "result": true
+}
+```
+
+### isGroupMember
+
+- Purpose: **Check if a user is member of a group**
+- Parameters:
+ - **group_id** (integer, required)
+ - **user_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "isGroupMember",
+ "id": 1052800865,
+ "params": [
+ 1,
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1052800865,
+ "result": false
+}
+```
diff --git a/doc/api-group-procedures.markdown b/doc/api-group-procedures.markdown
new file mode 100644
index 00000000..1e976b8c
--- /dev/null
+++ b/doc/api-group-procedures.markdown
@@ -0,0 +1,174 @@
+Group API Procedures
+====================
+
+### createGroup
+
+- Purpose: **Create a new group**
+- Parameters:
+ - **name** (string, required)
+ - **external_id** (string, optional)
+- Result on success: **link_id**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "createGroup",
+ "id": 1416806551,
+ "params": [
+ "My Group B",
+ "1234"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1416806551,
+ "result": 2
+}
+```
+
+### updateGroup
+
+- Purpose: **Update a group**
+- Parameters:
+ - **group_id** (integer, required)
+ - **name** (string, optional)
+ - **external_id** (string, optional)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "updateGroup",
+ "id": 866078030,
+ "params": {
+ "group_id": "1",
+ "name": "ABC",
+ "external_id": "something"
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 866078030,
+ "result": true
+}
+```
+
+### removeGroup
+
+- Purpose: **Remove a group**
+- Parameters:
+ - **group_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeGroup",
+ "id": 566000661,
+ "params": [
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 566000661,
+ "result": true
+}
+```
+
+### getGroup
+
+- Purpose: **Get one group**
+- Parameters:
+ - **group_id** (integer, required)
+- Result on success: **Group dictionary**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getGroup",
+ "id": 1968647622,
+ "params": [
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1968647622,
+ "result": {
+ "id": "1",
+ "external_id": "",
+ "name": "My Group A"
+ }
+}
+```
+
+### getAllGroups
+
+- Purpose: **Get all groups**
+- Parameters: none
+- Result on success: **list of groups**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getAllGroups",
+ "id": 546070742
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 546070742,
+ "result": [
+ {
+ "id": "1",
+ "external_id": "",
+ "name": "My Group A"
+ },
+ {
+ "id": "2",
+ "external_id": "1234",
+ "name": "My Group B"
+ }
+ ]
+}
+```
diff --git a/doc/api-json-rpc.markdown b/doc/api-json-rpc.markdown
index 710b0b1b..34559df5 100644
--- a/doc/api-json-rpc.markdown
+++ b/doc/api-json-rpc.markdown
@@ -48,16 +48,19 @@ Usage
- [Authentication](api-authentication.markdown)
- [Examples](api-examples.markdown)
-- [Application Procedures](api-application-procedures.markdown)
-- [Project Procedures](api-project-procedures.markdown)
-- [Board Procedures](api-board-procedures.markdown)
-- [Swimlane Procedures](api-swimlane-procedures.markdown)
-- [Category Procedures](api-category-procedures.markdown)
-- [Automatic Action Procedures](api-action-procedures.markdown)
-- [Task Procedures](api-task-procedures.markdown)
-- [Subtask Procedures](api-subtask-procedures.markdown)
-- [File Procedures](api-file-procedures.markdown)
-- [Link Procedures](api-link-procedures.markdown)
-- [Comment Procedures](api-comment-procedures.markdown)
-- [User Procedures](api-user-procedures.markdown)
-- [User API Access Procedures](api-me-procedures.markdown)
+- [Application](api-application-procedures.markdown)
+- [Projects](api-project-procedures.markdown)
+- [Project Permissions](api-project-permission-procedures.markdown)
+- [Boards](api-board-procedures.markdown)
+- [Swimlanes](api-swimlane-procedures.markdown)
+- [Categories](api-category-procedures.markdown)
+- [Automatic Actions](api-action-procedures.markdown)
+- [Tasks](api-task-procedures.markdown)
+- [Subtasks](api-subtask-procedures.markdown)
+- [Files](api-file-procedures.markdown)
+- [Links](api-link-procedures.markdown)
+- [Comments](api-comment-procedures.markdown)
+- [Users](api-user-procedures.markdown)
+- [Groups](api-group-procedures.markdown)
+- [Group Members](api-group-member-procedures.markdown)
+- [Me](api-me-procedures.markdown)
diff --git a/doc/api-project-permission-procedures.markdown b/doc/api-project-permission-procedures.markdown
new file mode 100644
index 00000000..f64028b9
--- /dev/null
+++ b/doc/api-project-permission-procedures.markdown
@@ -0,0 +1,274 @@
+Project Permission API Procedures
+=================================
+
+### getProjectUsers
+
+- Purpose: **Get all members of a project**
+- Parameters:
+ - **project_id** (integer, required)
+- Result on success: **Dictionary of user_id => user name**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getProjectUsers",
+ "id": 1601016721,
+ "params": [
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1601016721,
+ "result": {
+ "1": "admin"
+ }
+}
+```
+
+### getAssignableUsers
+
+- Purpose: **Get users that can be assigned to a task for a project** (all members except viewers)
+- Parameters:
+ - **project_id** (integer, required)
+ - **prepend_unassigned** (boolean, optional, default is false)
+- Result on success: **Dictionary of user_id => user name**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getAssignableUsers",
+ "id": 658294870,
+ "params": [
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 658294870,
+ "result": {
+ "1": "admin"
+ }
+}
+```
+
+### addProjectUser
+
+- Purpose: **Grant access to a project for a user**
+- Parameters:
+ - **project_id** (integer, required)
+ - **user_id** (integer, required)
+ - **role** (string, optional)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "addProjectUser",
+ "id": 1294688355,
+ "params": [
+ "1",
+ "1",
+ "project-viewer"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1294688355,
+ "result": true
+}
+```
+
+### addProjectGroup
+
+- Purpose: **Grant access to a project for a group**
+- Parameters:
+ - **project_id** (integer, required)
+ - **group_id** (integer, required)
+ - **role** (string, optional)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "addProjectGroup",
+ "id": 1694959089,
+ "params": [
+ "1",
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1694959089,
+ "result": true
+}
+```
+
+### removeProjectUser
+
+- Purpose: **Revoke user access to a project**
+- Parameters:
+ - **project_id** (integer, required)
+ - **user_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeProjectUser",
+ "id": 645233805,
+ "params": [
+ 1,
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 645233805,
+ "result": true
+}
+```
+
+### removeProjectGroup
+
+- Purpose: **Revoke group access to a project**
+- Parameters:
+ - **project_id** (integer, required)
+ - **group_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeProjectGroup",
+ "id": 557146966,
+ "params": [
+ 1,
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 557146966,
+ "result": true
+}
+```
+
+### changeProjectUserRole
+
+- Purpose: **Change role of a user for a project**
+- Parameters:
+ - **project_id** (integer, required)
+ - **user_id** (integer, required)
+ - **role** (string, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "changeProjectUserRole",
+ "id": 193473170,
+ "params": [
+ "1",
+ "1",
+ "project-viewer"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 193473170,
+ "result": true
+}
+```
+
+### changeProjectGroupRole
+
+- Purpose: **Change role of a group for a project**
+- Parameters:
+ - **project_id** (integer, required)
+ - **group_id** (integer, required)
+ - **role** (string, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "changeProjectGroupRole",
+ "id": 2114673298,
+ "params": [
+ "1",
+ "1",
+ "project-viewer"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 2114673298,
+ "result": true
+}
+```
diff --git a/doc/api-project-procedures.markdown b/doc/api-project-procedures.markdown
index acb4297e..2d23615b 100644
--- a/doc/api-project-procedures.markdown
+++ b/doc/api-project-procedures.markdown
@@ -409,104 +409,3 @@ Request example:
]
}
```
-
-### getMembers
-
-- Purpose: **Get members of a project**
-- Parameters:
- - **project_id** (integer, required)
-- Result on success: Key/value pair of user_id and username
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getMembers",
- "id": 1944388643,
- "params": [
- 1
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1944388643,
- "result": {
- "1": "user1",
- "2": "user2",
- "3": "user3"
- }
-}
-```
-
-### revokeUser
-
-- Purpose: **Revoke user access for a given project**
-- Parameters:
- - **project_id** (integer, required)
- - **user_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "revokeUser",
- "id": 251218350,
- "params": [
- 1,
- 2
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 251218350,
- "result": true
-}
-```
-
-### allowUser
-
-- Purpose: **Grant user access for a given project**
-- Parameters:
- - **project_id** (integer, required)
- - **user_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "allowUser",
- "id": 2111451404,
- "params": [
- 1,
- 2
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 2111451404,
- "result": true
-}
-```
diff --git a/doc/tests.markdown b/doc/tests.markdown
index 5e5ad389..b2d95491 100644
--- a/doc/tests.markdown
+++ b/doc/tests.markdown
@@ -1,7 +1,7 @@
-How to run units and functional tests?
-======================================
+Automated tests
+===============
-[PHPUnit](https://phpunit.de/) is used to run automatic tests on Kanboard.
+[PHPUnit](https://phpunit.de/) is used to run automated tests on Kanboard.
You can run tests across different databases (Sqlite, Mysql and Postgresql) to be sure that the result is the same everywhere.
@@ -9,31 +9,18 @@ Requirements
------------
- Linux/Unix machine
-- PHP command line
+- PHP cli
- PHPUnit installed
- Mysql and Postgresql (optional)
-Install the latest version of PHPUnit
--------------------------------------
-
-Simply download the PHPUnit PHAR et copy the file somewhere in your `$PATH`:
-
-```bash
-wget https://phar.phpunit.de/phpunit.phar
-chmod +x phpunit.phar
-sudo mv phpunit.phar /usr/local/bin/phpunit
-phpunit --version
-PHPUnit 4.2.6 by Sebastian Bergmann.
-```
-
-Running unit tests
-------------------
+Unit Tests
+----------
-### Testing with Sqlite
+### Test with Sqlite
Sqlite tests use a in-memory database, nothing is written on the file system.
-The config file is `tests/units.sqlite.xml`.
+The PHPUnit config file is `tests/units.sqlite.xml`.
From your Kanboard directory, run the command `phpunit -c tests/units.sqlite.xml`.
Example:
@@ -41,21 +28,26 @@ Example:
```bash
phpunit -c tests/units.sqlite.xml
-PHPUnit 4.2.6 by Sebastian Bergmann.
+PHPUnit 5.0.0 by Sebastian Bergmann and contributors.
-Configuration read from /Volumes/Devel/apps/kanboard/tests/units.sqlite.xml
+............................................................... 63 / 649 ( 9%)
+............................................................... 126 / 649 ( 19%)
+............................................................... 189 / 649 ( 29%)
+............................................................... 252 / 649 ( 38%)
+............................................................... 315 / 649 ( 48%)
+............................................................... 378 / 649 ( 58%)
+............................................................... 441 / 649 ( 67%)
+............................................................... 504 / 649 ( 77%)
+............................................................... 567 / 649 ( 87%)
+............................................................... 630 / 649 ( 97%)
+................... 649 / 649 (100%)
-................................................................. 65 / 74 ( 87%)
-.........
+Time: 1.22 minutes, Memory: 151.25Mb
-Time: 9.05 seconds, Memory: 17.75Mb
-
-OK (74 tests, 6145 assertions)
+OK (649 tests, 43595 assertions)
```
-**NOTE:** PHPUnit is already included in the Vagrant environment
-
-### Testing with Mysql
+### Test with Mysql
You must have Mysql or MariaDb installed on localhost.
@@ -68,27 +60,10 @@ By default, those credentials are used:
For each execution the database is dropped and created again.
-The config file is `tests/units.mysql.xml`.
+The PHPUnit config file is `tests/units.mysql.xml`.
From your Kanboard directory, run the command `phpunit -c tests/units.mysql.xml`.
-Example:
-
-```bash
-phpunit -c tests/units.mysql.xml
-
-PHPUnit 4.2.6 by Sebastian Bergmann.
-
-Configuration read from /Volumes/Devel/apps/kanboard/tests/units.mysql.xml
-
-................................................................. 65 / 74 ( 87%)
-.........
-
-Time: 49.77 seconds, Memory: 17.50Mb
-
-OK (74 tests, 6145 assertions)
-```
-
-### Testing with Postgresql
+### Test with Postgresql
You must have Postgresql installed on localhost.
@@ -100,37 +75,20 @@ By default, those credentials are used:
- Database: **kanboard_unit_test**
Be sure to allow the user `postgres` to create and drop databases.
-For each execution the database is dropped and created again.
+The database is recreated for each execution.
-The config file is `tests/units.postgres.xml`.
+The PHPUnit config file is `tests/units.postgres.xml`.
From your Kanboard directory, run the command `phpunit -c tests/units.postgres.xml`.
-Example:
-
-```bash
-phpunit -c tests/units.postgres.xml
-
-PHPUnit 4.2.6 by Sebastian Bergmann.
-
-Configuration read from /Volumes/Devel/apps/kanboard/tests/units.postgres.xml
-
-................................................................. 65 / 74 ( 87%)
-.........
-
-Time: 52.66 seconds, Memory: 17.50Mb
-
-OK (74 tests, 6145 assertions)
-```
-
-Running functionals tests
--------------------------
+Integration Tests
+-----------------
Actually only the API calls are tested.
Real HTTP calls are made with those tests.
-So a local instance of Kanboard is necessary and must listen on `http://localhost:8000`.
+So a local instance of Kanboard is necessary and must listen on `http://localhost:8000/`.
-Don't forget that all data will be removed/altered by the test suite.
+All data will be removed/altered by the test suite.
Moreover the script will reset and set a new API key.
1. Start a local instance of Kanboard `php -S 127.0.0.1:8000`
@@ -138,27 +96,27 @@ Moreover the script will reset and set a new API key.
The same method as above is used to run tests across different databases:
-- Sqlite: `phpunit -c tests/functionals.sqlite.xml`
-- Mysql: `phpunit -c tests/functionals.mysql.xml`
-- Postgresql: `phpunit -c tests/functionals.postgres.xml`
+- Sqlite: `phpunit -c tests/integration.sqlite.xml`
+- Mysql: `phpunit -c tests/integration.mysql.xml`
+- Postgresql: `phpunit -c tests/integration.postgres.xml`
Example:
```bash
-phpunit -c tests/functionals.sqlite.xml
-
-PHPUnit 4.2.6 by Sebastian Bergmann.
+phpunit -c tests/integration.sqlite.xml
-Configuration read from /Volumes/Devel/apps/kanboard/tests/functionals.sqlite.xml
+PHPUnit 5.0.0 by Sebastian Bergmann and contributors.
-..........................................
+............................................................... 63 / 135 ( 46%)
+............................................................... 126 / 135 ( 93%)
+......... 135 / 135 (100%)
-Time: 1.72 seconds, Memory: 4.25Mb
+Time: 1.18 minutes, Memory: 14.75Mb
-OK (42 tests, 160 assertions)
+OK (135 tests, 526 assertions)
```
-Continuous Integration with Travis-ci
+Continuous Integration with Travis-CI
-------------------------------------
After each commit pushed on the main repository, unit tests are executed across 5 different versions of PHP:
@@ -171,6 +129,4 @@ After each commit pushed on the main repository, unit tests are executed across
Each version of PHP is tested against the 3 supported database: Sqlite, Mysql and Postgresql.
-That mean we run 15 jobs each time the repository is updated. The execution time is around 25 minutes.
-
The Travis config file `.travis.yml` is located on the root directory of Kanboard.
diff --git a/jsonrpc.php b/jsonrpc.php
index 6778603f..1d59d4ea 100644
--- a/jsonrpc.php
+++ b/jsonrpc.php
@@ -19,6 +19,8 @@ use Kanboard\Api\Swimlane;
use Kanboard\Api\Task;
use Kanboard\Api\TaskLink;
use Kanboard\Api\User;
+use Kanboard\Api\Group;
+use Kanboard\Api\GroupMember;
$server = new Server;
$server->setAuthenticationHeader(API_AUTHENTICATION_HEADER);
@@ -39,5 +41,7 @@ $server->attach(new Swimlane($container));
$server->attach(new Task($container));
$server->attach(new TaskLink($container));
$server->attach(new User($container));
+$server->attach(new Group($container));
+$server->attach(new GroupMember($container));
echo $server->execute();
diff --git a/tests/functionals.mysql.xml b/tests/integration.mysql.xml
index a8877f92..30769371 100644
--- a/tests/functionals.mysql.xml
+++ b/tests/integration.mysql.xml
@@ -1,7 +1,7 @@
<phpunit stopOnError="true" stopOnFailure="true" colors="true">
<testsuites>
<testsuite name="Kanboard">
- <directory>functionals</directory>
+ <directory>integration</directory>
</testsuite>
</testsuites>
<php>
diff --git a/tests/functionals.postgres.xml b/tests/integration.postgres.xml
index 61da8574..ed8a3de3 100644
--- a/tests/functionals.postgres.xml
+++ b/tests/integration.postgres.xml
@@ -1,7 +1,7 @@
<phpunit stopOnError="true" stopOnFailure="true" colors="true">
<testsuites>
<testsuite name="Kanboard">
- <directory>functionals</directory>
+ <directory>integration</directory>
</testsuite>
</testsuites>
<php>
diff --git a/tests/functionals.sqlite.xml b/tests/integration.sqlite.xml
index 9da59b20..1964f822 100644
--- a/tests/functionals.sqlite.xml
+++ b/tests/integration.sqlite.xml
@@ -1,7 +1,7 @@
<phpunit stopOnError="true" stopOnFailure="true" colors="true">
<testsuites>
<testsuite name="Kanboard">
- <directory>functionals</directory>
+ <directory>integration</directory>
</testsuite>
</testsuites>
<php>
diff --git a/tests/functionals/ApiTest.php b/tests/integration/ApiTest.php
index b250254e..798bde42 100644
--- a/tests/functionals/ApiTest.php
+++ b/tests/integration/ApiTest.php
@@ -45,16 +45,6 @@ class Api extends PHPUnit_Framework_TestCase
return $tasks[0]['id'];
}
- public function testGetTimezone()
- {
- $this->assertEquals('Europe/Paris', $this->client->getTimezone());
- }
-
- public function testGetVersion()
- {
- $this->assertEquals('master', $this->client->getVersion());
- }
-
public function testRemoveAll()
{
$projects = $this->client->getAllProjects();
diff --git a/tests/integration/AppTest.php b/tests/integration/AppTest.php
new file mode 100644
index 00000000..6575fbb8
--- /dev/null
+++ b/tests/integration/AppTest.php
@@ -0,0 +1,34 @@
+<?php
+
+require_once __DIR__.'/Base.php';
+
+class AppTest extends Base
+{
+ public function testGetTimezone()
+ {
+ $this->assertEquals('UTC', $this->app->getTimezone());
+ }
+
+ public function testGetVersion()
+ {
+ $this->assertEquals('master', $this->app->getVersion());
+ }
+
+ public function testGetApplicationRoles()
+ {
+ $roles = $this->app->getApplicationRoles();
+ $this->assertCount(3, $roles);
+ $this->assertEquals('Administrator', $roles['app-admin']);
+ $this->assertEquals('Manager', $roles['app-manager']);
+ $this->assertEquals('User', $roles['app-user']);
+ }
+
+ public function testGetProjectRoles()
+ {
+ $roles = $this->app->getProjectRoles();
+ $this->assertCount(3, $roles);
+ $this->assertEquals('Project Manager', $roles['project-manager']);
+ $this->assertEquals('Project Member', $roles['project-member']);
+ $this->assertEquals('Project Viewer', $roles['project-viewer']);
+ }
+}
diff --git a/tests/integration/Base.php b/tests/integration/Base.php
new file mode 100644
index 00000000..6facd9ce
--- /dev/null
+++ b/tests/integration/Base.php
@@ -0,0 +1,62 @@
+<?php
+
+require_once __DIR__.'/../../vendor/autoload.php';
+
+abstract class Base extends PHPUnit_Framework_TestCase
+{
+ protected $app = null;
+ protected $admin = null;
+ protected $user = null;
+
+ public static function setUpBeforeClass()
+ {
+ if (DB_DRIVER === 'sqlite') {
+ @unlink(DB_FILENAME);
+ } elseif (DB_DRIVER === 'mysql') {
+ $pdo = new PDO('mysql:host='.DB_HOSTNAME, DB_USERNAME, DB_PASSWORD);
+ $pdo->exec('DROP DATABASE '.DB_NAME);
+ $pdo->exec('CREATE DATABASE '.DB_NAME);
+ $pdo = null;
+ } elseif (DB_DRIVER === 'postgres') {
+ $pdo = new PDO('pgsql:host='.DB_HOSTNAME, DB_USERNAME, DB_PASSWORD);
+ $pdo->exec('DROP DATABASE '.DB_NAME);
+ $pdo->exec('CREATE DATABASE '.DB_NAME.' WITH OWNER '.DB_USERNAME);
+ $pdo = null;
+ }
+
+ $service = new Kanboard\ServiceProvider\DatabaseProvider;
+
+ $db = $service->getInstance();
+ $db->table('settings')->eq('option', 'api_token')->update(array('value' => API_KEY));
+ $db->closeConnection();
+ }
+
+ public function setUp()
+ {
+ $this->app = new JsonRPC\Client(API_URL);
+ $this->app->authentication('jsonrpc', API_KEY);
+ $this->app->debug = true;
+
+ $this->admin = new JsonRPC\Client(API_URL);
+ $this->admin->authentication('admin', 'admin');
+ // $this->admin->debug = true;
+
+ $this->user = new JsonRPC\Client(API_URL);
+ $this->user->authentication('user', 'password');
+ // $this->user->debug = true;
+ }
+
+ protected function getProjectId()
+ {
+ $projects = $this->app->getAllProjects();
+ $this->assertNotEmpty($projects);
+ return $projects[0]['id'];
+ }
+
+ protected function getGroupId()
+ {
+ $groups = $this->app->getAllGroups();
+ $this->assertNotEmpty($groups);
+ return $groups[0]['id'];
+ }
+}
diff --git a/tests/integration/GroupMemberTest.php b/tests/integration/GroupMemberTest.php
new file mode 100644
index 00000000..e84c0734
--- /dev/null
+++ b/tests/integration/GroupMemberTest.php
@@ -0,0 +1,39 @@
+<?php
+
+require_once __DIR__.'/Base.php';
+
+class GroupMemberTest extends Base
+{
+ public function testAddMember()
+ {
+ $this->assertNotFalse($this->app->createGroup('My Group A'));
+ $this->assertNotFalse($this->app->createGroup('My Group B'));
+
+ $groupId = $this->getGroupId();
+ $this->assertTrue($this->app->addGroupMember($groupId, 1));
+ }
+
+ public function testGetMembers()
+ {
+ $groups = $this->app->getAllGroups();
+ $members = $this->app->getGroupMembers($groups[0]['id']);
+ $this->assertCount(1, $members);
+ $this->assertEquals('admin', $members[0]['username']);
+
+ $this->assertSame(array(), $this->app->getGroupMembers($groups[1]['id']));
+ }
+
+ public function testIsGroupMember()
+ {
+ $groupId = $this->getGroupId();
+ $this->assertTrue($this->app->isGroupMember($groupId, 1));
+ $this->assertFalse($this->app->isGroupMember($groupId, 2));
+ }
+
+ public function testRemove()
+ {
+ $groupId = $this->getGroupId();
+ $this->assertTrue($this->app->removeGroupMember($groupId, 1));
+ $this->assertFalse($this->app->isGroupMember($groupId, 1));
+ }
+}
diff --git a/tests/integration/GroupTest.php b/tests/integration/GroupTest.php
new file mode 100644
index 00000000..7a5bccc9
--- /dev/null
+++ b/tests/integration/GroupTest.php
@@ -0,0 +1,48 @@
+<?php
+
+require_once __DIR__.'/Base.php';
+
+class GroupTest extends Base
+{
+ public function testCreateGroup()
+ {
+ $this->assertNotFalse($this->app->createGroup('My Group A'));
+ $this->assertNotFalse($this->app->createGroup('My Group B', '1234'));
+ }
+
+ public function testGetter()
+ {
+ $groups = $this->app->getAllGroups();
+ $this->assertCount(2, $groups);
+ $this->assertEquals('My Group A', $groups[0]['name']);
+ $this->assertEquals('', $groups[0]['external_id']);
+ $this->assertEquals('My Group B', $groups[1]['name']);
+ $this->assertEquals('1234', $groups[1]['external_id']);
+
+ $group = $this->app->getGroup($groups[0]['id']);
+ $this->assertNotEmpty($group);
+ $this->assertEquals('My Group A', $group['name']);
+ $this->assertEquals('', $group['external_id']);
+ }
+
+ public function testUpdate()
+ {
+ $groups = $this->app->getAllGroups();
+
+ $this->assertTrue($this->app->updateGroup(array('group_id' => $groups[0]['id'], 'name' => 'ABC', 'external_id' => 'something')));
+ $this->assertTrue($this->app->updateGroup(array('group_id' => $groups[1]['id'], 'external_id' => '')));
+
+ $groups = $this->app->getAllGroups();
+ $this->assertEquals('ABC', $groups[0]['name']);
+ $this->assertEquals('something', $groups[0]['external_id']);
+ $this->assertEquals('', $groups[1]['external_id']);
+ }
+
+ public function testRemove()
+ {
+ $groups = $this->app->getAllGroups();
+ $this->assertTrue($this->app->removeGroup($groups[0]['id']));
+ $this->assertTrue($this->app->removeGroup($groups[1]['id']));
+ $this->assertSame(array(), $this->app->getAllGroups());
+ }
+}
diff --git a/tests/functionals/UserApiTest.php b/tests/integration/MeTest.php
index 3c7fc04e..21f61756 100644
--- a/tests/functionals/UserApiTest.php
+++ b/tests/integration/MeTest.php
@@ -1,51 +1,9 @@
<?php
-require_once __DIR__.'/../../vendor/autoload.php';
+require_once __DIR__.'/Base.php';
-class UserApi extends PHPUnit_Framework_TestCase
+class MeTest extends Base
{
- private $app = null;
- private $admin = null;
- private $user = null;
-
- public static function setUpBeforeClass()
- {
- if (DB_DRIVER === 'sqlite') {
- @unlink(DB_FILENAME);
- } elseif (DB_DRIVER === 'mysql') {
- $pdo = new PDO('mysql:host='.DB_HOSTNAME, DB_USERNAME, DB_PASSWORD);
- $pdo->exec('DROP DATABASE '.DB_NAME);
- $pdo->exec('CREATE DATABASE '.DB_NAME);
- $pdo = null;
- } elseif (DB_DRIVER === 'postgres') {
- $pdo = new PDO('pgsql:host='.DB_HOSTNAME, DB_USERNAME, DB_PASSWORD);
- $pdo->exec('DROP DATABASE '.DB_NAME);
- $pdo->exec('CREATE DATABASE '.DB_NAME.' WITH OWNER '.DB_USERNAME);
- $pdo = null;
- }
-
- $service = new Kanboard\ServiceProvider\DatabaseProvider;
-
- $db = $service->getInstance();
- $db->table('settings')->eq('option', 'api_token')->update(array('value' => API_KEY));
- $db->closeConnection();
- }
-
- public function setUp()
- {
- $this->app = new JsonRPC\Client(API_URL);
- $this->app->authentication('jsonrpc', API_KEY);
- // $this->app->debug = true;
-
- $this->admin = new JsonRPC\Client(API_URL);
- $this->admin->authentication('admin', 'admin');
- // $this->admin->debug = true;
-
- $this->user = new JsonRPC\Client(API_URL);
- $this->user->authentication('user', 'password');
- // $this->user->debug = true;
- }
-
public function testCreateProject()
{
$this->assertEquals(1, $this->app->createProject('team project'));
diff --git a/tests/integration/ProjectPermissionTest.php b/tests/integration/ProjectPermissionTest.php
new file mode 100644
index 00000000..b06ad4ad
--- /dev/null
+++ b/tests/integration/ProjectPermissionTest.php
@@ -0,0 +1,64 @@
+<?php
+
+require_once __DIR__.'/Base.php';
+
+class ProjectPermissionTest extends Base
+{
+ public function testGetProjectUsers()
+ {
+ $this->assertNotFalse($this->app->createProject('Test'));
+ $this->assertNotFalse($this->app->createGroup('Test'));
+
+ $projectId = $this->getProjectId();
+ $groupId = $this->getGroupId();
+
+ $this->assertTrue($this->app->addGroupMember($projectId, $groupId));
+ $this->assertSame(array(), $this->app->getProjectUsers($projectId));
+ }
+
+ public function testProjectUser()
+ {
+ $projectId = $this->getProjectId();
+ $this->assertTrue($this->app->addProjectUser($projectId, 1));
+
+ $users = $this->app->getProjectUsers($projectId);
+ $this->assertCount(1, $users);
+ $this->assertEquals('admin', $users[1]);
+
+ $users = $this->app->getAssignableUsers($projectId);
+ $this->assertCount(1, $users);
+ $this->assertEquals('admin', $users[1]);
+
+ $this->assertTrue($this->app->changeProjectUserRole($projectId, 1, 'project-viewer'));
+
+ $users = $this->app->getAssignableUsers($projectId);
+ $this->assertCount(0, $users);
+
+ $this->assertTrue($this->app->removeProjectUser($projectId, 1));
+ $this->assertSame(array(), $this->app->getProjectUsers($projectId));
+ }
+
+ public function testProjectGroup()
+ {
+ $projectId = $this->getProjectId();
+ $groupId = $this->getGroupId();
+
+ $this->assertTrue($this->app->addProjectGroup($projectId, $groupId));
+
+ $users = $this->app->getProjectUsers($projectId);
+ $this->assertCount(1, $users);
+ $this->assertEquals('admin', $users[1]);
+
+ $users = $this->app->getAssignableUsers($projectId);
+ $this->assertCount(1, $users);
+ $this->assertEquals('admin', $users[1]);
+
+ $this->assertTrue($this->app->changeProjectGroupRole($projectId, $groupId, 'project-viewer'));
+
+ $users = $this->app->getAssignableUsers($projectId);
+ $this->assertCount(0, $users);
+
+ $this->assertTrue($this->app->removeProjectGroup($projectId, 1));
+ $this->assertSame(array(), $this->app->getProjectUsers($projectId));
+ }
+}