From e9fedf3e5cd63aea4da7a71f6647ee427c62fa49 Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Sat, 5 Dec 2015 20:31:27 -0500 Subject: Rewrite of the authentication and authorization system --- doc/api-json-rpc.markdown | 4526 +-------------------------------------------- 1 file changed, 18 insertions(+), 4508 deletions(-) (limited to 'doc/api-json-rpc.markdown') diff --git a/doc/api-json-rpc.markdown b/doc/api-json-rpc.markdown index 359f8b05..710b0b1b 100644 --- a/doc/api-json-rpc.markdown +++ b/doc/api-json-rpc.markdown @@ -43,4511 +43,21 @@ You must call the API with a `POST` HTTP request. Kanboard support batch requests, so you can make multiple API calls in a single HTTP request. It's particularly useful for mobile clients with higher network latency. -Authentication --------------- - -### Default method (HTTP Basic) - -The API credentials are available on the settings page. - -- API end-point: `https://YOUR_SERVER/jsonrpc.php` - -If you want to use the "application api": - -- Username: `jsonrpc` -- Password: API token on the settings page - -Otherwise for the "user api", just use the real username/passsword. - -The API use the [HTTP Basic Authentication Scheme described in the RFC2617](http://www.ietf.org/rfc/rfc2617.txt). -If there is an authentication error, you will receive the HTTP status code `401 Not Authorized`. - -### Authorized User API procedures - -- getMe -- getMyDashboard -- getMyActivityStream -- createMyPrivateProject -- getMyProjectsList -- getMyProjects -- getTimezone -- getVersion -- getDefaultTaskColor -- getDefaultTaskColors -- getColorList -- getProjectById -- getTask -- getTaskByReference -- getAllTasks -- openTask -- closeTask -- moveTaskPosition -- createTask -- updateTask -- getBoard -- getProjectActivity -- getMyOverdueTasks - -### Custom HTTP header - -You can use an alternative HTTP header for the authentication if your server have a very specific configuration. - -- The header name can be anything you want, by example `X-API-Auth`. -- The header value is the `username:password` encoded in Base64. - -Configuration: - -1. Define your custom header in your `config.php`: `define('API_AUTHENTICATION_HEADER', 'X-API-Auth');` -2. Encode the credentials in Base64, example with PHP `base64_encode('jsonrpc:19ffd9709d03ce50675c3a43d1c49c1ac207f4bc45f06c5b2701fbdf8929');` -3. Test with curl: - -```bash -curl \ --H 'X-API-Auth: anNvbnJwYzoxOWZmZDk3MDlkMDNjZTUwNjc1YzNhNDNkMWM0OWMxYWMyMDdmNGJjNDVmMDZjNWIyNzAxZmJkZjg5Mjk=' \ --d '{"jsonrpc": "2.0", "method": "getAllProjects", "id": 1}' \ -http://localhost/kanboard/jsonrpc.php -``` - -Examples --------- - -### Example with cURL - -From the command line: - -```bash -curl \ --u "jsonrpc:19ffd9709d03ce50675c3a43d1c49c1ac207f4bc45f06c5b2701fbdf8929" \ --d '{"jsonrpc": "2.0", "method": "getAllProjects", "id": 1}' \ -http://localhost/kanboard/jsonrpc.php -``` - -Response from the server: - -```json -{ - "jsonrpc":"2.0", - "id":1, - "result":[ - { - "id":"1", - "name":"API test", - "is_active":"1", - "token":"6bd0932fe7f4b5e6e4bc3c72800bfdef36a2c5de2f38f756dfb5bd632ebf", - "last_modified":"1403392631" - } - ] -} -``` - -### Example with Python - -Here a basic example written in Python to create a task: - -```python -#!/usr/bin/env python - -import requests -import json - -def main(): - url = "http://demo.kanboard.net/jsonrpc.php" - api_key = "be4271664ca8169d32af49d8e1ec854edb0290bc3588a2e356275eab9505" - headers = {"content-type": "application/json"} - - payload = { - "method": "createTask", - "params": { - "title": "Python API test", - "project_id": 1 - }, - "jsonrpc": "2.0", - "id": 1, - } - - response = requests.post( - url, - data=json.dumps(payload), - headers=headers, - auth=("jsonrpc", api_key) - ) - - if response.status_code == 401: - print "Authentication failed" - else: - result = response.json() - - assert result["result"] == True - assert result["jsonrpc"] - assert result["id"] == 1 - - print "Task created successfully!" - -if __name__ == "__main__": - main() -``` - -Run this script from your terminal: - -```bash -python jsonrpc.py -Task created successfully! -``` - -### Example with a PHP client: - -I wrote a simple [Json-RPC Client/Server library in PHP](https://github.com/fguillot/JsonRPC), here an example: - -```php -authentication('jsonrpc', '19ffd9709d03ce50675c3a43d1c49c1ac207f4bc45f06c5b2701fbdf8929'); - -print_r($client->getAllProjects()); - -``` - -The response: - -``` -Array -( - [0] => Array - ( - [id] => 1 - [name] => API test - [is_active] => 1 - [token] => 6bd0932fe7f4b5e6e4bc3c72800bfdef36a2c5de2f38f756dfb5bd632ebf - [last_modified] => 1403392631 - ) - -) -``` - -### Example with Ruby - -This example can be used with Kanboard configured with Reverse-Proxy authentication and the API configured with a custom authentication header: - -```ruby -require 'faraday' - -conn = Faraday.new(:url => 'https://kanboard.example.com') do |faraday| - faraday.response :logger - faraday.headers['X-API-Auth'] = 'XXX' # base64_encode('jsonrpc:API_KEY') - faraday.basic_auth(ENV['user'], ENV['pw']) # user/pass to get through basic auth - faraday.adapter Faraday.default_adapter # make requests with Net::HTTP -end - -response = conn.post do |req| - req.url '/jsonrpc.php' - req.headers['Content-Type'] = 'application/json' - req.body = '{ "jsonrpc": "2.0", "id": 1, "method": "getAllProjects" }' -end - -puts response.body -``` - - -### Example with Java - -This is a basic example using Spring. For proper usage see [this link](http://spring.io/guides/gs/consuming-rest). - -```java -import java.io.UnsupportedEncodingException; -import java.util.Base64; - -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.web.client.RestTemplate; - -public class ProjectService { - - public void getAllProjects() throws UnsupportedEncodingException { - - RestTemplate restTemplate = new RestTemplate(); - - String url = "http://localhost/kanboard/jsonrpc.php"; - String requestJson = "{\"jsonrpc\": \"2.0\", \"method\": \"getAllProjects\", \"id\": 1}"; - String user = "jsonrpc"; - String apiToken = "19ffd9709d03ce50675c3a43d1c49c1ac207f4bc45f06c5b2701fbdf8929"; - - // encode api token - byte[] xApiAuthTokenBytes = String.join(":", user, apiToken).getBytes("utf-8"); - String xApiAuthToken = Base64.getEncoder().encodeToString(xApiAuthTokenBytes); - - // consume request - HttpHeaders headers = new HttpHeaders(); - headers.add("X-API-Auth", xApiAuthToken); - headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity entity = new HttpEntity(requestJson, headers); - String answer = restTemplate.postForObject(url, entity, String.class); - System.out.println(answer); - } -} -``` - -Procedures ----------- - -### getVersion - -- Purpose: **Get the application version** -- Parameters: none -- Result: **version** (Example: 1.0.12, master) - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getVersion", - "id": 1661138292 -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1661138292, - "result": "1.0.13" -} -``` - -### getTimezone - -- Purpose: **Get the application timezone** -- Parameters: none -- Result on success: **Timezone** (Example: UTC, Europe/Paris) -- Result on failure: **Default timezone** (UTC) - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getTimezone", - "id": 1661138292 -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1661138292, - "result": "Europe\/Paris" -} -``` - -### getDefaultTaskColors - -- Purpose: **Get all default task colors** -- Parameters: None -- Result on success: **Color properties** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getDefaultTaskColors", - "id": 2108929212 -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 2108929212, - "result": { - "yellow": { - "name": "Yellow", - "background": "rgb(245, 247, 196)", - "border": "rgb(223, 227, 45)" - }, - "blue": { - "name": "Blue", - "background": "rgb(219, 235, 255)", - "border": "rgb(168, 207, 255)" - }, - "green": { - "name": "Green", - "background": "rgb(189, 244, 203)", - "border": "rgb(74, 227, 113)" - }, - "purple": { - "name": "Purple", - "background": "rgb(223, 176, 255)", - "border": "rgb(205, 133, 254)" - }, - "red": { - "name": "Red", - "background": "rgb(255, 187, 187)", - "border": "rgb(255, 151, 151)" - }, - "orange": { - "name": "Orange", - "background": "rgb(255, 215, 179)", - "border": "rgb(255, 172, 98)" - }, - "grey": { - "name": "Grey", - "background": "rgb(238, 238, 238)", - "border": "rgb(204, 204, 204)" - }, - "brown": { - "name": "Brown", - "background": "#d7ccc8", - "border": "#4e342e" - }, - "deep_orange": { - "name": "Deep Orange", - "background": "#ffab91", - "border": "#e64a19" - }, - "dark_grey": { - "name": "Dark Grey", - "background": "#cfd8dc", - "border": "#455a64" - }, - "pink": { - "name": "Pink", - "background": "#f48fb1", - "border": "#d81b60" - }, - "teal": { - "name": "Teal", - "background": "#80cbc4", - "border": "#00695c" - }, - "cyan": { - "name": "Cyan", - "background": "#b2ebf2", - "border": "#00bcd4" - }, - "lime": { - "name": "Lime", - "background": "#e6ee9c", - "border": "#afb42b" - }, - "light_green": { - "name": "Light Green", - "background": "#dcedc8", - "border": "#689f38" - }, - "amber": { - "name": "Amber", - "background": "#ffe082", - "border": "#ffa000" - } - } -} -``` - -### getDefaultTaskColor - -- Purpose: **Get default task color** -- Parameters: None -- Result on success: **color_id** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getDefaultTaskColor", - "id": 1144775215 -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1144775215, - "result": "yellow" -} -``` - -### getColorList - -- Purpose: **Get the list of task colors** -- Parameters: none -- Result on success: **Dictionary of color_id => color_name** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getColorList", - "id": 1677051386 -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1677051386, - "result": { - "yellow": "Yellow", - "blue": "Blue", - "green": "Green", - "purple": "Purple", - "red": "Red", - "orange": "Orange", - "grey": "Grey", - "brown": "Brown", - "deep_orange": "Deep Orange", - "dark_grey": "Dark Grey", - "pink": "Pink", - "teal": "Teal", - "cyan": "Cyan", - "lime": "Lime", - "light_green": "Light Green", - "amber": "Amber" - } -} -``` - -### createProject - -- Purpose: **Create a new project** -- Parameters: - - **name** (string, required) - - **description** (string, optional) -- Result on success: **project_id** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "createProject", - "id": 1797076613, - "params": { - "name": "PHP client" - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1797076613, - "result": 2 -} -``` - -### getProjectById - -- Purpose: **Get project information** -- Parameters: - - **project_id** (integer, required) -- Result on success: **project properties** -- Result on failure: **null** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getProjectById", - "id": 226760253, - "params": { - "project_id": 1 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 226760253, - "result": { - "id": "1", - "name": "API test", - "is_active": "1", - "token": "", - "last_modified": "1436119135", - "is_public": "0", - "is_private": "0", - "is_everybody_allowed": "0", - "default_swimlane": "Default swimlane", - "show_default_swimlane": "1", - "description": "test", - "identifier": "", - "url": { - "board": "http:\/\/127.0.0.1:8000\/?controller=board&action=show&project_id=1", - "calendar": "http:\/\/127.0.0.1:8000\/?controller=calendar&action=show&project_id=1", - "list": "http:\/\/127.0.0.1:8000\/?controller=listing&action=show&project_id=1" - } - } -} -``` - -### getProjectByName - -- Purpose: **Get project information** -- Parameters: - - **name** (string, required) -- Result on success: **project properties** -- Result on failure: **null** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getProjectByName", - "id": 1620253806, - "params": { - "name": "Test" - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1620253806, - "result": { - "id": "1", - "name": "Test", - "is_active": "1", - "token": "", - "last_modified": "1436119135", - "is_public": "0", - "is_private": "0", - "is_everybody_allowed": "0", - "default_swimlane": "Default swimlane", - "show_default_swimlane": "1", - "description": "test", - "identifier": "", - "url": { - "board": "http:\/\/127.0.0.1:8000\/?controller=board&action=show&project_id=1", - "calendar": "http:\/\/127.0.0.1:8000\/?controller=calendar&action=show&project_id=1", - "list": "http:\/\/127.0.0.1:8000\/?controller=listing&action=show&project_id=1" - } - } -} -``` - -### getAllProjects - -- Purpose: **Get all available projects** -- Parameters: - - **none** -- Result on success: **List of projects** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getAllProjects", - "id": 2134420212 -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 2134420212, - "result": [ - { - "id": "1", - "name": "API test", - "is_active": "1", - "token": "", - "last_modified": "1436119570", - "is_public": "0", - "is_private": "0", - "is_everybody_allowed": "0", - "default_swimlane": "Default swimlane", - "show_default_swimlane": "1", - "description": null, - "identifier": "", - "url": { - "board": "http:\/\/127.0.0.1:8000\/?controller=board&action=show&project_id=1", - "calendar": "http:\/\/127.0.0.1:8000\/?controller=calendar&action=show&project_id=1", - "list": "http:\/\/127.0.0.1:8000\/?controller=listing&action=show&project_id=1" - } - } - ] -} -``` - -### updateProject - -- Purpose: **Update a project** -- Parameters: - - **id** (integer, required) - - **name** (string, required) - - **description** (string, optional) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "updateProject", - "id": 1853996288, - "params": { - "id": 1, - "name": "PHP client update" - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1853996288, - "result": true -} -``` - -### removeProject - -- Purpose: **Remove a project** -- Parameters: - **project_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "removeProject", - "id": 46285125, - "params": { - "project_id": "2" - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 46285125, - "result": true -} -``` - -### enableProject - -- Purpose: **Enable a project** -- Parameters: - - **project_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "enableProject", - "id": 1775494839, - "params": [ - "1" - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1775494839, - "result": true -} -``` - -### disableProject - -- Purpose: **Disable a project** -- Parameters: - - **project_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "disableProject", - "id": 1734202312, - "params": [ - "1" - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1734202312, - "result": true -} -``` - -### enableProjectPublicAccess - -- Purpose: **Enable public access for a given project** -- Parameters: - - **project_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "enableProjectPublicAccess", - "id": 103792571, - "params": [ - "1" - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 103792571, - "result": true -} -``` - -### disableProjectPublicAccess - -- Purpose: **Disable public access for a given project** -- Parameters: - - **project_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "disableProjectPublicAccess", - "id": 942472945, - "params": [ - "1" - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 942472945, - "result": true -} -``` - -### getProjectActivity - -- Purpose: **Get activity stream for a project** -- Parameters: - - **project_id** (integer, required) -- Result on success: **List of events** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getProjectActivity", - "id": 942472945, - "params": [ - "project_id": 1 - ] -} -``` - -### getProjectActivities - -- Purpose: **Get Activityfeed for Project(s)** -- Parameters: - - **project_ids** (integer array, required) -- Result on success: **List of events** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getProjectActivities", - "id": 942472945, - "params": [ - "project_ids": [1,2] - ] -} -``` - -### 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 -} -``` - - -### getBoard - -- Purpose: **Get all necessary information to display a board** -- Parameters: - - **project_id** (integer, required) -- Result on success: **board properties** -- Result on failure: **empty list** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getBoard", - "id": 827046470, - "params": [ - 1 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 827046470, - "result": [ - { - "id": 0, - "name": "Default swimlane", - "columns": [ - { - "id": "1", - "title": "Backlog", - "position": "1", - "project_id": "1", - "task_limit": "0", - "description": "", - "tasks": [], - "nb_tasks": 0, - "score": 0 - }, - { - "id": "2", - "title": "Ready", - "position": "2", - "project_id": "1", - "task_limit": "0", - "description": "", - "tasks": [ - { - "nb_comments":"0", - "nb_files":"0", - "nb_subtasks":"0", - "nb_completed_subtasks":"0", - "nb_links":"0", - "id":"2", - "reference":"", - "title":"Test", - "description":"", - "date_creation":"1430870507", - "date_modification":"1430870507", - "date_completed":null, - "date_due":"0", - "color_id":"yellow", - "project_id":"1", - "column_id":"2", - "swimlane_id":"0", - "owner_id":"0", - "creator_id":"1", - "position":"1", - "is_active":"1", - "score":"0", - "category_id":"0", - "date_moved":"1430870507", - "recurrence_status":"0", - "recurrence_trigger":"0", - "recurrence_factor":"0", - "recurrence_timeframe":"0", - "recurrence_basedate":"0", - "recurrence_parent":null, - "recurrence_child":null, - "assignee_username":null, - "assignee_name":null - } - ], - "nb_tasks": 1, - "score": 0 - }, - { - "id": "3", - "title": "Work in progress", - "position": "3", - "project_id": "1", - "task_limit": "0", - "description": "", - "tasks": [ - { - "nb_comments":"0", - "nb_files":"0", - "nb_subtasks":"1", - "nb_completed_subtasks":"0", - "nb_links":"0", - "id":"1", - "reference":"", - "title":"Task with comment", - "description":"", - "date_creation":"1430783188", - "date_modification":"1430783188", - "date_completed":null, - "date_due":"0", - "color_id":"red", - "project_id":"1", - "column_id":"3", - "swimlane_id":"0", - "owner_id":"1", - "creator_id":"0", - "position":"1", - "is_active":"1", - "score":"0", - "category_id":"0", - "date_moved":"1430783191", - "recurrence_status":"0", - "recurrence_trigger":"0", - "recurrence_factor":"0", - "recurrence_timeframe":"0", - "recurrence_basedate":"0", - "recurrence_parent":null, - "recurrence_child":null, - "assignee_username":"admin", - "assignee_name":null - } - ], - "nb_tasks": 1, - "score": 0 - }, - { - "id": "4", - "title": "Done", - "position": "4", - "project_id": "1", - "task_limit": "0", - "description": "", - "tasks": [], - "nb_tasks": 0, - "score": 0 - } - ], - "nb_columns": 4, - "nb_tasks": 2 - } - ] -} -``` - -### getColumns - -- Purpose: **Get all columns information for a given project** -- Parameters: - - **project_id** (integer, required) -- Result on success: **columns properties** -- Result on failure: **empty list** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getColumns", - "id": 887036325, - "params": [ - 1 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 887036325, - "result": [ - { - "id": "1", - "title": "Backlog", - "position": "1", - "project_id": "1", - "task_limit": "0" - }, - { - "id": "2", - "title": "Ready", - "position": "2", - "project_id": "1", - "task_limit": "0" - }, - { - "id": "3", - "title": "Work in progress", - "position": "3", - "project_id": "1", - "task_limit": "0" - } - ] -} -``` - -### getColumn - -- Purpose: **Get a single column** -- Parameters: - - **column_id** (integer, required) -- Result on success: **column properties** -- Result on failure: **null** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getColumn", - "id": 1242049935, - "params": [ - 2 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1242049935, - "result": { - "id": "2", - "title": "Youpi", - "position": "2", - "project_id": "1", - "task_limit": "5" - } -} -``` - -### moveColumnUp - -- Purpose: **Move up the column position** -- Parameters: - - **project_id** (integer, required) - - **column_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "moveColumnUp", - "id": 99275573, - "params": [ - 1, - 2 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 99275573, - "result": true -} -``` - -### moveColumnDown - -- Purpose: **Move down the column position** -- Parameters: - - **project_id** (integer, required) - - **column_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "moveColumnDown", - "id": 957090649, - "params": { - "project_id": 1, - "column_id": 2 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 957090649, - "result": true -} -``` - -### updateColumn - -- Purpose: **Update column properties** -- Parameters: - - **column_id** (integer, required) - - **title** (string, required) - - **task_limit** (integer, optional) - - **description** (string, optional) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "updateColumn", - "id": 480740641, - "params": [ - 2, - "Boo", - 5 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 480740641, - "result": true -} -``` - -### addColumn - -- Purpose: **Add a new column** -- Parameters: - - **project_id** (integer, required) - - **title** (string, required) - - **task_limit** (integer, optional) - - **description** (string, optional) -- Result on success: **column_id** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "addColumn", - "id": 638544704, - "params": [ - 1, - "Boo" - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 638544704, - "result": 5 -} -``` - -### removeColumn - -- Purpose: **Remove a column** -- Parameters: - - **column_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "removeColumn", - "id": 1433237746, - "params": [ - 1 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1433237746, - "result": true -} -``` - -### getDefaultSwimlane - -- Purpose: **Get the default swimlane for a project** -- Parameters: - - **project_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getDefaultSwimlane", - "id": 898774713, - "params": [ - 1 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 898774713, - "result": { - "id": "1", - "default_swimlane": "Default swimlane", - "show_default_swimlane": "1" - } -} -``` - -### getActiveSwimlanes - -- Purpose: **Get the list of enabled swimlanes of a project (include default swimlane if enabled)** -- Parameters: - - **project_id** (integer, required) -- Result on success: **List of swimlanes** -- Result on failure: **null** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getActiveSwimlanes", - "id": 934789422, - "params": [ - 1 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 934789422, - "result": [ - { - "id": 0, - "name": "Default swimlane" - }, - { - "id": "2", - "name": "Swimlane A" - } - ] -} -``` - -### getAllSwimlanes - -- Purpose: **Get the list of all swimlanes of a project (enabled or disabled) and sorted by position** -- Parameters: - - **project_id** (integer, required) -- Result on success: **List of swimlanes** -- Result on failure: **null** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getAllSwimlanes", - "id": 509791576, - "params": [ - 1 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 509791576, - "result": [ - { - "id": "1", - "name": "Another swimlane", - "position": "1", - "is_active": "1", - "project_id": "1" - }, - { - "id": "2", - "name": "Swimlane A", - "position": "2", - "is_active": "1", - "project_id": "1" - } - ] -} -``` - -### getSwimlane - -- Purpose: **Get the a swimlane by id** -- Parameters: - - **swimlane_id** (integer, required) -- Result on success: **swimlane properties** -- Result on failure: **null** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getSwimlane", - "id": 131071870, - "params": [ - 1 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 131071870, - "result": { - "id": "1", - "name": "Swimlane 1", - "position": "1", - "is_active": "1", - "project_id": "1" - } -} -``` - -### getSwimlaneById - -- Purpose: **Get the a swimlane by id** -- Parameters: - - **swimlane_id** (integer, required) -- Result on success: **swimlane properties** -- Result on failure: **null** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getSwimlaneById", - "id": 131071870, - "params": [ - 1 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 131071870, - "result": { - "id": "1", - "name": "Swimlane 1", - "position": "1", - "is_active": "1", - "project_id": "1" - } -} -``` - -### getSwimlaneByName - -- Purpose: **Get the a swimlane by name** -- Parameters: - - **project_id** (integer, required) - - **name** (string, required) -- Result on success: **swimlane properties** -- Result on failure: **null** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getSwimlaneByName", - "id": 824623567, - "params": [ - 1, - "Swimlane 1" - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 824623567, - "result": { - "id": "1", - "name": "Swimlane 1", - "position": "1", - "is_active": "1", - "project_id": "1" - } -} -``` - -### moveSwimlaneUp - -- Purpose: **Move up the swimlane position** -- Parameters: - - **project_id** (integer, required) - - **swimlane_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "moveSwimlaneUp", - "id": 99275573, - "params": [ - 1, - 2 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 99275573, - "result": true -} -``` - -### moveSwimlaneDown - -- Purpose: **Move down the swimlane position** -- Parameters: - - **project_id** (integer, required) - - **swimlane_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "moveSwimlaneDown", - "id": 957090649, - "params": { - "project_id": 1, - "swimlane_id": 2 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 957090649, - "result": true -} -``` - -### updateSwimlane - -- Purpose: **Update swimlane properties** -- Parameters: - - **swimlane_id** (integer, required) - - **name** (string, required) - - **description** (string, optional) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "updateSwimlane", - "id": 87102426, - "params": [ - "1", - "Another swimlane" - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 87102426, - "result": true -} -``` - -### addSwimlane - -- Purpose: **Add a new swimlane** -- Parameters: - - **project_id** (integer, required) - - **name** (string, required) - - **description** (string, optional) -- Result on success: **swimlane_id** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "addSwimlane", - "id": 849940086, - "params": [ - 1, - "Swimlane 1" - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 849940086, - "result": 1 -} -``` - -### removeSwimlane - -- Purpose: **Remove a swimlane** -- Parameters: - - **project_id** (integer, required) - - **swimlane_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "removeSwimlane", - "id": 1433237746, - "params": [ - 2, - 1 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1433237746, - "result": true -} -``` - -### disableSwimlane - -- Purpose: **Enable a swimlane** -- Parameters: - - **project_id** (integer, required) - - **swimlane_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "disableSwimlane", - "id": 1433237746, - "params": [ - 2, - 1 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1433237746, - "result": true -} -``` - -### enableSwimlane - -- Purpose: **Enable a swimlane** -- Parameters: - - **project_id** (integer, required) - - **swimlane_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "enableSwimlane", - "id": 1433237746, - "params": [ - 2, - 1 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1433237746, - "result": true -} -``` - -### getAvailableActions - -- Purpose: **Get list of available automatic actions** -- Parameters: none -- Result on success: **list of actions** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getAvailableActions", - "id": 1217735483 -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1217735483, - "result": { - "TaskLogMoveAnotherColumn": "Add a comment logging moving the task between columns", - "TaskAssignColorUser": "Assign a color to a specific user", - "TaskAssignColorColumn": "Assign a color when the task is moved to a specific column", - "TaskAssignCategoryColor": "Assign automatically a category based on a color", - "TaskAssignColorCategory": "Assign automatically a color based on a category", - "TaskAssignSpecificUser": "Assign the task to a specific user", - "TaskAssignCurrentUser": "Assign the task to the person who does the action", - "TaskUpdateStartDate": "Automatically update the start date", - "TaskAssignUser": "Change the assignee based on an external username", - "TaskAssignCategoryLabel": "Change the category based on an external label", - "TaskClose": "Close a task", - "CommentCreation": "Create a comment from an external provider", - "TaskCreation": "Create a task from an external provider", - "TaskDuplicateAnotherProject": "Duplicate the task to another project", - "TaskMoveColumnAssigned": "Move the task to another column when assigned to a user", - "TaskMoveColumnUnAssigned": "Move the task to another column when assignee is cleared", - "TaskMoveAnotherProject": "Move the task to another project", - "TaskOpen": "Open a task" - } -} -``` - -### getAvailableActionEvents - -- Purpose: **Get list of available events for actions** -- Parameters: none -- Result on success: **list of events** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getAvailableActionEvents", - "id": 2116665643 -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 2116665643, - "result": { - "bitbucket.webhook.commit": "Bitbucket commit received", - "task.close": "Closing a task", - "github.webhook.commit": "Github commit received", - "github.webhook.issue.assignee": "Github issue assignee change", - "github.webhook.issue.closed": "Github issue closed", - "github.webhook.issue.commented": "Github issue comment created", - "github.webhook.issue.label": "Github issue label change", - "github.webhook.issue.opened": "Github issue opened", - "github.webhook.issue.reopened": "Github issue reopened", - "gitlab.webhook.commit": "Gitlab commit received", - "gitlab.webhook.issue.closed": "Gitlab issue closed", - "gitlab.webhook.issue.opened": "Gitlab issue opened", - "task.move.column": "Move a task to another column", - "task.open": "Open a closed task", - "task.assignee_change": "Task assignee change", - "task.create": "Task creation", - "task.create_update": "Task creation or modification", - "task.update": "Task modification" - } -} -``` - -### getCompatibleActionEvents - -- Purpose: **Get list of events compatible with an action** -- Parameters: - - **action_name** (string, required) -- Result on success: **list of events** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getCompatibleActionEvents", - "id": 899370297, - "params": [ - "TaskClose" - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 899370297, - "result": { - "bitbucket.webhook.commit": "Bitbucket commit received", - "github.webhook.commit": "Github commit received", - "github.webhook.issue.closed": "Github issue closed", - "gitlab.webhook.commit": "Gitlab commit received", - "gitlab.webhook.issue.closed": "Gitlab issue closed", - "task.move.column": "Move a task to another column" - } -} -``` - -### getActions - -- Purpose: **Get list of actions for a project** -- Parameters: - - **project_id** (integer, required) -- Result on success: **list of actions properties** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getActions", - "id": 1433237746, - "params": [ - "1" - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1433237746, - "result": [ - { - "id" : "13", - "project_id" : "2", - "event_name" : "task.move.column", - "action_name" : "TaskAssignSpecificUser", - "params" : { - "column_id" : "5", - "user_id" : "1" - } - } - ] -} -``` - -### createAction - -- Purpose: **Create an action** -- Parameters: - - **project_id** (integer, required) - - **event_name** (string, required) - - **action_name** (string, required) - - **params** (key/value parameters, required) -- Result on success: **action_id** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "createAction", - "id": 1433237746, - "params": { - "project_id" : "2", - "event_name" : "task.move.column", - "action_name" : "TaskAssignSpecificUser", - "params" : { - "column_id" : "3", - "user_id" : "2" - } - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1433237746, - "result": 14 -} -``` - -### removeAction - -- Purpose: **Remove an action** -- Parameters: - - **action_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "removeAction", - "id": 1510741671, - "params": [ - 1 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1510741671, - "result": true -} -``` - -### createTask - -- Purpose: **Create a new task** -- Parameters: - - **title** (string, required) - - **project_id** (integer, required) - - **color_id** (string, optional) - - **column_id** (integer, optional) - - **owner_id** (integer, optional) - - **creator_id** (integer, optional) - - **date_due**: ISO8601 format (string, optional) - - **description** Markdown content (string, optional) - - **category_id** (integer, optional) - - **score** (integer, optional) - - **swimlane_id** (integer, optional) - - **recurrence_status** (integer, optional) - - **recurrence_trigger** (integer, optional) - - **recurrence_factor** (integer, optional) - - **recurrence_timeframe** (integer, optional) - - **recurrence_basedate** (integer, optional) -- Result on success: **task_id** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "createTask", - "id": 1176509098, - "params": { - "owner_id": 1, - "creator_id": 0, - "date_due": "", - "description": "", - "category_id": 0, - "score": 0, - "title": "Test", - "project_id": 1, - "color_id": "green", - "column_id": 2, - "recurrence_status": 0, - "recurrence_trigger": 0, - "recurrence_factor": 0, - "recurrence_timeframe": 0, - "recurrence_basedate": 0 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1176509098, - "result": 3 -} -``` - -### getTask - -- Purpose: **Get task by the unique id** -- Parameters: - - **task_id** (integer, required) -- Result on success: **task properties** -- Result on failure: **null** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getTask", - "id": 700738119, - "params": { - "task_id": 1 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 700738119, - "result": { - "id": "1", - "title": "Task #1", - "description": "", - "date_creation": "1409963206", - "color_id": "blue", - "project_id": "1", - "column_id": "2", - "owner_id": "1", - "position": "1", - "is_active": "1", - "date_completed": null, - "score": "0", - "date_due": "0", - "category_id": "0", - "creator_id": "0", - "date_modification": "1409963206", - "reference": "", - "date_started": null, - "time_spent": "0", - "time_estimated": "0", - "swimlane_id": "0", - "date_moved": "1430875287", - "recurrence_status": "0", - "recurrence_trigger": "0", - "recurrence_factor": "0", - "recurrence_timeframe": "0", - "recurrence_basedate": "0", - "recurrence_parent": null, - "recurrence_child": null, - "url": "http:\/\/127.0.0.1:8000\/?controller=task&action=show&task_id=1&project_id=1", - "color": { - "name": "Yellow", - "background": "rgb(245, 247, 196)", - "border": "rgb(223, 227, 45)" - } - } -} -``` - -### getTaskByReference - -- Purpose: **Get task by the external reference** -- Parameters: - - **project_id** (integer, required) - - **reference** (string, required) -- Result on success: **task properties** -- Result on failure: **null** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getTaskByReference", - "id": 1992081213, - "params": { - "project_id": 1, - "reference": "TICKET-1234" - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1992081213, - "result": { - "id": "5", - "title": "Task with external ticket number", - "description": "[Link to my ticket](http:\/\/my-ticketing-system\/1234)", - "date_creation": "1434227446", - "color_id": "yellow", - "project_id": "1", - "column_id": "1", - "owner_id": "0", - "position": "4", - "is_active": "1", - "date_completed": null, - "score": "0", - "date_due": "0", - "category_id": "0", - "creator_id": "0", - "date_modification": "1434227446", - "reference": "TICKET-1234", - "date_started": null, - "time_spent": "0", - "time_estimated": "0", - "swimlane_id": "0", - "date_moved": "1434227446", - "recurrence_status": "0", - "recurrence_trigger": "0", - "recurrence_factor": "0", - "recurrence_timeframe": "0", - "recurrence_basedate": "0", - "recurrence_parent": null, - "recurrence_child": null, - "url": "http:\/\/127.0.0.1:8000\/?controller=task&action=show&task_id=5&project_id=1" - } -} -``` - -### getAllTasks - -- Purpose: **Get all available tasks** -- Parameters: - - **project_id** (integer, required) - - **status_id**: The value 1 for active tasks and 0 for inactive (integer, required) -- Result on success: **List of tasks** -- Result on failure: **false** - -Request example to fetch all tasks on the board: - -```json -{ - "jsonrpc": "2.0", - "method": "getAllTasks", - "id": 133280317, - "params": { - "project_id": 1, - "status_id": 1 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 133280317, - "result": [ - { - "id": "1", - "title": "Task #1", - "description": "", - "date_creation": "1409961789", - "color_id": "blue", - "project_id": "1", - "column_id": "2", - "owner_id": "1", - "position": "1", - "is_active": "1", - "date_completed": null, - "score": "0", - "date_due": "0", - "category_id": "0", - "creator_id": "0", - "date_modification": "1409961789", - "reference": "", - "date_started": null, - "time_spent": "0", - "time_estimated": "0", - "swimlane_id": "0", - "date_moved": "1430783191", - "recurrence_status": "0", - "recurrence_trigger": "0", - "recurrence_factor": "0", - "recurrence_timeframe": "0", - "recurrence_basedate": "0", - "recurrence_parent": null, - "recurrence_child": null, - "url": "http:\/\/127.0.0.1:8000\/?controller=task&action=show&task_id=1&project_id=1" - }, - { - "id": "2", - "title": "Test", - "description": "", - "date_creation": "1409962115", - "color_id": "green", - "project_id": "1", - "column_id": "2", - "owner_id": "1", - "position": "2", - "is_active": "1", - "date_completed": null, - "score": "0", - "date_due": "0", - "category_id": "0", - "creator_id": "0", - "date_modification": "1409962115", - "reference": "", - "date_started": null, - "time_spent": "0", - "time_estimated": "0", - "swimlane_id": "0", - "date_moved": "1430783191", - "recurrence_status": "0", - "recurrence_trigger": "0", - "recurrence_factor": "0", - "recurrence_timeframe": "0", - "recurrence_basedate": "0", - "recurrence_parent": null, - "recurrence_child": null, - "url": "http:\/\/127.0.0.1:8000\/?controller=task&action=show&task_id=2&project_id=1" - }, - ... - ] -} -``` - -### getOverdueTasks - -- Purpose: **Get all overdue tasks** -- Result on success: **List of tasks** -- Result on failure: **false** - -Request example to fetch all tasks on the board: - -```json -{ - "jsonrpc": "2.0", - "method": "getOverdueTasks", - "id": 133280317 -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 133280317, - "result": [ - { - "id": "1", - "title": "Task #1", - "date_due": "1409961789", - "project_id": "1", - "project_name": "Test", - "assignee_username":"admin", - "assignee_name": null - }, - { - "id": "2", - "title": "Test", - "date_due": "1409962115", - "project_id": "1", - "project_name": "Test", - "assignee_username":"admin", - "assignee_name": null - }, - ... - ] -} -``` - -### getOverdueTasksByProject - -- Purpose: **Get all overdue tasks for a special project** -- Result on success: **List of tasks** -- Result on failure: **false** - -Request example to fetch all tasks on the board: - -```json -{ - "jsonrpc": "2.0", - "method": "getOverdueTasksByProject", - "id": 133280317, - "params": { - "project_id": 1 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 133280317, - "result": [ - { - "id": "1", - "title": "Task #1", - "date_due": "1409961789", - "project_id": "1", - "project_name": "Test", - "assignee_username":"admin", - "assignee_name": null - }, - { - "id": "2", - "title": "Test", - "date_due": "1409962115", - "project_id": "1", - "project_name": "Test", - "assignee_username":"admin", - "assignee_name": null - }, - ... - ] -} -``` - -### updateTask - -- Purpose: **Update a task** -- Parameters: - - **id** (integer, required) - - **title** (string, optional) - - **project_id** (integer, optional) - - **color_id** (string, optional) - - **owner_id** (integer, optional) - - **creator_id** (integer, optional) - - **date_due**: ISO8601 format (string, optional) - - **description** Markdown content (string, optional) - - **category_id** (integer, optional) - - **score** (integer, optional) - - **recurrence_status** (integer, optional) - - **recurrence_trigger** (integer, optional) - - **recurrence_factor** (integer, optional) - - **recurrence_timeframe** (integer, optional) - - **recurrence_basedate** (integer, optional) -- Result on success: **true** -- Result on failure: **false** - -Request example to change the task color: - -```json -{ - "jsonrpc": "2.0", - "method": "updateTask", - "id": 1406803059, - "params": { - "id": 1, - "color_id": "blue" - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1406803059, - "result": true -} -``` - -### openTask - -- Purpose: **Set a task to the status open** -- Parameters: - - **task_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "openTask", - "id": 1888531925, - "params": { - "task_id": 1 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1888531925, - "result": true -} -``` - -### closeTask - -- Purpose: **Set a task to the status close** -- Parameters: - - **task_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "closeTask", - "id": 1654396960, - "params": { - "task_id": 1 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1654396960, - "result": true -} -``` - -### removeTask - -- Purpose: **Remove a task** -- Parameters: - - **task_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "removeTask", - "id": 1423501287, - "params": { - "task_id": 1 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1423501287, - "result": true -} -``` - -### moveTaskPosition - -- Purpose: **Move a task to another column or another position** -- Parameters: - - **project_id** (integer, required) - - **task_id** (integer, required) - - **column_id** (integer, required) - - **position** (integer, required) - - **swimlane_id** (integer, optional, default=0) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "moveTaskPosition", - "id": 117211800, - "params": { - "project_id": 1, - "task_id": 1, - "column_id": 2, - "position": 1 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 117211800, - "result": true -} -``` - -### createUser - -- Purpose: **Create a new user** -- Parameters: - - **username** Must be unique (string, required) - - **password** Must have at least 6 characters (string, required) - - **name** (string, optional) - - **email** (string, optional) - - **is_admin** Set the value 1 for admins or 0 for regular users (integer, optional) - - **is_project_admin** Set the value 1 for project admins or 0 for regular users (integer, optional) -- Result on success: **user_id** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "createUser", - "id": 1518863034, - "params": { - "username": "biloute", - "password": "123456" - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1518863034, - "result": 22 -} -``` - -### createLdapUser - -- Purpose: **Create a new user authentified by LDAP** -- Parameters: - - **username** (string, optional if email is set) - - **email** (string, optional if username is set) - - **is_admin** Set the value 1 for admins or 0 for regular users (integer, optional) - - **is_project_admin** Set the value 1 for project admins or 0 for regular users (integer, optional) -- Result on success: **user_id** -- Result on failure: **false** - -The user will only be created if a matching is found on the LDAP server. -Username or email (or both) must be provided. - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "createLdapUser", - "id": 1518863034, - "params": { - "username": "biloute", - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1518863034, - "result": 22 -} -``` - -### getUser - -- Purpose: **Get user information** -- Parameters: - - **user_id** (integer, required) -- Result on success: **user properties** -- Result on failure: **null** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getUser", - "id": 1769674781, - "params": { - "user_id": 1 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1769674781, - "result": { - "id": "1", - "username": "biloute", - "password": "$2y$10$dRs6pPoBu935RpmsrhmbjevJH5MgZ7Kr9QrnVINwwyZ3.MOwqg.0m", - "is_admin": "0", - "is_ldap_user": "0", - "name": "", - "email": "", - "google_id": null, - "github_id": null, - "notifications_enabled": "0" - } -} -``` - -### getAllUsers - -- Purpose: **Get all available users** -- Parameters: - - **none** -- Result on success: **List of users** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getAllUsers", - "id": 1438712131 -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1438712131, - "result": [ - { - "id": "1", - "username": "biloute", - "name": "", - "email": "", - "is_admin": "0", - "is_ldap_user": "0", - "notifications_enabled": "0", - "google_id": null, - "github_id": null - }, - ... - ] -} -``` - -### updateUser - -- Purpose: **Update a user** -- Parameters: - - **id** (integer) - - **username** (string, optional) - - **name** (string, optional) - - **email** (string, optional) - - **is_admin** (integer, optional) - - **is_project_admin** Set the value 1 for project admins or 0 for regular users (integer, optional) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "updateUser", - "id": 322123657, - "params": { - "id": 1, - "is_admin": 1 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 322123657, - "result": true -} -``` - -### removeUser - -- Purpose: **Remove a user** -- Parameters: - - **user_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "removeUser", - "id": 2094191872, - "params": { - "user_id": 1 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 2094191872, - "result": true -} -``` - - -### createCategory - -- Purpose: **Create a new category** -- Parameters: -- **project_id** (integer, required) - - **name** (string, required, must be unique for the given project) -- Result on success: **category_id** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "createCategory", - "id": 541909890, - "params": { - "name": "Super category", - "project_id": 1 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 541909890, - "result": 4 -} -``` - -### getCategory - -- Purpose: **Get category information** -- Parameters: - - **category_id** (integer, required) -- Result on success: **category properties** -- Result on failure: **null** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getCategory", - "id": 203539163, - "params": { - "category_id": 1 - } -} -``` - -Response example: - -```json -{ - - "jsonrpc": "2.0", - "id": 203539163, - "result": { - "id": "1", - "name": "Super category", - "project_id": "1" - } -} -``` - -### getAllCategories - -- Purpose: **Get all available categories** -- Parameters: - - **project_id** (integer, required) -- Result on success: **List of categories** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getAllCategories", - "id": 1261777968, - "params": { - "project_id": 1 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1261777968, - "result": [ - { - "id": "1", - "name": "Super category", - "project_id": "1" - } - ] -} -``` - -### updateCategory - -- Purpose: **Update a category** -- Parameters: - - **id** (integer, required) - - **name** (string, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "updateCategory", - "id": 570195391, - "params": { - "id": 1, - "name": "Renamed category" - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 570195391, - "result": true -} -``` - -### removeCategory - -- Purpose: **Remove a category** -- Parameters: - - **category_id** (integer) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "removeCategory", - "id": 88225706, - "params": { - "category_id": 1 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 88225706, - "result": true -} -``` - - -### createComment - -- Purpose: **Create a new comment** -- Parameters: - - **task_id** (integer, required) - - **user_id** (integer, required) - - **content** Markdown content (string, required) -- Result on success: **comment_id** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "createComment", - "id": 1580417921, - "params": { - "task_id": 1, - "user_id": 1, - "content": "Comment #1" - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1580417921, - "result": 11 -} -``` - -### getComment - -- Purpose: **Get comment information** -- Parameters: - - **comment_id** (integer, required) -- Result on success: **comment properties** -- Result on failure: **null** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getComment", - "id": 867839500, - "params": { - "comment_id": 1 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 867839500, - "result": { - "id": "1", - "task_id": "1", - "user_id": "1", - "date_creation": "1410881970", - "comment": "Comment #1", - "username": "admin", - "name": null - } -} -``` - -### getAllComments - -- Purpose: **Get all available comments** -- Parameters: - - **task_id** (integer, required) -- Result on success: **List of comments** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getAllComments", - "id": 148484683, - "params": { - "task_id": 1 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 148484683, - "result": [ - { - "id": "1", - "date_creation": "1410882272", - "task_id": "1", - "user_id": "1", - "comment": "Comment #1", - "username": "admin", - "name": null - }, - ... - ] -} -``` - -### updateComment - -- Purpose: **Update a comment** -- Parameters: - - **id** (integer, required) - - **content** Markdown content (string, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "updateComment", - "id": 496470023, - "params": { - "id": 1, - "content": "Comment #1 updated" - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1493368950, - "result": true -} -``` - -### removeComment - -- Purpose: **Remove a comment** -- Parameters: - - **comment_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "removeComment", - "id": 328836871, - "params": { - "comment_id": 1 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 328836871, - "result": true -} -``` - -### createSubtask - -- Purpose: **Create a new subtask** -- Parameters: - - **task_id** (integer, required) - - **title** (integer, required) - - **user_id** (int, optional) - - **time_estimated** (int, optional) - - **time_spent** (int, optional) - - **status** (int, optional) -- Result on success: **subtask_id** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "createSubtask", - "id": 2041554661, - "params": { - "task_id": 1, - "title": "Subtask #1" - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 2041554661, - "result": 45 -} -``` - -### getSubtask - -- Purpose: **Get subtask information** -- Parameters: - - **subtask_id** (integer) -- Result on success: **subtask properties** -- Result on failure: **null** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getSubtask", - "id": 133184525, - "params": { - "subtask_id": 1 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 133184525, - "result": { - "id": "1", - "title": "Subtask #1", - "status": "0", - "time_estimated": "0", - "time_spent": "0", - "task_id": "1", - "user_id": "0" - } -} -``` - -### getAllSubtasks - -- Purpose: **Get all available subtasks** -- Parameters: - - **task_id** (integer, required) -- Result on success: **List of subtasks** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getAllSubtasks", - "id": 2087700490, - "params": { - "task_id": 1 - } -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 2087700490, - "result": [ - { - "id": "1", - "title": "Subtask #1", - "status": "0", - "time_estimated": "0", - "time_spent": "0", - "task_id": "1", - "user_id": "0", - "username": null, - "name": null, - "status_name": "Todo" - }, - ... - ] -} -``` - -### updateSubtask - -- Purpose: **Update a subtask** -- Parameters: - - **id** (integer, required) - - **task_id** (integer, required) - - **title** (integer, optional) - - **user_id** (integer, optional) - - **time_estimated** (integer, optional) - - **time_spent** (integer, optional) - - **status** (integer, optional) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "updateSubtask", - "id": 191749979, - "params": { - "id": 1, - "task_id": 1, - "status": 1, - "time_spent": 5, - "user_id": 1 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 191749979, - "result": true -} -``` - -### removeSubtask - -- Purpose: **Remove a subtask** -- Parameters: - - **subtask_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "removeSubtask", - "id": 1382487306, - "params": { - "subtask_id": 1 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1382487306, - "result": true -} -``` - -### getAllLinks - -- Purpose: **Get the list of possible relations between tasks** -- Parameters: none -- Result on success: **List of links** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getAllLinks", - "id": 113057196 -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 113057196, - "result": [ - { - "id": "1", - "label": "relates to", - "opposite_id": "0" - }, - { - "id": "2", - "label": "blocks", - "opposite_id": "3" - }, - { - "id": "3", - "label": "is blocked by", - "opposite_id": "2" - }, - { - "id": "4", - "label": "duplicates", - "opposite_id": "5" - }, - { - "id": "5", - "label": "is duplicated by", - "opposite_id": "4" - }, - { - "id": "6", - "label": "is a child of", - "opposite_id": "7" - }, - { - "id": "7", - "label": "is a parent of", - "opposite_id": "6" - }, - { - "id": "8", - "label": "targets milestone", - "opposite_id": "9" - }, - { - "id": "9", - "label": "is a milestone of", - "opposite_id": "8" - }, - { - "id": "10", - "label": "fixes", - "opposite_id": "11" - }, - { - "id": "11", - "label": "is fixed by", - "opposite_id": "10" - } - ] -} -``` - -### getOppositeLinkId - -- Purpose: **Get the opposite link id of a task link** -- Parameters: - - **link_id** (integer, required) -- Result on success: **link_id** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getOppositeLinkId", - "id": 407062448, - "params": [ - 2 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 407062448, - "result": "3" -} -``` - -### getLinkByLabel - -- Purpose: **Get a link by label** -- Parameters: - - **label** (integer, required) -- Result on success: **link properties** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getLinkByLabel", - "id": 1796123316, - "params": [ - "blocks" - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1796123316, - "result": { - "id": "2", - "label": "blocks", - "opposite_id": "3" - } -} -``` - -### getLinkById - -- Purpose: **Get a link by id** -- Parameters: - - **link_id** (integer, required) -- Result on success: **link properties** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getLinkById", - "id": 1190238402, - "params": [ - 4 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1190238402, - "result": { - "id": "4", - "label": "duplicates", - "opposite_id": "5" - } -} -``` - -### createLink - -- Purpose: **Create a new task relation** -- Parameters: - - **label** (integer, required) - - **opposite_label** (integer, optional) -- Result on success: **link_id** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "createLink", - "id": 1040237496, - "params": [ - "foo", - "bar" - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1040237496, - "result": 13 -} -``` - -### updateLink - -- Purpose: **Update a link** -- Parameters: - - **link_id** (integer, required) - - **opposite_link_id** (integer, required) - - **label** (string, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "updateLink", - "id": 2110446926, - "params": [ - "14", - "12", - "boo" - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 2110446926, - "result": true -} -``` - -### removeLink - -- Purpose: **Remove a link** -- Parameters: - - **link_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "removeLink", - "id": 2136522739, - "params": [ - "14" - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 2136522739, - "result": true -} -``` - -### createTaskLink - -- Purpose: **Create a link between two tasks** -- Parameters: - - **task_id** (integer, required) - - **opposite_task_id** (integer, required) - - **link_id** (integer, required) -- Result on success: **task_link_id** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "createTaskLink", - "id": 509742912, - "params": [ - 2, - 3, - 1 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 509742912, - "result": 1 -} -``` - -### updateTaskLink - -- Purpose: **Update task link** -- Parameters: - - **task_link_id** (integer, required) - - **task_id** (integer, required) - - **opposite_task_id** (integer, required) - - **link_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "updateTaskLink", - "id": 669037109, - "params": [ - 1, - 2, - 4, - 2 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 669037109, - "result": true -} -``` - -### getTaskLinkById - -- Purpose: **Get a task link** -- Parameters: - - **task_link_id** (integer, required) -- Result on success: **task link properties** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getTaskLinkById", - "id": 809885202, - "params": [ - 1 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 809885202, - "result": { - "id": "1", - "link_id": "1", - "task_id": "2", - "opposite_task_id": "3" - } -} -``` - -### getAllTaskLinks - -- Purpose: **Get all links related to a task** -- Parameters: - - **task_id** (integer, required) -- Result on success: **list of task link** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getAllTaskLinks", - "id": 810848359, - "params": [ - 2 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 810848359, - "result": [ - { - "id": "1", - "task_id": "3", - "label": "relates to", - "title": "B", - "is_active": "1", - "project_id": "1", - "task_time_spent": "0", - "task_time_estimated": "0", - "task_assignee_id": "0", - "task_assignee_username": null, - "task_assignee_name": null, - "column_title": "Backlog" - } - ] -} -``` - -### removeTaskLink - -- Purpose: **Remove a link between two tasks** -- Parameters: - - **task_link_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "removeTaskLink", - "id": 473028226, - "params": [ - 1 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 473028226, - "result": true -} -``` - -### createFile - -- Purpose: **Create and upload a new task attachment** -- Parameters: - - **project_id** (integer, required) - - **task_id** (integer, required) - - **filename** (integer, required) - - **blob** File content encoded in base64 (string, required) -- Result on success: **file_id** -- Result on failure: **false** -- Note: **The maximum file size depends of your PHP configuration, this method should not be used to upload large files** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "createFile", - "id": 94500810, - "params": [ - 1, - 1, - "My file", - "cGxhaW4gdGV4dCBmaWxl" - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 94500810, - "result": 1 -} -``` - -### getAllFiles - -- Purpose: **Get all files attached to task** -- Parameters: - - **task_id** (integer, required) -- Result on success: **list of files** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getAllFiles", - "id": 1880662820, - "params": { - "task_id": 1 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1880662820, - "result": [ - { - "id": "1", - "name": "My file", - "path": "1\/1\/0db4d0a897a4c852f6e12f0239d4805f7b4ab596", - "is_image": "0", - "task_id": "1", - "date": "1432509941", - "user_id": "0", - "size": "15", - "username": null, - "user_name": null - } - ] -} -``` - -### getFile - -- Purpose: **Get file information** -- Parameters: - - **file_id** (integer, required) -- Result on success: **file properties** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getFile", - "id": 318676852, - "params": [ - "1" - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 318676852, - "result": { - "id": "1", - "name": "My file", - "path": "1\/1\/0db4d0a897a4c852f6e12f0239d4805f7b4ab596", - "is_image": "0", - "task_id": "1", - "date": "1432509941", - "user_id": "0", - "size": "15" - } -} -``` - -### downloadFile - -- Purpose: **Download file contents (encoded in base64)** -- Parameters: - - **file_id** (integer, required) -- Result on success: **base64 encoded string** -- Result on failure: **empty string** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "downloadFile", - "id": 235943344, - "params": [ - "1" - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 235943344, - "result": "cGxhaW4gdGV4dCBmaWxl" -} -``` - -### removeFile - -- Purpose: **Remove file** -- Parameters: - - **file_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "removeFile", - "id": 447036524, - "params": [ - "1" - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 447036524, - "result": true -} -``` - -### removeAllFiles - -- Purpose: **Remove all files associated to a task** -- Parameters: - - **task_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "removeAllFiles", - "id": 593312993, - "params": { - "task_id": 1 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 593312993, - "result": true -} -``` - -### getMe - -- Purpose: **Get logged user session** -- Parameters: None -- Result on success: **user session data** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getMe", - "id": 1718627783 -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1718627783, - "result": { - "id": 2, - "username": "user", - "is_admin": false, - "is_ldap_user": false, - "name": "", - "email": "", - "google_id": null, - "github_id": null, - "notifications_enabled": "0", - "timezone": null, - "language": null, - "disable_login_form": "0", - "twofactor_activated": false, - "twofactor_secret": null, - "token": "", - "notifications_filter": "4" - } -} -``` - -### getMyDashboard - -- Purpose: **Get the dashboard of the logged user without pagination** -- Parameters: None -- Result on success: **Dashboard information** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getMyDashboard", - "id": 447898718 -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1563664593, - "result": { - "projects": [ - { - "id": "2", - "name": "my project", - "is_active": "1", - "token": "", - "last_modified": "1438205337", - "is_public": "0", - "is_private": "1", - "is_everybody_allowed": "0", - "default_swimlane": "Default swimlane", - "show_default_swimlane": "1", - "description": null, - "identifier": "", - "columns": [ - { - "id": "5", - "title": "Backlog", - "position": "1", - "project_id": "2", - "task_limit": "0", - "description": "", - "nb_tasks": 0 - }, - { - "id": "6", - "title": "Ready", - "position": "2", - "project_id": "2", - "task_limit": "0", - "description": "", - "nb_tasks": 0 - }, - { - "id": "7", - "title": "Work in progress", - "position": "3", - "project_id": "2", - "task_limit": "0", - "description": "", - "nb_tasks": 0 - }, - { - "id": "8", - "title": "Done", - "position": "4", - "project_id": "2", - "task_limit": "0", - "description": "", - "nb_tasks": 0 - } - ], - "url": { - "board": "http:\/\/127.0.0.1:8000\/?controller=board&action=show&project_id=2", - "calendar": "http:\/\/127.0.0.1:8000\/?controller=calendar&action=show&project_id=2", - "list": "http:\/\/127.0.0.1:8000\/?controller=listing&action=show&project_id=2" - } - } - ], - "tasks": [ - { - "id": "1", - "title": "new title", - "date_due": "0", - "date_creation": "1438205336", - "project_id": "2", - "color_id": "yellow", - "time_spent": "0", - "time_estimated": "0", - "project_name": "my project", - "url": "http:\/\/127.0.0.1:8000\/?controller=task&action=show&task_id=1&project_id=2" - } - ], - "subtasks": [] - } -} -``` - -### getMyActivityStream - -- Purpose: **Get the last 100 events for the logged user** -- Parameters: None -- Result on success: **List of events** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getMyActivityStream", - "id": 1132562181 -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1132562181, - "result": [ - { - "id": "1", - "date_creation": "1438205054", - "event_name": "task.create", - "creator_id": "2", - "project_id": "2", - "task_id": "1", - "author_username": "user", - "author_name": "", - "email": "", - "task": { - "id": "1", - "reference": "", - "title": "my user title", - "description": "", - "date_creation": "1438205054", - "date_completed": null, - "date_modification": "1438205054", - "date_due": "0", - "date_started": null, - "time_estimated": "0", - "time_spent": "0", - "color_id": "yellow", - "project_id": "2", - "column_id": "5", - "owner_id": "0", - "creator_id": "2", - "position": "1", - "is_active": "1", - "score": "0", - "category_id": "0", - "swimlane_id": "0", - "date_moved": "1438205054", - "recurrence_status": "0", - "recurrence_trigger": "0", - "recurrence_factor": "0", - "recurrence_timeframe": "0", - "recurrence_basedate": "0", - "recurrence_parent": null, - "recurrence_child": null, - "category_name": null, - "swimlane_name": null, - "project_name": "my project", - "default_swimlane": "Default swimlane", - "column_title": "Backlog", - "assignee_username": null, - "assignee_name": null, - "creator_username": "user", - "creator_name": "" - }, - "changes": [], - "author": "user", - "event_title": "user created the task #1", - "event_content": "\n

\n user created the task #1<\/a><\/p>\n

\n my user title<\/em>\n<\/p>" - } - ] -} -``` - -### createMyPrivateProject - -- Purpose: **Create a private project for the logged user** -- Parameters: - - **name** (string, required) - - **description** (string, optional) -- Result on success: **project_id** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "createMyPrivateProject", - "id": 1271580569, - "params": [ - "my project" - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1271580569, - "result": 2 -} -``` - -### getMyProjectsList - -- Purpose: **Get projects of the connected user** -- Parameters: None -- Result on success: **dictionary of project_id => project_name** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getMyProjectsList", - "id": 987834805 -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 987834805, - "result": { - "2": "my project" - } -} -``` -### getMyOverdueTasks - -- Purpose: **Get my overdue tasks** -- Result on success: **List of tasks** -- Result on failure: **false** - -Request example to fetch all tasks on the board: - -```json -{ - "jsonrpc": "2.0", - "method": "getMyOverdueTasks", - "id": 133280317 -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 133280317, - "result": [ - { - "id": "1", - "title": "Task #1", - "date_due": "1409961789", - "project_id": "1", - "project_name": "Test", - "assignee_username":"admin", - "assignee_name": null - }, - { - "id": "2", - "title": "Test", - "date_due": "1409962115", - "project_id": "1", - "project_name": "Test", - "assignee_username":"admin", - "assignee_name": null - }, - ... - ] -} -``` - -### getMyProjects - -- Purpose: **Get projects of connected user with full details** -- Parameters: - - **none** -- Result on success: **List of projects with details** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getmyProjects", - "id": 2134420212 -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 2134420212, - "result": [ - { - "id": "1", - "name": "API test", - "is_active": "1", - "token": "", - "last_modified": "1436119570", - "is_public": "0", - "is_private": "0", - "is_everybody_allowed": "0", - "default_swimlane": "Default swimlane", - "show_default_swimlane": "1", - "description": null, - "identifier": "", - "url": { - "board": "http:\/\/127.0.0.1:8000\/?controller=board&action=show&project_id=1", - "calendar": "http:\/\/127.0.0.1:8000\/?controller=calendar&action=show&project_id=1", - "list": "http:\/\/127.0.0.1:8000\/?controller=listing&action=show&project_id=1" - } - } - ] -} -``` +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) -- cgit v1.2.3 From ad8fcf035ab92d8cd06179959000b9a1681b1505 Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Fri, 22 Jan 2016 21:23:12 -0500 Subject: Add new API procedures for groups, roles and project permissions --- ChangeLog | 1 + app/Api/App.php | 10 + app/Api/Auth.php | 2 +- app/Api/File.php | 10 +- app/Api/Group.php | 49 ++ app/Api/GroupMember.php | 32 + app/Api/ProjectPermission.php | 51 +- app/Core/Security/Role.php | 2 +- doc/api-application-procedures.markdown | 60 ++ doc/api-group-member-procedures.markdown | 152 ++++ doc/api-group-procedures.markdown | 174 ++++ doc/api-json-rpc.markdown | 29 +- doc/api-project-permission-procedures.markdown | 274 ++++++ doc/api-project-procedures.markdown | 101 --- doc/tests.markdown | 128 +-- jsonrpc.php | 4 + tests/functionals.mysql.xml | 17 - tests/functionals.postgres.xml | 17 - tests/functionals.sqlite.xml | 13 - tests/functionals/ApiTest.php | 1122 ------------------------ tests/functionals/UserApiTest.php | 289 ------ tests/integration.mysql.xml | 17 + tests/integration.postgres.xml | 17 + tests/integration.sqlite.xml | 13 + tests/integration/ApiTest.php | 1112 +++++++++++++++++++++++ tests/integration/AppTest.php | 34 + tests/integration/Base.php | 62 ++ tests/integration/GroupMemberTest.php | 39 + tests/integration/GroupTest.php | 48 + tests/integration/MeTest.php | 247 ++++++ tests/integration/ProjectPermissionTest.php | 64 ++ 31 files changed, 2523 insertions(+), 1667 deletions(-) create mode 100644 app/Api/Group.php create mode 100644 app/Api/GroupMember.php create mode 100644 doc/api-group-member-procedures.markdown create mode 100644 doc/api-group-procedures.markdown create mode 100644 doc/api-project-permission-procedures.markdown delete mode 100644 tests/functionals.mysql.xml delete mode 100644 tests/functionals.postgres.xml delete mode 100644 tests/functionals.sqlite.xml delete mode 100644 tests/functionals/ApiTest.php delete mode 100644 tests/functionals/UserApiTest.php create mode 100644 tests/integration.mysql.xml create mode 100644 tests/integration.postgres.xml create mode 100644 tests/integration.sqlite.xml create mode 100644 tests/integration/ApiTest.php create mode 100644 tests/integration/AppTest.php create mode 100644 tests/integration/Base.php create mode 100644 tests/integration/GroupMemberTest.php create mode 100644 tests/integration/GroupTest.php create mode 100644 tests/integration/MeTest.php create mode 100644 tests/integration/ProjectPermissionTest.php (limited to 'doc/api-json-rpc.markdown') 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 @@ +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 @@ +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/functionals.mysql.xml deleted file mode 100644 index a8877f92..00000000 --- a/tests/functionals.mysql.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - functionals - - - - - - - - - - - - - \ No newline at end of file diff --git a/tests/functionals.postgres.xml b/tests/functionals.postgres.xml deleted file mode 100644 index 61da8574..00000000 --- a/tests/functionals.postgres.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - functionals - - - - - - - - - - - - - \ No newline at end of file diff --git a/tests/functionals.sqlite.xml b/tests/functionals.sqlite.xml deleted file mode 100644 index 9da59b20..00000000 --- a/tests/functionals.sqlite.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - functionals - - - - - - - - - \ No newline at end of file diff --git a/tests/functionals/ApiTest.php b/tests/functionals/ApiTest.php deleted file mode 100644 index b250254e..00000000 --- a/tests/functionals/ApiTest.php +++ /dev/null @@ -1,1122 +0,0 @@ -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->table('settings')->eq('option', 'application_timezone')->update(array('value' => 'Europe/Paris')); - $db->closeConnection(); - } - - public function setUp() - { - $this->client = new JsonRPC\Client(API_URL); - $this->client->authentication('jsonrpc', API_KEY); - // $this->client->debug = true; - } - - private function getTaskId() - { - $tasks = $this->client->getAllTasks(1, 1); - $this->assertNotEmpty($tasks); - - 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(); - - if ($projects) { - foreach ($projects as $project) { - $this->assertEquals('http://127.0.0.1:8000/?controller=board&action=show&project_id='.$project['id'], $project['url']['board']); - $this->assertEquals('http://127.0.0.1:8000/?controller=calendar&action=show&project_id='.$project['id'], $project['url']['calendar']); - $this->assertEquals('http://127.0.0.1:8000/?controller=listing&action=show&project_id='.$project['id'], $project['url']['list']); - $this->assertTrue($this->client->removeProject($project['id'])); - } - } - } - - public function testCreateProject() - { - $project_id = $this->client->createProject('API test'); - $this->assertNotFalse($project_id); - $this->assertInternalType('int', $project_id); - } - - public function testGetProjectById() - { - $project = $this->client->getProjectById(1); - $this->assertNotEmpty($project); - $this->assertEquals(1, $project['id']); - $this->assertEquals('http://127.0.0.1:8000/?controller=board&action=show&project_id='.$project['id'], $project['url']['board']); - $this->assertEquals('http://127.0.0.1:8000/?controller=calendar&action=show&project_id='.$project['id'], $project['url']['calendar']); - $this->assertEquals('http://127.0.0.1:8000/?controller=listing&action=show&project_id='.$project['id'], $project['url']['list']); - } - - public function testGetProjectByName() - { - $project = $this->client->getProjectByName('API test'); - $this->assertNotEmpty($project); - $this->assertEquals(1, $project['id']); - $this->assertEquals('http://127.0.0.1:8000/?controller=board&action=show&project_id='.$project['id'], $project['url']['board']); - $this->assertEquals('http://127.0.0.1:8000/?controller=calendar&action=show&project_id='.$project['id'], $project['url']['calendar']); - $this->assertEquals('http://127.0.0.1:8000/?controller=listing&action=show&project_id='.$project['id'], $project['url']['list']); - - $project = $this->client->getProjectByName(array('name' => 'API test')); - $this->assertNotEmpty($project); - $this->assertEquals(1, $project['id']); - - $project = $this->client->getProjectByName('None'); - $this->assertEmpty($project); - $this->assertNull($project); - } - - public function testGetAllProjects() - { - $projects = $this->client->getAllProjects(); - $this->assertNotEmpty($projects); - - foreach ($projects as $project) { - $this->assertEquals('http://127.0.0.1:8000/?controller=board&action=show&project_id='.$project['id'], $project['url']['board']); - $this->assertEquals('http://127.0.0.1:8000/?controller=calendar&action=show&project_id='.$project['id'], $project['url']['calendar']); - $this->assertEquals('http://127.0.0.1:8000/?controller=listing&action=show&project_id='.$project['id'], $project['url']['list']); - } - } - - public function testUpdateProject() - { - $project = $this->client->getProjectById(1); - $this->assertNotEmpty($project); - $this->assertTrue($this->client->execute('updateProject', array('id' => 1, 'name' => 'API test 2'))); - - $project = $this->client->getProjectById(1); - $this->assertEquals('API test 2', $project['name']); - - $this->assertTrue($this->client->execute('updateProject', array('id' => 1, 'name' => 'API test', 'description' => 'test'))); - - $project = $this->client->getProjectById(1); - $this->assertEquals('API test', $project['name']); - $this->assertEquals('test', $project['description']); - } - - public function testDisableProject() - { - $this->assertTrue($this->client->disableProject(1)); - $project = $this->client->getProjectById(1); - $this->assertNotEmpty($project); - $this->assertEquals(0, $project['is_active']); - } - - public function testEnableProject() - { - $this->assertTrue($this->client->enableProject(1)); - $project = $this->client->getProjectById(1); - $this->assertNotEmpty($project); - $this->assertEquals(1, $project['is_active']); - } - - public function testEnableProjectPublicAccess() - { - $this->assertTrue($this->client->enableProjectPublicAccess(1)); - $project = $this->client->getProjectById(1); - $this->assertNotEmpty($project); - $this->assertEquals(1, $project['is_public']); - $this->assertNotEmpty($project['token']); - } - - public function testDisableProjectPublicAccess() - { - $this->assertTrue($this->client->disableProjectPublicAccess(1)); - $project = $this->client->getProjectById(1); - $this->assertNotEmpty($project); - $this->assertEquals(0, $project['is_public']); - $this->assertEmpty($project['token']); - } - - public function testgetProjectActivities() - { - $activities = $this->client->getProjectActivities(array('project_ids' => array(1))); - $this->assertInternalType('array', $activities); - $this->assertCount(0, $activities); - } - - public function testgetProjectActivity() - { - $activities = $this->client->getProjectActivity(1); - $this->assertInternalType('array', $activities); - $this->assertCount(0, $activities); - } - - public function testGetBoard() - { - $board = $this->client->getBoard(1); - $this->assertTrue(is_array($board)); - $this->assertEquals(1, count($board)); - $this->assertEquals('Default swimlane', $board[0]['name']); - $this->assertEquals(4, count($board[0]['columns'])); - } - - public function testGetColumns() - { - $columns = $this->client->getColumns(1); - $this->assertTrue(is_array($columns)); - $this->assertEquals(4, count($columns)); - $this->assertEquals('Done', $columns[3]['title']); - } - - public function testMoveColumnUp() - { - $this->assertTrue($this->client->moveColumnUp(1, 4)); - - $columns = $this->client->getColumns(1); - $this->assertTrue(is_array($columns)); - $this->assertEquals('Done', $columns[2]['title']); - $this->assertEquals('Work in progress', $columns[3]['title']); - } - - public function testMoveColumnDown() - { - $this->assertTrue($this->client->moveColumnDown(1, 4)); - - $columns = $this->client->getColumns(1); - $this->assertTrue(is_array($columns)); - $this->assertEquals('Work in progress', $columns[2]['title']); - $this->assertEquals('Done', $columns[3]['title']); - } - - public function testUpdateColumn() - { - $this->assertTrue($this->client->updateColumn(4, 'Boo', 2)); - - $columns = $this->client->getColumns(1); - $this->assertTrue(is_array($columns)); - $this->assertEquals('Boo', $columns[3]['title']); - $this->assertEquals(2, $columns[3]['task_limit']); - } - - public function testAddColumn() - { - $column_id = $this->client->addColumn(1, 'New column'); - - $this->assertNotFalse($column_id); - $this->assertInternalType('int', $column_id); - $this->assertTrue($column_id > 0); - - $columns = $this->client->getColumns(1); - $this->assertTrue(is_array($columns)); - $this->assertEquals(5, count($columns)); - $this->assertEquals('New column', $columns[4]['title']); - } - - public function testRemoveColumn() - { - $this->assertTrue($this->client->removeColumn(5)); - - $columns = $this->client->getColumns(1); - $this->assertTrue(is_array($columns)); - $this->assertEquals(4, count($columns)); - } - - public function testGetDefaultSwimlane() - { - $swimlane = $this->client->getDefaultSwimlane(1); - $this->assertNotEmpty($swimlane); - $this->assertEquals('Default swimlane', $swimlane['default_swimlane']); - } - - public function testAddSwimlane() - { - $swimlane_id = $this->client->addSwimlane(1, 'Swimlane 1'); - $this->assertNotFalse($swimlane_id); - $this->assertInternalType('int', $swimlane_id); - - $swimlane = $this->client->getSwimlaneById($swimlane_id); - $this->assertNotEmpty($swimlane); - $this->assertInternalType('array', $swimlane); - $this->assertEquals('Swimlane 1', $swimlane['name']); - } - - public function testGetSwimlane() - { - $swimlane = $this->client->getSwimlane(1); - $this->assertNotEmpty($swimlane); - $this->assertInternalType('array', $swimlane); - $this->assertEquals('Swimlane 1', $swimlane['name']); - } - - public function testUpdateSwimlane() - { - $swimlane = $this->client->getSwimlaneByName(1, 'Swimlane 1'); - $this->assertNotEmpty($swimlane); - $this->assertInternalType('array', $swimlane); - $this->assertEquals(1, $swimlane['id']); - $this->assertEquals('Swimlane 1', $swimlane['name']); - - $this->assertTrue($this->client->updateSwimlane($swimlane['id'], 'Another swimlane')); - - $swimlane = $this->client->getSwimlaneById($swimlane['id']); - $this->assertNotEmpty($swimlane); - $this->assertEquals('Another swimlane', $swimlane['name']); - } - - public function testDisableSwimlane() - { - $this->assertTrue($this->client->disableSwimlane(1, 1)); - - $swimlane = $this->client->getSwimlaneById(1); - $this->assertNotEmpty($swimlane); - $this->assertEquals(0, $swimlane['is_active']); - } - - public function testEnableSwimlane() - { - $this->assertTrue($this->client->enableSwimlane(1, 1)); - - $swimlane = $this->client->getSwimlaneById(1); - $this->assertNotEmpty($swimlane); - $this->assertEquals(1, $swimlane['is_active']); - } - - public function testGetAllSwimlanes() - { - $this->assertNotFalse($this->client->addSwimlane(1, 'Swimlane A')); - - $swimlanes = $this->client->getAllSwimlanes(1); - $this->assertNotEmpty($swimlanes); - $this->assertCount(2, $swimlanes); - $this->assertEquals('Another swimlane', $swimlanes[0]['name']); - $this->assertEquals('Swimlane A', $swimlanes[1]['name']); - } - - public function testGetActiveSwimlane() - { - $this->assertTrue($this->client->disableSwimlane(1, 1)); - - $swimlanes = $this->client->getActiveSwimlanes(1); - $this->assertNotEmpty($swimlanes); - $this->assertCount(2, $swimlanes); - $this->assertEquals('Default swimlane', $swimlanes[0]['name']); - $this->assertEquals('Swimlane A', $swimlanes[1]['name']); - } - - public function testMoveSwimlaneUp() - { - $this->assertTrue($this->client->enableSwimlane(1, 1)); - $this->assertTrue($this->client->moveSwimlaneUp(1, 1)); - - $swimlanes = $this->client->getActiveSwimlanes(1); - $this->assertNotEmpty($swimlanes); - $this->assertCount(3, $swimlanes); - $this->assertEquals('Default swimlane', $swimlanes[0]['name']); - $this->assertEquals('Another swimlane', $swimlanes[1]['name']); - $this->assertEquals('Swimlane A', $swimlanes[2]['name']); - - $this->assertTrue($this->client->moveSwimlaneUp(1, 2)); - - $swimlanes = $this->client->getActiveSwimlanes(1); - $this->assertNotEmpty($swimlanes); - $this->assertCount(3, $swimlanes); - $this->assertEquals('Default swimlane', $swimlanes[0]['name']); - $this->assertEquals('Swimlane A', $swimlanes[1]['name']); - $this->assertEquals('Another swimlane', $swimlanes[2]['name']); - } - - public function testMoveSwimlaneDown() - { - $this->assertTrue($this->client->moveSwimlaneDown(1, 2)); - - $swimlanes = $this->client->getActiveSwimlanes(1); - $this->assertNotEmpty($swimlanes); - $this->assertCount(3, $swimlanes); - $this->assertEquals('Default swimlane', $swimlanes[0]['name']); - $this->assertEquals('Another swimlane', $swimlanes[1]['name']); - $this->assertEquals('Swimlane A', $swimlanes[2]['name']); - } - - public function testCreateTaskWithWrongMember() - { - $task = array( - 'title' => 'Task #1', - 'color_id' => 'blue', - 'owner_id' => 1, - 'project_id' => 1, - 'column_id' => 2, - ); - - $task_id = $this->client->createTask($task); - - $this->assertFalse($task_id); - } - - public function testGetAllowedUsers() - { - $users = $this->client->getMembers(1); - $this->assertNotFalse($users); - $this->assertEquals(array(), $users); - } - - public function testAddMember() - { - $this->assertTrue($this->client->allowUser(1, 1)); - } - - public function testCreateTask() - { - $task = array( - 'title' => 'Task #1', - 'color_id' => 'blue', - 'owner_id' => 1, - 'project_id' => 1, - 'column_id' => 2, - ); - - $task_id = $this->client->createTask($task); - - $this->assertNotFalse($task_id); - $this->assertInternalType('int', $task_id); - $this->assertTrue($task_id > 0); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testCreateTaskWithBadParams() - { - $task = array( - 'title' => 'Task #1', - 'color_id' => 'blue', - 'owner_id' => 1, - ); - - $this->client->createTask($task); - } - - public function testGetTask() - { - $task = $this->client->getTask(1); - - $this->assertNotFalse($task); - $this->assertTrue(is_array($task)); - $this->assertEquals('Task #1', $task['title']); - $this->assertEquals('http://127.0.0.1:8000/?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'], $task['url']); - } - - public function testGetAllTasks() - { - $tasks = $this->client->getAllTasks(1, 1); - - $this->assertNotFalse($tasks); - $this->assertTrue(is_array($tasks)); - $this->assertEquals('Task #1', $tasks[0]['title']); - $this->assertEquals('http://127.0.0.1:8000/?controller=task&action=show&task_id='.$tasks[0]['id'].'&project_id='.$tasks[0]['project_id'], $tasks[0]['url']); - - $tasks = $this->client->getAllTasks(2, 0); - - $this->assertNotFalse($tasks); - $this->assertTrue(is_array($tasks)); - $this->assertEmpty($tasks); - } - - public function testMoveTaskSwimlane() - { - $task_id = $this->getTaskId(); - - $task = $this->client->getTask($task_id); - $this->assertNotFalse($task); - $this->assertTrue(is_array($task)); - $this->assertEquals(1, $task['position']); - $this->assertEquals(2, $task['column_id']); - $this->assertEquals(0, $task['swimlane_id']); - - $moved_timestamp = $task['date_moved']; - sleep(1); - $this->assertTrue($this->client->moveTaskPosition(1, $task_id, 4, 1, 2)); - - $task = $this->client->getTask($task_id); - $this->assertNotFalse($task); - $this->assertTrue(is_array($task)); - $this->assertEquals(1, $task['position']); - $this->assertEquals(4, $task['column_id']); - $this->assertEquals(2, $task['swimlane_id']); - $this->assertNotEquals($moved_timestamp, $task['date_moved']); - } - - public function testRemoveSwimlane() - { - $this->assertTrue($this->client->removeSwimlane(1, 2)); - - $task = $this->client->getTask($this->getTaskId()); - $this->assertNotFalse($task); - $this->assertTrue(is_array($task)); - $this->assertEquals(1, $task['position']); - $this->assertEquals(4, $task['column_id']); - $this->assertEquals(0, $task['swimlane_id']); - } - - public function testUpdateTask() - { - $task = $this->client->getTask(1); - - $values = array(); - $values['id'] = $task['id']; - $values['color_id'] = 'green'; - $values['description'] = 'test'; - $values['date_due'] = ''; - - $this->assertTrue($this->client->execute('updateTask', $values)); - } - - public function testRemoveTask() - { - $this->assertTrue($this->client->removeTask(1)); - } - - public function testRemoveUsers() - { - $users = $this->client->getAllUsers(); - $this->assertNotFalse($users); - $this->assertNotEmpty($users); - - foreach ($users as $user) { - if ($user['id'] > 1) { - $this->assertTrue($this->client->removeUser($user['id'])); - } - } - } - - public function testCreateUser() - { - $user = array( - 'username' => 'toto', - 'name' => 'Toto', - 'password' => '123456', - ); - - $user_id = $this->client->execute('createUser', $user); - $this->assertNotFalse($user_id); - $this->assertInternalType('int', $user_id); - $this->assertTrue($user_id > 0); - } - - public function testCreateManagerUser() - { - $user = array( - 'username' => 'manager', - 'name' => 'Manager', - 'password' => '123456', - 'role' => 'app-manager' - ); - - $user_id = $this->client->execute('createUser', $user); - $this->assertNotFalse($user_id); - $this->assertInternalType('int', $user_id); - $this->assertTrue($user_id > 0); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testCreateUserWithBadParams() - { - $user = array( - 'name' => 'Titi', - 'password' => '123456', - ); - - $this->assertNull($this->client->execute('createUser', $user)); - } - - public function testGetUser() - { - $user = $this->client->getUser(2); - $this->assertNotFalse($user); - $this->assertTrue(is_array($user)); - $this->assertEquals('toto', $user['username']); - - $user = $this->client->getUser(3); - $this->assertNotEmpty($user); - $this->assertEquals('app-manager', $user['role']); - - $this->assertNull($this->client->getUser(2222)); - } - - public function testUpdateUser() - { - $user = array(); - $user['id'] = 2; - $user['username'] = 'titi'; - $user['name'] = 'Titi'; - - $this->assertTrue($this->client->execute('updateUser', $user)); - - $user = $this->client->getUser(2); - $this->assertNotFalse($user); - $this->assertTrue(is_array($user)); - $this->assertEquals('titi', $user['username']); - $this->assertEquals('Titi', $user['name']); - - $user = array(); - $user['id'] = 2; - $user['email'] = 'titi@localhost'; - - $this->assertTrue($this->client->execute('updateUser', $user)); - - $user = $this->client->getUser(2); - $this->assertNotFalse($user); - $this->assertTrue(is_array($user)); - $this->assertEquals('titi@localhost', $user['email']); - } - - public function testAllowedUser() - { - $this->assertTrue($this->client->allowUser(1, 2)); - - $users = $this->client->getMembers(1); - $this->assertNotFalse($users); - $this->assertEquals(array(1 => 'admin', 2 => 'Titi'), $users); - } - - public function testRevokeUser() - { - $this->assertTrue($this->client->revokeUser(1, 2)); - - $users = $this->client->getMembers(1); - $this->assertNotFalse($users); - $this->assertEquals(array(1 => 'admin'), $users); - } - - public function testCreateComment() - { - $task = array( - 'title' => 'Task with comment', - 'color_id' => 'red', - 'owner_id' => 1, - 'project_id' => 1, - 'column_id' => 1, - ); - - $this->assertNotFalse($this->client->execute('createTask', $task)); - - $tasks = $this->client->getAllTasks(1, 1); - $this->assertNotEmpty($tasks); - $this->assertEquals(1, count($tasks)); - - $comment = array( - 'task_id' => $tasks[0]['id'], - 'user_id' => 2, - 'content' => 'boo', - ); - - $comment_id = $this->client->execute('createComment', $comment); - - $this->assertNotFalse($comment_id); - $this->assertInternalType('int', $comment_id); - $this->assertTrue($comment_id > 0); - } - - public function testGetComment() - { - $comment = $this->client->getComment(1); - $this->assertNotFalse($comment); - $this->assertNotEmpty($comment); - $this->assertEquals(2, $comment['user_id']); - $this->assertEquals('boo', $comment['comment']); - } - - public function testUpdateComment() - { - $comment = array(); - $comment['id'] = 1; - $comment['content'] = 'test'; - - $this->assertTrue($this->client->execute('updateComment', $comment)); - - $comment = $this->client->getComment(1); - $this->assertEquals('test', $comment['comment']); - } - - public function testGetAllComments() - { - $task_id = $this->getTaskId(); - - $comment = array( - 'task_id' => $task_id, - 'user_id' => 1, - 'content' => 'blabla', - ); - - $comment_id = $this->client->createComment($comment); - - $this->assertNotFalse($comment_id); - $this->assertInternalType('int', $comment_id); - $this->assertTrue($comment_id > 0); - - $comments = $this->client->getAllComments($task_id); - $this->assertNotFalse($comments); - $this->assertNotEmpty($comments); - $this->assertTrue(is_array($comments)); - $this->assertEquals(2, count($comments)); - } - - public function testRemoveComment() - { - $task_id = $this->getTaskId(); - - $comments = $this->client->getAllComments($task_id); - $this->assertNotFalse($comments); - $this->assertNotEmpty($comments); - $this->assertTrue(is_array($comments)); - - foreach ($comments as $comment) { - $this->assertTrue($this->client->removeComment($comment['id'])); - } - - $comments = $this->client->getAllComments($task_id); - $this->assertNotFalse($comments); - $this->assertEmpty($comments); - $this->assertTrue(is_array($comments)); - } - - public function testCreateSubtask() - { - $subtask = array( - 'task_id' => $this->getTaskId(), - 'title' => 'subtask #1', - ); - - $subtask_id = $this->client->createSubtask($subtask); - - $this->assertNotFalse($subtask_id); - $this->assertInternalType('int', $subtask_id); - $this->assertTrue($subtask_id > 0); - } - - public function testGetSubtask() - { - $subtask = $this->client->getSubtask(1); - $this->assertNotFalse($subtask); - $this->assertNotEmpty($subtask); - $this->assertEquals($this->getTaskId(), $subtask['task_id']); - $this->assertEquals(0, $subtask['user_id']); - $this->assertEquals('subtask #1', $subtask['title']); - } - - public function testUpdateSubtask() - { - $subtask = array(); - $subtask['id'] = 1; - $subtask['task_id'] = $this->getTaskId(); - $subtask['title'] = 'test'; - - $this->assertTrue($this->client->execute('updateSubtask', $subtask)); - - $subtask = $this->client->getSubtask(1); - $this->assertEquals('test', $subtask['title']); - } - - public function testGetAllSubtasks() - { - $subtask = array( - 'task_id' => $this->getTaskId(), - 'user_id' => 2, - 'title' => 'Subtask #2', - ); - - $this->assertNotFalse($this->client->execute('createSubtask', $subtask)); - - $subtasks = $this->client->getAllSubtasks($this->getTaskId()); - $this->assertNotFalse($subtasks); - $this->assertNotEmpty($subtasks); - $this->assertTrue(is_array($subtasks)); - $this->assertEquals(2, count($subtasks)); - } - - public function testRemoveSubtask() - { - $this->assertTrue($this->client->removeSubtask(1)); - - $subtasks = $this->client->getAllSubtasks($this->getTaskId()); - $this->assertNotFalse($subtasks); - $this->assertNotEmpty($subtasks); - $this->assertTrue(is_array($subtasks)); - $this->assertEquals(1, count($subtasks)); - } - - public function testMoveTaskPosition() - { - $task_id = $this->getTaskId(); - $this->assertTrue($this->client->moveTaskPosition(1, $task_id, 3, 1)); - - $task = $this->client->getTask($task_id); - $this->assertNotFalse($task); - $this->assertTrue(is_array($task)); - $this->assertEquals(1, $task['position']); - $this->assertEquals(3, $task['column_id']); - } - - public function testCategoryCreation() - { - $category = array( - 'name' => 'Category', - 'project_id' => 1, - ); - - $cat_id = $this->client->execute('createCategory', $category); - $this->assertNotFalse($cat_id); - $this->assertInternalType('int', $cat_id); - $this->assertTrue($cat_id > 0); - - // Duplicate - - $category = array( - 'name' => 'Category', - 'project_id' => 1, - ); - - $this->assertFalse($this->client->execute('createCategory', $category)); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testCategoryCreationWithBadParams() - { - // Missing project id - $category = array( - 'name' => 'Category', - ); - - $this->assertNull($this->client->execute('createCategory', $category)); - } - - public function testCategoryRead() - { - $category = $this->client->getCategory(1); - - $this->assertTrue(is_array($category)); - $this->assertNotEmpty($category); - $this->assertEquals(1, $category['id']); - $this->assertEquals('Category', $category['name']); - $this->assertEquals(1, $category['project_id']); - } - - public function testGetAllCategories() - { - $categories = $this->client->getAllCategories(1); - - $this->assertNotEmpty($categories); - $this->assertNotFalse($categories); - $this->assertTrue(is_array($categories)); - $this->assertEquals(1, count($categories)); - $this->assertEquals(1, $categories[0]['id']); - $this->assertEquals('Category', $categories[0]['name']); - $this->assertEquals(1, $categories[0]['project_id']); - } - - public function testCategoryUpdate() - { - $category = array( - 'id' => 1, - 'name' => 'Renamed category', - ); - - $this->assertTrue($this->client->execute('updateCategory', $category)); - - $category = $this->client->getCategory(1); - $this->assertTrue(is_array($category)); - $this->assertNotEmpty($category); - $this->assertEquals(1, $category['id']); - $this->assertEquals('Renamed category', $category['name']); - $this->assertEquals(1, $category['project_id']); - } - - public function testCategoryRemove() - { - $this->assertTrue($this->client->removeCategory(1)); - $this->assertFalse($this->client->removeCategory(1)); - $this->assertFalse($this->client->removeCategory(1111)); - } - - public function testGetAvailableActions() - { - $actions = $this->client->getAvailableActions(); - $this->assertNotEmpty($actions); - $this->assertInternalType('array', $actions); - $this->assertArrayHasKey('\Kanboard\Action\TaskCloseColumn', $actions); - } - - public function testGetAvailableActionEvents() - { - $events = $this->client->getAvailableActionEvents(); - $this->assertNotEmpty($events); - $this->assertInternalType('array', $events); - $this->assertArrayHasKey('task.move.column', $events); - } - - public function testGetCompatibleActionEvents() - { - $events = $this->client->getCompatibleActionEvents('\Kanboard\Action\TaskCloseColumn'); - $this->assertNotEmpty($events); - $this->assertInternalType('array', $events); - $this->assertArrayHasKey('task.move.column', $events); - } - - public function testCreateAction() - { - $action_id = $this->client->createAction(1, 'task.move.column', '\Kanboard\Action\TaskCloseColumn', array('column_id' => 1)); - $this->assertNotFalse($action_id); - $this->assertEquals(1, $action_id); - } - - public function testGetActions() - { - $actions = $this->client->getActions(1); - $this->assertNotEmpty($actions); - $this->assertInternalType('array', $actions); - $this->assertCount(1, $actions); - $this->assertArrayHasKey('id', $actions[0]); - $this->assertArrayHasKey('project_id', $actions[0]); - $this->assertArrayHasKey('event_name', $actions[0]); - $this->assertArrayHasKey('action_name', $actions[0]); - $this->assertArrayHasKey('params', $actions[0]); - $this->assertArrayHasKey('column_id', $actions[0]['params']); - } - - public function testRemoveAction() - { - $this->assertTrue($this->client->removeAction(1)); - - $actions = $this->client->getActions(1); - $this->assertEmpty($actions); - $this->assertCount(0, $actions); - } - - public function testGetAllLinks() - { - $links = $this->client->getAllLinks(); - $this->assertNotEmpty($links); - $this->assertArrayHasKey('id', $links[0]); - $this->assertArrayHasKey('label', $links[0]); - $this->assertArrayHasKey('opposite_id', $links[0]); - } - - public function testGetOppositeLink() - { - $link = $this->client->getOppositeLinkId(1); - $this->assertEquals(1, $link); - - $link = $this->client->getOppositeLinkId(2); - $this->assertEquals(3, $link); - } - - public function testGetLinkByLabel() - { - $link = $this->client->getLinkByLabel('blocks'); - $this->assertNotEmpty($link); - $this->assertEquals(2, $link['id']); - $this->assertEquals(3, $link['opposite_id']); - } - - public function testGetLinkById() - { - $link = $this->client->getLinkById(4); - $this->assertNotEmpty($link); - $this->assertEquals(4, $link['id']); - $this->assertEquals(5, $link['opposite_id']); - $this->assertEquals('duplicates', $link['label']); - } - - public function testCreateLink() - { - $link_id = $this->client->createLink(array('label' => 'test')); - $this->assertNotFalse($link_id); - $this->assertInternalType('int', $link_id); - - $link_id = $this->client->createLink(array('label' => 'foo', 'opposite_label' => 'bar')); - $this->assertNotFalse($link_id); - $this->assertInternalType('int', $link_id); - } - - public function testUpdateLink() - { - $link1 = $this->client->getLinkByLabel('bar'); - $this->assertNotEmpty($link1); - - $link2 = $this->client->getLinkByLabel('test'); - $this->assertNotEmpty($link2); - - $this->assertNotFalse($this->client->updateLink($link1['id'], $link2['id'], 'boo')); - - $link = $this->client->getLinkById($link1['id']); - $this->assertNotEmpty($link); - $this->assertEquals($link2['id'], $link['opposite_id']); - $this->assertEquals('boo', $link['label']); - - $this->assertTrue($this->client->removeLink($link1['id'])); - } - - public function testCreateTaskLink() - { - $task_id1 = $this->client->createTask(array('project_id' => 1, 'title' => 'A')); - $this->assertNotFalse($task_id1); - - $task_id2 = $this->client->createTask(array('project_id' => 1, 'title' => 'B')); - $this->assertNotFalse($task_id2); - - $task_id3 = $this->client->createTask(array('project_id' => 1, 'title' => 'C')); - $this->assertNotFalse($task_id3); - - $task_link_id = $this->client->createTaskLink($task_id1, $task_id2, 1); - $this->assertNotFalse($task_link_id); - - $task_link = $this->client->getTaskLinkById($task_link_id); - $this->assertNotEmpty($task_link); - $this->assertEquals($task_id1, $task_link['task_id']); - $this->assertEquals($task_id2, $task_link['opposite_task_id']); - $this->assertEquals(1, $task_link['link_id']); - - $task_links = $this->client->getAllTaskLinks($task_id1); - $this->assertNotEmpty($task_links); - $this->assertCount(1, $task_links); - - $this->assertTrue($this->client->updateTaskLink($task_link_id, $task_id1, $task_id3, 2)); - - $task_link = $this->client->getTaskLinkById($task_link_id); - $this->assertNotEmpty($task_link); - $this->assertEquals($task_id1, $task_link['task_id']); - $this->assertEquals($task_id3, $task_link['opposite_task_id']); - $this->assertEquals(2, $task_link['link_id']); - - $this->assertTrue($this->client->removeTaskLink($task_link_id)); - $this->assertEmpty($this->client->getAllTaskLinks($task_id1)); - } - - public function testCreateFile() - { - $this->assertNotFalse($this->client->createFile(1, $this->getTaskId(), 'My file', base64_encode('plain text file'))); - } - - public function testGetAllFiles() - { - $files = $this->client->getAllFiles(array('task_id' => $this->getTaskId())); - - $this->assertNotEmpty($files); - $this->assertCount(1, $files); - $this->assertEquals('My file', $files[0]['name']); - - $file = $this->client->getFile($files[0]['id']); - $this->assertNotEmpty($file); - $this->assertEquals('My file', $file['name']); - - $content = $this->client->downloadFile($file['id']); - $this->assertNotEmpty($content); - $this->assertEquals('plain text file', base64_decode($content)); - - $content = $this->client->downloadFile(1234567); - $this->assertEmpty($content); - - $this->assertTrue($this->client->removeFile($file['id'])); - $this->assertEmpty($this->client->getAllFiles(1)); - } - - public function testRemoveAllFiles() - { - $this->assertNotFalse($this->client->createFile(1, $this->getTaskId(), 'My file 1', base64_encode('plain text file'))); - $this->assertNotFalse($this->client->createFile(1, $this->getTaskId(), 'My file 2', base64_encode('plain text file'))); - - $files = $this->client->getAllFiles(array('task_id' => $this->getTaskId())); - $this->assertNotEmpty($files); - $this->assertCount(2, $files); - - $this->assertTrue($this->client->removeAllFiles(array('task_id' => $this->getTaskId()))); - - $files = $this->client->getAllFiles(array('task_id' => $this->getTaskId())); - $this->assertEmpty($files); - } - - public function testCreateTaskWithReference() - { - $task = array( - 'title' => 'Task with external ticket number', - 'reference' => 'TICKET-1234', - 'project_id' => 1, - 'description' => '[Link to my ticket](http://my-ticketing-system/1234)', - ); - - $task_id = $this->client->createTask($task); - - $this->assertNotFalse($task_id); - $this->assertInternalType('int', $task_id); - $this->assertTrue($task_id > 0); - } - - public function testGetTaskByReference() - { - $task = $this->client->getTaskByReference(array('project_id' => 1, 'reference' => 'TICKET-1234')); - - $this->assertNotEmpty($task); - $this->assertEquals('Task with external ticket number', $task['title']); - $this->assertEquals('TICKET-1234', $task['reference']); - $this->assertEquals('http://127.0.0.1:8000/?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'], $task['url']); - } - - public function testCreateOverdueTask() - { - $this->assertNotFalse($this->client->createTask(array( - 'title' => 'overdue task', - 'project_id' => 1, - 'date_due' => date('Y-m-d', strtotime('-2days')), - ))); - } - - public function testGetOverdueTasksByProject() - { - $tasks = $this->client->getOverdueTasksByProject(1); - $this->assertNotEmpty($tasks); - $this->assertCount(1, $tasks); - $this->assertEquals('overdue task', $tasks[0]['title']); - $this->assertEquals('API test', $tasks[0]['project_name']); - } - - public function testGetOverdueTasks() - { - $tasks = $this->client->getOverdueTasks(); - $this->assertNotEmpty($tasks); - $this->assertCount(1, $tasks); - $this->assertEquals('overdue task', $tasks[0]['title']); - $this->assertEquals('API test', $tasks[0]['project_name']); - } -} diff --git a/tests/functionals/UserApiTest.php b/tests/functionals/UserApiTest.php deleted file mode 100644 index 3c7fc04e..00000000 --- a/tests/functionals/UserApiTest.php +++ /dev/null @@ -1,289 +0,0 @@ -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')); - } - - public function testCreateUser() - { - $this->assertEquals(2, $this->app->createUser('user', 'password')); - } - - /** - * @expectedException JsonRPC\AccessDeniedException - */ - public function testNotAllowedAppProcedure() - { - $this->app->getMe(); - } - - /** - * @expectedException JsonRPC\AccessDeniedException - */ - public function testNotAllowedUserProcedure() - { - $this->user->getAllProjects(); - } - - /** - * @expectedException JsonRPC\AccessDeniedException - */ - public function testNotAllowedProjectForUser() - { - $this->user->getProjectById(1); - } - - public function testAllowedProjectForAdmin() - { - $this->assertNotEmpty($this->admin->getProjectById(1)); - } - - public function testGetTimezone() - { - $this->assertEquals('UTC', $this->user->getTimezone()); - } - - public function testGetVersion() - { - $this->assertEquals('master', $this->user->getVersion()); - } - - public function testGetDefaultColor() - { - $this->assertEquals('yellow', $this->user->getDefaultTaskColor()); - } - - public function testGetDefaultColors() - { - $colors = $this->user->getDefaultTaskColors(); - $this->assertNotEmpty($colors); - $this->assertArrayHasKey('red', $colors); - } - - public function testGetColorList() - { - $colors = $this->user->getColorList(); - $this->assertNotEmpty($colors); - $this->assertArrayHasKey('red', $colors); - $this->assertEquals('Red', $colors['red']); - } - - public function testGetMe() - { - $profile = $this->user->getMe(); - $this->assertNotEmpty($profile); - $this->assertEquals(2, $profile['id']); - $this->assertEquals('user', $profile['username']); - } - - public function testCreateMyPrivateProject() - { - $this->assertEquals(2, $this->user->createMyPrivateProject('my project')); - } - - public function testGetMyProjectsList() - { - $projects = $this->user->getMyProjectsList(); - $this->assertNotEmpty($projects); - $this->assertArrayNotHasKey(1, $projects); - $this->assertArrayHasKey(2, $projects); - $this->assertEquals('my project', $projects[2]); - } - - public function testGetMyProjects() - { - $projects = $this->user->getMyProjects(); - $this->assertNotEmpty($projects); - $this->assertCount(1, $projects); - $this->assertEquals(2, $projects[0]['id']); - $this->assertEquals('my project', $projects[0]['name']); - $this->assertNotEmpty($projects[0]['url']['calendar']); - $this->assertNotEmpty($projects[0]['url']['board']); - $this->assertNotEmpty($projects[0]['url']['list']); - } - - public function testGetProjectById() - { - $project = $this->user->getProjectById(2); - $this->assertNotEmpty($project); - $this->assertEquals('my project', $project['name']); - $this->assertEquals(1, $project['is_private']); - } - - public function testCreateTask() - { - $this->assertEquals(1, $this->user->createTask('my user title', 2)); - $this->assertEquals(2, $this->admin->createTask('my admin title', 1)); - } - - public function testCreateTaskWithWrongMember() - { - $this->assertFalse($this->user->createTask(array('title' => 'something', 'project_id' => 2, 'owner_id' => 1))); - $this->assertFalse($this->app->createTask(array('title' => 'something', 'project_id' => 1, 'owner_id' => 2))); - } - - public function testGetTask() - { - $task = $this->user->getTask(1); - $this->assertNotEmpty($task); - $this->assertEquals('my user title', $task['title']); - $this->assertEquals('yellow', $task['color_id']); - $this->assertArrayHasKey('color', $task); - $this->assertArrayHasKey('name', $task['color']); - $this->assertArrayHasKey('border', $task['color']); - $this->assertArrayHasKey('background', $task['color']); - } - - /** - * @expectedException JsonRPC\AccessDeniedException - */ - public function testGetAdminTask() - { - $this->user->getTask(2); - } - - /** - * @expectedException JsonRPC\AccessDeniedException - */ - public function testGetProjectActivityDenied() - { - $this->user->getProjectActivity(1); - } - - public function testGetProjectActivityAllowed() - { - $activity = $this->user->getProjectActivity(2); - $this->assertNotEmpty($activity); - } - - public function testGetMyActivityStream() - { - $activity = $this->user->getMyActivityStream(); - $this->assertNotEmpty($activity); - } - - public function testCloseTask() - { - $this->assertTrue($this->user->closeTask(1)); - } - - public function testOpenTask() - { - $this->assertTrue($this->user->openTask(1)); - } - - public function testMoveTaskPosition() - { - $this->assertTrue($this->user->moveTaskPosition(2, 1, 2, 1)); - } - - public function testUpdateTaskWithWrongMember() - { - $this->assertFalse($this->user->updateTask(array('id' => 1, 'title' => 'new title', 'reference' => 'test', 'owner_id' => 1))); - } - - public function testUpdateTask() - { - $this->assertTrue($this->user->updateTask(array('id' => 1, 'title' => 'new title', 'reference' => 'test', 'owner_id' => 2))); - } - - public function testGetbyReference() - { - $task = $this->user->getTaskByReference(2, 'test'); - $this->assertNotEmpty($task); - $this->assertEquals('new title', $task['title']); - $this->assertEquals(2, $task['column_id']); - $this->assertEquals(1, $task['position']); - } - - public function testGetMyDashboard() - { - $dashboard = $this->user->getMyDashboard(); - $this->assertNotEmpty($dashboard); - $this->assertArrayHasKey('projects', $dashboard); - $this->assertArrayHasKey('tasks', $dashboard); - $this->assertArrayHasKey('subtasks', $dashboard); - $this->assertNotEmpty($dashboard['projects']); - $this->assertNotEmpty($dashboard['tasks']); - } - - public function testGetBoard() - { - $this->assertNotEmpty($this->user->getBoard(2)); - } - - public function testCreateOverdueTask() - { - $this->assertNotFalse($this->user->createTask(array( - 'title' => 'overdue task', - 'project_id' => 2, - 'date_due' => date('Y-m-d', strtotime('-2days')), - 'owner_id' => 2, - ))); - } - - public function testGetMyOverdueTasks() - { - $tasks = $this->user->getMyOverdueTasks(); - $this->assertNotEmpty($tasks); - $this->assertCount(1, $tasks); - $this->assertEquals('overdue task', $tasks[0]['title']); - $this->assertEquals('my project', $tasks[0]['project_name']); - } - - public function testGetOverdueTasksByProject() - { - $tasks = $this->user->getOverdueTasksByProject(2); - $this->assertNotEmpty($tasks); - $this->assertCount(1, $tasks); - $this->assertEquals('overdue task', $tasks[0]['title']); - $this->assertEquals('my project', $tasks[0]['project_name']); - } -} diff --git a/tests/integration.mysql.xml b/tests/integration.mysql.xml new file mode 100644 index 00000000..30769371 --- /dev/null +++ b/tests/integration.mysql.xml @@ -0,0 +1,17 @@ + + + + integration + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/integration.postgres.xml b/tests/integration.postgres.xml new file mode 100644 index 00000000..ed8a3de3 --- /dev/null +++ b/tests/integration.postgres.xml @@ -0,0 +1,17 @@ + + + + integration + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/integration.sqlite.xml b/tests/integration.sqlite.xml new file mode 100644 index 00000000..1964f822 --- /dev/null +++ b/tests/integration.sqlite.xml @@ -0,0 +1,13 @@ + + + + integration + + + + + + + + + \ No newline at end of file diff --git a/tests/integration/ApiTest.php b/tests/integration/ApiTest.php new file mode 100644 index 00000000..798bde42 --- /dev/null +++ b/tests/integration/ApiTest.php @@ -0,0 +1,1112 @@ +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->table('settings')->eq('option', 'application_timezone')->update(array('value' => 'Europe/Paris')); + $db->closeConnection(); + } + + public function setUp() + { + $this->client = new JsonRPC\Client(API_URL); + $this->client->authentication('jsonrpc', API_KEY); + // $this->client->debug = true; + } + + private function getTaskId() + { + $tasks = $this->client->getAllTasks(1, 1); + $this->assertNotEmpty($tasks); + + return $tasks[0]['id']; + } + + public function testRemoveAll() + { + $projects = $this->client->getAllProjects(); + + if ($projects) { + foreach ($projects as $project) { + $this->assertEquals('http://127.0.0.1:8000/?controller=board&action=show&project_id='.$project['id'], $project['url']['board']); + $this->assertEquals('http://127.0.0.1:8000/?controller=calendar&action=show&project_id='.$project['id'], $project['url']['calendar']); + $this->assertEquals('http://127.0.0.1:8000/?controller=listing&action=show&project_id='.$project['id'], $project['url']['list']); + $this->assertTrue($this->client->removeProject($project['id'])); + } + } + } + + public function testCreateProject() + { + $project_id = $this->client->createProject('API test'); + $this->assertNotFalse($project_id); + $this->assertInternalType('int', $project_id); + } + + public function testGetProjectById() + { + $project = $this->client->getProjectById(1); + $this->assertNotEmpty($project); + $this->assertEquals(1, $project['id']); + $this->assertEquals('http://127.0.0.1:8000/?controller=board&action=show&project_id='.$project['id'], $project['url']['board']); + $this->assertEquals('http://127.0.0.1:8000/?controller=calendar&action=show&project_id='.$project['id'], $project['url']['calendar']); + $this->assertEquals('http://127.0.0.1:8000/?controller=listing&action=show&project_id='.$project['id'], $project['url']['list']); + } + + public function testGetProjectByName() + { + $project = $this->client->getProjectByName('API test'); + $this->assertNotEmpty($project); + $this->assertEquals(1, $project['id']); + $this->assertEquals('http://127.0.0.1:8000/?controller=board&action=show&project_id='.$project['id'], $project['url']['board']); + $this->assertEquals('http://127.0.0.1:8000/?controller=calendar&action=show&project_id='.$project['id'], $project['url']['calendar']); + $this->assertEquals('http://127.0.0.1:8000/?controller=listing&action=show&project_id='.$project['id'], $project['url']['list']); + + $project = $this->client->getProjectByName(array('name' => 'API test')); + $this->assertNotEmpty($project); + $this->assertEquals(1, $project['id']); + + $project = $this->client->getProjectByName('None'); + $this->assertEmpty($project); + $this->assertNull($project); + } + + public function testGetAllProjects() + { + $projects = $this->client->getAllProjects(); + $this->assertNotEmpty($projects); + + foreach ($projects as $project) { + $this->assertEquals('http://127.0.0.1:8000/?controller=board&action=show&project_id='.$project['id'], $project['url']['board']); + $this->assertEquals('http://127.0.0.1:8000/?controller=calendar&action=show&project_id='.$project['id'], $project['url']['calendar']); + $this->assertEquals('http://127.0.0.1:8000/?controller=listing&action=show&project_id='.$project['id'], $project['url']['list']); + } + } + + public function testUpdateProject() + { + $project = $this->client->getProjectById(1); + $this->assertNotEmpty($project); + $this->assertTrue($this->client->execute('updateProject', array('id' => 1, 'name' => 'API test 2'))); + + $project = $this->client->getProjectById(1); + $this->assertEquals('API test 2', $project['name']); + + $this->assertTrue($this->client->execute('updateProject', array('id' => 1, 'name' => 'API test', 'description' => 'test'))); + + $project = $this->client->getProjectById(1); + $this->assertEquals('API test', $project['name']); + $this->assertEquals('test', $project['description']); + } + + public function testDisableProject() + { + $this->assertTrue($this->client->disableProject(1)); + $project = $this->client->getProjectById(1); + $this->assertNotEmpty($project); + $this->assertEquals(0, $project['is_active']); + } + + public function testEnableProject() + { + $this->assertTrue($this->client->enableProject(1)); + $project = $this->client->getProjectById(1); + $this->assertNotEmpty($project); + $this->assertEquals(1, $project['is_active']); + } + + public function testEnableProjectPublicAccess() + { + $this->assertTrue($this->client->enableProjectPublicAccess(1)); + $project = $this->client->getProjectById(1); + $this->assertNotEmpty($project); + $this->assertEquals(1, $project['is_public']); + $this->assertNotEmpty($project['token']); + } + + public function testDisableProjectPublicAccess() + { + $this->assertTrue($this->client->disableProjectPublicAccess(1)); + $project = $this->client->getProjectById(1); + $this->assertNotEmpty($project); + $this->assertEquals(0, $project['is_public']); + $this->assertEmpty($project['token']); + } + + public function testgetProjectActivities() + { + $activities = $this->client->getProjectActivities(array('project_ids' => array(1))); + $this->assertInternalType('array', $activities); + $this->assertCount(0, $activities); + } + + public function testgetProjectActivity() + { + $activities = $this->client->getProjectActivity(1); + $this->assertInternalType('array', $activities); + $this->assertCount(0, $activities); + } + + public function testGetBoard() + { + $board = $this->client->getBoard(1); + $this->assertTrue(is_array($board)); + $this->assertEquals(1, count($board)); + $this->assertEquals('Default swimlane', $board[0]['name']); + $this->assertEquals(4, count($board[0]['columns'])); + } + + public function testGetColumns() + { + $columns = $this->client->getColumns(1); + $this->assertTrue(is_array($columns)); + $this->assertEquals(4, count($columns)); + $this->assertEquals('Done', $columns[3]['title']); + } + + public function testMoveColumnUp() + { + $this->assertTrue($this->client->moveColumnUp(1, 4)); + + $columns = $this->client->getColumns(1); + $this->assertTrue(is_array($columns)); + $this->assertEquals('Done', $columns[2]['title']); + $this->assertEquals('Work in progress', $columns[3]['title']); + } + + public function testMoveColumnDown() + { + $this->assertTrue($this->client->moveColumnDown(1, 4)); + + $columns = $this->client->getColumns(1); + $this->assertTrue(is_array($columns)); + $this->assertEquals('Work in progress', $columns[2]['title']); + $this->assertEquals('Done', $columns[3]['title']); + } + + public function testUpdateColumn() + { + $this->assertTrue($this->client->updateColumn(4, 'Boo', 2)); + + $columns = $this->client->getColumns(1); + $this->assertTrue(is_array($columns)); + $this->assertEquals('Boo', $columns[3]['title']); + $this->assertEquals(2, $columns[3]['task_limit']); + } + + public function testAddColumn() + { + $column_id = $this->client->addColumn(1, 'New column'); + + $this->assertNotFalse($column_id); + $this->assertInternalType('int', $column_id); + $this->assertTrue($column_id > 0); + + $columns = $this->client->getColumns(1); + $this->assertTrue(is_array($columns)); + $this->assertEquals(5, count($columns)); + $this->assertEquals('New column', $columns[4]['title']); + } + + public function testRemoveColumn() + { + $this->assertTrue($this->client->removeColumn(5)); + + $columns = $this->client->getColumns(1); + $this->assertTrue(is_array($columns)); + $this->assertEquals(4, count($columns)); + } + + public function testGetDefaultSwimlane() + { + $swimlane = $this->client->getDefaultSwimlane(1); + $this->assertNotEmpty($swimlane); + $this->assertEquals('Default swimlane', $swimlane['default_swimlane']); + } + + public function testAddSwimlane() + { + $swimlane_id = $this->client->addSwimlane(1, 'Swimlane 1'); + $this->assertNotFalse($swimlane_id); + $this->assertInternalType('int', $swimlane_id); + + $swimlane = $this->client->getSwimlaneById($swimlane_id); + $this->assertNotEmpty($swimlane); + $this->assertInternalType('array', $swimlane); + $this->assertEquals('Swimlane 1', $swimlane['name']); + } + + public function testGetSwimlane() + { + $swimlane = $this->client->getSwimlane(1); + $this->assertNotEmpty($swimlane); + $this->assertInternalType('array', $swimlane); + $this->assertEquals('Swimlane 1', $swimlane['name']); + } + + public function testUpdateSwimlane() + { + $swimlane = $this->client->getSwimlaneByName(1, 'Swimlane 1'); + $this->assertNotEmpty($swimlane); + $this->assertInternalType('array', $swimlane); + $this->assertEquals(1, $swimlane['id']); + $this->assertEquals('Swimlane 1', $swimlane['name']); + + $this->assertTrue($this->client->updateSwimlane($swimlane['id'], 'Another swimlane')); + + $swimlane = $this->client->getSwimlaneById($swimlane['id']); + $this->assertNotEmpty($swimlane); + $this->assertEquals('Another swimlane', $swimlane['name']); + } + + public function testDisableSwimlane() + { + $this->assertTrue($this->client->disableSwimlane(1, 1)); + + $swimlane = $this->client->getSwimlaneById(1); + $this->assertNotEmpty($swimlane); + $this->assertEquals(0, $swimlane['is_active']); + } + + public function testEnableSwimlane() + { + $this->assertTrue($this->client->enableSwimlane(1, 1)); + + $swimlane = $this->client->getSwimlaneById(1); + $this->assertNotEmpty($swimlane); + $this->assertEquals(1, $swimlane['is_active']); + } + + public function testGetAllSwimlanes() + { + $this->assertNotFalse($this->client->addSwimlane(1, 'Swimlane A')); + + $swimlanes = $this->client->getAllSwimlanes(1); + $this->assertNotEmpty($swimlanes); + $this->assertCount(2, $swimlanes); + $this->assertEquals('Another swimlane', $swimlanes[0]['name']); + $this->assertEquals('Swimlane A', $swimlanes[1]['name']); + } + + public function testGetActiveSwimlane() + { + $this->assertTrue($this->client->disableSwimlane(1, 1)); + + $swimlanes = $this->client->getActiveSwimlanes(1); + $this->assertNotEmpty($swimlanes); + $this->assertCount(2, $swimlanes); + $this->assertEquals('Default swimlane', $swimlanes[0]['name']); + $this->assertEquals('Swimlane A', $swimlanes[1]['name']); + } + + public function testMoveSwimlaneUp() + { + $this->assertTrue($this->client->enableSwimlane(1, 1)); + $this->assertTrue($this->client->moveSwimlaneUp(1, 1)); + + $swimlanes = $this->client->getActiveSwimlanes(1); + $this->assertNotEmpty($swimlanes); + $this->assertCount(3, $swimlanes); + $this->assertEquals('Default swimlane', $swimlanes[0]['name']); + $this->assertEquals('Another swimlane', $swimlanes[1]['name']); + $this->assertEquals('Swimlane A', $swimlanes[2]['name']); + + $this->assertTrue($this->client->moveSwimlaneUp(1, 2)); + + $swimlanes = $this->client->getActiveSwimlanes(1); + $this->assertNotEmpty($swimlanes); + $this->assertCount(3, $swimlanes); + $this->assertEquals('Default swimlane', $swimlanes[0]['name']); + $this->assertEquals('Swimlane A', $swimlanes[1]['name']); + $this->assertEquals('Another swimlane', $swimlanes[2]['name']); + } + + public function testMoveSwimlaneDown() + { + $this->assertTrue($this->client->moveSwimlaneDown(1, 2)); + + $swimlanes = $this->client->getActiveSwimlanes(1); + $this->assertNotEmpty($swimlanes); + $this->assertCount(3, $swimlanes); + $this->assertEquals('Default swimlane', $swimlanes[0]['name']); + $this->assertEquals('Another swimlane', $swimlanes[1]['name']); + $this->assertEquals('Swimlane A', $swimlanes[2]['name']); + } + + public function testCreateTaskWithWrongMember() + { + $task = array( + 'title' => 'Task #1', + 'color_id' => 'blue', + 'owner_id' => 1, + 'project_id' => 1, + 'column_id' => 2, + ); + + $task_id = $this->client->createTask($task); + + $this->assertFalse($task_id); + } + + public function testGetAllowedUsers() + { + $users = $this->client->getMembers(1); + $this->assertNotFalse($users); + $this->assertEquals(array(), $users); + } + + public function testAddMember() + { + $this->assertTrue($this->client->allowUser(1, 1)); + } + + public function testCreateTask() + { + $task = array( + 'title' => 'Task #1', + 'color_id' => 'blue', + 'owner_id' => 1, + 'project_id' => 1, + 'column_id' => 2, + ); + + $task_id = $this->client->createTask($task); + + $this->assertNotFalse($task_id); + $this->assertInternalType('int', $task_id); + $this->assertTrue($task_id > 0); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testCreateTaskWithBadParams() + { + $task = array( + 'title' => 'Task #1', + 'color_id' => 'blue', + 'owner_id' => 1, + ); + + $this->client->createTask($task); + } + + public function testGetTask() + { + $task = $this->client->getTask(1); + + $this->assertNotFalse($task); + $this->assertTrue(is_array($task)); + $this->assertEquals('Task #1', $task['title']); + $this->assertEquals('http://127.0.0.1:8000/?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'], $task['url']); + } + + public function testGetAllTasks() + { + $tasks = $this->client->getAllTasks(1, 1); + + $this->assertNotFalse($tasks); + $this->assertTrue(is_array($tasks)); + $this->assertEquals('Task #1', $tasks[0]['title']); + $this->assertEquals('http://127.0.0.1:8000/?controller=task&action=show&task_id='.$tasks[0]['id'].'&project_id='.$tasks[0]['project_id'], $tasks[0]['url']); + + $tasks = $this->client->getAllTasks(2, 0); + + $this->assertNotFalse($tasks); + $this->assertTrue(is_array($tasks)); + $this->assertEmpty($tasks); + } + + public function testMoveTaskSwimlane() + { + $task_id = $this->getTaskId(); + + $task = $this->client->getTask($task_id); + $this->assertNotFalse($task); + $this->assertTrue(is_array($task)); + $this->assertEquals(1, $task['position']); + $this->assertEquals(2, $task['column_id']); + $this->assertEquals(0, $task['swimlane_id']); + + $moved_timestamp = $task['date_moved']; + sleep(1); + $this->assertTrue($this->client->moveTaskPosition(1, $task_id, 4, 1, 2)); + + $task = $this->client->getTask($task_id); + $this->assertNotFalse($task); + $this->assertTrue(is_array($task)); + $this->assertEquals(1, $task['position']); + $this->assertEquals(4, $task['column_id']); + $this->assertEquals(2, $task['swimlane_id']); + $this->assertNotEquals($moved_timestamp, $task['date_moved']); + } + + public function testRemoveSwimlane() + { + $this->assertTrue($this->client->removeSwimlane(1, 2)); + + $task = $this->client->getTask($this->getTaskId()); + $this->assertNotFalse($task); + $this->assertTrue(is_array($task)); + $this->assertEquals(1, $task['position']); + $this->assertEquals(4, $task['column_id']); + $this->assertEquals(0, $task['swimlane_id']); + } + + public function testUpdateTask() + { + $task = $this->client->getTask(1); + + $values = array(); + $values['id'] = $task['id']; + $values['color_id'] = 'green'; + $values['description'] = 'test'; + $values['date_due'] = ''; + + $this->assertTrue($this->client->execute('updateTask', $values)); + } + + public function testRemoveTask() + { + $this->assertTrue($this->client->removeTask(1)); + } + + public function testRemoveUsers() + { + $users = $this->client->getAllUsers(); + $this->assertNotFalse($users); + $this->assertNotEmpty($users); + + foreach ($users as $user) { + if ($user['id'] > 1) { + $this->assertTrue($this->client->removeUser($user['id'])); + } + } + } + + public function testCreateUser() + { + $user = array( + 'username' => 'toto', + 'name' => 'Toto', + 'password' => '123456', + ); + + $user_id = $this->client->execute('createUser', $user); + $this->assertNotFalse($user_id); + $this->assertInternalType('int', $user_id); + $this->assertTrue($user_id > 0); + } + + public function testCreateManagerUser() + { + $user = array( + 'username' => 'manager', + 'name' => 'Manager', + 'password' => '123456', + 'role' => 'app-manager' + ); + + $user_id = $this->client->execute('createUser', $user); + $this->assertNotFalse($user_id); + $this->assertInternalType('int', $user_id); + $this->assertTrue($user_id > 0); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testCreateUserWithBadParams() + { + $user = array( + 'name' => 'Titi', + 'password' => '123456', + ); + + $this->assertNull($this->client->execute('createUser', $user)); + } + + public function testGetUser() + { + $user = $this->client->getUser(2); + $this->assertNotFalse($user); + $this->assertTrue(is_array($user)); + $this->assertEquals('toto', $user['username']); + + $user = $this->client->getUser(3); + $this->assertNotEmpty($user); + $this->assertEquals('app-manager', $user['role']); + + $this->assertNull($this->client->getUser(2222)); + } + + public function testUpdateUser() + { + $user = array(); + $user['id'] = 2; + $user['username'] = 'titi'; + $user['name'] = 'Titi'; + + $this->assertTrue($this->client->execute('updateUser', $user)); + + $user = $this->client->getUser(2); + $this->assertNotFalse($user); + $this->assertTrue(is_array($user)); + $this->assertEquals('titi', $user['username']); + $this->assertEquals('Titi', $user['name']); + + $user = array(); + $user['id'] = 2; + $user['email'] = 'titi@localhost'; + + $this->assertTrue($this->client->execute('updateUser', $user)); + + $user = $this->client->getUser(2); + $this->assertNotFalse($user); + $this->assertTrue(is_array($user)); + $this->assertEquals('titi@localhost', $user['email']); + } + + public function testAllowedUser() + { + $this->assertTrue($this->client->allowUser(1, 2)); + + $users = $this->client->getMembers(1); + $this->assertNotFalse($users); + $this->assertEquals(array(1 => 'admin', 2 => 'Titi'), $users); + } + + public function testRevokeUser() + { + $this->assertTrue($this->client->revokeUser(1, 2)); + + $users = $this->client->getMembers(1); + $this->assertNotFalse($users); + $this->assertEquals(array(1 => 'admin'), $users); + } + + public function testCreateComment() + { + $task = array( + 'title' => 'Task with comment', + 'color_id' => 'red', + 'owner_id' => 1, + 'project_id' => 1, + 'column_id' => 1, + ); + + $this->assertNotFalse($this->client->execute('createTask', $task)); + + $tasks = $this->client->getAllTasks(1, 1); + $this->assertNotEmpty($tasks); + $this->assertEquals(1, count($tasks)); + + $comment = array( + 'task_id' => $tasks[0]['id'], + 'user_id' => 2, + 'content' => 'boo', + ); + + $comment_id = $this->client->execute('createComment', $comment); + + $this->assertNotFalse($comment_id); + $this->assertInternalType('int', $comment_id); + $this->assertTrue($comment_id > 0); + } + + public function testGetComment() + { + $comment = $this->client->getComment(1); + $this->assertNotFalse($comment); + $this->assertNotEmpty($comment); + $this->assertEquals(2, $comment['user_id']); + $this->assertEquals('boo', $comment['comment']); + } + + public function testUpdateComment() + { + $comment = array(); + $comment['id'] = 1; + $comment['content'] = 'test'; + + $this->assertTrue($this->client->execute('updateComment', $comment)); + + $comment = $this->client->getComment(1); + $this->assertEquals('test', $comment['comment']); + } + + public function testGetAllComments() + { + $task_id = $this->getTaskId(); + + $comment = array( + 'task_id' => $task_id, + 'user_id' => 1, + 'content' => 'blabla', + ); + + $comment_id = $this->client->createComment($comment); + + $this->assertNotFalse($comment_id); + $this->assertInternalType('int', $comment_id); + $this->assertTrue($comment_id > 0); + + $comments = $this->client->getAllComments($task_id); + $this->assertNotFalse($comments); + $this->assertNotEmpty($comments); + $this->assertTrue(is_array($comments)); + $this->assertEquals(2, count($comments)); + } + + public function testRemoveComment() + { + $task_id = $this->getTaskId(); + + $comments = $this->client->getAllComments($task_id); + $this->assertNotFalse($comments); + $this->assertNotEmpty($comments); + $this->assertTrue(is_array($comments)); + + foreach ($comments as $comment) { + $this->assertTrue($this->client->removeComment($comment['id'])); + } + + $comments = $this->client->getAllComments($task_id); + $this->assertNotFalse($comments); + $this->assertEmpty($comments); + $this->assertTrue(is_array($comments)); + } + + public function testCreateSubtask() + { + $subtask = array( + 'task_id' => $this->getTaskId(), + 'title' => 'subtask #1', + ); + + $subtask_id = $this->client->createSubtask($subtask); + + $this->assertNotFalse($subtask_id); + $this->assertInternalType('int', $subtask_id); + $this->assertTrue($subtask_id > 0); + } + + public function testGetSubtask() + { + $subtask = $this->client->getSubtask(1); + $this->assertNotFalse($subtask); + $this->assertNotEmpty($subtask); + $this->assertEquals($this->getTaskId(), $subtask['task_id']); + $this->assertEquals(0, $subtask['user_id']); + $this->assertEquals('subtask #1', $subtask['title']); + } + + public function testUpdateSubtask() + { + $subtask = array(); + $subtask['id'] = 1; + $subtask['task_id'] = $this->getTaskId(); + $subtask['title'] = 'test'; + + $this->assertTrue($this->client->execute('updateSubtask', $subtask)); + + $subtask = $this->client->getSubtask(1); + $this->assertEquals('test', $subtask['title']); + } + + public function testGetAllSubtasks() + { + $subtask = array( + 'task_id' => $this->getTaskId(), + 'user_id' => 2, + 'title' => 'Subtask #2', + ); + + $this->assertNotFalse($this->client->execute('createSubtask', $subtask)); + + $subtasks = $this->client->getAllSubtasks($this->getTaskId()); + $this->assertNotFalse($subtasks); + $this->assertNotEmpty($subtasks); + $this->assertTrue(is_array($subtasks)); + $this->assertEquals(2, count($subtasks)); + } + + public function testRemoveSubtask() + { + $this->assertTrue($this->client->removeSubtask(1)); + + $subtasks = $this->client->getAllSubtasks($this->getTaskId()); + $this->assertNotFalse($subtasks); + $this->assertNotEmpty($subtasks); + $this->assertTrue(is_array($subtasks)); + $this->assertEquals(1, count($subtasks)); + } + + public function testMoveTaskPosition() + { + $task_id = $this->getTaskId(); + $this->assertTrue($this->client->moveTaskPosition(1, $task_id, 3, 1)); + + $task = $this->client->getTask($task_id); + $this->assertNotFalse($task); + $this->assertTrue(is_array($task)); + $this->assertEquals(1, $task['position']); + $this->assertEquals(3, $task['column_id']); + } + + public function testCategoryCreation() + { + $category = array( + 'name' => 'Category', + 'project_id' => 1, + ); + + $cat_id = $this->client->execute('createCategory', $category); + $this->assertNotFalse($cat_id); + $this->assertInternalType('int', $cat_id); + $this->assertTrue($cat_id > 0); + + // Duplicate + + $category = array( + 'name' => 'Category', + 'project_id' => 1, + ); + + $this->assertFalse($this->client->execute('createCategory', $category)); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testCategoryCreationWithBadParams() + { + // Missing project id + $category = array( + 'name' => 'Category', + ); + + $this->assertNull($this->client->execute('createCategory', $category)); + } + + public function testCategoryRead() + { + $category = $this->client->getCategory(1); + + $this->assertTrue(is_array($category)); + $this->assertNotEmpty($category); + $this->assertEquals(1, $category['id']); + $this->assertEquals('Category', $category['name']); + $this->assertEquals(1, $category['project_id']); + } + + public function testGetAllCategories() + { + $categories = $this->client->getAllCategories(1); + + $this->assertNotEmpty($categories); + $this->assertNotFalse($categories); + $this->assertTrue(is_array($categories)); + $this->assertEquals(1, count($categories)); + $this->assertEquals(1, $categories[0]['id']); + $this->assertEquals('Category', $categories[0]['name']); + $this->assertEquals(1, $categories[0]['project_id']); + } + + public function testCategoryUpdate() + { + $category = array( + 'id' => 1, + 'name' => 'Renamed category', + ); + + $this->assertTrue($this->client->execute('updateCategory', $category)); + + $category = $this->client->getCategory(1); + $this->assertTrue(is_array($category)); + $this->assertNotEmpty($category); + $this->assertEquals(1, $category['id']); + $this->assertEquals('Renamed category', $category['name']); + $this->assertEquals(1, $category['project_id']); + } + + public function testCategoryRemove() + { + $this->assertTrue($this->client->removeCategory(1)); + $this->assertFalse($this->client->removeCategory(1)); + $this->assertFalse($this->client->removeCategory(1111)); + } + + public function testGetAvailableActions() + { + $actions = $this->client->getAvailableActions(); + $this->assertNotEmpty($actions); + $this->assertInternalType('array', $actions); + $this->assertArrayHasKey('\Kanboard\Action\TaskCloseColumn', $actions); + } + + public function testGetAvailableActionEvents() + { + $events = $this->client->getAvailableActionEvents(); + $this->assertNotEmpty($events); + $this->assertInternalType('array', $events); + $this->assertArrayHasKey('task.move.column', $events); + } + + public function testGetCompatibleActionEvents() + { + $events = $this->client->getCompatibleActionEvents('\Kanboard\Action\TaskCloseColumn'); + $this->assertNotEmpty($events); + $this->assertInternalType('array', $events); + $this->assertArrayHasKey('task.move.column', $events); + } + + public function testCreateAction() + { + $action_id = $this->client->createAction(1, 'task.move.column', '\Kanboard\Action\TaskCloseColumn', array('column_id' => 1)); + $this->assertNotFalse($action_id); + $this->assertEquals(1, $action_id); + } + + public function testGetActions() + { + $actions = $this->client->getActions(1); + $this->assertNotEmpty($actions); + $this->assertInternalType('array', $actions); + $this->assertCount(1, $actions); + $this->assertArrayHasKey('id', $actions[0]); + $this->assertArrayHasKey('project_id', $actions[0]); + $this->assertArrayHasKey('event_name', $actions[0]); + $this->assertArrayHasKey('action_name', $actions[0]); + $this->assertArrayHasKey('params', $actions[0]); + $this->assertArrayHasKey('column_id', $actions[0]['params']); + } + + public function testRemoveAction() + { + $this->assertTrue($this->client->removeAction(1)); + + $actions = $this->client->getActions(1); + $this->assertEmpty($actions); + $this->assertCount(0, $actions); + } + + public function testGetAllLinks() + { + $links = $this->client->getAllLinks(); + $this->assertNotEmpty($links); + $this->assertArrayHasKey('id', $links[0]); + $this->assertArrayHasKey('label', $links[0]); + $this->assertArrayHasKey('opposite_id', $links[0]); + } + + public function testGetOppositeLink() + { + $link = $this->client->getOppositeLinkId(1); + $this->assertEquals(1, $link); + + $link = $this->client->getOppositeLinkId(2); + $this->assertEquals(3, $link); + } + + public function testGetLinkByLabel() + { + $link = $this->client->getLinkByLabel('blocks'); + $this->assertNotEmpty($link); + $this->assertEquals(2, $link['id']); + $this->assertEquals(3, $link['opposite_id']); + } + + public function testGetLinkById() + { + $link = $this->client->getLinkById(4); + $this->assertNotEmpty($link); + $this->assertEquals(4, $link['id']); + $this->assertEquals(5, $link['opposite_id']); + $this->assertEquals('duplicates', $link['label']); + } + + public function testCreateLink() + { + $link_id = $this->client->createLink(array('label' => 'test')); + $this->assertNotFalse($link_id); + $this->assertInternalType('int', $link_id); + + $link_id = $this->client->createLink(array('label' => 'foo', 'opposite_label' => 'bar')); + $this->assertNotFalse($link_id); + $this->assertInternalType('int', $link_id); + } + + public function testUpdateLink() + { + $link1 = $this->client->getLinkByLabel('bar'); + $this->assertNotEmpty($link1); + + $link2 = $this->client->getLinkByLabel('test'); + $this->assertNotEmpty($link2); + + $this->assertNotFalse($this->client->updateLink($link1['id'], $link2['id'], 'boo')); + + $link = $this->client->getLinkById($link1['id']); + $this->assertNotEmpty($link); + $this->assertEquals($link2['id'], $link['opposite_id']); + $this->assertEquals('boo', $link['label']); + + $this->assertTrue($this->client->removeLink($link1['id'])); + } + + public function testCreateTaskLink() + { + $task_id1 = $this->client->createTask(array('project_id' => 1, 'title' => 'A')); + $this->assertNotFalse($task_id1); + + $task_id2 = $this->client->createTask(array('project_id' => 1, 'title' => 'B')); + $this->assertNotFalse($task_id2); + + $task_id3 = $this->client->createTask(array('project_id' => 1, 'title' => 'C')); + $this->assertNotFalse($task_id3); + + $task_link_id = $this->client->createTaskLink($task_id1, $task_id2, 1); + $this->assertNotFalse($task_link_id); + + $task_link = $this->client->getTaskLinkById($task_link_id); + $this->assertNotEmpty($task_link); + $this->assertEquals($task_id1, $task_link['task_id']); + $this->assertEquals($task_id2, $task_link['opposite_task_id']); + $this->assertEquals(1, $task_link['link_id']); + + $task_links = $this->client->getAllTaskLinks($task_id1); + $this->assertNotEmpty($task_links); + $this->assertCount(1, $task_links); + + $this->assertTrue($this->client->updateTaskLink($task_link_id, $task_id1, $task_id3, 2)); + + $task_link = $this->client->getTaskLinkById($task_link_id); + $this->assertNotEmpty($task_link); + $this->assertEquals($task_id1, $task_link['task_id']); + $this->assertEquals($task_id3, $task_link['opposite_task_id']); + $this->assertEquals(2, $task_link['link_id']); + + $this->assertTrue($this->client->removeTaskLink($task_link_id)); + $this->assertEmpty($this->client->getAllTaskLinks($task_id1)); + } + + public function testCreateFile() + { + $this->assertNotFalse($this->client->createFile(1, $this->getTaskId(), 'My file', base64_encode('plain text file'))); + } + + public function testGetAllFiles() + { + $files = $this->client->getAllFiles(array('task_id' => $this->getTaskId())); + + $this->assertNotEmpty($files); + $this->assertCount(1, $files); + $this->assertEquals('My file', $files[0]['name']); + + $file = $this->client->getFile($files[0]['id']); + $this->assertNotEmpty($file); + $this->assertEquals('My file', $file['name']); + + $content = $this->client->downloadFile($file['id']); + $this->assertNotEmpty($content); + $this->assertEquals('plain text file', base64_decode($content)); + + $content = $this->client->downloadFile(1234567); + $this->assertEmpty($content); + + $this->assertTrue($this->client->removeFile($file['id'])); + $this->assertEmpty($this->client->getAllFiles(1)); + } + + public function testRemoveAllFiles() + { + $this->assertNotFalse($this->client->createFile(1, $this->getTaskId(), 'My file 1', base64_encode('plain text file'))); + $this->assertNotFalse($this->client->createFile(1, $this->getTaskId(), 'My file 2', base64_encode('plain text file'))); + + $files = $this->client->getAllFiles(array('task_id' => $this->getTaskId())); + $this->assertNotEmpty($files); + $this->assertCount(2, $files); + + $this->assertTrue($this->client->removeAllFiles(array('task_id' => $this->getTaskId()))); + + $files = $this->client->getAllFiles(array('task_id' => $this->getTaskId())); + $this->assertEmpty($files); + } + + public function testCreateTaskWithReference() + { + $task = array( + 'title' => 'Task with external ticket number', + 'reference' => 'TICKET-1234', + 'project_id' => 1, + 'description' => '[Link to my ticket](http://my-ticketing-system/1234)', + ); + + $task_id = $this->client->createTask($task); + + $this->assertNotFalse($task_id); + $this->assertInternalType('int', $task_id); + $this->assertTrue($task_id > 0); + } + + public function testGetTaskByReference() + { + $task = $this->client->getTaskByReference(array('project_id' => 1, 'reference' => 'TICKET-1234')); + + $this->assertNotEmpty($task); + $this->assertEquals('Task with external ticket number', $task['title']); + $this->assertEquals('TICKET-1234', $task['reference']); + $this->assertEquals('http://127.0.0.1:8000/?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'], $task['url']); + } + + public function testCreateOverdueTask() + { + $this->assertNotFalse($this->client->createTask(array( + 'title' => 'overdue task', + 'project_id' => 1, + 'date_due' => date('Y-m-d', strtotime('-2days')), + ))); + } + + public function testGetOverdueTasksByProject() + { + $tasks = $this->client->getOverdueTasksByProject(1); + $this->assertNotEmpty($tasks); + $this->assertCount(1, $tasks); + $this->assertEquals('overdue task', $tasks[0]['title']); + $this->assertEquals('API test', $tasks[0]['project_name']); + } + + public function testGetOverdueTasks() + { + $tasks = $this->client->getOverdueTasks(); + $this->assertNotEmpty($tasks); + $this->assertCount(1, $tasks); + $this->assertEquals('overdue task', $tasks[0]['title']); + $this->assertEquals('API test', $tasks[0]['project_name']); + } +} 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 @@ +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 @@ +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 @@ +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 @@ +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/integration/MeTest.php b/tests/integration/MeTest.php new file mode 100644 index 00000000..21f61756 --- /dev/null +++ b/tests/integration/MeTest.php @@ -0,0 +1,247 @@ +assertEquals(1, $this->app->createProject('team project')); + } + + public function testCreateUser() + { + $this->assertEquals(2, $this->app->createUser('user', 'password')); + } + + /** + * @expectedException JsonRPC\AccessDeniedException + */ + public function testNotAllowedAppProcedure() + { + $this->app->getMe(); + } + + /** + * @expectedException JsonRPC\AccessDeniedException + */ + public function testNotAllowedUserProcedure() + { + $this->user->getAllProjects(); + } + + /** + * @expectedException JsonRPC\AccessDeniedException + */ + public function testNotAllowedProjectForUser() + { + $this->user->getProjectById(1); + } + + public function testAllowedProjectForAdmin() + { + $this->assertNotEmpty($this->admin->getProjectById(1)); + } + + public function testGetTimezone() + { + $this->assertEquals('UTC', $this->user->getTimezone()); + } + + public function testGetVersion() + { + $this->assertEquals('master', $this->user->getVersion()); + } + + public function testGetDefaultColor() + { + $this->assertEquals('yellow', $this->user->getDefaultTaskColor()); + } + + public function testGetDefaultColors() + { + $colors = $this->user->getDefaultTaskColors(); + $this->assertNotEmpty($colors); + $this->assertArrayHasKey('red', $colors); + } + + public function testGetColorList() + { + $colors = $this->user->getColorList(); + $this->assertNotEmpty($colors); + $this->assertArrayHasKey('red', $colors); + $this->assertEquals('Red', $colors['red']); + } + + public function testGetMe() + { + $profile = $this->user->getMe(); + $this->assertNotEmpty($profile); + $this->assertEquals(2, $profile['id']); + $this->assertEquals('user', $profile['username']); + } + + public function testCreateMyPrivateProject() + { + $this->assertEquals(2, $this->user->createMyPrivateProject('my project')); + } + + public function testGetMyProjectsList() + { + $projects = $this->user->getMyProjectsList(); + $this->assertNotEmpty($projects); + $this->assertArrayNotHasKey(1, $projects); + $this->assertArrayHasKey(2, $projects); + $this->assertEquals('my project', $projects[2]); + } + + public function testGetMyProjects() + { + $projects = $this->user->getMyProjects(); + $this->assertNotEmpty($projects); + $this->assertCount(1, $projects); + $this->assertEquals(2, $projects[0]['id']); + $this->assertEquals('my project', $projects[0]['name']); + $this->assertNotEmpty($projects[0]['url']['calendar']); + $this->assertNotEmpty($projects[0]['url']['board']); + $this->assertNotEmpty($projects[0]['url']['list']); + } + + public function testGetProjectById() + { + $project = $this->user->getProjectById(2); + $this->assertNotEmpty($project); + $this->assertEquals('my project', $project['name']); + $this->assertEquals(1, $project['is_private']); + } + + public function testCreateTask() + { + $this->assertEquals(1, $this->user->createTask('my user title', 2)); + $this->assertEquals(2, $this->admin->createTask('my admin title', 1)); + } + + public function testCreateTaskWithWrongMember() + { + $this->assertFalse($this->user->createTask(array('title' => 'something', 'project_id' => 2, 'owner_id' => 1))); + $this->assertFalse($this->app->createTask(array('title' => 'something', 'project_id' => 1, 'owner_id' => 2))); + } + + public function testGetTask() + { + $task = $this->user->getTask(1); + $this->assertNotEmpty($task); + $this->assertEquals('my user title', $task['title']); + $this->assertEquals('yellow', $task['color_id']); + $this->assertArrayHasKey('color', $task); + $this->assertArrayHasKey('name', $task['color']); + $this->assertArrayHasKey('border', $task['color']); + $this->assertArrayHasKey('background', $task['color']); + } + + /** + * @expectedException JsonRPC\AccessDeniedException + */ + public function testGetAdminTask() + { + $this->user->getTask(2); + } + + /** + * @expectedException JsonRPC\AccessDeniedException + */ + public function testGetProjectActivityDenied() + { + $this->user->getProjectActivity(1); + } + + public function testGetProjectActivityAllowed() + { + $activity = $this->user->getProjectActivity(2); + $this->assertNotEmpty($activity); + } + + public function testGetMyActivityStream() + { + $activity = $this->user->getMyActivityStream(); + $this->assertNotEmpty($activity); + } + + public function testCloseTask() + { + $this->assertTrue($this->user->closeTask(1)); + } + + public function testOpenTask() + { + $this->assertTrue($this->user->openTask(1)); + } + + public function testMoveTaskPosition() + { + $this->assertTrue($this->user->moveTaskPosition(2, 1, 2, 1)); + } + + public function testUpdateTaskWithWrongMember() + { + $this->assertFalse($this->user->updateTask(array('id' => 1, 'title' => 'new title', 'reference' => 'test', 'owner_id' => 1))); + } + + public function testUpdateTask() + { + $this->assertTrue($this->user->updateTask(array('id' => 1, 'title' => 'new title', 'reference' => 'test', 'owner_id' => 2))); + } + + public function testGetbyReference() + { + $task = $this->user->getTaskByReference(2, 'test'); + $this->assertNotEmpty($task); + $this->assertEquals('new title', $task['title']); + $this->assertEquals(2, $task['column_id']); + $this->assertEquals(1, $task['position']); + } + + public function testGetMyDashboard() + { + $dashboard = $this->user->getMyDashboard(); + $this->assertNotEmpty($dashboard); + $this->assertArrayHasKey('projects', $dashboard); + $this->assertArrayHasKey('tasks', $dashboard); + $this->assertArrayHasKey('subtasks', $dashboard); + $this->assertNotEmpty($dashboard['projects']); + $this->assertNotEmpty($dashboard['tasks']); + } + + public function testGetBoard() + { + $this->assertNotEmpty($this->user->getBoard(2)); + } + + public function testCreateOverdueTask() + { + $this->assertNotFalse($this->user->createTask(array( + 'title' => 'overdue task', + 'project_id' => 2, + 'date_due' => date('Y-m-d', strtotime('-2days')), + 'owner_id' => 2, + ))); + } + + public function testGetMyOverdueTasks() + { + $tasks = $this->user->getMyOverdueTasks(); + $this->assertNotEmpty($tasks); + $this->assertCount(1, $tasks); + $this->assertEquals('overdue task', $tasks[0]['title']); + $this->assertEquals('my project', $tasks[0]['project_name']); + } + + public function testGetOverdueTasksByProject() + { + $tasks = $this->user->getOverdueTasksByProject(2); + $this->assertNotEmpty($tasks); + $this->assertCount(1, $tasks); + $this->assertEquals('overdue task', $tasks[0]['title']); + $this->assertEquals('my project', $tasks[0]['project_name']); + } +} 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 @@ +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)); + } +} -- cgit v1.2.3 From 0261c751cf539030f47756bc1ea1650123ed2312 Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Sat, 20 Feb 2016 15:31:26 -0500 Subject: Fix cosmetic issues and update api documentation --- ChangeLog | 4 + app/Api/File.php | 1 - app/Api/Task.php | 12 +- app/Controller/Auth.php | 3 +- app/Core/Base.php | 1 + app/Model/Board.php | 2 - app/Model/LastLogin.php | 6 +- app/Model/User.php | 2 +- doc/api-board-procedures.markdown | 258 ------------------------------------- doc/api-column-procedures.markdown | 229 ++++++++++++++++++++++++++++++++ doc/api-json-rpc.markdown | 1 + 11 files changed, 246 insertions(+), 273 deletions(-) create mode 100644 doc/api-column-procedures.markdown (limited to 'doc/api-json-rpc.markdown') diff --git a/ChangeLog b/ChangeLog index 190a57ad..11dbbfb4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,10 @@ Version 1.0.26 (unreleased) -------------- +Breaking changes: + +* API procedures: "moveColumnUp" and "moveColumnDown" are replace by "changeColumnPosition" + New features: * Add drag and drop to change subtasks and columns positions diff --git a/app/Api/File.php b/app/Api/File.php index e4204e6d..71c31c76 100644 --- a/app/Api/File.php +++ b/app/Api/File.php @@ -2,7 +2,6 @@ namespace Kanboard\Api; -use Kanboard\Core\Base; use Kanboard\Core\ObjectStorage\ObjectStorageException; /** diff --git a/app/Api/Task.php b/app/Api/Task.php index f132bcd6..177a09c6 100644 --- a/app/Api/Task.php +++ b/app/Api/Task.php @@ -75,9 +75,9 @@ class Task extends Base } public function createTask($title, $project_id, $color_id = '', $column_id = 0, $owner_id = 0, $creator_id = 0, - $date_due = '', $description = '', $category_id = 0, $score = 0, $swimlane_id = 0, - $recurrence_status = 0, $recurrence_trigger = 0, $recurrence_factor = 0, $recurrence_timeframe = 0, - $recurrence_basedate = 0, $reference = '') + $date_due = '', $description = '', $category_id = 0, $score = 0, $swimlane_id = 0, + $recurrence_status = 0, $recurrence_trigger = 0, $recurrence_factor = 0, $recurrence_timeframe = 0, + $recurrence_basedate = 0, $reference = '') { $this->checkProjectPermission($project_id); @@ -115,9 +115,9 @@ class Task extends Base } public function updateTask($id, $title = null, $color_id = null, $owner_id = null, - $date_due = null, $description = null, $category_id = null, $score = null, - $recurrence_status = null, $recurrence_trigger = null, $recurrence_factor = null, - $recurrence_timeframe = null, $recurrence_basedate = null, $reference = null) + $date_due = null, $description = null, $category_id = null, $score = null, + $recurrence_status = null, $recurrence_trigger = null, $recurrence_factor = null, + $recurrence_timeframe = null, $recurrence_basedate = null, $reference = null) { $this->checkTaskPermission($id); diff --git a/app/Controller/Auth.php b/app/Controller/Auth.php index b98dff5d..46b5a546 100644 --- a/app/Controller/Auth.php +++ b/app/Controller/Auth.php @@ -58,8 +58,7 @@ class Auth extends Base if (! DISABLE_LOGOUT) { $this->sessionManager->close(); $this->response->redirect($this->helper->url->to('auth', 'login')); - } - else { + } else { $this->response->redirect($this->helper->url->to('auth', 'index')); } } diff --git a/app/Core/Base.php b/app/Core/Base.php index 31c07355..f1053114 100644 --- a/app/Core/Base.php +++ b/app/Core/Base.php @@ -63,6 +63,7 @@ use Pimple\Container; * @property \Kanboard\Model\Board $board * @property \Kanboard\Model\Category $category * @property \Kanboard\Model\Color $color + * @property \Kanboard\Model\Column $column * @property \Kanboard\Model\Comment $comment * @property \Kanboard\Model\Config $config * @property \Kanboard\Model\Currency $currency diff --git a/app/Model/Board.php b/app/Model/Board.php index f677266f..c10be19f 100644 --- a/app/Model/Board.php +++ b/app/Model/Board.php @@ -2,8 +2,6 @@ namespace Kanboard\Model; -use PicoDb\Database; - /** * Board model * diff --git a/app/Model/LastLogin.php b/app/Model/LastLogin.php index a1734819..feb5f5a3 100644 --- a/app/Model/LastLogin.php +++ b/app/Model/LastLogin.php @@ -65,9 +65,9 @@ class LastLogin extends Base if (count($connections) >= self::NB_LOGINS) { $this->db->table(self::TABLE) - ->eq('user_id', $user_id) - ->notin('id', array_slice($connections, 0, self::NB_LOGINS - 1)) - ->remove(); + ->eq('user_id', $user_id) + ->notin('id', array_slice($connections, 0, self::NB_LOGINS - 1)) + ->remove(); } } diff --git a/app/Model/User.php b/app/Model/User.php index e2494c4c..2d87d35b 100644 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -137,7 +137,7 @@ class User extends Base * * @access public * @param string $username Username - * @return array + * @return integer */ public function getIdByUsername($username) { diff --git a/doc/api-board-procedures.markdown b/doc/api-board-procedures.markdown index d8503d6d..6f8a878e 100644 --- a/doc/api-board-procedures.markdown +++ b/doc/api-board-procedures.markdown @@ -156,261 +156,3 @@ Response example: ] } ``` - -## getColumns - -- Purpose: **Get all columns information for a given project** -- Parameters: - - **project_id** (integer, required) -- Result on success: **columns properties** -- Result on failure: **empty list** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getColumns", - "id": 887036325, - "params": [ - 1 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 887036325, - "result": [ - { - "id": "1", - "title": "Backlog", - "position": "1", - "project_id": "1", - "task_limit": "0" - }, - { - "id": "2", - "title": "Ready", - "position": "2", - "project_id": "1", - "task_limit": "0" - }, - { - "id": "3", - "title": "Work in progress", - "position": "3", - "project_id": "1", - "task_limit": "0" - } - ] -} -``` - -## getColumn - -- Purpose: **Get a single column** -- Parameters: - - **column_id** (integer, required) -- Result on success: **column properties** -- Result on failure: **null** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "getColumn", - "id": 1242049935, - "params": [ - 2 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1242049935, - "result": { - "id": "2", - "title": "Youpi", - "position": "2", - "project_id": "1", - "task_limit": "5" - } -} -``` - -## moveColumnUp - -- Purpose: **Move up the column position** -- Parameters: - - **project_id** (integer, required) - - **column_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "moveColumnUp", - "id": 99275573, - "params": [ - 1, - 2 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 99275573, - "result": true -} -``` - -## moveColumnDown - -- Purpose: **Move down the column position** -- Parameters: - - **project_id** (integer, required) - - **column_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "moveColumnDown", - "id": 957090649, - "params": { - "project_id": 1, - "column_id": 2 - } -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 957090649, - "result": true -} -``` - -## updateColumn - -- Purpose: **Update column properties** -- Parameters: - - **column_id** (integer, required) - - **title** (string, required) - - **task_limit** (integer, optional) - - **description** (string, optional) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "updateColumn", - "id": 480740641, - "params": [ - 2, - "Boo", - 5 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 480740641, - "result": true -} -``` - -## addColumn - -- Purpose: **Add a new column** -- Parameters: - - **project_id** (integer, required) - - **title** (string, required) - - **task_limit** (integer, optional) - - **description** (string, optional) -- Result on success: **column_id** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "addColumn", - "id": 638544704, - "params": [ - 1, - "Boo" - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 638544704, - "result": 5 -} -``` - -## removeColumn - -- Purpose: **Remove a column** -- Parameters: - - **column_id** (integer, required) -- Result on success: **true** -- Result on failure: **false** - -Request example: - -```json -{ - "jsonrpc": "2.0", - "method": "removeColumn", - "id": 1433237746, - "params": [ - 1 - ] -} -``` - -Response example: - -```json -{ - "jsonrpc": "2.0", - "id": 1433237746, - "result": true -} -``` diff --git a/doc/api-column-procedures.markdown b/doc/api-column-procedures.markdown new file mode 100644 index 00000000..5bae7f76 --- /dev/null +++ b/doc/api-column-procedures.markdown @@ -0,0 +1,229 @@ +API Column Procedures +===================== + +## getColumns + +- Purpose: **Get all columns information for a given project** +- Parameters: + - **project_id** (integer, required) +- Result on success: **columns properties** +- Result on failure: **empty list** + +Request example: + +```json +{ + "jsonrpc": "2.0", + "method": "getColumns", + "id": 887036325, + "params": [ + 1 + ] +} +``` + +Response example: + +```json +{ + "jsonrpc": "2.0", + "id": 887036325, + "result": [ + { + "id": "1", + "title": "Backlog", + "position": "1", + "project_id": "1", + "task_limit": "0" + }, + { + "id": "2", + "title": "Ready", + "position": "2", + "project_id": "1", + "task_limit": "0" + }, + { + "id": "3", + "title": "Work in progress", + "position": "3", + "project_id": "1", + "task_limit": "0" + } + ] +} +``` + +## getColumn + +- Purpose: **Get a single column** +- Parameters: + - **column_id** (integer, required) +- Result on success: **column properties** +- Result on failure: **null** + +Request example: + +```json +{ + "jsonrpc": "2.0", + "method": "getColumn", + "id": 1242049935, + "params": [ + 2 + ] +} +``` + +Response example: + +```json +{ + "jsonrpc": "2.0", + "id": 1242049935, + "result": { + "id": "2", + "title": "Youpi", + "position": "2", + "project_id": "1", + "task_limit": "5" + } +} +``` + +## changeColumnPosition + +- Purpose: **Change the column position** +- Parameters: + - **project_id** (integer, required) + - **column_id** (integer, required) + - **position** (integer, required) +- Result on success: **true** +- Result on failure: **false** + +Request example: + +```json +{ + "jsonrpc": "2.0", + "method": "changeColumnPosition", + "id": 99275573, + "params": [ + 1, + 2, + 3 + ] +} +``` + +Response example: + +```json +{ + "jsonrpc": "2.0", + "id": 99275573, + "result": true +} +``` + +## updateColumn + +- Purpose: **Update column properties** +- Parameters: + - **column_id** (integer, required) + - **title** (string, required) + - **task_limit** (integer, optional) + - **description** (string, optional) +- Result on success: **true** +- Result on failure: **false** + +Request example: + +```json +{ + "jsonrpc": "2.0", + "method": "updateColumn", + "id": 480740641, + "params": [ + 2, + "Boo", + 5 + ] +} +``` + +Response example: + +```json +{ + "jsonrpc": "2.0", + "id": 480740641, + "result": true +} +``` + +## addColumn + +- Purpose: **Add a new column** +- Parameters: + - **project_id** (integer, required) + - **title** (string, required) + - **task_limit** (integer, optional) + - **description** (string, optional) +- Result on success: **column_id** +- Result on failure: **false** + +Request example: + +```json +{ + "jsonrpc": "2.0", + "method": "addColumn", + "id": 638544704, + "params": [ + 1, + "Boo" + ] +} +``` + +Response example: + +```json +{ + "jsonrpc": "2.0", + "id": 638544704, + "result": 5 +} +``` + +## removeColumn + +- Purpose: **Remove a column** +- Parameters: + - **column_id** (integer, required) +- Result on success: **true** +- Result on failure: **false** + +Request example: + +```json +{ + "jsonrpc": "2.0", + "method": "removeColumn", + "id": 1433237746, + "params": [ + 1 + ] +} +``` + +Response example: + +```json +{ + "jsonrpc": "2.0", + "id": 1433237746, + "result": true +} +``` diff --git a/doc/api-json-rpc.markdown b/doc/api-json-rpc.markdown index 34559df5..bb14b008 100644 --- a/doc/api-json-rpc.markdown +++ b/doc/api-json-rpc.markdown @@ -52,6 +52,7 @@ Usage - [Projects](api-project-procedures.markdown) - [Project Permissions](api-project-permission-procedures.markdown) - [Boards](api-board-procedures.markdown) +- [Columns](api-column-procedures.markdown) - [Swimlanes](api-swimlane-procedures.markdown) - [Categories](api-category-procedures.markdown) - [Automatic Actions](api-action-procedures.markdown) -- cgit v1.2.3